@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.30 → 0.1.1-alpha.32
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 +221 -19
- 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
|
-
|
|
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:
|
|
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,
|
|
673
|
+
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) return {
|
|
649
674
|
pass: false,
|
|
650
|
-
message:
|
|
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
|
-
],
|
|
704
|
+
], DEFAULT_FEISHU_APP_SECRET);
|
|
679
705
|
}
|
|
680
706
|
};
|
|
681
|
-
FeishuChannelRule =
|
|
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(
|
|
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(
|
|
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;
|
|
@@ -704,12 +869,17 @@ let GatewayRule = class GatewayRule extends DiagnoseRule {
|
|
|
704
869
|
_GatewayRule = this;
|
|
705
870
|
}
|
|
706
871
|
static DEFAULT_PORT = 18789;
|
|
872
|
+
static DEFAULT_MODE = "local";
|
|
873
|
+
static DEFAULT_BIND = "loopback";
|
|
707
874
|
static DEFAULT_AUTH_MODE = "token";
|
|
708
875
|
static DEFAULT_AUTH_TOKEN = {
|
|
709
876
|
source: "file",
|
|
710
877
|
provider: "miaoda-secret-provider",
|
|
711
878
|
id: "/gateway_auth_token"
|
|
712
879
|
};
|
|
880
|
+
/** Required entries in gateway.trustedProxies. Repair appends any missing
|
|
881
|
+
* entries while preserving caller-added extras (no overwrite). */
|
|
882
|
+
static DEFAULT_TRUSTED_PROXIES = ["::1", "127.0.0.1"];
|
|
713
883
|
validate(ctx) {
|
|
714
884
|
const gateway = ctx.config.gateway;
|
|
715
885
|
if (!gateway || typeof gateway !== "object") return {
|
|
@@ -721,6 +891,14 @@ let GatewayRule = class GatewayRule extends DiagnoseRule {
|
|
|
721
891
|
pass: false,
|
|
722
892
|
message: "gateway.port mismatch: got " + gw.port + ", expected " + _GatewayRule.DEFAULT_PORT
|
|
723
893
|
};
|
|
894
|
+
if (gw.mode !== _GatewayRule.DEFAULT_MODE) return {
|
|
895
|
+
pass: false,
|
|
896
|
+
message: "gateway.mode mismatch: got " + gw.mode + ", expected " + _GatewayRule.DEFAULT_MODE
|
|
897
|
+
};
|
|
898
|
+
if (gw.bind !== _GatewayRule.DEFAULT_BIND) return {
|
|
899
|
+
pass: false,
|
|
900
|
+
message: "gateway.bind mismatch: got " + gw.bind + ", expected " + _GatewayRule.DEFAULT_BIND
|
|
901
|
+
};
|
|
724
902
|
const auth = gw.auth;
|
|
725
903
|
if (!auth || typeof auth !== "object") return {
|
|
726
904
|
pass: false,
|
|
@@ -755,10 +933,22 @@ let GatewayRule = class GatewayRule extends DiagnoseRule {
|
|
|
755
933
|
pass: false,
|
|
756
934
|
message: "gateway.controlUi.dangerouslyDisableDeviceAuth must be true, got " + controlUi.dangerouslyDisableDeviceAuth
|
|
757
935
|
};
|
|
936
|
+
const proxies = gw.trustedProxies;
|
|
937
|
+
if (!Array.isArray(proxies)) return {
|
|
938
|
+
pass: false,
|
|
939
|
+
message: "gateway.trustedProxies missing or not an array"
|
|
940
|
+
};
|
|
941
|
+
const missing = _GatewayRule.DEFAULT_TRUSTED_PROXIES.filter((p) => !proxies.includes(p));
|
|
942
|
+
if (missing.length > 0) return {
|
|
943
|
+
pass: false,
|
|
944
|
+
message: "gateway.trustedProxies missing: " + JSON.stringify(missing)
|
|
945
|
+
};
|
|
758
946
|
return { pass: true };
|
|
759
947
|
}
|
|
760
948
|
repair(ctx) {
|
|
761
949
|
setNestedValue(ctx.config, ["gateway", "port"], _GatewayRule.DEFAULT_PORT);
|
|
950
|
+
setNestedValue(ctx.config, ["gateway", "mode"], _GatewayRule.DEFAULT_MODE);
|
|
951
|
+
setNestedValue(ctx.config, ["gateway", "bind"], _GatewayRule.DEFAULT_BIND);
|
|
762
952
|
setNestedValue(ctx.config, [
|
|
763
953
|
"gateway",
|
|
764
954
|
"auth",
|
|
@@ -774,6 +964,14 @@ let GatewayRule = class GatewayRule extends DiagnoseRule {
|
|
|
774
964
|
"controlUi",
|
|
775
965
|
"dangerouslyDisableDeviceAuth"
|
|
776
966
|
], true);
|
|
967
|
+
const gw = ctx.config.gateway ?? {};
|
|
968
|
+
const current = Array.isArray(gw.trustedProxies) ? gw.trustedProxies.slice() : [];
|
|
969
|
+
const seen = new Set(current.map((v) => String(v)));
|
|
970
|
+
for (const p of _GatewayRule.DEFAULT_TRUSTED_PROXIES) if (!seen.has(p)) {
|
|
971
|
+
current.push(p);
|
|
972
|
+
seen.add(p);
|
|
973
|
+
}
|
|
974
|
+
setNestedValue(ctx.config, ["gateway", "trustedProxies"], current);
|
|
777
975
|
}
|
|
778
976
|
};
|
|
779
977
|
GatewayRule = _GatewayRule = __decorate([Rule({
|
|
@@ -2452,6 +2650,7 @@ function fillApp(src) {
|
|
|
2452
2650
|
return {
|
|
2453
2651
|
feishuAppID: src.feishuAppID ?? "",
|
|
2454
2652
|
feishuAppSecret: src.feishuAppSecret ?? "",
|
|
2653
|
+
teamChatID: typeof src.teamChatID === "string" && src.teamChatID !== "" ? src.teamChatID : void 0,
|
|
2455
2654
|
feishuOpenID: src.feishuOpenID ?? "",
|
|
2456
2655
|
openClawName: src.openClawName ?? "",
|
|
2457
2656
|
gatewayToken: src.gatewayToken ?? "",
|
|
@@ -2497,6 +2696,7 @@ function buildCheckInput(raw, configPathOverride) {
|
|
|
2497
2696
|
vars: {
|
|
2498
2697
|
feishuAppID: ctx.app.feishuAppID,
|
|
2499
2698
|
feishuAppSecret: ctx.app.feishuAppSecret,
|
|
2699
|
+
teamChatID: ctx.app.teamChatID,
|
|
2500
2700
|
innerAPIKey: ctx.app.innerAPIKey,
|
|
2501
2701
|
gatewayToken: ctx.app.gatewayToken,
|
|
2502
2702
|
baseURL: ctx.app.baseURL,
|
|
@@ -2522,6 +2722,7 @@ function buildRepairInput(raw, configPathOverride) {
|
|
|
2522
2722
|
vars: {
|
|
2523
2723
|
feishuAppID: ctx.app.feishuAppID,
|
|
2524
2724
|
feishuAppSecret: ctx.app.feishuAppSecret,
|
|
2725
|
+
teamChatID: ctx.app.teamChatID,
|
|
2525
2726
|
innerAPIKey: ctx.app.innerAPIKey,
|
|
2526
2727
|
gatewayToken: ctx.app.gatewayToken,
|
|
2527
2728
|
baseURL: ctx.app.baseURL,
|
|
@@ -2551,6 +2752,7 @@ function buildResetInput(raw, configPathOverride) {
|
|
|
2551
2752
|
vars: {
|
|
2552
2753
|
feishuAppID: ctx.app.feishuAppID,
|
|
2553
2754
|
feishuAppSecret: ctx.app.feishuAppSecret,
|
|
2755
|
+
teamChatID: ctx.app.teamChatID,
|
|
2554
2756
|
innerAPIKey: ctx.app.innerAPIKey,
|
|
2555
2757
|
gatewayToken: ctx.app.gatewayToken,
|
|
2556
2758
|
baseURL: ctx.app.baseURL,
|
|
@@ -2593,7 +2795,7 @@ async function runDoctor(rawCtx, opts) {
|
|
|
2593
2795
|
//#region src/help.ts
|
|
2594
2796
|
const BIN = "mclaw-diagnose";
|
|
2595
2797
|
function versionBanner() {
|
|
2596
|
-
return `v0.1.1-alpha.
|
|
2798
|
+
return `v0.1.1-alpha.32`;
|
|
2597
2799
|
}
|
|
2598
2800
|
const COMMANDS = [
|
|
2599
2801
|
{
|
package/package.json
CHANGED