@lark-apaas/openclaw-scripts-diagnose-cli 0.1.15-alpha.12 → 0.1.15-alpha.14

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/dist/index.cjs +160 -30
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -52,7 +52,7 @@ node_assert = __toESM(node_assert);
52
52
  * it terse and parseable.
53
53
  */
54
54
  function getVersion() {
55
- return "0.1.15-alpha.12";
55
+ return "0.1.15-alpha.14";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -2634,6 +2634,12 @@ const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-s
2634
2634
  /** Absolute path to the openclaw config JSON. */
2635
2635
  const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
2636
2636
  /**
2637
+ * upgrade-lark 场景专属修复状态的信号文件目录。
2638
+ * fixStatus 有值时在此目录下创建同名文件(如 /tmp/event/PORT_FIX_READY),
2639
+ * 文件内容为完整的 UpgradeLarkResult JSON,供外部进程轮询感知升级结果。
2640
+ */
2641
+ const FIX_EVENT_DIR = "/tmp/event";
2642
+ /**
2637
2643
  * upgrade-lark 每次运行的日志文件路径,含时间戳便于按时间排序定位。
2638
2644
  * checkOnly=true 时文件名含 "-check" 后缀,便于与正式安装日志区分。
2639
2645
  */
@@ -3313,6 +3319,13 @@ function execCaptureErr(cmd) {
3313
3319
  throw new Error(stderrStr ? `${base}\nstderr: ${stderrStr}` : base);
3314
3320
  }
3315
3321
  }
3322
+ /**
3323
+ * Synchronous sleep using Atomics.wait on a shared buffer.
3324
+ * Works on the main thread (unlike setTimeout which requires an event loop tick).
3325
+ */
3326
+ function sleepSync(ms) {
3327
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
3328
+ }
3316
3329
  /** POSIX single-quote shell escape. Paths with embedded quotes are rare but
3317
3330
  * the token-file path conventions in sandboxes don't guarantee cleanliness. */
3318
3331
  function shellQuote(s) {
@@ -7365,14 +7378,6 @@ function getResetTask(taskId) {
7365
7378
  progress: "等待中..."
7366
7379
  };
7367
7380
  }
7368
- /**
7369
- * Synchronous sleep using Atomics.wait on a shared buffer.
7370
- */
7371
- function sleepSync(ms) {
7372
- const buf = new SharedArrayBuffer(4);
7373
- const arr = new Int32Array(buf);
7374
- Atomics.wait(arr, 0, 0, ms);
7375
- }
7376
7381
  //#endregion
7377
7382
  //#region src/oss/resolveOssFileMap.ts
7378
7383
  /**
@@ -10692,7 +10697,7 @@ async function reportCliRun(opts) {
10692
10697
  //#region src/help.ts
10693
10698
  const BIN = "mclaw-diagnose";
10694
10699
  function versionBanner() {
10695
- return `v0.1.15-alpha.12`;
10700
+ return `v0.1.15-alpha.14`;
10696
10701
  }
10697
10702
  const COMMANDS = [
10698
10703
  {
@@ -11295,6 +11300,7 @@ function reportDoctorRunToSlardar(opts) {
11295
11300
  * - rollback_ok:回滚是否成功(对应 UpgradeLarkResult.rollbackOk,未触发回滚时为空)
11296
11301
  * - validation_error:安装后校验失败信息(对应 UpgradeLarkResult.validationError)
11297
11302
  * - error:命令级错误信息(对应 UpgradeLarkResult.error)
11303
+ * - port_check_ok:端口存活检测结果(对应 UpgradeLarkResult.portCheckOk,未执行时为空)
11298
11304
  * - result:执行结果一行摘要(派生字段,见 buildUpgradeLarkResultSummary)
11299
11305
  * - log_file:日志文件绝对路径(对应 UpgradeLarkResult.logFile)
11300
11306
  *
@@ -11325,6 +11331,7 @@ function reportUpgradeLarkToSlardar(opts) {
11325
11331
  rollback_ok: String(opts.rollbackOk ?? ""),
11326
11332
  validation_error: opts.validationError ?? "",
11327
11333
  error: opts.error ?? "",
11334
+ port_check_ok: String(opts.portCheckOk ?? ""),
11328
11335
  result: opts.resultSummary,
11329
11336
  log_file: opts.logFile
11330
11337
  },
@@ -11334,7 +11341,8 @@ function reportUpgradeLarkToSlardar(opts) {
11334
11341
  backup_ms: t.backupMs ?? -1,
11335
11342
  npx_install_ms: t.npxInstallMs ?? -1,
11336
11343
  post_probe_ms: t.postProbeMs ?? -1,
11337
- doctor_fix_ms: t.doctorFixMs ?? -1
11344
+ doctor_fix_ms: t.doctorFixMs ?? -1,
11345
+ port_check_ms: t.portCheckMs ?? -1
11338
11346
  }
11339
11347
  });
11340
11348
  }
@@ -11493,6 +11501,82 @@ function probeChannels(label, log, timeoutMs) {
11493
11501
  };
11494
11502
  }
11495
11503
  }
11504
+ /**
11505
+ * 根据 scene、最终状态和端口检测结果,计算场景专属修复状态。
11506
+ * scene 不在已知场景列表时返回 undefined。
11507
+ */
11508
+ function computeFixStatus(scene, status, portCheckOk = void 0) {
11509
+ if (scene === "FromPreviewFailed") return status === "success" && portCheckOk !== false ? "PORT_FIX_READY" : "PORT_FIX_FAILED";
11510
+ if (scene === "FromChannelFailed") return status === "success" ? "CHANNEL_FIX_READY" : "CHANNEL_FIX_FAILED";
11511
+ }
11512
+ /**
11513
+ * 轮询端口检测脚本,确认 openclaw-gateway 在重启后监听指定端口。
11514
+ *
11515
+ * 逻辑:
11516
+ * 1. 先等待 initialWaitMs(让服务有时间完成重启)
11517
+ * 2. 循环执行 `bash ${BUILTIN_PATH}/tool/port_check.sh <port>`,至多 maxAttempts 次:
11518
+ * - 输出为空 → 服务尚未就绪,等 intervalMs 后重试
11519
+ * - 输出含端口号 → 端口存活,返回 ok=true
11520
+ * - 输出非空但不含端口号 → 端口异常,立即返回 ok=false
11521
+ * 3. 超过 maxAttempts 仍无响应 → 返回 ok=false
11522
+ *
11523
+ * 注意:${BUILTIN_PATH} 是沙箱 shell 环境变量,通过 `bash -c` 让 shell 自行展开,
11524
+ * Node 侧不读取、不拼接该路径。
11525
+ */
11526
+ function pollPortCheck(opts) {
11527
+ const { port, initialWaitMs, intervalMs, maxAttempts, log } = opts;
11528
+ const portStr = String(port);
11529
+ const cmd = "bash ${BUILTIN_PATH}/tool/port_check.sh " + portStr;
11530
+ log(` waiting ${initialWaitMs / 1e3}s before first port-check poll...`);
11531
+ if (initialWaitMs > 0) sleepSync(initialWaitMs);
11532
+ for (let i = 1; i <= maxAttempts; i++) {
11533
+ const r = (0, node_child_process.spawnSync)("bash", ["-c", cmd], {
11534
+ encoding: "utf-8",
11535
+ stdio: [
11536
+ "ignore",
11537
+ "pipe",
11538
+ "pipe"
11539
+ ],
11540
+ timeout: 1e4
11541
+ });
11542
+ const output = (r.stdout ?? "").trim();
11543
+ const errout = (r.stderr ?? "").trim();
11544
+ log(` port-check [${i}/${maxAttempts}]: exit=${r.status ?? "null"} output=${JSON.stringify(output)}${errout ? ` stderr=${JSON.stringify(errout)}` : ""}`);
11545
+ if (!output) {
11546
+ sleepSync(intervalMs);
11547
+ continue;
11548
+ }
11549
+ if (output.includes(portStr)) {
11550
+ log(` port-check: SUCCESS — port ${port} confirmed in output`);
11551
+ return {
11552
+ ok: true,
11553
+ output
11554
+ };
11555
+ }
11556
+ log(` port-check: FAILED — output non-empty but port ${port} not found`);
11557
+ return {
11558
+ ok: false,
11559
+ output
11560
+ };
11561
+ }
11562
+ log(` port-check: FAILED — no response after ${maxAttempts} attempts`);
11563
+ return { ok: false };
11564
+ }
11565
+ /**
11566
+ * 将 fixStatus 信号文件写入 /tmp/event/<fixStatus>,内容为完整的 UpgradeLarkResult JSON。
11567
+ * 外部进程(如服务端诊断流程)通过轮询此文件感知升级结果。
11568
+ * 写入失败只记录日志,不影响主流程返回值。
11569
+ */
11570
+ function writeFixStatusEvent(fixStatus, result, log) {
11571
+ const filePath = `${FIX_EVENT_DIR}/${fixStatus}`;
11572
+ try {
11573
+ node_fs.default.mkdirSync(FIX_EVENT_DIR, { recursive: true });
11574
+ node_fs.default.writeFileSync(filePath, JSON.stringify(result, null, 2));
11575
+ log(`[fix-event] written: ${filePath}`);
11576
+ } catch (e) {
11577
+ log(`[fix-event] write failed: ${filePath} — ${e.message}`);
11578
+ }
11579
+ }
11496
11580
  function runUpgradeLark(opts) {
11497
11581
  const cwd = opts.cwd ?? "/home/gem/workspace/agent";
11498
11582
  const configPath = opts.configPath ?? CONFIG_PATH;
@@ -11512,6 +11596,26 @@ function runUpgradeLark(opts) {
11512
11596
  log(` configPath : ${configPath}`);
11513
11597
  log(`${"=".repeat(60)}`);
11514
11598
  const timing = {};
11599
+ for (const s of [
11600
+ "PORT_FIX_READY",
11601
+ "PORT_FIX_FAILED",
11602
+ "CHANNEL_FIX_READY",
11603
+ "CHANNEL_FIX_FAILED"
11604
+ ]) {
11605
+ const f = `${FIX_EVENT_DIR}/${s}`;
11606
+ try {
11607
+ if (node_fs.default.existsSync(f)) {
11608
+ node_fs.default.rmSync(f);
11609
+ log(`[fix-event] cleared stale: ${f}`);
11610
+ }
11611
+ } catch (e) {
11612
+ log(`[fix-event] clear failed: ${f} — ${e.message}`);
11613
+ }
11614
+ }
11615
+ const finalReturn = (r) => {
11616
+ if (r.fixStatus !== void 0) writeFixStatusEvent(r.fixStatus, r, log);
11617
+ return r;
11618
+ };
11515
11619
  log("");
11516
11620
  log("── [Pre-check A] channels probe(升级前)────────────────");
11517
11621
  const t_preProbeStart = Date.now();
@@ -11550,13 +11654,14 @@ function runUpgradeLark(opts) {
11550
11654
  log(`${"=".repeat(60)}`);
11551
11655
  log("upgrade-lark skipped (pre-check gate)");
11552
11656
  log(`${"=".repeat(60)}`);
11553
- return {
11657
+ return finalReturn({
11554
11658
  status: "skipped",
11555
11659
  skipReason: reason,
11556
11660
  upgradeNeeded: false,
11557
11661
  timing,
11558
- logFile
11559
- };
11662
+ logFile,
11663
+ fixStatus: computeFixStatus(opts.scene, "skipped")
11664
+ });
11560
11665
  }
11561
11666
  if (beforeChannels.anyAccountWorking) {
11562
11667
  const reason = "channels are working — upgrade not needed (issue detected but system is functional)";
@@ -11564,13 +11669,14 @@ function runUpgradeLark(opts) {
11564
11669
  log(`${"=".repeat(60)}`);
11565
11670
  log("upgrade-lark skipped (pre-check gate)");
11566
11671
  log(`${"=".repeat(60)}`);
11567
- return {
11672
+ return finalReturn({
11568
11673
  status: "skipped",
11569
11674
  skipReason: reason,
11570
11675
  upgradeNeeded: false,
11571
11676
  timing,
11572
- logFile
11573
- };
11677
+ logFile,
11678
+ fixStatus: computeFixStatus(opts.scene, "skipped")
11679
+ });
11574
11680
  }
11575
11681
  log(` PROCEED: requiresLarkUpgrade=true (version=${versionIncompatible}, feishuConfig=${feishuConfigInvalid}) AND channels not working → running upgrade`);
11576
11682
  if (opts.checkOnly) {
@@ -11578,13 +11684,13 @@ function runUpgradeLark(opts) {
11578
11684
  log(`${"=".repeat(60)}`);
11579
11685
  log("upgrade-lark check complete");
11580
11686
  log(`${"=".repeat(60)}`);
11581
- return {
11687
+ return finalReturn({
11582
11688
  status: "skipped",
11583
11689
  skipReason: "check",
11584
11690
  upgradeNeeded: true,
11585
11691
  timing,
11586
11692
  logFile
11587
- };
11693
+ });
11588
11694
  }
11589
11695
  log("");
11590
11696
  log("── [1/6] 文件备份 ────────────────────────────────────────");
@@ -11594,12 +11700,13 @@ function runUpgradeLark(opts) {
11594
11700
  timing.backupMs = Date.now() - t_backupStart;
11595
11701
  if (!backup.ok) {
11596
11702
  log(`ERROR: ${backup.error}`);
11597
- return {
11703
+ return finalReturn({
11598
11704
  status: "failed",
11599
11705
  error: backup.error,
11600
11706
  timing,
11601
- logFile
11602
- };
11707
+ logFile,
11708
+ fixStatus: computeFixStatus(opts.scene, "failed")
11709
+ });
11603
11710
  }
11604
11711
  log("backup: ok");
11605
11712
  logVersionSnapshot("before-versions", snapshotVersions(cwd, log), log);
@@ -11640,14 +11747,14 @@ function runUpgradeLark(opts) {
11640
11747
  if (statusCheckDelayMs > 0) {
11641
11748
  log("");
11642
11749
  log(`── 等待 ${statusCheckDelayMs / 1e3}s(让 openclaw 服务完成重启) ─────────────`);
11643
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, statusCheckDelayMs);
11750
+ sleepSync(statusCheckDelayMs);
11644
11751
  log("wait done");
11645
11752
  }
11646
11753
  const doRollback = (reason) => {
11647
11754
  log(`ERROR: ${reason}`);
11648
11755
  const rollbackOk = restoreFiles(fsOpts);
11649
11756
  log(`rollback: ${rollbackOk ? "ok" : "FAILED"}`);
11650
- return {
11757
+ return finalReturn({
11651
11758
  status: "failed",
11652
11759
  error: reason,
11653
11760
  validationError: reason,
@@ -11656,8 +11763,9 @@ function runUpgradeLark(opts) {
11656
11763
  exitCode: npxExitCode,
11657
11764
  rollbackOk,
11658
11765
  timing,
11659
- logFile
11660
- };
11766
+ logFile,
11767
+ fixStatus: computeFixStatus(opts.scene, "failed")
11768
+ });
11661
11769
  };
11662
11770
  log("");
11663
11771
  log("── [4/5] 安装后诊断校验 ─────────────────────────────────");
@@ -11709,8 +11817,9 @@ function runUpgradeLark(opts) {
11709
11817
  if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
11710
11818
  log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
11711
11819
  log("");
11712
- log("── [7/7] 重启 openclaw 服务 ──────────────────────────────");
11820
+ log("── [7/8] 重启 openclaw 服务 ──────────────────────────────");
11713
11821
  const restartScript = "/opt/force/bin/openclaw_scripts/restart.sh";
11822
+ let restartExecuted = false;
11714
11823
  if (opts.skipRestart) log(" skipped: --skip-restart");
11715
11824
  else if (node_fs.default.existsSync(restartScript)) {
11716
11825
  const t_restart = Date.now();
@@ -11727,19 +11836,39 @@ function runUpgradeLark(opts) {
11727
11836
  if (restartResult.stdout?.trim()) log(` restart stdout:\n${restartResult.stdout.trim()}`);
11728
11837
  if (restartResult.stderr?.trim()) log(` restart stderr:\n${restartResult.stderr.trim()}`);
11729
11838
  log(` restart.sh exit: ${restartResult.status ?? "null"} (${restartMs}ms)${restartResult.error ? ` error: ${restartResult.error.message}` : ""}`);
11839
+ restartExecuted = true;
11730
11840
  } else log(` skipped: ${restartScript} not found`);
11731
11841
  log("");
11842
+ log("── [8/8] 端口存活检测 ────────────────────────────────────");
11843
+ let portCheckOk;
11844
+ if (!restartExecuted) log(" skipped: restart was not executed");
11845
+ else {
11846
+ const t_portCheck = Date.now();
11847
+ const pcResult = pollPortCheck({
11848
+ port: 18789,
11849
+ initialWaitMs: opts.portCheckInitialWaitMs ?? 3e3,
11850
+ intervalMs: opts.portCheckIntervalMs ?? 1e3,
11851
+ maxAttempts: opts.portCheckMaxAttempts ?? 30,
11852
+ log
11853
+ });
11854
+ timing.portCheckMs = Date.now() - t_portCheck;
11855
+ portCheckOk = pcResult.ok;
11856
+ log(` port-check result: ${portCheckOk ? "ok" : "FAILED"} (${timing.portCheckMs}ms)`);
11857
+ }
11858
+ log("");
11732
11859
  log(`${"=".repeat(60)}`);
11733
11860
  log("upgrade-lark completed successfully");
11734
11861
  log(`${"=".repeat(60)}`);
11735
- return {
11862
+ return finalReturn({
11736
11863
  status: "success",
11737
11864
  stdout: npxStdout,
11738
11865
  stderr: npxStderr,
11739
11866
  exitCode: npxExitCode,
11867
+ portCheckOk,
11740
11868
  timing,
11741
- logFile
11742
- };
11869
+ logFile,
11870
+ fixStatus: computeFixStatus(opts.scene, "success", portCheckOk)
11871
+ });
11743
11872
  }
11744
11873
  //#endregion
11745
11874
  //#region src/index.ts
@@ -12255,6 +12384,7 @@ async function main() {
12255
12384
  rollbackOk: result.rollbackOk,
12256
12385
  validationError: result.validationError,
12257
12386
  error: result.error,
12387
+ portCheckOk: result.portCheckOk,
12258
12388
  timing: result.timing
12259
12389
  });
12260
12390
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.15-alpha.12",
3
+ "version": "0.1.15-alpha.14",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {