@yanhaidao/wecom 2.3.4 → 2.3.9
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/README.md +213 -339
- package/assets/03.bot.page.png +0 -0
- package/changelog/v2.3.9.md +22 -0
- package/compat-single-account.md +32 -2
- package/index.ts +5 -5
- package/package.json +8 -7
- package/src/agent/api-client.upload.test.ts +1 -2
- package/src/agent/handler.ts +82 -9
- package/src/agent/index.ts +1 -1
- package/src/app/account-runtime.ts +245 -0
- package/src/app/bootstrap.ts +29 -0
- package/src/app/index.ts +31 -0
- package/src/capability/agent/delivery-service.ts +79 -0
- package/src/capability/agent/fallback-policy.ts +13 -0
- package/src/capability/agent/index.ts +3 -0
- package/src/capability/agent/ingress-service.ts +38 -0
- package/src/capability/bot/dispatch-config.ts +47 -0
- package/src/capability/bot/fallback-delivery.ts +178 -0
- package/src/capability/bot/index.ts +1 -0
- package/src/capability/bot/local-path-delivery.ts +215 -0
- package/src/capability/bot/service.ts +56 -0
- package/src/capability/bot/stream-delivery.ts +379 -0
- package/src/capability/bot/stream-finalizer.ts +120 -0
- package/src/capability/bot/stream-orchestrator.ts +352 -0
- package/src/capability/bot/types.ts +8 -0
- package/src/capability/index.ts +2 -0
- package/src/channel.lifecycle.test.ts +9 -6
- package/src/channel.meta.test.ts +12 -0
- package/src/channel.ts +48 -21
- package/src/config/accounts.ts +223 -283
- package/src/config/derived-paths.test.ts +111 -0
- package/src/config/derived-paths.ts +41 -0
- package/src/config/index.ts +10 -12
- package/src/config/runtime-config.ts +46 -0
- package/src/config/schema.ts +59 -102
- package/src/domain/models.ts +7 -0
- package/src/domain/policies.ts +36 -0
- package/src/dynamic-agent.ts +6 -0
- package/src/gateway-monitor.ts +43 -93
- package/src/http.ts +23 -2
- package/src/monitor/limits.ts +7 -0
- package/src/monitor/state.ts +28 -508
- package/src/monitor.active.test.ts +3 -3
- package/src/monitor.integration.test.ts +0 -1
- package/src/monitor.ts +64 -2603
- package/src/monitor.webhook.test.ts +127 -42
- package/src/observability/audit-log.ts +48 -0
- package/src/observability/legacy-operational-event-store.ts +36 -0
- package/src/observability/raw-envelope-log.ts +28 -0
- package/src/observability/status-registry.ts +13 -0
- package/src/observability/transport-session-view.ts +14 -0
- package/src/onboarding.test.ts +219 -0
- package/src/onboarding.ts +88 -71
- package/src/outbound.test.ts +5 -5
- package/src/outbound.ts +18 -66
- package/src/runtime/dispatcher.ts +52 -0
- package/src/runtime/index.ts +4 -0
- package/src/runtime/outbound-intent.ts +4 -0
- package/src/runtime/reply-orchestrator.test.ts +38 -0
- package/src/runtime/reply-orchestrator.ts +55 -0
- package/src/runtime/routing-bridge.ts +19 -0
- package/src/runtime/session-manager.ts +76 -0
- package/src/runtime.ts +7 -14
- package/src/shared/command-auth.ts +1 -17
- package/src/shared/media-service.ts +36 -0
- package/src/shared/media-types.ts +5 -0
- package/src/store/active-reply-store.ts +42 -0
- package/src/store/interfaces.ts +11 -0
- package/src/store/memory-store.ts +43 -0
- package/src/store/stream-batch-store.ts +350 -0
- package/src/target.ts +28 -0
- package/src/transport/agent-api/client.ts +44 -0
- package/src/transport/agent-api/core.ts +367 -0
- package/src/transport/agent-api/delivery.ts +41 -0
- package/src/transport/agent-api/media-upload.ts +11 -0
- package/src/transport/agent-api/reply.ts +39 -0
- package/src/transport/agent-callback/http-handler.ts +47 -0
- package/src/transport/agent-callback/inbound.ts +5 -0
- package/src/transport/agent-callback/reply.ts +13 -0
- package/src/transport/agent-callback/request-handler.ts +244 -0
- package/src/transport/agent-callback/session.ts +23 -0
- package/src/transport/bot-webhook/active-reply.ts +36 -0
- package/src/transport/bot-webhook/http-handler.ts +48 -0
- package/src/transport/bot-webhook/inbound-normalizer.ts +371 -0
- package/src/transport/bot-webhook/inbound.ts +5 -0
- package/src/transport/bot-webhook/message-shape.ts +89 -0
- package/src/transport/bot-webhook/protocol.ts +148 -0
- package/src/transport/bot-webhook/reply.ts +15 -0
- package/src/transport/bot-webhook/request-handler.ts +394 -0
- package/src/transport/bot-webhook/session.ts +23 -0
- package/src/transport/bot-ws/inbound.ts +109 -0
- package/src/transport/bot-ws/reply.ts +48 -0
- package/src/transport/bot-ws/sdk-adapter.ts +180 -0
- package/src/transport/bot-ws/session.ts +28 -0
- package/src/transport/http/common.ts +109 -0
- package/src/transport/http/registry.ts +92 -0
- package/src/transport/http/request-handler.ts +84 -0
- package/src/transport/index.ts +14 -0
- package/src/types/account.ts +56 -91
- package/src/types/config.ts +59 -112
- package/src/types/constants.ts +20 -35
- package/src/types/events.ts +21 -0
- package/src/types/index.ts +14 -38
- package/src/types/legacy-stream.ts +50 -0
- package/src/types/runtime-context.ts +28 -0
- package/src/types/runtime.ts +161 -0
- package/src/agent/api-client.ts +0 -383
- package/src/monitor/types.ts +0 -136
package/src/config/accounts.ts
CHANGED
|
@@ -1,334 +1,274 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WeCom 账号解析与模式检测
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
6
3
|
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
ResolvedAgentAccount,
|
|
5
|
+
ResolvedBotAccount,
|
|
6
|
+
ResolvedMode,
|
|
7
|
+
ResolvedWecomAccount,
|
|
8
|
+
ResolvedWecomAccounts,
|
|
9
|
+
WecomAccountConfig,
|
|
10
|
+
WecomAgentConfig,
|
|
11
|
+
WecomBotConfig,
|
|
12
|
+
WecomConfig,
|
|
13
|
+
WecomNetworkConfig,
|
|
17
14
|
} from "../types/index.js";
|
|
18
15
|
|
|
19
16
|
export const DEFAULT_ACCOUNT_ID = "default";
|
|
20
17
|
|
|
21
18
|
export type WecomAccountConflict = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
type: "duplicate_bot_id" | "duplicate_agent_id";
|
|
20
|
+
accountId: string;
|
|
21
|
+
ownerAccountId: string;
|
|
22
|
+
message: string;
|
|
26
23
|
};
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!config || config.enabled === false) return "disabled";
|
|
33
|
-
|
|
34
|
-
const accounts = config.accounts;
|
|
35
|
-
if (accounts && typeof accounts === "object") {
|
|
36
|
-
const enabledEntries = Object.values(accounts).filter(
|
|
37
|
-
(entry) => entry && entry.enabled !== false,
|
|
38
|
-
);
|
|
39
|
-
if (enabledEntries.length > 0) return "matrix";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return "legacy";
|
|
25
|
+
function toNumber(value: number | string | undefined): number | undefined {
|
|
26
|
+
if (value == null) return undefined;
|
|
27
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
28
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
43
29
|
}
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
31
|
+
function resolveBotAccount(
|
|
32
|
+
accountId: string,
|
|
33
|
+
config: WecomBotConfig,
|
|
34
|
+
network?: WecomNetworkConfig,
|
|
35
|
+
): ResolvedBotAccount {
|
|
36
|
+
const primaryTransport = config.primaryTransport ?? (config.ws ? "ws" : "webhook");
|
|
37
|
+
const wsConfigured = Boolean(config.ws?.botId && config.ws?.secret);
|
|
38
|
+
const webhookConfigured = Boolean(config.webhook?.token && config.webhook?.encodingAESKey);
|
|
39
|
+
const configured = primaryTransport === "ws" ? wsConfigured : webhookConfigured;
|
|
40
|
+
return {
|
|
41
|
+
accountId,
|
|
42
|
+
configured,
|
|
43
|
+
primaryTransport,
|
|
44
|
+
wsConfigured,
|
|
45
|
+
webhookConfigured,
|
|
46
|
+
config,
|
|
47
|
+
network,
|
|
48
|
+
ws: config.ws
|
|
49
|
+
? {
|
|
50
|
+
botId: config.ws.botId,
|
|
51
|
+
secret: config.ws.secret,
|
|
52
|
+
}
|
|
53
|
+
: undefined,
|
|
54
|
+
webhook: config.webhook
|
|
55
|
+
? {
|
|
56
|
+
token: config.webhook.token,
|
|
57
|
+
encodingAESKey: config.webhook.encodingAESKey,
|
|
58
|
+
receiveId: config.webhook.receiveId?.trim() ?? "",
|
|
59
|
+
}
|
|
60
|
+
: undefined,
|
|
61
|
+
token: config.webhook?.token ?? "",
|
|
62
|
+
encodingAESKey: config.webhook?.encodingAESKey ?? "",
|
|
63
|
+
receiveId: config.webhook?.receiveId?.trim() ?? "",
|
|
64
|
+
botId: config.ws?.botId ?? "",
|
|
65
|
+
secret: config.ws?.secret ?? "",
|
|
66
|
+
};
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
encodingAESKey: config.encodingAESKey,
|
|
83
|
-
config,
|
|
84
|
-
network,
|
|
85
|
-
};
|
|
69
|
+
function resolveAgentAccount(
|
|
70
|
+
accountId: string,
|
|
71
|
+
config: WecomAgentConfig,
|
|
72
|
+
network?: WecomNetworkConfig,
|
|
73
|
+
): ResolvedAgentAccount {
|
|
74
|
+
const agentId = toNumber(config.agentId);
|
|
75
|
+
const callbackConfigured = Boolean(config.token && config.encodingAESKey);
|
|
76
|
+
const apiConfigured = Boolean(config.corpId && config.corpSecret && agentId);
|
|
77
|
+
return {
|
|
78
|
+
accountId,
|
|
79
|
+
configured: callbackConfigured || apiConfigured,
|
|
80
|
+
callbackConfigured,
|
|
81
|
+
apiConfigured,
|
|
82
|
+
corpId: config.corpId,
|
|
83
|
+
corpSecret: config.corpSecret,
|
|
84
|
+
agentId,
|
|
85
|
+
token: config.token,
|
|
86
|
+
encodingAESKey: config.encodingAESKey,
|
|
87
|
+
config,
|
|
88
|
+
network,
|
|
89
|
+
};
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
function toResolvedAccount(params: {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
accountId: string;
|
|
94
|
+
enabled: boolean;
|
|
95
|
+
name?: string;
|
|
96
|
+
config: WecomAccountConfig;
|
|
97
|
+
network?: WecomNetworkConfig;
|
|
94
98
|
}): ResolvedWecomAccount {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
99
|
+
const bot = params.config.bot
|
|
100
|
+
? resolveBotAccount(params.accountId, params.config.bot, params.network)
|
|
101
|
+
: undefined;
|
|
102
|
+
const agent = params.config.agent
|
|
103
|
+
? resolveAgentAccount(params.accountId, params.config.agent, params.network)
|
|
104
|
+
: undefined;
|
|
105
|
+
return {
|
|
106
|
+
accountId: params.accountId,
|
|
107
|
+
name: params.name,
|
|
108
|
+
enabled: params.enabled,
|
|
109
|
+
configured: Boolean(bot?.configured || agent?.configured),
|
|
110
|
+
config: params.config,
|
|
111
|
+
bot,
|
|
112
|
+
agent,
|
|
113
|
+
};
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const config: WecomAccountConfig = {
|
|
123
|
-
enabled: entry.enabled,
|
|
124
|
-
name: entry.name,
|
|
125
|
-
bot: entry.bot,
|
|
126
|
-
agent: entry.agent,
|
|
127
|
-
};
|
|
128
|
-
resolved[accountId] = toResolvedAccount({
|
|
129
|
-
accountId,
|
|
130
|
-
enabled,
|
|
131
|
-
name: entry.name,
|
|
132
|
-
config,
|
|
133
|
-
network: wecom.network,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return resolved;
|
|
116
|
+
export function detectMode(config: WecomConfig | undefined): ResolvedMode {
|
|
117
|
+
if (!config || config.enabled === false) return "disabled";
|
|
118
|
+
if (config.accounts && Object.keys(config.accounts).length > 0) {
|
|
119
|
+
return "matrix";
|
|
120
|
+
}
|
|
121
|
+
if (config.bot || config.agent) {
|
|
122
|
+
return "legacy";
|
|
123
|
+
}
|
|
124
|
+
return "disabled";
|
|
137
125
|
}
|
|
138
126
|
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
127
|
+
function resolveMatrixAccounts(wecom: WecomConfig): Record<string, ResolvedWecomAccount> {
|
|
128
|
+
const resolved: Record<string, ResolvedWecomAccount> = {};
|
|
129
|
+
for (const [rawId, entry] of Object.entries(wecom.accounts ?? {})) {
|
|
130
|
+
const accountId = rawId.trim();
|
|
131
|
+
if (!accountId || !entry) continue;
|
|
132
|
+
resolved[accountId] = toResolvedAccount({
|
|
133
|
+
accountId,
|
|
134
|
+
enabled: wecom.enabled !== false && entry.enabled !== false,
|
|
135
|
+
name: entry.name,
|
|
136
|
+
config: entry,
|
|
137
|
+
network: wecom.network,
|
|
149
138
|
});
|
|
150
|
-
|
|
139
|
+
}
|
|
140
|
+
return resolved;
|
|
151
141
|
}
|
|
152
142
|
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function formatBotAibotidConflict(params: { accountId: string; ownerAccountId: string }): WecomAccountConflict {
|
|
169
|
-
return {
|
|
170
|
-
type: "duplicate_bot_aibotid",
|
|
171
|
-
accountId: params.accountId,
|
|
172
|
-
ownerAccountId: params.ownerAccountId,
|
|
173
|
-
message:
|
|
174
|
-
`Duplicate WeCom bot aibotid: account "${params.accountId}" shares aibotid with account "${params.ownerAccountId}". ` +
|
|
175
|
-
"Keep one owner account per aibotid.",
|
|
176
|
-
};
|
|
143
|
+
function resolveLegacyAccounts(wecom: WecomConfig): Record<string, ResolvedWecomAccount> {
|
|
144
|
+
const config: WecomAccountConfig = {
|
|
145
|
+
bot: wecom.bot,
|
|
146
|
+
agent: wecom.agent,
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
[DEFAULT_ACCOUNT_ID]: toResolvedAccount({
|
|
150
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
151
|
+
enabled: wecom.enabled !== false,
|
|
152
|
+
config,
|
|
153
|
+
network: wecom.network,
|
|
154
|
+
}),
|
|
155
|
+
};
|
|
177
156
|
}
|
|
178
157
|
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
type: "duplicate_agent_id",
|
|
182
|
-
accountId: params.accountId,
|
|
183
|
-
ownerAccountId: params.ownerAccountId,
|
|
184
|
-
message:
|
|
185
|
-
`Duplicate WeCom agent identity: account "${params.accountId}" shares corpId/agentId (${params.corpId}/${params.agentId}) with account "${params.ownerAccountId}". ` +
|
|
186
|
-
"Keep one owner account per corpId/agentId pair.",
|
|
187
|
-
};
|
|
158
|
+
function normalizeKey(value: string): string {
|
|
159
|
+
return value.trim().toLowerCase();
|
|
188
160
|
}
|
|
189
161
|
|
|
190
162
|
function collectWecomAccountConflicts(cfg: OpenClawConfig): Map<string, WecomAccountConflict> {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const botAibotid = bot?.config.aibotid?.trim();
|
|
218
|
-
if (botAibotid) {
|
|
219
|
-
const key = normalizeDuplicateKey(botAibotid);
|
|
220
|
-
const owner = botAibotidOwners.get(key);
|
|
221
|
-
if (owner && owner !== accountId) {
|
|
222
|
-
conflicts.set(accountId, formatBotAibotidConflict({ accountId, ownerAccountId: owner }));
|
|
223
|
-
} else {
|
|
224
|
-
botAibotidOwners.set(key, accountId);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
163
|
+
const resolved = resolveWecomAccounts(cfg);
|
|
164
|
+
const conflicts = new Map<string, WecomAccountConflict>();
|
|
165
|
+
const botOwners = new Map<string, string>();
|
|
166
|
+
const agentOwners = new Map<string, string>();
|
|
167
|
+
|
|
168
|
+
for (const accountId of Object.keys(resolved.accounts).sort((a, b) => a.localeCompare(b))) {
|
|
169
|
+
const account = resolved.accounts[accountId];
|
|
170
|
+
if (!account || account.enabled === false) continue;
|
|
171
|
+
|
|
172
|
+
const botId = account.bot?.botId?.trim();
|
|
173
|
+
if (botId) {
|
|
174
|
+
const key = normalizeKey(botId);
|
|
175
|
+
const owner = botOwners.get(key);
|
|
176
|
+
if (owner && owner !== accountId) {
|
|
177
|
+
conflicts.set(accountId, {
|
|
178
|
+
type: "duplicate_bot_id",
|
|
179
|
+
accountId,
|
|
180
|
+
ownerAccountId: owner,
|
|
181
|
+
message:
|
|
182
|
+
`Duplicate WeCom botId: account "${accountId}" shares botId with account "${owner}". ` +
|
|
183
|
+
"Keep one owner account per botId.",
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
botOwners.set(key, accountId);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
227
189
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
190
|
+
const corpId = account.agent?.corpId?.trim();
|
|
191
|
+
const agentId = account.agent?.agentId;
|
|
192
|
+
if (corpId && typeof agentId === "number") {
|
|
193
|
+
const key = `${normalizeKey(corpId)}:${agentId}`;
|
|
194
|
+
const owner = agentOwners.get(key);
|
|
195
|
+
if (owner && owner !== accountId) {
|
|
196
|
+
conflicts.set(accountId, {
|
|
197
|
+
type: "duplicate_agent_id",
|
|
198
|
+
accountId,
|
|
199
|
+
ownerAccountId: owner,
|
|
200
|
+
message:
|
|
201
|
+
`Duplicate WeCom agent identity: account "${accountId}" shares corpId/agentId (${corpId}/${agentId}) with account "${owner}". ` +
|
|
202
|
+
"Keep one owner account per corpId/agentId pair.",
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
agentOwners.set(key, accountId);
|
|
206
|
+
}
|
|
239
207
|
}
|
|
208
|
+
}
|
|
240
209
|
|
|
241
|
-
|
|
210
|
+
return conflicts;
|
|
242
211
|
}
|
|
243
212
|
|
|
244
213
|
export function resolveWecomAccountConflict(params: {
|
|
245
|
-
|
|
246
|
-
|
|
214
|
+
cfg: OpenClawConfig;
|
|
215
|
+
accountId: string;
|
|
247
216
|
}): WecomAccountConflict | undefined {
|
|
248
|
-
|
|
217
|
+
return collectWecomAccountConflicts(params.cfg).get(params.accountId);
|
|
249
218
|
}
|
|
250
219
|
|
|
251
220
|
export function listWecomAccountIds(cfg: OpenClawConfig): string[] {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
221
|
+
const wecom = cfg.channels?.wecom as WecomConfig | undefined;
|
|
222
|
+
const mode = detectMode(wecom);
|
|
223
|
+
if (mode === "matrix") {
|
|
224
|
+
return Object.keys(wecom?.accounts ?? {})
|
|
225
|
+
.map((value) => value.trim())
|
|
226
|
+
.filter(Boolean)
|
|
227
|
+
.sort((a, b) => a.localeCompare(b));
|
|
228
|
+
}
|
|
229
|
+
if (mode === "legacy") {
|
|
261
230
|
return [DEFAULT_ACCOUNT_ID];
|
|
231
|
+
}
|
|
232
|
+
return [];
|
|
262
233
|
}
|
|
263
234
|
|
|
264
235
|
export function resolveDefaultWecomAccountId(cfg: OpenClawConfig): string {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
236
|
+
const wecom = cfg.channels?.wecom as WecomConfig | undefined;
|
|
237
|
+
const ids = listWecomAccountIds(cfg);
|
|
238
|
+
if (wecom?.defaultAccount && ids.includes(wecom.defaultAccount)) {
|
|
239
|
+
return wecom.defaultAccount;
|
|
240
|
+
}
|
|
241
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
270
242
|
}
|
|
271
243
|
|
|
272
|
-
export function resolveWecomAccount(params: {
|
|
273
|
-
cfg: OpenClawConfig;
|
|
274
|
-
accountId?: string | null;
|
|
275
|
-
}): ResolvedWecomAccount {
|
|
276
|
-
const resolved = resolveWecomAccounts(params.cfg);
|
|
277
|
-
const fallbackId = resolved.defaultAccountId;
|
|
278
|
-
const requestedId = params.accountId?.trim();
|
|
279
|
-
if (requestedId) {
|
|
280
|
-
return (
|
|
281
|
-
resolved.accounts[requestedId] ??
|
|
282
|
-
toResolvedAccount({
|
|
283
|
-
accountId: requestedId,
|
|
284
|
-
enabled: false,
|
|
285
|
-
config: {},
|
|
286
|
-
})
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
return (
|
|
290
|
-
resolved.accounts[fallbackId] ??
|
|
291
|
-
resolved.accounts[DEFAULT_ACCOUNT_ID] ??
|
|
292
|
-
toResolvedAccount({
|
|
293
|
-
accountId: fallbackId,
|
|
294
|
-
enabled: false,
|
|
295
|
-
config: {},
|
|
296
|
-
})
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* 解析 WeCom 账号 (双模式)
|
|
302
|
-
*/
|
|
303
244
|
export function resolveWecomAccounts(cfg: OpenClawConfig): ResolvedWecomAccounts {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const defaultAccountId = resolveDefaultWecomAccountId(cfg);
|
|
317
|
-
const defaultAccount = accounts[defaultAccountId] ?? accounts[DEFAULT_ACCOUNT_ID];
|
|
245
|
+
const wecom = (cfg.channels?.wecom as WecomConfig | undefined) ?? {};
|
|
246
|
+
const mode = detectMode(wecom);
|
|
247
|
+
const accounts = mode === "matrix" ? resolveMatrixAccounts(wecom) : mode === "legacy" ? resolveLegacyAccounts(wecom) : {};
|
|
248
|
+
const defaultAccountId = resolveDefaultWecomAccountId(cfg);
|
|
249
|
+
return {
|
|
250
|
+
mode,
|
|
251
|
+
defaultAccountId,
|
|
252
|
+
accounts,
|
|
253
|
+
bot: accounts[defaultAccountId]?.bot,
|
|
254
|
+
agent: accounts[defaultAccountId]?.agent,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
318
257
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
258
|
+
export function resolveWecomAccount(params: {
|
|
259
|
+
cfg: OpenClawConfig;
|
|
260
|
+
accountId?: string | null;
|
|
261
|
+
}): ResolvedWecomAccount {
|
|
262
|
+
const resolved = resolveWecomAccounts(params.cfg);
|
|
263
|
+
const accountId = params.accountId?.trim() || resolved.defaultAccountId;
|
|
264
|
+
const account = resolved.accounts[accountId];
|
|
265
|
+
if (!account) {
|
|
266
|
+
throw new Error(`WeCom account "${accountId}" not found.`);
|
|
267
|
+
}
|
|
268
|
+
return account;
|
|
326
269
|
}
|
|
327
270
|
|
|
328
|
-
/**
|
|
329
|
-
* 检查是否有任何模式启用
|
|
330
|
-
*/
|
|
331
271
|
export function isWecomEnabled(cfg: OpenClawConfig): boolean {
|
|
332
|
-
|
|
333
|
-
|
|
272
|
+
const resolved = resolveWecomAccounts(cfg);
|
|
273
|
+
return Object.values(resolved.accounts).some((account) => account.enabled && account.configured);
|
|
334
274
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
|
|
4
|
+
|
|
5
|
+
import { resolveDerivedPathSummary } from "./derived-paths.js";
|
|
6
|
+
import {
|
|
7
|
+
hasMatrixExplicitRoutesRegistered,
|
|
8
|
+
registerAgentWebhookTarget,
|
|
9
|
+
registerWecomWebhookTarget,
|
|
10
|
+
} from "../transport/http/registry.js";
|
|
11
|
+
import type { ResolvedAgentAccount, ResolvedBotAccount } from "../types/index.js";
|
|
12
|
+
|
|
13
|
+
function createBotAccount(accountId: string): ResolvedBotAccount {
|
|
14
|
+
return {
|
|
15
|
+
accountId,
|
|
16
|
+
configured: true,
|
|
17
|
+
primaryTransport: "webhook",
|
|
18
|
+
wsConfigured: false,
|
|
19
|
+
webhookConfigured: true,
|
|
20
|
+
config: {} as ResolvedBotAccount["config"],
|
|
21
|
+
token: "token",
|
|
22
|
+
encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
|
|
23
|
+
receiveId: "",
|
|
24
|
+
botId: "",
|
|
25
|
+
secret: "",
|
|
26
|
+
webhook: {
|
|
27
|
+
token: "token",
|
|
28
|
+
encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
|
|
29
|
+
receiveId: "",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createAgentAccount(accountId: string): ResolvedAgentAccount {
|
|
35
|
+
return {
|
|
36
|
+
accountId,
|
|
37
|
+
configured: true,
|
|
38
|
+
callbackConfigured: true,
|
|
39
|
+
apiConfigured: true,
|
|
40
|
+
corpId: `corp-${accountId}`,
|
|
41
|
+
corpSecret: `secret-${accountId}`,
|
|
42
|
+
agentId: 1001,
|
|
43
|
+
token: "token",
|
|
44
|
+
encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
|
|
45
|
+
config: {} as ResolvedAgentAccount["config"],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const emptyConfig = {} as OpenClawConfig;
|
|
50
|
+
const emptyCore = {} as PluginRuntime;
|
|
51
|
+
|
|
52
|
+
describe("resolveDerivedPathSummary", () => {
|
|
53
|
+
it("registers scoped aliases first for the default account", () => {
|
|
54
|
+
expect(resolveDerivedPathSummary("default")).toEqual({
|
|
55
|
+
botWebhook: [
|
|
56
|
+
"/plugins/wecom/bot/default",
|
|
57
|
+
"/wecom/bot/default",
|
|
58
|
+
"/plugins/wecom/bot",
|
|
59
|
+
"/wecom",
|
|
60
|
+
"/wecom/bot",
|
|
61
|
+
],
|
|
62
|
+
agentCallback: [
|
|
63
|
+
"/plugins/wecom/agent/default",
|
|
64
|
+
"/wecom/agent/default",
|
|
65
|
+
"/plugins/wecom/agent",
|
|
66
|
+
"/wecom/agent",
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("hasMatrixExplicitRoutesRegistered", () => {
|
|
73
|
+
it("ignores default-account scoped aliases", () => {
|
|
74
|
+
const unregisterBot = registerWecomWebhookTarget({
|
|
75
|
+
account: createBotAccount("default"),
|
|
76
|
+
config: emptyConfig,
|
|
77
|
+
runtime: {},
|
|
78
|
+
core: emptyCore,
|
|
79
|
+
path: "/plugins/wecom/bot/default",
|
|
80
|
+
});
|
|
81
|
+
const unregisterAgent = registerAgentWebhookTarget({
|
|
82
|
+
agent: createAgentAccount("default"),
|
|
83
|
+
config: emptyConfig,
|
|
84
|
+
runtimeEnv: {},
|
|
85
|
+
path: "/plugins/wecom/agent/default",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
expect(hasMatrixExplicitRoutesRegistered()).toBe(false);
|
|
90
|
+
} finally {
|
|
91
|
+
unregisterAgent();
|
|
92
|
+
unregisterBot();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("detects non-default explicit account routes", () => {
|
|
97
|
+
const unregister = registerWecomWebhookTarget({
|
|
98
|
+
account: createBotAccount("acct-a"),
|
|
99
|
+
config: emptyConfig,
|
|
100
|
+
runtime: {},
|
|
101
|
+
core: emptyCore,
|
|
102
|
+
path: "/plugins/wecom/bot/acct-a",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
expect(hasMatrixExplicitRoutesRegistered()).toBe(true);
|
|
107
|
+
} finally {
|
|
108
|
+
unregister();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|