@lark-apaas/openclaw-scripts-diagnose-cli 0.1.15-alpha.1 → 0.1.15-alpha.3

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 +105 -33
  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.1";
55
+ return "0.1.15-alpha.3";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -2543,9 +2543,13 @@ const PROVIDER_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-provider-
2543
2543
  const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-secrets.json";
2544
2544
  /** Absolute path to the openclaw config JSON. */
2545
2545
  const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
2546
- /** upgrade-lark 每次运行的日志文件路径,含时间戳便于按时间排序定位。 */
2547
- function upgradeLarkLogFile(runId) {
2548
- return `${DIAGNOSE_DIR}/upgrade-lark-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}-${runId.slice(0, 8)}.log`;
2546
+ /**
2547
+ * upgrade-lark 每次运行的日志文件路径,含时间戳便于按时间排序定位。
2548
+ * checkOnly=true 时文件名含 "-check" 后缀,便于与正式安装日志区分。
2549
+ */
2550
+ function upgradeLarkLogFile(runId, checkOnly = false) {
2551
+ const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-");
2552
+ return `${DIAGNOSE_DIR}/upgrade-lark${checkOnly ? "-check" : ""}-${ts}-${runId.slice(0, 8)}.log`;
2549
2553
  }
2550
2554
  //#endregion
2551
2555
  //#region src/lark-cli-init.ts
@@ -10490,6 +10494,7 @@ function runChannelsProbe(timeoutMs = 6e4, { ignoreProbeFailed = true } = {}) {
10490
10494
  }
10491
10495
  if (stdout.trim()) return {
10492
10496
  available: true,
10497
+ rawOutput: stdout.trim(),
10493
10498
  ...parseChannelsProbeOutput(stdout, { ignoreProbeFailed })
10494
10499
  };
10495
10500
  return {
@@ -10501,6 +10506,22 @@ function runChannelsProbe(timeoutMs = 6e4, { ignoreProbeFailed = true } = {}) {
10501
10506
  error: execError ?? "no output from openclaw channels status --probe"
10502
10507
  };
10503
10508
  }
10509
+ /**
10510
+ * 判断 channels probe 结果是否处于"仅一个 Feishu 默认账号、enabled 但未配置"的状态。
10511
+ *
10512
+ * 这是插件全新安装后的初始状态:npx 工具创建了一个默认账号占位,但用户尚未
10513
+ * 填写 AppID / Secret,因此账号显示为 enabled 但 not configured。
10514
+ * 此时 anyAccountWorking=false(configured 缺失),但安装本身是成功的。
10515
+ *
10516
+ * 用于安装后校验的补充分支:当安装前 channels 不可用,且安装后恰好处于此状态时,
10517
+ * 视为安装成功,不触发回滚。
10518
+ */
10519
+ function isDefaultOnlyState(result) {
10520
+ if (result.accounts.length !== 1) return false;
10521
+ const acct = result.accounts[0];
10522
+ const bitTokens = new Set(acct.bits.map((b) => b.trim().split(":")[0]));
10523
+ return bitTokens.has("enabled") && !bitTokens.has("configured");
10524
+ }
10504
10525
  //#endregion
10505
10526
  //#region src/innerapi/reportCliRun.ts
10506
10527
  /**
@@ -10581,7 +10602,7 @@ async function reportCliRun(opts) {
10581
10602
  //#region src/help.ts
10582
10603
  const BIN = "mclaw-diagnose";
10583
10604
  function versionBanner() {
10584
- return `v0.1.15-alpha.1`;
10605
+ return `v0.1.15-alpha.3`;
10585
10606
  }
10586
10607
  const COMMANDS = [
10587
10608
  {
@@ -10875,7 +10896,7 @@ EXIT CODES
10875
10896
  hidden: false,
10876
10897
  summary: "Upgrade the Feishu/Lark plugin via @larksuite/openclaw-lark-tools",
10877
10898
  help: `USAGE
10878
- ${BIN} upgrade-lark [--check-only] [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
10899
+ ${BIN} upgrade-lark [--check] [--skip-restart] [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
10879
10900
 
10880
10901
  DESCRIPTION
10881
10902
  Upgrades the Feishu/Lark plugin by running:
@@ -10896,6 +10917,10 @@ DESCRIPTION
10896
10917
  If the upgrade command fails, or validation fails, the backed-up files are
10897
10918
  restored to roll back the changes.
10898
10919
 
10920
+ After a successful upgrade, the openclaw service is restarted via
10921
+ /opt/force/bin/openclaw_scripts/restart.sh
10922
+ Pass --skip-restart to skip this step (e.g. when restart is handled externally).
10923
+
10899
10924
  Execution is logged to /tmp/openclaw-diagnose/upgrade-lark-<runId>.log.
10900
10925
 
10901
10926
  Output is a single JSON object on stdout:
@@ -10903,13 +10928,14 @@ DESCRIPTION
10903
10928
  { "ok": false, "error": "...", "stderr": "...", "exitCode": 1,
10904
10929
  "rollbackOk": true, "validationError": "...", "logFile": "..." }
10905
10930
 
10906
- With --check-only:
10931
+ With --check:
10907
10932
  { "ok": true, "skipped": true, "upgradeNeeded": false, "logFile": "..." }
10908
10933
  { "ok": true, "skipped": true, "upgradeNeeded": true, "logFile": "..." } ← exit 1
10909
10934
 
10910
10935
  OPTIONS
10911
- --check-only Diagnose only: run the pre-check gate and report whether
10936
+ --check Diagnose only: run the pre-check gate and report whether
10912
10937
  upgrade is needed without installing. Exit 1 if needed.
10938
+ --skip-restart Skip the post-install service restart (default: restart).
10913
10939
  --scene=<scene> Telemetry label forwarded to Slardar only.
10914
10940
  Known values: PageUpgradeLark, etc. Custom strings accepted.
10915
10941
  --caller=<name> Optional metadata passed to innerapi.
@@ -10919,7 +10945,7 @@ EXIT CODES
10919
10945
  0 Success: upgrade ran and all validations passed; or gate skipped upgrade.
10920
10946
  1 Failure: npx error, validation failed, or git commit failed.
10921
10947
  File rollback was attempted (see rollbackOk in the JSON output).
10922
- With --check-only: exit 1 means upgrade IS needed.
10948
+ With --check: exit 1 means upgrade IS needed.
10923
10949
  `
10924
10950
  },
10925
10951
  {
@@ -11162,10 +11188,19 @@ function reportDoctorRunToSlardar(opts) {
11162
11188
  }
11163
11189
  });
11164
11190
  }
11165
- /** 读取日志文件全文;文件不存在或读取失败时返回空字符串。 */
11166
- function readLogFile(filePath) {
11191
+ const LOG_PREFIX_RE = /^\[\d{4}-\d{2}-\d{2}T[\d:.]+Z\](?:\s+\[run=[^\]]+\])?\s*/;
11192
+ /**
11193
+ * 读取日志文件末尾最多 maxBytes 字节,过滤噪音后返回纯内容。
11194
+ * - 剥除每行的时间戳 + run tag 前缀
11195
+ * - 过滤纯分隔符行(全为 '=')
11196
+ * 日志头部为固定 banner,有价值的内容集中在末尾,从尾部截取。
11197
+ */
11198
+ function readLogFileTail(filePath, maxBytes = 4e3) {
11167
11199
  try {
11168
- return node_fs.default.readFileSync(filePath, "utf-8");
11200
+ const buf = node_fs.default.readFileSync(filePath);
11201
+ let start = buf.length > maxBytes ? buf.length - maxBytes : 0;
11202
+ while (start < buf.length && buf[start] !== 10) start++;
11203
+ return buf.subarray(start > 0 ? start + 1 : 0).toString("utf-8").split("\n").map((line) => line.replace(LOG_PREFIX_RE, "")).filter((line) => !/^=+$/.test(line.trim())).join("\n").trim();
11169
11204
  } catch {
11170
11205
  return "";
11171
11206
  }
@@ -11174,21 +11209,22 @@ function readLogFile(filePath) {
11174
11209
  * 向 Slardar 上报 upgrade-lark 运行结果(upgrade_lark_run 事件)。
11175
11210
  *
11176
11211
  * extraCategories 记录字符串维度:scene、exit_code、rollback_ok、
11177
- * validation_error、error_msg、log_content(日志文件全文)。
11212
+ * validation_error、error_msg、log_content(日志文件末尾 4000 字节,含关键结果)。
11178
11213
  *
11179
11214
  * extraMetrics 记录各阶段耗时(毫秒);未执行的阶段上报 -1 作为哨兵值,
11180
11215
  * 便于在 Slardar 查询时区分"未运行"和"运行了 0ms"。
11181
11216
  */
11182
11217
  function reportUpgradeLarkToSlardar(opts) {
11183
- console.error(`[slardar] upgrade_lark_run scene=${opts.scene ?? ""} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
11218
+ console.error(`[slardar] upgrade_lark_run scene=${opts.scene ?? ""} checkOnly=${opts.checkOnly} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
11184
11219
  const t = opts.timing ?? {};
11185
- const logContent = readLogFile(opts.logFile);
11220
+ const logContent = readLogFileTail(opts.logFile);
11186
11221
  reportTask({
11187
11222
  eventName: "upgrade_lark_run",
11188
11223
  durationMs: opts.durationMs,
11189
11224
  status: opts.success ? "success" : "failed",
11190
11225
  extraCategories: {
11191
11226
  scene: opts.scene ?? "",
11227
+ check_only: String(opts.checkOnly),
11192
11228
  exit_code: String(opts.exitCode ?? ""),
11193
11229
  rollback_ok: opts.rollbackOk != null ? String(opts.rollbackOk) : "",
11194
11230
  validation_error: opts.validationError ?? "",
@@ -11219,6 +11255,7 @@ function backupFiles(opts) {
11219
11255
  node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
11220
11256
  log(` backed up: openclaw.json (${stat.size} bytes)`);
11221
11257
  } else log(` skipped: openclaw.json (not found)`);
11258
+ node_fs.default.mkdirSync(node_path.default.join(backupDir, "extensions"), { recursive: true });
11222
11259
  const extSrc = node_path.default.join(workspaceDir, "extensions");
11223
11260
  for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11224
11261
  const src = node_path.default.join(extSrc, pluginDir);
@@ -11240,23 +11277,32 @@ function backupFiles(opts) {
11240
11277
  function restoreFiles(opts) {
11241
11278
  const { workspaceDir, configPath, backupDir, log } = opts;
11242
11279
  try {
11280
+ if (node_fs.default.existsSync(configPath)) {
11281
+ node_fs.default.rmSync(configPath, { force: true });
11282
+ log(` deleted: openclaw.json`);
11283
+ }
11284
+ const extDst = node_path.default.join(workspaceDir, "extensions");
11285
+ for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11286
+ const dst = node_path.default.join(extDst, pluginDir);
11287
+ if (node_fs.default.existsSync(dst)) {
11288
+ node_fs.default.rmSync(dst, {
11289
+ recursive: true,
11290
+ force: true
11291
+ });
11292
+ log(` deleted: extensions/${pluginDir}`);
11293
+ }
11294
+ }
11243
11295
  const configBackup = node_path.default.join(backupDir, "openclaw.json");
11244
11296
  if (node_fs.default.existsSync(configBackup)) {
11245
11297
  node_fs.default.copyFileSync(configBackup, configPath);
11246
11298
  log(` restored: openclaw.json`);
11247
- }
11248
- const extDst = node_path.default.join(workspaceDir, "extensions");
11299
+ } else log(` skipped restore: openclaw.json (not in backup — was not present before upgrade)`);
11249
11300
  for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11250
11301
  const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
11251
11302
  if (node_fs.default.existsSync(backupSrc)) {
11252
- const dst = node_path.default.join(extDst, pluginDir);
11253
- if (node_fs.default.existsSync(dst)) node_fs.default.rmSync(dst, {
11254
- recursive: true,
11255
- force: true
11256
- });
11257
- node_fs.default.cpSync(backupSrc, dst, { recursive: true });
11303
+ node_fs.default.cpSync(backupSrc, node_path.default.join(extDst, pluginDir), { recursive: true });
11258
11304
  log(` restored: extensions/${pluginDir}`);
11259
- }
11305
+ } else log(` skipped restore: extensions/${pluginDir} (not in backup — was not present before upgrade)`);
11260
11306
  }
11261
11307
  return true;
11262
11308
  } catch (e) {
@@ -11318,6 +11364,7 @@ function probeChannels(label, log, timeoutMs) {
11318
11364
  if (r.error) log(` ${label} error: ${r.error}`);
11319
11365
  if (r.gatewayReachable != null) log(` ${label} gatewayReachable: ${r.gatewayReachable}`);
11320
11366
  for (const acct of r.accounts ?? []) log(` ${label} account ${acct.id}: isWorking=${acct.isWorking} bits=[${acct.bits.join(",")}]`);
11367
+ if (r.rawOutput) log(` ${label} raw output:\n${r.rawOutput}`);
11321
11368
  return r;
11322
11369
  } catch (e) {
11323
11370
  log(` ${label} channels probe threw: ${e.message}`);
@@ -11333,7 +11380,7 @@ function probeChannels(label, log, timeoutMs) {
11333
11380
  function runUpgradeLark(opts) {
11334
11381
  const cwd = opts.cwd ?? "/home/gem/workspace/agent";
11335
11382
  const configPath = opts.configPath ?? CONFIG_PATH;
11336
- const logFile = upgradeLarkLogFile(opts.runId);
11383
+ const logFile = upgradeLarkLogFile(opts.runId, opts.checkOnly);
11337
11384
  const log = makeLogger(logFile);
11338
11385
  const fsOpts = {
11339
11386
  workspaceDir: cwd,
@@ -11413,14 +11460,14 @@ function runUpgradeLark(opts) {
11413
11460
  }
11414
11461
  log(` PROCEED: requiresLarkUpgrade=true (version=${versionIncompatible}, feishuConfig=${feishuConfigInvalid}) AND channels not working → running upgrade`);
11415
11462
  if (opts.checkOnly) {
11416
- log(` check-only: upgrade IS needed returning without installing`);
11463
+ log(` --check 模式:需要升级不执行安装,直接返回`);
11417
11464
  log(`${"=".repeat(60)}`);
11418
- log("upgrade-lark check-only complete");
11465
+ log("upgrade-lark check complete");
11419
11466
  log(`${"=".repeat(60)}`);
11420
11467
  return {
11421
11468
  ok: true,
11422
11469
  skipped: true,
11423
- skipReason: "check-only",
11470
+ skipReason: "check",
11424
11471
  upgradeNeeded: true,
11425
11472
  timing,
11426
11473
  logFile
@@ -11523,9 +11570,11 @@ function runUpgradeLark(opts) {
11523
11570
  timing.postProbeMs = Date.now() - t_postProbeStart;
11524
11571
  log(` feishu config invalid after: ${afterChannels.feishuConfigInvalid}`);
11525
11572
  const stillNeedsUpgrade = (afterVersionIncompatible || afterChannels.feishuConfigInvalid) && !afterChannels.anyAccountWorking;
11526
- log(` post-check: stillNeedsUpgrade=${stillNeedsUpgrade} (version=${afterVersionIncompatible}, feishuConfig=${afterChannels.feishuConfigInvalid}, channelsWorking=${afterChannels.anyAccountWorking})`);
11527
- if (stillNeedsUpgrade) return doRollback(`post-install diagnosis still shows anomaly: versionIncompatible=${afterVersionIncompatible}, feishuConfigInvalid=${afterChannels.feishuConfigInvalid}, anyAccountWorking=${afterChannels.anyAccountWorking}`);
11528
- log(" post-install diagnosis: ok (upgrade conditions resolved)");
11573
+ const isNewDefaultOnly = !beforeChannels.anyAccountWorking && isDefaultOnlyState(afterChannels);
11574
+ log(` post-check: stillNeedsUpgrade=${stillNeedsUpgrade} (version=${afterVersionIncompatible}, feishuConfig=${afterChannels.feishuConfigInvalid}, channelsWorking=${afterChannels.anyAccountWorking}) isNewDefaultOnly=${isNewDefaultOnly}`);
11575
+ if (stillNeedsUpgrade && !isNewDefaultOnly) return doRollback(`post-install diagnosis still shows anomaly: versionIncompatible=${afterVersionIncompatible}, feishuConfigInvalid=${afterChannels.feishuConfigInvalid}, anyAccountWorking=${afterChannels.anyAccountWorking}`);
11576
+ if (isNewDefaultOnly) log(" post-install diagnosis: ok (new default account — plugin installed, awaiting configuration)");
11577
+ else log(" post-install diagnosis: ok (upgrade conditions resolved)");
11529
11578
  log("");
11530
11579
  log("── [6/6] doctor --fix ────────────────────────────────────");
11531
11580
  const fixArgs = ["doctor", "--fix"];
@@ -11547,6 +11596,26 @@ function runUpgradeLark(opts) {
11547
11596
  if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
11548
11597
  log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
11549
11598
  log("");
11599
+ log("── [7/7] 重启 openclaw 服务 ──────────────────────────────");
11600
+ const restartScript = "/opt/force/bin/openclaw_scripts/restart.sh";
11601
+ if (opts.skipRestart) log(" skipped: --skip-restart");
11602
+ else if (node_fs.default.existsSync(restartScript)) {
11603
+ const t_restart = Date.now();
11604
+ const restartResult = (0, node_child_process.spawnSync)("bash", [restartScript], {
11605
+ encoding: "utf-8",
11606
+ stdio: [
11607
+ "ignore",
11608
+ "pipe",
11609
+ "pipe"
11610
+ ],
11611
+ timeout: 3e4
11612
+ });
11613
+ const restartMs = Date.now() - t_restart;
11614
+ if (restartResult.stdout?.trim()) log(` restart stdout:\n${restartResult.stdout.trim()}`);
11615
+ if (restartResult.stderr?.trim()) log(` restart stderr:\n${restartResult.stderr.trim()}`);
11616
+ log(` restart.sh exit: ${restartResult.status ?? "null"} (${restartMs}ms)${restartResult.error ? ` error: ${restartResult.error.message}` : ""}`);
11617
+ } else log(` skipped: ${restartScript} not found`);
11618
+ log("");
11550
11619
  log(`${"=".repeat(60)}`);
11551
11620
  log("upgrade-lark completed successfully");
11552
11621
  log(`${"=".repeat(60)}`);
@@ -12050,16 +12119,19 @@ async function main() {
12050
12119
  break;
12051
12120
  }
12052
12121
  case "upgrade-lark": {
12053
- const checkOnly = args.includes("--check-only");
12122
+ const checkOnly = args.includes("--check");
12123
+ const skipRestart = args.includes("--skip-restart");
12054
12124
  const result = runUpgradeLark({
12055
12125
  runId: rc.runId,
12056
12126
  scene,
12057
- checkOnly
12127
+ checkOnly,
12128
+ skipRestart
12058
12129
  });
12059
12130
  const upgradeDurationMs = Date.now() - t0;
12060
12131
  console.log(JSON.stringify(result));
12061
12132
  reportUpgradeLarkToSlardar({
12062
12133
  scene,
12134
+ checkOnly,
12063
12135
  durationMs: upgradeDurationMs,
12064
12136
  success: result.ok,
12065
12137
  logFile: result.logFile,
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.1",
3
+ "version": "0.1.15-alpha.3",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {