openclawsetup 2.8.8 → 2.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/cli.mjs +95 -47
  2. package/package.json +1 -1
package/bin/cli.mjs CHANGED
@@ -83,7 +83,7 @@ const log = {
83
83
 
84
84
  const DEFAULT_GATEWAY_PORT = 18789;
85
85
  const STRONG_FIX_MAX_PASSES = 3;
86
- const STATUS_CMD_TIMEOUT_MS = platform() === 'win32' ? 8000 : 5000;
86
+ const STATUS_CMD_TIMEOUT_MS = platform() === 'win32' ? 15000 : 10000;
87
87
  const PORT_CHECK_TIMEOUT_MS = platform() === 'win32' ? 6000 : 4000;
88
88
  const MODEL_CHAT_TIMEOUT_MS = platform() === 'win32' ? 25000 : 18000;
89
89
  const STRONG_PORT_CANDIDATES = [
@@ -953,6 +953,76 @@ function isLikelyOpenClawProcess(output = '') {
953
953
  );
954
954
  }
955
955
 
956
+ /**
957
+ * 通过端口直接检测 Gateway 进程信息(不依赖 openclaw status 命令)
958
+ * 返回 { found, pid, processName, cmdline, isOpenClaw }
959
+ */
960
+ function detectGatewayProcess(port) {
961
+ const result = { found: false, pid: null, processName: '', cmdline: '', isOpenClaw: false };
962
+
963
+ if (platform() === 'win32') {
964
+ // Windows: netstat 找 PID,再用 wmic 查进程详情
965
+ const netstat = safeExec(`netstat -ano -p tcp | findstr LISTENING | findstr :${port}`, { timeout: 8000 });
966
+ if (!netstat.ok || !netstat.output) return result;
967
+
968
+ // 提取 PID(netstat 输出最后一列是 PID)
969
+ const pidMatch = netstat.output.match(/LISTENING\s+(\d+)/);
970
+ if (!pidMatch) return result;
971
+ result.pid = pidMatch[1];
972
+ result.found = true;
973
+
974
+ // 用 wmic 获取进程命令行
975
+ const wmic = safeExec(`wmic process where "ProcessId=${result.pid}" get CommandLine,Name /format:list`, { timeout: 5000 });
976
+ if (wmic.ok && wmic.output) {
977
+ const nameMatch = wmic.output.match(/Name=(.+)/i);
978
+ const cmdMatch = wmic.output.match(/CommandLine=(.+)/i);
979
+ result.processName = nameMatch ? nameMatch[1].trim() : '';
980
+ result.cmdline = cmdMatch ? cmdMatch[1].trim() : '';
981
+ }
982
+
983
+ // wmic 可能不可用(新版 Windows),回退用 tasklist
984
+ if (!result.processName) {
985
+ const tasklist = safeExec(`tasklist /FI "PID eq ${result.pid}" /FO CSV /NH`, { timeout: 5000 });
986
+ if (tasklist.ok && tasklist.output) {
987
+ const parts = tasklist.output.split(',');
988
+ if (parts.length > 0) {
989
+ result.processName = parts[0].replace(/"/g, '').trim();
990
+ }
991
+ }
992
+ }
993
+ } else {
994
+ // Linux/macOS: lsof 找 PID 和进程名
995
+ const lsof = safeExec(`lsof -nP -iTCP:${port} -sTCP:LISTEN -F pcn 2>/dev/null`, { timeout: 5000 });
996
+ if (lsof.ok && lsof.output) {
997
+ const pidMatch = lsof.output.match(/p(\d+)/);
998
+ const nameMatch = lsof.output.match(/c(.+)/);
999
+ if (pidMatch) {
1000
+ result.pid = pidMatch[1];
1001
+ result.found = true;
1002
+ result.processName = nameMatch ? nameMatch[1].trim() : '';
1003
+ }
1004
+ }
1005
+
1006
+ // 获取完整命令行
1007
+ if (result.pid) {
1008
+ const cmdline = safeExec(`cat /proc/${result.pid}/cmdline 2>/dev/null | tr '\\0' ' ' || ps -p ${result.pid} -o args= 2>/dev/null`, { timeout: 3000 });
1009
+ if (cmdline.ok && cmdline.output) {
1010
+ result.cmdline = cmdline.output.trim();
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ // 判断是否是 OpenClaw 进程
1016
+ const combined = `${result.processName} ${result.cmdline}`.toLowerCase();
1017
+ result.isOpenClaw = combined.includes('openclaw') || combined.includes('clawdbot') || combined.includes('moltbot');
1018
+ // node 进程运行 gateway 也算
1019
+ if (!result.isOpenClaw && combined.includes('node') && combined.includes('gateway')) {
1020
+ result.isOpenClaw = true;
1021
+ }
1022
+
1023
+ return result;
1024
+ }
1025
+
956
1026
  function hasListeningState(output = '') {
957
1027
  if (!output) return false;
958
1028
  if (platform() !== 'win32') return Boolean(output.trim());
@@ -2233,11 +2303,6 @@ async function runHealthCheck(cliName, autoFix = false, strongMode = false) {
2233
2303
  const canUsePortFallback = statusState === 'unknown' || statusProbe.timedOut;
2234
2304
  if (statusState === 'running' || (canUsePortFallback && statusPortListening)) {
2235
2305
  log.success('Gateway 进程正在运行');
2236
- if (statusProbe.timedOut) {
2237
- log.hint('状态命令超时,已按端口监听判定为运行中');
2238
- } else if (canUsePortFallback && statusPortListening) {
2239
- log.hint('状态命令返回非运行,但端口可访问(兼容判定)');
2240
- }
2241
2306
  } else {
2242
2307
  const issue = summarizeIssue(
2243
2308
  'error',
@@ -2578,61 +2643,44 @@ async function showStatusInfo(cliName) {
2578
2643
  const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
2579
2644
 
2580
2645
  console.log(colors.bold(colors.cyan('\n📊 OpenClaw 状态信息\n')));
2581
- console.log(colors.gray(' … 正在检查服务状态(超时自动跳过)...'));
2646
+ console.log(colors.gray(' … 正在检查服务状态...'));
2582
2647
 
2583
- // 服务状态(兼容不同 CLI 版本,避免 Windows 下误判)
2584
- const statusAttempts = [
2585
- { cmd: `${cliName} status`, label: 'status' },
2586
- { cmd: `${cliName} gateway status`, label: 'gateway status' },
2587
- ];
2588
- let statusState = 'unknown';
2589
- let statusOutput = '';
2590
- let statusTimedOut = false;
2591
-
2592
- for (const attempt of statusAttempts) {
2593
- const statusResult = safeExec(attempt.cmd, { timeout: STATUS_CMD_TIMEOUT_MS });
2594
- if (!statusResult.ok && /timed out|etimedout|SIGTERM|killed/i.test(`${statusResult.error || ''} ${statusResult.stderr || ''}`)) {
2595
- statusTimedOut = true;
2596
- continue;
2597
- }
2598
- if (!statusResult.ok || !statusResult.output) continue;
2599
- statusOutput = statusResult.output;
2600
- const parsed = parseStatusOutput(statusResult.output);
2601
- if (parsed === 'running') {
2602
- statusState = 'running';
2603
- break;
2604
- }
2605
- if (parsed === 'stopped') {
2606
- statusState = 'stopped';
2607
- }
2608
- }
2609
-
2610
- // 端口检查(作为状态命令兜底)
2648
+ // 1. 直接通过端口检测进程(不依赖 openclaw status 命令)
2649
+ const processInfo = detectGatewayProcess(port);
2611
2650
  const portResult = getPortCheckOutput(port, PORT_CHECK_TIMEOUT_MS);
2612
2651
  const portListening = Boolean(portResult.ok && portResult.output);
2613
2652
 
2614
- if (statusTimedOut) {
2615
- console.log(colors.yellow(' 状态命令超时,已自动切换端口探测模式'));
2616
- }
2617
- if (portResolved.source === 'runtime' && Number(config?.port) !== Number(port)) {
2618
- console.log(colors.gray(` … 运行时端口为 ${port}(与本地配置文件不一致)`));
2653
+ // 2. 如果直接检测没结果,再尝试 openclaw status 命令
2654
+ let statusState = 'unknown';
2655
+ if (!processInfo.found && !portListening) {
2656
+ const statusProbe = probeGatewayStatus(cliName, STATUS_CMD_TIMEOUT_MS);
2657
+ statusState = statusProbe.state;
2619
2658
  }
2620
2659
 
2621
- if (statusState === 'running') {
2660
+ // 3. 综合判断并显示
2661
+ if (processInfo.found && processInfo.isOpenClaw) {
2622
2662
  console.log(colors.green(' ✓ Gateway 服务正在运行'));
2663
+ console.log(colors.gray(` 进程: ${processInfo.processName || 'node'} (PID ${processInfo.pid})`));
2664
+ } else if (processInfo.found && !processInfo.isOpenClaw) {
2665
+ console.log(colors.yellow(` ⚠ 端口 ${port} 被其他进程占用`));
2666
+ console.log(colors.gray(` 进程: ${processInfo.processName || '未知'} (PID ${processInfo.pid})`));
2667
+ if (processInfo.cmdline) {
2668
+ console.log(colors.gray(` 命令: ${processInfo.cmdline.substring(0, 120)}`));
2669
+ }
2670
+ console.log(colors.yellow(' → 请先选择「检查修复」解决端口冲突\n'));
2671
+ return;
2623
2672
  } else if (portListening) {
2624
- console.log(colors.yellow(' ⚠ 状态命令显示未运行,但端口可访问'));
2625
- console.log(colors.gray(' 兼容模式判定:Gateway 可能正在运行(常见于旧版/Windows)'));
2673
+ // 端口在监听但无法获取进程详情(权限不足等)
2674
+ console.log(colors.green('Gateway 服务正在运行'));
2675
+ } else if (statusState === 'running') {
2676
+ console.log(colors.green(' ✓ Gateway 服务正在运行'));
2626
2677
  } else {
2627
2678
  console.log(colors.red(' ✗ Gateway 服务未运行'));
2628
- if (statusOutput) {
2629
- console.log(colors.gray(` 状态输出: ${statusOutput.split('\n')[0].slice(0, 100)}`));
2630
- }
2631
2679
  console.log(colors.yellow(' → 请先选择「检查修复」自动修复此问题\n'));
2632
2680
  return;
2633
2681
  }
2634
2682
 
2635
- if (portResult.ok && portResult.output) {
2683
+ if (portListening) {
2636
2684
  console.log(colors.green(` ✓ 端口 ${port} 正在监听`));
2637
2685
  } else {
2638
2686
  console.log(colors.red(` ✗ 端口 ${port} 未监听`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawsetup",
3
- "version": "2.8.8",
3
+ "version": "2.8.10",
4
4
  "description": "OpenClaw 安装向导 - 智能安装、诊断、自动修复",
5
5
  "type": "module",
6
6
  "bin": {