From 06bbbcb2a4cd49f2fccc595f7244d2579fa4929b Mon Sep 17 00:00:00 2001 From: Ahmed Abushagur Date: Fri, 13 Mar 2026 13:47:50 -0700 Subject: [PATCH] fix: move channel setup to after gateway starts (#2590) * fix: move Telegram/WhatsApp channel setup to after gateway starts OpenClaw's `channels add` and `channels login` commands require a running gateway. Previously, Telegram token configuration ran in setupOpenclawConfig (pre-gateway) using `openclaw config set`, causing the gateway to hang on startup when a token was present for a disabled-by-default plugin. Now: - Plugin enables stay in setupOpenclawConfig (pre-gateway) - Channel config (token add, QR login) runs in orchestrate.ts step 11c after the gateway is up, using `openclaw channels add/login` Co-Authored-By: Claude Opus 4.6 * security: use shellQuote instead of jsonEscape for Telegram token jsonEscape uses JSON.stringify which produces double-quoted strings that the shell interprets, creating a command injection vector. shellQuote wraps in single quotes, preventing shell interpretation. Co-Authored-By: Claude Opus 4.6 * chore: fix biome export ordering in interactive.ts and manifest.ts Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: L <6723574+louisgv@users.noreply.github.com> --- packages/cli/src/shared/agent-setup.ts | 37 ++---------------------- packages/cli/src/shared/orchestrate.ts | 39 ++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/packages/cli/src/shared/agent-setup.ts b/packages/cli/src/shared/agent-setup.ts index 8881c0cc..b1fd59fa 100644 --- a/packages/cli/src/shared/agent-setup.ts +++ b/packages/cli/src/shared/agent-setup.ts @@ -9,7 +9,7 @@ import { join } from "node:path"; import { getTmpDir } from "./paths"; import { asyncTryCatch, asyncTryCatchIf, isOperationalError, tryCatchIf } from "./result.js"; import { getErrorMessage } from "./type-guards"; -import { Err, jsonEscape, logError, logInfo, logStep, logWarn, Ok, prompt, shellQuote, withRetry } from "./ui"; +import { Err, jsonEscape, logError, logInfo, logStep, logWarn, Ok, shellQuote, withRetry } from "./ui"; /** * Wrap an SSH-based async operation into a Result for use with withRetry. @@ -393,39 +393,8 @@ async function setupOpenclawConfig( logWarn("Gateway token re-assertion failed (non-fatal) — dashboard may show Unauthorized"); } - // Telegram channel setup — check env var first, then prompt interactively - if (enabledSteps?.has("telegram")) { - logStep("Setting up Telegram..."); - const envToken = process.env.TELEGRAM_BOT_TOKEN; - if (!envToken) { - logInfo("To get a bot token:"); - logInfo(" 1. Open Telegram and search for @BotFather"); - logInfo(" 2. Send /newbot and follow the prompts"); - logInfo(" 3. Copy the token (looks like 123456:ABC-DEF...)"); - logInfo(" Press Enter to skip if you don't have one yet."); - } - const trimmedToken = envToken?.trim() || (await prompt("Telegram bot token: ")).trim(); - - if (trimmedToken) { - const escapedBotToken = shellQuote(trimmedToken); - const telegramResult = await asyncTryCatchIf(isOperationalError, () => - runner.runServer( - "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + - `openclaw config set channels.telegram.botToken ${escapedBotToken}`, - ), - ); - if (telegramResult.ok) { - logInfo("Telegram bot token configured"); - } else { - logWarn("Telegram config failed — set it up via the web dashboard after launch"); - } - } else { - logInfo("No token entered — set up Telegram via the web dashboard after launch"); - } - } - - // WhatsApp — QR code scanning happens interactively in orchestrate.ts - // after the gateway starts and tunnel is set up. No config needed here. + // Channel configuration (Telegram token, WhatsApp QR) happens in orchestrate.ts + // AFTER the gateway starts — openclaw channels commands need a running gateway. // Write USER.md bootstrap file — guides users to the web dashboard for // visual tasks like WhatsApp QR code scanning that don't work in the TUI. diff --git a/packages/cli/src/shared/orchestrate.ts b/packages/cli/src/shared/orchestrate.ts index 079600b6..239ccbe4 100644 --- a/packages/cli/src/shared/orchestrate.ts +++ b/packages/cli/src/shared/orchestrate.ts @@ -25,6 +25,8 @@ import { logWarn, openBrowser, prepareStdinForHandoff, + prompt, + shellQuote, validateModelId, withRetry, } from "./ui"; @@ -291,15 +293,42 @@ export async function runOrchestration( } } - // 11c. Interactive channel login (WhatsApp QR scan, Telegram bot link) - // Runs before the TUI so users can link messaging channels during setup. + // 11c. Channel setup (runs after gateway is up so openclaw commands work) + const ocPath = "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH"; + + if (enabledSteps?.has("telegram")) { + logStep("Setting up Telegram..."); + const envToken = process.env.TELEGRAM_BOT_TOKEN; + if (!envToken) { + logInfo("To get a bot token:"); + logInfo(" 1. Open Telegram and search for @BotFather"); + logInfo(" 2. Send /newbot and follow the prompts"); + logInfo(" 3. Copy the token (looks like 123456:ABC-DEF...)"); + logInfo(" Press Enter to skip if you don't have one yet."); + } + const trimmedToken = envToken?.trim() || (await prompt("Telegram bot token: ")).trim(); + if (trimmedToken) { + const escaped = shellQuote(trimmedToken); + const result = await asyncTryCatchIf(isOperationalError, () => + cloud.runner.runServer( + `source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw channels add --channel telegram --token ${escaped}`, + ), + ); + if (result.ok) { + logInfo("Telegram channel added"); + } else { + logWarn("Telegram setup failed — configure it via the web dashboard after launch"); + } + } else { + logInfo("No token entered — set up Telegram via the web dashboard after launch"); + } + } + if (enabledSteps?.has("whatsapp")) { 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; export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + - "openclaw channels login --channel whatsapp"; + const whatsappCmd = `source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw channels login --channel whatsapp`; prepareStdinForHandoff(); await cloud.interactiveSession(whatsappCmd); }