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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* chore: fix biome export ordering in interactive.ts and manifest.ts

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
Ahmed Abushagur 2026-03-13 13:47:50 -07:00 committed by GitHub
parent 39622b68ab
commit 06bbbcb2a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 39 deletions

View file

@ -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.

View file

@ -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);
}