aicq-chat-plugin 3.3.1 → 3.4.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/README.md +14 -9
- package/SKILL.md +17 -13
- package/{cli.js → cli.cjs} +19 -34
- package/index.js +191 -257
- package/lib/package.json +3 -0
- package/openclaw.plugin.json +2 -2
- package/package.json +17 -12
- package/{postinstall.js → postinstall.cjs} +5 -4
- package/setup-entry.js +8 -55
- package/src/channel.js +218 -136
- package/src/ui-routes.js +545 -420
- package/index.mjs +0 -70
- package/setup-entry.mjs +0 -9
- package/src/channel.mjs +0 -254
- package/src/gateway-handlers.mjs +0 -178
package/index.js
CHANGED
|
@@ -3,193 +3,77 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Architecture: Channel (in-process, no independent port)
|
|
5
5
|
* - Runs inside the OpenClaw process
|
|
6
|
-
* - Uses
|
|
7
|
-
* - Provides Gateway
|
|
6
|
+
* - Uses defineChannelPluginEntry from the official Channel Plugin SDK
|
|
7
|
+
* - Provides Gateway RPC methods for the SPA UI and agent tools
|
|
8
8
|
* - No sidecar process needed
|
|
9
|
+
*
|
|
10
|
+
* ESM module — this file IS the openclaw extension entry.
|
|
9
11
|
*/
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const os = require('os');
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
14
|
+
import { aicqChatPlugin, runtime } from "./src/channel.js";
|
|
15
|
+
import { createRequire } from "module";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import os from "os";
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
|
|
20
|
+
// ── CJS interop — lib/ modules are CommonJS ──────────────────────────
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
|
|
23
|
+
// ── Configuration ────────────────────────────────────────────────────
|
|
24
|
+
const DATA_DIR = process.env.AICQ_DATA_DIR || path.join(os.homedir(), ".aicq-plugin");
|
|
25
|
+
const SERVER_URL = process.env.AICQ_SERVER_URL || "https://aicq.online";
|
|
17
26
|
|
|
18
27
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
19
28
|
|
|
20
|
-
// ── Lazy-loaded modules (
|
|
29
|
+
// ── Lazy-loaded CJS modules (need async db init) ────────────────────
|
|
21
30
|
let _db = null;
|
|
22
31
|
let _identity = null;
|
|
23
32
|
let _serverClient = null;
|
|
24
33
|
let _handshake = null;
|
|
25
34
|
let _chat = null;
|
|
26
|
-
let _channel = null;
|
|
27
|
-
let _uiRoutes = null;
|
|
28
|
-
let _initialized = false;
|
|
29
35
|
|
|
30
36
|
/**
|
|
31
|
-
* Initialize all plugin components (async, called once)
|
|
37
|
+
* Initialize all plugin components (async, called once from registerFull).
|
|
32
38
|
*/
|
|
33
39
|
async function ensureInitialized() {
|
|
34
|
-
if (_initialized) return;
|
|
40
|
+
if (runtime._initialized) return;
|
|
35
41
|
|
|
36
|
-
const PluginDatabase = require(
|
|
37
|
-
const IdentityManager = require(
|
|
38
|
-
const ServerClient = require(
|
|
39
|
-
const HandshakeManager = require(
|
|
40
|
-
const ChatManager = require(
|
|
42
|
+
const PluginDatabase = require("./lib/database");
|
|
43
|
+
const IdentityManager = require("./lib/identity");
|
|
44
|
+
const ServerClient = require("./lib/server-client");
|
|
45
|
+
const HandshakeManager = require("./lib/handshake");
|
|
46
|
+
const ChatManager = require("./lib/chat");
|
|
41
47
|
|
|
42
48
|
// Initialize database
|
|
43
49
|
_db = new PluginDatabase(DATA_DIR);
|
|
44
50
|
await _db.init();
|
|
45
|
-
console.log(
|
|
51
|
+
console.log("[AICQ Channel] Database initialized");
|
|
46
52
|
|
|
47
53
|
// Initialize managers
|
|
48
54
|
_identity = new IdentityManager(_db);
|
|
49
55
|
_serverClient = new ServerClient(_identity, _db, SERVER_URL);
|
|
50
56
|
_handshake = new HandshakeManager(_identity, _serverClient, _db);
|
|
51
|
-
_chat = new ChatManager(_identity, _serverClient, _db, path.join(DATA_DIR,
|
|
52
|
-
|
|
53
|
-
// Load channel and UI route creators
|
|
54
|
-
const { createAicqChannel } = require('./src/channel');
|
|
55
|
-
const { createUiRoutes } = require('./src/ui-routes');
|
|
57
|
+
_chat = new ChatManager(_identity, _serverClient, _db, path.join(DATA_DIR, "uploads"));
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
_uiRoutes = createUiRoutes({
|
|
68
|
-
db: _db,
|
|
69
|
-
identity: _identity,
|
|
70
|
-
serverClient: _serverClient,
|
|
71
|
-
handshake: _handshake,
|
|
72
|
-
chat: _chat,
|
|
73
|
-
dataDir: DATA_DIR,
|
|
74
|
-
});
|
|
59
|
+
// Populate the shared runtime store so channel adapters can use it
|
|
60
|
+
runtime.db = _db;
|
|
61
|
+
runtime.identity = _identity;
|
|
62
|
+
runtime.serverClient = _serverClient;
|
|
63
|
+
runtime.handshake = _handshake;
|
|
64
|
+
runtime.chat = _chat;
|
|
65
|
+
runtime.dataDir = DATA_DIR;
|
|
66
|
+
runtime.serverUrl = SERVER_URL;
|
|
67
|
+
runtime.handleGateway = handleGatewayMethod;
|
|
75
68
|
|
|
76
69
|
// Periodic cleanup
|
|
77
70
|
setInterval(() => _db.cleanup(), 3600000);
|
|
78
71
|
|
|
79
|
-
_initialized = true;
|
|
80
|
-
console.log(
|
|
72
|
+
runtime._initialized = true;
|
|
73
|
+
console.log("[AICQ Channel] Plugin runtime initialized");
|
|
81
74
|
}
|
|
82
75
|
|
|
83
|
-
// ──
|
|
84
|
-
function register() {
|
|
85
|
-
return {
|
|
86
|
-
id: 'aicq-chat',
|
|
87
|
-
name: 'AICQ Encrypted Chat',
|
|
88
|
-
version: '3.0.0',
|
|
89
|
-
description: 'End-to-end encrypted chat channel plugin for OpenClaw agents',
|
|
90
|
-
kind: 'channel',
|
|
91
|
-
|
|
92
|
-
// Channel configuration
|
|
93
|
-
channel: {
|
|
94
|
-
id: 'aicq-chat',
|
|
95
|
-
label: 'AICQ Encrypted Chat',
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
// Tool definitions for OpenClaw agent use
|
|
99
|
-
tools: {
|
|
100
|
-
'chat-friend': {
|
|
101
|
-
description: 'Manage AICQ friends — list, add by friend code, remove, view requests, accept/reject requests',
|
|
102
|
-
parameters: {
|
|
103
|
-
type: 'object',
|
|
104
|
-
properties: {
|
|
105
|
-
action: {
|
|
106
|
-
type: 'string',
|
|
107
|
-
enum: ['list', 'add', 'remove', 'requests', 'accept', 'reject'],
|
|
108
|
-
description: 'The friend management action to perform',
|
|
109
|
-
},
|
|
110
|
-
friend_code: {
|
|
111
|
-
type: 'string',
|
|
112
|
-
description: 'Friend code or temp number for adding a friend',
|
|
113
|
-
},
|
|
114
|
-
friend_id: {
|
|
115
|
-
type: 'string',
|
|
116
|
-
description: 'Friend ID for remove/accept/reject actions',
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
required: ['action'],
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
'chat-send': {
|
|
123
|
-
description: 'Send an encrypted message to a friend or group via AICQ',
|
|
124
|
-
parameters: {
|
|
125
|
-
type: 'object',
|
|
126
|
-
properties: {
|
|
127
|
-
targetId: {
|
|
128
|
-
type: 'string',
|
|
129
|
-
description: 'The friend ID or group ID to send the message to',
|
|
130
|
-
},
|
|
131
|
-
content: {
|
|
132
|
-
type: 'string',
|
|
133
|
-
description: 'The message content to send',
|
|
134
|
-
},
|
|
135
|
-
isGroup: {
|
|
136
|
-
type: 'boolean',
|
|
137
|
-
description: 'Whether the target is a group (default: false)',
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
required: ['targetId', 'content'],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
'chat-export-key': {
|
|
144
|
-
description: 'Export your AICQ identity public key and fingerprint for sharing',
|
|
145
|
-
parameters: {
|
|
146
|
-
type: 'object',
|
|
147
|
-
properties: {
|
|
148
|
-
format: {
|
|
149
|
-
type: 'string',
|
|
150
|
-
enum: ['json', 'qr'],
|
|
151
|
-
description: 'Output format: json for key data, qr for QR code image (default: json)',
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ── activate() — Called by OpenClaw when the plugin is enabled ───────
|
|
161
|
-
async function activate(config) {
|
|
162
|
-
await ensureInitialized();
|
|
163
|
-
|
|
164
|
-
// Channel mode: do NOT auto-create a default agent identity.
|
|
165
|
-
// Agent identities are created on-demand via channel.lifecycle.onAccountCreate
|
|
166
|
-
// which is triggered when OpenClaw assigns an agent to this channel.
|
|
167
|
-
// The resolveAccount method also handles auto-creation if needed.
|
|
168
|
-
const agents = _identity.listAgents();
|
|
169
|
-
let currentAgentId = agents.length > 0 ? agents[0].agent_id : null;
|
|
170
|
-
|
|
171
|
-
// Only connect to AICQ server if we have an existing agent
|
|
172
|
-
if (currentAgentId) {
|
|
173
|
-
try {
|
|
174
|
-
await _serverClient.start(currentAgentId);
|
|
175
|
-
await syncFriendsFromServer(currentAgentId);
|
|
176
|
-
await syncGroupsFromServer(currentAgentId);
|
|
177
|
-
} catch (e) {
|
|
178
|
-
console.error('[AICQ Channel] Initial server connection failed:', e.message);
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
console.log('[AICQ Channel] No existing agent — will connect when an agent is assigned via channel.lifecycle.onAccountCreate');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
handleTool,
|
|
186
|
-
handleGateway,
|
|
187
|
-
channel: _channel,
|
|
188
|
-
gatewayRoutes: _uiRoutes,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ── Sync helpers ────────────────────────────────────────────────────
|
|
76
|
+
// ── Sync helpers ─────────────────────────────────────────────────────
|
|
193
77
|
async function syncFriendsFromServer(agentId) {
|
|
194
78
|
try {
|
|
195
79
|
await _serverClient.ensureAuth(agentId);
|
|
@@ -201,10 +85,10 @@ async function syncFriendsFromServer(agentId) {
|
|
|
201
85
|
_db.addFriend({
|
|
202
86
|
agent_id: agentId,
|
|
203
87
|
id: f.id,
|
|
204
|
-
public_key: f.public_key || f.publicKey ||
|
|
205
|
-
fingerprint: f.fingerprint ||
|
|
206
|
-
friend_type: f.type || f.friend_type ||
|
|
207
|
-
ai_name: f.agent_name || f.ai_name || f.displayName ||
|
|
88
|
+
public_key: f.public_key || f.publicKey || "",
|
|
89
|
+
fingerprint: f.fingerprint || "",
|
|
90
|
+
friend_type: f.type || f.friend_type || "ai",
|
|
91
|
+
ai_name: f.agent_name || f.ai_name || f.displayName || "",
|
|
208
92
|
});
|
|
209
93
|
} else {
|
|
210
94
|
_db.updateFriendOnline(agentId, f.id, f.is_online || f.isOnline || false);
|
|
@@ -212,7 +96,7 @@ async function syncFriendsFromServer(agentId) {
|
|
|
212
96
|
}
|
|
213
97
|
}
|
|
214
98
|
} catch (e) {
|
|
215
|
-
console.error(
|
|
99
|
+
console.error("[AICQ Channel] Sync friends failed:", e.message);
|
|
216
100
|
}
|
|
217
101
|
}
|
|
218
102
|
|
|
@@ -226,132 +110,110 @@ async function syncGroupsFromServer(agentId) {
|
|
|
226
110
|
agent_id: agentId,
|
|
227
111
|
id: g.id,
|
|
228
112
|
name: g.name,
|
|
229
|
-
owner_id: g.owner_id || g.ownerId ||
|
|
230
|
-
members_json: g.members || g.members_json ||
|
|
231
|
-
description: g.description ||
|
|
113
|
+
owner_id: g.owner_id || g.ownerId || "",
|
|
114
|
+
members_json: g.members || g.members_json || "[]",
|
|
115
|
+
description: g.description || "",
|
|
232
116
|
});
|
|
233
117
|
}
|
|
234
118
|
}
|
|
235
119
|
} catch (e) {
|
|
236
|
-
console.error(
|
|
120
|
+
console.error("[AICQ Channel] Sync groups failed:", e.message);
|
|
237
121
|
}
|
|
238
122
|
}
|
|
239
123
|
|
|
240
|
-
// ──
|
|
241
|
-
async function
|
|
242
|
-
await ensureInitialized();
|
|
243
|
-
const agents = _identity.listAgents();
|
|
244
|
-
const currentAgentId = agents.length > 0 ? agents[0].agent_id : null;
|
|
245
|
-
|
|
246
|
-
switch (toolName) {
|
|
247
|
-
case 'chat-friend': {
|
|
248
|
-
const { action, friend_code, friend_id } = params || {};
|
|
249
|
-
switch (action) {
|
|
250
|
-
case 'list':
|
|
251
|
-
return { friends: _db.listFriends(currentAgentId) };
|
|
252
|
-
case 'add':
|
|
253
|
-
return await _handshake.addFriendByCode(currentAgentId, friend_code);
|
|
254
|
-
case 'remove':
|
|
255
|
-
_db.removeFriend(currentAgentId, friend_id);
|
|
256
|
-
try { await _serverClient.removeFriend(friend_id); } catch (e) {}
|
|
257
|
-
return { success: true };
|
|
258
|
-
case 'requests':
|
|
259
|
-
return { requests: _db.getPendingRequests(currentAgentId) };
|
|
260
|
-
case 'accept':
|
|
261
|
-
return await _handshake.acceptRequest(currentAgentId, friend_id);
|
|
262
|
-
case 'reject':
|
|
263
|
-
return await _handshake.rejectRequest(currentAgentId, friend_id);
|
|
264
|
-
default:
|
|
265
|
-
return { error: `Unknown friend action: ${action}` };
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
case 'chat-send':
|
|
269
|
-
return await _chat.sendMessage(
|
|
270
|
-
currentAgentId,
|
|
271
|
-
params.targetId,
|
|
272
|
-
params.content,
|
|
273
|
-
{ isGroup: params.isGroup || false }
|
|
274
|
-
);
|
|
275
|
-
case 'chat-export-key':
|
|
276
|
-
return _identity.getInfo(currentAgentId) || {};
|
|
277
|
-
default:
|
|
278
|
-
return { error: `Unknown tool: ${toolName}` };
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ── Gateway handler ─────────────────────────────────────────────────
|
|
283
|
-
async function handleGateway(method, kwargs = {}) {
|
|
284
|
-
await ensureInitialized();
|
|
124
|
+
// ── Gateway method handler ───────────────────────────────────────────
|
|
125
|
+
async function handleGatewayMethod(method, kwargs = {}) {
|
|
285
126
|
const agents = _identity.listAgents();
|
|
286
127
|
const currentAgentId = agents.length > 0 ? agents[0].agent_id : null;
|
|
287
128
|
|
|
288
129
|
switch (method) {
|
|
289
|
-
case
|
|
130
|
+
case "aicq.status":
|
|
290
131
|
return {
|
|
291
|
-
state: _serverClient.connected ?
|
|
132
|
+
state: _serverClient.connected ? "connected" : "disconnected",
|
|
292
133
|
agent_id: currentAgentId,
|
|
293
|
-
version:
|
|
294
|
-
architecture:
|
|
134
|
+
version: "3.2.0",
|
|
135
|
+
architecture: "channel",
|
|
295
136
|
};
|
|
296
|
-
case
|
|
137
|
+
case "aicq.friends.list":
|
|
297
138
|
return { friends: _db.listFriends(currentAgentId) };
|
|
298
|
-
case
|
|
139
|
+
case "aicq.friends.add":
|
|
299
140
|
return await _handshake.addFriendByCode(currentAgentId, kwargs.temp_number);
|
|
300
|
-
case
|
|
141
|
+
case "aicq.friends.remove":
|
|
301
142
|
_db.removeFriend(currentAgentId, kwargs.friend_id);
|
|
302
143
|
return { success: true };
|
|
303
|
-
case
|
|
144
|
+
case "aicq.friends.requests":
|
|
304
145
|
return { requests: _db.getPendingRequests(currentAgentId) };
|
|
305
|
-
case
|
|
146
|
+
case "aicq.friends.acceptRequest":
|
|
306
147
|
return await _handshake.acceptRequest(currentAgentId, kwargs.request_id);
|
|
307
|
-
case
|
|
148
|
+
case "aicq.friends.rejectRequest":
|
|
308
149
|
return await _handshake.rejectRequest(currentAgentId, kwargs.request_id);
|
|
309
|
-
case
|
|
150
|
+
case "aicq.identity.info":
|
|
310
151
|
return _identity.getInfo(currentAgentId) || {};
|
|
311
|
-
case
|
|
152
|
+
case "aicq.agent.create":
|
|
312
153
|
_identity.createAgent(kwargs.agent_id, kwargs.nickname);
|
|
313
154
|
return { success: true };
|
|
314
|
-
case
|
|
155
|
+
case "aicq.agent.delete":
|
|
315
156
|
_identity.deleteAgent(kwargs.agent_id);
|
|
316
157
|
return { success: true };
|
|
317
|
-
case
|
|
318
|
-
return await _chat.sendMessage(currentAgentId, kwargs.targetId, kwargs.content, {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
case
|
|
158
|
+
case "aicq.chat.send":
|
|
159
|
+
return await _chat.sendMessage(currentAgentId, kwargs.targetId, kwargs.content, {
|
|
160
|
+
isGroup: kwargs.isGroup,
|
|
161
|
+
});
|
|
162
|
+
case "aicq.chat.history":
|
|
163
|
+
return {
|
|
164
|
+
messages: _db.getChatHistory(currentAgentId, kwargs.targetId, {
|
|
165
|
+
limit: kwargs.limit || 50,
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
case "aicq.chat.delete":
|
|
322
169
|
_db.deleteMessage(currentAgentId, kwargs.message_id);
|
|
323
170
|
return { success: true };
|
|
324
|
-
case
|
|
325
|
-
if (!kwargs.friend_id && !kwargs.targetId)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
171
|
+
case "aicq.chat.streamChunk": {
|
|
172
|
+
if (!kwargs.friend_id && !kwargs.targetId)
|
|
173
|
+
return { error: "friend_id or targetId is required" };
|
|
174
|
+
if (!kwargs.data) return { error: "data is required" };
|
|
175
|
+
const chunkType = kwargs.chunk_type || kwargs.chunkType || "text";
|
|
176
|
+
const ALLOWED_CHUNK_TYPES = [
|
|
177
|
+
"text",
|
|
178
|
+
"reasoning",
|
|
179
|
+
"thinking",
|
|
180
|
+
"clear_text",
|
|
181
|
+
"tool_call",
|
|
182
|
+
"tool_result",
|
|
183
|
+
];
|
|
184
|
+
if (!ALLOWED_CHUNK_TYPES.includes(chunkType))
|
|
185
|
+
return {
|
|
186
|
+
error: `Invalid chunk_type: ${chunkType}. Allowed: ${ALLOWED_CHUNK_TYPES.join(", ")}`,
|
|
187
|
+
};
|
|
330
188
|
const streamTarget = kwargs.friend_id || kwargs.targetId;
|
|
331
189
|
const sent = _serverClient.sendWS({
|
|
332
|
-
type:
|
|
190
|
+
type: "stream_chunk",
|
|
333
191
|
to: streamTarget,
|
|
334
|
-
chunkType
|
|
192
|
+
chunkType,
|
|
335
193
|
data: kwargs.data,
|
|
336
194
|
});
|
|
337
|
-
if (!sent) return { error:
|
|
195
|
+
if (!sent) return { error: "Not connected to server", success: false };
|
|
338
196
|
return { success: true };
|
|
339
197
|
}
|
|
340
|
-
case
|
|
341
|
-
if (!kwargs.friend_id && !kwargs.targetId)
|
|
198
|
+
case "aicq.chat.streamEnd": {
|
|
199
|
+
if (!kwargs.friend_id && !kwargs.targetId)
|
|
200
|
+
return { error: "friend_id or targetId is required" };
|
|
342
201
|
const endTarget = kwargs.friend_id || kwargs.targetId;
|
|
343
|
-
const msgId =
|
|
202
|
+
const msgId =
|
|
203
|
+
kwargs.message_id ||
|
|
204
|
+
kwargs.messageId ||
|
|
205
|
+
"msg_" + Date.now() + "_" + Math.random().toString(36).substr(2, 6);
|
|
344
206
|
const endSent = _serverClient.sendWS({
|
|
345
|
-
type:
|
|
207
|
+
type: "stream_end",
|
|
346
208
|
to: endTarget,
|
|
347
209
|
messageId: msgId,
|
|
348
210
|
});
|
|
349
|
-
if (!endSent) return { error:
|
|
211
|
+
if (!endSent) return { error: "Not connected to server", success: false };
|
|
350
212
|
return { success: true, messageId: msgId };
|
|
351
213
|
}
|
|
352
|
-
case
|
|
214
|
+
case "aicq.groups.list":
|
|
353
215
|
return { groups: _db.listGroups(currentAgentId) };
|
|
354
|
-
case
|
|
216
|
+
case "aicq.groups.create": {
|
|
355
217
|
await _serverClient.ensureAuth(currentAgentId);
|
|
356
218
|
const result = await _serverClient.createGroup(kwargs.name, kwargs.description);
|
|
357
219
|
if (result.id) {
|
|
@@ -360,34 +222,106 @@ async function handleGateway(method, kwargs = {}) {
|
|
|
360
222
|
id: result.id,
|
|
361
223
|
name: kwargs.name,
|
|
362
224
|
owner_id: currentAgentId,
|
|
363
|
-
members_json: result.members ||
|
|
364
|
-
description: kwargs.description ||
|
|
225
|
+
members_json: result.members || "[]",
|
|
226
|
+
description: kwargs.description || "",
|
|
365
227
|
});
|
|
366
228
|
}
|
|
367
229
|
return { success: true, group: result };
|
|
368
230
|
}
|
|
369
|
-
case
|
|
231
|
+
case "aicq.groups.join":
|
|
370
232
|
await _serverClient.ensureAuth(currentAgentId);
|
|
371
233
|
return await _serverClient.inviteGroupMember(kwargs.group_id, currentAgentId);
|
|
372
|
-
case
|
|
234
|
+
case "aicq.groups.messages": {
|
|
373
235
|
await _serverClient.ensureAuth(currentAgentId);
|
|
374
236
|
return await _serverClient.getGroupMessages(kwargs.group_id, kwargs.limit || 50);
|
|
375
237
|
}
|
|
376
|
-
case
|
|
238
|
+
case "aicq.groups.silent":
|
|
377
239
|
_db.setGroupSilentMode(currentAgentId, kwargs.group_id, !!kwargs.silent);
|
|
378
240
|
return { success: true, silent: !!kwargs.silent };
|
|
379
|
-
case
|
|
241
|
+
case "aicq.sessions.list":
|
|
380
242
|
return { sessions: [] };
|
|
381
243
|
default:
|
|
382
244
|
return { error: `Unknown method: ${method}` };
|
|
383
245
|
}
|
|
384
246
|
}
|
|
385
247
|
|
|
386
|
-
// ──
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
248
|
+
// ── CLI metadata registration (lightweight, no runtime init) ─────────
|
|
249
|
+
function registerCliMetadata(api) {
|
|
250
|
+
api.registerCli(
|
|
251
|
+
({ program }) => {
|
|
252
|
+
program
|
|
253
|
+
.command("aicq-chat")
|
|
254
|
+
.description("AICQ Encrypted Chat management");
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
descriptors: [
|
|
258
|
+
{
|
|
259
|
+
name: "aicq-chat",
|
|
260
|
+
description: "AICQ Encrypted Chat management",
|
|
261
|
+
hasSubcommands: false,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Full runtime registration ────────────────────────────────────────
|
|
269
|
+
async function registerFull(api) {
|
|
270
|
+
// Register gateway RPC methods — each wraps handleGatewayMethod
|
|
271
|
+
const GATEWAY_METHODS = [
|
|
272
|
+
"aicq.status",
|
|
273
|
+
"aicq.friends.list",
|
|
274
|
+
"aicq.friends.add",
|
|
275
|
+
"aicq.friends.remove",
|
|
276
|
+
"aicq.friends.requests",
|
|
277
|
+
"aicq.friends.acceptRequest",
|
|
278
|
+
"aicq.friends.rejectRequest",
|
|
279
|
+
"aicq.identity.info",
|
|
280
|
+
"aicq.agent.create",
|
|
281
|
+
"aicq.agent.delete",
|
|
282
|
+
"aicq.chat.send",
|
|
283
|
+
"aicq.chat.history",
|
|
284
|
+
"aicq.chat.delete",
|
|
285
|
+
"aicq.chat.streamChunk",
|
|
286
|
+
"aicq.chat.streamEnd",
|
|
287
|
+
"aicq.groups.list",
|
|
288
|
+
"aicq.groups.create",
|
|
289
|
+
"aicq.groups.join",
|
|
290
|
+
"aicq.groups.messages",
|
|
291
|
+
"aicq.groups.silent",
|
|
292
|
+
"aicq.sessions.list",
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
for (const method of GATEWAY_METHODS) {
|
|
296
|
+
api.registerGatewayMethod(method, async (opts) => {
|
|
297
|
+
try {
|
|
298
|
+
await ensureInitialized();
|
|
299
|
+
const result = await handleGatewayMethod(method, opts.params || {});
|
|
300
|
+
opts.respond(true, result);
|
|
301
|
+
} catch (e) {
|
|
302
|
+
opts.respond(false, undefined, { message: e.message, code: "AICQ_ERROR" });
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Register HTTP routes for the SPA UI and REST API.
|
|
308
|
+
// Lazy-loaded to keep the entry narrow — the ui-routes module pulls in
|
|
309
|
+
// qrcode and multer which are not needed during setup-only registration.
|
|
310
|
+
try {
|
|
311
|
+
const { registerHttpRoutes } = await import("./src/ui-routes.js");
|
|
312
|
+
registerHttpRoutes(api, { ensureInitialized, runtime, DATA_DIR, SERVER_URL });
|
|
313
|
+
} catch (e) {
|
|
314
|
+
console.error("[AICQ Channel] Failed to register HTTP routes:", e.message);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── Export the entry point ───────────────────────────────────────────
|
|
319
|
+
export default defineChannelPluginEntry({
|
|
320
|
+
id: "aicq-chat",
|
|
321
|
+
name: "AICQ Encrypted Chat",
|
|
322
|
+
description:
|
|
323
|
+
"End-to-end encrypted chat channel plugin for OpenClaw agents — NaCl (X25519 + XSalsa20-Poly1305)",
|
|
324
|
+
plugin: aicqChatPlugin,
|
|
325
|
+
registerCliMetadata,
|
|
326
|
+
registerFull,
|
|
327
|
+
});
|
package/lib/package.json
ADDED
package/openclaw.plugin.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"kind": "channel",
|
|
3
3
|
"id": "aicq-chat",
|
|
4
4
|
"name": "AICQ Encrypted Chat",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.4.1",
|
|
6
6
|
"description": "End-to-end encrypted chat channel via AICQ protocol — in-process Channel plugin using OpenClaw Channel SDK",
|
|
7
|
-
"entry": "index.
|
|
7
|
+
"entry": "index.js",
|
|
8
8
|
"activation": {
|
|
9
9
|
"onStartup": true
|
|
10
10
|
},
|
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicq-chat-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.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
|
-
"main": "index.
|
|
6
|
+
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"aicq-plugin": "cli.
|
|
8
|
+
"aicq-plugin": "cli.cjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"index.mjs",
|
|
12
11
|
"index.js",
|
|
13
|
-
"setup-entry.mjs",
|
|
14
12
|
"setup-entry.js",
|
|
15
|
-
"cli.
|
|
16
|
-
"postinstall.
|
|
13
|
+
"cli.cjs",
|
|
14
|
+
"postinstall.cjs",
|
|
17
15
|
"src/",
|
|
18
16
|
"lib/",
|
|
19
17
|
"public/",
|
|
@@ -22,9 +20,8 @@
|
|
|
22
20
|
"README.md"
|
|
23
21
|
],
|
|
24
22
|
"scripts": {
|
|
25
|
-
"start": "node index.
|
|
26
|
-
"
|
|
27
|
-
"postinstall": "node postinstall.js",
|
|
23
|
+
"start": "node index.js",
|
|
24
|
+
"postinstall": "node postinstall.cjs",
|
|
28
25
|
"install-deps": "npm install"
|
|
29
26
|
},
|
|
30
27
|
"keywords": [
|
|
@@ -61,14 +58,22 @@
|
|
|
61
58
|
"tweetnacl-util": "^0.15.1",
|
|
62
59
|
"ws": "^8.16.0"
|
|
63
60
|
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"openclaw": ">=2026.3.24-beta.2"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"openclaw": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
64
69
|
"engines": {
|
|
65
70
|
"node": ">=18.0.0"
|
|
66
71
|
},
|
|
67
72
|
"openclaw": {
|
|
68
73
|
"extensions": [
|
|
69
|
-
"./index.
|
|
74
|
+
"./index.js"
|
|
70
75
|
],
|
|
71
|
-
"setupEntry": "./setup-entry.
|
|
76
|
+
"setupEntry": "./setup-entry.js",
|
|
72
77
|
"channel": {
|
|
73
78
|
"id": "aicq-chat",
|
|
74
79
|
"label": "AICQ Encrypted Chat",
|