@lark-apaas/openclaw-scripts-diagnose-cli 0.1.15-alpha.14 → 0.1.15-alpha.16
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 +176 -36
- 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.16";
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/rule-engine/base.ts
|
|
@@ -2640,6 +2640,13 @@ const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
|
|
|
2640
2640
|
*/
|
|
2641
2641
|
const FIX_EVENT_DIR = "/tmp/event";
|
|
2642
2642
|
/**
|
|
2643
|
+
* 安装指令互斥锁文件路径。
|
|
2644
|
+
* upgrade-lark / install-openclaw / install-extension / install-cli / reset --worker
|
|
2645
|
+
* 共享此锁,同一时刻只允许一个安装指令运行。
|
|
2646
|
+
* 锁文件内容:{ pid, command, startedAt }。
|
|
2647
|
+
*/
|
|
2648
|
+
const INSTALL_LOCK_FILE = `${DIAGNOSE_DIR}/install.lock`;
|
|
2649
|
+
/**
|
|
2643
2650
|
* upgrade-lark 每次运行的日志文件路径,含时间戳便于按时间排序定位。
|
|
2644
2651
|
* checkOnly=true 时文件名含 "-check" 后缀,便于与正式安装日志区分。
|
|
2645
2652
|
*/
|
|
@@ -4173,9 +4180,10 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
|
|
|
4173
4180
|
};
|
|
4174
4181
|
}
|
|
4175
4182
|
/** Check a single bot entry (either an account object or the feishu channel itself).
|
|
4176
|
-
* appSecret is validated based on its current type:
|
|
4177
4183
|
* - object → must match canonical provider-ref
|
|
4178
4184
|
* - string → must match larkApps plaintext
|
|
4185
|
+
* - undefined/null → missing
|
|
4186
|
+
* - other → unexpected type
|
|
4179
4187
|
*/
|
|
4180
4188
|
checkBot(label, bot, larkApp, issues) {
|
|
4181
4189
|
const creatorOpenID = larkApp.creatorOpenID;
|
|
@@ -4188,7 +4196,8 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
|
|
|
4188
4196
|
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${label} appSecret is a provider-ref but not the canonical one`);
|
|
4189
4197
|
} else if (typeof secret === "string") {
|
|
4190
4198
|
if (secret !== larkApp.appSecret) issues.push(`${label} appSecret plaintext mismatch`);
|
|
4191
|
-
} else issues.push(`${label} appSecret
|
|
4199
|
+
} else if (secret === void 0 || secret === null) issues.push(`${label} appSecret is missing`);
|
|
4200
|
+
else issues.push(`${label} appSecret has unexpected type ${typeof secret}`);
|
|
4192
4201
|
}
|
|
4193
4202
|
repair(ctx) {
|
|
4194
4203
|
const larkApps = ctx.vars.larkApps;
|
|
@@ -4212,9 +4221,8 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
|
|
|
4212
4221
|
}
|
|
4213
4222
|
}
|
|
4214
4223
|
/** Fix a single bot entry in-place.
|
|
4215
|
-
*
|
|
4216
|
-
* -
|
|
4217
|
-
* - string → fix to larkApps plaintext
|
|
4224
|
+
* - object (provider-ref) → fix to canonical
|
|
4225
|
+
* - otherwise → fix to larkApps plaintext
|
|
4218
4226
|
*/
|
|
4219
4227
|
fixBot(bot, larkApp) {
|
|
4220
4228
|
const creatorOpenID = larkApp.creatorOpenID;
|
|
@@ -4228,9 +4236,7 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
|
|
|
4228
4236
|
const secret = bot.appSecret;
|
|
4229
4237
|
if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
4230
4238
|
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
|
|
4231
|
-
} else if (
|
|
4232
|
-
if (secret !== larkApp.appSecret) bot.appSecret = larkApp.appSecret;
|
|
4233
|
-
}
|
|
4239
|
+
} else if (secret !== larkApp.appSecret) bot.appSecret = larkApp.appSecret;
|
|
4234
4240
|
}
|
|
4235
4241
|
};
|
|
4236
4242
|
FeishuBotChannelConfigRule = __decorate([Rule({
|
|
@@ -7116,45 +7122,56 @@ function fixBotChannelConfig(configPath, larkApps, log) {
|
|
|
7116
7122
|
return;
|
|
7117
7123
|
}
|
|
7118
7124
|
const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
7119
|
-
const
|
|
7120
|
-
if (!
|
|
7121
|
-
log("no feishu
|
|
7125
|
+
const feishu = asRecord(getNestedMap(config, "channels", "feishu"));
|
|
7126
|
+
if (!feishu) {
|
|
7127
|
+
log("no feishu channel in config, skip bot channel config fix");
|
|
7122
7128
|
return;
|
|
7123
7129
|
}
|
|
7124
7130
|
let fixCount = 0;
|
|
7125
|
-
|
|
7131
|
+
const accounts = asRecord(feishu.accounts);
|
|
7132
|
+
if (accounts) for (const [, account] of Object.entries(accounts)) {
|
|
7126
7133
|
const bot = asRecord(account);
|
|
7127
7134
|
if (!bot) continue;
|
|
7128
7135
|
const appId = bot.appId;
|
|
7129
7136
|
if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
|
|
7130
7137
|
const larkApp = larkApps.find((e) => e.larkAppID === appId);
|
|
7131
7138
|
if (!larkApp) continue;
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
fixCount++;
|
|
7139
|
-
}
|
|
7140
|
-
}
|
|
7141
|
-
const secret = bot.appSecret;
|
|
7142
|
-
let needsFix = false;
|
|
7143
|
-
if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
7144
|
-
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
|
|
7145
|
-
} else if (typeof secret === "string") {
|
|
7146
|
-
if (secret !== larkApp.appSecret) needsFix = true;
|
|
7147
|
-
} else needsFix = true;
|
|
7148
|
-
if (needsFix) {
|
|
7149
|
-
bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
|
|
7150
|
-
fixCount++;
|
|
7151
|
-
}
|
|
7139
|
+
fixCount += fixBot(bot, larkApp);
|
|
7140
|
+
}
|
|
7141
|
+
const singleAppId = feishu.appId;
|
|
7142
|
+
if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
|
|
7143
|
+
const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
|
|
7144
|
+
if (larkApp) fixCount += fixBot(feishu, larkApp);
|
|
7152
7145
|
}
|
|
7153
7146
|
if (fixCount > 0) {
|
|
7154
7147
|
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
7155
7148
|
log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
|
|
7156
7149
|
} else log("bot channel config ok, no fixes needed");
|
|
7157
7150
|
}
|
|
7151
|
+
/** Fix a single bot entry in-place. Returns number of fixes applied. */
|
|
7152
|
+
function fixBot(bot, larkApp) {
|
|
7153
|
+
let fixCount = 0;
|
|
7154
|
+
const creatorOpenID = larkApp.creatorOpenID;
|
|
7155
|
+
if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
|
|
7156
|
+
const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
|
|
7157
|
+
if (!allowFrom.includes(creatorOpenID)) {
|
|
7158
|
+
allowFrom.push(creatorOpenID);
|
|
7159
|
+
bot.allowFrom = allowFrom;
|
|
7160
|
+
fixCount++;
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
const secret = bot.appSecret;
|
|
7164
|
+
if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
7165
|
+
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) {
|
|
7166
|
+
bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
|
|
7167
|
+
fixCount++;
|
|
7168
|
+
}
|
|
7169
|
+
} else if (secret !== larkApp.appSecret) {
|
|
7170
|
+
bot.appSecret = larkApp.appSecret;
|
|
7171
|
+
fixCount++;
|
|
7172
|
+
}
|
|
7173
|
+
return fixCount;
|
|
7174
|
+
}
|
|
7158
7175
|
/**
|
|
7159
7176
|
* Step 7: Verify startup scripts landed in configDir/scripts/.
|
|
7160
7177
|
*
|
|
@@ -10697,7 +10714,7 @@ async function reportCliRun(opts) {
|
|
|
10697
10714
|
//#region src/help.ts
|
|
10698
10715
|
const BIN = "mclaw-diagnose";
|
|
10699
10716
|
function versionBanner() {
|
|
10700
|
-
return `v0.1.15-alpha.
|
|
10717
|
+
return `v0.1.15-alpha.16`;
|
|
10701
10718
|
}
|
|
10702
10719
|
const COMMANDS = [
|
|
10703
10720
|
{
|
|
@@ -11871,9 +11888,88 @@ function runUpgradeLark(opts) {
|
|
|
11871
11888
|
});
|
|
11872
11889
|
}
|
|
11873
11890
|
//#endregion
|
|
11891
|
+
//#region src/install-lock.ts
|
|
11892
|
+
/**
|
|
11893
|
+
* 检查指定 PID 是否仍在运行(向进程发 signal 0,不影响其运行状态)。
|
|
11894
|
+
* 跨平台:Linux/macOS 均支持;Windows 不在目标平台范围内。
|
|
11895
|
+
*/
|
|
11896
|
+
function isPidAlive(pid) {
|
|
11897
|
+
try {
|
|
11898
|
+
process.kill(pid, 0);
|
|
11899
|
+
return true;
|
|
11900
|
+
} catch (e) {
|
|
11901
|
+
if (e.code === "EPERM") return true;
|
|
11902
|
+
return false;
|
|
11903
|
+
}
|
|
11904
|
+
}
|
|
11905
|
+
/**
|
|
11906
|
+
* 获取安装操作互斥锁。
|
|
11907
|
+
*
|
|
11908
|
+
* 利用 open(O_CREAT | O_EXCL) 的 POSIX 原子性确保同一时刻只有一个安装指令运行。
|
|
11909
|
+
* 覆盖的安装指令:upgrade-lark / install-openclaw / install-extension / install-cli / reset --worker。
|
|
11910
|
+
*
|
|
11911
|
+
* 若锁被活跃进程持有,抛出包含持有者信息的 Error。
|
|
11912
|
+
* 若锁文件属于已退出进程(crash 遗留),视为过期锁并覆盖。
|
|
11913
|
+
*
|
|
11914
|
+
* 成功获取后自动注册 process.on('exit') 释放锁,无需调用方手动清理。
|
|
11915
|
+
*/
|
|
11916
|
+
function acquireInstallLock(command) {
|
|
11917
|
+
node_fs.default.mkdirSync(DIAGNOSE_DIR, { recursive: true });
|
|
11918
|
+
const info = {
|
|
11919
|
+
pid: process.pid,
|
|
11920
|
+
command,
|
|
11921
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11922
|
+
};
|
|
11923
|
+
const content = JSON.stringify(info);
|
|
11924
|
+
while (true) try {
|
|
11925
|
+
const fd = node_fs.default.openSync(INSTALL_LOCK_FILE, node_fs.default.constants.O_CREAT | node_fs.default.constants.O_EXCL | node_fs.default.constants.O_WRONLY, 420);
|
|
11926
|
+
node_fs.default.writeSync(fd, content);
|
|
11927
|
+
node_fs.default.closeSync(fd);
|
|
11928
|
+
process.once("exit", releaseInstallLock);
|
|
11929
|
+
console.error(`[install-lock] acquired command=${command} pid=${process.pid}`);
|
|
11930
|
+
return;
|
|
11931
|
+
} catch (e) {
|
|
11932
|
+
if (e.code !== "EEXIST") throw e;
|
|
11933
|
+
let existing = null;
|
|
11934
|
+
try {
|
|
11935
|
+
existing = JSON.parse(node_fs.default.readFileSync(INSTALL_LOCK_FILE, "utf-8"));
|
|
11936
|
+
} catch {
|
|
11937
|
+
try {
|
|
11938
|
+
node_fs.default.rmSync(INSTALL_LOCK_FILE, { force: true });
|
|
11939
|
+
} catch {}
|
|
11940
|
+
continue;
|
|
11941
|
+
}
|
|
11942
|
+
if (!isPidAlive(existing.pid)) {
|
|
11943
|
+
console.error(`[install-lock] stale lock detected (pid=${existing.pid} command=${existing.command}), removing`);
|
|
11944
|
+
try {
|
|
11945
|
+
node_fs.default.rmSync(INSTALL_LOCK_FILE, { force: true });
|
|
11946
|
+
} catch {}
|
|
11947
|
+
continue;
|
|
11948
|
+
}
|
|
11949
|
+
throw new Error(`另一个安装指令正在运行,请等待其完成后重试。\n 占用指令: ${existing.command}\n 进程 PID : ${existing.pid}\n 开始时间: ${existing.startedAt}\n 锁文件 : ${INSTALL_LOCK_FILE}`);
|
|
11950
|
+
}
|
|
11951
|
+
}
|
|
11952
|
+
/**
|
|
11953
|
+
* 释放安装操作互斥锁。
|
|
11954
|
+
* 仅删除由本进程创建的锁文件,避免误删其他进程的锁。
|
|
11955
|
+
* 通常由 process.on('exit') 自动调用,不需要手动调用。
|
|
11956
|
+
*/
|
|
11957
|
+
function releaseInstallLock() {
|
|
11958
|
+
try {
|
|
11959
|
+
const raw = node_fs.default.readFileSync(INSTALL_LOCK_FILE, "utf-8");
|
|
11960
|
+
const info = JSON.parse(raw);
|
|
11961
|
+
if (info.pid === process.pid) {
|
|
11962
|
+
node_fs.default.rmSync(INSTALL_LOCK_FILE, { force: true });
|
|
11963
|
+
console.error(`[install-lock] released command=${info.command} pid=${process.pid}`);
|
|
11964
|
+
}
|
|
11965
|
+
} catch {}
|
|
11966
|
+
}
|
|
11967
|
+
//#endregion
|
|
11874
11968
|
//#region src/index.ts
|
|
11875
11969
|
const args = node_process.default.argv.slice(2);
|
|
11876
11970
|
const mode = args.find((a) => !a.startsWith("-"));
|
|
11971
|
+
const t0 = Date.now();
|
|
11972
|
+
const scene = getFlag(args, "scene");
|
|
11877
11973
|
/**
|
|
11878
11974
|
* Pull the first non-flag positional after the mode name.
|
|
11879
11975
|
* (The mode itself is args[0] in the filtered set, so we skip index 0.)
|
|
@@ -11957,13 +12053,11 @@ async function main() {
|
|
|
11957
12053
|
}
|
|
11958
12054
|
const caller = getFlag(args, "caller");
|
|
11959
12055
|
const traceId = getFlag(args, "trace-id");
|
|
11960
|
-
const scene = getFlag(args, "scene");
|
|
11961
12056
|
const profile = getFlag(args, "profile") === "experimental" ? "experimental" : "standard";
|
|
11962
12057
|
const rc = setRunContext({
|
|
11963
12058
|
caller,
|
|
11964
12059
|
traceId
|
|
11965
12060
|
});
|
|
11966
|
-
const t0 = Date.now();
|
|
11967
12061
|
console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
|
|
11968
12062
|
switch (mode) {
|
|
11969
12063
|
case "check": {
|
|
@@ -12070,6 +12164,7 @@ async function main() {
|
|
|
12070
12164
|
console.error("Error: --task-id=<id> is required for worker");
|
|
12071
12165
|
node_process.default.exit(1);
|
|
12072
12166
|
}
|
|
12167
|
+
acquireInstallLock("reset");
|
|
12073
12168
|
const resultFile = resetResultFile(taskId);
|
|
12074
12169
|
const raw = await fetchCtxViaInnerApi({
|
|
12075
12170
|
populate: planCtxPopulate({ command: "reset" }),
|
|
@@ -12114,6 +12209,7 @@ async function main() {
|
|
|
12114
12209
|
console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
|
|
12115
12210
|
node_process.default.exit(1);
|
|
12116
12211
|
}
|
|
12212
|
+
acquireInstallLock("install-openclaw");
|
|
12117
12213
|
const ossFileMapFlag = getFlag(args, "oss_file_map");
|
|
12118
12214
|
let installOssFileMap;
|
|
12119
12215
|
let rawForTelemetry;
|
|
@@ -12156,6 +12252,7 @@ async function main() {
|
|
|
12156
12252
|
console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
|
|
12157
12253
|
node_process.default.exit(1);
|
|
12158
12254
|
}
|
|
12255
|
+
acquireInstallLock("install-extension");
|
|
12159
12256
|
const all = args.includes("--all");
|
|
12160
12257
|
const names = getMultiFlag(args, "extension");
|
|
12161
12258
|
const homeBase = getFlag(args, "home_base");
|
|
@@ -12219,6 +12316,7 @@ async function main() {
|
|
|
12219
12316
|
console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
|
|
12220
12317
|
node_process.default.exit(1);
|
|
12221
12318
|
}
|
|
12319
|
+
acquireInstallLock("install-cli");
|
|
12222
12320
|
const homeBase = getFlag(args, "home_base");
|
|
12223
12321
|
const ossFileMapFlag = getFlag(args, "oss_file_map");
|
|
12224
12322
|
let installOssFileMap;
|
|
@@ -12362,6 +12460,7 @@ async function main() {
|
|
|
12362
12460
|
}
|
|
12363
12461
|
case "upgrade-lark": {
|
|
12364
12462
|
const checkOnly = args.includes("--check");
|
|
12463
|
+
if (!checkOnly) acquireInstallLock("upgrade-lark");
|
|
12365
12464
|
const skipRestart = args.includes("--skip-restart");
|
|
12366
12465
|
const result = runUpgradeLark({
|
|
12367
12466
|
runId: rc.runId,
|
|
@@ -12427,6 +12526,47 @@ main().catch((err) => {
|
|
|
12427
12526
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12428
12527
|
console.error(`${mode ?? "<no-mode>"}: error message=${msg}`);
|
|
12429
12528
|
node_process.default.stderr.write(`Error: ${msg}\n`);
|
|
12529
|
+
if (mode) {
|
|
12530
|
+
const durationMs = Date.now() - t0;
|
|
12531
|
+
if (mode === "upgrade-lark") {
|
|
12532
|
+
const checkOnly = args.includes("--check");
|
|
12533
|
+
const fixStatus = computeFixStatus(scene, "failed");
|
|
12534
|
+
const failResult = {
|
|
12535
|
+
status: "failed",
|
|
12536
|
+
error: msg,
|
|
12537
|
+
logFile: "",
|
|
12538
|
+
fixStatus
|
|
12539
|
+
};
|
|
12540
|
+
if (fixStatus !== void 0) writeFixStatusEvent(fixStatus, failResult, console.error);
|
|
12541
|
+
console.log(JSON.stringify(failResult));
|
|
12542
|
+
reportUpgradeLarkToSlardar({
|
|
12543
|
+
scene,
|
|
12544
|
+
checkOnly,
|
|
12545
|
+
durationMs,
|
|
12546
|
+
resultStatus: "failed",
|
|
12547
|
+
error: msg,
|
|
12548
|
+
logFile: "",
|
|
12549
|
+
resultSummary: buildUpgradeLarkResultSummary(failResult, checkOnly)
|
|
12550
|
+
});
|
|
12551
|
+
} else if ([
|
|
12552
|
+
"doctor",
|
|
12553
|
+
"check",
|
|
12554
|
+
"repair",
|
|
12555
|
+
"install-openclaw",
|
|
12556
|
+
"install-extension",
|
|
12557
|
+
"install-cli",
|
|
12558
|
+
"download-resource",
|
|
12559
|
+
"reset",
|
|
12560
|
+
"lark-cli-init"
|
|
12561
|
+
].includes(mode)) reportDoctorRunToSlardar({
|
|
12562
|
+
command: mode,
|
|
12563
|
+
scene,
|
|
12564
|
+
profile: getFlag(args, "profile") === "experimental" ? "experimental" : "standard",
|
|
12565
|
+
fix: args.includes("--fix"),
|
|
12566
|
+
durationMs,
|
|
12567
|
+
success: false
|
|
12568
|
+
});
|
|
12569
|
+
}
|
|
12430
12570
|
node_process.default.exitCode = 1;
|
|
12431
12571
|
});
|
|
12432
12572
|
//#endregion
|
package/package.json
CHANGED