/** Implementation of `openclaw models status`. */ import path from "node:path"; import { stripSelfProviderModelPrefix } from "@openclaw/model-catalog-core/provider-model-id-normalization"; import { parseStrictFiniteNumber, parseStrictPositiveInteger, } from "@openclaw/normalization-core/number-coercion"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { colorize, theme } from "../../../packages/terminal-core/src/theme.js"; import { resolveAgentExplicitModelPrimary, resolveAgentModelFallbacksOverride, resolveAgentWorkspaceDir, } from "../../agents/agent-scope.js"; import { buildAuthHealthSummary, DEFAULT_OAUTH_WARN_MS, formatRemainingShort, } from "../../agents/auth-health.js"; import { buildAuthProfileUnusableHint } from "../../agents/auth-profiles/oauth-refresh-failure.js"; import { resolveAuthStorePathForDisplay } from "../../agents/auth-profiles/paths.js"; import { ensureAuthProfileStore, ensureAuthProfileStoreWithoutExternalProfiles, } from "../../agents/auth-profiles/store-runtime.js"; import { getRuntimeAuthProfileStoreSnapshot } from "../../agents/auth-profiles/store.js"; import type { AuthProfileCredential } from "../../agents/auth-profiles/types.js"; import { resolveProfileUnusableUntilForDisplay } from "../../agents/auth-profiles/usage.js"; import { resolveAgentHarnessPolicy } from "../../agents/harness/policy.js"; import { resolveAgentHarnessOwnerPluginIds, resolveAgentHarnessRuntimeAvailability, type AgentHarnessRuntimeAvailability, } from "../../agents/harness/runtime-plugin.js"; import { createModelAuthAvailabilityResolver, type ModelAuthAvailabilityEvaluation, type ModelAuthAvailabilityResolver, } from "../../agents/model-auth-availability.js"; import { listProviderEnvAuthLookupKeys, resolveProviderEnvAuthLookupMaps, } from "../../agents/model-auth-env-vars.js"; import { resolveEnvApiKey } from "../../agents/model-auth.js"; import { resolveCliRuntimeExecutionProvider } from "../../agents/model-runtime-aliases.js"; import { resolveConfiguredModelPolicyAllow } from "../../agents/model-selection-shared.js"; import { buildModelAliasIndex, isCliProvider, modelKey, normalizeProviderId, resolveConfiguredModelRef, resolveModelRefFromString, } from "../../agents/model-selection.js"; import { createModelVisibilityPolicy } from "../../agents/model-visibility-policy.js"; import { resolveModelCatalogIdentityKey } from "../../agents/openai-model-routes.js"; import { OPENAI_PROVIDER_ID } from "../../agents/openai-routing.js"; import { loadPreparedModelCatalogSnapshot } from "../../agents/prepared-model-catalog.js"; import { resolveProviderIdForAuth } from "../../agents/provider-auth-aliases.js"; import { readUtilityModelSetting, resolveUtilityModelRefForAgent, } from "../../agents/utility-model.js"; import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js"; import { requestExitAfterOneShotOutput } from "../../cli/one-shot-exit.js"; import { createConfigIO } from "../../config/config.js"; import { resolveAgentModelFallbackValues, resolveAgentModelPrimaryValue, } from "../../config/model-input.js"; import { parseModelPolicyWildcardRef } from "../../config/model-policy-ref.js"; import { resolveMergedModelProviderConfig } from "../../config/model-provider-config.js"; import { getShellEnvAppliedKeys, shouldEnableShellEnvFallback } from "../../infra/shell-env.js"; import type { ProviderModelRouteCandidate } from "../../plugin-sdk/provider-model-types.js"; import { withPluginMetadataSnapshotScope } from "../../plugins/current-plugin-metadata-snapshot.js"; import { loadManifestMetadataSnapshot } from "../../plugins/manifest-contract-eligibility.js"; import type { ProviderSyntheticAuthResult } from "../../plugins/provider-external-auth.types.js"; import { prepareProviderSyntheticAuthWithPlugin } from "../../plugins/provider-runtime.js"; import { resolveRuntimeSyntheticAuthProviderRefs } from "../../plugins/synthetic-auth.runtime.js"; import { type RuntimeEnv, writeRuntimeJson, writeRuntimeStdout } from "../../runtime.js"; import { createLazyImportLoader } from "../../shared/lazy-promise.js"; import { resolveUserPath, shortenHomePath } from "../../utils.js"; import { formatProviderAuthProfileCounts, resolveProviderAuthOverview, } from "./list.auth-overview.js"; import { isRich } from "./list.format.js"; import type { AuthProbeSummary } from "./list.probe.js"; import type { ProviderAuthOverview } from "./list.types.js"; import { loadModelsConfig } from "./load-config.js"; import { DEFAULT_MODEL, DEFAULT_PROVIDER, ensureFlagCompatibility, resolveModelsTargetAgent, } from "./shared.js"; type ProviderUsageRuntime = typeof import("../../infra/provider-usage.js"); type ProgressRuntime = typeof import("../../cli/progress.js"); function resolveEnvAgentDirOverride(env: NodeJS.ProcessEnv = process.env): string | undefined { const override = env.OPENCLAW_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim(); return override ? resolveUserPath(override, env) : undefined; } type TerminalTableRuntime = typeof import("../../../packages/terminal-core/src/table.js"); type ListProbeRuntime = typeof import("./list.probe.js"); const providerUsageRuntimeLoader = createLazyImportLoader( () => import("../../infra/provider-usage.js"), ); const progressRuntimeLoader = createLazyImportLoader( () => import("../../cli/progress.js"), ); const terminalTableRuntimeLoader = createLazyImportLoader( () => import("../../../packages/terminal-core/src/table.js"), ); const listProbeRuntimeLoader = createLazyImportLoader( () => import("./list.probe.js"), ); const DISPLAY_MODEL_PARSE_OPTIONS = { allowPluginNormalization: false } as const; type StatusSyntheticAuth = { value: string; source: string; credential?: string; mode?: ProviderSyntheticAuthResult["mode"]; expiresAt?: number; }; type StatusProviderUseRef = { provider: string; model: string; allowCodexRuntimeFallback: boolean; /** Text uses get per-model route analysis; image auth stays provider-wide. */ routeScope: "text" | "image"; }; type StatusProviderUse = { provider: string; model: string; allowCodexRuntimeFallback: boolean; evaluation: ModelAuthAvailabilityEvaluation; usesCodexRuntimeAuth: boolean; runtimeAvailability?: AgentHarnessRuntimeAvailability; runtimeIncompatibility?: { code: string; message: string }; }; function resolveStatusProviderUseIncompatibility(usage: StatusProviderUse) { const routeResolution = usage.evaluation.routeResolution; return routeResolution?.kind === "incompatible" ? routeResolution : usage.runtimeIncompatibility; } type StatusRuntimeAuthStatus = "usable" | "missing" | "indeterminate"; type StatusRuntimeAuthRouteBase = { provider: string; runtime: string; authProvider: string; effective: ProviderAuthOverview["effective"]; }; type StatusRuntimeAuthRoute = | (StatusRuntimeAuthRouteBase & { status: StatusRuntimeAuthStatus; }) | (StatusRuntimeAuthRouteBase & { status: "unavailable"; authStatus: StatusRuntimeAuthStatus; runtimeStatus: "unavailable"; runtimeReason: Extract["reason"]; runtimeDetail: string; runtimePluginIds: string[]; }); type StatusModelRouteIssue = | { kind: "incompatible"; provider: string; model: string; code: string; message: string; } | { kind: "indeterminate"; provider: string; model: string; evidence?: ModelAuthAvailabilityEvaluation["evidence"]; message: string; } | { kind: "missing-auth"; provider: string; model: string; authRequirement: ProviderModelRouteCandidate["authRequirement"]; message: string; }; function parseOptionalPositiveFiniteOption(raw: unknown, label: string, fallback: number): number { if (raw === undefined || raw === null) { return fallback; } const parsed = parseStrictFiniteNumber(raw); if (parsed === undefined || parsed <= 0) { throw new Error(`${label} must be a positive number.`); } return parsed; } function parseOptionalPositiveIntegerOption(raw: unknown, label: string, fallback: number): number { if (raw === undefined || raw === null) { return fallback; } const parsed = parseStrictPositiveInteger(raw); if (parsed === undefined) { throw new Error(`${label} must be a positive integer.`); } return parsed; } function syntheticAuthCredential( provider: string, auth: StatusSyntheticAuth, ): AuthProfileCredential | undefined { if (!auth.mode) { return undefined; } if (auth.mode === "api-key") { return { type: "api_key", provider, key: auth.credential, }; } if (auth.mode === "token" || auth.mode === "oauth") { // Plugin synthetic OAuth is already materialized as a bearer token. Keep // it token-shaped so non-expiring credentials do not require refresh data. return { type: "token", provider, token: auth.credential, expires: auth.expiresAt, }; } return undefined; } function finishModelsStatusOutput( runtime: RuntimeEnv, check: boolean | undefined, checkStatus: number, ): void { if (check) { if (!requestExitAfterOneShotOutput(runtime, checkStatus)) { runtime.exit(checkStatus); } return; } requestExitAfterOneShotOutput(runtime); } /** Prints model default, auth, provider, and optional probe status. */ export async function modelsStatusCommand( opts: { json?: boolean; plain?: boolean; check?: boolean; probe?: boolean; probeProvider?: string; probeProfile?: string | string[]; probeTimeout?: string; probeConcurrency?: string; probeMaxTokens?: string; agent?: string; }, runtime: RuntimeEnv, ) { ensureFlagCompatibility(opts); if (opts.plain && opts.probe) { throw new Error("--probe cannot be used with --plain output."); } const configPath = createConfigIO().configPath; const cfg = await loadModelsConfig({ commandName: "models status", runtime, skipPluginValidation: opts.probe !== true, }); const explicitAgentId = opts.agent?.trim(); const { agentId: workspaceAgentId, agentDir } = resolveModelsTargetAgent(cfg, opts.agent, { agentDirOverride: explicitAgentId ? undefined : resolveEnvAgentDirOverride(), kind: "read", }); // Only an explicit --agent narrows the reported model/fallback overrides; an inferred // system-agent target still reports unscoped defaults, matching this command's shipped output. const agentId = explicitAgentId ? workspaceAgentId : undefined; const workspaceDir = resolveAgentWorkspaceDir(cfg, workspaceAgentId) ?? resolveDefaultAgentWorkspaceDir(); const agentModelPrimary = agentId ? resolveAgentExplicitModelPrimary(cfg, agentId) : undefined; const agentFallbacksOverride = agentId ? resolveAgentModelFallbacksOverride(cfg, agentId) : undefined; const metadataSnapshot = loadManifestMetadataSnapshot({ config: cfg, workspaceDir, env: process.env, }); const selectedPluginRootDirs = new Map( [...metadataSnapshot.byPluginId].map(([pluginId, plugin]) => [pluginId, plugin.rootDir]), ); const codexRuntimeAvailabilityByProvider = new Map< string, Promise >(); const resolveCodexRuntimeAvailability = ( provider: string, ): Promise => { const cached = codexRuntimeAvailabilityByProvider.get(provider); if (cached) { return cached; } const pending = (async () => { const { runPluginPayloadSmokeCheckForManifestRecords } = await import("../../plugins/payload-verification.js"); const ownerPluginIds = resolveAgentHarnessOwnerPluginIds({ runtime: "codex", provider, config: cfg, workspaceDir, }); const pluginPayloadSmoke = await runPluginPayloadSmokeCheckForManifestRecords({ plugins: ownerPluginIds.flatMap((pluginId) => { const plugin = metadataSnapshot.byPluginId.get(pluginId); return plugin ? [plugin] : []; }), env: process.env, }); return resolveAgentHarnessRuntimeAvailability({ runtime: "codex", provider, config: cfg, workspaceDir, payloadFailures: pluginPayloadSmoke.failures, payloadCheckedPluginIds: pluginPayloadSmoke.checked, selectedPluginRootDirs, }); })(); codexRuntimeAvailabilityByProvider.set(provider, pending); return pending; }; return withPluginMetadataSnapshotScope( metadataSnapshot, async () => { // agentId, not a synthetic config carrying the agent's primary into global // defaults: the canonical resolvers merge per-agent model rows themselves, so // the synthetic route resolved a bare per-agent alias against global defaults // and reported a different provider than runtime selects. const resolved = resolveConfiguredModelRef({ cfg, agentId, defaultProvider: DEFAULT_PROVIDER, defaultModel: DEFAULT_MODEL, ...DISPLAY_MODEL_PARSE_OPTIONS, }); const rawDefaultsModel = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model) ?? ""; const rawModel = agentModelPrimary ?? rawDefaultsModel; const resolvedLabel = modelKey(resolved.provider, resolved.model); const defaultLabel = rawModel || resolvedLabel; const defaultsFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.model); const fallbacks = agentFallbacksOverride ?? defaultsFallbacks; const imageModel = resolveAgentModelPrimaryValue(cfg.agents?.defaults?.imageModel) ?? ""; const imageFallbacks = resolveAgentModelFallbackValues(cfg.agents?.defaults?.imageModel); // Narration/titles ride the utility model on a plain API auth path that can // differ from the primary's (e.g. OAuth primary, api_key utility); show the // resolved ref so a broken utility credential is inspectable here. const utilitySetting = readUtilityModelSetting(cfg, workspaceAgentId); const utilityModelRef = resolveUtilityModelRefForAgent({ cfg, agentId: workspaceAgentId }); // resolveUtilityModelRefForAgent never falls back to the primary model: an // unset setting yields the provider-declared default or undefined, so a // non-null auto result really is a provider default (unlike the runtime's // simple-completion selection, which does fall back to the primary). const utilityModelSource = utilitySetting.kind === "explicit" ? "config" : utilitySetting.kind === "disabled" ? "disabled" : utilityModelRef ? "provider-default" : "none"; const configuredAllowRefs = [ ...resolveConfiguredModelPolicyAllow({ cfg, agentId: workspaceAgentId }).refs, ]; const modelsPath = path.join(agentDir, "models.json"); const aliasIndex = buildModelAliasIndex({ cfg, agentId, defaultProvider: DEFAULT_PROVIDER, ...DISPLAY_MODEL_PARSE_OPTIONS, }); // The index is the same effective alias set the resolvers use, so per-agent // rows that replace or empty-disable a default alias are reflected here // instead of being read straight off `agents.defaults.models`. const aliases = Object.fromEntries( [...aliasIndex.byAlias.values()].map((match) => [ match.alias, modelKey(match.ref.provider, match.ref.model), ]), ); const resolveStatusModelRef = (raw: string | undefined) => { const modelRef = raw?.trim(); if (!modelRef) { return undefined; } return resolveModelRefFromString({ cfg, agentId, raw: modelRef, defaultProvider: DEFAULT_PROVIDER, aliasIndex, ...DISPLAY_MODEL_PARSE_OPTIONS, })?.ref; }; const textUsesOpenAI = [defaultLabel, ...fallbacks].some( (raw) => normalizeProviderId(resolveStatusModelRef(raw)?.provider ?? "") === OPENAI_PROVIDER_ID, ); // Match execution's read-only, provider-scoped Codex CLI overlay. This lets // status select the same OpenAI subscription profile without scanning // unrelated external CLIs or prompting the keychain. const store = textUsesOpenAI ? ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false, config: cfg, externalCliProviderIds: [OPENAI_PROVIDER_ID], readOnly: true, }) : ensureAuthProfileStoreWithoutExternalProfiles(agentDir); const providersFromStore = new Set( Object.values(store.profiles) .map((profile) => normalizeProviderId(profile.provider)) .filter((p): p is string => Boolean(p)), ); const providersFromConfig = new Set( Object.keys(cfg.models?.providers ?? {}) .map((p) => (typeof p === "string" ? normalizeProviderId(p) : "")) .filter(Boolean), ); const providersFromModels = new Set(); const providerUseRefs: StatusProviderUseRef[] = []; const addProviderUse = ( raw: string | undefined, allowCodexRuntimeFallback: boolean, routeScope: StatusProviderUseRef["routeScope"], ) => { const ref = resolveStatusModelRef(raw); if (ref?.provider) { const provider = normalizeProviderId(ref.provider); providerUseRefs.push({ provider, model: ref.model, allowCodexRuntimeFallback, routeScope, }); } }; for (const raw of [ defaultLabel, ...fallbacks, imageModel, ...imageFallbacks, utilityModelRef ?? "", ...configuredAllowRefs, ]) { const ref = resolveStatusModelRef(raw); if (ref?.provider) { providersFromModels.add(normalizeProviderId(ref.provider)); } } for (const raw of [defaultLabel, ...fallbacks]) { addProviderUse(raw, true, "text"); } for (const raw of [imageModel, ...imageFallbacks]) { addProviderUse(raw, false, "image"); } // Utility completions (narration/titles) are a text-route auth consumer in // their own right: an OAuth-healthy primary does not prove the utility's // plain API path, so the ref gets full route analysis without inheriting // the primary's codex-runtime fallback. addProviderUse(utilityModelRef, false, "text"); // Display the canonical provider/model: utilityModel accepts aliases, and // route issues/probes always report the resolved ref. const resolvedUtilityRef = utilityModelRef ? resolveStatusModelRef(utilityModelRef) : undefined; const utilityModelDisplayRef = resolvedUtilityRef ? modelKey(normalizeProviderId(resolvedUtilityRef.provider), resolvedUtilityRef.model) : (utilityModelRef ?? null); const providersFromEnv = new Set(); // Use the shared provider-env registry so `models status` stays aligned with // env-backed providers beyond the text-model defaults (for example image-gen). const envLookupParams = { config: cfg, workspaceDir, env: process.env, metadataSnapshot, }; const { aliasMap, envCandidateMap, authEvidenceMap } = resolveProviderEnvAuthLookupMaps(envLookupParams); for (const provider of listProviderEnvAuthLookupKeys({ envCandidateMap, authEvidenceMap })) { if ( resolveEnvApiKey(provider, process.env, { config: cfg, workspaceDir, aliasMap, candidateMap: envCandidateMap, authEvidenceMap, skipSetupProviderFallback: true, }) ) { providersFromEnv.add(provider); } } const syntheticAuthProviderRefs = new Set( resolveRuntimeSyntheticAuthProviderRefs({ index: metadataSnapshot.index, registryDiagnostics: metadataSnapshot.registryDiagnostics, }).map((provider) => normalizeProviderId(provider)), ); const createStatusAuthResolver = ( authStore: Parameters[0]["authStore"], nativeMode?: "api_key" | "oauth" | "token", ) => createModelAuthAvailabilityResolver({ cfg, agentId: workspaceAgentId, authStore, agentDir, workspaceDir, env: process.env, // Native mode chooses a harness route without becoming a host credential. syntheticAuthProviderRefs: [...syntheticAuthProviderRefs], preparedRuntimeAuthModes: nativeMode ? { codex: { source: "native", mode: nativeMode } } : {}, metadataSnapshot, }); let authResolver = createStatusAuthResolver(store); // Status already owns the complete provider/auth use set. Carry it into the // catalog owner so a read-only status does not discover every provider plugin. const probedProvider = normalizeOptionalString(opts.probeProvider); const providerDiscoveryProviderIds = [ ...new Set([ ...authResolver.providerDiscoveryProviderIds, ...providersFromConfig, ...providersFromModels, ...(probedProvider ? [normalizeProviderId(probedProvider)] : []), ]), ].toSorted((left, right) => left.localeCompare(right)); const catalog = await loadPreparedModelCatalogSnapshot({ config: cfg, agentId: workspaceAgentId, providerDiscoveryProviderIds, readOnly: true, }); const visibilityPolicy = createModelVisibilityPolicy({ cfg, catalog: catalog.entries, defaultProvider: resolved.provider, defaultModel: resolved.model, agentId: workspaceAgentId, ...DISPLAY_MODEL_PARSE_OPTIONS, }); const allowed = visibilityPolicy.allowAny ? [] : [ ...new Set([ ...visibilityPolicy.allowedCatalog.map((entry) => modelKey(entry.provider, entry.id)), ...configuredAllowRefs.flatMap((raw) => { const wildcard = parseModelPolicyWildcardRef(raw); if (!wildcard) { return []; } const prefix = wildcard.key.slice(0, -1); const hasCatalogMatch = catalog.entries.some((entry) => modelKey(entry.provider, entry.id).startsWith(prefix), ); return hasCatalogMatch ? [] : [wildcard.key]; }), ]), ].toSorted(); const routeSourcesByModel = new Map< string, Array<{ api?: (typeof catalog.routeVariants)[number]["api"]; baseUrl?: string }> >(); const resolveStatusRouteIdentityKey = (entry: { provider: string; id: string }) => { const provider = normalizeProviderId(entry.provider); // Physical catalog rows may repeat their provider in the model id. // Collapse only that prefix; the remaining model id stays case-sensitive. return resolveModelCatalogIdentityKey({ provider, id: stripSelfProviderModelPrefix(provider, modelKey(provider, entry.id)), }); }; for (const entry of catalog.routeVariants) { if (entry.api === undefined && entry.baseUrl === undefined) { continue; } const key = resolveStatusRouteIdentityKey(entry); const sources = routeSourcesByModel.get(key) ?? []; sources.push({ api: entry.api, baseUrl: entry.baseUrl }); routeSourcesByModel.set(key, sources); } const resolveProviderUses = async ( resolver: ModelAuthAvailabilityResolver, ): Promise => await Promise.all( providerUseRefs.map(async (usage) => { const observedRoutes = routeSourcesByModel.get( resolveStatusRouteIdentityKey({ provider: usage.provider, id: usage.model }), ); const ref = { modelId: usage.model, ...(observedRoutes ? { observedRoutes } : {}), }; // Image tools own their provider auth behavior. The text-route artifact // must not reinterpret image auth as an OpenAI text transport. const rawEvaluation: ModelAuthAvailabilityEvaluation = usage.routeScope === "text" ? usage.allowCodexRuntimeFallback ? resolver.evaluateRuntimeModelAuth(usage.provider, ref) : resolver.evaluateModelAuth(usage.provider, ref) : { availability: resolver.resolveProviderAuthAvailability(usage.provider, ref), routeResolution: null, }; const providerRouteIncompatible = rawEvaluation.routeResolution?.kind === "incompatible"; const requestedCodexRuntimeAuth = !providerRouteIncompatible && usage.allowCodexRuntimeFallback && resolveAgentHarnessPolicy({ provider: usage.provider, modelId: usage.model, ...(rawEvaluation.selectedRoute ? { modelApi: rawEvaluation.selectedRoute.api, modelBaseUrl: rawEvaluation.selectedRoute.baseUrl, } : {}), config: cfg, agentId: workspaceAgentId, }).runtime === "codex"; const runtimeIncompatibility = requestedCodexRuntimeAuth && usage.provider !== OPENAI_PROVIDER_ID && usage.provider !== "codex" ? { code: "unsupported-codex-runtime-provider", message: `The Codex runtime does not support provider ${usage.provider}.`, } : undefined; const usesCodexRuntimeAuth = requestedCodexRuntimeAuth && runtimeIncompatibility === undefined; const runtimeAvailability = usesCodexRuntimeAuth ? await resolveCodexRuntimeAvailability(usage.provider) : undefined; return { provider: usage.provider, model: usage.model, allowCodexRuntimeFallback: usage.allowCodexRuntimeFallback, evaluation: rawEvaluation, usesCodexRuntimeAuth, runtimeAvailability, runtimeIncompatibility, }; }), ); let providerUses = await resolveProviderUses(authResolver); const syntheticAuthByProvider = new Map(); const runtimeSyntheticAuthByProvider = new Map(); const cliRuntimeAuthUsages = providerUses // Codex harness auth is already modeled by the selected OpenAI route. // CLI-runtime aliases are only for distinct backends such as Gemini CLI. .filter((usage) => usage.allowCodexRuntimeFallback && !usage.usesCodexRuntimeAuth) .map((usage) => { const runtimeProvider = resolveCliRuntimeExecutionProvider({ provider: usage.provider, modelId: usage.model, cfg, agentId: workspaceAgentId, }); const normalizedRuntime = runtimeProvider ? normalizeProviderId(runtimeProvider) : undefined; return normalizedRuntime && normalizedRuntime !== usage.provider ? { provider: usage.provider, model: usage.model, allowCodexRuntimeFallback: usage.allowCodexRuntimeFallback, runtime: normalizedRuntime, } : undefined; }) .filter((usage): usage is NonNullable => Boolean(usage)); const providers = Array.from( new Set([ ...providersFromStore, ...providersFromConfig, ...providersFromModels, ...providersFromEnv, ...cliRuntimeAuthUsages.map((usage) => usage.runtime), ]), ) .map((p) => normalizeOptionalString(p) ?? "") .filter(Boolean) .toSorted((a, b) => a.localeCompare(b)); const syntheticProvidersToProbe = new Set( providers.map((provider) => normalizeProviderId(provider)), ); const codexProvider = normalizeProviderId(OPENAI_PROVIDER_ID); const codexProviderAlias = aliasMap[codexProvider] ?? codexProvider; let codexRuntimeAuthUsages = providerUses.filter((usage) => usage.usesCodexRuntimeAuth); if (codexRuntimeAuthUsages.length > 0) { syntheticProvidersToProbe.add(codexProvider); syntheticProvidersToProbe.add(codexProviderAlias); syntheticProvidersToProbe.add("codex"); } for (const provider of syntheticProvidersToProbe) { const normalized = normalizeProviderId(provider); if (!syntheticAuthProviderRefs.has(normalized)) { continue; } const resolvedLocal = await prepareProviderSyntheticAuthWithPlugin({ provider: normalized, config: cfg, workspaceDir, context: { config: cfg, provider: normalized, providerConfig: resolveMergedModelProviderConfig(cfg, normalized), }, }); if (!resolvedLocal) { continue; } const syntheticAuth: StatusSyntheticAuth = { value: "plugin-owned", source: resolvedLocal.source, credential: resolvedLocal.apiKey, mode: resolvedLocal.mode, expiresAt: resolvedLocal.expiresAt, }; syntheticAuthByProvider.set(normalized, syntheticAuth); // The generic Codex token authenticates the local harness, not an // OpenAI model route. Only provider-owned synthetic credentials may // become concrete evaluator profiles. if (normalized !== "codex") { runtimeSyntheticAuthByProvider.set(normalized, syntheticAuth); } if (normalized !== "codex" && normalized === codexProviderAlias) { syntheticAuthByProvider.set(codexProvider, syntheticAuth); } } const runtimeCredentialsByProvider = new Map( Array.from(runtimeSyntheticAuthByProvider.entries()) .map(([provider, auth]) => [provider, syntheticAuthCredential(provider, auth)] as const) .filter((entry): entry is readonly [string, AuthProfileCredential] => Boolean(entry[1])), ); const nativeCodexMode = syntheticAuthByProvider.get("codex")?.mode; if (runtimeCredentialsByProvider.size > 0 || nativeCodexMode) { const syntheticProfiles = Object.fromEntries( Array.from(runtimeCredentialsByProvider.entries()).map(([provider, credential]) => [ `${provider}:runtime-synthetic`, credential, ]), ); authResolver = createStatusAuthResolver( { ...store, profiles: { ...store.profiles, ...syntheticProfiles }, }, nativeCodexMode === "api-key" ? "api_key" : nativeCodexMode, ); providerUses = await resolveProviderUses(authResolver); codexRuntimeAuthUsages = providerUses.filter((usage) => usage.usesCodexRuntimeAuth); } const applied = getShellEnvAppliedKeys(); const shellFallbackEnabled = shouldEnableShellEnvFallback(process.env) || cfg.env?.shellEnv?.enabled === true; const providerAuth = Array.from( new Set([ ...providers, ...(codexRuntimeAuthUsages.length > 0 && syntheticAuthByProvider.has(codexProvider) ? [codexProvider] : []), ]), ) .toSorted((a, b) => a.localeCompare(b)) .map((provider) => resolveProviderAuthOverview({ provider, cfg, store, modelsPath, agentDir, workspaceDir, syntheticAuth: syntheticAuthByProvider.get(provider), aliasMap, envCandidateMap, authEvidenceMap, }), ) .filter((entry) => { const hasAny = entry.profiles.count > 0 || Boolean(entry.env) || Boolean(entry.modelsJson) || Boolean(entry.syntheticAuth); return hasAny; }); const providerAuthMap = new Map(providerAuth.map((entry) => [entry.provider, entry])); const missingProviderAuthEffective: ProviderAuthOverview["effective"] = { kind: "missing", detail: "missing", }; const runtimeAuthStore = getRuntimeAuthProfileStoreSnapshot(agentDir); const healthStore = runtimeAuthStore ? { ...store, profiles: { ...store.profiles, ...runtimeAuthStore.profiles }, } : store; const authHealth = buildAuthHealthSummary({ store: healthStore, cfg, warnAfterMs: DEFAULT_OAUTH_WARN_MS, runtimeCredentialsByProvider, allowKeychainPrompt: false, }); const authProfileHealthById = new Map( authHealth.profiles.map((profile) => [profile.profileId, profile]), ); const resolveProviderAuthHealthId = (provider: string): string => resolveProviderIdForAuth(provider, envLookupParams); const resolveRuntimeAuthRouteEffective = ( provider: string, evaluation?: ModelAuthAvailabilityEvaluation, ): ProviderAuthOverview["effective"] => { if (!evaluation) { return providerAuthMap.get(provider)?.effective ?? missingProviderAuthEffective; } if (evaluation?.availability === false) { return missingProviderAuthEffective; } if (evaluation.runtimeAuth?.source === "native") { return { kind: "synthetic", detail: syntheticAuthByProvider.get(evaluation.runtimeAuth.id)?.source ?? "native login", }; } const candidates = Array.from( new Set([normalizeProviderId(provider), resolveProviderAuthHealthId(provider)]), ); const profileId = evaluation.selectedProfileId; if (profileId) { const credentialProvider = store.profiles[profileId]?.provider ?? provider; const source = providerAuthMap.get( resolveProviderAuthHealthId(credentialProvider), )?.effective; return source && source.kind !== "missing" ? source : { kind: "profiles", detail: profileId }; } for (const candidate of candidates) { const auth = providerAuthMap.get(candidate); if (evaluation.evidence === "environment" && auth?.env) { return { kind: "env", detail: auth.env.value }; } if ( (evaluation.evidence === "provider-config" || evaluation.evidence === "runtime") && auth?.modelsJson ) { return { kind: "models.json", detail: auth.modelsJson.value }; } if (evaluation.evidence === "synthetic" && syntheticAuthByProvider.has(candidate)) { return { kind: "synthetic", detail: syntheticAuthByProvider.get(candidate)?.source ?? "plugin-owned", }; } } const direct = providerAuthMap.get(provider)?.effective; return direct ?? missingProviderAuthEffective; }; const resolveCliRuntimeAuthProvider = (usage: (typeof providerUses)[number]) => cliRuntimeAuthUsages.find( (candidate) => candidate.provider === usage.provider && candidate.model === usage.model && candidate.allowCodexRuntimeFallback === usage.allowCodexRuntimeFallback, )?.runtime; const hasUsableAuthForProviderInUse = (usage: (typeof providerUses)[number]): boolean => { const cliRuntimeAuthProvider = resolveCliRuntimeAuthProvider(usage); if (cliRuntimeAuthProvider) { return authResolver.resolveProviderAuthAvailability(cliRuntimeAuthProvider) !== false; } if (resolveStatusProviderUseIncompatibility(usage)) { // Route contract failures are reported separately from missing auth. return true; } // Unknown evidence is reported as indeterminate, not missing auth. return usage.evaluation.availability !== false; }; const codexRuntimeUsagesByProvider = new Map(); for (const usage of codexRuntimeAuthUsages) { const usages = codexRuntimeUsagesByProvider.get(usage.provider) ?? []; usages.push(usage); codexRuntimeUsagesByProvider.set(usage.provider, usages); } const runtimeAuthRouteEntries: Array = [ ...Array.from(codexRuntimeUsagesByProvider.entries()).map(([provider, usages]) => { const representative = usages.find((usage) => usage.evaluation.availability === true) ?? usages[0]; const effective = resolveRuntimeAuthRouteEffective( codexProvider, representative?.evaluation, ); const availabilities = usages.map((usage) => usage.evaluation.availability); const authStatus = availabilities.every((availability) => availability === true) ? "usable" : availabilities.some((availability) => availability === false) ? "missing" : "indeterminate"; const runtimeAvailability = representative?.runtimeAvailability; const route: StatusRuntimeAuthRoute = runtimeAvailability?.status === "unavailable" ? { provider, runtime: "codex", authProvider: codexProvider, status: "unavailable", effective, authStatus, runtimeStatus: runtimeAvailability.status, runtimeReason: runtimeAvailability.reason, runtimeDetail: runtimeAvailability.detail, runtimePluginIds: runtimeAvailability.ownerPluginIds, } : { provider, runtime: "codex", authProvider: codexProvider, status: authStatus, effective, }; return [`${provider}:codex:${codexProvider}`, route] as const; }), ...cliRuntimeAuthUsages.map((usage) => { const evaluation = authResolver.evaluateModelAuth(usage.runtime); const effective = resolveRuntimeAuthRouteEffective(usage.runtime, evaluation); return [ `${usage.provider}:${usage.runtime}:${usage.runtime}`, { provider: usage.provider, runtime: usage.runtime, authProvider: usage.runtime, status: evaluation.availability === true ? "usable" : evaluation.availability === false ? "missing" : "indeterminate", effective, }, ] as const; }), ]; const runtimeAuthRoutes = Array.from( new Map(runtimeAuthRouteEntries).values(), ).toSorted((a, b) => a.provider.localeCompare(b.provider)); const modelRouteIssues = providerUses.flatMap((usage) => { const cliRuntimeAuthProvider = resolveCliRuntimeAuthProvider(usage); const evaluation = cliRuntimeAuthProvider ? authResolver.evaluateModelAuth(cliRuntimeAuthProvider) : usage.evaluation; const incompatibility = resolveStatusProviderUseIncompatibility(usage); if (incompatibility) { return [ { kind: "incompatible" as const, provider: usage.provider, model: usage.model, code: incompatibility.code, message: incompatibility.message, }, ]; } if (evaluation.availability === undefined) { return [ { kind: "indeterminate" as const, provider: usage.provider, model: usage.model, ...(evaluation.evidence ? { evidence: evaluation.evidence } : {}), message: `Auth readiness could not be confirmed for ${usage.provider}/${usage.model}.`, }, ]; } if (!usage.evaluation.selectedRoute || evaluation.availability) { return []; } const authRequirement = usage.evaluation.selectedRoute.authRequirement; return [ { kind: "missing-auth" as const, provider: usage.provider, model: usage.model, authRequirement, message: `No usable ${authRequirement} authentication is available for ${usage.provider}/${usage.model}.`, }, ]; }); // Utility (or duplicate fallback) refs can repeat a configured model; // identical diagnostics collapse while genuinely different evaluations // for the same model (e.g. codex-fallback vs plain route) stay separate. const seenRouteIssues = new Set(); const dedupedModelRouteIssues = modelRouteIssues.filter((issue) => { const key = JSON.stringify(issue); if (seenRouteIssues.has(key)) { return false; } seenRouteIssues.add(key); return true; }); const missingProvidersInUse = Array.from( new Set( providerUses .filter((usage) => !hasUsableAuthForProviderInUse(usage)) .map((usage) => resolveCliRuntimeAuthProvider(usage) ?? usage.provider), ), ) .filter( (provider) => !isCliProvider(provider, cfg) || cliRuntimeAuthUsages.some((usage) => usage.runtime === provider), ) .toSorted((a, b) => a.localeCompare(b)); const probeProfileIds = (() => { if (!opts.probeProfile) { return []; } const raw = Array.isArray(opts.probeProfile) ? opts.probeProfile : [opts.probeProfile]; return raw .flatMap((value) => (value ?? "").split(",")) .map((value) => value.trim()) .filter(Boolean); })(); const probeTimeoutMs = parseOptionalPositiveFiniteOption( opts.probeTimeout, "--probe-timeout", 8000, ); const probeConcurrency = parseOptionalPositiveIntegerOption( opts.probeConcurrency, "--probe-concurrency", 2, ); const probeMaxTokens = parseOptionalPositiveIntegerOption( opts.probeMaxTokens, "--probe-max-tokens", 8, ); const rawCandidates = [ rawModel || resolvedLabel, ...fallbacks, imageModel, ...imageFallbacks, // Probe the configured utility model itself; an arbitrary catalog model // from the same provider can sit on a different auth route. utilityModelRef ?? "", ...configuredAllowRefs, ].filter(Boolean); const resolvedCandidates = rawCandidates .map( (raw) => resolveModelRefFromString({ cfg, agentId, raw: raw ?? "", defaultProvider: DEFAULT_PROVIDER, aliasIndex, ...DISPLAY_MODEL_PARSE_OPTIONS, })?.ref, ) .filter((ref): ref is { provider: string; model: string } => Boolean(ref)); const modelCandidates = resolvedCandidates.map((ref) => `${ref.provider}/${ref.model}`); let probeSummary: AuthProbeSummary | undefined; if (opts.probe) { const [{ withProgressTotals }, { runAuthProbes }] = await Promise.all([ progressRuntimeLoader.load(), listProbeRuntimeLoader.load(), ]); probeSummary = await withProgressTotals( { label: "Probing auth profiles…", total: 1 }, async (update) => { return await runAuthProbes({ cfg, agentId: workspaceAgentId, agentDir, workspaceDir, providers, modelCandidates, options: { provider: opts.probeProvider, profileIds: probeProfileIds, timeoutMs: probeTimeoutMs, concurrency: probeConcurrency, maxTokens: probeMaxTokens, }, // Direct CLI probes create hidden sessions in the canonical agent DB. // Gateway RPC probes omit this because the Gateway already owns the lock. stateOwnership: { mode: "exclusive" }, onProgress: update, }); }, ); } const providersWithOauth = providerAuth .filter( (entry) => entry.profiles.oauth > 0 || entry.profiles.token > 0 || entry.env?.value === "OAuth (env)", ) .map((entry) => { const count = entry.profiles.oauth + entry.profiles.token + (entry.env?.value === "OAuth (env)" ? 1 : 0); return `${entry.provider} (${count})`; }); const oauthProfiles = authHealth.profiles.filter( (profile) => profile.type === "oauth" || profile.type === "token", ); const unusableProfiles = (() => { const now = Date.now(); const out: Array<{ profileId: string; provider?: string; kind: "cooldown" | "disabled"; reason?: string; classification?: string; recoveryHint: string; until: number; remainingMs: number; }> = []; for (const profileId of Object.keys(store.usageStats ?? {})) { const unusableUntil = resolveProfileUnusableUntilForDisplay(store, profileId); if (!unusableUntil || now >= unusableUntil) { continue; } const stats = store.usageStats?.[profileId]; const kind = typeof stats?.disabledUntil === "number" && now < stats.disabledUntil ? "disabled" : "cooldown"; const reason = kind === "disabled" ? stats?.disabledReason : stats?.cooldownReason; const classification = kind === "cooldown" ? stats?.cooldownClassification : undefined; const provider = store.profiles[profileId]?.provider; out.push({ profileId, provider, kind, reason, ...(classification ? { classification } : {}), recoveryHint: buildAuthProfileUnusableHint({ kind, reason, provider: provider ?? profileId, profileId, }), until: unusableUntil, remainingMs: unusableUntil - now, }); } return out.toSorted((a, b) => a.remainingMs - b.remainingMs); })(); const checkStatus = (() => { type RequirementHealth = "ok" | "expiring" | "missing" | "indeterminate"; const resolveRouteAuthHealth = (usage: StatusProviderUse): RequirementHealth => { if (resolveStatusProviderUseIncompatibility(usage)) { return "missing"; } const cliRuntimeAuthProvider = resolveCliRuntimeAuthProvider(usage); const evaluation = cliRuntimeAuthProvider ? authResolver.evaluateModelAuth(cliRuntimeAuthProvider) : usage.evaluation; if (evaluation.availability === undefined) { return "indeterminate"; } if (!evaluation.availability) { return "missing"; } const profileId = evaluation.selectedProfileId; if (!profileId) { return "ok"; } const health = authProfileHealthById.get(profileId); if (health?.status === "expiring") { return "expiring"; } if (health?.status === "expired" || health?.status === "missing") { return "missing"; } return "ok"; }; const routeAuthHealth = new Set(providerUses.map(resolveRouteAuthHealth)); const hasExpiredOrMissing = dedupedModelRouteIssues.some( (issue) => issue.kind === "incompatible" || issue.kind === "indeterminate", ) || routeAuthHealth.has("missing") || routeAuthHealth.has("indeterminate") || runtimeAuthRoutes.some((route) => route.status === "unavailable") || missingProvidersInUse.length > 0; const hasExpiring = routeAuthHealth.has("expiring"); if (hasExpiredOrMissing) { return 1; } if (hasExpiring) { return 2; } return 0; })(); if (opts.json) { writeRuntimeJson(runtime, { configPath, ...(agentId ? { agentId } : {}), agentDir, defaultModel: defaultLabel, resolvedDefault: resolvedLabel, fallbacks, imageModel: imageModel || null, imageFallbacks, utilityModel: { ref: utilityModelDisplayRef, source: utilityModelSource }, ...(agentId ? { modelConfig: { defaultSource: agentModelPrimary ? "agent" : "defaults", fallbacksSource: agentFallbacksOverride !== undefined ? "agent" : "defaults", }, } : {}), aliases, allowed, auth: { storePath: resolveAuthStorePathForDisplay(agentDir), shellEnvFallback: { enabled: shellFallbackEnabled, appliedKeys: applied, }, providersWithOAuth: providersWithOauth, missingProvidersInUse, modelRouteIssues: dedupedModelRouteIssues, runtimeAuthRoutes, providers: providerAuth, unusableProfiles, oauth: { warnAfterMs: authHealth.warnAfterMs, profiles: authHealth.profiles, providers: authHealth.providers, }, probes: probeSummary, }, }); finishModelsStatusOutput(runtime, opts.check, checkStatus); return; } if (opts.plain) { writeRuntimeStdout(runtime, resolvedLabel); finishModelsStatusOutput(runtime, opts.check, checkStatus); return; } const rich = isRich(opts); type ModelConfigSource = "agent" | "defaults"; const label = (value: string) => colorize(rich, theme.accent, value.padEnd(14)); const labelWithSource = (value: string, source?: ModelConfigSource) => label(source ? `${value} (${source})` : value); const displayDefault = rawModel && rawModel !== resolvedLabel ? `${resolvedLabel} (from ${rawModel})` : resolvedLabel; runtime.log( `${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, shortenHomePath(configPath))}`, ); runtime.log( `${label("Agent dir")}${colorize(rich, theme.muted, ":")} ${colorize( rich, theme.info, shortenHomePath(agentDir), )}`, ); runtime.log( `${labelWithSource("Default", agentId ? (agentModelPrimary ? "agent" : "defaults") : undefined)}${colorize( rich, theme.muted, ":", )} ${colorize(rich, theme.success, displayDefault)}`, ); runtime.log( `${labelWithSource( `Fallbacks (${fallbacks.length || 0})`, agentId ? (agentFallbacksOverride !== undefined ? "agent" : "defaults") : undefined, )}${colorize(rich, theme.muted, ":")} ${colorize( rich, fallbacks.length ? theme.warn : theme.muted, fallbacks.length ? fallbacks.join(", ") : "-", )}`, ); runtime.log( `${label("Utility model")}${colorize(rich, theme.muted, ":")} ${colorize( rich, utilityModelDisplayRef ? theme.success : theme.muted, utilityModelDisplayRef ? `${utilityModelDisplayRef}${utilityModelSource === "provider-default" ? " (provider default)" : ""}` : utilityModelSource === "disabled" ? "off" : "-", )}`, ); runtime.log( `${labelWithSource("Image model", agentId ? "defaults" : undefined)}${colorize( rich, theme.muted, ":", )} ${colorize(rich, imageModel ? theme.accentBright : theme.muted, imageModel || "-")}`, ); runtime.log( `${labelWithSource( `Image fallbacks (${imageFallbacks.length || 0})`, agentId ? "defaults" : undefined, )}${colorize(rich, theme.muted, ":")} ${colorize( rich, imageFallbacks.length ? theme.accentBright : theme.muted, imageFallbacks.length ? imageFallbacks.join(", ") : "-", )}`, ); runtime.log( `${label(`Aliases (${Object.keys(aliases).length || 0})`)}${colorize(rich, theme.muted, ":")} ${colorize( rich, Object.keys(aliases).length ? theme.accent : theme.muted, Object.keys(aliases).length ? Object.entries(aliases) .map(([alias, target]) => rich ? `${theme.accentDim(alias)} ${theme.muted("->")} ${theme.info(target)}` : `${alias} -> ${target}`, ) .join(", ") : "-", )}`, ); runtime.log( `${label(`Allowed models (${allowed.length || 0})`)}${colorize(rich, theme.muted, ":")} ${colorize( rich, allowed.length ? theme.info : theme.muted, allowed.length ? allowed.join(", ") : "all", )}`, ); runtime.log(""); runtime.log(colorize(rich, theme.heading, "Auth overview")); runtime.log( `${label("Auth store")}${colorize(rich, theme.muted, ":")} ${colorize( rich, theme.info, shortenHomePath(resolveAuthStorePathForDisplay(agentDir)), )}`, ); runtime.log( `${label("Shell env")}${colorize(rich, theme.muted, ":")} ${colorize( rich, shellFallbackEnabled ? theme.success : theme.muted, shellFallbackEnabled ? "on" : "off", )}${applied.length ? colorize(rich, theme.muted, ` (applied: ${applied.join(", ")})`) : ""}`, ); runtime.log( `${label(`Providers w/ OAuth/tokens (${providersWithOauth.length || 0})`)}${colorize( rich, theme.muted, ":", )} ${colorize( rich, providersWithOauth.length ? theme.info : theme.muted, providersWithOauth.length ? providersWithOauth.join(", ") : "-", )}`, ); const formatKey = (key: string) => colorize(rich, theme.warn, key); const formatKeyValue = (key: string, value: string) => `${formatKey(key)}=${colorize(rich, theme.info, value)}`; const formatSeparator = () => colorize(rich, theme.muted, " | "); for (const entry of providerAuth) { const separator = formatSeparator(); const bits: string[] = []; bits.push( formatKeyValue( "effective", `${colorize(rich, theme.accentBright, entry.effective.kind)}:${colorize( rich, theme.muted, entry.effective.detail, )}`, ), ); if (entry.profiles.count > 0) { bits.push(formatKeyValue("profiles", formatProviderAuthProfileCounts(entry.profiles))); if (entry.profiles.labels.length > 0) { bits.push(colorize(rich, theme.info, entry.profiles.labels.join(", "))); } } if (entry.env) { bits.push( formatKeyValue( "env", `${entry.env.value}${separator}${formatKeyValue("source", entry.env.source)}`, ), ); } if (entry.modelsJson) { bits.push( formatKeyValue( "models.json", `${entry.modelsJson.value}${separator}${formatKeyValue("source", entry.modelsJson.source)}`, ), ); } if (entry.syntheticAuth) { bits.push( formatKeyValue( "synthetic", `${entry.syntheticAuth.value}${separator}${formatKeyValue("source", entry.syntheticAuth.source)}`, ), ); } runtime.log(`- ${theme.heading(entry.provider)} ${bits.join(separator)}`); } if (runtimeAuthRoutes.length > 0) { runtime.log(""); runtime.log(colorize(rich, theme.heading, "Runtime auth")); for (const route of runtimeAuthRoutes) { const runtimeAvailability = route.status === "unavailable" ? `${formatSeparator()}${formatKeyValue( "auth", route.authStatus, )}${formatSeparator()}${formatKeyValue("runtime", route.runtimeStatus)}${ route.runtimeDetail ? `${formatSeparator()}${colorize(rich, theme.muted, route.runtimeDetail)}` : "" }` : ""; runtime.log( `- ${theme.heading(route.provider)} via ${colorize( rich, theme.accentBright, route.runtime, )} uses ${theme.heading(route.authProvider)} ${formatKeyValue( "effective", `${colorize(rich, theme.accentBright, route.effective.kind)}:${colorize( rich, theme.muted, route.effective.detail, )}`, )}${formatSeparator()}${formatKeyValue("status", route.status)}${runtimeAvailability}`, ); } } if (dedupedModelRouteIssues.length > 0) { runtime.log(""); runtime.log(colorize(rich, theme.heading, "Model route issues")); for (const issue of dedupedModelRouteIssues) { const modelRef = `${issue.provider}/${issue.model}`; if (issue.kind === "incompatible") { runtime.log(`- ${theme.heading(modelRef)} [${issue.code}] ${issue.message}`); continue; } if (issue.kind === "indeterminate") { runtime.log(`- ${theme.heading(modelRef)} [indeterminate] ${issue.message}`); continue; } runtime.log( `- ${theme.heading(modelRef)} requires ${issue.authRequirement} auth: ${issue.message}`, ); } } if (missingProvidersInUse.length > 0) { const { buildProviderAuthRecoveryHint } = await import("../../agents/provider-auth-recovery-hint.js"); runtime.log(""); runtime.log(colorize(rich, theme.heading, "Missing auth")); for (const provider of missingProvidersInUse) { const requiresSubscription = dedupedModelRouteIssues.some( (issue) => issue.kind === "missing-auth" && issue.provider === provider && issue.authRequirement === "subscription", ); const hint = buildProviderAuthRecoveryHint({ provider, config: cfg, includeEnvVar: !requiresSubscription, }); runtime.log(`- ${theme.heading(provider)} ${hint}`); } } if (unusableProfiles.length > 0) { runtime.log(""); runtime.log(colorize(rich, theme.heading, "Unavailable auth profiles")); for (const profile of unusableProfiles) { const diagnostic = profile.classification ?? profile.reason; const reason = diagnostic ? `:${diagnostic}` : ""; const provider = profile.provider ? ` (${profile.provider})` : ""; runtime.log( `- ${theme.heading(profile.profileId)}${provider} ${profile.kind}${reason} (${formatRemainingShort(profile.remainingMs)}) — ${profile.recoveryHint}`, ); } } runtime.log(""); runtime.log(colorize(rich, theme.heading, "OAuth/token status")); if (oauthProfiles.length === 0) { runtime.log(colorize(rich, theme.muted, "- none")); } else { const { formatUsageWindowSummary, loadProviderUsageSummary, resolveUsageProviderId } = await providerUsageRuntimeLoader.load(); const usageByProvider = new Map(); const usageProviders = Array.from( new Set( oauthProfiles .map((profile) => resolveUsageProviderId(profile.provider, { credentialType: profile.type }), ) .filter((provider): provider is NonNullable => Boolean(provider)), ), ); if (usageProviders.length > 0) { try { const usageSummary = await loadProviderUsageSummary({ providers: usageProviders, agentDir, timeoutMs: 3500, }); for (const snapshot of usageSummary.providers) { const formatted = formatUsageWindowSummary(snapshot, { now: Date.now(), maxWindows: 2, includeResets: true, }); if (formatted) { usageByProvider.set(snapshot.provider, formatted); } } } catch { // ignore usage failures } } const formatStatus = (status: string) => { if (status === "ok") { return colorize(rich, theme.success, "ok"); } if (status === "static") { return colorize(rich, theme.muted, "static"); } if (status === "expiring") { return colorize(rich, theme.warn, "expiring"); } if (status === "missing") { return colorize(rich, theme.warn, "unknown"); } return colorize(rich, theme.error, "expired"); }; const profilesByProvider = new Map(); for (const profile of oauthProfiles) { const current = profilesByProvider.get(profile.provider); if (current) { current.push(profile); } else { profilesByProvider.set(profile.provider, [profile]); } } for (const [provider, profiles] of profilesByProvider) { const usageProfile = profiles.find( (profile) => profile.type === "oauth" || profile.type === "token", ); const usageKey = resolveUsageProviderId(provider, { credentialType: usageProfile?.type, }); const usage = usageKey ? usageByProvider.get(usageKey) : undefined; const usageSuffix = usage ? colorize(rich, theme.muted, ` usage: ${usage}`) : ""; runtime.log(`- ${colorize(rich, theme.heading, provider)}${usageSuffix}`); for (const profile of profiles) { const labelText = profile.label || profile.profileId; const labelLocal = colorize(rich, theme.accent, labelText); const status = formatStatus(profile.status); const expiry = profile.status === "static" ? "" : profile.expiresAt ? ` expires in ${formatRemainingShort(profile.remainingMs)}` : " expires unknown"; runtime.log(` - ${labelLocal} ${status}${expiry}`); } } } if (probeSummary) { const [ { getTerminalTableWidth, renderTable }, { describeProbeSummary, formatProbeLatency, sortProbeResults }, ] = await Promise.all([terminalTableRuntimeLoader.load(), listProbeRuntimeLoader.load()]); runtime.log(""); runtime.log(colorize(rich, theme.heading, "Auth probes")); if (probeSummary.results.length === 0) { runtime.log(colorize(rich, theme.muted, "- none")); } else { const tableWidth = getTerminalTableWidth(); const sorted = sortProbeResults(probeSummary.results); const statusColor = (status: string) => { if (status === "ok") { return theme.success; } if (status === "rate_limit") { return theme.warn; } if (status === "timeout" || status === "billing") { return theme.warn; } if (status === "auth" || status === "format") { return theme.error; } if (status === "no_model") { return theme.muted; } return theme.muted; }; const rows = sorted.map((result) => { const status = colorize(rich, statusColor(result.status), result.status); const latency = formatProbeLatency(result.latencyMs); const modelLabel = result.model ?? `${result.provider}/-`; const modeLabel = result.mode ? ` ${colorize(rich, theme.muted, `(${result.mode})`)}` : ""; const profile = `${colorize(rich, theme.accent, result.label)}${modeLabel}`; const detail = result.error?.trim(); const detailLabel = detail ? `\n${colorize(rich, theme.muted, `↳ ${detail}`)}` : ""; const statusLabel = `${status}${colorize(rich, theme.muted, ` · ${latency}`)}${detailLabel}`; return { Model: colorize(rich, theme.heading, modelLabel), Profile: profile, Status: statusLabel, }; }); runtime.log( renderTable({ width: tableWidth, columns: [ { key: "Model", header: "Model", minWidth: 18 }, { key: "Profile", header: "Profile", minWidth: 24 }, { key: "Status", header: "Status", minWidth: 12 }, ], rows, }).trimEnd(), ); runtime.log(colorize(rich, theme.muted, describeProbeSummary(probeSummary))); } } finishModelsStatusOutput(runtime, opts.check, checkStatus); }, { config: cfg, workspaceDir, env: process.env }, ); } /* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */