@lark-apaas/openclaw-scripts-diagnose-cli 0.1.15-alpha.15 → 0.1.15-alpha.17

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 +144 -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.15";
55
+ return "0.1.15-alpha.17";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -4180,9 +4180,10 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
4180
4180
  };
4181
4181
  }
4182
4182
  /** Check a single bot entry (either an account object or the feishu channel itself).
4183
- * appSecret is validated based on its current type:
4184
4183
  * - object → must match canonical provider-ref
4185
4184
  * - string → must match larkApps plaintext
4185
+ * - undefined/null → missing
4186
+ * - other → unexpected type
4186
4187
  */
4187
4188
  checkBot(label, bot, larkApp, issues) {
4188
4189
  const creatorOpenID = larkApp.creatorOpenID;
@@ -4195,7 +4196,8 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
4195
4196
  if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${label} appSecret is a provider-ref but not the canonical one`);
4196
4197
  } else if (typeof secret === "string") {
4197
4198
  if (secret !== larkApp.appSecret) issues.push(`${label} appSecret plaintext mismatch`);
4198
- } else issues.push(`${label} appSecret has unexpected type ${typeof secret}`);
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}`);
4199
4201
  }
4200
4202
  repair(ctx) {
4201
4203
  const larkApps = ctx.vars.larkApps;
@@ -4219,9 +4221,8 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
4219
4221
  }
4220
4222
  }
4221
4223
  /** Fix a single bot entry in-place.
4222
- * appSecret is repaired based on its current type:
4223
- * - object → fix to canonical provider-ref
4224
- * - string → fix to larkApps plaintext
4224
+ * - object (provider-ref) fix to canonical
4225
+ * - otherwise → fix to larkApps plaintext
4225
4226
  */
4226
4227
  fixBot(bot, larkApp) {
4227
4228
  const creatorOpenID = larkApp.creatorOpenID;
@@ -4235,9 +4236,7 @@ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends Diagno
4235
4236
  const secret = bot.appSecret;
4236
4237
  if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
4237
4238
  if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
4238
- } else if (typeof secret === "string") {
4239
- if (secret !== larkApp.appSecret) bot.appSecret = larkApp.appSecret;
4240
- }
4239
+ } else if (secret !== larkApp.appSecret) bot.appSecret = larkApp.appSecret;
4241
4240
  }
4242
4241
  };
4243
4242
  FeishuBotChannelConfigRule = __decorate([Rule({
@@ -7123,45 +7122,56 @@ function fixBotChannelConfig(configPath, larkApps, log) {
7123
7122
  return;
7124
7123
  }
7125
7124
  const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
7126
- const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
7127
- if (!accounts) {
7128
- log("no feishu accounts in config, skip bot channel config fix");
7125
+ const feishu = asRecord(getNestedMap(config, "channels", "feishu"));
7126
+ if (!feishu) {
7127
+ log("no feishu channel in config, skip bot channel config fix");
7129
7128
  return;
7130
7129
  }
7131
7130
  let fixCount = 0;
7132
- for (const [, account] of Object.entries(accounts)) {
7131
+ const accounts = asRecord(feishu.accounts);
7132
+ if (accounts) for (const [, account] of Object.entries(accounts)) {
7133
7133
  const bot = asRecord(account);
7134
7134
  if (!bot) continue;
7135
7135
  const appId = bot.appId;
7136
7136
  if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
7137
7137
  const larkApp = larkApps.find((e) => e.larkAppID === appId);
7138
7138
  if (!larkApp) continue;
7139
- const creatorOpenID = larkApp.creatorOpenID;
7140
- if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
7141
- const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
7142
- if (!allowFrom.includes(creatorOpenID)) {
7143
- allowFrom.push(creatorOpenID);
7144
- bot.allowFrom = allowFrom;
7145
- fixCount++;
7146
- }
7147
- }
7148
- const secret = bot.appSecret;
7149
- let needsFix = false;
7150
- if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
7151
- if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
7152
- } else if (typeof secret === "string") {
7153
- if (secret !== larkApp.appSecret) needsFix = true;
7154
- } else needsFix = true;
7155
- if (needsFix) {
7156
- bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
7157
- fixCount++;
7158
- }
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);
7159
7145
  }
7160
7146
  if (fixCount > 0) {
7161
7147
  node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
7162
7148
  log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
7163
7149
  } else log("bot channel config ok, no fixes needed");
7164
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
+ }
7165
7175
  /**
7166
7176
  * Step 7: Verify startup scripts landed in configDir/scripts/.
7167
7177
  *
@@ -10704,7 +10714,7 @@ async function reportCliRun(opts) {
10704
10714
  //#region src/help.ts
10705
10715
  const BIN = "mclaw-diagnose";
10706
10716
  function versionBanner() {
10707
- return `v0.1.15-alpha.15`;
10717
+ return `v0.1.15-alpha.17`;
10708
10718
  }
10709
10719
  const COMMANDS = [
10710
10720
  {
@@ -11893,15 +11903,15 @@ function isPidAlive(pid) {
11893
11903
  }
11894
11904
  }
11895
11905
  /**
11896
- * 获取安装操作互斥锁。
11906
+ * 尝试获取安装操作互斥锁。
11897
11907
  *
11898
11908
  * 利用 open(O_CREAT | O_EXCL) 的 POSIX 原子性确保同一时刻只有一个安装指令运行。
11899
11909
  * 覆盖的安装指令:upgrade-lark / install-openclaw / install-extension / install-cli / reset --worker。
11900
11910
  *
11901
- * 若锁被活跃进程持有,抛出包含持有者信息的 Error。
11902
- * 若锁文件属于已退出进程(crash 遗留),视为过期锁并覆盖。
11911
+ * 返回 null 表示成功获取锁;返回错误信息字符串表示锁被其他进程持有。
11912
+ * 与抛异常相比,返回值让调用方可以输出符合各自格式的 result,而不依赖 catch handler 统一处理。
11903
11913
  *
11904
- * 成功获取后自动注册 process.on('exit') 释放锁,无需调用方手动清理。
11914
+ * 成功获取后自动注册 process.once('exit') 释放锁,无需调用方手动清理。
11905
11915
  */
11906
11916
  function acquireInstallLock(command) {
11907
11917
  node_fs.default.mkdirSync(DIAGNOSE_DIR, { recursive: true });
@@ -11917,7 +11927,7 @@ function acquireInstallLock(command) {
11917
11927
  node_fs.default.closeSync(fd);
11918
11928
  process.once("exit", releaseInstallLock);
11919
11929
  console.error(`[install-lock] acquired command=${command} pid=${process.pid}`);
11920
- return;
11930
+ return null;
11921
11931
  } catch (e) {
11922
11932
  if (e.code !== "EEXIST") throw e;
11923
11933
  let existing = null;
@@ -11936,13 +11946,13 @@ function acquireInstallLock(command) {
11936
11946
  } catch {}
11937
11947
  continue;
11938
11948
  }
11939
- throw new Error(`另一个安装指令正在运行,请等待其完成后重试。\n 占用指令: ${existing.command}\n 进程 PID : ${existing.pid}\n 开始时间: ${existing.startedAt}\n 锁文件 : ${INSTALL_LOCK_FILE}`);
11949
+ return `另一个安装指令正在运行,请等待其完成后重试。\n 占用指令: ${existing.command}\n 进程 PID : ${existing.pid}\n 开始时间: ${existing.startedAt}\n 锁文件 : ${INSTALL_LOCK_FILE}`;
11940
11950
  }
11941
11951
  }
11942
11952
  /**
11943
11953
  * 释放安装操作互斥锁。
11944
11954
  * 仅删除由本进程创建的锁文件,避免误删其他进程的锁。
11945
- * 通常由 process.on('exit') 自动调用,不需要手动调用。
11955
+ * 通常由 process.once('exit') 自动调用,不需要手动调用。
11946
11956
  */
11947
11957
  function releaseInstallLock() {
11948
11958
  try {
@@ -11956,8 +11966,18 @@ function releaseInstallLock() {
11956
11966
  }
11957
11967
  //#endregion
11958
11968
  //#region src/index.ts
11969
+ /** 锁冲突时各 install 命令的统一处理:输出 { ok: false } 并退出 */
11970
+ function handleInstallLockConflict(lockErr) {
11971
+ console.log(JSON.stringify({
11972
+ ok: false,
11973
+ error: lockErr
11974
+ }));
11975
+ node_process.default.exit(1);
11976
+ }
11959
11977
  const args = node_process.default.argv.slice(2);
11960
11978
  const mode = args.find((a) => !a.startsWith("-"));
11979
+ const t0 = Date.now();
11980
+ const scene = getFlag(args, "scene");
11961
11981
  /**
11962
11982
  * Pull the first non-flag positional after the mode name.
11963
11983
  * (The mode itself is args[0] in the filtered set, so we skip index 0.)
@@ -12041,13 +12061,11 @@ async function main() {
12041
12061
  }
12042
12062
  const caller = getFlag(args, "caller");
12043
12063
  const traceId = getFlag(args, "trace-id");
12044
- const scene = getFlag(args, "scene");
12045
12064
  const profile = getFlag(args, "profile") === "experimental" ? "experimental" : "standard";
12046
12065
  const rc = setRunContext({
12047
12066
  caller,
12048
12067
  traceId
12049
12068
  });
12050
- const t0 = Date.now();
12051
12069
  console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
12052
12070
  switch (mode) {
12053
12071
  case "check": {
@@ -12154,8 +12172,18 @@ async function main() {
12154
12172
  console.error("Error: --task-id=<id> is required for worker");
12155
12173
  node_process.default.exit(1);
12156
12174
  }
12157
- acquireInstallLock("reset");
12158
12175
  const resultFile = resetResultFile(taskId);
12176
+ const resetLockErr = acquireInstallLock("reset");
12177
+ if (resetLockErr) {
12178
+ try {
12179
+ node_fs.default.writeFileSync(resultFile, JSON.stringify({
12180
+ status: "failed",
12181
+ error: resetLockErr
12182
+ }));
12183
+ } catch {}
12184
+ node_process.default.exitCode = 1;
12185
+ return;
12186
+ }
12159
12187
  const raw = await fetchCtxViaInnerApi({
12160
12188
  populate: planCtxPopulate({ command: "reset" }),
12161
12189
  caller,
@@ -12199,7 +12227,8 @@ async function main() {
12199
12227
  console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
12200
12228
  node_process.default.exit(1);
12201
12229
  }
12202
- acquireInstallLock("install-openclaw");
12230
+ const installOcLockErr = acquireInstallLock("install-openclaw");
12231
+ if (installOcLockErr) handleInstallLockConflict(installOcLockErr);
12203
12232
  const ossFileMapFlag = getFlag(args, "oss_file_map");
12204
12233
  let installOssFileMap;
12205
12234
  let rawForTelemetry;
@@ -12242,7 +12271,8 @@ async function main() {
12242
12271
  console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
12243
12272
  node_process.default.exit(1);
12244
12273
  }
12245
- acquireInstallLock("install-extension");
12274
+ const installExtLockErr = acquireInstallLock("install-extension");
12275
+ if (installExtLockErr) handleInstallLockConflict(installExtLockErr);
12246
12276
  const all = args.includes("--all");
12247
12277
  const names = getMultiFlag(args, "extension");
12248
12278
  const homeBase = getFlag(args, "home_base");
@@ -12306,7 +12336,8 @@ async function main() {
12306
12336
  console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
12307
12337
  node_process.default.exit(1);
12308
12338
  }
12309
- acquireInstallLock("install-cli");
12339
+ const installCliLockErr = acquireInstallLock("install-cli");
12340
+ if (installCliLockErr) handleInstallLockConflict(installCliLockErr);
12310
12341
  const homeBase = getFlag(args, "home_base");
12311
12342
  const ossFileMapFlag = getFlag(args, "oss_file_map");
12312
12343
  let installOssFileMap;
@@ -12450,7 +12481,31 @@ async function main() {
12450
12481
  }
12451
12482
  case "upgrade-lark": {
12452
12483
  const checkOnly = args.includes("--check");
12453
- if (!checkOnly) acquireInstallLock("upgrade-lark");
12484
+ if (!checkOnly) {
12485
+ const upgradeLockErr = acquireInstallLock("upgrade-lark");
12486
+ if (upgradeLockErr) {
12487
+ const fixStatus = computeFixStatus(scene, "failed");
12488
+ const failResult = {
12489
+ status: "failed",
12490
+ error: upgradeLockErr,
12491
+ logFile: "",
12492
+ fixStatus
12493
+ };
12494
+ if (fixStatus !== void 0) writeFixStatusEvent(fixStatus, failResult, console.error);
12495
+ console.log(JSON.stringify(failResult));
12496
+ reportUpgradeLarkToSlardar({
12497
+ scene,
12498
+ checkOnly,
12499
+ durationMs: Date.now() - t0,
12500
+ resultStatus: "failed",
12501
+ error: upgradeLockErr,
12502
+ logFile: "",
12503
+ resultSummary: buildUpgradeLarkResultSummary(failResult, checkOnly)
12504
+ });
12505
+ node_process.default.exitCode = 1;
12506
+ return;
12507
+ }
12508
+ }
12454
12509
  const skipRestart = args.includes("--skip-restart");
12455
12510
  const result = runUpgradeLark({
12456
12511
  runId: rc.runId,
@@ -12516,6 +12571,47 @@ main().catch((err) => {
12516
12571
  const msg = err instanceof Error ? err.message : String(err);
12517
12572
  console.error(`${mode ?? "<no-mode>"}: error message=${msg}`);
12518
12573
  node_process.default.stderr.write(`Error: ${msg}\n`);
12574
+ if (mode) {
12575
+ const durationMs = Date.now() - t0;
12576
+ if (mode === "upgrade-lark") {
12577
+ const checkOnly = args.includes("--check");
12578
+ const fixStatus = computeFixStatus(scene, "failed");
12579
+ const failResult = {
12580
+ status: "failed",
12581
+ error: msg,
12582
+ logFile: "",
12583
+ fixStatus
12584
+ };
12585
+ if (fixStatus !== void 0) writeFixStatusEvent(fixStatus, failResult, console.error);
12586
+ console.log(JSON.stringify(failResult));
12587
+ reportUpgradeLarkToSlardar({
12588
+ scene,
12589
+ checkOnly,
12590
+ durationMs,
12591
+ resultStatus: "failed",
12592
+ error: msg,
12593
+ logFile: "",
12594
+ resultSummary: buildUpgradeLarkResultSummary(failResult, checkOnly)
12595
+ });
12596
+ } else if ([
12597
+ "doctor",
12598
+ "check",
12599
+ "repair",
12600
+ "install-openclaw",
12601
+ "install-extension",
12602
+ "install-cli",
12603
+ "download-resource",
12604
+ "reset",
12605
+ "lark-cli-init"
12606
+ ].includes(mode)) reportDoctorRunToSlardar({
12607
+ command: mode,
12608
+ scene,
12609
+ profile: getFlag(args, "profile") === "experimental" ? "experimental" : "standard",
12610
+ fix: args.includes("--fix"),
12611
+ durationMs,
12612
+ success: false
12613
+ });
12614
+ }
12519
12615
  node_process.default.exitCode = 1;
12520
12616
  });
12521
12617
  //#endregion
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.15",
3
+ "version": "0.1.15-alpha.17",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {