UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

262 lines (261 loc) 13.1 kB
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js"; import "./utils-P__uGsPB.js"; import { g as resolveAmbientOwnerAgentId } from "./agent-scope-config-DcbEhP0R.js"; import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js"; import { a as parseProviderModelRef } from "./model-catalog-refs-BdjEHOKQ.js"; import { t as DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace-default-DPT1Dhad.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js"; import { a as normalizeOptionalAgentRuntimeId } from "./agent-runtime-id-BpfvnoN1.js"; import { o as resolveAgentEffectiveModelPrimary } from "./agent-scope-DbtJyKUL.js"; import { t as resolveModelRuntimePolicy } from "./model-runtime-policy-BAKiaBCi.js"; import { n as enablePluginInConfig, r as enablePluginWithCapabilityConsent } from "./enable-3OwjFbBV.js"; import { t as areRuntimeModelRefsEquivalent } from "./model-runtime-aliases-BJ2qM8I_.js"; import { r as resolveManifestProviderAuthChoices } from "./provider-auth-choices-DsRM415K.js"; import { t as listRecommendedToolInstalls } from "./recommended-tool-installs-Bwv75ix-.js"; import { a as supportsSetupTextInference, n as listSetupInferenceManualProviders, r as listSetupInferencePrepareOptions, t as listSetupInferenceAuthOptions } from "./setup-inference-auth-options-DDoD9isn.js"; import { t as resolveSetupInferenceCandidateBrandId } from "./setup-inference-brand-vm6tI0LG.js"; //#region src/system-agent/setup-inference-core.ts const setupInferenceLog = createSubsystemLogger("system-agent/setup-inference"); /** * Inference is the one required onboarding step (docs/cli/setup.md * "Setup bootstrap"). This module gives structured clients (macOS app) the * same ladder the conversation uses, with one hard guarantee: a candidate is * persisted as the default model only after a real completion round-trips. * A failing candidate must never leave config pointing at a broken model. */ const SETUP_INFERENCE_TEST_TIMEOUT_MS = 9e4; const SETUP_INFERENCE_TEST_PROMPT = "Reply with the single word OK. Do not use tools."; const PROVIDER_AUTO_SETUP_KIND_PREFIX = "provider-auto:"; const AUTO_LOCAL_MODEL_LEAN_ANNOUNCEMENT = "I enabled the lean tool surface for this local runtime."; /** * The config commit may have happened, so callers must verify current setup * instead of treating this like a definitive candidate failure and retrying. */ var SetupInferenceActivationIndeterminateError = class extends Error { constructor(..._args) { super(..._args); this.name = "SetupInferenceActivationIndeterminateError"; } }; var SetupInferenceActivationUnavailableError = class extends Error { constructor(..._args2) { super(..._args2); this.name = "SetupInferenceActivationUnavailableError"; } }; /** * The live-tested owner no longer matches current config. Activation maps this * to `{ ok: false, status: "auth" }` so the guided-onboarding ladder can move * to its next candidate instead of crashing the CLI. */ var SetupInferenceOwnerDriftError = class extends Error { constructor(..._args3) { super(..._args3); this.name = "SetupInferenceOwnerDriftError"; } }; var SetupInferenceCancelledError = class extends Error { constructor() { super("Provider login was cancelled."); } }; function throwIfSetupInferenceCancelled(params) { if (params.signal?.aborted || params.isCancelled?.()) throw new SetupInferenceCancelledError(); } async function waitForProviderAuth(promise, signal) { if (!signal) return await promise; if (signal.aborted) throw new SetupInferenceCancelledError(); let rejectAborted; const aborted = new Promise((_resolve, reject) => { rejectAborted = reject; }); const onAbort = () => rejectAborted?.(new SetupInferenceCancelledError()); signal.addEventListener("abort", onAbort, { once: true }); try { return await Promise.race([promise, aborted]); } finally { signal.removeEventListener("abort", onAbort); } } function toProviderAutoSetupKind(choiceId) { return `${PROVIDER_AUTO_SETUP_KIND_PREFIX}${encodeURIComponent(choiceId)}`; } function parseProviderAutoSetupChoiceId(kind) { if (!kind.startsWith(PROVIDER_AUTO_SETUP_KIND_PREFIX)) return; const encoded = kind.slice(14); if (!encoded) return; try { return decodeURIComponent(encoded) || void 0; } catch { return; } } function invalidSetupConfigError(snapshot) { const issue = snapshot.issues?.[0]; const detail = issue ? ` (${issue.path ? `${issue.path}: ` : ""}${issue.message})` : ""; return `OpenClaw config ${snapshot.path} is invalid${detail}. Fix it before running setup.`; } async function redactSetupInferenceError(message, ...apiKeys) { const secrets = new Set(apiKeys.flatMap((apiKey) => [apiKey, apiKey?.trim()]).filter((value) => Boolean(value))); let redacted = message; for (const secret of Array.from(secrets).toSorted((a, b) => b.length - a.length)) redacted = redacted.split(secret).join("[redacted]"); const { redactToolPayloadText } = await import("./redact-CCqT1fF7.js"); return redactToolPayloadText(redacted); } function resolveCandidatePresentation(candidate, authChoices) { const choice = authChoices.find((entry) => entry.choiceId === candidate.kind || entry.deprecatedChoiceIds?.includes(candidate.kind) === true); const brandId = resolveSetupInferenceCandidateBrandId(candidate, choice?.providerId); return { ...brandId ? { brandId } : {}, ...choice?.icon ? { icon: choice.icon } : {}, ...choice?.website ? { website: choice.website } : {} }; } function resolveSetupInferenceWorkspace(snapshot) { const config = snapshot.exists && snapshot.valid ? snapshot.sourceConfig ?? snapshot.config : void 0; return resolveUserPath(config?.agents?.defaults?.workspace?.trim() || DEFAULT_AGENT_WORKSPACE_DIR); } //#endregion //#region src/system-agent/setup-inference-detect.ts function resolveConfiguredCandidateKind(config, modelRef, agentId) { if (!modelRef) return; const ref = parseProviderModelRef(modelRef); if (!ref) return; const runtime = normalizeOptionalAgentRuntimeId(resolveModelRuntimePolicy({ config, provider: ref.provider, modelId: ref.model, agentId: resolveAmbientOwnerAgentId(config ?? {}, agentId) }).policy?.id); if (runtime === "codex") return "codex-cli"; if (runtime === "claude-cli") return "claude-cli"; } async function prepareSetupInferenceOptions(deps, agentId) { const { readConfigFileSnapshotWithPluginMetadata } = await import("./config/config.js"); const { snapshot, pluginMetadataSnapshot } = await readConfigFileSnapshotWithPluginMetadata(); if (snapshot.exists && !snapshot.valid) throw new Error(invalidSetupConfigError(snapshot)); const cfg = snapshot.runtimeConfig ?? snapshot.config; const targetAgentId = resolveAmbientOwnerAgentId(cfg, agentId); const workspace = resolveSetupInferenceWorkspace(snapshot); const authChoices = (deps.resolveManifestProviderAuthChoices ?? resolveManifestProviderAuthChoices)({ config: cfg, workspaceDir: workspace, metadataSnapshot: pluginMetadataSnapshot, includeUntrustedWorkspacePlugins: false, includeWorkspacePlugins: false }).filter((choice) => (deps.enablePluginInConfig ?? enablePluginInConfig)(cfg, choice.pluginId).enabled); return { cfg, targetAgentId, authChoices, manual: { manualProviders: listSetupInferenceManualProviders(authChoices), authOptions: listSetupInferenceAuthOptions(authChoices), prepareOptions: listSetupInferencePrepareOptions(authChoices), workspace, setupComplete: Boolean(resolveAgentEffectiveModelPrimary(cfg, targetAgentId)) } }; } /** Manual setup options use only config and manifests, never machine or credential probes. */ async function listManualSetupInferenceOptions(deps = {}, agentId) { return (await prepareSetupInferenceOptions(deps, agentId)).manual; } async function detectSetupInference(deps = {}, agentId) { const { cfg, targetAgentId, authChoices, manual } = await prepareSetupInferenceOptions(deps, agentId); const { workspace } = manual; const partial = { ...manual, candidates: [], unavailableCandidates: [], recommendedInstalls: listRecommendedToolInstalls() }; deps.onPartial?.(partial); const detected = await (deps.detectInferenceBackends ?? (await import("./onboard-inference-BqJRzeUU.js")).detectInferenceBackends)({ config: cfg, agentId: targetAgentId }); const unavailableCandidates = []; const probe = deps.probeLocalCommand ?? (await import("./probes-8VSd0PeN.js")).probeLocalCommand; const [pi, opencode] = await Promise.all([probe("pi"), probe("opencode")]); if (pi.found && !pi.timedOut) unavailableCandidates.push({ id: "pi-cli", label: "Pi CLI", detail: "installed", reason: "Pi CLI is installed, but its whole-agent sessions require separate setup and are not a reusable guided-setup inference route." }); if (opencode.found && !opencode.timedOut) unavailableCandidates.push({ id: "opencode-cli", label: "OpenCode CLI", detail: "installed", reason: "OpenCode CLI is installed, but its ACP harness requires separate setup and is not a reusable guided-setup inference route." }); const configuredModel = detected.find((candidate) => candidate.kind === "existing-model")?.modelRef; const configuredCandidateKind = resolveConfiguredCandidateKind(cfg, configuredModel, targetAgentId); const candidates = detected.filter((candidate) => candidate.kind !== "gemini-cli" && !(candidate.kind === configuredCandidateKind && configuredModel && areRuntimeModelRefsEquivalent(candidate.modelRef, configuredModel, { config: cfg }))).map((candidate) => Object.assign(candidate, { recommended: false }, resolveCandidatePresentation(candidate, authChoices))); const discoveryChoices = authChoices.filter((choice) => choice.appGuidedDiscovery === true && supportsSetupTextInference(choice.onboardingScopes)); if (discoveryChoices.length > 0) { const { withPluginLifecycleLease } = await import("./plugin-lifecycle-lease-CLKma701.js"); const discovery = await withPluginLifecycleLease({}, async () => { let discoveryConfig = cfg; const enabledChoices = []; for (const choice of discoveryChoices) { if (!(await enablePluginWithCapabilityConsent(cfg, choice.pluginId, { workspaceDir: workspace })).enabled) continue; discoveryConfig = (deps.enablePluginInConfig ?? enablePluginInConfig)(discoveryConfig, choice.pluginId).config; enabledChoices.push(choice); } const providers = enabledChoices.length ? (deps.resolvePluginProviders ?? (await import("./providers.runtime.js")).resolvePluginProvidersCore)({ config: discoveryConfig, workspaceDir: workspace, mode: "setup", includeUntrustedWorkspacePlugins: false, onlyPluginIds: [...new Set(enabledChoices.map((choice) => choice.pluginId))] }) : []; return { discoveryConfig, enabledChoices, providers }; }); const discovered = await Promise.all(discovery.enabledChoices.map(async (choice) => { const method = discovery.providers.find((candidate) => candidate.pluginId === choice.pluginId && normalizeProviderId(candidate.id) === normalizeProviderId(choice.providerId))?.auth.find((candidate) => candidate.id === choice.methodId); if (!method?.appGuidedSetup) return null; try { const candidate = await method.appGuidedSetup.detect({ config: discovery.discoveryConfig, env: process.env, workspaceDir: workspace }); if (!candidate) return null; const ref = parseProviderModelRef(candidate.modelRef); if (!ref || normalizeProviderId(ref.provider) !== normalizeProviderId(choice.providerId)) { setupInferenceLog.warn(`Ignoring invalid app-guided model ${candidate.modelRef} from ${choice.choiceId}.`); return null; } return Object.assign({ kind: toProviderAutoSetupKind(choice.choiceId), brandId: choice.providerId, label: choice.choiceLabel, detail: candidate.detail?.trim() || "available locally", modelRef: candidate.modelRef, recommended: false, credentials: true }, choice.icon ? { icon: choice.icon } : {}, choice.website ? { website: choice.website } : {}); } catch (error) { setupInferenceLog.debug(`App-guided discovery failed for ${choice.choiceId}: ${formatErrorMessage(error)}`); return null; } })); candidates.push(...discovered.filter((candidate) => candidate !== null)); } return { ...partial, candidates, unavailableCandidates, ...configuredModel ? { configuredModel } : {}, setupComplete: Boolean(configuredModel) }; } //#endregion export { SETUP_INFERENCE_TEST_TIMEOUT_MS as a, SetupInferenceCancelledError as c, parseProviderAutoSetupChoiceId as d, redactSetupInferenceError as f, waitForProviderAuth as g, throwIfSetupInferenceCancelled as h, SETUP_INFERENCE_TEST_PROMPT as i, SetupInferenceOwnerDriftError as l, setupInferenceLog as m, listManualSetupInferenceOptions as n, SetupInferenceActivationIndeterminateError as o, resolveSetupInferenceWorkspace as p, AUTO_LOCAL_MODEL_LEAN_ANNOUNCEMENT as r, SetupInferenceActivationUnavailableError as s, detectSetupInference as t, invalidSetupConfigError as u };