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

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 +144 -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.13";
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.13`;
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,10 @@ function runUpgradeLark(opts) {
11512
11596
  log(` configPath : ${configPath}`);
11513
11597
  log(`${"=".repeat(60)}`);
11514
11598
  const timing = {};
11599
+ const finalReturn = (r) => {
11600
+ if (r.fixStatus !== void 0) writeFixStatusEvent(r.fixStatus, r, log);
11601
+ return r;
11602
+ };
11515
11603
  log("");
11516
11604
  log("── [Pre-check A] channels probe(升级前)────────────────");
11517
11605
  const t_preProbeStart = Date.now();
@@ -11550,13 +11638,14 @@ function runUpgradeLark(opts) {
11550
11638
  log(`${"=".repeat(60)}`);
11551
11639
  log("upgrade-lark skipped (pre-check gate)");
11552
11640
  log(`${"=".repeat(60)}`);
11553
- return {
11641
+ return finalReturn({
11554
11642
  status: "skipped",
11555
11643
  skipReason: reason,
11556
11644
  upgradeNeeded: false,
11557
11645
  timing,
11558
- logFile
11559
- };
11646
+ logFile,
11647
+ fixStatus: computeFixStatus(opts.scene, "skipped")
11648
+ });
11560
11649
  }
11561
11650
  if (beforeChannels.anyAccountWorking) {
11562
11651
  const reason = "channels are working — upgrade not needed (issue detected but system is functional)";
@@ -11564,13 +11653,14 @@ function runUpgradeLark(opts) {
11564
11653
  log(`${"=".repeat(60)}`);
11565
11654
  log("upgrade-lark skipped (pre-check gate)");
11566
11655
  log(`${"=".repeat(60)}`);
11567
- return {
11656
+ return finalReturn({
11568
11657
  status: "skipped",
11569
11658
  skipReason: reason,
11570
11659
  upgradeNeeded: false,
11571
11660
  timing,
11572
- logFile
11573
- };
11661
+ logFile,
11662
+ fixStatus: computeFixStatus(opts.scene, "skipped")
11663
+ });
11574
11664
  }
11575
11665
  log(` PROCEED: requiresLarkUpgrade=true (version=${versionIncompatible}, feishuConfig=${feishuConfigInvalid}) AND channels not working → running upgrade`);
11576
11666
  if (opts.checkOnly) {
@@ -11578,13 +11668,13 @@ function runUpgradeLark(opts) {
11578
11668
  log(`${"=".repeat(60)}`);
11579
11669
  log("upgrade-lark check complete");
11580
11670
  log(`${"=".repeat(60)}`);
11581
- return {
11671
+ return finalReturn({
11582
11672
  status: "skipped",
11583
11673
  skipReason: "check",
11584
11674
  upgradeNeeded: true,
11585
11675
  timing,
11586
11676
  logFile
11587
- };
11677
+ });
11588
11678
  }
11589
11679
  log("");
11590
11680
  log("── [1/6] 文件备份 ────────────────────────────────────────");
@@ -11594,12 +11684,13 @@ function runUpgradeLark(opts) {
11594
11684
  timing.backupMs = Date.now() - t_backupStart;
11595
11685
  if (!backup.ok) {
11596
11686
  log(`ERROR: ${backup.error}`);
11597
- return {
11687
+ return finalReturn({
11598
11688
  status: "failed",
11599
11689
  error: backup.error,
11600
11690
  timing,
11601
- logFile
11602
- };
11691
+ logFile,
11692
+ fixStatus: computeFixStatus(opts.scene, "failed")
11693
+ });
11603
11694
  }
11604
11695
  log("backup: ok");
11605
11696
  logVersionSnapshot("before-versions", snapshotVersions(cwd, log), log);
@@ -11640,14 +11731,14 @@ function runUpgradeLark(opts) {
11640
11731
  if (statusCheckDelayMs > 0) {
11641
11732
  log("");
11642
11733
  log(`── 等待 ${statusCheckDelayMs / 1e3}s(让 openclaw 服务完成重启) ─────────────`);
11643
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, statusCheckDelayMs);
11734
+ sleepSync(statusCheckDelayMs);
11644
11735
  log("wait done");
11645
11736
  }
11646
11737
  const doRollback = (reason) => {
11647
11738
  log(`ERROR: ${reason}`);
11648
11739
  const rollbackOk = restoreFiles(fsOpts);
11649
11740
  log(`rollback: ${rollbackOk ? "ok" : "FAILED"}`);
11650
- return {
11741
+ return finalReturn({
11651
11742
  status: "failed",
11652
11743
  error: reason,
11653
11744
  validationError: reason,
@@ -11656,8 +11747,9 @@ function runUpgradeLark(opts) {
11656
11747
  exitCode: npxExitCode,
11657
11748
  rollbackOk,
11658
11749
  timing,
11659
- logFile
11660
- };
11750
+ logFile,
11751
+ fixStatus: computeFixStatus(opts.scene, "failed")
11752
+ });
11661
11753
  };
11662
11754
  log("");
11663
11755
  log("── [4/5] 安装后诊断校验 ─────────────────────────────────");
@@ -11709,8 +11801,9 @@ function runUpgradeLark(opts) {
11709
11801
  if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
11710
11802
  log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
11711
11803
  log("");
11712
- log("── [7/7] 重启 openclaw 服务 ──────────────────────────────");
11804
+ log("── [7/8] 重启 openclaw 服务 ──────────────────────────────");
11713
11805
  const restartScript = "/opt/force/bin/openclaw_scripts/restart.sh";
11806
+ let restartExecuted = false;
11714
11807
  if (opts.skipRestart) log(" skipped: --skip-restart");
11715
11808
  else if (node_fs.default.existsSync(restartScript)) {
11716
11809
  const t_restart = Date.now();
@@ -11727,19 +11820,39 @@ function runUpgradeLark(opts) {
11727
11820
  if (restartResult.stdout?.trim()) log(` restart stdout:\n${restartResult.stdout.trim()}`);
11728
11821
  if (restartResult.stderr?.trim()) log(` restart stderr:\n${restartResult.stderr.trim()}`);
11729
11822
  log(` restart.sh exit: ${restartResult.status ?? "null"} (${restartMs}ms)${restartResult.error ? ` error: ${restartResult.error.message}` : ""}`);
11823
+ restartExecuted = true;
11730
11824
  } else log(` skipped: ${restartScript} not found`);
11731
11825
  log("");
11826
+ log("── [8/8] 端口存活检测 ────────────────────────────────────");
11827
+ let portCheckOk;
11828
+ if (!restartExecuted) log(" skipped: restart was not executed");
11829
+ else {
11830
+ const t_portCheck = Date.now();
11831
+ const pcResult = pollPortCheck({
11832
+ port: 18789,
11833
+ initialWaitMs: opts.portCheckInitialWaitMs ?? 3e3,
11834
+ intervalMs: opts.portCheckIntervalMs ?? 1e3,
11835
+ maxAttempts: opts.portCheckMaxAttempts ?? 30,
11836
+ log
11837
+ });
11838
+ timing.portCheckMs = Date.now() - t_portCheck;
11839
+ portCheckOk = pcResult.ok;
11840
+ log(` port-check result: ${portCheckOk ? "ok" : "FAILED"} (${timing.portCheckMs}ms)`);
11841
+ }
11842
+ log("");
11732
11843
  log(`${"=".repeat(60)}`);
11733
11844
  log("upgrade-lark completed successfully");
11734
11845
  log(`${"=".repeat(60)}`);
11735
- return {
11846
+ return finalReturn({
11736
11847
  status: "success",
11737
11848
  stdout: npxStdout,
11738
11849
  stderr: npxStderr,
11739
11850
  exitCode: npxExitCode,
11851
+ portCheckOk,
11740
11852
  timing,
11741
- logFile
11742
- };
11853
+ logFile,
11854
+ fixStatus: computeFixStatus(opts.scene, "success", portCheckOk)
11855
+ });
11743
11856
  }
11744
11857
  //#endregion
11745
11858
  //#region src/index.ts
@@ -12255,6 +12368,7 @@ async function main() {
12255
12368
  rollbackOk: result.rollbackOk,
12256
12369
  validationError: result.validationError,
12257
12370
  error: result.error,
12371
+ portCheckOk: result.portCheckOk,
12258
12372
  timing: result.timing
12259
12373
  });
12260
12374
  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.13",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {