openclawsetup 2.8.9 → 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 +93 -40
  2. package/package.json +1 -1
package/bin/cli.mjs CHANGED
@@ -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());
@@ -2573,61 +2643,44 @@ async function showStatusInfo(cliName) {
2573
2643
  const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
2574
2644
 
2575
2645
  console.log(colors.bold(colors.cyan('\n📊 OpenClaw 状态信息\n')));
2576
- console.log(colors.gray(' … 正在检查服务状态(超时自动跳过)...'));
2577
-
2578
- // 服务状态(兼容不同 CLI 版本,避免 Windows 下误判)
2579
- const statusAttempts = [
2580
- { cmd: `${cliName} status`, label: 'status' },
2581
- { cmd: `${cliName} gateway status`, label: 'gateway status' },
2582
- ];
2583
- let statusState = 'unknown';
2584
- let statusOutput = '';
2585
- let statusTimedOut = false;
2586
-
2587
- for (const attempt of statusAttempts) {
2588
- const statusResult = safeExec(attempt.cmd, { timeout: STATUS_CMD_TIMEOUT_MS });
2589
- if (!statusResult.ok && /timed out|etimedout|SIGTERM|killed/i.test(`${statusResult.error || ''} ${statusResult.stderr || ''}`)) {
2590
- statusTimedOut = true;
2591
- continue;
2592
- }
2593
- if (!statusResult.ok || !statusResult.output) continue;
2594
- statusOutput = statusResult.output;
2595
- const parsed = parseStatusOutput(statusResult.output);
2596
- if (parsed === 'running') {
2597
- statusState = 'running';
2598
- break;
2599
- }
2600
- if (parsed === 'stopped') {
2601
- statusState = 'stopped';
2602
- }
2603
- }
2646
+ console.log(colors.gray(' … 正在检查服务状态...'));
2604
2647
 
2605
- // 端口检查(作为状态命令兜底)
2648
+ // 1. 直接通过端口检测进程(不依赖 openclaw status 命令)
2649
+ const processInfo = detectGatewayProcess(port);
2606
2650
  const portResult = getPortCheckOutput(port, PORT_CHECK_TIMEOUT_MS);
2607
2651
  const portListening = Boolean(portResult.ok && portResult.output);
2608
2652
 
2609
- if (statusTimedOut && !portListening) {
2610
- console.log(colors.yellow(' 状态命令超时,已自动切换端口探测模式'));
2611
- }
2612
- if (portResolved.source === 'runtime' && Number(config?.port) !== Number(port)) {
2613
- 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;
2614
2658
  }
2615
2659
 
2616
- if (statusState === 'running') {
2660
+ // 3. 综合判断并显示
2661
+ if (processInfo.found && processInfo.isOpenClaw) {
2617
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;
2618
2672
  } else if (portListening) {
2619
- // 端口在监听 = Gateway 在运行,状态命令超时/不准确不影响判断
2673
+ // 端口在监听但无法获取进程详情(权限不足等)
2674
+ console.log(colors.green(' ✓ Gateway 服务正在运行'));
2675
+ } else if (statusState === 'running') {
2620
2676
  console.log(colors.green(' ✓ Gateway 服务正在运行'));
2621
2677
  } else {
2622
2678
  console.log(colors.red(' ✗ Gateway 服务未运行'));
2623
- if (statusOutput) {
2624
- console.log(colors.gray(` 状态输出: ${statusOutput.split('\n')[0].slice(0, 100)}`));
2625
- }
2626
2679
  console.log(colors.yellow(' → 请先选择「检查修复」自动修复此问题\n'));
2627
2680
  return;
2628
2681
  }
2629
2682
 
2630
- if (portResult.ok && portResult.output) {
2683
+ if (portListening) {
2631
2684
  console.log(colors.green(` ✓ 端口 ${port} 正在监听`));
2632
2685
  } else {
2633
2686
  console.log(colors.red(` ✗ 端口 ${port} 未监听`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawsetup",
3
- "version": "2.8.9",
3
+ "version": "2.8.10",
4
4
  "description": "OpenClaw 安装向导 - 智能安装、诊断、自动修复",
5
5
  "type": "module",
6
6
  "bin": {