Download src/commands/models/list.status-command.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 67.4 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/commands/models/list.status-command.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/commands/models/list.status-command.ts
-
curl -L -o list.status-command.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/commands/models/list.status-command.ts
67.4 kB
| /** 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<ProviderUsageRuntime>( | |
| () => import("../../infra/provider-usage.js"), | |
| ); | |
| const progressRuntimeLoader = createLazyImportLoader<ProgressRuntime>( | |
| () => import("../../cli/progress.js"), | |
| ); | |
| const terminalTableRuntimeLoader = createLazyImportLoader<TerminalTableRuntime>( | |
| () => import("../../../packages/terminal-core/src/table.js"), | |
| ); | |
| const listProbeRuntimeLoader = createLazyImportLoader<ListProbeRuntime>( | |
| () => 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<AgentHarnessRuntimeAvailability, { status: "unavailable" }>["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<AgentHarnessRuntimeAvailability> | |
| >(); | |
| const resolveCodexRuntimeAvailability = ( | |
| provider: string, | |
| ): Promise<AgentHarnessRuntimeAvailability> => { | |
| 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<string>(); | |
| 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<string>(); | |
| // 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<typeof createModelAuthAvailabilityResolver>[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<StatusProviderUse[]> => | |
| 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<string, StatusSyntheticAuth>(); | |
| const runtimeSyntheticAuthByProvider = new Map<string, StatusSyntheticAuth>(); | |
| 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<typeof usage> => 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<string, StatusProviderUse[]>(); | |
| for (const usage of codexRuntimeAuthUsages) { | |
| const usages = codexRuntimeUsagesByProvider.get(usage.provider) ?? []; | |
| usages.push(usage); | |
| codexRuntimeUsagesByProvider.set(usage.provider, usages); | |
| } | |
| const runtimeAuthRouteEntries: Array<readonly [string, StatusRuntimeAuthRoute]> = [ | |
| ...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<string, StatusRuntimeAuthRoute>(runtimeAuthRouteEntries).values(), | |
| ).toSorted((a, b) => a.provider.localeCompare(b.provider)); | |
| const modelRouteIssues = providerUses.flatMap<StatusModelRouteIssue>((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<string>(); | |
| 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<string, string>(); | |
| const usageProviders = Array.from( | |
| new Set( | |
| oauthProfiles | |
| .map((profile) => | |
| resolveUsageProviderId(profile.provider, { credentialType: profile.type }), | |
| ) | |
| .filter((provider): provider is NonNullable<typeof provider> => 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<string, typeof oauthProfiles>(); | |
| 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. */ | |