fix: getDashboardInfo uses spawn+Promise to handle long-running process

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-15 14:09:41 +08:00
parent 516d0d26ee
commit 64cd7432e1
2 changed files with 41 additions and 22 deletions

View File

@@ -20,10 +20,9 @@ class ClawClient {
this._dashInfo = {}; // { dashboard_token, dashboard_port } this._dashInfo = {}; // { dashboard_token, dashboard_port }
} }
start() { async start() {
console.log(`[clawd] 启动中... 服务器 = ${this._cfg.server}`); console.log(`[clawd] 启动中... 服务器 = ${this._cfg.server}`);
// 启动前提取 openclaw dashboard 信息(耗时操作放后台,不阻塞连接) this._dashInfo = await getDashboardInfo();
this._dashInfo = getDashboardInfo();
this._connect(); this._connect();
} }

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const { execSync, spawn, execFileSync } = require('child_process'); const { execSync, spawn } = require('child_process');
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
@@ -16,28 +16,48 @@ const FRPC_CONFIG = path.join(CONFIG_DIR, 'frpc.toml');
const FRP_VERSION = '0.62.0'; const FRP_VERSION = '0.62.0';
/** /**
* 提取 openclaw dashboard 的访问 token 和端口。 * 启动 openclaw dashboard(若未运行),等待输出中出现 Dashboard URL
* 执行 `openclaw dashboard`,从输出中解析 Dashboard URL * 解析并返回 { dashboard_token, dashboard_port }
* 返回 { dashboard_token, dashboard_port } 或 {}(命令不存在/失败时) * 超时或命令不存在时返回 {}
* dashboard 进程会持续运行(不会被 kill
*/ */
function getDashboardInfo() { function getDashboardInfo() {
return new Promise((resolve) => {
let done = false;
const finish = (result) => {
if (!done) { done = true; resolve(result); }
};
// 8 秒内未收到 URL 则放弃
const timer = setTimeout(() => finish({}), 8000);
let proc;
try { try {
const out = execSync( proc = spawn('openclaw', ['dashboard'], {
`openclaw dashboard 2>&1 | grep 'Dashboard URL' | sed -E 's|.*:([0-9]+)/.*#token=([a-f0-9]+).*|\\1 \\2|'`, stdio: ['ignore', 'pipe', 'pipe'],
{ timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] } });
).toString().trim();
if (!out) return {};
const [portStr, token] = out.split(' ');
const port = parseInt(portStr, 10);
if (!token || isNaN(port)) return {};
console.log(`[frpc] openclaw dashboard: port=${port}, token=${token.substring(0, 8)}...`);
return { dashboard_port: port, dashboard_token: token };
} catch (e) { } catch (e) {
// openclaw 未安装或命令失败,跳过 clearTimeout(timer);
return {}; 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({}); });
});
} }
/** /**