From 3554004968a0c59f08eaaa323fa31df7dd19d104 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 10 Jan 2026 01:07:49 +0100 Subject: [PATCH] fix: QuickStart provider selection (#485) --- CHANGELOG.md | 1 + src/commands/onboard-providers.test.ts | 131 +++++++++++-------------- src/commands/onboard-providers.ts | 33 +++++-- 3 files changed, 86 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d1d07792..5f6630ef3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - macOS: replace relay smoke test with version check in packaging script. (#615) — thanks @YuriNachos - macOS: avoid clearing Launch at Login during app initialization. (#607) — thanks @wes-davis - Onboarding: skip systemd checks/daemon installs when systemd user services are unavailable; add onboarding flags to skip flow steps and stabilize Docker E2E. (#573) — thanks @steipete +- Onboarding: QuickStart provider picker uses single-select to avoid accidental Telegram token prompts when choosing WhatsApp. (#485) — thanks @frankstallone - macOS: add node bridge heartbeat pings to detect half-open sockets and reconnect cleanly. (#572) — thanks @ngutman - Node bridge: harden keepalive + heartbeat handling (TCP keepalive, better disconnects, and keepalive config tests). (#577) — thanks @steipete - Control UI: improve mobile responsiveness. (#558) — thanks @carlulsoe diff --git a/src/commands/onboard-providers.test.ts b/src/commands/onboard-providers.test.ts index 45ccb7f5f5..1cfd2649dc 100644 --- a/src/commands/onboard-providers.test.ts +++ b/src/commands/onboard-providers.test.ts @@ -1,81 +1,70 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import type { ClawdbotConfig } from "../config/config.js"; -import { - mergeWhatsAppConfig, - setWhatsAppAllowFrom, - setWhatsAppDmPolicy, - setWhatsAppSelfChatMode, -} from "./onboard-providers.js"; +import type { RuntimeEnv } from "../runtime.js"; +import type { WizardPrompter } from "../wizard/prompts.js"; +import { setupProviders } from "./onboard-providers.js"; -describe("onboard-providers WhatsApp setters", () => { - it("preserves existing WhatsApp fields when updating allowFrom", () => { - const cfg: ClawdbotConfig = { - whatsapp: { - selfChatMode: true, - dmPolicy: "pairing", - allowFrom: ["*"], - accounts: { - default: { enabled: false }, - }, - }, - }; +vi.mock("node:fs/promises", () => ({ + default: { + access: vi.fn(async () => { + throw new Error("ENOENT"); + }), + }, +})); - const next = setWhatsAppAllowFrom(cfg, ["+15555550123"]); +vi.mock("../provider-web.js", () => ({ + loginWeb: vi.fn(async () => {}), +})); - expect(next.whatsapp?.selfChatMode).toBe(true); - expect(next.whatsapp?.dmPolicy).toBe("pairing"); - expect(next.whatsapp?.allowFrom).toEqual(["+15555550123"]); - expect(next.whatsapp?.accounts?.default?.enabled).toBe(false); - }); +vi.mock("./onboard-helpers.js", () => ({ + detectBinary: vi.fn(async () => false), +})); - it("updates dmPolicy without dropping selfChatMode", () => { - const cfg: ClawdbotConfig = { - whatsapp: { - selfChatMode: true, - dmPolicy: "pairing", - }, - }; - - const next = setWhatsAppDmPolicy(cfg, "open"); - - expect(next.whatsapp?.dmPolicy).toBe("open"); - expect(next.whatsapp?.selfChatMode).toBe(true); - }); - - it("updates selfChatMode without dropping allowFrom", () => { - const cfg: ClawdbotConfig = { - whatsapp: { - allowFrom: ["+15555550123"], - }, - }; - - const next = setWhatsAppSelfChatMode(cfg, true); - - expect(next.whatsapp?.selfChatMode).toBe(true); - expect(next.whatsapp?.allowFrom).toEqual(["+15555550123"]); - }); - - it("merges WhatsApp config without clobbering fields", () => { - const cfg: ClawdbotConfig = { - whatsapp: { - dmPolicy: "pairing", - allowFrom: ["*"], - }, - }; - - const merged = mergeWhatsAppConfig(cfg, { - dmPolicy: "open", - allowFrom: undefined, +describe("setupProviders", () => { + it("QuickStart uses single-select (no multiselect) and doesn't prompt for Telegram token when WhatsApp is chosen", async () => { + const select = vi.fn(async () => "whatsapp"); + const multiselect = vi.fn(async () => { + throw new Error("unexpected multiselect"); + }); + const text = vi.fn(async ({ message }: { message: string }) => { + if (message.includes("Enter Telegram bot token")) { + throw new Error("unexpected Telegram token prompt"); + } + if (message.includes("Your personal WhatsApp number")) { + return "+15555550123"; + } + throw new Error(`unexpected text prompt: ${message}`); }); - const cleared = mergeWhatsAppConfig( - cfg, - { allowFrom: undefined }, - { unsetOnUndefined: ["allowFrom"] }, - ); - expect(merged.whatsapp?.dmPolicy).toBe("open"); - expect(merged.whatsapp?.allowFrom).toEqual(["*"]); - expect(cleared.whatsapp?.allowFrom).toBeUndefined(); + const prompter: WizardPrompter = { + intro: vi.fn(async () => {}), + outro: vi.fn(async () => {}), + note: vi.fn(async () => {}), + select, + multiselect, + text: text as unknown as WizardPrompter["text"], + 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 setupProviders({} as ClawdbotConfig, runtime, prompter, { + skipConfirm: true, + quickstartDefaults: true, + forceAllowFromProviders: ["whatsapp"], + }); + + expect(select).toHaveBeenCalledWith( + expect.objectContaining({ message: "Select provider (QuickStart)" }), + ); + expect(multiselect).not.toHaveBeenCalled(); }); }); diff --git a/src/commands/onboard-providers.ts b/src/commands/onboard-providers.ts index dd08e30cf1..60cd3de7ad 100644 --- a/src/commands/onboard-providers.ts +++ b/src/commands/onboard-providers.ts @@ -872,14 +872,31 @@ export async function setupProviders( } }); - const initialSelection = - options?.initialSelection ?? - (options?.quickstartDefaults && !telegramConfigured ? ["telegram"] : []); - const selection = (await prompter.multiselect({ - message: "Select providers", - options: selectionOptions, - initialValues: initialSelection.length ? initialSelection : undefined, - })) as ProviderChoice[]; + let selection: ProviderChoice[]; + if (options?.quickstartDefaults) { + const choice = (await prompter.select({ + message: "Select provider (QuickStart)", + options: [ + ...selectionOptions, + { + value: "__skip__", + label: "Skip for now", + hint: "You can add providers later via `clawdbot providers add`", + }, + ], + initialValue: + options?.initialSelection?.[0] ?? + (!telegramConfigured ? "telegram" : "whatsapp"), + })) as ProviderChoice | "__skip__"; + selection = choice === "__skip__" ? [] : [choice]; + } else { + const initialSelection = options?.initialSelection ?? []; + selection = (await prompter.multiselect({ + message: "Select providers (Space to toggle, Enter to continue)", + options: selectionOptions, + initialValues: initialSelection.length ? initialSelection : undefined, + })) as ProviderChoice[]; + } options?.onSelection?.(selection); const accountOverrides: Partial> = {