aicq-chat-plugin 3.5.1 → 3.5.3

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 CHANGED
@@ -65,6 +65,7 @@ async function ensureInitialized() {
65
65
  runtime.dataDir = DATA_DIR;
66
66
  runtime.serverUrl = SERVER_URL;
67
67
  runtime.handleGateway = handleGatewayMethod;
68
+ runtime.ensureInitialized = ensureInitialized;
68
69
 
69
70
  // Periodic cleanup
70
71
  setInterval(() => _db.cleanup(), 3600000);
@@ -2,7 +2,7 @@
2
2
  "kind": "channel",
3
3
  "id": "aicq-chat",
4
4
  "name": "AICQ Encrypted Chat",
5
- "version": "3.5.1",
5
+ "version": "3.5.3",
6
6
  "description": "End-to-end encrypted chat channel via AICQ protocol — in-process Channel plugin using OpenClaw Channel SDK",
7
7
  "entry": "index.js",
8
8
  "activation": {
@@ -49,6 +49,14 @@
49
49
  "open",
50
50
  "disabled"
51
51
  ]
52
+ },
53
+ "allowFrom": {
54
+ "type": "array",
55
+ "items": {
56
+ "type": "string"
57
+ },
58
+ "default": [],
59
+ "description": "允许发消息的好友 ID 列表(dmPolicy 为 allowlist 时生效)"
52
60
  }
53
61
  },
54
62
  "required": [
@@ -71,6 +79,10 @@
71
79
  "dmPolicy": {
72
80
  "label": "DM Policy",
73
81
  "help": "Who can send direct messages: allowlist (friends only), open, or disabled"
82
+ },
83
+ "allowFrom": {
84
+ "label": "Allow From",
85
+ "help": "Friend IDs allowed to send DMs (used when dmPolicy is allowlist)"
74
86
  }
75
87
  }
76
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicq-chat-plugin",
3
- "version": "3.5.1",
3
+ "version": "3.5.3",
4
4
  "description": "AICQ End-to-end Encrypted Chat Channel Plugin for OpenClaw — In-process Channel SDK architecture with friend management, group chat, file transfer, and AI agent communication",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/setup-entry.js CHANGED
File without changes
package/src/channel.js CHANGED
@@ -35,18 +35,29 @@ export const runtime = {
35
35
  // ── Template variable resolver ───────────────────────────────────────
36
36
  // OpenClaw stores accountId as-is (e.g. "{{agent.id}}") in config.
37
37
  // Plugins must resolve template variables at runtime.
38
+ //
39
+ // The default agent ID in OpenClaw is "main" (DEFAULT_AGENT_ID).
40
+ // When cfg.agents.list is empty/undefined (no explicit agent config),
41
+ // the implicit default agent "main" is used.
42
+
43
+ const OPENCLAW_DEFAULT_AGENT_ID = "main";
38
44
 
39
45
  function resolveTemplateVar(cfg, value) {
40
46
  if (typeof value !== "string") return value;
41
47
  const match = value.match(/^\{\{(\w[\w.]*)\}\}$/);
42
48
  if (!match) return value;
43
49
 
44
- const path = match[1]; // e.g. "agent.id"
45
- if (path === "agent.id") {
46
- const agents = cfg.agents?.list || [];
47
- if (agents.length > 0) return agents[0].id;
48
- // Fallback: use the default agent ID from config
49
- return cfg.agents?.defaultId || "default";
50
+ const tmplPath = match[1]; // e.g. "agent.id"
51
+ if (tmplPath === "agent.id") {
52
+ // Strategy: look for explicit agents in config first
53
+ const agents = cfg.agents?.list;
54
+ if (Array.isArray(agents) && agents.length > 0) {
55
+ // Use the default=true agent, or the first one
56
+ const defaultAgent = agents.find((a) => a.default) || agents[0];
57
+ if (defaultAgent?.id) return defaultAgent.id;
58
+ }
59
+ // Fallback: OpenClaw's implicit default agent ID
60
+ return OPENCLAW_DEFAULT_AGENT_ID;
50
61
  }
51
62
 
52
63
  return value; // unknown template — return as-is
@@ -74,13 +85,19 @@ function resolveAccount(cfg, accountId) {
74
85
  // Resolve template variables like {{agent.id}}
75
86
  const resolvedAccountId = resolveTemplateVar(cfg, rawAccountId);
76
87
 
88
+ // Resolve allowFrom entries (may contain {{agent.id}} or friend IDs)
89
+ const rawAllowFrom = section.allowFrom || [];
90
+ const resolvedAllowFrom = Array.isArray(rawAllowFrom)
91
+ ? rawAllowFrom.map((entry) => resolveTemplateVar(cfg, entry))
92
+ : rawAllowFrom;
93
+
77
94
  return {
78
95
  accountId: resolvedAccountId,
79
96
  serverUrl: section.serverUrl || "https://aicq.online",
80
97
  autoAcceptFriends: section.autoAcceptFriends ?? true,
81
98
  enabled: section.enabled ?? true,
82
99
  dmPolicy: section.dmPolicy || "allowlist",
83
- allowFrom: section.allowFrom ?? [],
100
+ allowFrom: resolvedAllowFrom,
84
101
  };
85
102
  }
86
103
 
@@ -219,24 +236,46 @@ _plugin.gateway = {
219
236
  * listening for inbound messages.
220
237
  */
221
238
  async startAccount(ctx) {
222
- const { cfg, accountId, account, setStatus } = ctx;
239
+ const { cfg, accountId, account, setStatus, log } = ctx;
223
240
 
224
- console.log(`[AICQ Channel] startAccount called for ${accountId}`);
241
+ const logger = log || console;
242
+ logger.info?.(`[AICQ Channel] startAccount called for ${accountId}`) || console.log(`[AICQ Channel] startAccount called for ${accountId}`);
225
243
 
226
- // Ensure the runtime (DB, identity, transport) is initialised
227
- if (runtime.handleGateway) {
228
- // Runtime already initialised via registerFull just verify
244
+ // Ensure the runtime (DB, identity, transport) is initialised.
245
+ // The runtime is populated by registerFull() in index.js, but startAccount
246
+ // may be called before any gateway method is invoked, so we must ensure
247
+ // initialization here too.
248
+ if (!runtime._initialized && typeof runtime.ensureInitialized === "function") {
249
+ try {
250
+ await runtime.ensureInitialized();
251
+ logger.info?.("[AICQ Channel] Runtime initialized via startAccount") || console.log("[AICQ Channel] Runtime initialized via startAccount");
252
+ } catch (e) {
253
+ console.error("[AICQ Channel] Runtime initialization failed:", e.message);
254
+ setStatus({
255
+ accountId,
256
+ enabled: true,
257
+ configured: true,
258
+ running: false,
259
+ lastError: `Initialization failed: ${e.message}`,
260
+ });
261
+ return;
262
+ }
229
263
  }
230
264
 
231
- // Resolve the agent ID from OpenClaw config
232
- const agents = cfg.agents?.list || [];
233
- const agentId = agents.length > 0 ? agents[0].id : accountId;
265
+ // Resolve the agent ID: prefer the resolved accountId from
266
+ // resolveAccount (which already handles {{agent.id}}), then
267
+ // fall back to the OpenClaw default agent ID.
268
+ const agents = cfg.agents?.list;
269
+ const agentId = account?.accountId || accountId || OPENCLAW_DEFAULT_AGENT_ID;
234
270
 
235
271
  // Ensure we have an identity in the plugin DB
236
272
  if (runtime.identity) {
237
273
  const existing = runtime.identity.listAgents();
238
274
  if (existing.length === 0) {
239
- runtime.identity.createAgent(agentId, agents[0]?.name || "AICQ Agent");
275
+ const agentName = (Array.isArray(agents) && agents.length > 0 && agents[0]?.name)
276
+ ? agents[0].name
277
+ : "AICQ Agent";
278
+ runtime.identity.createAgent(agentId, agentName);
240
279
  console.log(`[AICQ Channel] Created agent identity: ${agentId}`);
241
280
  }
242
281
  }
@@ -248,9 +287,13 @@ _plugin.gateway = {
248
287
  console.log(`[AICQ Channel] Authenticated as ${agentId}`);
249
288
 
250
289
  // Connect WebSocket for real-time messages
251
- if (typeof runtime.serverClient.connect === "function") {
252
- await runtime.serverClient.connect(agentId);
290
+ // ServerClient.start() does ensureAuth + connectWS
291
+ if (typeof runtime.serverClient.start === "function") {
292
+ await runtime.serverClient.start(agentId);
253
293
  console.log("[AICQ Channel] WebSocket connected");
294
+ } else if (typeof runtime.serverClient.connectWS === "function") {
295
+ runtime.serverClient.connectWS();
296
+ console.log("[AICQ Channel] WebSocket connecting");
254
297
  }
255
298
 
256
299
  // Sync friends and groups from server
@@ -277,7 +320,7 @@ _plugin.gateway = {
277
320
  if (runtime.serverClient && typeof runtime.serverClient.onMessage === "function") {
278
321
  runtime.serverClient.onMessage(async (msg) => {
279
322
  try {
280
- const resolvedAgentId = agents.length > 0 ? agents[0].id : accountId;
323
+ const resolvedAgentId = agentId;
281
324
  const routeResult = await routing.resolveAgentRoute({
282
325
  channelId: "aicq-chat",
283
326
  accountId,
@@ -340,9 +383,13 @@ _plugin.gateway = {
340
383
  const { accountId } = ctx;
341
384
  console.log(`[AICQ Channel] stopAccount called for ${accountId}`);
342
385
 
343
- if (runtime.serverClient && typeof runtime.serverClient.disconnect === "function") {
386
+ if (runtime.serverClient) {
344
387
  try {
345
- runtime.serverClient.disconnect();
388
+ if (typeof runtime.serverClient.stop === "function") {
389
+ runtime.serverClient.stop();
390
+ } else if (typeof runtime.serverClient.disconnect === "function") {
391
+ runtime.serverClient.disconnect();
392
+ }
346
393
  console.log("[AICQ Channel] WebSocket disconnected");
347
394
  } catch (e) {
348
395
  console.warn("[AICQ Channel] Disconnect error:", e.message);
@@ -391,7 +438,7 @@ _plugin.config = {
391
438
  if (section.accountId) {
392
439
  return resolveTemplateVar(cfg, section.accountId);
393
440
  }
394
- return "default";
441
+ return OPENCLAW_DEFAULT_AGENT_ID;
395
442
  },
396
443
 
397
444
  /**
package/src/ui-routes.js CHANGED
File without changes