openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
402 lines (401 loc) • 18.4 kB
JavaScript
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 };