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

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 +176 -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-beta.0";
55
+ return "0.1.13-alpha.0";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -3555,6 +3555,86 @@ 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}`);
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
+ })], FeishuBotChannelConfigRule);
3637
+ //#endregion
3558
3638
  //#region src/check.ts
3559
3639
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
3560
3640
  * AND a DoctorReport-shape payload (for `openclaw.report_cli_run`). The
@@ -4154,9 +4234,10 @@ function makeLogger(logFile) {
4154
4234
  /**
4155
4235
  * Start an async reset task: spawn a detached child process and return the taskId.
4156
4236
  *
4157
- * The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
4237
+ * The child process runs: node cli.js reset --worker --task-id=xxx
4238
+ * The worker fetches ctx from innerApi itself — no --ctx passthrough.
4158
4239
  */
4159
- function startAsyncReset(ctxBase64) {
4240
+ function startAsyncReset() {
4160
4241
  const taskId = (0, node_crypto.randomUUID)();
4161
4242
  const resultFile = resetResultFile(taskId);
4162
4243
  const log = makeLogger(resetLogFile(taskId));
@@ -4180,8 +4261,7 @@ function startAsyncReset(ctxBase64) {
4180
4261
  process.argv[1],
4181
4262
  "reset",
4182
4263
  "--worker",
4183
- `--task-id=${taskId}`,
4184
- `--ctx=${ctxBase64}`
4264
+ `--task-id=${taskId}`
4185
4265
  ], {
4186
4266
  detached: true,
4187
4267
  stdio: "ignore",
@@ -6695,6 +6775,60 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6695
6775
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6696
6776
  }
6697
6777
  /**
6778
+ * Fix bot account allowFrom and appSecret using larkApps from innerApi.
6779
+ *
6780
+ * For each bot account (key starts with `bot-cli_`):
6781
+ * - allowFrom must contain the bot's own creatorOpenID from larkApps
6782
+ * - appSecret must be either the canonical provider-ref or match larkApps plaintext
6783
+ *
6784
+ * Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
6785
+ */
6786
+ function fixBotChannelConfig(configPath, larkApps, log) {
6787
+ if (!larkApps || larkApps.length === 0) {
6788
+ log("no larkApps data, skip bot channel config fix");
6789
+ return;
6790
+ }
6791
+ const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
6792
+ const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
6793
+ if (!accounts) {
6794
+ log("no feishu accounts in config, skip bot channel config fix");
6795
+ return;
6796
+ }
6797
+ let fixCount = 0;
6798
+ for (const [, account] of Object.entries(accounts)) {
6799
+ const bot = asRecord(account);
6800
+ if (!bot) continue;
6801
+ const appId = bot.appId;
6802
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
6803
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
6804
+ if (!larkApp) continue;
6805
+ const creatorOpenID = larkApp.creatorOpenID;
6806
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
6807
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
6808
+ if (!allowFrom.includes(creatorOpenID)) {
6809
+ allowFrom.push(creatorOpenID);
6810
+ bot.allowFrom = allowFrom;
6811
+ fixCount++;
6812
+ }
6813
+ }
6814
+ const secret = bot.appSecret;
6815
+ let needsFix = false;
6816
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
6817
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
6818
+ } else if (typeof secret === "string") {
6819
+ if (secret !== larkApp.appSecret) needsFix = true;
6820
+ } else needsFix = true;
6821
+ if (needsFix) {
6822
+ bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
6823
+ fixCount++;
6824
+ }
6825
+ }
6826
+ if (fixCount > 0) {
6827
+ node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
6828
+ log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
6829
+ } else log("bot channel config ok, no fixes needed");
6830
+ }
6831
+ /**
6698
6832
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6699
6833
  *
6700
6834
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -6839,6 +6973,7 @@ async function runReset(input, taskId, resultFile) {
6839
6973
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
6840
6974
  step(6);
6841
6975
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
6976
+ fixBotChannelConfig(configPath, vars.larkApps, log);
6842
6977
  step(7);
6843
6978
  verifyStartupScripts(configDir, log);
6844
6979
  step(8);
@@ -7637,7 +7772,8 @@ function normalizeCtx(raw) {
7637
7772
  reset: {
7638
7773
  templateVars: r.reset.templateVars ?? {},
7639
7774
  coreBackup: r.reset.coreBackup
7640
- }
7775
+ },
7776
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7641
7777
  };
7642
7778
  }
7643
7779
  const vars = r.vars ?? {};
@@ -7662,7 +7798,8 @@ function normalizeCtx(raw) {
7662
7798
  reset: {
7663
7799
  templateVars: resetData.templateVars ?? {},
7664
7800
  coreBackup: resetData.coreBackup
7665
- }
7801
+ },
7802
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7666
7803
  };
7667
7804
  }
7668
7805
  function fillApp(src) {
@@ -7727,7 +7864,8 @@ function buildCheckInput(raw, configPathOverride) {
7727
7864
  providerFilePath: PROVIDER_FILE_PATH,
7728
7865
  secretsFilePath: SECRETS_FILE_PATH,
7729
7866
  templateVars: ctx.app.templateVars,
7730
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7867
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7868
+ larkApps: ctx.larkApps
7731
7869
  },
7732
7870
  templateVars: ctx.app.templateVars
7733
7871
  };
@@ -7759,7 +7897,8 @@ function buildRepairInput(raw, configPathOverride) {
7759
7897
  providerFilePath: PROVIDER_FILE_PATH,
7760
7898
  secretsFilePath: SECRETS_FILE_PATH,
7761
7899
  templateVars: ctx.app.templateVars,
7762
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7900
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7901
+ larkApps: ctx.larkApps
7763
7902
  },
7764
7903
  repairData: {
7765
7904
  secretsContent: ctx.secrets.secretsContent,
@@ -7795,7 +7934,8 @@ function buildResetInput(raw, configPathOverride) {
7795
7934
  providerFilePath: PROVIDER_FILE_PATH,
7796
7935
  secretsFilePath: SECRETS_FILE_PATH,
7797
7936
  templateVars: ctx.app.templateVars,
7798
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7937
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7938
+ larkApps: ctx.larkApps
7799
7939
  },
7800
7940
  resetData: {
7801
7941
  templateVars: ctx.reset.templateVars,
@@ -10105,7 +10245,7 @@ async function reportCliRun(opts) {
10105
10245
  //#region src/help.ts
10106
10246
  const BIN = "mclaw-diagnose";
10107
10247
  function versionBanner() {
10108
- return `v0.1.12-beta.0`;
10248
+ return `v0.1.13-alpha.0`;
10109
10249
  }
10110
10250
  const COMMANDS = [
10111
10251
  {
@@ -10528,6 +10668,20 @@ function planVarsFields(opts = {}) {
10528
10668
  }
10529
10669
  return [...fields].sort();
10530
10670
  }
10671
+ /** Check if any enabled rule declares a specific Vars field in usesVars. */
10672
+ function doesAnyRuleUseVar(field, opts = {}) {
10673
+ const disabled = new Set(opts.disabled ?? []);
10674
+ const only = opts.onlyRules && opts.onlyRules.length > 0 ? new Set(opts.onlyRules) : null;
10675
+ const profile = opts.profile ?? "standard";
10676
+ for (const rule of getAllRules()) {
10677
+ const key = rule.meta.key;
10678
+ if (disabled.has(key)) continue;
10679
+ if (only && !only.has(key)) continue;
10680
+ if (!isProfileEnabled(rule, profile)) continue;
10681
+ if ((rule.meta.usesVars ?? []).includes(field)) return true;
10682
+ }
10683
+ return false;
10684
+ }
10531
10685
  /**
10532
10686
  * Plan a `populate` selector for a given subcommand.
10533
10687
  *
@@ -10552,6 +10706,11 @@ function planCtxPopulate(opts) {
10552
10706
  profile: opts.profile
10553
10707
  });
10554
10708
  if (appFields.length > 0) populate.app = appFields;
10709
+ if (doesAnyRuleUseVar("larkApps", {
10710
+ disabled: opts.disabled,
10711
+ onlyRules: opts.onlyRules,
10712
+ profile: opts.profile
10713
+ })) populate.larkApps = true;
10555
10714
  if (opts.command === "repair") populate.secrets = true;
10556
10715
  else if (opts.command === "reset") {
10557
10716
  populate.secrets = true;
@@ -10658,8 +10817,8 @@ function getMultiFlag(args, name) {
10658
10817
  * case but is no longer consulted.
10659
10818
  */
10660
10819
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
10661
- scene,
10662
- profile,
10820
+ scene: void 0,
10821
+ profile: "standard",
10663
10822
  fix: false
10664
10823
  }) {
10665
10824
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -10819,27 +10978,15 @@ async function main() {
10819
10978
  break;
10820
10979
  }
10821
10980
  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")) {
10981
+ if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
10982
+ else if (args.includes("--worker")) {
10836
10983
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
10837
10984
  if (!taskId) {
10838
10985
  console.error("Error: --task-id=<id> is required for worker");
10839
10986
  node_process.default.exit(1);
10840
10987
  }
10841
10988
  const resultFile = resetResultFile(taskId);
10842
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10989
+ const raw = await fetchCtxViaInnerApi({
10843
10990
  populate: planCtxPopulate({ command: "reset" }),
10844
10991
  caller,
10845
10992
  traceId
@@ -10863,7 +11010,7 @@ async function main() {
10863
11010
  return;
10864
11011
  }
10865
11012
  } else {
10866
- console.error("Usage: reset --async [--ctx=<base64>] | reset --worker --task-id=<id> [--ctx=<base64>]");
11013
+ console.error("Usage: reset --async | reset --worker --task-id=<id>");
10867
11014
  node_process.default.exit(1);
10868
11015
  }
10869
11016
  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-beta.0",
3
+ "version": "0.1.13-alpha.0",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {