From 58d5b39c9aa635b08ce08d03ef0f17ec0908e1f2 Mon Sep 17 00:00:00 2001 From: Shakker Date: Tue, 3 Feb 2026 06:11:11 +0000 Subject: [PATCH] Onboarding: keep TUI flow exclusive --- CHANGELOG.md | 2 ++ src/tui/tui.ts | 6 ++++++ src/wizard/onboarding.finalize.ts | 22 +++++++++------------- src/wizard/onboarding.ts | 5 ++++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c3e3685b..1a8f55859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Docs: https://docs.openclaw.ai ### Fixes +- Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed). +- TUI: block onboarding output while TUI is active and restore terminal state on exit. - Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode. - fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001) - fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz) diff --git a/src/tui/tui.ts b/src/tui/tui.ts index a2250746e..fd693b4db 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -662,4 +662,10 @@ export async function runTui(opts: TuiOptions) { updateFooter(); tui.start(); client.start(); + await new Promise((resolve) => { + const finish = () => resolve(); + process.once("exit", finish); + process.once("SIGINT", finish); + process.once("SIGTERM", finish); + }); } diff --git a/src/wizard/onboarding.finalize.ts b/src/wizard/onboarding.finalize.ts index 70b7f8430..9ef3024b4 100644 --- a/src/wizard/onboarding.finalize.ts +++ b/src/wizard/onboarding.finalize.ts @@ -21,7 +21,6 @@ import { detectBrowserOpenSupport, formatControlUiSshHint, openUrl, - openUrlInBackground, probeGatewayReachable, waitForGatewayReachable, resolveControlUiLinks, @@ -29,6 +28,7 @@ import { import { resolveGatewayService } from "../daemon/service.js"; import { isSystemdUserServiceAvailable } from "../daemon/systemd.js"; import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js"; +import { restoreTerminalState } from "../terminal/restore.js"; import { runTui } from "../tui/tui.js"; import { resolveUserPath } from "../utils.js"; @@ -43,7 +43,9 @@ type FinalizeOnboardingOptions = { runtime: RuntimeEnv; }; -export async function finalizeOnboardingWizard(options: FinalizeOnboardingOptions) { +export async function finalizeOnboardingWizard( + options: FinalizeOnboardingOptions, +): Promise<{ launchedTui: boolean }> { const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } = options; const withWizardProgress = async ( @@ -286,6 +288,7 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption let controlUiOpenHint: string | undefined; let seededInBackground = false; let hatchChoice: "tui" | "web" | "later" | null = null; + let launchedTui = false; if (!opts.skipUi && gatewayProbe.ok) { if (hasBootstrap) { @@ -321,6 +324,7 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption }); if (hatchChoice === "tui") { + restoreTerminalState("pre-onboarding tui"); await runTui({ url: links.wsUrl, token: settings.authMode === "token" ? settings.gatewayToken : undefined, @@ -329,17 +333,7 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption deliver: false, message: hasBootstrap ? "Wake up, my friend!" : undefined, }); - if (settings.authMode === "token" && settings.gatewayToken) { - seededInBackground = await openUrlInBackground(authedUrl); - } - if (seededInBackground) { - await prompter.note( - `Web UI seeded in the background. Open later with: ${formatCliCommand( - "openclaw dashboard --no-open", - )}`, - "Web UI", - ); - } + launchedTui = true; } else if (hatchChoice === "web") { const browserSupport = await detectBrowserOpenSupport(); if (browserSupport.ok) { @@ -471,4 +465,6 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption ? "Onboarding complete. Web UI seeded in the background; open it anytime with the tokenized link above." : "Onboarding complete. Use the tokenized dashboard link above to control OpenClaw.", ); + + return { launchedTui }; } diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index de1c6c36f..544e455ec 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -455,7 +455,7 @@ export async function runOnboardingWizard( nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode }); await writeConfigFile(nextConfig); - await finalizeOnboardingWizard({ + const { launchedTui } = await finalizeOnboardingWizard({ flow, opts, baseConfig, @@ -465,6 +465,9 @@ export async function runOnboardingWizard( prompter, runtime, }); + if (launchedTui) { + return; + } const installShell = await prompter.confirm({ message: "Install shell completion script?",