UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

402 lines (401 loc) 18.4 kB
import { n as findNormalizedProviderValue, r as normalizeProviderId } from "./provider-id-DMd-TDFp.js"; import { n as resolveProviderIdForAuth } from "./provider-auth-aliases-DhA9c2am.js"; import { A as isSafeToCopyOAuthIdentity, C as isSafeToAdoptBootstrapOAuthIdentity, E as shouldBootstrapFromExternalCliCredential, F as MINIMAX_CLI_PROFILE_ID, P as EXTERNAL_CLI_SYNC_TTL_MS, R as OPENAI_CODEX_DEFAULT_PROFILE_ID, T as overlayRuntimeExternalOAuthProfiles, b as setRuntimeExternalCliProfileIds, g as getRuntimeExternalCliProfileIds, k as cloneAuthProfileStore, x as areOAuthCredentialsEquivalent, y as removeRuntimeExternalProfileReferences, z as authProfilesLog } from "./persisted-B_qhhBlh.js"; import { r as hasUsableOAuthCredential } from "./credential-state-N1MIGw99.js"; import { i as readMiniMaxCliCredentialsCached, n as readCodexCliCredentialsCached } from "./cli-credentials-CHuqM5pH.js"; import { t as resolveExternalAuthProfilesWithPlugins } from "./provider-external-auth-C3Dsqtle.js"; //#region src/agents/auth-profiles/external-cli-sync.ts /** * External CLI OAuth synchronization. * Reads supported CLI credential stores, decides whether those credentials can * safely bootstrap local auth profiles, and returns runtime/persisted overlays. */ /** Return true when imported CLI credentials match an existing profile identity. */ function isSafeToUseExternalCliCredential(existing, imported) { if (!existing) return true; if (existing.provider !== imported.provider) return false; return isSafeToCopyOAuthIdentity(existing, imported); } const EXTERNAL_CLI_SYNC_PROVIDERS = [{ profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID, profileAliases: ["openai:default"], provider: "openai", aliases: [ "openai", "codex", "codex-cli", "codex-app-server" ], readCredentials: (options) => readCodexCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS, allowKeychainPrompt: options?.allowKeychainPrompt }), bootstrapOnly: true }, { profileId: MINIMAX_CLI_PROFILE_ID, provider: "minimax-portal", aliases: ["minimax", "minimax-cli"], readCredentials: () => readMiniMaxCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }) }]; function resolveExternalCliSyncProvider(params) { const provider = EXTERNAL_CLI_SYNC_PROVIDERS.find((entry) => externalCliProfileIdMatches(entry, params.profileId)); if (!provider) return null; if (params.credential && !listExternalCliProviderIds(provider).includes(params.credential.provider)) return null; return provider; } function listExternalCliProfileIds(providerConfig) { return [providerConfig.profileId, ...providerConfig.profileAliases ?? []]; } function listExternalCliProviderIds(providerConfig) { return [providerConfig.provider, ...providerConfig.aliases ?? []]; } /** Provider ids whose external CLI credentials can be refreshed by this owner. */ function listExternalCliSyncProviderIds() { return [...new Set(EXTERNAL_CLI_SYNC_PROVIDERS.flatMap(listExternalCliProviderIds))]; } function normalizeExternalCliCredentialProvider(credential, provider) { return credential ? { ...credential, provider } : null; } function getAuthProfileProviderPrefix(profileId) { return profileId.split(":", 1)[0]?.trim() ?? ""; } function externalCliProfileIdMatches(providerConfig, profileId, options) { if (listExternalCliProfileIds(providerConfig).includes(profileId)) return true; if (!options?.allowLegacyNamespace || providerConfig.profileId !== "openai:default") return false; return normalizeProviderId(getAuthProfileProviderPrefix(profileId)) === "openai"; } function hasInlineOAuthTokenMaterial(credential) { return [ credential.access, credential.refresh, credential.idToken ].some((value) => typeof value === "string" && value.trim().length > 0); } function hasManagedProviderOAuth(store, providerConfig) { return Object.values(store.profiles).some((credential) => credential?.type === "oauth" && listExternalCliProviderIds(providerConfig).includes(credential.provider) && hasInlineOAuthTokenMaterial(credential)); } /** Read a CLI credential only for safe bootstrap of an unusable local profile. */ function readExternalCliBootstrapCredential(params) { const provider = resolveExternalCliSyncProvider(params); if (!provider) return null; if (provider.bootstrapOnly && hasManagedProviderOAuth(params.store, provider)) return null; if (provider.bootstrapOnly && !params.allowInlineOAuthTokenMaterial && hasInlineOAuthTokenMaterial(params.credential)) return null; return normalizeExternalCliCredentialProvider(provider.readCredentials({ allowKeychainPrompt: params.allowKeychainPrompt }), params.credential.provider); } function normalizeProviderScope(values) { if (values === void 0) return; const out = /* @__PURE__ */ new Set(); for (const value of values) { const raw = value.trim(); if (!raw) continue; out.add(raw.toLowerCase()); const normalized = normalizeProviderId(raw); if (normalized) out.add(normalized); } return out; } function isExternalCliProviderInScope(params) { const { providerConfig, options, store } = params; const providerScope = normalizeProviderScope(options?.providerIds); if (providerScope === void 0 && options?.profileIds === void 0) return Object.entries(store.profiles).some(([profileId, existing]) => { return externalCliProfileIdMatches(providerConfig, profileId) && existing?.type === "oauth" && listExternalCliProviderIds(providerConfig).includes(existing.provider); }); if (Array.from(options?.profileIds ?? []).some((profileId) => externalCliProfileIdMatches(providerConfig, profileId.trim(), { allowLegacyNamespace: true }))) return true; if (!providerScope || providerScope.size === 0) return false; return listExternalCliProviderIds(providerConfig).some((alias) => { const raw = alias.trim().toLowerCase(); const normalized = normalizeProviderId(alias); return providerScope.has(raw) || (normalized ? providerScope.has(normalized) : false); }); } /** True when a previously resolved built-in CLI profile belongs to this refresh scope. */ function isExternalCliAuthProfileInScope(params) { const credential = params.store.profiles[params.profileId]; const providerConfig = resolveExternalCliSyncProvider({ profileId: params.profileId, ...credential?.type === "oauth" ? { credential } : {} }); return providerConfig ? isExternalCliProviderInScope({ providerConfig, store: params.store, options: { ...params.providerIds ? { providerIds: params.providerIds } : {}, ...params.profileIds ? { profileIds: params.profileIds } : {} } }) : false; } function listScopedExternalCliProfileIds(params) { const { options, providerConfig, store } = params; if (providerConfig.bootstrapOnly && hasManagedProviderOAuth(store, providerConfig)) return []; const matchingRequestedProfileIds = Array.from(options?.profileIds ?? []).map((value) => value.trim()).filter((value) => value.length > 0).filter((profileId) => externalCliProfileIdMatches(providerConfig, profileId, { allowLegacyNamespace: true })); if (matchingRequestedProfileIds.length > 0) return matchingRequestedProfileIds; const existingProfileIds = Object.keys(store.profiles).filter((profileId) => externalCliProfileIdMatches(providerConfig, profileId)); if (existingProfileIds.length > 0) return existingProfileIds; return options?.providerIds ? [providerConfig.profileId] : []; } function backfillExternalCliIdentity(params) { if (params.existingOAuth.email) return null; const creds = params.providerConfig.readCredentials({ allowKeychainPrompt: params.allowKeychainPrompt }); return creds?.email && (creds.refresh === params.existingOAuth.refresh || creds.access === params.existingOAuth.access) ? { ...params.existingOAuth, email: creds.email } : null; } /** Resolve scoped external CLI auth profiles available to overlay or persist. */ function resolveExternalCliAuthProfiles(store, options) { const profiles = []; const now = Date.now(); for (const providerConfig of EXTERNAL_CLI_SYNC_PROVIDERS) { if (!isExternalCliProviderInScope({ providerConfig, store, options })) continue; const scopedProfileIds = listScopedExternalCliProfileIds({ providerConfig, store, options }); for (const profileId of scopedProfileIds) { const existing = store.profiles[profileId]; const existingOAuth = existing?.type === "oauth" && listExternalCliProviderIds(providerConfig).includes(existing.provider) ? existing : void 0; if (existing && !existingOAuth) { authProfilesLog.debug("kept explicit local auth over external cli bootstrap", { profileId, provider: providerConfig.provider, localType: existing.type, localProvider: existing.provider }); continue; } if (providerConfig.bootstrapOnly && existingOAuth && hasInlineOAuthTokenMaterial(existingOAuth)) { authProfilesLog.debug("kept local oauth over external cli bootstrap-only provider", { profileId, provider: providerConfig.provider }); continue; } if (existingOAuth && !providerConfig.bootstrapOnly && hasUsableOAuthCredential(existingOAuth, { now })) { const backfilled = backfillExternalCliIdentity({ providerConfig, existingOAuth, allowKeychainPrompt: options?.allowKeychainPrompt }); if (backfilled) profiles.push({ profileId, credential: backfilled, persistence: providerConfig.persistence ?? "persisted" }); continue; } const creds = normalizeExternalCliCredentialProvider(providerConfig.readCredentials({ allowKeychainPrompt: options?.allowKeychainPrompt }), existingOAuth?.provider ?? providerConfig.provider); if (!creds) continue; if (existingOAuth && !isSafeToUseExternalCliCredential(existingOAuth, creds)) { authProfilesLog.warn("refused external cli oauth bootstrap: identity mismatch", { profileId, provider: providerConfig.provider }); continue; } if (existingOAuth && !isSafeToAdoptBootstrapOAuthIdentity(existingOAuth, creds) && !areOAuthCredentialsEquivalent(existingOAuth, creds)) { authProfilesLog.warn("refused external cli oauth bootstrap: identity mismatch or missing binding", { profileId, provider: providerConfig.provider }); continue; } if (!shouldBootstrapFromExternalCliCredential({ existing: existingOAuth, imported: creds, now })) { if (existingOAuth) authProfilesLog.debug("kept usable local oauth over external cli bootstrap", { profileId, provider: providerConfig.provider, localExpires: existingOAuth.expires, externalExpires: creds.expires }); continue; } authProfilesLog.debug("used external cli oauth bootstrap because local oauth was missing or unusable", { profileId, provider: providerConfig.provider, localExpires: existingOAuth?.expires, externalExpires: creds.expires }); profiles.push({ profileId, credential: creds, persistence: providerConfig.persistence ?? (providerConfig.bootstrapOnly ? "runtime-only" : "persisted") }); } } return profiles; } //#endregion //#region src/agents/auth-profiles/ambient-auth.ts /** Provider auth-pin policy for credentials discovered outside OpenClaw storage. */ /** Returns whether ambient credential material agrees with a provider's declared auth mode. */ function isAmbientCredentialAllowedByProviderAuthPin(params) { const providers = params.config?.models?.providers; const direct = findNormalizedProviderValue(providers, params.provider); const providerAuthKey = resolveProviderIdForAuth(params.provider, { config: params.config, ...params.authAliasLookupParams }); const auth = direct?.auth ?? findNormalizedProviderValue(providers, providerAuthKey)?.auth; if (auth === "api-key") return params.type === "api_key"; if (auth === "oauth") return params.type === "oauth" || params.type === "token"; if (auth === "token") return params.type === "token"; return auth === void 0; } //#endregion //#region src/agents/auth-profiles/external-auth.ts let resolveExternalAuthProfilesForRuntime; /** Test-only resolver injection for provider external auth profiles. */ const testing = { resetResolveExternalAuthProfilesForTest() { resolveExternalAuthProfilesForRuntime = void 0; }, setResolveExternalAuthProfilesForTest(resolver) { resolveExternalAuthProfilesForRuntime = resolver; } }; if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.externalAuthTestApi")] = testing; function normalizeExternalAuthProfile(profile) { if (!profile?.profileId || !profile.credential) return null; return { ...profile, persistence: profile.persistence ?? "runtime-only" }; } function resolveExplicitProfileIds(values) { if (values === void 0) return; return new Set(Array.from(values, (value) => value.trim()).filter((value) => value.length > 0)); } function isExternalAuthProfileAllowed(profile, store, config, explicitProfileIds, env) { if (store.profiles[profile.profileId] || explicitProfileIds?.has(profile.profileId)) return true; return isAmbientCredentialAllowedByProviderAuthPin({ config, authAliasLookupParams: { env }, provider: profile.credential.provider, type: profile.credential.type }); } function resolveExternalAuthProfiles(params) { const env = params.env ?? process.env; const profiles = (resolveExternalAuthProfilesForRuntime ?? resolveExternalAuthProfilesWithPlugins)({ env, config: params.externalCli?.config, context: { config: params.externalCli?.config, agentDir: params.agentDir, workspaceDir: void 0, env, store: params.store } }); const externalCli = params.externalCli; const resolved = resolveExternalCliAuthProfileMap({ ...params, externalCli }); const runtimeExternalCliProfileIds = new Set([...resolved.values()].filter((profile) => profile.persistence !== "persisted").map((profile) => profile.profileId)); const pluginProfileIds = /* @__PURE__ */ new Set(); const explicitProfileIds = resolveExplicitProfileIds(params.externalCli?.externalCliProfileIds); for (const rawProfile of profiles) { const profile = normalizeExternalAuthProfile(rawProfile); if (!profile) continue; if (!isExternalAuthProfileAllowed(profile, params.store, params.externalCli?.config, explicitProfileIds, env)) continue; resolved.set(profile.profileId, profile); pluginProfileIds.add(profile.profileId); runtimeExternalCliProfileIds.delete(profile.profileId); } return { profiles: resolved, pluginProfileIds, runtimeExternalCliProfileIds }; } function resolveAllowedExternalCliAuthProfiles(params) { const env = params.env ?? process.env; const explicitProfileIds = resolveExplicitProfileIds(params.externalCli?.externalCliProfileIds); return (resolveExternalCliAuthProfiles?.(params.store, { allowKeychainPrompt: params.externalCli?.allowKeychainPrompt, providerIds: params.externalCli?.externalCliProviderIds, profileIds: explicitProfileIds }) ?? []).flatMap((profile) => isExternalAuthProfileAllowed(profile, params.store, params.externalCli?.config, explicitProfileIds, env) ? [{ profileId: profile.profileId, credential: profile.credential, persistence: profile.persistence ?? "runtime-only" }] : []); } function resolveExternalCliAuthProfileMap(params) { return new Map(resolveAllowedExternalCliAuthProfiles(params).map((profile) => [profile.profileId, profile])); } /** List runtime-only and persisted external auth profiles for this store. */ function listRuntimeExternalAuthProfiles(params) { return Array.from(resolveExternalAuthProfiles({ store: params.store, agentDir: params.agentDir, env: params.env, externalCli: params.externalCli }).profiles.values()); } function hasPersistableExternalCliSyncCandidate(store, params) { if (params?.externalCliProviderIds || params?.externalCliProfileIds) return true; for (const profileId of [MINIMAX_CLI_PROFILE_ID]) if (store.profiles[profileId]?.type === "oauth") return true; return false; } function hasScopedExternalCliOverlay(params) { return Boolean(params?.externalCliProviderIds || params?.externalCliProfileIds); } /** Overlay external auth profiles onto a cloned auth store for runtime use. */ function overlayExternalAuthProfiles(store, params) { const scoped = hasScopedExternalCliOverlay(params); const runtimeExternalCliProfileIds = new Set(getRuntimeExternalCliProfileIds(store)); const refreshedProfileIds = new Set((store.runtimeExternalProfileIds ?? []).filter((profileId) => !runtimeExternalCliProfileIds.has(profileId))); for (const profileId of runtimeExternalCliProfileIds) if (scoped && isExternalCliAuthProfileInScope({ store, profileId, providerIds: params?.externalCliProviderIds, profileIds: params?.externalCliProfileIds })) refreshedProfileIds.add(profileId); const base = removeRuntimeExternalProfileReferences({ store, profileIds: refreshedProfileIds }); const resolved = resolveExternalAuthProfiles({ store: base, agentDir: params?.agentDir, env: params?.env, externalCli: params }); const next = overlayRuntimeExternalOAuthProfiles(base, resolved.profiles.values(), { runtimeExternalProfileIdsAuthoritative: !scoped }); const retainedCliProfileIds = getRuntimeExternalCliProfileIds(base).filter((profileId) => !resolved.pluginProfileIds.has(profileId)); setRuntimeExternalCliProfileIds(next, [...retainedCliProfileIds, ...resolved.runtimeExternalCliProfileIds]); return next; } /** Persist safe external CLI OAuth profiles that own their local profile slot. */ function syncPersistedExternalCliAuthProfiles(store, params) { if (!hasPersistableExternalCliSyncCandidate(store, params)) return store; const persistedProfiles = resolveAllowedExternalCliAuthProfiles({ store, env: params?.env, externalCli: params }).filter((profile) => profile.persistence === "persisted"); if (persistedProfiles.length === 0) return store; let next; for (const profile of persistedProfiles) { const existing = (next ?? store).profiles[profile.profileId]; if (existing?.type === "oauth" && areOAuthCredentialsEquivalent(existing, profile.credential)) continue; next ??= cloneAuthProfileStore(store); next.profiles[profile.profileId] = profile.credential; } return next ?? store; } //#endregion export { listExternalCliSyncProviderIds as a, isAmbientCredentialAllowedByProviderAuthPin as i, overlayExternalAuthProfiles as n, readExternalCliBootstrapCredential as o, syncPersistedExternalCliAuthProfiles as r, resolveExternalCliAuthProfiles as s, listRuntimeExternalAuthProfiles as t };