fix: messaging UX — silence doctor, fix groupPolicy, drop early WhatsApp pairing (#2607)

* fix: messaging UX — silence doctor, fix groupPolicy, remove early WhatsApp pairing

- Set groupPolicy to "open" for both Telegram and WhatsApp (was
  "allowlist" with empty allowFrom, causing doctor warnings)
- Suppress doctor warning spam by redirecting openclaw config set
  stdout to /dev/null
- Remove WhatsApp pairing prompt (appeared immediately after QR scan
  before user could message the bot — now just tells them the command)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: improve Telegram/WhatsApp pairing instructions

Add step-by-step instructions for Telegram pairing so users know to
search for their bot in Telegram and message it. Improve WhatsApp
post-link instructions to explain how contacts pair.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: pre-select Telegram in setup options as recommended channel

Telegram has the smoothest setup UX (bot token + pairing code) compared
to WhatsApp (QR scan + separate device). Pre-select it alongside Chrome
in the multiselect and label it as "recommended" in the hint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ahmed Abushagur 2026-03-14 00:46:19 -07:00 committed by GitHub
parent ca5fe851cd
commit 9f51244cb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 29 additions and 39 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "0.17.16",
"version": "0.17.17",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -176,12 +176,15 @@ async function promptSetupOptions(agentName: string): Promise<Set<string> | unde
// 1. Arrow keys work immediately (multiple items to navigate)
// 2. Users can explicitly skip all steps without pressing Enter on an empty list
// 3. Users who accidentally select another option can deselect it and choose None
const hasBrowserDefault = filteredSteps.some((s) => s.value === "browser");
const defaultValues = hasBrowserDefault
? filteredSteps.filter((s) => s.value === "browser").map((s) => s.value)
: [
NONE_STEP,
];
// Pre-select browser + telegram by default (telegram has the smoothest setup UX)
const recommendedDefaults = [
"browser",
"telegram",
];
const defaultValues = filteredSteps.filter((s) => recommendedDefaults.includes(s.value)).map((s) => s.value);
if (defaultValues.length === 0) {
defaultValues.push(NONE_STEP);
}
const selected = await p.multiselect({
message: "Setup options (↑/↓ navigate, space to select, enter to confirm)",

View file

@ -374,6 +374,7 @@ async function setupOpenclawConfig(
enabled: true,
botToken: telegramBotToken,
dmPolicy: "pairing",
groupPolicy: "open",
groups: {
"*": {
requireMention: true,
@ -386,7 +387,7 @@ async function setupOpenclawConfig(
if (enabledSteps?.has("whatsapp")) {
channels.whatsapp = {
dmPolicy: "pairing",
groupPolicy: "allowlist",
groupPolicy: "open",
sendReadReceipts: true,
};
}
@ -399,14 +400,14 @@ async function setupOpenclawConfig(
await uploadConfigFile(runner, config, "$HOME/.openclaw/openclaw.json");
// Configure browser via CLI (openclaw config set) — the supported way to set
// browser options. Writing JSON directly may not be picked up by all versions.
// browser options. Redirect stdout to suppress doctor warnings on each call.
const browserResult = await asyncTryCatchIf(isOperationalError, () =>
runner.runServer(
"export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " +
"openclaw config set browser.executablePath /usr/bin/google-chrome-stable; " +
"openclaw config set browser.noSandbox true; " +
"openclaw config set browser.headless true; " +
"openclaw config set browser.defaultProfile openclaw",
"openclaw config set browser.executablePath /usr/bin/google-chrome-stable >/dev/null; " +
"openclaw config set browser.noSandbox true >/dev/null; " +
"openclaw config set browser.headless true >/dev/null; " +
"openclaw config set browser.defaultProfile openclaw >/dev/null",
),
);
if (!browserResult.ok) {
@ -419,7 +420,7 @@ async function setupOpenclawConfig(
const gatewayTokenResult = await asyncTryCatchIf(isOperationalError, () =>
runner.runServer(
"export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " +
`openclaw config set gateway.auth.token ${shellQuote(gatewayToken)}`,
`openclaw config set gateway.auth.token ${shellQuote(gatewayToken)} >/dev/null`,
),
);
if (!gatewayTokenResult.ok) {

View file

@ -63,7 +63,7 @@ const AGENT_EXTRA_STEPS: Record<string, OptionalStep[]> = {
{
value: "telegram",
label: "Telegram",
hint: "connect via bot token from @BotFather",
hint: "recommended — connect via bot token from @BotFather",
dataEnvVar: "TELEGRAM_BOT_TOKEN",
},
{

View file

@ -298,8 +298,12 @@ export async function runOrchestration(
if (enabledSteps?.has("telegram")) {
logStep("Telegram pairing...");
logInfo("DM your Telegram bot to get a pairing code, then enter it below.");
logInfo("Waiting for pairing code...");
logInfo("To pair your Telegram account:");
logInfo(" 1. Open Telegram on your phone");
logInfo(" 2. Search for the bot you created with @BotFather");
logInfo(' 3. Send it any message (e.g. "hello")');
logInfo(" 4. The bot will reply with a pairing code");
logInfo(" 5. Enter the code below");
process.stderr.write("\n");
const pairingCode = (await prompt("Telegram pairing code: ")).trim();
if (pairingCode) {
@ -320,34 +324,16 @@ export async function runOrchestration(
}
if (enabledSteps?.has("whatsapp")) {
// Step 1: QR code scan to link the WhatsApp device
logStep("Linking WhatsApp — scan the QR code with your phone...");
logInfo("Open WhatsApp > Settings > Linked Devices > Link a Device");
process.stderr.write("\n");
const whatsappCmd = `source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw channels login --channel whatsapp`;
prepareStdinForHandoff();
await cloud.interactiveSession(whatsappCmd);
// Step 2: Pairing — approve your own number so the bot responds to you
logStep("WhatsApp pairing...");
logInfo("Send a message to your bot on WhatsApp to get a pairing code, then enter it below.");
process.stderr.write("\n");
const pairingCode = (await prompt("WhatsApp pairing code: ")).trim();
if (pairingCode) {
const escaped = shellQuote(pairingCode);
const result = await asyncTryCatchIf(isOperationalError, () =>
cloud.runner.runServer(
`source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw pairing approve whatsapp ${escaped}`,
),
);
if (result.ok) {
logInfo("WhatsApp paired successfully");
} else {
logWarn("Pairing failed — you can pair later via: openclaw pairing approve whatsapp <CODE>");
}
} else {
logInfo("No code entered — pair later via: openclaw pairing approve whatsapp <CODE>");
}
logInfo("WhatsApp linked! To pair a contact:");
logInfo(" 1. Have someone message your WhatsApp number");
logInfo(" 2. They'll get a pairing code from the bot");
logInfo(" 3. Approve via: openclaw pairing approve whatsapp <CODE>");
}
// 11d. Agent-specific pre-launch tip (e.g. channel setup ordering hint)