From 64cd7432e15fee6d6c782bf8cbade772a892a4b1 Mon Sep 17 00:00:00 2001 From: stswangzhiping <59632378+stswangzhiping@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:09:41 +0800 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFfix:=20getDashboardInfo=20uses=20spawn?= =?UTF-8?q?+Promise=20to=20handle=20long-running=20process?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- lib/client.js | 5 ++--- lib/frpc.js | 58 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/lib/client.js b/lib/client.js index 5d6a71e..c388398 100644 --- a/lib/client.js +++ b/lib/client.js @@ -20,10 +20,9 @@ class ClawClient { this._dashInfo = {}; // { dashboard_token, dashboard_port } } - start() { + async start() { console.log(`[clawd] 启动中... 服务器 = ${this._cfg.server}`); - // 启动前提取 openclaw dashboard 信息(耗时操作放后台,不阻塞连接) - this._dashInfo = getDashboardInfo(); + this._dashInfo = await getDashboardInfo(); this._connect(); } diff --git a/lib/frpc.js b/lib/frpc.js index f7d92c4..d0cd75b 100644 --- a/lib/frpc.js +++ b/lib/frpc.js @@ -1,6 +1,6 @@ 'use strict'; -const { execSync, spawn, execFileSync } = require('child_process'); +const { execSync, spawn } = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); @@ -16,28 +16,48 @@ const FRPC_CONFIG = path.join(CONFIG_DIR, 'frpc.toml'); const FRP_VERSION = '0.62.0'; /** - * 提取 openclaw dashboard 的访问 token 和端口。 - * 执行 `openclaw dashboard`,从输出中解析 Dashboard URL。 - * 返回 { dashboard_token, dashboard_port } 或 {}(命令不存在/失败时)。 + * 启动 openclaw dashboard(若未运行),等待输出中出现 Dashboard URL, + * 解析并返回 { dashboard_token, dashboard_port }。 + * 超时或命令不存在时返回 {}。 + * dashboard 进程会持续运行(不会被 kill)。 */ function getDashboardInfo() { - try { - const out = execSync( - `openclaw dashboard 2>&1 | grep 'Dashboard URL' | sed -E 's|.*:([0-9]+)/.*#token=([a-f0-9]+).*|\\1 \\2|'`, - { timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] } - ).toString().trim(); + return new Promise((resolve) => { + let done = false; + const finish = (result) => { + if (!done) { done = true; resolve(result); } + }; - if (!out) return {}; - const [portStr, token] = out.split(' '); - const port = parseInt(portStr, 10); - if (!token || isNaN(port)) return {}; + // 8 秒内未收到 URL 则放弃 + const timer = setTimeout(() => finish({}), 8000); - console.log(`[frpc] openclaw dashboard: port=${port}, token=${token.substring(0, 8)}...`); - return { dashboard_port: port, dashboard_token: token }; - } catch (e) { - // openclaw 未安装或命令失败,跳过 - return {}; - } + let proc; + try { + proc = spawn('openclaw', ['dashboard'], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + } catch (e) { + clearTimeout(timer); + return finish({}); + } + + const handleLine = (line) => { + const match = line.match(/Dashboard URL:.*:(\d+)\/#token=([a-f0-9]+)/); + if (match) { + clearTimeout(timer); + const port = parseInt(match[1], 10); + const token = match[2]; + console.log(`[frpc] openclaw dashboard: port=${port}, token=${token.substring(0, 8)}...`); + // 进程继续运行(dashboard 服务),不 kill + finish({ dashboard_port: port, dashboard_token: token }); + } + }; + + proc.stdout.on('data', d => handleLine(d.toString())); + proc.stderr.on('data', d => handleLine(d.toString())); + proc.on('error', () => { clearTimeout(timer); finish({}); }); + proc.on('exit', () => { clearTimeout(timer); finish({}); }); + }); } /**