@sunnoy/wecom 1.1.0 → 1.1.2
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/index.js +175 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -80,6 +80,8 @@ function checkCommandAllowlist(message, config) {
|
|
|
80
80
|
// Runtime state (module-level singleton)
|
|
81
81
|
let _runtime = null;
|
|
82
82
|
let _openclawConfig = null;
|
|
83
|
+
const ensuredDynamicAgentIds = new Set();
|
|
84
|
+
let ensureDynamicAgentWriteQueue = Promise.resolve();
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Set the plugin runtime (called during plugin registration)
|
|
@@ -95,6 +97,82 @@ function getRuntime() {
|
|
|
95
97
|
return _runtime;
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
function upsertAgentIdOnlyEntry(cfg, agentId) {
|
|
101
|
+
const normalizedId = String(agentId || "").trim().toLowerCase();
|
|
102
|
+
if (!normalizedId) return false;
|
|
103
|
+
|
|
104
|
+
if (!cfg.agents || typeof cfg.agents !== "object") {
|
|
105
|
+
cfg.agents = {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const currentList = Array.isArray(cfg.agents.list) ? cfg.agents.list : [];
|
|
109
|
+
const existingIds = new Set(
|
|
110
|
+
currentList
|
|
111
|
+
.map((entry) => (entry && typeof entry.id === "string" ? entry.id.trim().toLowerCase() : ""))
|
|
112
|
+
.filter(Boolean),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
let changed = false;
|
|
116
|
+
const nextList = [...currentList];
|
|
117
|
+
|
|
118
|
+
// Keep "main" as the explicit default when creating agents.list for the first time.
|
|
119
|
+
if (nextList.length === 0) {
|
|
120
|
+
nextList.push({ id: "main" });
|
|
121
|
+
existingIds.add("main");
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!existingIds.has(normalizedId)) {
|
|
126
|
+
nextList.push({ id: normalizedId });
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (changed) {
|
|
131
|
+
cfg.agents.list = nextList;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return changed;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function ensureDynamicAgentListed(agentId) {
|
|
138
|
+
const normalizedId = String(agentId || "").trim().toLowerCase();
|
|
139
|
+
if (!normalizedId) return;
|
|
140
|
+
if (ensuredDynamicAgentIds.has(normalizedId)) return;
|
|
141
|
+
|
|
142
|
+
const runtime = getRuntime();
|
|
143
|
+
const configRuntime = runtime?.config;
|
|
144
|
+
if (!configRuntime?.loadConfig || !configRuntime?.writeConfigFile) return;
|
|
145
|
+
|
|
146
|
+
ensureDynamicAgentWriteQueue = ensureDynamicAgentWriteQueue
|
|
147
|
+
.then(async () => {
|
|
148
|
+
if (ensuredDynamicAgentIds.has(normalizedId)) return;
|
|
149
|
+
|
|
150
|
+
const latestConfig = configRuntime.loadConfig();
|
|
151
|
+
if (!latestConfig || typeof latestConfig !== "object") return;
|
|
152
|
+
|
|
153
|
+
const changed = upsertAgentIdOnlyEntry(latestConfig, normalizedId);
|
|
154
|
+
if (changed) {
|
|
155
|
+
await configRuntime.writeConfigFile(latestConfig);
|
|
156
|
+
logger.info("WeCom: dynamic agent added to agents.list", { agentId: normalizedId });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Keep runtime in-memory config aligned to avoid stale reads in this process.
|
|
160
|
+
if (_openclawConfig && typeof _openclawConfig === "object") {
|
|
161
|
+
upsertAgentIdOnlyEntry(_openclawConfig, normalizedId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
ensuredDynamicAgentIds.add(normalizedId);
|
|
165
|
+
})
|
|
166
|
+
.catch((err) => {
|
|
167
|
+
logger.warn("WeCom: failed to sync dynamic agent into agents.list", {
|
|
168
|
+
agentId: normalizedId,
|
|
169
|
+
error: err?.message || String(err),
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await ensureDynamicAgentWriteQueue;
|
|
174
|
+
}
|
|
175
|
+
|
|
98
176
|
// Webhook targets registry (similar to Google Chat)
|
|
99
177
|
const webhookTargets = new Map();
|
|
100
178
|
|
|
@@ -189,6 +267,102 @@ const wecomChannelPlugin = {
|
|
|
189
267
|
blockStreaming: true, // WeCom AI Bot uses stream response format
|
|
190
268
|
},
|
|
191
269
|
reload: { configPrefixes: ["channels.wecom"] },
|
|
270
|
+
configSchema: {
|
|
271
|
+
schema: {
|
|
272
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
273
|
+
"type": "object",
|
|
274
|
+
"additionalProperties": false,
|
|
275
|
+
"properties": {
|
|
276
|
+
"enabled": {
|
|
277
|
+
"type": "boolean",
|
|
278
|
+
"description": "Enable WeCom channel",
|
|
279
|
+
"default": true
|
|
280
|
+
},
|
|
281
|
+
"token": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"description": "WeCom bot token from admin console"
|
|
284
|
+
},
|
|
285
|
+
"encodingAesKey": {
|
|
286
|
+
"type": "string",
|
|
287
|
+
"description": "WeCom message encryption key (43 characters)",
|
|
288
|
+
"minLength": 43,
|
|
289
|
+
"maxLength": 43
|
|
290
|
+
},
|
|
291
|
+
"commands": {
|
|
292
|
+
"type": "object",
|
|
293
|
+
"description": "Command whitelist configuration",
|
|
294
|
+
"additionalProperties": false,
|
|
295
|
+
"properties": {
|
|
296
|
+
"enabled": {
|
|
297
|
+
"type": "boolean",
|
|
298
|
+
"description": "Enable command whitelist filtering",
|
|
299
|
+
"default": true
|
|
300
|
+
},
|
|
301
|
+
"allowlist": {
|
|
302
|
+
"type": "array",
|
|
303
|
+
"description": "Allowed commands (e.g., /new, /status, /help)",
|
|
304
|
+
"items": {
|
|
305
|
+
"type": "string"
|
|
306
|
+
},
|
|
307
|
+
"default": ["/new", "/status", "/help", "/compact"]
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"dynamicAgents": {
|
|
312
|
+
"type": "object",
|
|
313
|
+
"description": "Dynamic agent routing configuration",
|
|
314
|
+
"additionalProperties": false,
|
|
315
|
+
"properties": {
|
|
316
|
+
"enabled": {
|
|
317
|
+
"type": "boolean",
|
|
318
|
+
"description": "Enable per-user/per-group agent isolation",
|
|
319
|
+
"default": true
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"dm": {
|
|
324
|
+
"type": "object",
|
|
325
|
+
"description": "Direct message (private chat) configuration",
|
|
326
|
+
"additionalProperties": false,
|
|
327
|
+
"properties": {
|
|
328
|
+
"createAgentOnFirstMessage": {
|
|
329
|
+
"type": "boolean",
|
|
330
|
+
"description": "Create separate agent for each user",
|
|
331
|
+
"default": true
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"groupChat": {
|
|
336
|
+
"type": "object",
|
|
337
|
+
"description": "Group chat configuration",
|
|
338
|
+
"additionalProperties": false,
|
|
339
|
+
"properties": {
|
|
340
|
+
"enabled": {
|
|
341
|
+
"type": "boolean",
|
|
342
|
+
"description": "Enable group chat support",
|
|
343
|
+
"default": true
|
|
344
|
+
},
|
|
345
|
+
"requireMention": {
|
|
346
|
+
"type": "boolean",
|
|
347
|
+
"description": "Only respond when @mentioned in groups",
|
|
348
|
+
"default": true
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
uiHints: {
|
|
355
|
+
"token": {
|
|
356
|
+
"sensitive": true,
|
|
357
|
+
"label": "Bot Token"
|
|
358
|
+
},
|
|
359
|
+
"encodingAesKey": {
|
|
360
|
+
"sensitive": true,
|
|
361
|
+
"label": "Encoding AES Key",
|
|
362
|
+
"help": "43-character encryption key from WeCom admin console"
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
192
366
|
config: {
|
|
193
367
|
listAccountIds: (cfg) => {
|
|
194
368
|
const wecom = cfg?.channels?.wecom;
|
|
@@ -674,6 +848,7 @@ async function processInboundMessage({ message, streamId, timestamp, nonce, acco
|
|
|
674
848
|
const targetAgentId = dynamicConfig.enabled ? generateAgentId(peerKind, peerId) : null;
|
|
675
849
|
|
|
676
850
|
if (targetAgentId) {
|
|
851
|
+
await ensureDynamicAgentListed(targetAgentId);
|
|
677
852
|
logger.debug("Using dynamic agent", { agentId: targetAgentId, chatType: peerKind, peerId });
|
|
678
853
|
}
|
|
679
854
|
|