import { resolveOpenClawAgentDir } from "../../agents/agent-paths.js"; import {
type AuthHealthSummary,
type AuthProfileHealthStatus,
type AuthProviderHealth,
type AuthProviderHealthStatus,
buildAuthHealthSummary,
formatRemainingShort,
} from "../../agents/auth-health.js"; import { ensureAuthProfileStore } from "../../agents/auth-profiles.js"; import { normalizeProviderId } from "../../agents/provider-id.js"; import { loadConfig, type OpenClawConfig } from "../../config/config.js"; import { isSecretRef } from "../../config/types.secrets.js"; import { loadProviderUsageSummary } from "../../infra/provider-usage.load.js"; import { PROVIDER_LABELS, resolveUsageProviderId } from "../../infra/provider-usage.shared.js"; import type { UsageProviderId, UsageWindow } from "../../infra/provider-usage.types.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { ErrorCodes, errorShape } from "../protocol/index.js"; import { formatForLog } from "../ws-log.js"; import type { GatewayRequestHandlers } from "./types.js";
export type ModelAuthStatusResult = { /** Snapshot build time, ms since epoch. 0 = never loaded (UI fallback sentinel). */
ts: number;
providers: ModelAuthStatusProvider[];
};
/** *CollectproviderIDswithrefreshablecredentials(OAuthorbearertoken) *soaconfigured-but-not-logged-inprovidersurfacesas`missing`rather *thanbeingsilentlyabsent.API-keyandAWS-SDKprovidersareexcluded— *theircredentialsdon'texpireonaschedulethisendpointcanmeaningfully *monitor,andsurfacingthemherewouldflasharedalertonahealthy *API-keysetup. * *Providerswith`models.providers.<id>.apiKey`set(commonlyviaa *SecretRefenvbinding)areexcludedfromthe"missing"synthesiseven *whentheir`auth`modeis`oauth`or`token`—anenv-backedcredential *isalreadypresent,soflaggingthedashboardasmissingwouldcrywolf *foraworkingauthpath.Theycanstillshowupwithrealstatusifthe *profilestorehasanentryforthem.
*/ function resolveConfiguredProviders(cfg: OpenClawConfig): {
providers: string[];
expectsOAuth: Set<string>;
} { const out = new Set<string>(); const expectsOAuth = new Set<string>(); // Providers with a resolvable apiKey (inline or SecretRef pointing at a // set env var) are treated as env-backed and skipped from the "missing" // synthesis. Captured once up front so both the models.providers scan // and the auth.profiles scan apply the escape hatch consistently. const envBacked = new Set<string>(); for (const [id, provider] of Object.entries(cfg.models?.providers ?? {})) { const apiKey = provider?.apiKey; if (!id || apiKey === undefined || apiKey === null) { continue;
} // Treat as env-backed when the credential is currently resolvable: // - inline string literal → always resolvable (satisfies auth today) // - env SecretRef → check process.env for the referenced id (the only // source we can cheaply verify synchronously on a dashboard read) // - file/exec SecretRef → conservatively treat as env-backed; we can't // read files or run commands here without making this a heavy async // path, and the alternative is crying wolf on valid configs // A SecretRef pointing at an unset env var falls through to the normal // "missing" synthesis so the dashboard surfaces the broken config.
let resolvable = false; if (typeof apiKey === "string" && apiKey.length > 0) {
resolvable = true;
} elseif (isSecretRef(apiKey)) { if (apiKey.source === "env") { const envValue = process.env[apiKey.id];
resolvable = typeof envValue === "string" && envValue.length > 0;
} else {
resolvable = true;
}
} if (resolvable) {
envBacked.add(normalizeProviderId(id));
}
} for (const [id, provider] of Object.entries(cfg.models?.providers ?? {})) { if (!id) { continue;
} // Only include providers whose configured auth mode is refreshable. // `undefined` / "api-key" / "aws-sdk" are deliberately skipped. const mode = provider?.auth; if (mode !== "oauth" && mode !== "token") { continue;
} if (envBacked.has(normalizeProviderId(id))) { continue;
}
out.add(id); if (mode === "oauth") { // Store normalized id so lookups against `AuthProviderHealth.provider` // (which is already normalized by buildAuthHealthSummary) match even // when the config uses an alias like `z.ai` that normalizes to `zai`.
expectsOAuth.add(normalizeProviderId(id));
}
} // auth.profiles entries explicitly opt into the refreshable set via // `mode: oauth | token`. api_key profiles are excluded (no lifecycle). for (const profile of Object.values(cfg.auth?.profiles ?? {})) { const provider = profile?.provider; const mode = profile?.mode; if ( typeof provider !== "string" ||
provider.length === 0 ||
(mode !== "oauth" && mode !== "token")
) { continue;
} if (envBacked.has(normalizeProviderId(provider))) { continue;
}
out.add(provider); if (mode === "oauth") {
expectsOAuth.add(normalizeProviderId(provider));
}
} return { providers: Array.from(out), expectsOAuth };
}
// Usage queries only for refreshable credentials. const usageProviderIds = [
...new Set(
authHealth.profiles
.filter((p) => p.type === "oauth" || p.type === "token")
.map((p) => resolveUsageProviderId(p.provider))
.filter((id): id is UsageProviderId => Boolean(id)),
),
];
const usageByProvider = new Map<string, { windows: UsageWindow[]; plan?: string }>(); if (usageProviderIds.length > 0) { try { const usage = await loadProviderUsageSummary({
providers: usageProviderIds,
agentDir,
timeoutMs: 3500,
}); for (const snap of usage.providers) {
usageByProvider.set(snap.provider, { windows: snap.windows, plan: snap.plan });
}
} catch (err) { // Usage data is auxiliary — failing here must not block auth status, // but log at debug so a silently-broken usage endpoint is still // diagnosable in gateway logs.
log.debug(
`usage enrichment failed (auth status still returned): providers=${usageProviderIds.join(",")} error=${formatForLog(err)}`,
);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.21Bemerkung:
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.