magi-code 0.97.5

Repository-aware CLI coding agent for terminal work
Documentation
//! Explicit single-provider refresh, using shared auth, discovery, enrichment and cache policy.
use super::*;
use crate::{cancellation::AgentCancellation, config::EffectiveConfig};

pub(crate) fn refresh_provider_catalog(
    config: &EffectiveConfig,
    settings: &crate::config::Settings,
    cancellation: &AgentCancellation,
) -> Result<(), &'static str> {
    let check = || cancellation.check().map_err(|_| "operation_cancelled");
    check()?;
    let provider = config.provider_id();
    let paths = &config.paths;
    let auth = &config.auth_state();
    let entries = match provider {
        providers::OPENAI_CODEX_PROVIDER => {
            let auth = crate::auth::refreshed_codex_auth_state(paths, auth)
                .map_err(|_| "authentication_unavailable")?;
            check()?;
            let AuthState::Ready {
                credential: ProviderCredential::OAuth { access, account_id },
                ..
            } = auth
            else {
                return Err("authentication_unavailable");
            };
            OpenAiCodexProvider::new(
                providers::DEFAULT_CODEX_MODEL,
                access,
                account_id,
                ReqwestHttpTransport,
            )
            .discover_model_catalog_cancellable(cancellation)
            .and_then(|entries| {
                cancellation.check()?;
                enrich_with_models_dev(entries)
            })
        }
        providers::ANTHROPIC_PROVIDER => {
            let AuthState::Ready {
                credential: ProviderCredential::ApiKey { key },
                ..
            } = auth
            else {
                return Err("authentication_unavailable");
            };
            AnthropicProvider::new(
                providers::DEFAULT_ANTHROPIC_MODEL,
                key.clone(),
                ReqwestHttpTransport,
            )
            .discover_model_catalog_cancellable(cancellation)
            .and_then(|entries| {
                cancellation.check()?;
                models_dev::enrich_anthropic_catalog(entries)
            })
        }
        providers::CLAUDE_SUBSCRIPTION_PROVIDER => SharedModelsDevLookup::default()
            .get()
            .map_err(anyhow::Error::from)
            .and_then(|value| claude_subscription_candidates(value).map_err(anyhow::Error::msg)),
        _ => {
            let custom = settings
                .custom_providers
                .get(provider)
                .ok_or("provider_unavailable")?;
            let credential = match auth {
                AuthState::Ready { credential, .. } => Some(credential),
                _ => None,
            };
            let api_key = custom_provider_catalog_api_key(provider, credential)
                .map_err(|_| "authentication_unavailable")?;
            let extra_models = crate::config::normalized_extra_models(&custom.extra_models)
                .map_err(|_| "configuration_invalid")?;
            crate::providers::OpenAiCompatibleProvider::custom(
                provider.to_owned(),
                providers::DEFAULT_CODEX_MODEL,
                api_key,
                custom.base_url.clone(),
                custom.use_responses_endpoint,
                ReqwestHttpTransport,
            )
            .discover_model_catalog_cancellable(cancellation)
            .and_then(|entries| {
                cancellation.check()?;
                enrich_custom_provider_catalog(entries, provider, custom)
            })
            .and_then(|entries| {
                merge_custom_provider_extra_models(entries, provider, &extra_models)
                    .map_err(anyhow::Error::msg)
            })
        }
    };
    check()?;
    let entries = entries.map_err(|error| {
        if let Some(failure) = error.downcast_ref::<crate::providers::ModelCatalogRequestFailure>()
        {
            return failure.code();
        }
        match error.downcast_ref::<models_dev::ModelsDevFetchError>() {
            Some(models_dev::ModelsDevFetchError::Timeout) => "remote_timeout",
            Some(models_dev::ModelsDevFetchError::HttpStatus(429)) => "rate_limited",
            _ => "remote_unavailable",
        }
    })?;
    if entries.is_empty() {
        return Err("empty_catalog");
    }
    if entries.len() > 2048 {
        return Err("catalog_limit");
    }
    // Bound the exact cache representation before replacing last-known usable contents.
    let fingerprint = match settings.custom_providers.get(provider) {
        Some(custom) => cache::custom_provider_catalog_cache_fingerprint(provider, custom)
            .map_err(|_| "configuration_invalid")?,
        None => cache::built_in_catalog_cache_fingerprint(provider),
    };
    let cached = cache::CachedCatalog {
        schema_version: cache::CACHE_SCHEMA_VERSION,
        provider: provider.to_owned(),
        cache_fingerprint: fingerprint.clone(),
        fetched_at: chrono::Utc::now(),
        entries,
    };
    let cached =
        cache::validate_catalog_cache(cached, provider, &fingerprint).ok_or("invalid_catalog")?;
    let identities: std::collections::BTreeSet<_> =
        cached.entries.iter().map(|entry| &entry.id).collect();
    if identities.len() != cached.entries.len() {
        return Err("invalid_catalog");
    }
    let bytes = serde_json::to_vec_pretty(&cached).map_err(|_| "catalog_limit")?;
    if bytes.len() > 1024 * 1024 {
        return Err("catalog_limit");
    }
    check()?;
    let path = cache::catalog_cache_path(paths, provider).ok_or("configuration_invalid")?;
    crate::persistence::atomic_write(&path, &bytes).map_err(|_| "cache_write_failed")
}