feat: add TUI bootstrap start

This commit is contained in:
Peter Steinberger
2026-01-09 16:25:11 +01:00
parent 25babbfdc4
commit 7957196924
5 changed files with 66 additions and 22 deletions

View File

@@ -18,6 +18,7 @@ export function registerTuiCli(program: Command) {
)
.option("--deliver", "Deliver assistant replies", false)
.option("--thinking <level>", "Thinking level override")
.option("--message <text>", "Send an initial message after connecting")
.option("--timeout-ms <ms>", "Agent timeout in ms", "30000")
.option("--history-limit <n>", "History entries to load", "200")
.action(async (opts) => {
@@ -37,6 +38,7 @@ export function registerTuiCli(program: Command) {
session: opts.session as string | undefined,
deliver: Boolean(opts.deliver),
thinking: opts.thinking as string | undefined,
message: opts.message as string | undefined,
timeoutMs: Number.isNaN(timeoutMs) ? undefined : timeoutMs,
historyLimit: Number.isNaN(historyLimit) ? undefined : historyLimit,
});

View File

@@ -34,6 +34,7 @@ export type TuiOptions = {
thinking?: string;
timeoutMs?: number;
historyLimit?: number;
message?: string;
};
type ChatEvent = {
@@ -146,6 +147,8 @@ export async function runTui(opts: TuiOptions) {
let toolsExpanded = false;
let showThinking = false;
let deliverDefault = Boolean(opts.deliver);
const autoMessage = opts.message?.trim();
let autoMessageSent = false;
let sessionInfo: SessionInfo = {};
let lastCtrlCAt = 0;
@@ -976,6 +979,10 @@ export async function runTui(opts: TuiOptions) {
await loadHistory();
chatLog.addSystem("gateway connected");
tui.requestRender();
if (!autoMessageSent && autoMessage) {
autoMessageSent = true;
await sendMessage(autoMessage);
}
} else {
chatLog.addSystem("gateway reconnected");
}

View File

@@ -1,5 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
import { DEFAULT_BOOTSTRAP_FILENAME } from "../agents/workspace.js";
import {
applyAuthChoice,
warnIfModelConfigLooksOff,
@@ -52,6 +54,7 @@ import { buildServiceEnvironment } from "../daemon/service-env.js";
import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js";
import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { runTui } from "../tui/tui.js";
import { resolveUserPath, sleep } from "../utils.js";
import type { WizardPrompter } from "./prompts.js";
@@ -654,6 +657,11 @@ export async function runOnboardingWizard(
const gatewayStatusLine = gatewayProbe.ok
? "Gateway: reachable"
: `Gateway: not detected${gatewayProbe.detail ? ` (${gatewayProbe.detail})` : ""}`;
const bootstrapPath = path.join(workspaceDir, DEFAULT_BOOTSTRAP_FILENAME);
const hasBootstrap = await fs
.access(bootstrapPath)
.then(() => true)
.catch(() => false);
await prompter.note(
[
@@ -668,33 +676,58 @@ export async function runOnboardingWizard(
"Control UI",
);
const browserSupport = await detectBrowserOpenSupport();
if (gatewayProbe.ok) {
if (!browserSupport.ok) {
if (hasBootstrap) {
await prompter.note(
formatControlUiSshHint({
port,
basePath: baseConfig.gateway?.controlUi?.basePath,
token: authMode === "token" ? gatewayToken : undefined,
}),
"Open Control UI",
[
"This is the defining action that makes your agent you.",
"Please take your time.",
"The more you tell it, the better the experience will be.",
'We will send: "Wake up, my friend!"',
].join("\n"),
"Start TUI (best option!)",
);
} else {
const wantsOpen = await prompter.confirm({
message: "Open Control UI now?",
const wantsTui = await prompter.confirm({
message: "Start TUI now? (best option!)",
initialValue: true,
});
if (wantsOpen) {
const opened = await openUrl(`${links.httpUrl}${tokenParam}`);
if (!opened) {
await prompter.note(
formatControlUiSshHint({
port,
basePath: baseConfig.gateway?.controlUi?.basePath,
token: authMode === "token" ? gatewayToken : undefined,
}),
"Open Control UI",
);
if (wantsTui) {
await runTui({
url: links.wsUrl,
token: authMode === "token" ? gatewayToken : undefined,
password:
authMode === "password" ? baseConfig.gateway?.auth?.password : "",
message: "Wake up, my friend!",
});
}
} else {
const browserSupport = await detectBrowserOpenSupport();
if (!browserSupport.ok) {
await prompter.note(
formatControlUiSshHint({
port,
basePath: baseConfig.gateway?.controlUi?.basePath,
token: authMode === "token" ? gatewayToken : undefined,
}),
"Open Control UI",
);
} else {
const wantsOpen = await prompter.confirm({
message: "Open Control UI now?",
initialValue: true,
});
if (wantsOpen) {
const opened = await openUrl(`${links.httpUrl}${tokenParam}`);
if (!opened) {
await prompter.note(
formatControlUiSshHint({
port,
basePath: baseConfig.gateway?.controlUi?.basePath,
token: authMode === "token" ? gatewayToken : undefined,
}),
"Open Control UI",
);
}
}
}
}