UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

239 lines (238 loc) 10.7 kB
import "./src-vebZIeLe.js"; import { t as expectDefined } from "./expect-CyE8FADM.js"; import { D as tryResolveLegacyCompatibilityAgentId, c as resolveAgentConfig } from "./agent-scope-config-DcbEhP0R.js"; import "./legacy.default-agent-owner-BGwEdQRe.js"; import { a as resolveAgentModelPrimaryValue } from "./model-input-BuGMCNOz.js"; import { t as resolveDefaultModelForAgent } from "./model-selection-config-BrdmmqKD.js"; import { i as tryLoadActivatedBundledPluginPublicSurfaceModule } from "./facade-runtime-BjmVS_9G.js"; import { n as readCodexCliCredentialsCached, r as readGeminiCliCredentialsCached } from "./cli-credentials-CHuqM5pH.js"; import "./model-selection-di2kjKCB.js"; import { n as probeLocalCommand } from "./probes-CspEEOIq.js"; import { i as GEMINI_CLI_DEFAULT_MODEL_REF, n as CLAUDE_CLI_DEFAULT_MODEL_REF, o as detectAmbientInferenceBackends, r as CODEX_APP_SERVER_DEFAULT_MODEL_REF } from "./onboard-inference-ambient-BvxNJ30C.js"; import path from "node:path"; import os from "node:os"; import { randomInt } from "node:crypto"; //#region src/commands/onboard-inference.ts function detectCliCredentialState(params) { if (!params.probe.found) return; if (params.hasStoredCredentials) return true; return params.platform === "darwin" ? void 0 : false; } const CLI_AUTH_KIND_LABEL = { "api-key": "API key (usage-billed)", "chatgpt-subscription": "ChatGPT account", "claude-subscription": "Claude account", token: "OAuth token" }; function describeCliDetail(state, loginHint) { if (state.authKind) { const identity = state.authKind === "chatgpt-subscription" || state.authKind === "claude-subscription" ? ` · ${state.email || "email unavailable"}` : ""; return `logged in · ${CLI_AUTH_KIND_LABEL[state.authKind]}${identity}`; } if (state.credentials === true) return "logged in · authentication method unavailable"; if (state.credentials === false) return `installed, not logged in — ${loginHint}, then check again`; return "installed"; } function describeGeminiCliDetail(credentials) { return credentials === true ? "installed; credentials found" : "installed; login status unavailable"; } async function classifyCodexLoginStatus(probe, command) { const status = await probe(command, ["login", "status"], { timeoutMs: 3e3 }); if (status.error) return { credentials: void 0 }; if (status.version === "Logged in using ChatGPT") return { credentials: true, authKind: "chatgpt-subscription" }; if (/^Logged in using an API key - .+$/u.test(status.version ?? "")) return { credentials: true, authKind: "api-key" }; return { credentials: true }; } async function detectClaudeLoginState(_probe, command, env = process.env) { try { const status = await (await tryLoadActivatedBundledPluginPublicSurfaceModule({ dirName: "anthropic", artifactBasename: "cli-auth-api.js", env }))?.probeClaudeCliAuthStatus({ command, env }); if (status?.status !== "available") return { credentials: status?.status === "missing" ? false : void 0 }; switch (status.authMethod) { case "claude.ai": return { credentials: true, authKind: "claude-subscription", email: status.email }; case "api_key": case "api_key_helper": return { credentials: true, authKind: "api-key" }; case "oauth_token": return { credentials: true, authKind: "token" }; default: return { credentials: true }; } } catch { return { credentials: void 0 }; } } async function readCodexNativeLoginState(command, env) { try { const account = await (await tryLoadActivatedBundledPluginPublicSurfaceModule({ dirName: "openai", artifactBasename: "cli-auth-api.js", env }))?.readCodexCliAccount({ command, env }); switch (account?.type) { case "apiKey": return { credentials: true, authKind: "api-key" }; case "chatgpt": return { credentials: true, authKind: "chatgpt-subscription", email: account.email }; case "none": return { credentials: account.requiresOpenaiAuth ? false : void 0 }; case "unknown": return { credentials: void 0 }; case void 0: return; } } catch {} } function randomizeClaudeCodexTie(candidates, pickRandomInt) { const claudeIndex = candidates.findIndex((candidate) => candidate.kind === "claude-cli" && candidate.credentials !== false); const codexIndex = candidates.findIndex((candidate) => candidate.kind === "codex-cli" && candidate.credentials !== false); if (claudeIndex === -1 || codexIndex === -1 || pickRandomInt(2) === 0) return; const claudeCandidate = candidates[claudeIndex]; const codexCandidate = candidates[codexIndex]; candidates[claudeIndex] = expectDefined(codexCandidate, "Codex onboarding candidate"); candidates[codexIndex] = expectDefined(claudeCandidate, "Claude onboarding candidate"); } const CODEX_MACOS_APP_NAMES = [ "ChatGPT.app", "Codex.app", "Codex Beta.app" ]; const CODEX_MACOS_APP_PROBE_TIMEOUT_MS = 3e3; async function probeCodexCommand(params) { const pathProbe = await params.probe("codex"); if (pathProbe.found || params.platform !== "darwin") return pathProbe; const home = params.env.HOME?.trim() || os.homedir(); const appExecutables = new Set(CODEX_MACOS_APP_NAMES.flatMap((appName) => [path.join("/Applications", appName, "Contents", "Resources", "codex"), path.join(home, "Applications", appName, "Contents", "Resources", "codex")])); for (const executable of appExecutables) { const appProbe = await params.probe(executable, ["--version"], { timeoutMs: CODEX_MACOS_APP_PROBE_TIMEOUT_MS }); if (appProbe.found) return appProbe; } return pathProbe; } /** Detects a native Codex App Server without coupling it to inference selection. */ async function detectNativeCodexAppServer(options = {}) { return await probeCodexCommand({ probe: options.probeLocalCommand ?? probeLocalCommand, env: options.env ?? process.env, platform: options.platform ?? process.platform }); } /** * Detect usable inference backends in ladder order. Returns candidates only * for backends that exist on this machine; the first entry is the bootstrap * default. Backends that are definitively logged out sink below logged-in and * unknown ones so a stale install never outranks a working login. */ async function detectInferenceBackends(options = {}) { const env = options.env ?? process.env; const platform = options.platform ?? process.platform; const probe = options.deps?.probeLocalCommand ?? probeLocalCommand; const readCodex = options.deps?.readCodexCliCredentials ?? (() => readCodexCliCredentialsCached({ allowKeychainPrompt: false, ttlMs: 6e4 })); const readGemini = options.deps?.readGeminiCliCredentials ?? (() => readGeminiCliCredentialsCached({ ttlMs: 6e4 })); const candidates = []; const defaultAgentId = options.config ? options.agentId?.trim() || tryResolveLegacyCompatibilityAgentId(options.config) : void 0; const defaultAgentModel = options.config && defaultAgentId ? resolveAgentConfig(options.config, defaultAgentId)?.model : void 0; if (resolveAgentModelPrimaryValue(defaultAgentModel) ?? resolveAgentModelPrimaryValue(options.config?.agents?.defaults?.model)) { const resolved = resolveDefaultModelForAgent({ cfg: options.config ?? {}, ...defaultAgentId ? { agentId: defaultAgentId } : {} }); const modelRef = `${resolved.provider}/${resolved.model}`; candidates.push({ kind: "existing-model", modelRef, label: "Current model", detail: `${modelRef} — already configured`, credentials: true }); } const envCandidates = detectAmbientInferenceBackends(env).filter((candidate) => candidate.kind === "openai-api-key" || candidate.kind === "anthropic-api-key"); const [claudeProbe, codexProbe, geminiProbe] = await Promise.all([ probe("claude"), detectNativeCodexAppServer({ probeLocalCommand: probe, env, platform }), probe("gemini") ]); const cliCandidates = []; const subscriptionPromotionEligibleCliKinds = /* @__PURE__ */ new Set(); if (claudeProbe.found && !claudeProbe.timedOut) { const loginState = options.deps?.detectClaudeLoginState ? await options.deps.detectClaudeLoginState(probe, claudeProbe.command) : await detectClaudeLoginState(probe, claudeProbe.command, env); const credentials = loginState.credentials; if (credentials === true && loginState.authKind === "claude-subscription") subscriptionPromotionEligibleCliKinds.add("claude-cli"); const detail = describeCliDetail(loginState, "run `claude auth login`"); cliCandidates.push({ kind: "claude-cli", modelRef: CLAUDE_CLI_DEFAULT_MODEL_REF, label: "Claude Code", detail, ...credentials === void 0 ? {} : { credentials } }); } if (codexProbe.found && !codexProbe.timedOut) { const codexCredential = readCodex(); const loginState = options.deps?.detectCodexLoginState ? { credentials: await options.deps.detectCodexLoginState(probe, codexProbe.command) } : await readCodexNativeLoginState(codexProbe.command, env) ?? (options.deps?.readCodexCliCredentials ? { credentials: detectCliCredentialState({ probe: codexProbe, hasStoredCredentials: codexCredential !== null, platform }), ...codexCredential?.type === "oauth" ? { authKind: "chatgpt-subscription" } : {} } : await classifyCodexLoginStatus(probe, codexProbe.command)); const credentials = loginState.credentials; if (credentials === true && loginState.authKind === "chatgpt-subscription" && codexCredential?.type === "oauth") subscriptionPromotionEligibleCliKinds.add("codex-cli"); cliCandidates.push({ kind: "codex-cli", modelRef: CODEX_APP_SERVER_DEFAULT_MODEL_REF, label: "Codex", detail: describeCliDetail(loginState, "run `codex login`"), ...credentials === void 0 ? {} : { credentials } }); } if (geminiProbe.found && !geminiProbe.timedOut) { const credentials = readGemini() !== null ? true : void 0; cliCandidates.push({ kind: "gemini-cli", modelRef: GEMINI_CLI_DEFAULT_MODEL_REF, label: "Gemini CLI", detail: describeGeminiCliDetail(credentials), ...credentials === void 0 ? {} : { credentials } }); } randomizeClaudeCodexTie(cliCandidates, options.deps?.randomInt ?? randomInt); const loggedInSubscriptionCliCandidates = cliCandidates.filter((candidate) => candidate.credentials === true && subscriptionPromotionEligibleCliKinds.has(candidate.kind)); const remainingCliCandidates = cliCandidates.filter((candidate) => !loggedInSubscriptionCliCandidates.includes(candidate)); candidates.push(...loggedInSubscriptionCliCandidates, ...envCandidates, ...remainingCliCandidates.filter((candidate) => candidate.credentials !== false), ...remainingCliCandidates.filter((candidate) => candidate.credentials === false)); return candidates; } //#endregion export { detectInferenceBackends as t };