@lark-apaas/openclaw-scripts-diagnose-cli 0.1.12 → 0.1.13-alpha.1

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 +177 -29
  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.12";
55
+ return "0.1.13-alpha.1";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -3555,6 +3555,87 @@ CleanupInstallBackupDirsRule = __decorate([Rule({
3555
3555
  level: "critical"
3556
3556
  })], CleanupInstallBackupDirsRule);
3557
3557
  //#endregion
3558
+ //#region src/rules/feishu-bot-channel-config.ts
3559
+ /**
3560
+ * Ensures each bot account's channel config is correct:
3561
+ * 1. `allowFrom` contains its own `creatorOpenID` from larkApps
3562
+ * 2. `appSecret` is either the canonical provider-ref or matches larkApps plaintext
3563
+ */
3564
+ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends DiagnoseRule {
3565
+ validate(ctx) {
3566
+ const larkApps = ctx.vars.larkApps;
3567
+ if (!larkApps || larkApps.length === 0) return { pass: true };
3568
+ const accounts = asRecord(getNestedMap(ctx.config, "channels", "feishu")?.accounts);
3569
+ if (!accounts) return { pass: true };
3570
+ const issues = [];
3571
+ for (const [accountId, account] of Object.entries(accounts)) {
3572
+ const bot = asRecord(account);
3573
+ if (!bot) continue;
3574
+ const appId = bot.appId;
3575
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3576
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
3577
+ if (!larkApp) continue;
3578
+ const creatorOpenID = larkApp.creatorOpenID;
3579
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3580
+ if (!(Array.isArray(bot.allowFrom) ? bot.allowFrom : []).includes(creatorOpenID)) issues.push(`${accountId} allowFrom missing creatorOpenID ${creatorOpenID.length > 8 ? creatorOpenID.slice(0, 4) + "***" + creatorOpenID.slice(-4) : "***"}`);
3581
+ }
3582
+ const secret = bot.appSecret;
3583
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3584
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${accountId} appSecret is a provider-ref but not the canonical one`);
3585
+ } else if (typeof secret === "string") {
3586
+ if (secret !== larkApp.appSecret) issues.push(`${accountId} appSecret plaintext mismatch`);
3587
+ } else issues.push(`${accountId} appSecret has unexpected type`);
3588
+ }
3589
+ if (issues.length === 0) return { pass: true };
3590
+ return {
3591
+ pass: false,
3592
+ message: issues.join("; ")
3593
+ };
3594
+ }
3595
+ repair(ctx) {
3596
+ const larkApps = ctx.vars.larkApps;
3597
+ if (!larkApps || larkApps.length === 0) return;
3598
+ const accounts = asRecord(getNestedMap(ctx.config, "channels", "feishu")?.accounts);
3599
+ if (!accounts) return;
3600
+ for (const [, account] of Object.entries(accounts)) {
3601
+ const bot = asRecord(account);
3602
+ if (!bot) continue;
3603
+ const appId = bot.appId;
3604
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3605
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
3606
+ if (!larkApp) continue;
3607
+ const creatorOpenID = larkApp.creatorOpenID;
3608
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3609
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
3610
+ if (!allowFrom.includes(creatorOpenID)) {
3611
+ allowFrom.push(creatorOpenID);
3612
+ bot.allowFrom = allowFrom;
3613
+ }
3614
+ }
3615
+ const secret = bot.appSecret;
3616
+ let needsFix = false;
3617
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3618
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
3619
+ } else if (typeof secret === "string") {
3620
+ if (secret !== larkApp.appSecret) needsFix = true;
3621
+ } else needsFix = true;
3622
+ if (needsFix) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
3623
+ }
3624
+ }
3625
+ };
3626
+ FeishuBotChannelConfigRule = __decorate([Rule({
3627
+ key: "feishu_bot_channel_config",
3628
+ description: "确保多账号飞书配置中每个 bot 账号的 allowFrom 包含其创建者 openID 且 appSecret 值正确",
3629
+ dependsOn: [
3630
+ "config_syntax_check",
3631
+ "feishu_default_account",
3632
+ "feishu_bot_id"
3633
+ ],
3634
+ repairMode: "standard",
3635
+ usesVars: ["larkApps"],
3636
+ level: "critical"
3637
+ })], FeishuBotChannelConfigRule);
3638
+ //#endregion
3558
3639
  //#region src/check.ts
3559
3640
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
3560
3641
  * AND a DoctorReport-shape payload (for `openclaw.report_cli_run`). The
@@ -4154,9 +4235,10 @@ function makeLogger(logFile) {
4154
4235
  /**
4155
4236
  * Start an async reset task: spawn a detached child process and return the taskId.
4156
4237
  *
4157
- * The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
4238
+ * The child process runs: node cli.js reset --worker --task-id=xxx
4239
+ * The worker fetches ctx from innerApi itself — no --ctx passthrough.
4158
4240
  */
4159
- function startAsyncReset(ctxBase64) {
4241
+ function startAsyncReset() {
4160
4242
  const taskId = (0, node_crypto.randomUUID)();
4161
4243
  const resultFile = resetResultFile(taskId);
4162
4244
  const log = makeLogger(resetLogFile(taskId));
@@ -4180,8 +4262,7 @@ function startAsyncReset(ctxBase64) {
4180
4262
  process.argv[1],
4181
4263
  "reset",
4182
4264
  "--worker",
4183
- `--task-id=${taskId}`,
4184
- `--ctx=${ctxBase64}`
4265
+ `--task-id=${taskId}`
4185
4266
  ], {
4186
4267
  detached: true,
4187
4268
  stdio: "ignore",
@@ -6695,6 +6776,60 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6695
6776
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6696
6777
  }
6697
6778
  /**
6779
+ * Fix bot account allowFrom and appSecret using larkApps from innerApi.
6780
+ *
6781
+ * For each bot account (key starts with `bot-cli_`):
6782
+ * - allowFrom must contain the bot's own creatorOpenID from larkApps
6783
+ * - appSecret must be either the canonical provider-ref or match larkApps plaintext
6784
+ *
6785
+ * Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
6786
+ */
6787
+ function fixBotChannelConfig(configPath, larkApps, log) {
6788
+ if (!larkApps || larkApps.length === 0) {
6789
+ log("no larkApps data, skip bot channel config fix");
6790
+ return;
6791
+ }
6792
+ const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
6793
+ const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
6794
+ if (!accounts) {
6795
+ log("no feishu accounts in config, skip bot channel config fix");
6796
+ return;
6797
+ }
6798
+ let fixCount = 0;
6799
+ for (const [, account] of Object.entries(accounts)) {
6800
+ const bot = asRecord(account);
6801
+ if (!bot) continue;
6802
+ const appId = bot.appId;
6803
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
6804
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
6805
+ if (!larkApp) continue;
6806
+ const creatorOpenID = larkApp.creatorOpenID;
6807
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
6808
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
6809
+ if (!allowFrom.includes(creatorOpenID)) {
6810
+ allowFrom.push(creatorOpenID);
6811
+ bot.allowFrom = allowFrom;
6812
+ fixCount++;
6813
+ }
6814
+ }
6815
+ const secret = bot.appSecret;
6816
+ let needsFix = false;
6817
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
6818
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
6819
+ } else if (typeof secret === "string") {
6820
+ if (secret !== larkApp.appSecret) needsFix = true;
6821
+ } else needsFix = true;
6822
+ if (needsFix) {
6823
+ bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
6824
+ fixCount++;
6825
+ }
6826
+ }
6827
+ if (fixCount > 0) {
6828
+ node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
6829
+ log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
6830
+ } else log("bot channel config ok, no fixes needed");
6831
+ }
6832
+ /**
6698
6833
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6699
6834
  *
6700
6835
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -6839,6 +6974,7 @@ async function runReset(input, taskId, resultFile) {
6839
6974
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
6840
6975
  step(6);
6841
6976
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
6977
+ fixBotChannelConfig(configPath, vars.larkApps, log);
6842
6978
  step(7);
6843
6979
  verifyStartupScripts(configDir, log);
6844
6980
  step(8);
@@ -7637,7 +7773,8 @@ function normalizeCtx(raw) {
7637
7773
  reset: {
7638
7774
  templateVars: r.reset.templateVars ?? {},
7639
7775
  coreBackup: r.reset.coreBackup
7640
- }
7776
+ },
7777
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7641
7778
  };
7642
7779
  }
7643
7780
  const vars = r.vars ?? {};
@@ -7662,7 +7799,8 @@ function normalizeCtx(raw) {
7662
7799
  reset: {
7663
7800
  templateVars: resetData.templateVars ?? {},
7664
7801
  coreBackup: resetData.coreBackup
7665
- }
7802
+ },
7803
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7666
7804
  };
7667
7805
  }
7668
7806
  function fillApp(src) {
@@ -7727,7 +7865,8 @@ function buildCheckInput(raw, configPathOverride) {
7727
7865
  providerFilePath: PROVIDER_FILE_PATH,
7728
7866
  secretsFilePath: SECRETS_FILE_PATH,
7729
7867
  templateVars: ctx.app.templateVars,
7730
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7868
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7869
+ larkApps: ctx.larkApps
7731
7870
  },
7732
7871
  templateVars: ctx.app.templateVars
7733
7872
  };
@@ -7759,7 +7898,8 @@ function buildRepairInput(raw, configPathOverride) {
7759
7898
  providerFilePath: PROVIDER_FILE_PATH,
7760
7899
  secretsFilePath: SECRETS_FILE_PATH,
7761
7900
  templateVars: ctx.app.templateVars,
7762
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7901
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7902
+ larkApps: ctx.larkApps
7763
7903
  },
7764
7904
  repairData: {
7765
7905
  secretsContent: ctx.secrets.secretsContent,
@@ -7795,7 +7935,8 @@ function buildResetInput(raw, configPathOverride) {
7795
7935
  providerFilePath: PROVIDER_FILE_PATH,
7796
7936
  secretsFilePath: SECRETS_FILE_PATH,
7797
7937
  templateVars: ctx.app.templateVars,
7798
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7938
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7939
+ larkApps: ctx.larkApps
7799
7940
  },
7800
7941
  resetData: {
7801
7942
  templateVars: ctx.reset.templateVars,
@@ -10105,7 +10246,7 @@ async function reportCliRun(opts) {
10105
10246
  //#region src/help.ts
10106
10247
  const BIN = "mclaw-diagnose";
10107
10248
  function versionBanner() {
10108
- return `v0.1.12`;
10249
+ return `v0.1.13-alpha.1`;
10109
10250
  }
10110
10251
  const COMMANDS = [
10111
10252
  {
@@ -10528,6 +10669,20 @@ function planVarsFields(opts = {}) {
10528
10669
  }
10529
10670
  return [...fields].sort();
10530
10671
  }
10672
+ /** Check if any enabled rule declares a specific Vars field in usesVars. */
10673
+ function doesAnyRuleUseVar(field, opts = {}) {
10674
+ const disabled = new Set(opts.disabled ?? []);
10675
+ const only = opts.onlyRules && opts.onlyRules.length > 0 ? new Set(opts.onlyRules) : null;
10676
+ const profile = opts.profile ?? "standard";
10677
+ for (const rule of getAllRules()) {
10678
+ const key = rule.meta.key;
10679
+ if (disabled.has(key)) continue;
10680
+ if (only && !only.has(key)) continue;
10681
+ if (!isProfileEnabled(rule, profile)) continue;
10682
+ if ((rule.meta.usesVars ?? []).includes(field)) return true;
10683
+ }
10684
+ return false;
10685
+ }
10531
10686
  /**
10532
10687
  * Plan a `populate` selector for a given subcommand.
10533
10688
  *
@@ -10552,6 +10707,11 @@ function planCtxPopulate(opts) {
10552
10707
  profile: opts.profile
10553
10708
  });
10554
10709
  if (appFields.length > 0) populate.app = appFields;
10710
+ if (doesAnyRuleUseVar("larkApps", {
10711
+ disabled: opts.disabled,
10712
+ onlyRules: opts.onlyRules,
10713
+ profile: opts.profile
10714
+ })) populate.larkApps = true;
10555
10715
  if (opts.command === "repair") populate.secrets = true;
10556
10716
  else if (opts.command === "reset") {
10557
10717
  populate.secrets = true;
@@ -10658,8 +10818,8 @@ function getMultiFlag(args, name) {
10658
10818
  * case but is no longer consulted.
10659
10819
  */
10660
10820
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
10661
- scene,
10662
- profile,
10821
+ scene: void 0,
10822
+ profile: "standard",
10663
10823
  fix: false
10664
10824
  }) {
10665
10825
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -10819,27 +10979,15 @@ async function main() {
10819
10979
  break;
10820
10980
  }
10821
10981
  case "reset":
10822
- if (args.includes("--async")) {
10823
- const ctxArg = args.find((a) => a.startsWith("--ctx="));
10824
- let ctxBase64;
10825
- if (ctxArg) ctxBase64 = ctxArg.slice(6);
10826
- else {
10827
- const fetched = await fetchCtxViaInnerApi({
10828
- populate: planCtxPopulate({ command: "reset" }),
10829
- caller,
10830
- traceId
10831
- });
10832
- ctxBase64 = Buffer.from(JSON.stringify(fetched), "utf-8").toString("base64");
10833
- }
10834
- console.log(JSON.stringify(startAsyncReset(ctxBase64)));
10835
- } else if (args.includes("--worker")) {
10982
+ if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
10983
+ else if (args.includes("--worker")) {
10836
10984
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
10837
10985
  if (!taskId) {
10838
10986
  console.error("Error: --task-id=<id> is required for worker");
10839
10987
  node_process.default.exit(1);
10840
10988
  }
10841
10989
  const resultFile = resetResultFile(taskId);
10842
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10990
+ const raw = await fetchCtxViaInnerApi({
10843
10991
  populate: planCtxPopulate({ command: "reset" }),
10844
10992
  caller,
10845
10993
  traceId
@@ -10863,7 +11011,7 @@ async function main() {
10863
11011
  return;
10864
11012
  }
10865
11013
  } else {
10866
- console.error("Usage: reset --async [--ctx=<base64>] | reset --worker --task-id=<id> [--ctx=<base64>]");
11014
+ console.error("Usage: reset --async | reset --worker --task-id=<id>");
10867
11015
  node_process.default.exit(1);
10868
11016
  }
10869
11017
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.13-alpha.1",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {