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