mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 01:40:44 +00:00
Fix setup TUI hatch terminal handoff (#69524)
* fix: relaunch setup tui in a fresh process
* fix: harden setup tui handoff
* fix: preserve tui hatch exit flow
* Revert "fix: preserve tui hatch exit flow"
This reverts commit f4f119a5a3.
* fix: let setup tui resolve gateway auth
* fix: support packaged tui relaunch
* fix: pin setup tui gateway target
* fix: preserve setup tui auth source
This commit is contained in:
@@ -4,7 +4,8 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
const runTui = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const launchTuiCli = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const restoreTerminalState = vi.hoisted(() => vi.fn());
|
||||
const probeGatewayReachable = vi.hoisted(() =>
|
||||
vi.fn<() => Promise<{ ok: boolean; detail?: string }>>(async () => ({ ok: true })),
|
||||
);
|
||||
@@ -134,11 +135,11 @@ vi.mock("../infra/control-ui-assets.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../terminal/restore.js", () => ({
|
||||
restoreTerminalState: vi.fn(),
|
||||
restoreTerminalState,
|
||||
}));
|
||||
|
||||
vi.mock("../tui/tui.js", () => ({
|
||||
runTui,
|
||||
vi.mock("../tui/tui-launch.js", () => ({
|
||||
launchTuiCli,
|
||||
}));
|
||||
|
||||
vi.mock("./setup.secret-input.js", () => ({
|
||||
@@ -236,7 +237,8 @@ function createAdvancedFinalizeArgs(params: AdvancedFinalizeArgs = {}) {
|
||||
|
||||
describe("finalizeSetupWizard", () => {
|
||||
beforeEach(() => {
|
||||
runTui.mockClear();
|
||||
launchTuiCli.mockClear();
|
||||
restoreTerminalState.mockClear();
|
||||
probeGatewayReachable.mockClear();
|
||||
waitForGatewayReachable.mockReset();
|
||||
waitForGatewayReachable.mockResolvedValue({ ok: true });
|
||||
@@ -265,7 +267,7 @@ describe("finalizeSetupWizard", () => {
|
||||
listConfiguredWebSearchProviders.mockReturnValue([]);
|
||||
});
|
||||
|
||||
it("resolves gateway password SecretRef for probe and TUI", async () => {
|
||||
it("resolves gateway password SecretRef for probe but omits auth from TUI hatch", async () => {
|
||||
const previous = process.env.OPENCLAW_GATEWAY_PASSWORD;
|
||||
process.env.OPENCLAW_GATEWAY_PASSWORD = "resolved-gateway-password"; // pragma: allowlist secret
|
||||
resolveSetupSecretInputString.mockResolvedValueOnce("resolved-gateway-password");
|
||||
@@ -337,14 +339,62 @@ describe("finalizeSetupWizard", () => {
|
||||
password: "resolved-gateway-password", // pragma: allowlist secret
|
||||
}),
|
||||
);
|
||||
expect(runTui).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "ws://127.0.0.1:18789",
|
||||
password: "resolved-gateway-password", // pragma: allowlist secret
|
||||
}),
|
||||
expect(launchTuiCli).toHaveBeenCalledWith(
|
||||
{
|
||||
deliver: false,
|
||||
message: undefined,
|
||||
},
|
||||
{
|
||||
authSource: "config",
|
||||
gatewayUrl: "ws://127.0.0.1:18789",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("restores terminal state after failed TUI hatch", async () => {
|
||||
launchTuiCli.mockRejectedValueOnce(new Error("TUI exited with code 1"));
|
||||
const select = vi.fn(async (params: { message: string }) => {
|
||||
if (params.message === "How do you want to hatch your bot?") {
|
||||
return "tui";
|
||||
}
|
||||
return "later";
|
||||
});
|
||||
const prompter = buildWizardPrompter({ select: select as never });
|
||||
|
||||
await expect(
|
||||
finalizeSetupWizard({
|
||||
flow: "advanced",
|
||||
opts: {
|
||||
acceptRisk: true,
|
||||
authChoice: "skip",
|
||||
installDaemon: false,
|
||||
skipHealth: true,
|
||||
skipUi: false,
|
||||
},
|
||||
baseConfig: {},
|
||||
nextConfig: {},
|
||||
workspaceDir: "/tmp",
|
||||
settings: {
|
||||
port: 18789,
|
||||
bind: "loopback",
|
||||
authMode: "token",
|
||||
gatewayToken: "test-token",
|
||||
tailscaleMode: "off",
|
||||
tailscaleResetOnExit: false,
|
||||
},
|
||||
prompter,
|
||||
runtime: createRuntime(),
|
||||
}),
|
||||
).rejects.toThrow("TUI exited with code 1");
|
||||
|
||||
expect(restoreTerminalState).toHaveBeenCalledWith("pre-setup tui", {
|
||||
resumeStdinIfPaused: true,
|
||||
});
|
||||
expect(restoreTerminalState).toHaveBeenCalledWith("post-setup tui", {
|
||||
resumeStdinIfPaused: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not persist resolved SecretRef token in daemon install plan", async () => {
|
||||
const prompter = buildWizardPrompter({
|
||||
select: vi.fn(async () => "later") as never,
|
||||
|
||||
@@ -30,7 +30,7 @@ import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { restoreTerminalState } from "../terminal/restore.js";
|
||||
import { runTui } from "../tui/tui.js";
|
||||
import { launchTuiCli } from "../tui/tui-launch.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { listConfiguredWebSearchProviders } from "../web-search/runtime.js";
|
||||
import type { WizardPrompter } from "./prompts.js";
|
||||
@@ -423,14 +423,21 @@ export async function finalizeSetupWizard(
|
||||
|
||||
if (hatchChoice === "tui") {
|
||||
restoreTerminalState("pre-setup tui", { resumeStdinIfPaused: true });
|
||||
await runTui({
|
||||
url: links.wsUrl,
|
||||
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
|
||||
password: settings.authMode === "password" ? resolvedGatewayPassword : "",
|
||||
// Safety: setup TUI should not auto-deliver to lastProvider/lastTo.
|
||||
deliver: false,
|
||||
message: hasBootstrap ? "Wake up, my friend!" : undefined,
|
||||
});
|
||||
try {
|
||||
await launchTuiCli(
|
||||
{
|
||||
// Safety: setup TUI should not auto-deliver to lastProvider/lastTo.
|
||||
deliver: false,
|
||||
message: hasBootstrap ? "Wake up, my friend!" : undefined,
|
||||
},
|
||||
{
|
||||
authSource: "config",
|
||||
gatewayUrl: links.wsUrl,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
restoreTerminalState("post-setup tui", { resumeStdinIfPaused: true });
|
||||
}
|
||||
launchedTui = true;
|
||||
} else if (hatchChoice === "web") {
|
||||
const browserSupport = await detectBrowserOpenSupport();
|
||||
|
||||
Reference in New Issue
Block a user