openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
681 lines (680 loc) • 29.8 kB
JavaScript
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js";
import { m as shortenHomePath } from "./utils-P__uGsPB.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { l as withConsoleSubsystemsSuppressed } from "./console-BSaonU6s.js";
import { n as formatConfigIssueLines } from "./issue-format-CQvxWNW8.js";
import { r as hasResolvedRosterBeforeMigrations } from "./agent-roster-provenance-TGrT9xta.js";
import { n as t } from "./i18n-hynzGFbD.js";
import { t as isUnconfiguredConfigSource } from "./fresh-install-config-DhP5LyQI.js";
import { t as CORE_AUTH_CHOICE_OPTIONS } from "./auth-choice-options.static-Dk9vjyDw.js";
import { a as requestTelemetryConsent, l as getSecurityNoteTitle, o as requireRiskAcknowledgement } from "./setup.shared-BnC_pCYr.js";
import { n as showSessionMigrationWarnings, t as promptFirstOnboardingAgent } from "./onboard-first-agent-BLGgn6O2.js";
import { t as enableDefaultOnboardingInternalHooks } from "./onboard-hooks-Ca9woCpp.js";
import { n as runInteractiveOnboarding, t as hasInteractiveOnboardingTty } from "./onboard-interactive-runner-7WVglo6Y.js";
import { randomUUID } from "node:crypto";
//#region src/commands/onboard-guided-consent.ts
async function persistRiskAcknowledgement(config) {
const securityAcknowledgedAt = config.wizard?.securityAcknowledgedAt;
if (!securityAcknowledgedAt) return;
const { mutateConfigFileWithRetry } = await import("./config/config.js");
return (await mutateConfigFileWithRetry({ mutate: (draft) => {
if (!draft.wizard?.securityAcknowledgedAt) draft.wizard = {
...draft.wizard,
securityAcknowledgedAt
};
if (config.telemetry?.consentedAt && !draft.telemetry?.consentedAt) draft.telemetry = config.telemetry;
} })).nextConfig.wizard?.securityAcknowledgedAt;
}
async function requestGuidedOnboardingConsent(params) {
const { opts, prompter, config: existingConfig, offerQuickstart } = params;
let quickstart = false;
if (offerQuickstart) {
if (!existingConfig.wizard?.securityAcknowledgedAt) await prompter.note(t("wizard.guided.laneSecurityLine"), getSecurityNoteTitle());
quickstart = await prompter.select({
message: t("wizard.guided.laneQuestion"),
options: [{
value: "quick",
label: t("wizard.guided.laneQuickLabel"),
hint: t("wizard.guided.laneQuickHint")
}, {
value: "custom",
label: t("wizard.guided.laneCustomLabel"),
hint: t("wizard.guided.laneCustomHint")
}],
initialValue: "quick"
}) === "quick";
}
let acknowledgedConfig = await requireRiskAcknowledgement({
opts: quickstart ? {
...opts,
acceptRisk: true
} : opts,
prompter,
config: existingConfig
});
if (!quickstart) acknowledgedConfig = await requestTelemetryConsent({
opts,
prompter,
config: acknowledgedConfig
});
let securityAcknowledgedAt = acknowledgedConfig.wizard?.securityAcknowledgedAt;
if (!existingConfig.wizard?.securityAcknowledgedAt || !existingConfig.telemetry?.consentedAt && acknowledgedConfig.telemetry?.consentedAt) {
const persistedAcknowledgement = await (params.persistRiskAcknowledgement ?? persistRiskAcknowledgement)(acknowledgedConfig);
if (persistedAcknowledgement) {
securityAcknowledgedAt = persistedAcknowledgement;
acknowledgedConfig = {
...acknowledgedConfig,
wizard: {
...acknowledgedConfig.wizard,
securityAcknowledgedAt
}
};
}
}
const onboardingSecurityAcknowledgedAt = securityAcknowledgedAt;
if (!onboardingSecurityAcknowledgedAt) throw new Error("Local onboarding requires its persisted security acknowledgement.");
return {
quickstart,
config: acknowledgedConfig,
securityAcknowledgedAt: onboardingSecurityAcknowledgedAt
};
}
//#endregion
//#region src/commands/onboard-guided-manual.ts
const SETUP_FAILURE_REASON_KEYS = {
auth: "wizard.guided.failureAuth",
rate_limit: "wizard.guided.failureRateLimit",
billing: "wizard.guided.failureBilling",
timeout: "wizard.guided.failureTimeout",
format: "wizard.guided.failureFormat",
unavailable: "wizard.guided.failureUnavailable",
unknown: "wizard.guided.failureUnknown"
};
function setupFailureReason(status) {
return t(SETUP_FAILURE_REASON_KEYS[status]);
}
function formatSetupCandidateFailure(failure) {
return t("wizard.guided.testFailure", {
label: failure.label,
reason: setupFailureReason(failure.result.status),
detail: failure.result.error
});
}
async function noteActivationFailure(params) {
await params.prompter.note(formatSetupCandidateFailure({
label: params.label,
result: params.result
}), t("wizard.guided.aiAccessTitle"));
}
async function tryCandidate(params) {
const progress = params.collectFailure ? params.prompter.progress(t("wizard.guided.testingCandidate", {
label: params.candidate.label,
modelRef: params.candidate.modelRef
})) : void 0;
let result;
try {
result = await withConsoleSubsystemsSuppressed(() => params.activate({
kind: params.candidate.kind,
modelRef: params.candidate.modelRef,
workspace: params.workspace,
surface: "cli",
runtime: params.runtime,
...params.collectFailure ? {} : { prompter: params.prompter }
}));
} finally {
progress?.stop(result?.ok ? t("wizard.guided.testPassed") : t("wizard.guided.testFailed"));
}
if (result.ok) return {
kind: "success",
result
};
if (params.collectFailure) params.collectFailure({
label: params.candidate.label,
result
});
else await noteActivationFailure({
prompter: params.prompter,
label: params.candidate.label,
result
});
return { kind: "failure" };
}
async function runManualStage(params) {
const interactiveOptions = [
...params.detection.authOptions,
...params.detection.prepareOptions ?? [],
...CORE_AUTH_CHOICE_OPTIONS.map((option) => ({
id: option.value,
label: option.label
}))
];
const allowedChoices = /* @__PURE__ */ new Set([...params.detection.manualProviders.map((provider) => provider.id), ...interactiveOptions.map((option) => option.id)]);
const detectedOptions = params.detection.candidates.map((candidate) => ({
value: `candidate:${candidate.kind}`,
label: t(params.autoAttemptedKinds.has(candidate.kind) ? "wizard.guided.retryCandidate" : "wizard.guided.tryCandidate", {
label: candidate.label,
detail: candidate.detail
})
}));
const additionalGroups = detectedOptions.length ? [{
value: "detected-ai",
label: t("wizard.guided.detectedGroupLabel"),
hint: params.detection.candidates.map((candidate) => candidate.label).join(", "),
methodMessage: t("wizard.guided.detectedGroupPrompt"),
options: detectedOptions
}] : [];
const [{ ensureAuthProfileStore }, { detectAvailableSetupProviderIds }, { promptAuthChoiceGrouped }] = await Promise.all([
import("./agents/auth-profiles.runtime.js"),
import("./provider-setup-availability-BaQOoNDv.js"),
import("./auth-choice-prompt-OYub_ykx.js")
]);
const store = ensureAuthProfileStore(void 0, { allowKeychainPrompt: false });
const detectedProviderIds = await detectAvailableSetupProviderIds({
config: params.config,
workspaceDir: params.workspace
});
while (true) {
const choice = await promptAuthChoiceGrouped({
prompter: params.prompter,
store,
includeSkip: true,
assistantVisibleOnly: false,
allowedChoices,
additionalGroups,
config: params.config,
workspaceDir: params.workspace,
detectedProviderIds
});
if (choice === "skip") {
if (params.hasActiveRoute) {
await params.prompter.note(t("wizard.guided.keepingCurrent"), t("wizard.guided.aiAccessTitle"));
return null;
}
await params.prompter.note(t("wizard.guided.nextStepsWithoutAi", { workspace: params.workspace }), t("wizard.guided.nextStepsTitle"));
return null;
}
if (choice.startsWith("candidate:")) {
const kind = choice.slice(10);
const candidate = params.detection.candidates.find((item) => item.kind === kind);
if (!candidate) continue;
const attempt = await tryCandidate({
candidate,
workspace: params.workspace,
runtime: params.runtime,
prompter: params.prompter,
activate: params.activate
});
if (attempt.kind === "success") return activationLines(attempt.result);
continue;
}
const providerAuthOption = interactiveOptions.find((item) => item.id === choice);
if (providerAuthOption) {
const result = await withConsoleSubsystemsSuppressed(() => params.activate({
kind: "provider-auth",
authChoice: providerAuthOption.id,
workspace: params.workspace,
surface: "cli",
runtime: params.runtime,
prompter: params.prompter
}));
if (result.ok) return activationLines(result);
await noteActivationFailure({
prompter: params.prompter,
label: providerAuthOption.label,
result
});
continue;
}
const provider = params.detection.manualProviders.find((item) => item.id === choice);
if (!provider) continue;
const apiKey = await params.prompter.text({
message: t("wizard.guided.apiKeyPrompt", { label: provider.label }),
sensitive: true,
validate: (value) => value.trim() ? void 0 : t("common.required")
});
const result = await withConsoleSubsystemsSuppressed(() => params.activate({
kind: "api-key",
authChoice: provider.id,
apiKey,
workspace: params.workspace,
surface: "cli",
runtime: params.runtime,
prompter: params.prompter
}));
if (result.ok) return activationLines(result);
await noteActivationFailure({
prompter: params.prompter,
label: provider.label,
result
});
}
}
function activationLines(result) {
return [...result.lines, t("wizard.guided.repliedIn", { seconds: (result.latencyMs / 1e3).toFixed(1) })];
}
//#endregion
//#region src/commands/onboard-guided.ts
async function openSystemAgentChat(deps, workspace, runtime, acceptRisk, agentName) {
await (deps.runSystemAgentChat ?? (async (setupWorkspace, chatRuntime, riskAccepted, setupAgentName) => {
const { runConversationalOnboarding } = await import("./onboard-interactive-CRD2PkQ_.js");
await runConversationalOnboarding({
workspace: setupWorkspace,
...setupAgentName ? { agentName: setupAgentName } : {},
...riskAccepted ? { acceptRisk: true } : {}
}, chatRuntime);
}))(workspace, runtime, acceptRisk, agentName);
}
async function runGuidedOnboardingFlow(opts, runtime, deps) {
const onboardHelpers = await import("./onboard-helpers-DC4-A9ie.js");
const prompter = await (deps.createPrompter?.() ?? import("./clack-prompter-Dr4XxfrM.js").then(({ createClackPrompter }) => createClackPrompter()));
await onboardHelpers.printWizardHeader(runtime);
await prompter.intro(t("wizard.guided.custodianIntro"));
await prompter.note(t("wizard.guided.escapeHatches"), t("wizard.guided.welcomeTitle"));
const { readConfigFileSnapshot } = await import("./config/config.js");
const snapshot = await readConfigFileSnapshot();
if (snapshot.exists && !snapshot.valid) {
const issues = snapshot.issues.length > 0 ? formatConfigIssueLines(snapshot.issues, "-").join("\n") : t("wizard.guided.invalidConfigUnknown");
await prompter.note(t("wizard.guided.invalidConfigDetails", {
path: shortenHomePath(snapshot.path),
issues
}), t("wizard.setup.invalidConfigTitle"));
await prompter.outro(t("wizard.guided.invalidConfigRepair", {
fixCommand: formatCliCommand("openclaw doctor --fix"),
inspectCommand: formatCliCommand("openclaw config validate")
}));
runtime.exit(1);
return null;
}
const existingConfig = snapshot.exists && snapshot.valid ? snapshot.sourceConfig ?? snapshot.config : {};
const custodianMode = (deps.handoffMode ?? "hatch") === "hatch";
const localOnboarding = custodianMode ? await import("./local-onboarding-state-COhpjJFR.js") : void 0;
const previousLocalSetup = localOnboarding?.readLocalOnboardingState(snapshot.path);
const consent = await requestGuidedOnboardingConsent({
opts,
prompter,
config: existingConfig,
offerQuickstart: custodianMode && (!snapshot.exists || isUnconfiguredConfigSource(existingConfig)) && existingConfig.wizard?.accessMode !== "guarded" && opts.nonInteractive !== true && opts.skipUi !== true && opts.tui !== true,
persistRiskAcknowledgement: deps.persistRiskAcknowledgement
});
let { quickstart } = consent;
const { config: acknowledgedConfig, securityAcknowledgedAt: onboardingSecurityAcknowledgedAt } = consent;
const hasAuthoredRoster = hasResolvedRosterBeforeMigrations(snapshot);
const firstAgent = await promptFirstOnboardingAgent(hasAuthoredRoster, opts.agentName, prompter, quickstart);
let localSetup = snapshot.exists ? localOnboarding?.readLocalOnboardingStateForConfig(snapshot.path, existingConfig) : void 0;
if (previousLocalSetup?.status === "pending" && localSetup === void 0) {
const currentSnapshot = await readConfigFileSnapshot();
const currentAcknowledgement = currentSnapshot.valid ? (currentSnapshot.sourceConfig ?? currentSnapshot.config).wizard?.securityAcknowledgedAt : void 0;
if (currentAcknowledgement === previousLocalSetup.securityAcknowledgedAt || currentAcknowledgement && currentAcknowledgement !== onboardingSecurityAcknowledgedAt) throw new Error("Another onboarding run already owns this installation. Retry setup.");
}
const replacePreviousSetup = !snapshot.exists || previousLocalSetup?.status === "pending" && localSetup === void 0 || previousLocalSetup?.status === "completed" && isUnconfiguredConfigSource(existingConfig);
const resumingSetup = localSetup?.status === "pending";
if (localSetup?.status === "pending" && opts.workspace?.trim() && resolveUserPath(opts.workspace.trim()) !== resolveUserPath(localSetup.workspace)) throw new Error("Another onboarding run owns a different workspace. Retry onboarding with its approved workspace.");
const assertLocalSetupOwner = (config) => {
if (localSetup?.status === "pending" && localOnboarding?.readLocalOnboardingStateForConfig(snapshot.path, config)?.runId !== localSetup.runId) throw new Error("Another onboarding run replaced this setup operation. Retry onboarding.");
};
let accessMode = "full";
if (custodianMode && !quickstart) accessMode = await prompter.select({
message: t("wizard.guided.accessQuestion"),
options: [{
value: "full",
label: t("wizard.guided.accessFullLabel"),
hint: t("wizard.guided.accessFullHint")
}, {
value: "guarded",
label: t("wizard.guided.accessGuardedLabel"),
hint: t("wizard.guided.accessGuardedHint")
}],
initialValue: existingConfig.wizard?.accessMode === "guarded" ? "guarded" : "full"
}) === "guarded" ? "guarded" : "full";
if (custodianMode && existingConfig.wizard?.accessMode !== accessMode) await (deps.persistAccessMode ?? persistAccessMode)(accessMode);
const workspace = resolveUserPath(opts.workspace?.trim() || (resumingSetup ? localSetup?.workspace : void 0) || acknowledgedConfig.agents?.defaults?.workspace?.trim() || onboardHelpers.DEFAULT_WORKSPACE);
const activateInference = deps.activate ?? (await import("./system-agent/setup-inference.js")).activateSetupInference;
const detect = deps.detect ?? (await import("./system-agent/setup-inference.js")).detectSetupInference;
const autoAttemptedKinds = /* @__PURE__ */ new Set();
const ladderFailures = [];
let detection;
let resultLines;
let successLabel;
const activate = async (params) => {
if (!localOnboarding || !resumingSetup && (existingConfig.gateway || detection?.setupComplete === true)) return await activateInference(params);
return await activateInference({
...params,
onCommitStarted: (sourceConfig) => {
params.onCommitStarted?.(sourceConfig);
const committedSecurityAcknowledgedAt = sourceConfig.wizard?.securityAcknowledgedAt;
if (committedSecurityAcknowledgedAt !== onboardingSecurityAcknowledgedAt) throw new Error("The onboarding configuration changed before inference could be saved. Retry onboarding.");
if (localSetup?.status === "pending") {
assertLocalSetupOwner(sourceConfig);
return;
}
const runId = randomUUID();
const claimedSetup = localOnboarding.beginLocalOnboarding({
configPath: snapshot.path,
workspace,
securityAcknowledgedAt: committedSecurityAcknowledgedAt,
runId,
...replacePreviousSetup ? {
replace: true,
...previousLocalSetup ? { expectedRunId: previousLocalSetup.runId } : {}
} : {}
});
if (claimedSetup.runId !== runId) throw new Error("Another onboarding run already owns this installation. Retry setup.");
localSetup = claimedSetup;
}
});
};
const wantsDiscovery = accessMode === "full" || await prompter.select({
message: t("wizard.guided.lookAroundQuestion"),
options: [{
value: "look",
label: t("wizard.guided.lookAroundYes")
}, {
value: "manual",
label: t("wizard.guided.lookAroundManual")
}],
initialValue: "look"
}) !== "manual";
if (wantsDiscovery) {
const detectionProgress = prompter.progress(t("wizard.guided.detecting"));
detection = await detect();
detectionProgress.stop(t("wizard.guided.detected"));
if (detection.candidates.length === 0) {
await prompter.note(t("wizard.guided.foundNothing"), t("wizard.guided.detectedTitle"));
if (detection.recommendedInstalls.length > 0) {
const recommendedInstalls = detection.recommendedInstalls.map((install) => t("wizard.guided.recommendedInstall", {
label: install.label,
hint: install.hint,
website: install.website
}));
await prompter.note(recommendedInstalls.join("\n"), t("wizard.guided.recommendedInstallsTitle"));
}
} else {
const candidates = detection.candidates.map((candidate) => t("wizard.guided.detectedCandidate", {
label: candidate.label,
detail: candidate.detail,
recommended: ""
}));
await prompter.note(candidates.join("\n"), t("wizard.guided.detectedTitle"));
const codingAgents = !custodianMode ? [] : detection.candidates.filter((candidate) => candidate.kind === "claude-cli" || candidate.kind === "codex-cli").map((candidate) => candidate.label);
if (codingAgents.length > 0) await prompter.note(t("wizard.guided.codingAgentQuip", { labels: codingAgents.join(", ") }), t("wizard.guided.detectedTitle"));
}
if (detection.unavailableCandidates.length > 0) {
const unavailable = detection.unavailableCandidates.map((candidate) => t("wizard.guided.unavailableCandidate", {
label: candidate.label,
detail: candidate.detail,
reason: candidate.reason
}));
await prompter.note(unavailable.join("\n"), t("wizard.guided.unavailableTitle"));
}
for (const candidate of detection.candidates.filter((item) => item.credentials !== false)) {
autoAttemptedKinds.add(candidate.kind);
const attempt = await tryCandidate({
candidate,
workspace,
runtime,
prompter,
activate,
...custodianMode ? { collectFailure: (failure) => ladderFailures.push(failure) } : {}
});
if (attempt.kind === "success") {
resultLines = activationLines(attempt.result);
successLabel = candidate.kind === "existing-model" ? `${candidate.label} (${candidate.modelRef})` : candidate.label;
break;
}
if (candidate.kind === "existing-model") {
await prompter.note(t("wizard.guided.existingModelKept"), t("wizard.guided.aiAccessTitle"));
break;
}
}
} else detection = {
candidates: [],
unavailableCandidates: [],
recommendedInstalls: [],
...await (deps.listManualOptions ?? (await import("./system-agent/setup-inference.js")).listManualSetupInferenceOptions)()
};
if (resultLines && successLabel && custodianMode) {
if (ladderFailures.length > 0) await prompter.note(t(quickstart ? "wizard.guided.quickstartSilentFailures" : "wizard.guided.silentFailures", { count: String(ladderFailures.length) }), t("wizard.guided.aiAccessTitle"));
if (quickstart) await prompter.note(t("wizard.guided.quickstartRoute", { label: successLabel }), t("wizard.guided.aiAccessTitle"));
if ((quickstart ? "use" : await prompter.select({
message: t("wizard.guided.routeConfirm", { label: successLabel }),
options: [{
value: "use",
label: t("wizard.guided.routeUse", { label: successLabel })
}, {
value: "other",
label: t("wizard.guided.routeOther")
}],
initialValue: "use"
})) === "other") {
if (ladderFailures.length > 0) await prompter.note([t("wizard.guided.failedOptionsIntro"), ...ladderFailures.map(formatSetupCandidateFailure)].join("\n"), t("wizard.guided.aiAccessTitle"));
const manualResult = await runManualStage({
detection,
autoAttemptedKinds,
config: existingConfig,
workspace,
runtime,
prompter,
activate,
hasActiveRoute: true
});
if (manualResult) resultLines = manualResult;
}
} else if (!resultLines) {
if (quickstart) {
await prompter.note(t("wizard.guided.quickstartManual"), t("wizard.guided.aiAccessTitle"));
quickstart = false;
}
if (ladderFailures.length > 0) {
const failureLines = ladderFailures.map(formatSetupCandidateFailure);
await prompter.note([t("wizard.guided.failedOptionsIntro"), ...failureLines].join("\n"), t("wizard.guided.aiAccessTitle"));
}
const manualResult = await runManualStage({
detection,
autoAttemptedKinds,
config: existingConfig,
workspace,
runtime,
prompter,
activate
});
if (!manualResult) return null;
resultLines = manualResult;
}
await prompter.note(resultLines.join("\n"), t("wizard.guided.appliedTitle"));
const persistedSnapshot = await readConfigFileSnapshot();
let persistedConfig = persistedSnapshot.valid ? persistedSnapshot.sourceConfig ?? persistedSnapshot.config : acknowledgedConfig;
if (!custodianMode) {
if (wantsDiscovery) await (deps.runSetupMemoryImportStep ?? (await import("./setup.memory-import-Dft2AxH2.js")).runSetupMemoryImportStep)({
config: persistedConfig,
prompter,
runtime
});
return {
workspace,
next: "chat",
...firstAgent ? { agentName: firstAgent.name } : {}
};
}
const alreadyConfigured = localSetup?.status !== "pending" && Boolean(detection?.setupComplete || existingConfig.gateway);
let gatewayExternallyManaged = false;
const { resolveSetupWorkspaceSelection } = await import("./setup.workspace-CWhcDWV2.js");
const workspaceSelection = await resolveSetupWorkspaceSelection({
baseConfig: existingConfig,
requestedWorkspaceDir: workspace,
prompter,
canConfirmMove: !alreadyConfigured
});
const { allowWorkspaceChange, conflict: workspaceConflict } = workspaceSelection;
const appliedWorkspace = workspaceSelection.workspaceDir;
if (localSetup?.status === "pending" && resolveUserPath(appliedWorkspace) !== localSetup.workspace) throw new Error("Another onboarding run owns a different workspace. Retry onboarding with its approved workspace.");
if (alreadyConfigured) {
await prompter.note(t("wizard.guided.alreadySetUp"), t("wizard.guided.welcomeTitle"));
if (workspaceConflict) await prompter.note(t("wizard.guided.workspaceConflictClassic", { command: formatCliCommand("openclaw onboard --classic") }), t("wizard.setup.workspaceConflictTitle"));
if (firstAgent) {
const { ensureOnboardingAgent } = await import("./onboard-agent-BaPkGQp-.js");
const created = await ensureOnboardingAgent({
config: persistedConfig,
workspace: appliedWorkspace,
baseConfig: persistedConfig,
firstAgent
});
persistedConfig = created.config;
await showSessionMigrationWarnings(prompter, created.sessionMigrationWarnings);
}
} else {
const applyProgress = prompter.progress(t("wizard.guided.settingUp"));
try {
if (localSetup?.status === "pending") {
const ownerSnapshot = await readConfigFileSnapshot();
if (!ownerSnapshot.exists || !ownerSnapshot.valid || resolveUserPath(ownerSnapshot.path) !== localSetup.configPath) throw new Error("Another onboarding run replaced this setup operation. Retry onboarding.");
assertLocalSetupOwner(ownerSnapshot.sourceConfig ?? ownerSnapshot.config);
}
const applySetup = deps.applySetup ?? (await import("./setup-apply-CtC8N7B0.js")).applySystemAgentSetup;
const applied = await withConsoleSubsystemsSuppressed(() => applySetup({
workspace,
...firstAgent ? { firstAgent } : {},
allowWorkspaceChange: allowWorkspaceChange || localSetup?.status === "pending",
...resumingSetup ? { resume: true } : {},
...localSetup?.status === "pending" ? { assertCommitPreconditions: assertLocalSetupOwner } : {},
...!opts.skipHooks ? { finalizeConfig: enableDefaultOnboardingInternalHooks } : {},
surface: "cli",
...quickstart ? { installDaemon: false } : {},
runtime
}));
if (applied.lines.length > 0) await prompter.note(applied.lines.join("\n"), t("wizard.guided.appliedTitle"));
if (!applied.workspaceReady) throw new Error("The agent workspace could not be prepared. Retry onboarding to finish setup.");
const gateway = applied.gateway;
if (gateway.status === "failed") throw new Error(gateway.error);
gatewayExternallyManaged = applied.gateway.status === "skipped" && applied.gateway.reason === "external";
const appliedSnapshot = localSetup?.status === "pending" ? await (await import("./setup-recovery-BvykTNNM.js")).completeLocalSetupRecovery({
owner: localSetup,
appliedConfigPath: applied.configPath
}) : await readConfigFileSnapshot();
if (!appliedSnapshot.valid) throw new Error("Setup wrote an invalid OpenClaw config.");
persistedConfig = appliedSnapshot.sourceConfig ?? appliedSnapshot.config;
applyProgress.stop(t("wizard.guided.setupDone"));
} catch (error) {
applyProgress.stop(t("wizard.guided.testFailed"));
await prompter.note(t("wizard.guided.applyFailedFallback", { detail: error instanceof Error ? error.message : String(error) }), t("wizard.guided.aiAccessTitle"));
return {
workspace,
next: "chat",
...firstAgent ? { agentName: firstAgent.name } : {}
};
}
}
if (wantsDiscovery && !quickstart) {
await (deps.runSetupMemoryImportStep ?? (await import("./setup.memory-import-Dft2AxH2.js")).runSetupMemoryImportStep)({
config: persistedConfig,
prompter,
runtime
});
const recommendationOutcome = await (deps.runAppRecommendations ?? (await import("./setup.app-recommendations-sbCkGILF.js")).setupAppRecommendations)({
config: persistedConfig,
prompter,
runtime,
workspaceDir: workspace,
modelRouteVerified: true
});
const recommendedConfig = recommendationOutcome.config;
if (recommendedConfig !== persistedConfig) {
const { writeWizardConfigFile } = await import("./setup.shared-mTYEiwX9.js");
persistedConfig = await writeWizardConfigFile(recommendedConfig, {
allowConfigSizeDrop: false,
mergeBase: persistedConfig
});
}
recommendationOutcome.commitResult();
}
const hatchWorkspace = alreadyConfigured ? resolveUserPath(existingConfig.agents?.defaults?.workspace?.trim() || onboardHelpers.DEFAULT_WORKSPACE) : appliedWorkspace;
if (quickstart && !gatewayExternallyManaged) {
await prompter.outro(t("wizard.guided.setupDone"));
return {
workspace: hatchWorkspace,
next: "foreground-gateway"
};
}
if (opts.skipUi === true) {
await prompter.outro(t("wizard.guided.complete"));
return null;
}
if (opts.tui !== true) {
if ((await (deps.runBrowserHandoff ?? (await import("./onboard-browser-handoff-CgHd7bD4.js")).runBrowserHatchHandoff)({
config: persistedConfig,
prompter,
...opts.suppressGatewayTokenOutput ? { suppressTokenOutput: true } : {}
})).handedOff) {
await prompter.outro(t("wizard.guided.browserHandoffReady"));
return {
workspace: hatchWorkspace,
next: "browser"
};
}
}
await prompter.note(t("wizard.guided.findMeLater"), t("wizard.guided.welcomeTitle"));
await prompter.outro(t("wizard.guided.hatchingNow"));
return {
workspace: hatchWorkspace,
next: "hatch",
local: alreadyConfigured
};
}
async function persistAccessMode(mode) {
const { mutateConfigFileWithRetry } = await import("./config/config.js");
await mutateConfigFileWithRetry({ mutate: (draft) => {
if (draft.wizard?.accessMode === mode) return;
draft.wizard = {
...draft.wizard,
accessMode: mode
};
} });
}
async function launchHatchTui(workspace, local) {
const [{ launchTuiCli }, { DEFAULT_BOOTSTRAP_FILENAME }, { restoreTerminalState }, fs, path] = await Promise.all([
import("./tui-launch-Dy4Xs2L6.js"),
import("./workspace-DYBYNr9B.js"),
import("./terminal-core/restore.js"),
import("node:fs"),
import("node:path")
]);
const hasBootstrap = fs.existsSync(path.join(workspace, DEFAULT_BOOTSTRAP_FILENAME));
restoreTerminalState("guided hatch tui", { resumeStdinIfPaused: false });
try {
await launchTuiCli({
...local ? { local: true } : {},
deliver: false,
...hasBootstrap ? { message: t("wizard.finalize.bootstrapHatchMessage") } : {}
});
} finally {
restoreTerminalState("post guided hatch tui", { resumeStdinIfPaused: false });
}
}
async function runGuidedOnboarding(opts, runtime, deps = {}) {
if (!hasInteractiveOnboardingTty()) {
runtime.error(t("wizard.guided.ttyRequired"));
runtime.exit(1);
return;
}
const state = { handoff: null };
await runInteractiveOnboarding(async () => {
state.handoff = await runGuidedOnboardingFlow(opts, runtime, deps);
}, runtime);
const handoff = state.handoff;
if (!handoff) return;
if (handoff.next === "foreground-gateway") {
await (deps.runForegroundGateway ?? (await import("./onboard-quickstart-host-D3YFnMJ3.js")).runQuickstartForegroundGateway)({
runtime,
...opts.suppressGatewayTokenOutput ? { suppressTokenOutput: true } : {}
});
return;
}
if (handoff.next === "hatch") {
if (deps.launchHatchTui) await deps.launchHatchTui(handoff.workspace);
else await launchHatchTui(handoff.workspace, handoff.local);
return;
}
if (handoff.next === "browser") return;
await openSystemAgentChat(deps, handoff.workspace, runtime, true, handoff.agentName);
}
//#endregion
export { runGuidedOnboarding as t };