openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
276 lines (275 loc) • 12.3 kB
JavaScript
import { c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js";
import { s as coerceSecretRef } from "./types.secrets-kC0nOetj.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { C as selectApplicableRuntimeConfig, l as getRuntimeConfigSourceSnapshot, o as getRuntimeConfigSnapshot } from "./runtime-snapshot-BaQikjTR.js";
import "./config-Cs0XXL3x.js";
import { s as mintSecretSentinel } from "./sentinel-ZgmfESkJ.js";
import { n as resolveApiKeyForProfile } from "./oauth-BNreHOU-.js";
import { t as normalizeOptionalSecretInput } from "./normalize-secret-input-Df_qhWv_.js";
import { n as readCodexCliCredentialsCached } from "./cli-credentials-CHuqM5pH.js";
import { n as listProfilesForProvider } from "./profile-list-DyfWX-d2.js";
import { a as resolveAuthProfileOrder } from "./order-CC2RBzI5.js";
import { o as getModelProviderRequestTransport, r as attachModelProviderRequestTransport } from "./provider-request-config-DIOYidiO.js";
import { c as isNonSecretApiKeyMarker, u as isSecretRefHeaderValueMarker } from "./model-auth-markers-jBKQn38x.js";
import "./model-auth-env-Dq9W4xg9.js";
import { S as resolveUsableCustomProviderApiKey, _ as resolveProviderConfig, f as resolveConfigAwareEnvApiKey, g as resolveProviderAuthOverride, h as resolveInlineProviderApiKeyCooldownUntil, l as profileTypeToAuthMode, o as hasUsableCustomProviderApiKey, p as resolveConfiguredAwsSdkProfileAuth, s as isConfigBackedInlineProviderApiKey, u as providerConfigMatchesRuntimeSnapshot } from "./model-auth-provider-config-C_kr_q2g.js";
import "./auth-profiles-BdUEhE7u.js";
import { c as isAuthModeAllowedForModel, r as resolveScopedAuthProfileStore, s as prepareSyntheticLocalProviderAuth, t as resolveApiKeyForProviderCore } from "./model-auth-provider-Dd_3dXeY.js";
//#region src/agents/model-auth-model.ts
/**
* Model-level auth diagnostics and request-header preparation.
*/
const log = createSubsystemLogger("model-auth");
/** Reports the strongest configured auth mode for provider-list UI and diagnostics. */
function resolveModelAuthMode(provider, cfg, store, options) {
const resolved = provider?.trim();
if (!resolved) return;
if (resolveProviderAuthOverride(cfg, resolved) === "aws-sdk") return "aws-sdk";
const authStore = store ?? resolveScopedAuthProfileStore({
cfg,
provider: resolved
});
const profiles = listProfilesForProvider(authStore, resolved);
if (profiles.length > 0) {
const modes = new Set(profiles.map((id) => authStore.profiles[id]?.type).filter((mode) => Boolean(mode)));
if ([
"oauth",
"token",
"api_key"
].filter((k) => modes.has(k)).length >= 2) return "mixed";
if (modes.has("oauth")) return "oauth";
if (modes.has("token")) return "token";
if (modes.has("api_key")) return "api-key";
}
const envKey = resolveConfigAwareEnvApiKey(cfg, resolved, options?.workspaceDir);
if (envKey?.apiKey) return envKey.source.includes("OAUTH_TOKEN") ? "oauth" : "api-key";
if (normalizeProviderId(resolved) === "codex" && readCodexCliCredentialsCached({
ttlMs: 5e3,
allowKeychainPrompt: false
})) return "oauth";
if (hasUsableCustomProviderApiKey(cfg, resolved)) return "api-key";
return "unknown";
}
/** Checks provider auth availability, including profile fallback order. */
async function hasAvailableAuthForProvider(params) {
const { provider, cfg, preferredProfile } = params;
if (resolveProviderAuthOverride(cfg, provider) === "aws-sdk") return true;
const store = params.store ?? resolveScopedAuthProfileStore({
agentDir: params.agentDir,
cfg,
provider,
preferredProfile
});
const inlineUnusableUntil = resolveInlineProviderApiKeyCooldownUntil(store, provider);
const inlineProviderApiKeyUsable = typeof inlineUnusableUntil !== "number" || inlineUnusableUntil <= Date.now();
const envAuth = resolveConfigAwareEnvApiKey(cfg, provider, params.workspaceDir);
if (envAuth && isAuthModeAllowedForModel({
provider,
modelApi: params.modelApi,
mode: envAuth.source.includes("OAUTH_TOKEN") ? "oauth" : "api-key"
}) && (!isConfigBackedInlineProviderApiKey({
cfg,
provider,
source: envAuth.source,
store
}) || inlineProviderApiKeyUsable)) return true;
if (resolveUsableCustomProviderApiKey({
cfg,
provider
}) && inlineProviderApiKeyUsable) return true;
const syntheticLocalAuth = await prepareSyntheticLocalProviderAuth({
cfg,
provider,
workspaceDir: params.workspaceDir
});
if (syntheticLocalAuth && (!isConfigBackedInlineProviderApiKey({
cfg,
provider,
source: syntheticLocalAuth.source,
store
}) || inlineProviderApiKeyUsable)) return true;
const order = resolveAuthProfileOrder({
cfg,
store,
provider,
preferredProfile,
forModel: params.modelId
});
for (const candidate of order) try {
if (resolveConfiguredAwsSdkProfileAuth({
cfg,
provider,
profileId: candidate
})) return true;
const candidateType = store.profiles[candidate]?.type;
if (candidateType && !isAuthModeAllowedForModel({
provider,
modelApi: params.modelApi,
mode: profileTypeToAuthMode(candidateType)
})) continue;
const resolved = await resolveApiKeyForProfile({
cfg,
store,
profileId: candidate,
agentDir: params.agentDir
});
const mode = resolved?.profileType ?? store.profiles[candidate]?.type;
if (resolved && isAuthModeAllowedForModel({
provider,
modelApi: params.modelApi,
mode: mode ? profileTypeToAuthMode(mode) : "api-key"
})) return true;
} catch (err) {
log.debug?.(`auth profile "${candidate}" failed for provider "${provider}": ${String(err)}`);
}
return false;
}
/** Resolves request credentials from the provider attached to a model descriptor. */
async function getApiKeyForModelCore(params) {
return resolveApiKeyForProviderCore({
provider: params.model.provider,
cfg: params.cfg,
profileId: params.profileId,
preferredProfile: params.preferredProfile,
store: params.store,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
lockedProfile: params.lockedProfile,
credentialPrecedence: params.credentialPrecedence,
allowAuthProfileFallback: params.allowAuthProfileFallback,
skipSetupProviderFallback: params.skipSetupProviderFallback,
modelId: params.model.id,
modelApi: params.model.api,
secretSentinels: params.secretSentinels
});
}
/** Clears auth for local OpenAI-compatible servers that explicitly use no auth. */
function applyLocalNoAuthHeaderOverride(model, auth) {
if (auth?.apiKey !== "custom-local" || model.api !== "openai-completions") return model;
const headers = {
...model.headers,
Authorization: null
};
return {
...model,
headers
};
}
function applySecretRefHeaderSentinels(model, cfg) {
if (!model.headers) return model;
const runtimeConfig = getRuntimeConfigSnapshot();
const runtimeSourceConfig = getRuntimeConfigSourceSnapshot();
const usesRuntimeProvider = selectApplicableRuntimeConfig({
inputConfig: cfg,
runtimeConfig,
runtimeSourceConfig
}) === runtimeConfig || providerConfigMatchesRuntimeSnapshot({
inputConfig: cfg,
runtimeConfig,
provider: model.provider
});
if (!runtimeConfig || !runtimeSourceConfig || !usesRuntimeProvider) return model;
const sourceProvider = resolveProviderConfig(runtimeSourceConfig, model.provider);
const runtimeProvider = resolveProviderConfig(runtimeConfig, model.provider);
const replacements = /* @__PURE__ */ new Map();
const isManagedSecret = (value) => coerceSecretRef(value) !== null || typeof value === "string" && isSecretRefHeaderValueMarker(value);
const addReplacement = (name, value, replacement) => {
replacements.set(name.trim().toLowerCase(), {
value,
replacement: replacement ?? mintSecretSentinel(value, { label: `model-auth:${model.provider}` })
});
};
for (const [name, sourceValue] of Object.entries(sourceProvider?.headers ?? {})) {
if (!isManagedSecret(sourceValue)) continue;
const value = normalizeOptionalSecretInput(runtimeProvider?.headers?.[name]);
if (value) addReplacement(name, value);
}
for (const [name, sourceValue] of Object.entries(sourceProvider?.request?.headers ?? {})) {
if (!isManagedSecret(sourceValue)) continue;
const value = normalizeOptionalSecretInput(runtimeProvider?.request?.headers?.[name]);
if (value) addReplacement(name, value);
}
const sourceAuth = sourceProvider?.request?.auth;
const runtimeAuth = runtimeProvider?.request?.auth;
const attachedRequest = getModelProviderRequestTransport(model);
let protectedRequest = attachedRequest;
let protectedRequestHeaders;
for (const [name, sourceValue] of Object.entries(sourceProvider?.request?.headers ?? {})) {
if (!isManagedSecret(sourceValue)) continue;
const value = normalizeOptionalSecretInput(runtimeProvider?.request?.headers?.[name]);
if (!value || attachedRequest?.headers?.[name] !== value) continue;
protectedRequestHeaders ??= { ...attachedRequest.headers };
protectedRequestHeaders[name] = mintSecretSentinel(value, { label: `model-auth:${model.provider}` });
}
if (protectedRequestHeaders && attachedRequest) protectedRequest = {
...attachedRequest,
headers: protectedRequestHeaders
};
if (sourceAuth?.mode === "authorization-bearer" && runtimeAuth?.mode === "authorization-bearer" && isManagedSecret(sourceAuth.token)) {
const token = normalizeOptionalSecretInput(runtimeAuth.token)?.trim();
if (token) {
if (attachedRequest?.auth?.mode === "authorization-bearer") protectedRequest = {
...protectedRequest,
auth: {
...attachedRequest.auth,
token: mintSecretSentinel(token, { label: `model-auth:${model.provider}` })
}
};
addReplacement("Authorization", `Bearer ${token}`, `Bearer ${mintSecretSentinel(token, { label: `model-auth:${model.provider}` })}`);
}
} else if (sourceAuth?.mode === "header" && runtimeAuth?.mode === "header" && isManagedSecret(sourceAuth.value)) {
const value = normalizeOptionalSecretInput(runtimeAuth.value)?.trim();
const headerName = runtimeAuth.headerName.trim();
const prefix = runtimeAuth.prefix?.trim() ?? "";
if (headerName && value) {
if (attachedRequest?.auth?.mode === "header") protectedRequest = {
...protectedRequest,
auth: {
...attachedRequest.auth,
value: mintSecretSentinel(value, { label: `model-auth:${model.provider}` })
}
};
addReplacement(headerName, `${prefix}${value}`, `${prefix}${mintSecretSentinel(value, { label: `model-auth:${model.provider}` })}`);
}
}
let headers;
for (const [name, value] of Object.entries(model.headers)) {
const replacement = replacements.get(name.trim().toLowerCase());
if (replacement?.value !== value) continue;
headers ??= { ...model.headers };
headers[name] = replacement.replacement;
}
const protectedModel = headers ? {
...model,
headers
} : model;
return protectedRequest && protectedRequest !== attachedRequest ? attachModelProviderRequestTransport(protectedModel, protectedRequest) : protectedModel;
}
/**
* When the provider config sets `authHeader: true`, inject an explicit
* `Authorization: Bearer <apiKey>` header into the model so downstream SDKs
* (e.g. `@google/genai`) send credentials via the standard HTTP Authorization
* header instead of vendor-specific headers like `x-goog-api-key`.
*
* This is a no-op when `authHeader` is not `true`, when no API key is
* available, or when the API key is a synthetic marker (e.g. local-server
* placeholders) rather than a real credential.
*/
function applyAuthHeaderOverride(model, auth, cfg) {
const sentinelModel = applySecretRefHeaderSentinels(model, cfg);
if (!auth?.apiKey) return sentinelModel;
if (isNonSecretApiKeyMarker(auth.apiKey)) return sentinelModel;
if (!resolveProviderConfig(cfg, sentinelModel.provider)?.authHeader) return sentinelModel;
const headers = {};
if (sentinelModel.headers) {
for (const [key, value] of Object.entries(sentinelModel.headers)) if (normalizeOptionalLowercaseString(key) !== "authorization") headers[key] = value;
}
headers.Authorization = `Bearer ${auth.apiKey}`;
return {
...sentinelModel,
headers
};
}
//#endregion
export { hasAvailableAuthForProvider as a, getApiKeyForModelCore as i, applyLocalNoAuthHeaderOverride as n, resolveModelAuthMode as o, applySecretRefHeaderSentinels as r, applyAuthHeaderOverride as t };