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