fix: always offer TUI hatch

This commit is contained in:
Peter Steinberger
2026-01-23 09:04:04 +00:00
parent 8aadcaa1bd
commit 03e8b7c4ba
2 changed files with 121 additions and 82 deletions

View File

@@ -299,6 +299,8 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption
].join("\n"), ].join("\n"),
"Start TUI (best option!)", "Start TUI (best option!)",
); );
}
await prompter.note( await prompter.note(
[ [
"Gateway token: shared auth for the Gateway + Control UI.", "Gateway token: shared auth for the Gateway + Control UI.",
@@ -326,7 +328,7 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption
password: settings.authMode === "password" ? nextConfig.gateway?.auth?.password : "", password: settings.authMode === "password" ? nextConfig.gateway?.auth?.password : "",
// Safety: onboarding TUI should not auto-deliver to lastProvider/lastTo. // Safety: onboarding TUI should not auto-deliver to lastProvider/lastTo.
deliver: false, deliver: false,
message: "Wake up, my friend!", message: hasBootstrap ? "Wake up, my friend!" : undefined,
}); });
if (settings.authMode === "token" && settings.gatewayToken) { if (settings.authMode === "token" && settings.gatewayToken) {
seededInBackground = await openUrlInBackground(authedUrl); seededInBackground = await openUrlInBackground(authedUrl);
@@ -375,24 +377,6 @@ export async function finalizeOnboardingWizard(options: FinalizeOnboardingOption
"Later", "Later",
); );
} }
} else {
const browserSupport = await detectBrowserOpenSupport();
if (!browserSupport.ok) {
await prompter.note(
formatControlUiSshHint({
port: settings.port,
basePath: controlUiBasePath,
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
}),
"Open Control UI",
);
} else {
await prompter.note(
"Opening Control UI automatically after onboarding (no extra prompts).",
"Open Control UI",
);
}
}
} else if (opts.skipUi) { } else if (opts.skipUi) {
await prompter.note("Skipping Control UI/TUI prompts.", "Control UI"); await prompter.note("Skipping Control UI/TUI prompts.", "Control UI");
} }

View File

@@ -227,6 +227,61 @@ describe("runOnboardingWizard", () => {
await fs.rm(workspaceDir, { recursive: true, force: true }); await fs.rm(workspaceDir, { recursive: true, force: true });
}); });
it("offers TUI hatch even without BOOTSTRAP.md", async () => {
runTui.mockClear();
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-onboard-"));
const select: WizardPrompter["select"] = vi.fn(async (opts) => {
if (opts.message === "How do you want to hatch your bot?") return "tui";
return "quickstart";
});
const prompter: WizardPrompter = {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select,
multiselect: vi.fn(async () => []),
text: vi.fn(async () => ""),
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
};
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn((code: number) => {
throw new Error(`exit:${code}`);
}),
};
await runOnboardingWizard(
{
acceptRisk: true,
flow: "quickstart",
mode: "local",
workspace: workspaceDir,
authChoice: "skip",
skipProviders: true,
skipSkills: true,
skipHealth: true,
installDaemon: false,
},
runtime,
prompter,
);
expect(runTui).toHaveBeenCalledWith(
expect.objectContaining({
deliver: false,
message: undefined,
}),
);
await fs.rm(workspaceDir, { recursive: true, force: true });
});
it("shows the web search hint at the end of onboarding", async () => { it("shows the web search hint at the end of onboarding", async () => {
const prevBraveKey = process.env.BRAVE_API_KEY; const prevBraveKey = process.env.BRAVE_API_KEY;
delete process.env.BRAVE_API_KEY; delete process.env.BRAVE_API_KEY;