UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,277 lines 59.7 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { a as writeRuntimeJson, r as defaultRuntime, t as ExitError } from "./runtime-CF2WjnNZ.js";
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js";
import { s as readConfigFileSnapshot } from "./io.runtime-B9iJRs3w.js";
import { l as redactSecrets } from "./redact-BtvPPfTi.js";
import "./utils-P__uGsPB.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { i as listAgentEntries } from "./agent-scope-config-DcbEhP0R.js";
import { _ as resolveGatewayPort, o as isDefaultStateDir, w as resolveStateDir } from "./paths-D2sRr1a_.js";
import { p as isValidEnvSecretRefId, v as resolveSecretInputRef } from "./types.secrets-kC0nOetj.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { r as createGatewayEnvSecretRef } from "./ref-contract-D92DqQ-r.js";
import { t as ConfigMutationConflictError } from "./mutation-conflict-Be0wSyDG.js";
import "./io-bdCzpGWJ.js";
import { r as replaceConfigFile } from "./mutate-ZNN4iFCn.js";
import "./config-Cs0XXL3x.js";
import { t as assertSupportedRuntime } from "./runtime-guard-2dUH2iVI.js";
import { r as isGatewayDaemonRuntime } from "./daemon-runtime-CdTwKGNX.js";
import { n as resolveConfiguredSecretInputWithFallback } from "./resolve-configured-secret-input-string-B8zlpQOU.js";
import { t as resolveGatewayAuthToken } from "./auth-token-resolution-B9de62Be.js";
import { t as randomToken } from "./random-token-B1woZa_H.js";
import { r as formatInvalidPortOption } from "./error-format-Cae7EwnT.js";
import { t as resolveGatewayStartupTiming } from "./gateway-startup-timing-D9NqKiRl.js";
import { r as resolveLocalControlUiProbeLinks } from "./control-ui-links-BY4F5DYV.js";
import { n as withOpenClawStateLease } from "./openclaw-state-lease-Ciyr2uVu.js";
import { r as resolveManifestProviderAuthChoices } from "./provider-auth-choices-DsRM415K.js";
import { o as normalizeTokenProviderInput } from "./provider-auth-input-B4MRIGbK.js";
import { n as t } from "./i18n-hynzGFbD.js";
import { t as withPluginLifecycleLease } from "./plugin-lifecycle-lease-BtAFH872.js";
import { n as applyWizardMetadata, o as handleReset, p as waitForGatewayReachable, s as normalizeGatewayTokenInput, t as DEFAULT_WORKSPACE } from "./onboard-helpers-BDY-6Z-t.js";
import { c as resolveOnboardingSetupTarget, i as ensureOnboardingAgentWorkspace, s as resolveOnboardingAgentTarget } from "./onboard-agent-target-CNKxVAZK.js";
import { i as resolveProviderMatch } from "./provider-auth-choice-helpers-CHV8Vtpf.js";
import { n as resolveProviderInstallCatalogEntries } from "./provider-install-catalog-BzkhdO6-.js";
import { r as logConfigUpdated } from "./logging-Idqd9ylL.js";
import { r as formatAuthChoiceChoicesForCli } from "./auth-choice-options-CT4g25ir.js";
import { n as GENERIC_PROVIDER_AUTH_CHOICES } from "./auth-choice-options.static-Dk9vjyDw.js";
import { i as resolveDeprecatedAuthChoiceReplacement, n as isDeprecatedAuthChoice, r as normalizeLegacyOnboardAuthChoice, t as formatDeprecatedNonInteractiveAuthChoiceError } from "./auth-choice-legacy-CxbJ5_3c.js";
import { t as resolvePluginProviders } from "./provider-auth-choice.runtime-C5tfLyPk.js";
import { t as createNonInteractiveLoggingPrompter } from "./non-interactive-prompter-DLCGsi38.js";
import { c as parseNonInteractiveCustomApiFlags, d as resolveCustomProviderId, n as applyCustomApiConfig, t as CustomApiError } from "./onboard-custom-config-DpIUL0Vo.js";
import { t as validateDottedDecimalIPv4Input } from "./ipv4-DSCxlk4V.js";
import { n as validateGatewayWebSocketUrl } from "./onboard-remote-BR-mUULq.js";
import { n as isOnboardFlow, t as isNodeManagerChoice } from "./onboard-types-Du2Y9b-2.js";
import { n as applySkipBootstrapConfig, r as resolveOnboardingWorkspaceConflict, t as applyLocalSetupWorkspaceConfig } from "./onboard-config-BiHHqyzH.js";
import { t as runGuidedOnboarding } from "./onboard-guided-BPGyHIiy.js";
import { t as enableDefaultOnboardingInternalHooks } from "./onboard-hooks-Ca9woCpp.js";
import { t as hasInteractiveOnboardingTty } from "./onboard-interactive-runner-7WVglo6Y.js";
import { u as withSetupMigrationTargetLock } from "./setup.migration-snapshot-DEcmXjFO.js";
import { n as runInteractiveSetup } from "./onboard-interactive-hp9gYp45.js";
import { t as inferAuthChoiceFromFlags } from "./auth-choice-inference-CIzedkxJ.js";
import { t as rejectOnboardingOption } from "./onboard-options-Tgvmf5eA.js";
import { t as resolveNonInteractiveApiKey } from "./api-keys-CNdvNbUX.js";
import { t as provisionGatewayTokenStoreRef } from "./auth-token-store-ref-B_2FhDbE.js";
import { isDeepStrictEqual } from "node:util";
import path from "node:path";
//#region src/commands/onboard-non-interactive/config-write.ts
/** Commits a non-interactive onboard config update with pending plugin records handled first. */
async function commitNonInteractiveOnboardConfig(params) {
	const { writeWizardConfigFile } = await import("./setup.shared-mTYEiwX9.js");
	return await writeWizardConfigFile(params.nextConfig, {
		allowConfigSizeDrop: params.reset === true,
		...params.baseHash !== void 0 ? { baseHash: params.baseHash } : {}
	});
}
//#endregion
//#region src/commands/onboard-non-interactive/local/gateway-config.ts
/**
* Gateway config mutation for local non-interactive onboarding.
*
* This module owns port/bind/auth validation and existing-setting preservation
* before the final config write happens.
*/
/** Resolves what `gateway.auth.token` should hold once setup owns the token value. */
function resolveGeneratedTokenInput(params) {
	if (params.secretInputMode !== "ref") return params.token ?? randomToken();
	if (params.ambientEnvOnly) return createGatewayEnvSecretRef(params.config, "OPENCLAW_GATEWAY_TOKEN");
	return provisionGatewayTokenStoreRef({
		config: params.config,
		...params.token ? { token: params.token } : {}
	}).ref;
}
/** Applies gateway CLI options to the pending config and returns normalized runtime settings. */
function applyNonInteractiveGatewayConfig(params) {
	const { opts, runtime } = params;
	const gatewayPort = opts.gatewayPort;
	if (gatewayPort !== void 0 && (!Number.isFinite(gatewayPort) || gatewayPort <= 0 || gatewayPort > 65535)) {
		rejectOnboardingOption(opts, runtime, formatInvalidPortOption("--gateway-port"));
		return null;
	}
	const existingGateway = params.nextConfig.gateway;
	const port = gatewayPort ?? params.defaultPort;
	let bind = opts.gatewayBind ?? existingGateway?.bind ?? "loopback";
	const explicitAuthMode = opts.gatewayAuth;
	if (explicitAuthMode !== void 0 && explicitAuthMode !== "token" && explicitAuthMode !== "password") {
		rejectOnboardingOption(opts, runtime, "Invalid --gateway-auth. Use \"token\" or \"password\".");
		return null;
	}
	const hasExplicitTokenAuthInput = opts.gatewayToken !== void 0 || opts.gatewayTokenRefEnv !== void 0;
	let authMode = explicitAuthMode ?? (hasExplicitTokenAuthInput ? "token" : opts.gatewayPassword !== void 0 ? "password" : existingGateway?.auth?.mode) ?? "token";
	const tailscaleMode = opts.tailscale ?? existingGateway?.tailscale?.mode ?? "off";
	if ((opts.gatewayBind !== void 0 || opts.tailscale !== void 0) && tailscaleMode !== "off" && bind !== "loopback") bind = "loopback";
	if (bind === "custom") {
		const customBindHostIssue = validateDottedDecimalIPv4Input(normalizeOptionalString(existingGateway?.customBindHost ?? ""));
		if (customBindHostIssue) {
			const setCommand = formatCliCommand("openclaw config set gateway.customBindHost <ipv4>");
			const interactiveCommand = formatCliCommand("openclaw onboard");
			rejectOnboardingOption(opts, runtime, `--gateway-bind custom requires gateway.customBindHost: ${customBindHostIssue}. Set it with ${setCommand} and rerun, or run ${interactiveCommand} interactively to be prompted for it.`);
			return null;
		}
	}
	const changesAuthOrTailscale = explicitAuthMode !== void 0 || hasExplicitTokenAuthInput || opts.tailscale !== void 0;
	if (changesAuthOrTailscale && tailscaleMode === "serve" && authMode === "none") authMode = "token";
	if (changesAuthOrTailscale && tailscaleMode === "funnel" && authMode !== "password") authMode = "password";
	let nextConfig = params.nextConfig;
	const explicitGatewayToken = normalizeGatewayTokenInput(opts.gatewayToken);
	const envGatewayToken = normalizeGatewayTokenInput(process.env.OPENCLAW_GATEWAY_TOKEN);
	const existingTokenInput = nextConfig.gateway?.auth?.token;
	const existingTokenRef = resolveSecretInputRef({
		value: existingTokenInput,
		defaults: nextConfig.secrets?.defaults
	}).ref;
	const existingPlaintextToken = normalizeGatewayTokenInput(existingTokenInput);
	const gatewayToken = explicitGatewayToken || existingPlaintextToken || envGatewayToken || void 0;
	const gatewayTokenRefEnv = normalizeOptionalString(opts.gatewayTokenRefEnv ?? "") ?? "";
	if (authMode === "token") {
		if (gatewayTokenRefEnv) {
			if (!isValidEnvSecretRefId(gatewayTokenRefEnv)) {
				rejectOnboardingOption(opts, runtime, "Invalid --gateway-token-ref-env. Use an environment variable name like OPENCLAW_GATEWAY_TOKEN.");
				return null;
			}
			if (explicitGatewayToken) {
				rejectOnboardingOption(opts, runtime, "Use either --gateway-token or --gateway-token-ref-env, not both. Prefer --gateway-token-ref-env to avoid writing plaintext tokens.");
				return null;
			}
			if (!process.env[gatewayTokenRefEnv]?.trim()) {
				rejectOnboardingOption(opts, runtime, `Environment variable "${gatewayTokenRefEnv}" is missing or empty. Export it first, then rerun ${formatCliCommand("openclaw onboard --non-interactive")}.`);
				return null;
			}
			nextConfig = {
				...nextConfig,
				gateway: {
					...nextConfig.gateway,
					auth: {
						...nextConfig.gateway?.auth,
						mode: "token",
						token: createGatewayEnvSecretRef(nextConfig, gatewayTokenRefEnv)
					}
				}
			};
		} else if (!explicitGatewayToken && existingTokenRef) nextConfig = {
			...nextConfig,
			gateway: {
				...nextConfig.gateway,
				auth: {
					...nextConfig.gateway?.auth,
					mode: "token"
				}
			}
		};
		else {
			const tokenInput = resolveGeneratedTokenInput({
				config: nextConfig,
				secretInputMode: opts.secretInputMode,
				token: gatewayToken,
				ambientEnvOnly: !explicitGatewayToken && !existingPlaintextToken && Boolean(envGatewayToken)
			});
			nextConfig = {
				...nextConfig,
				gateway: {
					...nextConfig.gateway,
					auth: {
						...nextConfig.gateway?.auth,
						mode: "token",
						token: tokenInput
					}
				}
			};
		}
	}
	if (authMode === "password") {
		const input = opts.gatewayPassword;
		const password = input === void 0 ? nextConfig.gateway?.auth?.password ?? normalizeOptionalString(process.env.OPENCLAW_GATEWAY_PASSWORD) : normalizeOptionalString(input);
		if (!password) {
			rejectOnboardingOption(opts, runtime, "Missing --gateway-password for password auth. Pass --gateway-password or use --gateway-auth token.");
			return null;
		}
		nextConfig = {
			...nextConfig,
			gateway: {
				...nextConfig.gateway,
				auth: {
					...nextConfig.gateway?.auth,
					mode: "password",
					...input !== void 0 ? { password: opts.secretInputMode === "ref" ? createGatewayEnvSecretRef(nextConfig, "OPENCLAW_GATEWAY_PASSWORD") : password } : {}
				}
			}
		};
	}
	nextConfig = {
		...nextConfig,
		gateway: {
			...nextConfig.gateway,
			port,
			bind,
			tailscale: {
				...nextConfig.gateway?.tailscale,
				mode: tailscaleMode
			}
		}
	};
	return {
		nextConfig,
		port,
		bind,
		authMode,
		tailscaleMode
	};
}
//#endregion
//#region src/commands/onboard-non-interactive/local/output.ts
/**
* Output helpers for non-interactive onboarding.
*
* JSON success/failure payloads and human-readable gateway health diagnostics
* are kept here so local and remote setup report failures consistently.
*/
/** Emits the JSON success payload for non-interactive onboarding when requested. */
function logNonInteractiveOnboardingJson(params) {
	if (!params.opts.json) return;
	writeRuntimeJson(params.runtime, {
		ok: true,
		mode: params.mode,
		workspace: params.workspaceDir,
		authChoice: params.authChoice,
		gateway: params.gateway,
		installDaemon: Boolean(params.installDaemon),
		daemonInstall: params.daemonInstall,
		daemonRuntime: params.daemonRuntime,
		skipSkills: Boolean(params.skipSkills),
		skipHealth: Boolean(params.skipHealth)
	});
}
function formatGatewayRuntimeSummary(diagnostics) {
	const service = diagnostics?.service;
	if (!service?.runtimeStatus) return;
	const parts = [service.runtimeStatus];
	if (typeof service.pid === "number") parts.push(`pid ${service.pid}`);
	if (service.state) parts.push(`state ${service.state}`);
	if (typeof service.lastExitStatus === "number") parts.push(`last exit ${service.lastExitStatus}`);
	if (service.lastExitReason) parts.push(`reason ${service.lastExitReason}`);
	return parts.join(", ");
}
function hasConnectionRefusedDetail(detail) {
	return /\b(?:econnrefused|connection refused|connect refused)\b/i.test(detail);
}
function classifyGatewayHealthFailure(params) {
	const detail = params.detail ?? "";
	const lastGatewayError = params.diagnostics?.lastGatewayError ?? "";
	const combined = `${detail}\n${lastGatewayError}`;
	if (/\b(?:unauthorized|forbidden|invalid token|invalid password|auth mismatch)\b/i.test(combined)) return "auth-mismatch";
	if (/\b(?:runtime[- ]deps?|runtime dependencies|cannot find (?:module|package)|(?:err_)?module_not_found|sqlite-vec|loadextension)\b/i.test(combined)) return "module-missing";
	if (params.diagnostics?.service?.loadState.status === "not-loaded" && hasConnectionRefusedDetail(detail)) return "service-missing";
	const runtimeStatus = params.diagnostics?.service?.runtimeStatus;
	if (runtimeStatus && runtimeStatus !== "running" && runtimeStatus !== "active" && hasConnectionRefusedDetail(detail)) return "service-stopped";
	if (lastGatewayError.trim()) return "startup-blocked";
	if (hasConnectionRefusedDetail(detail)) return "not-listening";
}
function recoveryHintForGatewayHealthFailure(classification) {
	switch (classification) {
		case "auth-mismatch":
		case "module-missing": return `Fix: run \`${formatCliCommand("openclaw doctor --fix")}\`.`;
		case "service-missing": return `Fix: run \`${formatCliCommand("openclaw gateway install --force")}\`.`;
		case "service-stopped": return `Fix: run \`${formatCliCommand("openclaw gateway restart")}\`.`;
		case "startup-blocked": return `Fix: run \`${formatCliCommand("openclaw gateway status --deep")}\`.`;
		case "not-listening": return `Fix: start \`${formatCliCommand("openclaw gateway run")}\`, or run \`${formatCliCommand("openclaw gateway restart")}\` for a managed gateway.`;
		default: return;
	}
}
/** Emits JSON or human-readable failure output for non-interactive onboarding. */
function logNonInteractiveOnboardingFailure(params) {
	const classification = classifyGatewayHealthFailure({
		detail: params.detail,
		diagnostics: params.diagnostics
	});
	const callerHints = params.hints?.filter(Boolean) ?? [];
	const recoveryHint = callerHints.some((hint) => hint.startsWith("Fix:")) ? void 0 : recoveryHintForGatewayHealthFailure(classification);
	const hints = [...recoveryHint ? [recoveryHint] : [], ...callerHints];
	const output = redactSecrets({
		message: params.message,
		detail: params.detail,
		hints,
		gateway: params.gateway,
		daemonInstall: params.daemonInstall,
		daemonRuntime: params.daemonRuntime,
		diagnostics: params.diagnostics
	});
	const gatewayRuntime = formatGatewayRuntimeSummary(output.diagnostics);
	const service = output.diagnostics?.service;
	const serviceLoadText = service ? service.loadState.status === "loaded" ? service.loadedText : service.loadState.status.replace("-", " ") : void 0;
	if (params.opts.json) {
		writeRuntimeJson(params.runtime, {
			ok: false,
			mode: params.mode,
			phase: params.phase,
			message: output.message,
			classification,
			detail: output.detail,
			gateway: output.gateway,
			installDaemon: Boolean(params.installDaemon),
			daemonInstall: output.daemonInstall,
			daemonRuntime: output.daemonRuntime,
			diagnostics: output.diagnostics,
			hints: output.hints.length > 0 ? output.hints : void 0
		});
		return;
	}
	const lines = [
		output.message,
		classification ? `Classification: ${classification}` : void 0,
		output.detail ? `Last probe: ${output.detail}` : void 0,
		service ? `Service: ${service.label} (${serviceLoadText})` : void 0,
		gatewayRuntime ? `Runtime: ${gatewayRuntime}` : void 0,
		output.diagnostics?.lastGatewayError ? `Last gateway error: ${output.diagnostics.lastGatewayError}` : void 0,
		output.diagnostics?.inspectError ? `Diagnostics warning: ${output.diagnostics.inspectError}` : void 0,
		output.hints.length > 0 ? output.hints.join("\n") : void 0
	].filter(Boolean).join("\n");
	if (params.informational) params.runtime.log(lines);
	else params.runtime.error(lines);
}
//#endregion
//#region src/commands/onboard-non-interactive/local/skills-config.ts
/** Applies the non-interactive skills install options to the pending config. */
function applyNonInteractiveSkillsConfig(params) {
	const { nextConfig, opts, runtime } = params;
	if (opts.skipSkills) return nextConfig;
	const nodeManager = opts.nodeManager;
	if (nodeManager !== void 0 && ![
		"npm",
		"pnpm",
		"bun"
	].includes(nodeManager)) {
		runtime.error("Invalid --node-manager. Use \"npm\", \"pnpm\", or \"bun\".");
		runtime.exit(1);
		return nextConfig;
	}
	return {
		...nextConfig,
		skills: {
			...nextConfig.skills,
			install: {
				...nextConfig.skills?.install,
				nodeManager: nodeManager ?? nextConfig.skills?.install?.nodeManager ?? "npm"
			}
		}
	};
}
//#endregion
//#region src/commands/onboard-non-interactive/local/workspace.ts
/**
* Workspace resolution for local non-interactive onboarding.
*
* CLI input wins, then existing config, then the computed default workspace,
* and the final value is expanded through the normal user-path resolver.
*/
/** Resolves the workspace directory used by local non-interactive setup. */
function resolveNonInteractiveWorkspaceDir(params) {
	const env = params.env ?? process.env;
	const requestedWorkspace = params.opts.workspace?.trim() || void 0;
	const configuredWorkspace = params.baseConfig.agents?.defaults?.workspace?.trim() || void 0;
	const workspaceOverride = env.OPENCLAW_WORKSPACE_DIR?.trim() || void 0;
	const implicitWorkspaceDir = isDefaultStateDir(env) ? params.defaultWorkspaceDir : path.join(resolveStateDir(env), "workspace");
	const raw = (requestedWorkspace ?? configuredWorkspace ?? workspaceOverride ?? implicitWorkspaceDir).trim();
	return resolveUserPath(raw, env);
}
//#endregion
//#region src/commands/onboard-non-interactive/local.ts
/**
* Local non-interactive onboarding orchestration.
*
* This entrypoint applies config changes, optionally installs the gateway
* daemon, verifies health, and emits machine-readable setup output.
*/
async function collectGatewayHealthFailureDiagnostics() {
	const diagnostics = {};
	try {
		const { readGatewayServiceState, resolveGatewayService } = await import("./service-DUXpzYMB.js");
		const service = resolveGatewayService();
		const env = process.env;
		const state = await readGatewayServiceState(service, { env });
		const runtime = state.runtime;
		const loaded = state.loadState.status === "unknown" ? null : state.loadState.status === "loaded";
		diagnostics.service = {
			label: service.label,
			loaded,
			loadState: state.loadState,
			loadedText: service.loadedText,
			runtimeStatus: runtime?.status,
			state: runtime?.state,
			pid: runtime?.pid,
			lastExitStatus: runtime?.lastExitStatus,
			lastExitReason: runtime?.lastExitReason
		};
	} catch (err) {
		diagnostics.inspectError = `service diagnostics failed: ${String(err)}`;
	}
	try {
		const { readLastGatewayErrorLine } = await import("./diagnostics-C7k8xw3w.js");
		diagnostics.lastGatewayError = await readLastGatewayErrorLine(process.env) ?? void 0;
	} catch (err) {
		diagnostics.inspectError = diagnostics.inspectError ? `${diagnostics.inspectError}; log diagnostics failed: ${String(err)}` : `log diagnostics failed: ${String(err)}`;
	}
	return diagnostics.service || diagnostics.lastGatewayError || diagnostics.inspectError ? diagnostics : void 0;
}
/** Resolves the auth material used by the post-setup gateway health probe. */
async function resolveGatewayHealthProbeToken(nextConfig) {
	if (nextConfig.gateway?.auth?.mode === "password") {
		const resolved = await resolveConfiguredSecretInputWithFallback({
			config: nextConfig,
			env: process.env,
			value: nextConfig.gateway.auth.password,
			path: "gateway.auth.password",
			unresolvedReasonStyle: "detailed",
			readFallback: () => process.env.OPENCLAW_GATEWAY_PASSWORD
		});
		return {
			password: resolved.value,
			unresolvedRefReason: resolved.unresolvedRefReason
		};
	}
	const resolved = await resolveGatewayAuthToken({
		cfg: nextConfig,
		env: process.env,
		unresolvedReasonStyle: "detailed"
	});
	const probeAuth = {};
	if (resolved.token) probeAuth.token = resolved.token;
	if (resolved.unresolvedRefReason) probeAuth.unresolvedRefReason = resolved.unresolvedRefReason;
	return probeAuth;
}
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.onboardNonInteractiveLocalTestApi")] = { resolveGatewayHealthProbeToken };
function formatGatewayHealthFailureDetail(params) {
	return [params.probeDetail, params.unresolvedRefReason].filter(Boolean).join("\n") || void 0;
}
/** Runs local non-interactive setup from config mutation through health verification. */
async function runNonInteractiveLocalSetup(params) {
	const { opts, runtime, baseConfig, sourceConfigBeforeMigrations, baseHash } = params;
	const mode = "local";
	const requestedWorkspaceDir = resolveNonInteractiveWorkspaceDir({
		opts,
		baseConfig,
		defaultWorkspaceDir: DEFAULT_WORKSPACE
	});
	const hasAuthoredRoster = listAgentEntries(sourceConfigBeforeMigrations).length > 0;
	const workspaceConflict = resolveOnboardingWorkspaceConflict(sourceConfigBeforeMigrations, requestedWorkspaceDir);
	const workspaceDir = workspaceConflict?.currentWorkspaceDir ?? requestedWorkspaceDir;
	if (workspaceConflict) runtime.error([
		"Warning: existing agents keep their current workspace during non-interactive onboarding.",
		`Current workspace: ${workspaceConflict.currentWorkspaceDir}`,
		`Requested workspace: ${workspaceConflict.requestedWorkspaceDir}`,
		`Run \`${formatCliCommand("openclaw onboard --classic")}\` to confirm moving the existing agent fleet.`
	].join("\n"));
	let nextConfig = applyLocalSetupWorkspaceConfig(baseConfig, requestedWorkspaceDir, { allowWorkspaceChange: !hasAuthoredRoster && !workspaceConflict });
	if (opts.skipBootstrap) nextConfig = applySkipBootstrapConfig(nextConfig);
	const authTarget = resolveOnboardingSetupTarget(nextConfig, opts.agentName && !hasAuthoredRoster ? {
		name: opts.agentName,
		workspaceDir
	} : void 0);
	const inferredAuthChoice = opts.authChoice ? void 0 : (await import("./auth-choice-inference-CEaqu-NJ.js")).inferAuthChoiceFromFlags(opts, {
		config: nextConfig,
		workspaceDir: authTarget.workspaceDir,
		env: process.env
	});
	if (!opts.authChoice && inferredAuthChoice && inferredAuthChoice.matches.length > 1) {
		const message = [
			"Multiple API key flags were provided for non-interactive setup.",
			"Use a single provider flag or pass --auth-choice explicitly.",
			`Flags: ${inferredAuthChoice.matches.map((match) => match.label).join(", ")}`
		].join("\n");
		rejectOnboardingOption(opts, runtime, message);
		return;
	}
	const authChoice = opts.authChoice ?? inferredAuthChoice?.choice ?? "skip";
	const gatewayResult = applyNonInteractiveGatewayConfig({
		nextConfig,
		opts,
		runtime,
		defaultPort: resolveGatewayPort(baseConfig)
	});
	if (!gatewayResult) return;
	nextConfig = gatewayResult.nextConfig;
	nextConfig = applyNonInteractiveSkillsConfig({
		nextConfig,
		opts,
		runtime
	});
	if (authChoice !== "skip") {
		const { applyNonInteractiveAuthChoice } = await import("./auth-choice-BQjXsDba.js");
		const nextConfigAfterAuth = await applyNonInteractiveAuthChoice({
			nextConfig,
			authChoice,
			opts,
			runtime,
			baseConfig,
			target: authTarget
		});
		if (!nextConfigAfterAuth) return;
		nextConfig = nextConfigAfterAuth;
	}
	if (!opts.skipHooks) nextConfig = enableDefaultOnboardingInternalHooks(nextConfig);
	const { ensureOnboardingAgent } = await import("./onboard-agent-BaPkGQp-.js");
	const created = await ensureOnboardingAgent({
		config: nextConfig,
		workspace: workspaceDir,
		baseConfig,
		firstAgent: { name: opts.agentName ?? "main" },
		expectedConfigHash: baseHash ?? null
	});
	for (const warning of created.sessionMigrationWarnings ?? []) runtime.log(`Warning: ${warning}`);
	nextConfig = applyLocalSetupWorkspaceConfig(created.config, requestedWorkspaceDir);
	const effectiveBaseHash = created.configHash ?? baseHash;
	if (opts.skipBootstrap) nextConfig = applySkipBootstrapConfig(nextConfig);
	const finalTarget = resolveOnboardingAgentTarget(nextConfig, created.agentId);
	await ensureOnboardingAgentWorkspace(finalTarget, runtime, {
		skipBootstrap: Boolean(nextConfig.agents?.defaults?.skipBootstrap),
		skipOptionalBootstrapFiles: nextConfig.agents?.defaults?.skipOptionalBootstrapFiles
	});
	nextConfig = applyWizardMetadata(nextConfig, {
		command: "onboard",
		mode
	});
	nextConfig = await commitNonInteractiveOnboardConfig({
		nextConfig,
		baseHash: effectiveBaseHash,
		reset: opts.reset
	});
	logConfigUpdated(runtime);
	const daemonRuntimeRaw = opts.daemonRuntime ?? "node";
	let daemonInstallStatus;
	let gatewayNotRunning = false;
	if (opts.installDaemon) {
		const { installGatewayDaemonNonInteractive } = await import("./daemon-install-B8msPCc9.js");
		const daemonInstall = await installGatewayDaemonNonInteractive({
			nextConfig,
			opts,
			runtime,
			port: gatewayResult.port
		});
		daemonInstallStatus = daemonInstall.installed ? {
			requested: true,
			installed: true
		} : {
			requested: true,
			installed: false,
			skippedReason: daemonInstall.skippedReason
		};
		if (!daemonInstall.installed) {
			logNonInteractiveOnboardingFailure({
				opts,
				runtime,
				mode,
				phase: "daemon-install",
				message: daemonInstall.skippedReason === "systemd-user-unavailable" ? "Gateway service install is unavailable because systemd user services are not reachable in this Linux session." : "Gateway service install did not complete successfully.",
				installDaemon: true,
				daemonInstall: {
					requested: true,
					installed: false,
					skippedReason: daemonInstall.skippedReason
				},
				daemonRuntime: daemonRuntimeRaw,
				hints: daemonInstall.skippedReason === "systemd-user-unavailable" ? ["Fix: rerun without `--install-daemon` for one-shot setup, or enable a working user-systemd session and retry.", "If your auth profile uses env-backed refs, keep those env vars set in the shell that runs `openclaw gateway run` or `openclaw agent --local`."] : [`Run \`${formatCliCommand("openclaw gateway status --deep")}\` for more detail.`]
			});
			runtime.exit(1);
			return;
		}
	}
	if (!opts.skipHealth) {
		const { healthCommandNonExiting } = await import("./health-YMSl1wN1.js");
		const links = resolveLocalControlUiProbeLinks({
			bind: gatewayResult.bind,
			port: gatewayResult.port,
			customBindHost: nextConfig.gateway?.customBindHost,
			basePath: void 0,
			tlsEnabled: nextConfig.gateway?.tls?.enabled === true
		});
		const startupTiming = opts.installDaemon ? resolveGatewayStartupTiming() : { deadlineMs: 15e3 };
		const probeAuth = await resolveGatewayHealthProbeToken(nextConfig);
		const probe = await waitForGatewayReachable({
			url: links.wsUrl,
			token: probeAuth.token,
			password: probeAuth.password,
			...startupTiming
		});
		if (!probe.ok) {
			const detail = formatGatewayHealthFailureDetail({
				probeDetail: probe.detail,
				unresolvedRefReason: probeAuth.unresolvedRefReason
			});
			const diagnostics = opts.installDaemon ? await collectGatewayHealthFailureDiagnostics() : void 0;
			const explicitlySkippedAbsentGateway = opts.installDaemon === false && classifyGatewayHealthFailure({
				detail,
				diagnostics
			}) === "not-listening";
			if (explicitlySkippedAbsentGateway && !opts.json) runtime.log("Setup complete; gateway was not installed or started because daemon installation was explicitly skipped.");
			if (!explicitlySkippedAbsentGateway || !opts.json) logNonInteractiveOnboardingFailure({
				opts,
				runtime,
				mode,
				phase: "gateway-health",
				message: `Gateway did not become reachable at ${links.wsUrl}.`,
				detail,
				gateway: {
					wsUrl: links.wsUrl,
					httpUrl: links.httpUrl
				},
				installDaemon: Boolean(opts.installDaemon),
				daemonInstall: daemonInstallStatus,
				daemonRuntime: opts.installDaemon ? daemonRuntimeRaw : void 0,
				diagnostics,
				hints: !opts.installDaemon ? [
					"Non-interactive local setup only waits for an already-running gateway unless you pass `--install-daemon` to `openclaw onboard`.",
					`Fix: start \`${formatCliCommand("openclaw gateway run")}\`, re-run \`${formatCliCommand("openclaw onboard --install-daemon")}\`, or use \`${formatCliCommand("openclaw onboard --skip-health")}\`.`,
					process.platform === "win32" ? "Native Windows managed gateway install tries Scheduled Tasks first and falls back to a per-user Startup-folder login item when task creation is denied." : void 0
				].filter((value) => Boolean(value)) : [`Run \`${formatCliCommand("openclaw gateway status --deep")}\` for more detail.`],
				informational: explicitlySkippedAbsentGateway
			});
			if (!explicitlySkippedAbsentGateway) {
				runtime.exit(1);
				return;
			}
			gatewayNotRunning = true;
		} else {
			const capturedHealthLines = [];
			const healthRuntime = opts.json ? {
				...runtime,
				log: (...args) => {
					capturedHealthLines.push(args.map(String).join(" "));
				}
			} : runtime;
			try {
				await healthCommandNonExiting({
					json: false,
					timeoutMs: opts.installDaemon && process.platform === "win32" ? 9e4 : 1e4,
					config: nextConfig,
					token: probeAuth.token,
					password: probeAuth.password
				}, healthRuntime);
			} catch (err) {
				const detail = err instanceof ExitError ? capturedHealthLines.join("\n") || void 0 : formatErrorMessage(err);
				logNonInteractiveOnboardingFailure({
					opts,
					runtime,
					mode,
					phase: "gateway-health",
					message: `Gateway is reachable at ${links.wsUrl}, but the health check failed.`,
					detail,
					gateway: {
						wsUrl: links.wsUrl,
						httpUrl: links.httpUrl
					},
					installDaemon: Boolean(opts.installDaemon),
					daemonInstall: daemonInstallStatus,
					daemonRuntime: opts.installDaemon ? daemonRuntimeRaw : void 0,
					hints: [`Run \`${formatCliCommand("openclaw health")}\` for full diagnostics.`]
				});
				runtime.exit(1);
				return;
			}
		}
	}
	logNonInteractiveOnboardingJson({
		opts,
		runtime,
		mode,
		workspaceDir: finalTarget.workspaceDir,
		authChoice,
		gateway: {
			port: gatewayResult.port,
			bind: gatewayResult.bind,
			authMode: gatewayResult.authMode,
			tailscaleMode: gatewayResult.tailscaleMode,
			...gatewayNotRunning ? { reachable: false } : {}
		},
		installDaemon: Boolean(opts.installDaemon),
		daemonInstall: daemonInstallStatus,
		daemonRuntime: opts.installDaemon ? daemonRuntimeRaw : void 0,
		skipSkills: Boolean(opts.skipSkills),
		skipHealth: Boolean(opts.skipHealth)
	});
	if (!opts.json) runtime.log(`Tip: run \`${formatCliCommand("openclaw configure --section web")}\` to store your Brave API key for web_search. Docs: https://docs.openclaw.ai/tools/web`);
}
//#endregion
//#region src/commands/onboard-non-interactive/remote.ts
/**
* Remote non-interactive onboarding orchestration.
*
* It writes gateway.remote config without local gateway setup, preserving the
* same config commit path as local onboarding.
*/
/** Runs non-interactive setup for clients that connect to an existing remote gateway. */
async function runNonInteractiveRemoteSetup(params) {
	const { opts, runtime, baseConfig, baseHash } = params;
	const mode = "remote";
	const remoteUrl = normalizeOptionalString(opts.remoteUrl);
	if (!remoteUrl) {
		runtime.error(`Missing --remote-url for remote mode. Example: ${formatCliCommand("openclaw onboard --non-interactive --mode remote --remote-url ws://127.0.0.1:3000")}.`);
		runtime.exit(1);
		return;
	}
	const remoteToken = normalizeOptionalString(opts.remoteToken);
	const remotePassword = normalizeOptionalString(opts.remotePassword);
	for (const [flag, input, normalized] of [[
		"--remote-token",
		opts.remoteToken,
		remoteToken
	], [
		"--remote-password",
		opts.remotePassword,
		remotePassword
	]]) if (input !== void 0 && !normalized) {
		runtime.error(`Invalid ${flag}: value cannot be empty.`);
		runtime.exit(1);
		return;
	}
	if (remoteToken && remotePassword) {
		runtime.error("Use either --remote-token or --remote-password, not both.");
		runtime.exit(1);
		return;
	}
	const existingRemote = baseConfig.gateway?.remote;
	const preservedRemote = normalizeOptionalString(existingRemote?.url) !== remoteUrl ? {} : { ...existingRemote };
	if (remoteToken) delete preservedRemote.password;
	if (remotePassword) delete preservedRemote.token;
	let nextConfig = {
		...baseConfig,
		gateway: {
			...baseConfig.gateway,
			mode: "remote",
			remote: {
				...preservedRemote,
				url: remoteUrl,
				...remoteToken ? { token: opts.secretInputMode === "ref" ? createGatewayEnvSecretRef(baseConfig, "OPENCLAW_GATEWAY_TOKEN") : remoteToken } : {},
				...remotePassword ? { password: opts.secretInputMode === "ref" ? createGatewayEnvSecretRef(baseConfig, "OPENCLAW_GATEWAY_PASSWORD") : remotePassword } : {}
			}
		}
	};
	if (opts.skipBootstrap) nextConfig = applySkipBootstrapConfig(nextConfig);
	nextConfig = applyWizardMetadata(nextConfig, {
		command: "onboard",
		mode
	});
	await commitNonInteractiveOnboardConfig({
		nextConfig,
		baseHash,
		reset: opts.reset
	});
	logConfigUpdated(runtime);
	const payload = {
		mode,
		remoteUrl,
		auth: nextConfig.gateway?.remote?.token ? "token" : nextConfig.gateway?.remote?.password ? ["pass", "word"].join("") : "none"
	};
	if (opts.json) writeRuntimeJson(runtime, payload);
	else {
		runtime.log(`Remote gateway: ${remoteUrl}`);
		runtime.log(`Auth: ${payload.auth}`);
		runtime.log(`Tip: run \`${formatCliCommand("openclaw configure --section web")}\` to store your Brave API key for web_search. Docs: https://docs.openclaw.ai/tools/web`);
	}
}
//#endregion
//#region src/commands/onboard-non-interactive.ts
/**
* Non-interactive onboarding command dispatcher.
*
* This module validates the existing config snapshot, routes local/remote
* setup, and handles explicit migration imports without interactive prompts.
*/
function isMigrationImport(opts) {
	return Boolean(opts.importFrom || opts.importSource || opts.importSecrets || opts.flow === "import");
}
/** Runs a setup migration import with non-interactive prompt failures. */
async function runNonInteractiveMigrationImport(params) {
	const providerId = params.opts.importFrom?.trim();
	if (!providerId) {
		rejectOnboardingOption(params.opts, params.runtime, `--import-from is required for non-interactive migration import. Run ${formatCliCommand("openclaw migrate list")} to choose a provider.`);
		return;
	}
	const { detectSetupMigrationSources, runSetupMigrationImport } = await import("./setup.migration-import-BvvdUhh_.js");
	const detections = await detectSetupMigrationSources({
		config: params.baseConfig,
		runtime: params.runtime
	});
	const outcome = await runSetupMigrationImport({
		opts: {
			...params.opts,
			importFrom: providerId,
			nonInteractive: true
		},
		baseConfig: params.baseConfig,
		detections,
		prompter: createNonInteractiveLoggingPrompter(params.runtime, (message) => `Non-interactive migration import needs explicit flags before prompting: ${message}`),
		runtime: params.runtime,
		async readConfigFile() {
			const snapshot = await readConfigFileSnapshot();
			if (!snapshot.valid) throw new Error("Migration target config became invalid. Run `openclaw doctor`.");
			return snapshot.exists ? snapshot.sourceConfig ?? snapshot.config : {};
		},
		async commitConfigFile(config, expectedConfig) {
			const latest = await readConfigFileSnapshot();
			if (!latest.valid) throw new Error("Migration target config became invalid. Run `openclaw doctor`.");
			const latestConfig = latest.exists ? latest.sourceConfig ?? latest.config : {};
			if (!isDeepStrictEqual(latestConfig, expectedConfig)) throw new ConfigMutationConflictError("config changed during migration promotion");
			const committed = await replaceConfigFile({
				nextConfig: config,
				snapshot: latest,
				...latest.hash !== void 0 ? { baseHash: latest.hash } : {},
				writeOptions: { allowConfigSizeDrop: true }
			});
			logConfigUpdated(params.runtime);
			return committed.nextConfig;
		}
	});
	if (outcome.kind === "back") throw new Error("Non-interactive migration import cannot navigate back.");
	await outcome.acknowledgePromotion?.();
}
async function runNonInteractiveSetupExclusive(opts, runtime) {
	const snapshot = await readConfigFileSnapshot();
	if (snapshot.exists && !snapshot.valid) {
		rejectOnboardingOption(opts, runtime, `Config invalid. Run \`${formatCliCommand("openclaw doctor")}\` to repair it, then re-run setup.`);
		return;
	}
	const baseConfig = snapshot.valid ? snapshot.exists ? snapshot.sourceConfig ?? snapshot.config : {} : {};
	const mode = opts.mode ?? "local";
	if (mode !== "local" && mode !== "remote") {
		rejectOnboardingOption(opts, runtime, `Invalid --mode "${String(mode)}". Use "local" or "remote", or run ${formatCliCommand("openclaw onboard")} for interactive setup.`);
		return;
	}
	if (isMigrationImport(opts)) {
		await runNonInteractiveMigrationImport({
			opts,
			runtime,
			baseConfig
		});
		return;
	}
	if (mode === "remote") {
		await runNonInteractiveRemoteSetup({
			opts,
			runtime,
			baseConfig,
			baseHash: snapshot.hash
		});
		return;
	}
	await runNonInteractiveLocalSetup({
		opts,
		runtime,
		baseConfig,
		sourceConfigBeforeMigrations: snapshot.sourceConfigBeforeMigrations ?? {},
		baseHash: snapshot.hash
	});
}
/** Runs non-interactive onboarding in local, remote, or migration-import mode. */
async function runNonInteractiveSetup(opts, runtime = defaultRuntime) {
	await withSetupMigrationTargetLock(resolveStateDir(), async () => {
		if (isMigrationImport(opts)) {
			await runNonInteractiveSetupExclusive(opts, runtime);
			return;
		}
		await withOpenClawStateLease({
			scope: "core:onboarding",
			key: "global",
			database: { scope: "shared" },
			leaseMs: 3e5,
			waitMs: 6e5,
			leaseLabel: "non-interactive onboarding lease",
			operationLabel: "onboarding.non-interactive.lease"
		}, async () => await withPluginLifecycleLease({}, async () => runNonInteractiveSetupExclusive(opts, runtime)));
	});
}
//#endregion
//#region src/commands/onboard.ts
/**
* Top-level `openclaw onboard` command entrypoint.
*
* It validates global setup flags, performs optional reset handling, and then
* routes to interactive or non-interactive onboarding.
*/
const VALID_RESET_SCOPES = /* @__PURE__ */ new Set([
	"config",
	"config+creds+sessions",
	"full"
]);
function validatePreflightOptions(opts, runtime) {
	if (opts.mode !== void 0 && opts.mode !== "local" && opts.mode !== "remote") return rejectOnboardingOption(opts, runtime, `Invalid --mode "${String(opts.mode)}". Use "local" or "remote", or run ${formatCliCommand("openclaw onboard")} for interactive setup.`);
	const remoteOnlyFlags = [
		opts.remoteUrl !== void 0 ? "--remote-url" : void 0,
		opts.remoteToken !== void 0 ? "--remote-token" : void 0,
		opts.remotePassword !== void 0 ? "--remote-password" : void 0
	].filter((flag) => flag !== void 0);
	if (opts.nonInteractive && (opts.mode ?? "local") === "local" && remoteOnlyFlags.length > 0) return rejectOnboardingOption(opts, runtime, `${remoteOnlyFlags.join(" and ")} ${remoteOnlyFlags.length === 1 ? "requires" : "require"} --mode remote in non-interactive setup.`);
	for (const [flag, value] of [["--remote-token", opts.remoteToken], ["--remote-password", opts.remotePassword]]) if (value !== void 0 && !value.trim()) return rejectOnboardingOption(opts, runtime, `Invalid ${flag}: value cannot be empty.`);
	if (opts.remoteToken !== void 0 && opts.remotePassword !== void 0) return rejectOnboardingOption(opts, runtime, "Use either --remote-token or --remote-password, not both.");
	if (opts.mode === "remote") {
		const localGatewayCredentials = [
			[
				"--gateway-password",
				opts.gatewayPassword,
				"--remote-password"
			],
			[
				"--gateway-token",
				opts.gatewayToken,
				"--remote-token"
			],
			[
				"--gateway-token-ref-env",
				opts.gatewayTokenRefEnv,
				"--remote-token with --secret-input-mode ref"
			]
		];
		for (const [flag, value, remoteFlag] of localGatewayCredentials) if (value !== void 0) return rejectOnboardingOption(opts, runtime, `${flag} configures local gateway auth. Use ${remoteFlag} in remote mode.`);
	}
	if (opts.nonInteractive && opts.secretInputMode === "ref") {
		const gatewayCredentials = [
			[
				"--gateway-password",
				opts.gatewayPassword,
				"OPENCLAW_GATEWAY_PASSWORD"
			],
			[
				"--remote-token",
				opts.remoteToken,
				"OPENCLAW_GATEWAY_TOKEN"
			],
			[
				"--remote-password",
				opts.remotePassword,
				"OPENCLAW_GATEWAY_PASSWORD"
			]
		];
		for (const [flag, value, envName] of gatewayCredentials) {
			if (value === void 0) continue;
			const envValue = process.env[envName]?.trim();
			if (!envValue) return rejectOnboardingOption(opts, runtime, `${flag} requires ${envName} to be set when --secret-input-mode ref is used.`);
			if (value.trim() !== envValue) return rejectOnboardingOption(opts, runtime, `${flag} does not match ${envName}. Set the environment variable to the same value or omit the flag.`);
		}
	}
	const choiceValidations = [
		[
			"--gateway-bind",
			opts.gatewayBind,
			[
				"loopback",
				"tailnet",
				"lan",
				"auto",
				"custom"
			]
		],
		[
			"--gateway-auth",
			opts.gatewayAuth,
			["token", "password"]
		],
		[
			"--tailscale",
			opts.tailscale,
			[
				"off",
				"serve",
				"funnel"
			]
		],
		[
			"--custom-compatibility",
			opts.customCompatibility,
			[
				"openai",
				"openai-responses",
				"anthropic"
			]
		]
	];
	for (const [flag, value, allowed] of choiceValidations) if (value !== void 0 && !allowed.includes(value)) return rejectOnboardingOption(opts, runtime, `Invalid ${flag} ${JSON.stringify(value)}. Use ${allowed.map((choice) => JSON.stringify(choice)).join(", ")}.`);
	if (opts.flow !== void 0 && !isOnboardFlow(opts.flow)) return rejectOnboardingOption(opts, runtime, "Invalid --flow. Use \"quickstart\", \"advanced\", \"manual\", or \"import\".");
	if (opts.daemonRuntime !== void 0 && !isGatewayDaemonRuntime(opts.daemonRuntime)) return rejectOnboardingOption(opts, runtime, "Invalid --daemon-runtime. Use \"node\" or \"bun\".");
	if (opts.nodeManager !== void 0 && !isNodeManagerChoice(opts.nodeManager)) return rejectOnboardingOption(opts, runtime, "Invalid --node-manager. Use \"npm\", \"pnpm\", or \"bun\".");
	if (opts.gatewayPort !== void 0 && (!Number.isFinite(opts.gatewayPort) || opts.gatewayPort <= 0 || opts.gatewayPort > 65535)) return rejectOnboardingOption(opts, runtime, formatInvalidPortOption("--gateway-port"));
	if (opts.gatewayTokenRefEnv !== void 0) {
		const gatewayTokenRefEnv = opts.gatewayTokenRefEnv.trim();
		if (!isValidEnvSecretRefId(gatewayTokenRefEnv)) return rejectOnboardingOption(opts, runtime, "Invalid --gateway-token-ref-env. Use an environment variable name like OPENCLAW_GATEWAY_TOKEN.");
		if (opts.gatewayToken !== void 0) return rejectOnboardingOption(opts, runtime, "Use either --gateway-token or --gateway-token-ref-env, not both. Prefer --gateway-token-ref-env to avoid writing plaintext tokens.");
		if (!process.env[gatewayTokenRefEnv]?.trim()) return rejectOnboardingOption(opts, runtime, `Environment variable "${gatewayTokenRefEnv}" is missing or empty. Export it first, then rerun ${formatCliCommand("openclaw onboard")}.`);
	}
	if (opts.nonInteractive && opts.mode === "remote" && !opts.remoteUrl?.trim()) return rejectOnboardingOption(opts, runtime, `Missing --remote-url for remote mode. Example: ${formatCliCommand("openclaw onboard --non-interactive --accept-risk --mode remote --remote-url ws://127.0.0.1:3000")}.`);
	if (opts.nonInteractive && opts.mode === "remote" && opts.remoteUrl?.trim()) {
		const remoteUrlError = validateGatewayWebSocketUrl(opts.remoteUrl);
		if (remoteUrlError) return rejectOnboardingOption(opts, runtime, remoteUrlError);
	}
	if (opts.nonInteractive && (opts.flow === "import" || opts.importSource || opts.importSecrets) && !opts.importFrom?.trim()) return rejectOnboardingOption(opts, runtime, `--import-from is required for non-interactive migration import. Run ${formatCliCommand("openclaw migrate list")} to choose a provider.`);
	return true;
}
async function validateResetAuthChoice(params) {
	const inferredAuthChoice = params.opts.authChoice || params.opts.mode === "remote" || !params.opts.nonInteractive && !wantsClassicInteractiveSetup(params.opts) ? void 0 : inferAuthChoiceFromFlags(params.opts, {
		config: params.baseConfig,
		workspaceDir: params.workspaceDir,
		env: process.env
	});
	if (inferredAuthChoice && inferredAuthChoice.matches.length > 1) return rejectOnboardingOption(params.opts, params.runtime, [
		`Multiple ${params.opts.nonInteractive ? "API key" : "provider credential"} flags were provided for ${params.opts.nonInteractive ? "non-interactive" : "interactive"} setup.`,
		"Use a single provider flag or pass --auth-choice explicitly.",
		`Flags: ${inferredAuthChoice.matches.map((match) => match.label).join(", ")}`
	].join("\n"));
	if (!params.opts.nonInteractive && inferredAuthChoice) return true;
	const authChoice = params.opts.authChoice ?? inferredAuthChoice?.choice;
	if (!authChoice) return true;
	if (!new Set(formatAuthChoiceChoicesForCli({
		includeSkip: true,
		config: params.baseConfig,
		workspaceDir: params.workspaceDir,
		env: process.env
	}).split("|")).has(authChoice)) return rejectOnboardingOption(params.opts, params.runtime, `Auth choice "${authChoice}" was not matched to a provider setup flow. Run ${formatCliCommand("openclaw onboard")} to choose interactively.`);
	const providerAuthChoices = [...resolveManifestProviderAuthChoices({
		config: params.baseConfig,
		workspaceDir: params.workspaceDir,
		env: process.env,
		includeUntrustedWorkspacePlugins: false
	}), ...resolveProviderInstallCatalogEntries({
		config: params.baseConfig,
		workspaceDir: params.workspaceDir,
		env: process.env,
		includeUntrustedWorkspacePlugins: false
	})];
	const isGenericProviderChoice = GENERIC_PROVIDER_AUTH_CHOICES.includes(authChoice);
	const normalizedTokenProvider = normalizeTokenProviderInput(params.opts.tokenProvider);
	const inferredOptionKey = inferredAuthChoice?.matches[0]?.optionKey;
	const providerAuthChoice = isGenericProviderChoice ? providerAuthChoices.find((choice) => {
		const providerMatches = normalizedTokenProvider ? normalizeTokenProviderInput(choice.providerId) === normalizedTokenProvider || choice.providerAliases?.some((alias) => normalizeTokenProviderInput(alias) === normalizedTokenProvider) : inferredOptionKey !== void 0 && choice.optionKey === inferredOptionKey;
		const methodId = choice.methodId.toLowerCase();
		const supportsAuthKind = authChoice === "apiKey" ? methodId.includes("api") && methodId.includes("key") : authChoice === "setup-token" ? methodId === "setup-token" : methodId.includes("token");
		return providerMatches && supportsAuthKind;
	}) : providerAuthChoices.find((choice) => choice.choiceId === authChoice);
	if (params.opts.nonInteractive && isGenericProviderChoice && !normalizedTokenProvider && !inferredOptionKey) return rejectOnboardingOption(params.opts, params.runtime, `Auth choice "${authChoice}" requires --token-provider in non-interactive setup.`);
	if (params.opts.nonInteractive && (authChoice === "token" || authChoice === "setup-token") && !params.opts.token?.trim()) return rejectOnboardingOption(params.opts, params.runtime, `Auth choice "${authChoice}" requires --token in non-interactive setup.`);
	if (params.opts.nonInteractive && isGenericProviderChoice && !providerAuthChoice) return rejectOnboardingOption(params.opts, params.runtime, `Auth choice "${authChoice}" was not matched to provider "${params.opts.tokenProvider?.trim()}".`);
	if (!params.opts.nonInteractive || authChoice === "skip") return true;
	const target = resolveOnboardingSetupTarget(params.baseConfig, params.opts.agentName ? {
		name: params.opts.agentName,
		workspaceDir: params.workspaceDir
	} : void 0);
	if (authChoice === "custom-api-key") try {
		const custom = parseNonInteractiveCustomApiFlags({
			baseUrl: params.opts.customBaseUrl,
			modelId: params.opts.customModelId,
			compatibility: params.opts.customCompatibility,
			apiKey: void 0,
			providerId: params.opts.customProviderId,
			supportsImageInput: params.opts.customImageInput
		});
		const customProviderId = resolveCustomProviderId({
			config: params.baseConfig,
			baseUrl: custom.baseUrl,
			providerId: custom.providerId
		}).providerId;
		const customCredential = await resolveNonInteractiveApiKey({
			provider: customProviderId,
			cfg: params.baseConfig,
			flagValue: params.opts.customApiKey,
			flagName: "--custom-api-key",
			envVar: "CUSTOM_API_KEY",
			runtime: params.runtime,
			agentDir: target.agentDir,
			workspaceDir: params.workspaceDir,
			allowProfile: params.resetScope === "config",
			required: false,
			secretInputMode: params.opts.secretInputMode,
			json: params.opts.json
		});
		if (params.opts.customApiKey?.trim() && !customCredential) return false;
		applyCustomApiConfig({
			config: params.baseConfig,
			baseUrl: custom.baseUrl,
			modelId: custom.modelId,
			compatibility: custom.compatibility,
			apiKey: void 0,
			providerId: custom.providerId,
			supportsImageInput: custom.supportsImageInput
		});
	} catch (error) {
		const message = error instanceof CustomApiError && (error.code === "missing_required" || error.code === "invalid_compatibility") ? error.message : `Invalid custom provider config: ${formatErrorMessage(error)}`;
		return rejectOnboardingOption(params.opts, params.runtime, message);
	}
	if (authChoice !== "custom-api-key") {
		const runtimeMethod = (providerAuthChoice ? resolveProviderMatch(resolvePluginProviders({
			config: params.baseConfig,
			workspaceDir: params.workspaceDir,
			mode: "setup",
			includeUntrustedWorkspacePlugins: false,
			providerRefs: [providerAuthChoice.providerId],
			activate: true
		}), providerAuthChoice.providerId) : null)?.auth.find((method) => method.id === providerAuthChoice?.methodId || method.wizard?.choiceId === providerAuthChoice?.choiceId);
		if (!runtimeMethod?.runNonInteractive || !runtimeMethod.validateNonInteractive) {
			const reason = !runtimeMethod ? "provider unavailable" : !runtimeMethod.runNonInteractive ? "non-interactive setup unsupported" : "reset validation unavailable";
			return rejectOnboardingOption(params.opts, params.runtime, `Auth choice "${authChoice}" cannot be safely preflighted with --reset (${reason}). Choose a provider method that supports non-interactive reset validation, or run setup without --reset.`);
		}
		if (!await runtimeMethod.validateNonInteractive({
			authChoice,
			config: params.baseConfig,
			baseConfig: params.baseConfig,
			opts: params.opts,
			runtime: params.runtime,
			agentDir: target.agentDir,
			workspaceDir: params.workspaceDir,
			resolveApiKey: async (input) => await resolveNonInteractiveApiKey({
				...input,
				cfg: params.baseConfig,
				runtime: params.runtime,
				agentDir: target.agentDir,
				workspaceDir: params.workspaceDir,
				allowProfile: input.allowProfile === false ? false : params.resetScope === "config",
				secretInputMode: params.opts.secretInputMode,
				json: params.opts.json
			})
		})) return false;
	}
	return true;
}
function validateResetMigrationImport(params) {
	if (!params.opts.importFrom && !params.opts.importSource && !params.opts.importSecrets && params.opts.flow !== "import") return true;
	return rejectOnboardingOption(params.opts, params.runtime, "Migration import cannot be combined with --reset because provider input must be planned before any state is removed. Run the import without --reset.");
}
function validateResetNonInteractiveGateway(params) {
	if (!params.opts.nonInteractive || (params.opts.mode ?? "local") === "remote") return true;
	return Boolean(applyNonInteractiveGatewayConfig({
		nextConfig: params.baseConfig,
		opts: params.opts,
		runtime: params.runtime,
		defaultPort: resolveGatewayPort(params.baseConfig)
	}));
}
/**
* Interactive onboarding defaults to guided setup. Any explicit
* setup flag beyond this allowlist keeps the classic wizard — those flags are
* a public automation contract and guided setup does not honor them.
* Most false booleans mean "not passed" because the command layer normalizes
* them with Boolean(). False-valued explicit choices preserve undefined when
* omitted, so daemon, Tailscale-reset, and custom-model input overrides are
* special-cased. `--modern` never reaches this dispatch; the command layer
* routes it through the inference-gated OpenClaw.
*/
const GUIDED_SAFE_ONBOARD_KEYS = /* @__PURE__ */ new Set([
	"workspace",
	"acceptRisk",
	"reset",
	"resetScope",
	"nonInteractive",
	"agentName",
	"tui",
	"skipUi",
	"suppressGatewayTokenOutput"
]);
function wantsClassicInteractiveSetup(opts) {
	if (opts.classic === true) return true;
	if (opts.installDaemon !== void 0 || opts.customImageInput !== void 0) return true;
	for (const [key, value] of Object.entries(opts)) {
		if (GUIDED_SAFE_ONBOARD_KEYS.has(key) || key === "installDaemon") continue;
		if (value === void 0 || value === false) continue;
		return true;
	}
	return false;
}
/** Runs the onboard command after normalizing legacy flags and setup mode. */
async function setupWizardCommand(opts, runtime = defaultRuntime) {
	assertSupportedRuntime(runtime);
	const originalAuthChoice = opts.authChoice;
	const normalizedAuthChoice = normalizeLegacyOnboardAuthChoice(originalAuthChoice, { env: process.env });
	if (opts.nonInteractive && isDeprecatedAuthChoice(originalAuthChoice, { env: process.env })) {
		rejectOnboardingOption(opts, runtime, formatDeprecatedNonInteractiveAuthChoiceError(originalAuthChoice, { env: process.env }));
		return;
	}
	if (isDeprecatedAuthChoice(originalAuthChoice, { env: process.env })) runtime.log(resolveDeprecatedAuthChoiceReplacement(originalAuthChoice, { env: process.env }).message);
	const flow = opts.flow === "manual" ? "advanced" : opts.flow;
	const normalizedOpts = normalizedAuthChoice === opts.authChoice && flow === opts.flow ? opts : {
		...opts,
		authChoice: normalizedAuthChoice,
		flow
	};
	if (normalizedOpts.agentName !== void 0) {
		const { validateFirstOnboardingAgentName } = await import("./onboard-agent-BaPkGQp-.js");
		const error = validateFirstOnboardingAgentName(normalizedOpts.agentName);
		if (error) {
			rejectOnboardingOption(normalizedOpts, runtime, `Invalid --agent-name: ${error}`);
			return;
		}
	}
	if (!validatePreflightOptions(normalizedOpts, runtime)) return;
	if (normalizedOpts.classic && normalizedOpts.nonInteractive) {
		rejectOnboardingOption(normalizedOpts, runtime, "--classic cannot be combined with --non-interactive. Remove --non-interactive to open the classic wizard, or remove --classic for automated setup.");
		return;
	}
	if (normalizedOpts.tui && normalizedOpts.nonInteractive) {
		rejectOnboardingOption(normalizedOpts, runtime, "--tui cannot be combined with --non-interactive. Remove --tui for automation, or remove --non-interactive to open the terminal hatch.");
		return;
	}
	if (normalizedOpts.secretInputMode && normalizedOpts.secretInputMode !== "plaintext" && normalizedOpts.secretInputMode !== "ref") {
		rejectOnboardingOption(normalizedOpts, runtime, `Invalid --secret-input-mode. Use "plaintext" or "ref", or run ${formatCliCommand("openclaw onboard")} for the interactive setup.`);
		return;
	}
	if (normalizedOpts.resetScope && !VALID_RESET_SCOPES.has(normalizedOpts.resetScope)) {
		rejectOnboardingOption(normalizedOpts, runtime, `Invalid --reset-scope. Use "config", "config+creds+sessions", or "full". Run ${formatCliCommand("openclaw onboard --reset --reset-scope config")} for a config-only reset.`);
		return;
	}
	if (normalizedOpts.resetScope && !normalizedOpts.reset) {
		rejectOnboardingOption(normalizedOpts, runtime, `--reset-scope requires --reset. Re-run with ${formatCliCommand(`openclaw onboard --reset --reset-scope ${normalizedOpts.resetScope}`)}.`);
		return;
	}
	if (normalizedOpts.nonInteractive && normalizedOpts.acceptRisk !== true) {
		rejectOnboardingOption(normalizedOpts, runtime, [
			"Non-interactive setup requires explicit risk acknowledgement.",
			"Read: https://docs.openclaw.ai/security",
			`Re-run with: ${formatCliCommand("openclaw onboard --non-interactive --accept-risk ...")}`
		].join("\n"));
		return;
	}
	if (!normalizedOpts.nonInteractive && !hasInteractiveOnboardingTty()) {
		rejectOnboardingOption(normalizedOpts, runtime, t("wizard.guided.ttyRequired"));
		return;
	}
	if (process.platform === "win32") runtime.log([
		"Windows detected - OpenClaw runs great on WSL2!",
		"Native Windows might be trickier.",
		"Quick setup: wsl --install (one command, one reboot)",
		"Guide: https://docs.openclaw.ai/windows"
	].join("\n"));
	const runSetup = normalizedOpts.nonInteractive ? runNonInteractiveSetup : wantsClassicInteractiveSetup(normalizedOpts) ? runInteractiveSetup : runGuidedOnboarding;
	const runSetupAfterOptionalReset = async () => {
		if (normalizedOpts.reset) {
			const snapshot = await readConfigFileSnapshot();
			const baseConfig = snapshot.sourceConfig ?? (snapshot.valid ? snapshot.config : {});
			const resetScope = normalizedOpts.resetScope ?? "config+creds+sessions";
			const setupBaseConfig = {};
			const setupWorkspaceDir = resolveUserPath(normalizedOpts.workspace ?? DEFAULT_WORKSPACE);
			const configuredWorkspace = normalizedOpts.workspace ?? baseConfig.agents?.defaults?.workspace;
			if (resetScope === "full" && normalizedOpts.workspace === void 0 && snapshot.exists && !snapshot.valid && snapshot.readError !== void 0) {
				rejectOnboardingOption(normalizedOpts, runtime, "Cannot determine the configured workspace from an unreadable config. Pass --workspace with the workspace to remove, or use a narrower --reset-scope.");
				return;
			}
			if (resetScope === "full" && configuredWorkspace !== void 0 && (typeof configuredWorkspace !== "string" || !configuredWorkspace.trim())) {
				rejectOnboardingOption(normalizedOpts, runtime, "Configured workspace is invalid. Pass --workspace with the workspace to remove, or use a narrower --reset-scope.");
				return;
			}
			const workspaceDir = resolveUserPath(typeof configuredWorkspace === "string" && configuredWorkspace.trim() ? configuredWorkspace : DEFAULT_WORKSPACE);
			if (!await validateResetAuthChoice({
				opts: normalizedOpts,
				runtime,
				baseConfig: setupBaseConfig,
				workspaceDir: setupWorkspaceDir,
				resetScope
			})) return;
			if (!validateResetNonInteractiveGateway({
				opts: normalizedOpts,
				runtime,
				baseConfig: setupBaseConfig
			})) return;
			if (!validateResetMigrationImport({
				opts: normalizedOpts,
				runtime
			})) return;
			await handleReset(resetScope, workspaceDir, runtime);
		}
		await runSetup(normalizedOpts, runtime);
	};
	await withSetupMigrationTargetLock(resolveStateDir(), runSetupAfterOptionalReset);
}
//#endregion
export { setupWizardCommand };