@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.
- package/dist/index.cjs +248 -48
- 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.
|
|
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
|
-
/**
|
|
2547
|
-
|
|
2548
|
-
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
11177
|
-
*
|
|
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
|
|
11180
|
-
*
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
|
11577
|
+
log(` --check 模式:需要升级 — 不执行安装,直接返回`);
|
|
11417
11578
|
log(`${"=".repeat(60)}`);
|
|
11418
|
-
log("upgrade-lark check
|
|
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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
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
|
|
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
|
|
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