UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

290 lines (289 loc) 14 kB
import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { a as resolveAgentModelPrimaryValue } from "./model-input-BuGMCNOz.js"; import { a as resolveOpenAICodexImportProfileName, i as resolveOpenAICodexAuthIdentity, r as resolveOpenAICodexAccessTokenExpiry, t as buildOpenAICodexCredentialExtra } from "./provider-openai-chatgpt-auth-BoLGEnud.js"; import { n as resolveAuthStorePathForDisplay } from "./paths-DLuhw03H.js"; import { m as loadAuthProfileStoreWithoutExternalProfiles } from "./store-F1B2duCT.js"; import "./provider-auth-BeZ7NZUU.js"; import { n as updateAuthProfileStoreWithLockCompat } from "./provider-auth-write-compat-B_rokROR.js"; import { t as buildOauthProviderAuthResult } from "./provider-auth-result-BJQq6asp.js"; import "./string-coerce-runtime-GQa0ehRA.js"; import { n as applyAgentDefaultModelPrimary } from "./provider-onboard-D4DWEIGi.js"; import "./agent-runtime-B15RhSG0.js"; import { d as markMigrationItemSkipped, l as markMigrationItemConflict, o as createMigrationItem, u as markMigrationItemError } from "./migration-q6QzXKht.js"; import { n as hasAuthProfileConfigConflict, r as hasCurrentAuthProfileConfigConflict, t as applyAuthProfileConfigWithConflictCheck } from "./auth-config-BQ4l7Wu7.js"; import { c as readText } from "./helpers-DWNr52wp.js"; import { n as readHermesCodexAuthCandidates, t as buildReauthenticationItems } from "./auth-source-BelFffM_.js"; import { i as HERMES_REASON_CONFIG_RUNTIME_UNAVAILABLE, l as HERMES_REASON_SECRET_NO_LONGER_PRESENT, n as HERMES_REASON_AUTH_PROFILE_EXISTS, o as HERMES_REASON_INCLUDE_SECRETS, r as HERMES_REASON_AUTH_PROFILE_WRITE_FAILED, s as HERMES_REASON_MISSING_SECRET_METADATA } from "./items-BfpzP26U.js"; import { createHash } from "node:crypto"; //#region extensions/migrate-hermes/auth.ts const OPENAI_PROVIDER_ID = "openai"; const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-5.6-sol"; const HERMES_AUTH_DISPLAY_NAME = "Hermes import"; function authProfileTarget(agentDir, profileId) { return `${resolveAuthStorePathForDisplay(agentDir)}#${profileId}`; } function sourceCredentialFingerprint(candidate) { const hash = createHash("sha256"); for (const part of [ candidate.sourceKind, candidate.accountId ?? "", candidate.access, candidate.refresh ]) { hash.update(part); hash.update("\0"); } return hash.digest("hex"); } async function readOpenCodeOpenAICandidates(authPath) { const raw = await readText(authPath); if (!raw || !authPath) return []; let parsed; try { parsed = JSON.parse(raw); } catch { return []; } if (!isRecord(parsed)) return []; const openai = isRecord(parsed.openai) ? parsed.openai : void 0; const access = normalizeOptionalString(openai?.access); const accountId = normalizeOptionalString(openai?.accountId); const refresh = normalizeOptionalString(openai?.refresh); if (!access || !refresh) return []; return [{ access, ...accountId ? { accountId } : {}, refresh, sourceKind: "opencode-auth-json", sourceSlot: "opencode", sourceCredentialIndex: 0, sourceLabel: "OpenCode OpenAI OAuth credential", sourcePath: authPath }]; } function buildAuthResult(candidate, fallbackProfileName = "hermes-import") { const identity = resolveOpenAICodexAuthIdentity({ access: candidate.access, accountId: candidate.accountId }); return buildOauthProviderAuthResult({ providerId: OPENAI_PROVIDER_ID, defaultModel: OPENAI_CODEX_DEFAULT_MODEL, access: candidate.access, refresh: candidate.refresh, expires: resolveOpenAICodexAccessTokenExpiry(candidate.access), email: identity.email, profileName: resolveOpenAICodexImportProfileName(identity, fallbackProfileName), displayName: HERMES_AUTH_DISPLAY_NAME, credentialExtra: buildOpenAICodexCredentialExtra(identity) }); } function readProviderAuthModelConfigs(result) { const models = result.configPatch?.agents?.defaults?.models; if (isRecord(models)) return { ...models }; return { [normalizeOptionalString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL]: {} }; } function mergeModelConfigEntry(existing, patch) { if (existing && isRecord(existing) && isRecord(patch)) return { ...existing, ...patch }; return existing ?? patch; } function applyOAuthModelConfigsToConfig(cfg, result) { const patchModels = readProviderAuthModelConfigs(result); const existingModels = cfg.agents?.defaults?.models ?? {}; const models = result.replaceDefaultModels ? { ...patchModels } : { ...existingModels }; if (!result.replaceDefaultModels) for (const [modelRef, modelConfig] of Object.entries(patchModels)) models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig); return { ...cfg, agents: { ...cfg.agents, defaults: { ...cfg.agents?.defaults, models } } }; } function authProfileDedupeKey(profile) { if (profile.credential.accountId) return `${profile.credential.provider}:account:${profile.credential.accountId}`; if (profile.credential.email) return `${profile.credential.provider}:email:${profile.credential.email}`; return `${profile.credential.provider}:profile:${profile.sourceProfileId}`; } async function readCodexAuthProfilesFromSource(source) { const profileHermesCandidates = await readHermesCodexAuthCandidates(source.authPath); const globalHermesCandidates = await readHermesCodexAuthCandidates(source.globalAuthPath); const profileProvider = profileHermesCandidates.find((candidate) => candidate.sourceSlot === "provider"); const profilePool = profileHermesCandidates.filter((candidate) => candidate.sourceSlot === "pool"); const globalProvider = globalHermesCandidates.find((candidate) => candidate.sourceSlot === "provider"); const globalPool = globalHermesCandidates.filter((candidate) => candidate.sourceSlot === "pool"); const candidates = [ ...profileProvider ? [profileProvider] : globalProvider ? [globalProvider] : [], ...profilePool.length > 0 ? profilePool : globalPool, ...await readOpenCodeOpenAICandidates(source.opencodeAuthPath) ].toSorted((left, right) => (right.updatedAt ?? 0) - (left.updatedAt ?? 0)); const profiles = []; const seen = /* @__PURE__ */ new Set(); for (const [index, candidate] of candidates.entries()) { const result = buildAuthResult(candidate, candidates.length === 1 ? "hermes-import" : `hermes-import-${index + 1}`); const profile = result.profiles[0]; if (!profile || profile.credential.type !== "oauth") continue; const entry = { candidate, credential: profile.credential, result, sourceProfileId: profile.profileId }; const dedupeKey = authProfileDedupeKey(entry); if (seen.has(dedupeKey)) continue; seen.add(dedupeKey); profiles.push(entry); } return profiles; } async function readCodexAuthProfilesFromPath(params) { if (params.sourceKind === "opencode-auth-json") return await readCodexAuthProfilesFromSource({ root: "", archivePaths: [], ...params.sourcePath ? { opencodeAuthPath: params.sourcePath } : {} }); return await readCodexAuthProfilesFromSource({ root: "", archivePaths: [], ...params.sourcePath ? { authPath: params.sourcePath } : {} }); } function findMatchingProfile(store, credential) { for (const [profileId, existing] of Object.entries(store.profiles)) { if (existing.type !== "oauth" || existing.provider !== credential.provider) continue; if (credential.accountId && existing.accountId === credential.accountId) return profileId; if ((!credential.accountId || !existing.accountId) && credential.email && existing.email === credential.email) return profileId; } } function oauthAuthProfileConfig(profileId, credential) { return { profileId, provider: credential.provider, mode: "oauth", ...credential.email ? { email: credential.email } : {}, ...credential.displayName ? { displayName: credential.displayName } : {} }; } function matchesSourceCredentialFingerprint(profile, fingerprint) { return sourceCredentialFingerprint(profile.candidate) === fingerprint; } function findPlannedAuthProfile(params) { const bySourceProfileId = params.profiles.find((entry) => entry.sourceProfileId === params.sourceProfileId); const fingerprint = params.sourceCredentialFingerprint; if (!fingerprint) return bySourceProfileId; if (bySourceProfileId && matchesSourceCredentialFingerprint(bySourceProfileId, fingerprint)) return bySourceProfileId; const byIndex = params.sourceCredentialIndex === void 0 ? void 0 : params.profiles.find((entry) => entry.candidate.sourceCredentialIndex === params.sourceCredentialIndex); if (byIndex && matchesSourceCredentialFingerprint(byIndex, fingerprint)) return byIndex; return params.profiles.find((entry) => matchesSourceCredentialFingerprint(entry, fingerprint)); } async function buildAuthItems(params) { const items = []; items.push(...await buildReauthenticationItems(params.source)); const profiles = await readCodexAuthProfilesFromSource(params.source); if (profiles.length === 0) return items; const store = loadAuthProfileStoreWithoutExternalProfiles(params.targets.agentDir); items.push(...profiles.map((profile) => { const matchedProfileId = findMatchingProfile(store, profile.credential); const profileId = matchedProfileId ?? profile.sourceProfileId; const targetExists = Boolean(store.profiles[profileId]); const skipped = !params.ctx.includeSecrets; const configConflict = hasAuthProfileConfigConflict(params.ctx.config, oauthAuthProfileConfig(profileId, profile.credential), Boolean(params.ctx.overwrite)); const conflict = (targetExists && !matchedProfileId && !params.ctx.overwrite || configConflict) && !skipped; const itemId = profiles.length === 1 ? `auth:${OPENAI_PROVIDER_ID}` : `auth:${OPENAI_PROVIDER_ID}:${profile.sourceProfileId}`; return createMigrationItem({ id: itemId, kind: "auth", action: skipped ? "skip" : "create", source: profile.candidate.sourcePath, target: authProfileTarget(params.targets.agentDir, profileId), status: skipped ? "skipped" : conflict ? "conflict" : "planned", sensitive: true, reason: skipped ? HERMES_REASON_INCLUDE_SECRETS : conflict ? HERMES_REASON_AUTH_PROFILE_EXISTS : void 0, message: skipped ? `OpenAI OAuth credentials detected in ${profile.candidate.sourceKind === "hermes-auth-json" ? "Hermes" : "OpenCode"}.` : "Import OpenAI OAuth credentials and configure OpenAI models.", details: { provider: OPENAI_PROVIDER_ID, profileId, ...typeof profile.candidate.sourceCredentialIndex === "number" ? { sourceCredentialIndex: profile.candidate.sourceCredentialIndex } : {}, sourceCredentialFingerprint: sourceCredentialFingerprint(profile.candidate), sourceProfileId: profile.sourceProfileId, sourceKind: profile.candidate.sourceKind, sourceLabel: profile.candidate.sourceLabel } }); })); return items; } async function applyAuthItem(ctx, item, targets) { if (item.status !== "planned") return item; const source = item.source; const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : ""; const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : profileId; const sourceCredentialIndex = typeof item.details?.sourceCredentialIndex === "number" ? item.details.sourceCredentialIndex : void 0; const sourceCredentialFingerprintLocal = typeof item.details?.sourceCredentialFingerprint === "string" ? item.details.sourceCredentialFingerprint : void 0; if (!source || !profileId) return markMigrationItemError(item, HERMES_REASON_MISSING_SECRET_METADATA); const profile = findPlannedAuthProfile({ profiles: await readCodexAuthProfilesFromPath({ sourcePath: source, sourceKind: item.details?.sourceKind }), sourceProfileId, ...sourceCredentialIndex === void 0 ? {} : { sourceCredentialIndex }, ...sourceCredentialFingerprintLocal ? { sourceCredentialFingerprint: sourceCredentialFingerprintLocal } : {} }); if (!profile) return markMigrationItemSkipped(item, HERMES_REASON_SECRET_NO_LONGER_PRESENT); let conflicted = false; let wrote = false; const credential = { ...profile.credential, displayName: "displayName" in profile.credential && profile.credential.displayName ? profile.credential.displayName : HERMES_AUTH_DISPLAY_NAME }; const configProfile = oauthAuthProfileConfig(profileId, credential); if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return markMigrationItemConflict(item, HERMES_REASON_AUTH_PROFILE_EXISTS); const store = await updateAuthProfileStoreWithLockCompat({ agentDir: targets.agentDir, stateDir: ctx.stateDir, updater: (freshStore) => { const existing = freshStore.profiles[profileId]; if (!ctx.overwrite && existing) { if (findMatchingProfile(freshStore, credential) !== profileId) { conflicted = true; return false; } return false; } freshStore.profiles[profileId] = credential; wrote = true; return true; } }); if (conflicted) return markMigrationItemConflict(item, HERMES_REASON_AUTH_PROFILE_EXISTS); if (!store?.profiles[profileId]) return markMigrationItemError(item, HERMES_REASON_AUTH_PROFILE_WRITE_FAILED); const configResult = await applyAuthProfileConfigWithConflictCheck({ ctx, profile: configProfile, applyConfigPatch(config) { const next = applyOAuthModelConfigsToConfig(config, profile.result); return resolveAgentModelPrimaryValue(next.agents?.defaults?.model) === void 0 ? applyAgentDefaultModelPrimary(next, OPENAI_CODEX_DEFAULT_MODEL) : next; } }); if (configResult === "conflict") return markMigrationItemConflict(item, HERMES_REASON_AUTH_PROFILE_EXISTS); return { ...item, status: "migrated", message: configResult === "configured" ? item.message : `${item.message ?? "Imported auth profile."} ${HERMES_REASON_CONFIG_RUNTIME_UNAVAILABLE}.`, details: { ...item.details, wroteAuthProfile: wrote, configUpdated: configResult === "configured" } }; } //#endregion export { buildAuthItems as n, applyAuthItem as t };