magi-code 0.104.0

Repository-aware CLI coding agent for terminal work
Documentation
//! Credential preparation for effective runtime configuration.

use super::{
    AuthState, ProviderCredential, codex_credential_from_store, read_auth,
    resolve_provider_credential,
};
use crate::config::{
    CliConfigOverrides, ConfigError, EffectiveConfig, McPaths, Settings, read_settings,
    reject_retired_provider_selection, resolve_output_style,
};

impl EffectiveConfig {
    pub(crate) fn from_loaded_settings(
        paths: McPaths,
        cli: CliConfigOverrides,
        settings: Settings,
    ) -> anyhow::Result<Self> {
        let (color_enabled, _) = resolve_output_style(&settings, cli.color);
        let (provider, model) = Self::selected_provider_model(&settings);
        let provider = cli.provider.or(provider);
        let model = cli.model.or(model);
        let provider_id = provider
            .as_deref()
            .unwrap_or(crate::provider_ids::OPENAI_CODEX_PROVIDER);
        reject_retired_provider_selection(provider_id, model.as_deref().unwrap_or(""))?;
        let auth_file = read_auth(&paths)?;
        let auth = resolve_provider_credential(
            provider_id,
            &auth_file,
            cli.api_key,
            &settings.custom_providers,
        )?;
        Ok(Self {
            provider,
            model,
            no_color: !color_enabled,
            file_autocomplete_respects_gitignore: settings.file_autocomplete_respects_gitignore,
            custom_providers: settings.custom_providers,
            thinking_level: settings.selected_model.thinking_level.unwrap_or_default(),
            auth,
            paths,
        })
    }

    pub(crate) fn auth_state(&self) -> AuthState {
        AuthState::for_provider_with_custom(
            self.provider_id(),
            self.auth.as_ref(),
            &self.custom_providers,
        )
    }

    pub(crate) fn require_auth(&self) -> Result<ProviderCredential, ConfigError> {
        self.auth_state()
            .credential()
            .cloned()
            .ok_or_else(|| self.missing_auth_error())
    }

    pub(crate) fn resolve_provider_auth_for_runtime(&self) -> anyhow::Result<ProviderCredential> {
        self.resolve_provider_auth_for_runtime_with(codex_credential_from_store)
    }

    fn resolve_provider_auth_for_runtime_with(
        &self,
        prepare_codex_auth: impl FnOnce(&McPaths) -> anyhow::Result<ProviderCredential>,
    ) -> anyhow::Result<ProviderCredential> {
        if self.provider_id() == crate::provider_ids::ANTHROPIC_PROVIDER {
            // Long-lived configs must not reuse a stored key after logout or replacement.
            return resolve_provider_credential(
                self.provider_id(),
                &read_auth(&self.paths)?,
                None,
                &self.custom_providers,
            )?
            .ok_or_else(|| self.missing_auth_error().into());
        }

        if self.provider_id() == crate::provider_ids::CLAUDE_SUBSCRIPTION_PROVIDER {
            return resolve_provider_credential(
                self.provider_id(),
                &read_auth(&self.paths)?,
                None,
                &self.custom_providers,
            )?
            .ok_or_else(|| self.missing_auth_error().into());
        }
        if self.provider_id() != crate::provider_ids::OPENAI_CODEX_PROVIDER {
            return self.require_auth().map_err(Into::into);
        }

        // ProviderCredential intentionally omits expiry, so Codex runtime auth must come from
        // the current store and be classified/refreshed before provider construction.
        prepare_codex_auth(&self.paths)
    }
}

pub(crate) fn load_effective_provider_selection(
    paths: &McPaths,
    provider: &str,
    model: &str,
) -> anyhow::Result<EffectiveConfig> {
    load_effective_provider_selection_with_settings(paths, provider, model, &read_settings(paths)?)
}

pub(crate) fn load_effective_provider_selection_with_settings(
    paths: &McPaths,
    provider: &str,
    model: &str,
    settings: &Settings,
) -> anyhow::Result<EffectiveConfig> {
    reject_retired_provider_selection(provider, model)?;
    let auth_file = read_auth(paths)?;
    let auth = resolve_provider_credential(provider, &auth_file, None, &settings.custom_providers)?;
    let (color_enabled, _) = resolve_output_style(settings, None);
    Ok(EffectiveConfig {
        provider: Some(provider.to_string()),
        model: Some(model.to_string()),
        no_color: !color_enabled,
        file_autocomplete_respects_gitignore: settings.file_autocomplete_respects_gitignore,
        custom_providers: settings.custom_providers.clone(),
        thinking_level: settings.selected_model.thinking_level.unwrap_or_default(),
        auth,
        paths: paths.clone(),
    })
}