aicq-chat-plugin 3.4.1 → 3.5.1
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/channel.js +189 -4
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"kind": "channel",
|
|
3
3
|
"id": "aicq-chat",
|
|
4
4
|
"name": "AICQ Encrypted Chat",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.5.1",
|
|
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": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicq-chat-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
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/src/channel.js
CHANGED
|
@@ -32,6 +32,26 @@ export const runtime = {
|
|
|
32
32
|
_initialized: false,
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
// ── Template variable resolver ───────────────────────────────────────
|
|
36
|
+
// OpenClaw stores accountId as-is (e.g. "{{agent.id}}") in config.
|
|
37
|
+
// Plugins must resolve template variables at runtime.
|
|
38
|
+
|
|
39
|
+
function resolveTemplateVar(cfg, value) {
|
|
40
|
+
if (typeof value !== "string") return value;
|
|
41
|
+
const match = value.match(/^\{\{(\w[\w.]*)\}\}$/);
|
|
42
|
+
if (!match) return value;
|
|
43
|
+
|
|
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
|
+
}
|
|
51
|
+
|
|
52
|
+
return value; // unknown template — return as-is
|
|
53
|
+
}
|
|
54
|
+
|
|
35
55
|
// ── Resolved account type ────────────────────────────────────────────
|
|
36
56
|
// This is the object returned by resolveAccount() and consumed by
|
|
37
57
|
// security / pairing / outbound adapters.
|
|
@@ -43,14 +63,17 @@ export const runtime = {
|
|
|
43
63
|
*/
|
|
44
64
|
function resolveAccount(cfg, accountId) {
|
|
45
65
|
const section = (cfg.channels || {})["aicq-chat"] || {};
|
|
46
|
-
const
|
|
66
|
+
const rawAccountId = accountId || section.accountId || null;
|
|
47
67
|
|
|
48
|
-
if (!
|
|
68
|
+
if (!rawAccountId) {
|
|
49
69
|
throw new Error(
|
|
50
70
|
"aicq-chat: accountId is required (set channels.aicq-chat.accountId)"
|
|
51
71
|
);
|
|
52
72
|
}
|
|
53
73
|
|
|
74
|
+
// Resolve template variables like {{agent.id}}
|
|
75
|
+
const resolvedAccountId = resolveTemplateVar(cfg, rawAccountId);
|
|
76
|
+
|
|
54
77
|
return {
|
|
55
78
|
accountId: resolvedAccountId,
|
|
56
79
|
serverUrl: section.serverUrl || "https://aicq.online",
|
|
@@ -184,18 +207,167 @@ const _plugin = createChatChannelPlugin({
|
|
|
184
207
|
},
|
|
185
208
|
});
|
|
186
209
|
|
|
210
|
+
// ── Gateway adapter: startAccount / stopAccount ───────────────────────
|
|
211
|
+
// OpenClaw calls startAccount when the channel is activated (on startup
|
|
212
|
+
// or when re-enabled). This is where we initialise the runtime, connect
|
|
213
|
+
// to the AICQ signalling server, and wire up inbound message delivery
|
|
214
|
+
// via the channelRuntime helpers.
|
|
215
|
+
|
|
216
|
+
_plugin.gateway = {
|
|
217
|
+
/**
|
|
218
|
+
* Start the channel account — connect to the AICQ server and begin
|
|
219
|
+
* listening for inbound messages.
|
|
220
|
+
*/
|
|
221
|
+
async startAccount(ctx) {
|
|
222
|
+
const { cfg, accountId, account, setStatus } = ctx;
|
|
223
|
+
|
|
224
|
+
console.log(`[AICQ Channel] startAccount called for ${accountId}`);
|
|
225
|
+
|
|
226
|
+
// Ensure the runtime (DB, identity, transport) is initialised
|
|
227
|
+
if (runtime.handleGateway) {
|
|
228
|
+
// Runtime already initialised via registerFull — just verify
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Resolve the agent ID from OpenClaw config
|
|
232
|
+
const agents = cfg.agents?.list || [];
|
|
233
|
+
const agentId = agents.length > 0 ? agents[0].id : accountId;
|
|
234
|
+
|
|
235
|
+
// Ensure we have an identity in the plugin DB
|
|
236
|
+
if (runtime.identity) {
|
|
237
|
+
const existing = runtime.identity.listAgents();
|
|
238
|
+
if (existing.length === 0) {
|
|
239
|
+
runtime.identity.createAgent(agentId, agents[0]?.name || "AICQ Agent");
|
|
240
|
+
console.log(`[AICQ Channel] Created agent identity: ${agentId}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Connect to the AICQ server
|
|
245
|
+
if (runtime.serverClient) {
|
|
246
|
+
try {
|
|
247
|
+
await runtime.serverClient.ensureAuth(agentId);
|
|
248
|
+
console.log(`[AICQ Channel] Authenticated as ${agentId}`);
|
|
249
|
+
|
|
250
|
+
// Connect WebSocket for real-time messages
|
|
251
|
+
if (typeof runtime.serverClient.connect === "function") {
|
|
252
|
+
await runtime.serverClient.connect(agentId);
|
|
253
|
+
console.log("[AICQ Channel] WebSocket connected");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Sync friends and groups from server
|
|
257
|
+
if (runtime.handleGateway) {
|
|
258
|
+
try {
|
|
259
|
+
await runtime.handleGateway("aicq.friends.list", {});
|
|
260
|
+
await runtime.handleGateway("aicq.groups.list", {});
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.warn("[AICQ Channel] Initial sync failed:", e.message);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch (e) {
|
|
266
|
+
console.error("[AICQ Channel] Failed to connect:", e.message);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Wire up inbound message handling via channelRuntime if available
|
|
271
|
+
if (ctx.channelRuntime) {
|
|
272
|
+
const { reply, routing } = ctx.channelRuntime;
|
|
273
|
+
if (reply && routing) {
|
|
274
|
+
console.log("[AICQ Channel] channelRuntime available — AI dispatch enabled");
|
|
275
|
+
|
|
276
|
+
// Register inbound message handler on the serverClient
|
|
277
|
+
if (runtime.serverClient && typeof runtime.serverClient.onMessage === "function") {
|
|
278
|
+
runtime.serverClient.onMessage(async (msg) => {
|
|
279
|
+
try {
|
|
280
|
+
const resolvedAgentId = agents.length > 0 ? agents[0].id : accountId;
|
|
281
|
+
const routeResult = await routing.resolveAgentRoute({
|
|
282
|
+
channelId: "aicq-chat",
|
|
283
|
+
accountId,
|
|
284
|
+
fromId: msg.from || msg.sender_id,
|
|
285
|
+
chatType: msg.isGroup ? "group" : "dm",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (routeResult?.agentId) {
|
|
289
|
+
await reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
290
|
+
ctx: {
|
|
291
|
+
channelId: "aicq-chat",
|
|
292
|
+
accountId,
|
|
293
|
+
fromId: msg.from || msg.sender_id,
|
|
294
|
+
text: msg.content || msg.text || "",
|
|
295
|
+
chatType: msg.isGroup ? "group" : "dm",
|
|
296
|
+
},
|
|
297
|
+
cfg,
|
|
298
|
+
dispatcherOptions: {
|
|
299
|
+
deliver: async (payload) => {
|
|
300
|
+
// Send AI reply back through AICQ
|
|
301
|
+
if (runtime.chat && payload.text) {
|
|
302
|
+
await runtime.chat.sendMessage(
|
|
303
|
+
resolvedAgentId,
|
|
304
|
+
msg.from || msg.sender_id,
|
|
305
|
+
payload.text,
|
|
306
|
+
{ isGroup: !!msg.isGroup }
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {
|
|
314
|
+
console.error("[AICQ Channel] Inbound message handling error:", e.message);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
console.log("[AICQ Channel] channelRuntime not available — running in standalone mode");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Update health status
|
|
324
|
+
setStatus({
|
|
325
|
+
accountId,
|
|
326
|
+
enabled: true,
|
|
327
|
+
configured: true,
|
|
328
|
+
running: true,
|
|
329
|
+
lastStartAt: Date.now(),
|
|
330
|
+
lastError: null,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
console.log(`[AICQ Channel] Account ${accountId} started successfully`);
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Stop the channel account — disconnect and clean up.
|
|
338
|
+
*/
|
|
339
|
+
async stopAccount(ctx) {
|
|
340
|
+
const { accountId } = ctx;
|
|
341
|
+
console.log(`[AICQ Channel] stopAccount called for ${accountId}`);
|
|
342
|
+
|
|
343
|
+
if (runtime.serverClient && typeof runtime.serverClient.disconnect === "function") {
|
|
344
|
+
try {
|
|
345
|
+
runtime.serverClient.disconnect();
|
|
346
|
+
console.log("[AICQ Channel] WebSocket disconnected");
|
|
347
|
+
} catch (e) {
|
|
348
|
+
console.warn("[AICQ Channel] Disconnect error:", e.message);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
|
|
187
354
|
// ── Add config helpers (required by OpenClaw channel loader) ──────────
|
|
188
355
|
// createChatChannelPlugin does not auto-attach config helpers,
|
|
189
356
|
// but the OpenClaw loader requires plugin.config.listAccountIds
|
|
190
357
|
// and plugin.config.resolveAccount for channel registration.
|
|
358
|
+
|
|
359
|
+
// resolveTemplateVar is defined at the top of this file.
|
|
360
|
+
|
|
191
361
|
_plugin.config = {
|
|
192
362
|
/**
|
|
193
363
|
* List all account IDs configured for this channel.
|
|
364
|
+
* Resolves template variables like {{agent.id}}.
|
|
194
365
|
*/
|
|
195
366
|
listAccountIds(cfg) {
|
|
196
367
|
const section = (cfg.channels || {})["aicq-chat"] || {};
|
|
197
368
|
if (section.accountId) {
|
|
198
|
-
|
|
369
|
+
const resolved = resolveTemplateVar(cfg, section.accountId);
|
|
370
|
+
return [resolved];
|
|
199
371
|
}
|
|
200
372
|
return [];
|
|
201
373
|
},
|
|
@@ -210,6 +382,18 @@ _plugin.config = {
|
|
|
210
382
|
*/
|
|
211
383
|
inspectAccount,
|
|
212
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Default account ID for this channel.
|
|
387
|
+
* Resolves {{agent.id}} to the actual agent ID.
|
|
388
|
+
*/
|
|
389
|
+
defaultAccountId(cfg) {
|
|
390
|
+
const section = (cfg.channels || {})["aicq-chat"] || {};
|
|
391
|
+
if (section.accountId) {
|
|
392
|
+
return resolveTemplateVar(cfg, section.accountId);
|
|
393
|
+
}
|
|
394
|
+
return "default";
|
|
395
|
+
},
|
|
396
|
+
|
|
213
397
|
/**
|
|
214
398
|
* Check if the channel is configured.
|
|
215
399
|
*/
|
|
@@ -234,8 +418,9 @@ _plugin.config = {
|
|
|
234
418
|
*/
|
|
235
419
|
describeAccount(cfg, accountId) {
|
|
236
420
|
const section = (cfg.channels || {})["aicq-chat"] || {};
|
|
421
|
+
const rawId = accountId || section.accountId || null;
|
|
237
422
|
return {
|
|
238
|
-
accountId:
|
|
423
|
+
accountId: rawId ? resolveTemplateVar(cfg, rawId) : null,
|
|
239
424
|
label: "AICQ Encrypted Chat",
|
|
240
425
|
enabled: section.enabled !== false,
|
|
241
426
|
};
|