Merge pull request #791 from roshanasingh4/fix/788-implicit-delivery-leak
Prevent onboarding TUI from auto-delivering to lastProvider/lastTo
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
- Google: apply patched pi-ai `google-gemini-cli` function call handling (strips ids) after upgrading to pi-ai 0.43.0.
|
- Google: apply patched pi-ai `google-gemini-cli` function call handling (strips ids) after upgrading to pi-ai 0.43.0.
|
||||||
- Auto-reply: elevated/reasoning toggles now enqueue system events so the model sees the mode change immediately.
|
- Auto-reply: elevated/reasoning toggles now enqueue system events so the model sees the mode change immediately.
|
||||||
- Tools: keep `image` available in sandbox and fail over when image models return empty output (fixes “(no text returned)”).
|
- Tools: keep `image` available in sandbox and fail over when image models return empty output (fixes “(no text returned)”).
|
||||||
|
- Onboarding: TUI defaults to `deliver: false` to avoid cross-provider auto-delivery leaks; onboarding spawns the TUI with explicit `deliver: false`. (#791 — thanks @roshanasingh4)
|
||||||
|
|
||||||
## 2026.1.11
|
## 2026.1.11
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import {
|
|||||||
agentsDeleteCommand,
|
agentsDeleteCommand,
|
||||||
agentsListCommand,
|
agentsListCommand,
|
||||||
} from "../commands/agents.js";
|
} from "../commands/agents.js";
|
||||||
import { dashboardCommand } from "../commands/dashboard.js";
|
|
||||||
import {
|
import {
|
||||||
CONFIGURE_WIZARD_SECTIONS,
|
CONFIGURE_WIZARD_SECTIONS,
|
||||||
configureCommand,
|
configureCommand,
|
||||||
configureCommandWithSections,
|
configureCommandWithSections,
|
||||||
} from "../commands/configure.js";
|
} from "../commands/configure.js";
|
||||||
|
import { dashboardCommand } from "../commands/dashboard.js";
|
||||||
import { doctorCommand } from "../commands/doctor.js";
|
import { doctorCommand } from "../commands/doctor.js";
|
||||||
import { healthCommand } from "../commands/health.js";
|
import { healthCommand } from "../commands/health.js";
|
||||||
import { messageCommand } from "../commands/message.js";
|
import { messageCommand } from "../commands/message.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { dashboardCommand } from "./dashboard.js";
|
import { dashboardCommand } from "./dashboard.js";
|
||||||
|
|
||||||
@@ -94,7 +94,10 @@ describe("dashboardCommand", () => {
|
|||||||
it("prints SSH hint when browser cannot open", async () => {
|
it("prints SSH hint when browser cannot open", async () => {
|
||||||
mockSnapshot("shhhh");
|
mockSnapshot("shhhh");
|
||||||
mocks.copyToClipboard.mockResolvedValue(false);
|
mocks.copyToClipboard.mockResolvedValue(false);
|
||||||
mocks.detectBrowserOpenSupport.mockResolvedValue({ ok: false, reason: "ssh" });
|
mocks.detectBrowserOpenSupport.mockResolvedValue({
|
||||||
|
ok: false,
|
||||||
|
reason: "ssh",
|
||||||
|
});
|
||||||
mocks.formatControlUiSshHint.mockReturnValue("ssh hint");
|
mocks.formatControlUiSshHint.mockReturnValue("ssh hint");
|
||||||
|
|
||||||
await dashboardCommand(runtime);
|
await dashboardCommand(runtime);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { resolveGatewayPort, readConfigFileSnapshot } from "../config/config.js";
|
import {
|
||||||
import { defaultRuntime } from "../runtime.js";
|
readConfigFileSnapshot,
|
||||||
|
resolveGatewayPort,
|
||||||
|
} from "../config/config.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
detectBrowserOpenSupport,
|
detectBrowserOpenSupport,
|
||||||
@@ -33,7 +36,9 @@ export async function dashboardCommand(
|
|||||||
runtime.log(`Dashboard URL: ${authedUrl}`);
|
runtime.log(`Dashboard URL: ${authedUrl}`);
|
||||||
|
|
||||||
const copied = await copyToClipboard(authedUrl).catch(() => false);
|
const copied = await copyToClipboard(authedUrl).catch(() => false);
|
||||||
runtime.log(copied ? "Copied to clipboard." : "Copy to clipboard unavailable.");
|
runtime.log(
|
||||||
|
copied ? "Copied to clipboard." : "Copy to clipboard unavailable.",
|
||||||
|
);
|
||||||
|
|
||||||
let opened = false;
|
let opened = false;
|
||||||
let hint: string | undefined;
|
let hint: string | undefined;
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
let toolsExpanded = false;
|
let toolsExpanded = false;
|
||||||
let showThinking = false;
|
let showThinking = false;
|
||||||
const deliverDefault = opts.deliver ?? true;
|
const deliverDefault = opts.deliver ?? false;
|
||||||
const autoMessage = opts.message?.trim();
|
const autoMessage = opts.message?.trim();
|
||||||
let autoMessageSent = false;
|
let autoMessageSent = false;
|
||||||
let sessionInfo: SessionInfo = {};
|
let sessionInfo: SessionInfo = {};
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import { DEFAULT_BOOTSTRAP_FILENAME } from "../agents/workspace.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { runOnboardingWizard } from "./onboarding.js";
|
import { runOnboardingWizard } from "./onboarding.js";
|
||||||
import type { WizardPrompter } from "./prompts.js";
|
import type { WizardPrompter } from "./prompts.js";
|
||||||
@@ -117,4 +121,64 @@ describe("runOnboardingWizard", () => {
|
|||||||
expect(healthCommand).not.toHaveBeenCalled();
|
expect(healthCommand).not.toHaveBeenCalled();
|
||||||
expect(runTui).not.toHaveBeenCalled();
|
expect(runTui).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("launches TUI without auto-delivery when hatching", async () => {
|
||||||
|
runTui.mockClear();
|
||||||
|
|
||||||
|
const workspaceDir = await fs.mkdtemp(
|
||||||
|
path.join(os.tmpdir(), "clawdbot-onboard-"),
|
||||||
|
);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(workspaceDir, DEFAULT_BOOTSTRAP_FILENAME),
|
||||||
|
"{}",
|
||||||
|
);
|
||||||
|
|
||||||
|
const confirm: WizardPrompter["confirm"] = vi.fn(async (opts) => {
|
||||||
|
if (opts.message === "Do you want to hatch your bot now?") return true;
|
||||||
|
return opts.initialValue ?? false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const prompter: WizardPrompter = {
|
||||||
|
intro: vi.fn(async () => {}),
|
||||||
|
outro: vi.fn(async () => {}),
|
||||||
|
note: vi.fn(async () => {}),
|
||||||
|
select: vi.fn(async () => "quickstart"),
|
||||||
|
multiselect: vi.fn(async () => []),
|
||||||
|
text: vi.fn(async () => ""),
|
||||||
|
confirm,
|
||||||
|
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(
|
||||||
|
{
|
||||||
|
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: "Wake up, my friend!",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await fs.rm(workspaceDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -796,6 +796,8 @@ export async function runOnboardingWizard(
|
|||||||
token: authMode === "token" ? gatewayToken : undefined,
|
token: authMode === "token" ? gatewayToken : undefined,
|
||||||
password:
|
password:
|
||||||
authMode === "password" ? baseConfig.gateway?.auth?.password : "",
|
authMode === "password" ? baseConfig.gateway?.auth?.password : "",
|
||||||
|
// Safety: onboarding TUI should not auto-deliver to lastProvider/lastTo.
|
||||||
|
deliver: false,
|
||||||
message: "Wake up, my friend!",
|
message: "Wake up, my friend!",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user