

















@@ -1,12 +1,12 @@
11#!/usr/bin/env -S node --import tsx
223-import { spawn } from "node:child_process";
43import { createHash } from "node:crypto";
54import { copyFile, mkdir, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
65import { tmpdir } from "node:os";
76import path from "node:path";
87import { pathToFileURL } from "node:url";
98import { normalizeCredentialPayloadForKind } from "../../qa/convex-credential-broker/convex/payload-validation.js";
9+import { fetchJsonWithTimeout, runCommand } from "./telegram-user-credential-io.ts";
1010import { expandHome, writePrivateJson } from "./telegram-user-credential-paths.ts";
11111212type JsonObject = Record<string, unknown>;
@@ -17,6 +17,14 @@ const DEFAULT_BOT_CREDENTIALS_FILE =
1717const DEFAULT_CONVEX_ENV_FILE = "~/.codex/skills/custom/telegram-e2e-bot-to-bot/convex.local.env";
1818const CHUNKED_PAYLOAD_MARKER = "__openclawQaCredentialPayloadChunksV1";
1919const TELEGRAM_USER_QA_CREDENTIAL_KIND = "telegram-user";
20+const COMMAND_TIMEOUT_MS = optionalPositiveInteger(
21+process.env.OPENCLAW_TELEGRAM_USER_CREDENTIAL_COMMAND_TIMEOUT_MS?.trim(),
22+120_000,
23+);
24+const BROKER_TIMEOUT_MS = optionalPositiveInteger(
25+process.env.OPENCLAW_TELEGRAM_USER_CREDENTIAL_BROKER_TIMEOUT_MS?.trim(),
26+30_000,
27+);
20282129function usage(): never {
2230throw new Error(
@@ -162,32 +170,6 @@ async function tgzBase64(path: string) {
162170return (await readFile(path)).toString("base64");
163171}
164172165-function runCommand(command: string, args: string[], cwd?: string) {
166-return new Promise<void>((resolve, reject) => {
167-const child = spawn(command, args, {
168- cwd,
169-stdio: ["ignore", "pipe", "pipe"],
170-});
171-let stdout = "";
172-let stderr = "";
173-child.stdout.on("data", (chunk: Buffer) => {
174-stdout += chunk.toString();
175-});
176-child.stderr.on("data", (chunk: Buffer) => {
177-stderr += chunk.toString();
178-});
179-child.on("error", reject);
180-child.on("close", (code, signal) => {
181-if (code === 0) {
182-resolve();
183-return;
184-}
185-const detail = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
186-reject(new Error(`${command} ${args.join(" ")} failed with ${detail}\n${stdout}${stderr}`));
187-});
188-});
189-}
190-191173function joinBrokerEndpoint(siteUrl: string, endpoint: string) {
192174const normalized = siteUrl.replace(/\/+$/u, "");
193175return `${normalized}/qa-credentials/v1/${endpoint}`;
@@ -210,15 +192,19 @@ async function postBroker(params: {
210192siteUrl: string;
211193token: string;
212194}) {
213-const response = await fetch(joinBrokerEndpoint(params.siteUrl, params.action), {
214-method: "POST",
215-headers: {
216-authorization: `Bearer ${params.token}`,
217-"content-type": "application/json",
195+const { payload, response } = await fetchJsonWithTimeout({
196+url: joinBrokerEndpoint(params.siteUrl, params.action),
197+label: `credential broker ${params.action}`,
198+timeoutMs: BROKER_TIMEOUT_MS,
199+init: {
200+method: "POST",
201+headers: {
202+authorization: `Bearer ${params.token}`,
203+"content-type": "application/json",
204+},
205+body: JSON.stringify(params.body),
218206},
219-body: JSON.stringify(params.body),
220207});
221-const payload = (await response.json()) as JsonObject;
222208if (!response.ok) {
223209assertBrokerSuccess(payload, params.action);
224210throw new Error(`${params.action} failed with HTTP ${response.status}.`);
@@ -371,29 +357,36 @@ async function createTelegramUserPayload(opts: Map<string, string>) {
371357const tdlibArchive = path.join(tempRoot, "tdlib.tgz");
372358const desktopArchive = path.join(tempRoot, "desktop-tdata.tgz");
373359try {
374-await runCommand("tar", ["-C", userDriverDir, "-czf", tdlibArchive, "db", "files"]);
360+await runCommand("tar", ["-C", userDriverDir, "-czf", tdlibArchive, "db", "files"], undefined, {
361+timeoutMs: COMMAND_TIMEOUT_MS,
362+});
375363if (desktopTdataArchiveInput) {
376364await copyFile(expandHome(desktopTdataArchiveInput), desktopArchive);
377365} else {
378-await runCommand("tar", [
379-"-C",
380-path.dirname(expandHome(desktopTdataDir!)),
381-"--exclude",
382-"tdata/countries",
383-"--exclude",
384-"tdata/dictionaries",
385-"--exclude",
386-"tdata/dumps",
387-"--exclude",
388-"tdata/emoji",
389-"--exclude",
390-"tdata/user_data",
391-"--exclude",
392-"tdata/working",
393-"-czf",
394-desktopArchive,
395-"tdata",
396-]);
366+await runCommand(
367+"tar",
368+[
369+"-C",
370+path.dirname(expandHome(desktopTdataDir!)),
371+"--exclude",
372+"tdata/countries",
373+"--exclude",
374+"tdata/dictionaries",
375+"--exclude",
376+"tdata/dumps",
377+"--exclude",
378+"tdata/emoji",
379+"--exclude",
380+"tdata/user_data",
381+"--exclude",
382+"tdata/working",
383+"-czf",
384+desktopArchive,
385+"tdata",
386+],
387+undefined,
388+{ timeoutMs: COMMAND_TIMEOUT_MS },
389+);
397390}
398391399392const payload = parseTelegramUserQaCredentialPayload({
@@ -461,8 +454,12 @@ async function restoreTelegramUserPayload(params: {
461454throw new Error("Telegram Desktop archive SHA-256 mismatch.");
462455}
463456464-await runCommand("tar", ["-C", expandHome(userDriverDir), "-xzf", tdlibArchive]);
465-await runCommand("tar", ["-C", expandHome(desktopWorkdir), "-xzf", desktopArchive]);
457+await runCommand("tar", ["-C", expandHome(userDriverDir), "-xzf", tdlibArchive], undefined, {
458+timeoutMs: COMMAND_TIMEOUT_MS,
459+});
460+await runCommand("tar", ["-C", expandHome(desktopWorkdir), "-xzf", desktopArchive], undefined, {
461+timeoutMs: COMMAND_TIMEOUT_MS,
462+});
466463await writePrivateJson(`${expandHome(userDriverDir)}/config.local.json`, {
467464apiId: Number(requireString(payload, "telegramApiId")),
468465apiHash: requireString(payload, "telegramApiHash"),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。