openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
588 lines (587 loc) • 26.9 kB
JavaScript
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { n as resolveCliName } from "./cli-name-CAJoj2J5.js";
import { t as formatCliCommand } from "./command-format-CKGmlpAQ.js";
import { t as restoreTerminalState } from "./restore-BWpek1U9.js";
import { t as attachChildProcessBridge } from "./child-process-bridge-Vp-FhPhG.js";
import { p as resolveUserPath, u as pathExists } from "./utils-CCC-BEJH.js";
import { n as buildGatewayInstallPlan, r as gatewayInstallErrorHint } from "./auth-install-policy-O7nc7i9I.js";
import { o as isSystemdUserServiceAvailable } from "./systemd-BXTM6gEb.js";
import { s as resolveDefaultAgentDir } from "./agent-scope-config-CgCYpZfK.js";
import { n as GATEWAY_DAEMON_RUNTIME_OPTIONS, t as DEFAULT_GATEWAY_DAEMON_RUNTIME } from "./daemon-runtime-C76za6vm.js";
import { t as resolveGatewayInstallToken } from "./gateway-install-token-jVqLCOms.js";
import { i as resolveGatewayService, t as describeGatewayServiceRestart } from "./service-MPqQWJ3s.js";
import { t as resolveControlUiLinks } from "./control-ui-links-HaNDPHaQ.js";
import { n as DEFAULT_BOOTSTRAP_FILENAME } from "./workspace-B0bmoo98.js";
import { t as describeCodexNativeWebSearch } from "./codex-native-web-search.shared-xyjcKUq2.js";
import { i as hasAuthProfileForProvider } from "./model-config.helpers-DcS9-P8x.js";
import { n as listConfiguredWebSearchProviders } from "./runtime-B83FaW0j.js";
import { a as installCompletion, i as formatCompletionReloadCommand, l as resolveCompletionProfilePath } from "./completion-runtime-CTM38KC1.js";
import { r as ensureCompletionCacheExists, t as checkShellCompletionStatus } from "./doctor-completion-BndH-Q_9.js";
import { t as ensureControlUiAssetsBuilt } from "./control-ui-assets-OWiFO3R_.js";
import { n as t } from "./i18n-7g_f4oaD.js";
import { n as openUrl, t as detectBrowserOpenSupport } from "./browser-open-dLnrbzcu.js";
import { i as formatControlUiSshHint, m as waitForGatewayReachable, u as probeGatewayReachable } from "./onboard-helpers-DXyUiHQ-.js";
import { t as resolveSetupSecretInputString } from "./setup.secret-input-BOdMUWYa.js";
import { n as formatHealthCheckFailure } from "./health-format-ebE8IYQV.js";
import { i as healthCommand } from "./health-DeCbGguB.js";
import { n as TUI_SETUP_AUTH_SOURCE_ENV, t as TUI_SETUP_AUTH_SOURCE_CONFIG } from "./setup-launch-env-DehdAyoV.js";
import path from "node:path";
import fs from "node:fs/promises";
import os from "node:os";
import { spawn } from "node:child_process";
//#region src/tui/tui-launch.ts
function appendOption(args, flag, value) {
if (value === void 0) return;
args.push(flag, String(value));
}
function filterTuiExecArgv(execArgv) {
const filtered = [];
for (let index = 0; index < execArgv.length; index += 1) {
const arg = execArgv[index] ?? "";
if (arg === "--inspect" || arg.startsWith("--inspect=") || arg === "--inspect-brk" || arg.startsWith("--inspect-brk=") || arg === "--inspect-wait" || arg.startsWith("--inspect-wait=")) {
const next = execArgv[index + 1];
if (!arg.includes("=") && typeof next === "string" && !next.startsWith("-")) index += 1;
continue;
}
if (arg === "--inspect-port") {
const next = execArgv[index + 1];
if (typeof next === "string" && !next.startsWith("-")) index += 1;
continue;
}
if (arg.startsWith("--inspect-port=")) continue;
filtered.push(arg);
}
return filtered;
}
function buildCurrentCliEntryArgs() {
const entry = process.argv[1]?.trim();
if (!entry) throw new Error("unable to relaunch TUI: current CLI entry path is unavailable");
return path.isAbsolute(entry) ? [entry] : [];
}
function buildTuiCliArgs(opts) {
const args = [
...filterTuiExecArgv(process.execArgv),
...buildCurrentCliEntryArgs(),
"tui"
];
if (opts.local) args.push("--local");
appendOption(args, "--url", opts.url);
appendOption(args, "--token", opts.token);
appendOption(args, "--password", opts.password);
appendOption(args, "--session", opts.session);
appendOption(args, "--thinking", opts.thinking);
appendOption(args, "--message", opts.message);
appendOption(args, "--timeout-ms", opts.timeoutMs);
appendOption(args, "--history-limit", opts.historyLimit);
if (opts.deliver) args.push("--deliver");
return args;
}
/** Launches a child TUI process with inherited stdio and setup-specific environment hints. */
async function launchTuiCli(opts, launchOptions = {}) {
const args = buildTuiCliArgs(opts);
const env = launchOptions.gatewayUrl || launchOptions.authSource ? {
...process.env,
...launchOptions.gatewayUrl ? { OPENCLAW_GATEWAY_URL: launchOptions.gatewayUrl } : {},
...launchOptions.authSource === "config" ? { [TUI_SETUP_AUTH_SOURCE_ENV]: TUI_SETUP_AUTH_SOURCE_CONFIG } : {}
} : process.env;
const stdinWasPaused = typeof process.stdin.isPaused === "function" ? process.stdin.isPaused() : false;
process.stdin.pause();
await new Promise((resolve, reject) => {
const child = spawn(process.execPath, args, {
stdio: "inherit",
env
});
const { detach } = attachChildProcessBridge(child);
child.once("error", (error) => {
detach();
reject(/* @__PURE__ */ new Error(`failed to launch TUI: ${formatErrorMessage(error)}`));
});
child.once("exit", (code, signal) => {
detach();
if (signal) {
reject(/* @__PURE__ */ new Error(`TUI exited from signal ${signal}`));
return;
}
if ((code ?? 0) !== 0) {
reject(/* @__PURE__ */ new Error(`TUI exited with code ${code ?? 1}`));
return;
}
resolve();
});
}).finally(() => {
if (!stdinWasPaused) process.stdin.resume();
});
}
//#endregion
//#region src/wizard/setup.completion.ts
async function resolveProfileHint(shell) {
const home = process.env.HOME || os.homedir();
if (shell === "zsh") return "~/.zshrc";
if (shell === "bash") return await pathExists(path.join(home, ".bashrc")) ? "~/.bashrc" : "~/.bash_profile";
if (shell === "fish") return "~/.config/fish/config.fish";
return resolveCompletionProfilePath("powershell");
}
function formatReloadHint(shell, profileHint) {
if (shell === "powershell") return t("wizard.completion.reloadPowerShell", { command: formatCompletionReloadCommand("powershell", profileHint) });
return t("wizard.completion.reloadShell", { profile: profileHint });
}
async function setupWizardShellCompletion(params) {
const deps = {
resolveCliName,
checkShellCompletionStatus,
ensureCompletionCacheExists,
installCompletion,
...params.deps
};
const cliName = deps.resolveCliName();
const completionStatus = await deps.checkShellCompletionStatus(cliName);
if (completionStatus.usesSlowPattern) {
if (await deps.ensureCompletionCacheExists(cliName)) await deps.installCompletion(completionStatus.shell, true, cliName);
return;
}
if (completionStatus.profileInstalled && !completionStatus.cacheExists) {
await deps.ensureCompletionCacheExists(cliName);
return;
}
if (!completionStatus.profileInstalled) {
if (!(params.flow === "quickstart" ? true : await params.prompter.confirm({
message: t("wizard.completion.enable", {
shell: completionStatus.shell,
cli: cliName
}),
initialValue: true
}))) return;
if (!await deps.ensureCompletionCacheExists(cliName)) {
await params.prompter.note(t("wizard.completion.cacheFailed", { command: `${cliName} completion --install` }), t("wizard.completion.title"));
return;
}
await deps.installCompletion(completionStatus.shell, true, cliName);
const profileHint = await resolveProfileHint(completionStatus.shell);
await params.prompter.note(t("wizard.completion.installed", { reloadHint: formatReloadHint(completionStatus.shell, profileHint) }), t("wizard.completion.title"));
}
}
//#endregion
//#region src/wizard/setup.finalize.ts
let onboardSearchModulePromise;
const HATCH_TUI_TIMEOUT_MS = 300 * 1e3;
async function showControlUiDashboardNote(params) {
let opened = false;
let openHint;
if ((await detectBrowserOpenSupport()).ok) {
opened = await openUrl(params.authedUrl);
if (!opened) openHint = formatControlUiSshHint({
port: params.settings.port,
basePath: params.controlUiBasePath,
token: params.hintToken
});
} else openHint = formatControlUiSshHint({
port: params.settings.port,
basePath: params.controlUiBasePath,
token: params.hintToken
});
await params.prompter.note([
t("wizard.finalize.dashboardLinkWithToken", { url: params.authedUrl }),
opened ? t("wizard.finalize.dashboardOpened") : t("wizard.finalize.dashboardCopyPaste"),
openHint
].filter(Boolean).join("\n"), t("wizard.finalize.dashboardReady"));
return { opened };
}
function getLocalizedGatewayDaemonRuntimeOptions() {
return GATEWAY_DAEMON_RUNTIME_OPTIONS.map((option) => ({
hint: option.value === "node" ? t("wizard.finalize.daemonRuntimeNodeHint") : option.hint ?? void 0,
label: option.value === "node" ? t("wizard.finalize.daemonRuntimeNode") : option.label,
value: option.value
}));
}
function loadOnboardSearchModule() {
onboardSearchModulePromise ??= import("./onboard-search-BaCIbWUO.js");
return onboardSearchModulePromise;
}
async function finalizeSetupWizard(options) {
const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } = options;
const suppressGatewayTokenOutput = opts.suppressGatewayTokenOutput === true;
let gatewayProbe = { ok: true };
let resolvedGatewayPassword = "";
const withWizardProgress = async (label, optionsLocal, work) => {
const progress = prompter.progress(label);
try {
return await work(progress);
} finally {
progress.stop(typeof optionsLocal.doneMessage === "function" ? optionsLocal.doneMessage() : optionsLocal.doneMessage);
}
};
const systemdAvailable = process.platform === "linux" ? await isSystemdUserServiceAvailable() : true;
if (process.platform === "linux" && !systemdAvailable) await prompter.note(t("wizard.finalize.systemdUnavailable"), "Systemd");
if (process.platform === "linux" && systemdAvailable) {
const { ensureSystemdUserLingerInteractive } = await import("./systemd-linger-CJppyf46.js");
await ensureSystemdUserLingerInteractive({
runtime,
prompter: {
confirm: prompter.confirm,
note: prompter.note
},
reason: t("wizard.finalize.systemdLingerReason"),
requireConfirm: false
});
}
const explicitInstallDaemon = typeof opts.installDaemon === "boolean" ? opts.installDaemon : void 0;
let installDaemon;
if (explicitInstallDaemon !== void 0) installDaemon = explicitInstallDaemon;
else if (process.platform === "linux" && !systemdAvailable) installDaemon = false;
else if (flow === "quickstart") installDaemon = true;
else installDaemon = await prompter.confirm({
message: t("wizard.finalize.installGateway"),
initialValue: true
});
if (process.platform === "linux" && !systemdAvailable && installDaemon) {
await prompter.note(t("wizard.finalize.systemdInstallSkipped"), t("wizard.finalize.gatewayService"));
installDaemon = false;
}
if (installDaemon) {
const daemonRuntime = flow === "quickstart" ? DEFAULT_GATEWAY_DAEMON_RUNTIME : await prompter.select({
message: t("wizard.finalize.daemonRuntime"),
options: getLocalizedGatewayDaemonRuntimeOptions(),
initialValue: opts.daemonRuntime ?? "node"
});
if (flow === "quickstart") await prompter.note(t("wizard.finalize.quickstartNodeRuntime"), t("wizard.finalize.daemonRuntime"));
const service = resolveGatewayService();
const loaded = await service.isLoaded({ env: process.env });
let restartWasScheduled = false;
if (loaded) {
const action = await prompter.select({
message: t("wizard.finalize.alreadyInstalled"),
options: [
{
value: "restart",
label: t("wizard.finalize.restart")
},
{
value: "reinstall",
label: t("wizard.finalize.reinstall")
},
{
value: "skip",
label: t("common.skip")
}
]
});
if (action === "restart") {
let restartDoneMessage = t("wizard.finalize.gatewayServiceRestarted");
await withWizardProgress(t("wizard.finalize.gatewayService"), { doneMessage: () => restartDoneMessage }, async (progress) => {
progress.update(t("wizard.finalize.gatewayServiceRestarting"));
const restartStatus = describeGatewayServiceRestart("Gateway", await service.restart({
env: process.env,
stdout: process.stdout
}));
restartDoneMessage = restartStatus.scheduled ? t("wizard.finalize.gatewayServiceRestartScheduled") : t("wizard.finalize.gatewayServiceRestarted");
restartWasScheduled = restartStatus.scheduled;
});
} else if (action === "reinstall") await withWizardProgress(t("wizard.finalize.gatewayService"), { doneMessage: t("wizard.finalize.gatewayServiceUninstalled") }, async (progress) => {
progress.update(t("wizard.finalize.gatewayServiceUninstalling"));
await service.uninstall({
env: process.env,
stdout: process.stdout
});
});
}
if (!loaded || !restartWasScheduled && loaded && !await service.isLoaded({ env: process.env })) {
const progress = prompter.progress(t("wizard.finalize.gatewayService"));
let installError = null;
try {
progress.update(t("wizard.finalize.gatewayServicePreparing"));
const tokenResolution = await resolveGatewayInstallToken({
config: nextConfig,
env: process.env
});
for (const warning of tokenResolution.warnings) await prompter.note(warning, "Gateway service");
if (tokenResolution.unavailableReason) installError = [
t("wizard.finalize.gatewayInstallBlocked"),
tokenResolution.unavailableReason,
t("wizard.finalize.gatewayInstallFixAuth")
].join(" ");
else {
const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
env: process.env,
port: settings.port,
runtime: daemonRuntime,
warn: (message, title) => {
prompter.note(message, title);
},
config: nextConfig
});
progress.update(t("wizard.finalize.gatewayServiceInstalling"));
await service.install({
env: process.env,
stdout: process.stdout,
programArguments,
workingDirectory,
environment
});
}
} catch (err) {
installError = formatErrorMessage(err);
} finally {
progress.stop(installError ? t("wizard.finalize.gatewayServiceInstallFailed") : t("wizard.finalize.gatewayServiceInstalled"));
}
if (installError) {
await prompter.note(t("wizard.finalize.gatewayServiceInstallFailedWithError", { error: installError }), "Gateway");
await prompter.note(gatewayInstallErrorHint(), "Gateway");
}
}
}
if (settings.authMode === "password") try {
resolvedGatewayPassword = await resolveSetupSecretInputString({
config: nextConfig,
value: nextConfig.gateway?.auth?.password,
path: "gateway.auth.password",
env: process.env
}) ?? "";
} catch (error) {
await prompter.note([t("wizard.finalize.secretRefAuthFailed", { field: "gateway.auth.password" }), formatErrorMessage(error)].join("\n"), t("wizard.gateway.auth"));
}
if (!opts.skipHealth) {
const probeLinks = resolveControlUiLinks({
bind: nextConfig.gateway?.bind ?? "loopback",
port: settings.port,
customBindHost: nextConfig.gateway?.customBindHost,
basePath: void 0,
tlsEnabled: nextConfig.gateway?.tls?.enabled === true
});
gatewayProbe = await waitForGatewayReachable({
url: probeLinks.wsUrl,
token: settings.authMode === "token" ? settings.gatewayToken : void 0,
password: settings.authMode === "password" ? resolvedGatewayPassword : void 0,
deadlineMs: 15e3
});
if (gatewayProbe.ok) try {
await healthCommand({
json: false,
timeoutMs: 1e4,
config: settings.authMode === "token" && settings.gatewayToken ? {
...nextConfig,
gateway: {
...nextConfig.gateway,
auth: {
...nextConfig.gateway?.auth,
mode: "token",
token: settings.gatewayToken
}
}
} : nextConfig,
token: settings.authMode === "token" ? settings.gatewayToken : void 0,
password: settings.authMode === "password" ? resolvedGatewayPassword : void 0
}, runtime);
} catch (err) {
runtime.error(formatHealthCheckFailure(err));
await prompter.note([
t("common.docs"),
"https://docs.openclaw.ai/gateway/health",
"https://docs.openclaw.ai/gateway/troubleshooting"
].join("\n"), t("wizard.finalize.healthCheckHelp"));
}
else if (installDaemon) {
runtime.error(formatHealthCheckFailure(new Error(gatewayProbe.detail ?? `gateway did not become reachable at ${probeLinks.wsUrl}`)));
await prompter.note([
t("common.docs"),
"https://docs.openclaw.ai/gateway/health",
"https://docs.openclaw.ai/gateway/troubleshooting"
].join("\n"), t("wizard.finalize.healthCheckHelp"));
} else await prompter.note([
t("wizard.finalize.gatewayNotDetected"),
t("wizard.finalize.noBackgroundGatewayExpected"),
t("wizard.finalize.startGatewayNow", { command: formatCliCommand("openclaw gateway run") }),
t("wizard.finalize.rerunInstallDaemon", { command: formatCliCommand("openclaw onboard --install-daemon") }),
t("wizard.finalize.skipHealthNextTime", { command: formatCliCommand("openclaw onboard --skip-health") })
].join("\n"), "Gateway");
}
const controlUiEnabled = nextConfig.gateway?.controlUi?.enabled ?? baseConfig.gateway?.controlUi?.enabled ?? true;
if (!opts.skipUi && controlUiEnabled) {
const controlUiAssets = await ensureControlUiAssetsBuilt(runtime);
if (!controlUiAssets.ok && controlUiAssets.message) runtime.error(controlUiAssets.message);
}
await prompter.note([
t("wizard.finalize.addNodes"),
`- ${t("wizard.finalize.nodeMac")}`,
`- ${t("wizard.finalize.nodeIos")}`,
`- ${t("wizard.finalize.nodeAndroid")}`
].join("\n"), t("wizard.finalize.optionalApps"));
const controlUiBasePath = nextConfig.gateway?.controlUi?.basePath ?? baseConfig.gateway?.controlUi?.basePath;
const links = resolveControlUiLinks({
bind: settings.bind,
port: settings.port,
customBindHost: settings.customBindHost,
basePath: controlUiBasePath,
tlsEnabled: nextConfig.gateway?.tls?.enabled === true
});
const authedUrl = settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput ? `${links.httpUrl}#token=${encodeURIComponent(settings.gatewayToken)}` : links.httpUrl;
if (opts.skipHealth || !gatewayProbe.ok) gatewayProbe = await probeGatewayReachable({
url: links.wsUrl,
token: settings.authMode === "token" ? settings.gatewayToken : void 0,
password: settings.authMode === "password" ? resolvedGatewayPassword : ""
});
const gatewayStatusLine = gatewayProbe.ok ? t("wizard.finalize.gatewayReachable") : t("wizard.finalize.gatewayNotDetectedStatus", { detail: gatewayProbe.detail ? ` (${gatewayProbe.detail})` : "" });
const bootstrapPath = path.join(resolveUserPath(options.workspaceDir), DEFAULT_BOOTSTRAP_FILENAME);
const hasBootstrap = await fs.access(bootstrapPath).then(() => true).catch(() => false);
await prompter.note([
t("wizard.finalize.webUiUrl", { url: links.httpUrl }),
settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput ? t("wizard.finalize.webUiWithTokenUrl", { url: authedUrl }) : void 0,
t("wizard.finalize.gatewayWsUrl", { url: links.wsUrl }),
gatewayStatusLine,
t("wizard.finalize.controlUiDocs")
].filter(Boolean).join("\n"), "Control UI");
let controlUiOpened = false;
let hatchChoice = null;
let launchedTui = false;
if (!opts.skipUi) {
if (hasBootstrap) await prompter.note([
t("wizard.finalize.workspaceReady"),
t("wizard.finalize.firstTerminalChat"),
t("wizard.finalize.editBootstrap")
].join("\n"), t("wizard.finalize.hatchYourAgent"));
if (gatewayProbe.ok) {
const tokenNotes = [
t("wizard.finalize.gatewayTokenShared"),
t("wizard.finalize.gatewayTokenStored"),
t("wizard.finalize.gatewayTokenView", { command: formatCliCommand("openclaw config get gateway.auth.token") }),
t("wizard.finalize.gatewayTokenGenerate", { command: formatCliCommand("openclaw doctor --generate-gateway-token") }),
suppressGatewayTokenOutput ? void 0 : t("wizard.finalize.dashboardTokenMemory"),
t("wizard.finalize.dashboardOpenAnytime", { command: formatCliCommand("openclaw dashboard --no-open") }),
suppressGatewayTokenOutput ? void 0 : t("wizard.finalize.dashboardTokenPrompt")
].filter(Boolean);
await prompter.note(tokenNotes.join("\n"), "Token");
}
const hatchOptions = [
{
value: "tui",
label: t("wizard.finalize.terminalHatch")
},
...gatewayProbe.ok ? [{
value: "web",
label: t("wizard.finalize.browserHatch")
}] : [],
{
value: "later",
label: t("wizard.finalize.hatchLater")
}
];
hatchChoice = await prompter.select({
message: t("wizard.finalize.hatchPrompt"),
options: hatchOptions,
initialValue: "tui"
});
if (hatchChoice === "tui") {
restoreTerminalState("pre-setup tui", { resumeStdinIfPaused: true });
try {
await launchTuiCli({
local: true,
deliver: false,
message: hasBootstrap ? t("wizard.finalize.bootstrapHatchMessage") : void 0,
timeoutMs: HATCH_TUI_TIMEOUT_MS
});
} finally {
restoreTerminalState("post-setup tui", { resumeStdinIfPaused: true });
}
launchedTui = true;
} else if (hatchChoice === "web") controlUiOpened = (await showControlUiDashboardNote({
prompter,
settings,
authedUrl,
controlUiBasePath,
hintToken: settings.authMode === "token" && !suppressGatewayTokenOutput ? settings.gatewayToken : void 0
})).opened;
else await prompter.note(t("wizard.finalize.dashboardWhenReady", { command: formatCliCommand("openclaw dashboard --no-open") }), t("wizard.finalize.laterTitle"));
} else if (opts.skipUi) await prompter.note(t("wizard.finalize.skipControlUi"), t("wizard.finalize.controlUiTitle"));
await prompter.note([t("wizard.finalize.backupWorkspace"), t("wizard.finalize.workspaceDocs")].join("\n"), t("wizard.finalize.workspaceBackupTitle"));
await prompter.note(t("wizard.finalize.securityReminder"), t("wizard.security.title"));
await setupWizardShellCompletion({
flow,
prompter
});
if (!opts.skipUi && gatewayProbe.ok && settings.authMode === "token" && Boolean(settings.gatewayToken) && !suppressGatewayTokenOutput && hatchChoice === null) controlUiOpened = (await showControlUiDashboardNote({
prompter,
settings,
authedUrl,
controlUiBasePath,
hintToken: settings.gatewayToken
})).opened;
const codexNativeSummary = describeCodexNativeWebSearch(nextConfig);
const webSearchProvider = nextConfig.tools?.web?.search?.provider;
const webSearchEnabled = nextConfig.tools?.web?.search?.enabled;
const configuredSearchProviders = listConfiguredWebSearchProviders({ config: nextConfig });
if (webSearchProvider) {
const { resolveExistingKey, hasExistingKey, hasKeyInEnv } = await loadOnboardSearchModule();
const entry = configuredSearchProviders.find((e) => e.id === webSearchProvider);
const label = entry?.label ?? webSearchProvider;
const storedKey = entry ? resolveExistingKey(nextConfig, webSearchProvider) : void 0;
const keyConfigured = entry ? hasExistingKey(nextConfig, webSearchProvider) : false;
const envAvailable = entry ? hasKeyInEnv(entry) : false;
const hasKey = keyConfigured || envAvailable;
const agentDir = resolveDefaultAgentDir(nextConfig);
const authProviderId = entry?.authProviderId?.trim();
const authProviderLabel = authProviderId === "xai" ? "xAI" : authProviderId;
const providerAuthProfileAvailable = authProviderId ? hasAuthProfileForProvider({
provider: authProviderId,
agentDir
}) : false;
const oauthAuthProfileAvailable = authProviderId && providerAuthProfileAvailable ? hasAuthProfileForProvider({
provider: authProviderId,
agentDir,
type: "oauth"
}) : false;
const hasCredential = hasKey || providerAuthProfileAvailable;
const keySource = storedKey ? t("wizard.finalize.webSearchKeyStored") : keyConfigured ? t("wizard.finalize.webSearchKeyRef") : envAvailable ? t("wizard.finalize.webSearchKeyEnv", { env: entry?.envVars.join(" / ") ?? "" }) : oauthAuthProfileAvailable && authProviderLabel ? t("wizard.finalize.webSearchOAuthProfile", { provider: authProviderLabel }) : providerAuthProfileAvailable && authProviderLabel ? t("wizard.finalize.webSearchAuthProfile", { provider: authProviderLabel }) : void 0;
if (!entry) await prompter.note([
t("wizard.finalize.webSearchProviderUnavailable", { provider: label }),
t("wizard.finalize.webSearchUnavailableAction"),
` ${formatCliCommand("openclaw configure --section web")}`,
"",
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
else if (webSearchEnabled !== false && hasCredential) await prompter.note([
t("wizard.finalize.webSearchEnabled"),
"",
t("wizard.finalize.webSearchProvider", { provider: label }),
...keySource ? [keySource] : [],
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
else if (!hasCredential) await prompter.note([
t("wizard.finalize.webSearchNoKey", { provider: label }),
t("wizard.finalize.webSearchNeedsKey"),
` ${formatCliCommand("openclaw configure --section web")}`,
"",
t("wizard.finalize.webSearchGetKey", { url: entry?.signupUrl ?? "https://docs.openclaw.ai/tools/web" }),
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
else await prompter.note([
t("wizard.finalize.webSearchDisabled", { provider: label }),
t("wizard.finalize.webSearchReenable", { command: formatCliCommand("openclaw configure --section web") }),
"",
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
} else {
const { hasExistingKey, hasKeyInEnv } = await loadOnboardSearchModule();
const legacyDetected = configuredSearchProviders.find((e) => hasExistingKey(nextConfig, e.id) || hasKeyInEnv(e));
if (legacyDetected) await prompter.note([t("wizard.finalize.webSearchAutoDetected", { provider: legacyDetected.label }), t("wizard.finalize.webDocs")].join("\n"), t("wizard.finalize.webSearchTitle"));
else if (codexNativeSummary) await prompter.note([
t("wizard.finalize.managedWebSearchSkipped"),
codexNativeSummary,
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
else await prompter.note([
t("wizard.finalize.webSearchSkipped"),
` ${formatCliCommand("openclaw configure --section web")}`,
"",
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.webSearchTitle"));
}
if (codexNativeSummary) await prompter.note([
codexNativeSummary,
t("wizard.finalize.codexNativeSearchOnly"),
t("wizard.finalize.webDocs")
].join("\n"), t("wizard.finalize.codexNativeSearchTitle"));
await prompter.note(t("wizard.finalize.whatNow"), t("wizard.finalize.whatNowTitle"));
await prompter.outro(controlUiOpened ? t("wizard.finalize.outroDashboardOpened") : t("wizard.finalize.outroDashboardLink"));
return { launchedTui };
}
//#endregion
export { finalizeSetupWizard };