@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.30 → 0.1.1-alpha.31

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 +188 -19
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -89,6 +89,17 @@ function topoSort(rules) {
89
89
  //#endregion
90
90
  //#region src/utils.ts
91
91
  /**
92
+ * Canonical provider-ref for the feishu app secret. Both
93
+ * `feishu_default_account` (multi-agent path) and `feishu_channel`
94
+ * (single-agent path) use this as the source-of-truth `appSecret`
95
+ * value when repairing.
96
+ */
97
+ const DEFAULT_FEISHU_APP_SECRET = {
98
+ source: "file",
99
+ provider: "miaoda-secret-provider",
100
+ id: "/channels_feishu_app_secret"
101
+ };
102
+ /**
92
103
  * Navigate nested object by keys, returning the value if it's a non-array object,
93
104
  * or undefined otherwise.
94
105
  */
@@ -153,6 +164,25 @@ function setNestedValue(obj, keys, value) {
153
164
  }
154
165
  current[keys[keys.length - 1]] = value;
155
166
  }
167
+ /**
168
+ * Locate the "main" agent in `agents.list`. Preference order:
169
+ * 1. Explicit `default: true` entry.
170
+ * 2. Entry with `id === 'main'` (project naming convention).
171
+ * 3. First entry in the list (positional fallback).
172
+ * Returns `undefined` when `agents.list` is missing or empty.
173
+ */
174
+ function findMainAgent(config) {
175
+ const agents = getNestedMap(config, "agents");
176
+ if (!agents) return void 0;
177
+ const list = agents.list;
178
+ if (!Array.isArray(list) || list.length === 0) return void 0;
179
+ const isObj = (a) => a != null && typeof a === "object" && !Array.isArray(a);
180
+ const explicit = list.find((a) => isObj(a) && a.default === true);
181
+ if (explicit) return explicit;
182
+ const namedMain = list.find((a) => isObj(a) && a.id === "main");
183
+ if (namedMain) return namedMain;
184
+ return isObj(list[0]) ? list[0] : void 0;
185
+ }
156
186
  /** Analyze which miaoda providers the config references. */
157
187
  function analyzeProviderDeps(config) {
158
188
  const deps = {
@@ -618,16 +648,11 @@ SecretProviderRule = _SecretProviderRule = __decorate([Rule({
618
648
  })], SecretProviderRule);
619
649
  //#endregion
620
650
  //#region src/rules/feishu-channel.ts
621
- var _FeishuChannelRule;
651
+ /**
652
+ * Owns `channels.feishu.enabled` + single-agent top-level appId/appSecret.
653
+ * Multi-agent shape (`accounts` present) belongs to `feishu_default_account`.
654
+ */
622
655
  let FeishuChannelRule = class FeishuChannelRule extends DiagnoseRule {
623
- static {
624
- _FeishuChannelRule = this;
625
- }
626
- static DEFAULT_APP_SECRET = {
627
- source: "file",
628
- provider: "miaoda-secret-provider",
629
- id: "/channels_feishu_app_secret"
630
- };
631
656
  validate(ctx) {
632
657
  const feishu = getNestedMap(ctx.config, "channels", "feishu");
633
658
  if (!feishu) return {
@@ -638,16 +663,16 @@ let FeishuChannelRule = class FeishuChannelRule extends DiagnoseRule {
638
663
  pass: false,
639
664
  message: "channels.feishu.enabled mismatch: got " + feishu.enabled + ", expected true"
640
665
  };
666
+ if (asRecord(feishu.accounts)) return { pass: true };
641
667
  if (feishu.appId !== ctx.vars.feishuAppID) return {
642
668
  pass: false,
643
- message: "channels.feishu.appId mismatch: got " + feishu.appId + ", expected " + ctx.vars.feishuAppID
669
+ message: `channels.feishu.appId mismatch: got ${feishu.appId}, expected ${ctx.vars.feishuAppID}`
644
670
  };
645
- const expectedSecret = _FeishuChannelRule.DEFAULT_APP_SECRET;
646
671
  const secret = feishu.appSecret;
647
672
  if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
648
- if (!matchMap(secret, expectedSecret)) return {
673
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) return {
649
674
  pass: false,
650
- message: "channels.feishu.appSecret object mismatch: got " + JSON.stringify(secret)
675
+ message: `channels.feishu.appSecret object mismatch: got ${JSON.stringify(secret)}`
651
676
  };
652
677
  } else if (typeof secret === "string") {
653
678
  if (secret !== ctx.vars.feishuAppSecret) return {
@@ -666,6 +691,7 @@ let FeishuChannelRule = class FeishuChannelRule extends DiagnoseRule {
666
691
  "feishu",
667
692
  "enabled"
668
693
  ], true);
694
+ if (asRecord(getNestedMap(ctx.config, "channels", "feishu").accounts)) return;
669
695
  setNestedValue(ctx.config, [
670
696
  "channels",
671
697
  "feishu",
@@ -675,27 +701,166 @@ let FeishuChannelRule = class FeishuChannelRule extends DiagnoseRule {
675
701
  "channels",
676
702
  "feishu",
677
703
  "appSecret"
678
- ], _FeishuChannelRule.DEFAULT_APP_SECRET);
704
+ ], DEFAULT_FEISHU_APP_SECRET);
679
705
  }
680
706
  };
681
- FeishuChannelRule = _FeishuChannelRule = __decorate([Rule({
707
+ FeishuChannelRule = __decorate([Rule({
682
708
  key: "feishu_channel",
683
- dependsOn: ["config_syntax_check"],
709
+ dependsOn: ["config_syntax_check", "feishu_default_account"],
684
710
  repairMode: "standard"
685
711
  })], FeishuChannelRule);
686
712
  //#endregion
687
713
  //#region src/rules/feishu-default-account.ts
714
+ /**
715
+ * Owner of the multi-agent feishu channel shape: migrates legacy v1/v2
716
+ * (top-level appId + defaultAccount/default) into v3 (`bot-<appId>` account),
717
+ * detects + fixes drift on the main bot's appId/appSecret. Single-agent
718
+ * configs (no `accounts`) are out of scope — handled by `feishu_channel`.
719
+ */
688
720
  let FeishuDefaultAccountRule = class FeishuDefaultAccountRule extends DiagnoseRule {
689
- validate(_ctx) {
721
+ validate(ctx) {
722
+ const feishu = getNestedMap(ctx.config, "channels", "feishu");
723
+ if (!feishu) return { pass: true };
724
+ const accounts = asRecord(feishu.accounts);
725
+ if (!accounts) return { pass: true };
726
+ const topAppId = feishu.appId;
727
+ if (typeof topAppId === "string" && topAppId !== "") return {
728
+ pass: false,
729
+ message: "channels.feishu has legacy shape; needs migration to accounts.bot-<appId>"
730
+ };
731
+ const mainBot = findMainBotAccount(ctx.config, accounts);
732
+ if (mainBot) {
733
+ const expectedAppId = ctx.vars?.feishuAppID;
734
+ if (typeof expectedAppId === "string" && expectedAppId !== "" && mainBot.acc.appId !== expectedAppId) return {
735
+ pass: false,
736
+ message: `accounts.${mainBot.accountId}.appId mismatch: got ${mainBot.acc.appId}, expected ${expectedAppId}`
737
+ };
738
+ if (!secretMatchesCanonical(mainBot.acc.appSecret)) return {
739
+ pass: false,
740
+ message: `accounts.${mainBot.accountId}.appSecret drift`
741
+ };
742
+ }
690
743
  return { pass: true };
691
744
  }
692
- repair(_ctx) {}
745
+ repair(ctx) {
746
+ const feishu = getNestedMap(ctx.config, "channels", "feishu");
747
+ if (!feishu) return;
748
+ const accounts = asRecord(feishu.accounts);
749
+ if (!accounts) return;
750
+ const topAppId = feishu.appId;
751
+ if (typeof topAppId === "string" && topAppId !== "") {
752
+ this.migrate(ctx, feishu, accounts, topAppId);
753
+ return;
754
+ }
755
+ this.enforceMainBotValues(ctx, accounts);
756
+ }
757
+ migrate(ctx, feishu, accounts, topAppId) {
758
+ const effectiveAppId = nonEmpty(ctx.vars?.feishuAppID) ?? topAppId;
759
+ const expectedKey = `bot-${effectiveAppId}`;
760
+ const existingBot = asRecord(accounts[expectedKey]) ?? {};
761
+ const defaultAccount = asRecord(accounts.defaultAccount) ?? {};
762
+ const defaultAcc = asRecord(accounts.default) ?? {};
763
+ const merged = {
764
+ ...existingBot,
765
+ ...defaultAccount,
766
+ ...defaultAcc,
767
+ appId: effectiveAppId,
768
+ appSecret: DEFAULT_FEISHU_APP_SECRET
769
+ };
770
+ const chatID = ctx.vars?.teamChatID;
771
+ if (typeof chatID === "string" && chatID !== "") {
772
+ const existingGroups = asRecord(merged.groups) ?? {};
773
+ if (!(chatID in existingGroups)) merged.groups = {
774
+ ...existingGroups,
775
+ [chatID]: { requireMention: false }
776
+ };
777
+ }
778
+ accounts[expectedKey] = merged;
779
+ delete accounts.defaultAccount;
780
+ delete accounts.default;
781
+ delete feishu.appId;
782
+ delete feishu.appSecret;
783
+ this.rewireBindings(ctx.config, expectedKey);
784
+ }
785
+ enforceMainBotValues(ctx, accounts) {
786
+ const mainBot = findMainBotAccount(ctx.config, accounts);
787
+ if (!mainBot) return;
788
+ const acc = accounts[mainBot.accountId];
789
+ const expectedAppId = nonEmpty(ctx.vars?.feishuAppID);
790
+ if (expectedAppId !== void 0 && acc.appId !== expectedAppId) acc.appId = expectedAppId;
791
+ if (!secretMatchesCanonical(acc.appSecret)) acc.appSecret = DEFAULT_FEISHU_APP_SECRET;
792
+ }
793
+ rewireBindings(config, expectedKey) {
794
+ if (!Array.isArray(config.bindings)) return;
795
+ const bindings = config.bindings;
796
+ for (const b of bindings) {
797
+ const match = asRecord(asRecord(b)?.match);
798
+ if (match && match.channel === "feishu" && (match.accountId === "defaultAccount" || match.accountId === "default")) match.accountId = expectedKey;
799
+ }
800
+ const seen = /* @__PURE__ */ new Set();
801
+ const deduped = [];
802
+ for (const b of bindings) {
803
+ const rec = asRecord(b);
804
+ if (!rec) continue;
805
+ const match = asRecord(rec.match);
806
+ const key = JSON.stringify([
807
+ rec.agentId,
808
+ match?.channel,
809
+ match?.accountId
810
+ ]);
811
+ if (seen.has(key)) continue;
812
+ seen.add(key);
813
+ deduped.push(rec);
814
+ }
815
+ const mainId = findMainAgent(config)?.id;
816
+ if (typeof mainId === "string" && mainId !== "") {
817
+ if (!deduped.some((b) => {
818
+ const m = asRecord(b.match);
819
+ return b.agentId === mainId && m?.channel === "feishu" && m?.accountId === expectedKey;
820
+ })) deduped.push({
821
+ type: "route",
822
+ agentId: mainId,
823
+ match: {
824
+ channel: "feishu",
825
+ accountId: expectedKey
826
+ }
827
+ });
828
+ }
829
+ config.bindings = deduped;
830
+ }
693
831
  };
694
832
  FeishuDefaultAccountRule = __decorate([Rule({
695
833
  key: "feishu_default_account",
696
834
  dependsOn: ["config_syntax_check"],
697
835
  repairMode: "standard"
698
836
  })], FeishuDefaultAccountRule);
837
+ function nonEmpty(v) {
838
+ return typeof v === "string" && v !== "" ? v : void 0;
839
+ }
840
+ function findMainBotAccount(config, accounts) {
841
+ const mainId = findMainAgent(config)?.id;
842
+ if (typeof mainId !== "string" || mainId === "") return void 0;
843
+ const bindings = Array.isArray(config.bindings) ? config.bindings : [];
844
+ for (const b of bindings) {
845
+ const rec = asRecord(b);
846
+ const match = asRecord(rec?.match);
847
+ if (rec && match && rec.agentId === mainId && match.channel === "feishu") {
848
+ const accountId = match.accountId;
849
+ if (typeof accountId === "string") {
850
+ const acc = asRecord(accounts[accountId]);
851
+ if (acc) return {
852
+ accountId,
853
+ acc
854
+ };
855
+ }
856
+ }
857
+ }
858
+ }
859
+ /** Bot accounts must carry the canonical provider-ref `appSecret`. */
860
+ function secretMatchesCanonical(secret) {
861
+ if (typeof secret !== "object" || secret === null || Array.isArray(secret)) return false;
862
+ return matchMap(secret, DEFAULT_FEISHU_APP_SECRET);
863
+ }
699
864
  //#endregion
700
865
  //#region src/rules/gateway.ts
701
866
  var _GatewayRule;
@@ -2452,6 +2617,7 @@ function fillApp(src) {
2452
2617
  return {
2453
2618
  feishuAppID: src.feishuAppID ?? "",
2454
2619
  feishuAppSecret: src.feishuAppSecret ?? "",
2620
+ teamChatID: typeof src.teamChatID === "string" && src.teamChatID !== "" ? src.teamChatID : void 0,
2455
2621
  feishuOpenID: src.feishuOpenID ?? "",
2456
2622
  openClawName: src.openClawName ?? "",
2457
2623
  gatewayToken: src.gatewayToken ?? "",
@@ -2497,6 +2663,7 @@ function buildCheckInput(raw, configPathOverride) {
2497
2663
  vars: {
2498
2664
  feishuAppID: ctx.app.feishuAppID,
2499
2665
  feishuAppSecret: ctx.app.feishuAppSecret,
2666
+ teamChatID: ctx.app.teamChatID,
2500
2667
  innerAPIKey: ctx.app.innerAPIKey,
2501
2668
  gatewayToken: ctx.app.gatewayToken,
2502
2669
  baseURL: ctx.app.baseURL,
@@ -2522,6 +2689,7 @@ function buildRepairInput(raw, configPathOverride) {
2522
2689
  vars: {
2523
2690
  feishuAppID: ctx.app.feishuAppID,
2524
2691
  feishuAppSecret: ctx.app.feishuAppSecret,
2692
+ teamChatID: ctx.app.teamChatID,
2525
2693
  innerAPIKey: ctx.app.innerAPIKey,
2526
2694
  gatewayToken: ctx.app.gatewayToken,
2527
2695
  baseURL: ctx.app.baseURL,
@@ -2551,6 +2719,7 @@ function buildResetInput(raw, configPathOverride) {
2551
2719
  vars: {
2552
2720
  feishuAppID: ctx.app.feishuAppID,
2553
2721
  feishuAppSecret: ctx.app.feishuAppSecret,
2722
+ teamChatID: ctx.app.teamChatID,
2554
2723
  innerAPIKey: ctx.app.innerAPIKey,
2555
2724
  gatewayToken: ctx.app.gatewayToken,
2556
2725
  baseURL: ctx.app.baseURL,
@@ -2593,7 +2762,7 @@ async function runDoctor(rawCtx, opts) {
2593
2762
  //#region src/help.ts
2594
2763
  const BIN = "mclaw-diagnose";
2595
2764
  function versionBanner() {
2596
- return `v0.1.1-alpha.30`;
2765
+ return `v0.1.1-alpha.31`;
2597
2766
  }
2598
2767
  const COMMANDS = [
2599
2768
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.1-alpha.30",
3
+ "version": "0.1.1-alpha.31",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {