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

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 +249 -48
  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.11";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -1770,6 +1770,97 @@ SessionPersistenceRule = __decorate([Rule({
1770
1770
  level: "silent"
1771
1771
  })], SessionPersistenceRule);
1772
1772
  //#endregion
1773
+ //#region src/rules/tools-allow-also-allow-conflict.ts
1774
+ /**
1775
+ * 检测 tools 配置中 allow 与 alsoAllow 同时非空的冲突。
1776
+ *
1777
+ * openclaw 的 Zod schema 在配置加载时会拒绝这种组合,原因是语义互斥:
1778
+ * - allow:完全替换式白名单,只允许列出的工具
1779
+ * - alsoAllow:追加式,在 profile 或默认基线上叠加额外工具
1780
+ * 两者同时存在会导致 openclaw 服务启动失败。
1781
+ *
1782
+ * 修复策略:将 alsoAllow 的条目去重合并进 allow,删除 alsoAllow。
1783
+ * 这是最保守的修复:保留原 allow 的精确白名单,不扩大权限范围。
1784
+ *
1785
+ * 检测范围:
1786
+ * - 顶层 tools
1787
+ * - tools.byProvider.*
1788
+ * - agents.list[].tools
1789
+ * - agents.list[].tools.byProvider.*
1790
+ */
1791
+ let ToolsAllowAlsoAllowConflictRule = class ToolsAllowAlsoAllowConflictRule extends DiagnoseRule {
1792
+ validate(ctx) {
1793
+ const conflicts = [];
1794
+ visitAllScopes(ctx.config, (scope, label) => {
1795
+ console.error(`[tools_allow_also_allow_conflict] scope=${label} hasConflict=${hasConflict(scope)} allow=${JSON.stringify(scope.allow)?.slice(0, 40)} alsoAllow=${JSON.stringify(scope.alsoAllow)?.slice(0, 40)}`);
1796
+ if (hasConflict(scope)) conflicts.push(label);
1797
+ });
1798
+ if (conflicts.length === 0) return { pass: true };
1799
+ return {
1800
+ pass: false,
1801
+ message: `tools allow 与 alsoAllow 冲突(${conflicts.length} 处):${conflicts.join(";")}。修复方式:将 alsoAllow 合并进 allow 并删除 alsoAllow`
1802
+ };
1803
+ }
1804
+ repair(ctx) {
1805
+ visitAllScopes(ctx.config, (scope) => mergeInScope(scope));
1806
+ }
1807
+ };
1808
+ ToolsAllowAlsoAllowConflictRule = __decorate([Rule({
1809
+ key: "tools_allow_also_allow_conflict",
1810
+ description: "tools 配置中 allow 与 alsoAllow 同时设置会导致 openclaw 启动失败;自动将 alsoAllow 合并进 allow(实验性)",
1811
+ dependsOn: ["config_syntax_check"],
1812
+ repairMode: "standard",
1813
+ level: "critical",
1814
+ profile: "experimental"
1815
+ })], ToolsAllowAlsoAllowConflictRule);
1816
+ /**
1817
+ * 遍历所有 tools-policy scope,对每个 scope 调用 callback。
1818
+ * validate 和 repair 共用同一套遍历逻辑,确保两者覆盖的 scope 始终一致。
1819
+ */
1820
+ function visitAllScopes(config, cb) {
1821
+ const topTools = asRecord(asRecord(config)?.tools);
1822
+ if (topTools) {
1823
+ cb(topTools, "tools");
1824
+ const byProvider = asRecord(topTools.byProvider);
1825
+ if (byProvider) for (const key of Object.keys(byProvider)) {
1826
+ const scope = asRecord(byProvider[key]);
1827
+ if (scope) cb(scope, `tools.byProvider.${key}`);
1828
+ }
1829
+ }
1830
+ const agents = asRecord(asRecord(config)?.agents);
1831
+ const list = Array.isArray(agents?.list) ? agents.list : [];
1832
+ for (let i = 0; i < list.length; i++) {
1833
+ const agent = asRecord(list[i]);
1834
+ const agentId = typeof agent?.id === "string" ? agent.id : String(i);
1835
+ const tools = asRecord(agent?.tools);
1836
+ if (tools) {
1837
+ cb(tools, `agents.list[${agentId}].tools`);
1838
+ const byProvider = asRecord(tools.byProvider);
1839
+ if (byProvider) for (const key of Object.keys(byProvider)) {
1840
+ const scope = asRecord(byProvider[key]);
1841
+ if (scope) cb(scope, `agents.list[${agentId}].tools.byProvider.${key}`);
1842
+ }
1843
+ }
1844
+ }
1845
+ }
1846
+ /** scope 中 allow 与 alsoAllow 同时非空 */
1847
+ function hasConflict(scope) {
1848
+ const allow = scope.allow;
1849
+ const alsoAllow = scope.alsoAllow;
1850
+ return Array.isArray(allow) && allow.length > 0 && Array.isArray(alsoAllow) && alsoAllow.length > 0;
1851
+ }
1852
+ /**
1853
+ * 将 alsoAllow 去重合并进 allow,删除 alsoAllow。
1854
+ * 仅保留字符串元素,过滤掉格式异常的非字符串条目,避免写入损坏配置。
1855
+ */
1856
+ function mergeInScope(scope) {
1857
+ if (!hasConflict(scope)) return;
1858
+ const allow = scope.allow.filter((v) => typeof v === "string");
1859
+ const alsoAllow = scope.alsoAllow.filter((v) => typeof v === "string");
1860
+ scope.allow = [...new Set([...allow, ...alsoAllow])];
1861
+ delete scope.alsoAllow;
1862
+ }
1863
+ //#endregion
1773
1864
  //#region src/rules/feishu-default-account.ts
1774
1865
  /**
1775
1866
  * Owns the multi-agent feishu-channel migration: turns legacy v1/v2
@@ -2543,9 +2634,13 @@ const PROVIDER_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-provider-
2543
2634
  const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-secrets.json";
2544
2635
  /** Absolute path to the openclaw config JSON. */
2545
2636
  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`;
2637
+ /**
2638
+ * upgrade-lark 每次运行的日志文件路径,含时间戳便于按时间排序定位。
2639
+ * checkOnly=true 时文件名含 "-check" 后缀,便于与正式安装日志区分。
2640
+ */
2641
+ function upgradeLarkLogFile(runId, checkOnly = false) {
2642
+ const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-");
2643
+ return `${DIAGNOSE_DIR}/upgrade-lark${checkOnly ? "-check" : ""}-${ts}-${runId.slice(0, 8)}.log`;
2549
2644
  }
2550
2645
  //#endregion
2551
2646
  //#region src/lark-cli-init.ts
@@ -10388,7 +10483,7 @@ function finalize(results, aborted) {
10388
10483
  //#endregion
10389
10484
  //#region src/channels-probe.ts
10390
10485
  const FEISHU_INVALID_CONFIG_MSG = "channels.feishu: invalid config: must NOT have additional properties";
10391
- const CHANNEL_LINE_RE = /^-\s+Feishu\s+([^:]+):\s+(.+)$/;
10486
+ const CHANNEL_LINE_RE = /^-\s+(?:Feishu|openclaw-lark|@larksuite\/openclaw-lark)\s+([^:]+):\s+(.+)$/i;
10392
10487
  /**
10393
10488
  * 判断单个飞书账号是否处于"可用"状态。
10394
10489
  * 移植自 feishu-channel-success-rate skill 的 Python `_account_is_working`。
@@ -10490,6 +10585,7 @@ function runChannelsProbe(timeoutMs = 6e4, { ignoreProbeFailed = true } = {}) {
10490
10585
  }
10491
10586
  if (stdout.trim()) return {
10492
10587
  available: true,
10588
+ rawOutput: stdout.trim(),
10493
10589
  ...parseChannelsProbeOutput(stdout, { ignoreProbeFailed })
10494
10590
  };
10495
10591
  return {
@@ -10501,6 +10597,22 @@ function runChannelsProbe(timeoutMs = 6e4, { ignoreProbeFailed = true } = {}) {
10501
10597
  error: execError ?? "no output from openclaw channels status --probe"
10502
10598
  };
10503
10599
  }
10600
+ /**
10601
+ * 判断 channels probe 结果是否处于"仅一个 Feishu 默认账号、enabled 但未配置"的状态。
10602
+ *
10603
+ * 这是插件全新安装后的初始状态:npx 工具创建了一个默认账号占位,但用户尚未
10604
+ * 填写 AppID / Secret,因此账号显示为 enabled 但 not configured。
10605
+ * 此时 anyAccountWorking=false(configured 缺失),但安装本身是成功的。
10606
+ *
10607
+ * 用于安装后校验的补充分支:当安装前 channels 不可用,且安装后恰好处于此状态时,
10608
+ * 视为安装成功,不触发回滚。
10609
+ */
10610
+ function isDefaultOnlyState(result) {
10611
+ if (result.accounts.length !== 1) return false;
10612
+ const acct = result.accounts[0];
10613
+ const bitTokens = new Set(acct.bits.map((b) => b.trim().split(":")[0]));
10614
+ return bitTokens.has("enabled") && !bitTokens.has("configured");
10615
+ }
10504
10616
  //#endregion
10505
10617
  //#region src/innerapi/reportCliRun.ts
10506
10618
  /**
@@ -10581,7 +10693,7 @@ async function reportCliRun(opts) {
10581
10693
  //#region src/help.ts
10582
10694
  const BIN = "mclaw-diagnose";
10583
10695
  function versionBanner() {
10584
- return `v0.1.15-alpha.1`;
10696
+ return `v0.1.15-alpha.11`;
10585
10697
  }
10586
10698
  const COMMANDS = [
10587
10699
  {
@@ -10875,7 +10987,7 @@ EXIT CODES
10875
10987
  hidden: false,
10876
10988
  summary: "Upgrade the Feishu/Lark plugin via @larksuite/openclaw-lark-tools",
10877
10989
  help: `USAGE
10878
- ${BIN} upgrade-lark [--check-only] [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
10990
+ ${BIN} upgrade-lark [--check] [--skip-restart] [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
10879
10991
 
10880
10992
  DESCRIPTION
10881
10993
  Upgrades the Feishu/Lark plugin by running:
@@ -10896,6 +11008,10 @@ DESCRIPTION
10896
11008
  If the upgrade command fails, or validation fails, the backed-up files are
10897
11009
  restored to roll back the changes.
10898
11010
 
11011
+ After a successful upgrade, the openclaw service is restarted via
11012
+ /opt/force/bin/openclaw_scripts/restart.sh
11013
+ Pass --skip-restart to skip this step (e.g. when restart is handled externally).
11014
+
10899
11015
  Execution is logged to /tmp/openclaw-diagnose/upgrade-lark-<runId>.log.
10900
11016
 
10901
11017
  Output is a single JSON object on stdout:
@@ -10903,13 +11019,14 @@ DESCRIPTION
10903
11019
  { "ok": false, "error": "...", "stderr": "...", "exitCode": 1,
10904
11020
  "rollbackOk": true, "validationError": "...", "logFile": "..." }
10905
11021
 
10906
- With --check-only:
11022
+ With --check:
10907
11023
  { "ok": true, "skipped": true, "upgradeNeeded": false, "logFile": "..." }
10908
11024
  { "ok": true, "skipped": true, "upgradeNeeded": true, "logFile": "..." } ← exit 1
10909
11025
 
10910
11026
  OPTIONS
10911
- --check-only Diagnose only: run the pre-check gate and report whether
11027
+ --check Diagnose only: run the pre-check gate and report whether
10912
11028
  upgrade is needed without installing. Exit 1 if needed.
11029
+ --skip-restart Skip the post-install service restart (default: restart).
10913
11030
  --scene=<scene> Telemetry label forwarded to Slardar only.
10914
11031
  Known values: PageUpgradeLark, etc. Custom strings accepted.
10915
11032
  --caller=<name> Optional metadata passed to innerapi.
@@ -10919,7 +11036,7 @@ EXIT CODES
10919
11036
  0 Success: upgrade ran and all validations passed; or gate skipped upgrade.
10920
11037
  1 Failure: npx error, validation failed, or git commit failed.
10921
11038
  File rollback was attempted (see rollbackOk in the JSON output).
10922
- With --check-only: exit 1 means upgrade IS needed.
11039
+ With --check: exit 1 means upgrade IS needed.
10923
11040
  `
10924
11041
  },
10925
11042
  {
@@ -11162,38 +11279,53 @@ function reportDoctorRunToSlardar(opts) {
11162
11279
  }
11163
11280
  });
11164
11281
  }
11165
- /** 读取日志文件全文;文件不存在或读取失败时返回空字符串。 */
11166
- function readLogFile(filePath) {
11167
- try {
11168
- return node_fs.default.readFileSync(filePath, "utf-8");
11169
- } catch {
11170
- return "";
11171
- }
11172
- }
11173
11282
  /**
11174
11283
  * 向 Slardar 上报 upgrade-lark 运行结果(upgrade_lark_run 事件)。
11175
11284
  *
11176
- * extraCategories 记录字符串维度:scene、exit_code、rollback_ok、
11177
- * validation_error、error_msg、log_content(日志文件全文)。
11285
+ * ## 标准字段
11286
+ * - durationMs:整条命令的总耗时(从 CLI 入口到 runUpgradeLark 返回),含重启阶段。
11287
+ * - status:success / failed
11288
+ *
11289
+ * ## extraCategories(字符串维度)
11290
+ * - scene:调用方标识(如 PageUpgradeLark)
11291
+ * - check_only:是否为 --check 仅诊断模式
11292
+ * - skipped:"true" 表示前置门控未触发跳过安装(含 --check 模式),"false" 表示执行了安装
11293
+ * - skip_reason:跳过原因描述(skipped=true 时有值,"check" 表示 --check 模式)
11294
+ * - exit_code:npx 子进程退出码(跳过安装时为空)
11295
+ * - rollback_ok:回滚是否成功(未触发回滚时为空)
11296
+ * - validation_error:安装后校验失败的错误信息
11297
+ * - error_msg:命令级错误信息
11298
+ * - result:执行结果一行摘要("success: ..."/"failed: ..."/"skipped: ..."/"check: ...")
11299
+ * - log_file:日志文件绝对路径,便于在沙箱中定位完整日志
11178
11300
  *
11179
- * extraMetrics 记录各阶段耗时(毫秒);未执行的阶段上报 -1 作为哨兵值,
11180
- * 便于在 Slardar 查询时区分"未运行"和"运行了 0ms"
11301
+ * ## extraMetrics(数值指标,单位毫秒)
11302
+ * 未执行的阶段上报 -1 作为哨兵值,便于与"运行了 0ms"区分。
11303
+ * - pre_probe_ms:[Pre-check A] 升级前 channels probe 耗时
11304
+ * - version_check_ms:[Pre-check B] 版本兼容性检测耗时
11305
+ * - backup_ms:[1/6] 文件备份耗时
11306
+ * - npx_install_ms:[3/6] npx install 耗时(不含安装后 5s 等待)
11307
+ * - post_probe_ms:[4/5] 安装后 channels probe 耗时
11308
+ * - doctor_fix_ms:[6/6] doctor --fix 耗时
11309
+ * 注:[7/7] 重启耗时写入日志但未单独上报,包含在 durationMs 总耗时中。
11181
11310
  */
11182
11311
  function reportUpgradeLarkToSlardar(opts) {
11183
- console.error(`[slardar] upgrade_lark_run scene=${opts.scene ?? ""} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
11312
+ console.error(`[slardar] upgrade_lark_run scene=${opts.scene ?? ""} checkOnly=${opts.checkOnly} skipped=${opts.skipped ?? false} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
11184
11313
  const t = opts.timing ?? {};
11185
- const logContent = readLogFile(opts.logFile);
11186
11314
  reportTask({
11187
11315
  eventName: "upgrade_lark_run",
11188
11316
  durationMs: opts.durationMs,
11189
11317
  status: opts.success ? "success" : "failed",
11190
11318
  extraCategories: {
11191
11319
  scene: opts.scene ?? "",
11320
+ check_only: String(opts.checkOnly),
11321
+ skipped: String(opts.skipped ?? false),
11322
+ skip_reason: opts.skipReason ?? "",
11192
11323
  exit_code: String(opts.exitCode ?? ""),
11193
11324
  rollback_ok: opts.rollbackOk != null ? String(opts.rollbackOk) : "",
11194
11325
  validation_error: opts.validationError ?? "",
11195
11326
  error_msg: opts.error ?? "",
11196
- log_content: logContent
11327
+ result: opts.resultSummary,
11328
+ log_file: opts.logFile
11197
11329
  },
11198
11330
  extraMetrics: {
11199
11331
  pre_probe_ms: t.preProbeMs ?? -1,
@@ -11205,6 +11337,23 @@ function reportUpgradeLarkToSlardar(opts) {
11205
11337
  }
11206
11338
  });
11207
11339
  }
11340
+ /**
11341
+ * 将 upgrade-lark 运行结果归纳为一行摘要字符串,用于 Slardar result 字段。
11342
+ *
11343
+ * 格式:
11344
+ * "check: upgrade needed"
11345
+ * "check: no upgrade needed"
11346
+ * "skipped: <skipReason>"
11347
+ * "success: upgrade installed"
11348
+ * "success: new default account (plugin installed, awaiting config)"
11349
+ * "failed: <error>"
11350
+ */
11351
+ function buildUpgradeLarkResultSummary(opts) {
11352
+ if (opts.checkOnly && opts.skipped) return opts.upgradeNeeded ? "check: upgrade needed" : "check: no upgrade needed";
11353
+ if (opts.skipped) return `skipped: ${opts.skipReason ?? "pre-check gate"}`;
11354
+ if (opts.ok) return "success: upgrade installed";
11355
+ return `failed: ${opts.error ?? opts.validationError ?? "unknown error"}${opts.rollbackOk === false ? " (rollback FAILED)" : opts.rollbackOk ? " (rolled back)" : ""}`;
11356
+ }
11208
11357
  //#endregion
11209
11358
  //#region src/upgrade-lark.ts
11210
11359
  /** 升级前需备份的 extensions/ 下的插件目录 */
@@ -11219,6 +11368,7 @@ function backupFiles(opts) {
11219
11368
  node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
11220
11369
  log(` backed up: openclaw.json (${stat.size} bytes)`);
11221
11370
  } else log(` skipped: openclaw.json (not found)`);
11371
+ node_fs.default.mkdirSync(node_path.default.join(backupDir, "extensions"), { recursive: true });
11222
11372
  const extSrc = node_path.default.join(workspaceDir, "extensions");
11223
11373
  for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11224
11374
  const src = node_path.default.join(extSrc, pluginDir);
@@ -11231,32 +11381,43 @@ function backupFiles(opts) {
11231
11381
  }
11232
11382
  return { ok: true };
11233
11383
  } catch (e) {
11384
+ const msg = `backup failed: ${e.message}`;
11385
+ log(`ERROR: ${msg}`);
11234
11386
  return {
11235
11387
  ok: false,
11236
- error: `backup failed: ${e.message}`
11388
+ error: msg
11237
11389
  };
11238
11390
  }
11239
11391
  }
11240
11392
  function restoreFiles(opts) {
11241
11393
  const { workspaceDir, configPath, backupDir, log } = opts;
11242
11394
  try {
11395
+ if (node_fs.default.existsSync(configPath)) {
11396
+ node_fs.default.rmSync(configPath, { force: true });
11397
+ log(` deleted: openclaw.json`);
11398
+ }
11399
+ const extDst = node_path.default.join(workspaceDir, "extensions");
11400
+ for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11401
+ const dst = node_path.default.join(extDst, pluginDir);
11402
+ if (node_fs.default.existsSync(dst)) {
11403
+ node_fs.default.rmSync(dst, {
11404
+ recursive: true,
11405
+ force: true
11406
+ });
11407
+ log(` deleted: extensions/${pluginDir}`);
11408
+ }
11409
+ }
11243
11410
  const configBackup = node_path.default.join(backupDir, "openclaw.json");
11244
11411
  if (node_fs.default.existsSync(configBackup)) {
11245
11412
  node_fs.default.copyFileSync(configBackup, configPath);
11246
11413
  log(` restored: openclaw.json`);
11247
- }
11248
- const extDst = node_path.default.join(workspaceDir, "extensions");
11414
+ } else log(` skipped restore: openclaw.json (not in backup — was not present before upgrade)`);
11249
11415
  for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11250
11416
  const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
11251
11417
  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 });
11418
+ node_fs.default.cpSync(backupSrc, node_path.default.join(extDst, pluginDir), { recursive: true });
11258
11419
  log(` restored: extensions/${pluginDir}`);
11259
- }
11420
+ } else log(` skipped restore: extensions/${pluginDir} (not in backup — was not present before upgrade)`);
11260
11421
  }
11261
11422
  return true;
11262
11423
  } catch (e) {
@@ -11318,6 +11479,7 @@ function probeChannels(label, log, timeoutMs) {
11318
11479
  if (r.error) log(` ${label} error: ${r.error}`);
11319
11480
  if (r.gatewayReachable != null) log(` ${label} gatewayReachable: ${r.gatewayReachable}`);
11320
11481
  for (const acct of r.accounts ?? []) log(` ${label} account ${acct.id}: isWorking=${acct.isWorking} bits=[${acct.bits.join(",")}]`);
11482
+ if (r.rawOutput) log(` ${label} raw output:\n${r.rawOutput}`);
11321
11483
  return r;
11322
11484
  } catch (e) {
11323
11485
  log(` ${label} channels probe threw: ${e.message}`);
@@ -11333,7 +11495,7 @@ function probeChannels(label, log, timeoutMs) {
11333
11495
  function runUpgradeLark(opts) {
11334
11496
  const cwd = opts.cwd ?? "/home/gem/workspace/agent";
11335
11497
  const configPath = opts.configPath ?? CONFIG_PATH;
11336
- const logFile = upgradeLarkLogFile(opts.runId);
11498
+ const logFile = upgradeLarkLogFile(opts.runId, opts.checkOnly);
11337
11499
  const log = makeLogger(logFile);
11338
11500
  const fsOpts = {
11339
11501
  workspaceDir: cwd,
@@ -11352,7 +11514,7 @@ function runUpgradeLark(opts) {
11352
11514
  log("");
11353
11515
  log("── [Pre-check A] channels probe(升级前)────────────────");
11354
11516
  const t_preProbeStart = Date.now();
11355
- const beforeChannels = probeChannels("before", log, 6e4);
11517
+ const beforeChannels = probeChannels("before", log, 3e5);
11356
11518
  timing.preProbeMs = Date.now() - t_preProbeStart;
11357
11519
  log("");
11358
11520
  log("── [Pre-check B] 版本兼容预检 ───────────────────────────");
@@ -11413,14 +11575,14 @@ function runUpgradeLark(opts) {
11413
11575
  }
11414
11576
  log(` PROCEED: requiresLarkUpgrade=true (version=${versionIncompatible}, feishuConfig=${feishuConfigInvalid}) AND channels not working → running upgrade`);
11415
11577
  if (opts.checkOnly) {
11416
- log(` check-only: upgrade IS needed returning without installing`);
11578
+ log(` --check 模式:需要升级不执行安装,直接返回`);
11417
11579
  log(`${"=".repeat(60)}`);
11418
- log("upgrade-lark check-only complete");
11580
+ log("upgrade-lark check complete");
11419
11581
  log(`${"=".repeat(60)}`);
11420
11582
  return {
11421
11583
  ok: true,
11422
11584
  skipped: true,
11423
- skipReason: "check-only",
11585
+ skipReason: "check",
11424
11586
  upgradeNeeded: true,
11425
11587
  timing,
11426
11588
  logFile
@@ -11468,7 +11630,7 @@ function runUpgradeLark(opts) {
11468
11630
  "pipe",
11469
11631
  "pipe"
11470
11632
  ],
11471
- timeout: 12e4
11633
+ timeout: 6e5
11472
11634
  });
11473
11635
  timing.npxInstallMs = Date.now() - t_npxStart;
11474
11636
  const npxStdout = npxResult.stdout?.trim() ?? "";
@@ -11519,13 +11681,15 @@ function runUpgradeLark(opts) {
11519
11681
  log(` version-compat post-check error: ${e.message} — version signal unavailable`);
11520
11682
  }
11521
11683
  const t_postProbeStart = Date.now();
11522
- const afterChannels = probeChannels("after", log, 6e4);
11684
+ const afterChannels = probeChannels("after", log, 3e5);
11523
11685
  timing.postProbeMs = Date.now() - t_postProbeStart;
11524
11686
  log(` feishu config invalid after: ${afterChannels.feishuConfigInvalid}`);
11525
11687
  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)");
11688
+ const isNewDefaultOnly = !afterVersionIncompatible && !afterChannels.feishuConfigInvalid && !beforeChannels.anyAccountWorking && isDefaultOnlyState(afterChannels);
11689
+ log(` post-check: stillNeedsUpgrade=${stillNeedsUpgrade} (version=${afterVersionIncompatible}, feishuConfig=${afterChannels.feishuConfigInvalid}, channelsWorking=${afterChannels.anyAccountWorking}) isNewDefaultOnly=${isNewDefaultOnly}`);
11690
+ if (stillNeedsUpgrade && !isNewDefaultOnly) return doRollback(`post-install diagnosis still shows anomaly: versionIncompatible=${afterVersionIncompatible}, feishuConfigInvalid=${afterChannels.feishuConfigInvalid}, anyAccountWorking=${afterChannels.anyAccountWorking}`);
11691
+ if (isNewDefaultOnly) log(" post-install diagnosis: ok (new default account — plugin installed, awaiting configuration)");
11692
+ else log(" post-install diagnosis: ok (upgrade conditions resolved)");
11529
11693
  log("");
11530
11694
  log("── [6/6] doctor --fix ────────────────────────────────────");
11531
11695
  const fixArgs = ["doctor", "--fix"];
@@ -11547,6 +11711,26 @@ function runUpgradeLark(opts) {
11547
11711
  if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
11548
11712
  log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
11549
11713
  log("");
11714
+ log("── [7/7] 重启 openclaw 服务 ──────────────────────────────");
11715
+ const restartScript = "/opt/force/bin/openclaw_scripts/restart.sh";
11716
+ if (opts.skipRestart) log(" skipped: --skip-restart");
11717
+ else if (node_fs.default.existsSync(restartScript)) {
11718
+ const t_restart = Date.now();
11719
+ const restartResult = (0, node_child_process.spawnSync)("bash", [restartScript], {
11720
+ encoding: "utf-8",
11721
+ stdio: [
11722
+ "ignore",
11723
+ "pipe",
11724
+ "pipe"
11725
+ ],
11726
+ timeout: 3e4
11727
+ });
11728
+ const restartMs = Date.now() - t_restart;
11729
+ if (restartResult.stdout?.trim()) log(` restart stdout:\n${restartResult.stdout.trim()}`);
11730
+ if (restartResult.stderr?.trim()) log(` restart stderr:\n${restartResult.stderr.trim()}`);
11731
+ log(` restart.sh exit: ${restartResult.status ?? "null"} (${restartMs}ms)${restartResult.error ? ` error: ${restartResult.error.message}` : ""}`);
11732
+ } else log(` skipped: ${restartScript} not found`);
11733
+ log("");
11550
11734
  log(`${"=".repeat(60)}`);
11551
11735
  log("upgrade-lark completed successfully");
11552
11736
  log(`${"=".repeat(60)}`);
@@ -12050,19 +12234,34 @@ async function main() {
12050
12234
  break;
12051
12235
  }
12052
12236
  case "upgrade-lark": {
12053
- const checkOnly = args.includes("--check-only");
12237
+ const checkOnly = args.includes("--check");
12238
+ const skipRestart = args.includes("--skip-restart");
12054
12239
  const result = runUpgradeLark({
12055
12240
  runId: rc.runId,
12056
12241
  scene,
12057
- checkOnly
12242
+ checkOnly,
12243
+ skipRestart
12058
12244
  });
12059
12245
  const upgradeDurationMs = Date.now() - t0;
12060
12246
  console.log(JSON.stringify(result));
12061
12247
  reportUpgradeLarkToSlardar({
12062
12248
  scene,
12249
+ checkOnly,
12063
12250
  durationMs: upgradeDurationMs,
12064
12251
  success: result.ok,
12252
+ skipped: result.skipped,
12253
+ skipReason: result.skipReason,
12065
12254
  logFile: result.logFile,
12255
+ resultSummary: buildUpgradeLarkResultSummary({
12256
+ ok: result.ok,
12257
+ checkOnly,
12258
+ skipped: result.skipped,
12259
+ skipReason: result.skipReason,
12260
+ upgradeNeeded: result.upgradeNeeded,
12261
+ error: result.error,
12262
+ validationError: result.validationError,
12263
+ rollbackOk: result.rollbackOk
12264
+ }),
12066
12265
  exitCode: result.exitCode,
12067
12266
  rollbackOk: result.rollbackOk,
12068
12267
  validationError: result.validationError,
@@ -12092,7 +12291,9 @@ async function main() {
12092
12291
  break;
12093
12292
  }
12094
12293
  case "channels-probe": {
12095
- const result = runChannelsProbe(getFlag(args, "timeout") ? Number(getFlag(args, "timeout")) : void 0);
12294
+ const timeoutRaw = getFlag(args, "timeout");
12295
+ const parsed = timeoutRaw != null ? Number(timeoutRaw) : NaN;
12296
+ const result = runChannelsProbe(timeoutRaw != null ? Number.isNaN(parsed) ? 6e4 : Math.max(1e3, parsed) : void 0);
12096
12297
  console.log(JSON.stringify(result));
12097
12298
  break;
12098
12299
  }
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.11",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {