@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.
Files changed (2) hide show
  1. package/index.js +175 -0
  2. 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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunnoy/wecom",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Enterprise WeChat AI Bot channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",