aicq-chat-plugin 2.6.7 → 3.0.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/cli.js CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * AICQ Chat Plugin — CLI Entry Point
3
+ * AICQ Chat Plugin — CLI Entry Point (v3.0 Channel)
4
4
  *
5
5
  * Usage:
6
6
  * openclaw plugins install npm:aicq-chat-plugin Install via openclaw CLI
7
7
  * openclaw plugins uninstall aicq-chat Uninstall old version
8
8
  * openclaw gateway restart Restart gateway
9
- * aicq-plugin Start plugin server
10
- * aicq-plugin start Start the plugin server
9
+ * aicq-plugin Start plugin server (standalone mode)
10
+ * aicq-plugin start Start the plugin server (standalone mode)
11
11
  * aicq-plugin install Install plugin to OpenClaw only
12
12
  * aicq-plugin uninstall Remove plugin from OpenClaw
13
13
  * aicq-plugin status Check plugin status
14
- * aicq-plugin --port <port> Specify port (default 6109)
14
+ * aicq-plugin --server <url> Specify AICQ server URL
15
15
  * aicq-plugin --help Show help
16
16
  */
17
17
  const { spawn, execSync } = require('child_process');
@@ -23,14 +23,9 @@ const args = process.argv.slice(2);
23
23
  const command = args[0] || 'start';
24
24
 
25
25
  // Parse options
26
- let port = process.env.AICQ_PORT || '6109';
27
26
  let serverUrl = process.env.AICQ_SERVER_URL || 'https://aicq.online';
28
27
 
29
28
  for (let i = 0; i < args.length; i++) {
30
- if ((args[i] === '--port' || args[i] === '-p') && args[i + 1]) {
31
- port = args[i + 1];
32
- i++;
33
- }
34
29
  if ((args[i] === '--server' || args[i] === '-s') && args[i + 1]) {
35
30
  serverUrl = args[i + 1];
36
31
  i++;
@@ -40,7 +35,7 @@ for (let i = 0; i < args.length; i++) {
40
35
  // ── SKILL.md template ─────────────────────────────────────────────
41
36
  const SKILL_MD_TEMPLATE = `---
42
37
  name: aicq-chat
43
- description: AICQ End-to-end Encrypted Chat Plugin for OpenClaw — Full UI with friend management, group chat, file transfer, and AI agent communication
38
+ description: AICQ End-to-end Encrypted Chat Channel Plugin for OpenClaw — In-process Channel architecture with friend management, group chat, file transfer, and AI agent communication
44
39
  license: MIT
45
40
  metadata:
46
41
  author: AICQ
@@ -49,61 +44,27 @@ metadata:
49
44
 
50
45
  # AICQ Encrypted Chat
51
46
 
52
- AICQ 是一个端到端加密聊天插件,适用于 OpenClaw 的完整聊天 UI。支持好友管理、群组聊天、文件传输和 AI Agent 通信。
53
-
54
- ## 功能特性
55
-
56
- - **端到端加密 (E2EE)** — 基于 NaCl (libsodium) 的加密体系,消息仅通信双方可读
57
- - **Agent 管理** — 支持多 Agent 切换、创建和删除
58
- - **好友管理** — 好友码添加、QR 码扫描、好友列表同步
59
- - **群组聊天** — 创建群组、邀请成员、静默模式
60
- - **消息功能** — Markdown/LaTeX 渲染、图片/文件上传、@提及
61
- - **密钥管理** — 公钥/私钥显示、密钥轮换、指纹验证
62
- - **P2P 通信** — 握手、文本传输、文件传输
47
+ AICQ 是一个端到端加密聊天频道插件,基于 OpenClaw Channel 架构。支持好友管理、群组聊天、文件传输和 AI Agent 通信。
63
48
 
64
49
  ## 一键启动
65
50
 
66
51
  \`\`\`bash
67
- # 1. 卸载旧版
68
- openclaw plugins uninstall aicq-chat
69
-
70
- # 2. 安装新版
71
52
  openclaw plugins install npm:aicq-chat-plugin
72
-
73
- # 3. 重启 gateway
74
53
  openclaw gateway restart
75
-
76
- # 4. 浏览器访问聊天界面
77
- open http://localhost:6109
78
54
  \`\`\`
79
55
 
80
- ## OpenClaw 集成
81
-
82
- 插件会自动注册为 OpenClaw sidecar,提供以下工具和网关:
83
-
84
- ### 工具
85
- - \`chat-friend\` — 好友管理
86
- - \`chat-send\` — 发送消息
87
- - \`chat-export-key\` — 导出密钥
88
-
89
- ### 网关方法
90
- - \`aicq.status\` — 插件状态
91
- - \`aicq.friends.list/add/remove\` — 好友操作
92
- - \`aicq.chat.send/history/delete\` — 聊天操作
93
- - \`aicq.groups.list/create/join\` — 群组操作
94
- - \`aicq.identity.info\` — 身份信息
95
-
96
- ## 配置
56
+ ## 功能特性
97
57
 
98
- | 变量 | 默认值 | 说明 |
99
- |------|--------|------|
100
- | \`AICQ_PORT\` | 6109 | 插件服务端口 |
101
- | \`AICQ_SERVER_URL\` | https://aicq.online | AICQ 服务器地址 |
102
- | \`AICQ_DATA_DIR\` | ~/.aicq-plugin | 数据存储目录 |
58
+ - **端到端加密 (E2EE)** 基于 NaCl (libsodium) 的加密体系
59
+ - **Channel 架构** — 进程内运行,无独立端口
60
+ - **好友管理** 好友码添加、QR 码扫描
61
+ - **群组聊天** 创建群组、邀请成员
62
+ - **流式消息** 支持 AI 流式输出
103
63
 
104
- ## Chat UI
64
+ ## UI 路由
105
65
 
106
- 启动后访问 http://localhost:6109 即可使用完整的聊天界面。
66
+ - /plugins/aicq-chat/ui/ — 聊天界面
67
+ - /plugins/aicq-chat/api/* — API 端点
107
68
  `;
108
69
 
109
70
  // ── Find OpenClaw installation ──────────────────────────────────────
@@ -122,34 +83,24 @@ function findOpenClawDir() {
122
83
  return null;
123
84
  }
124
85
 
125
- // ── Find OpenClaw workspace (for skills/ directory) ────────────────
86
+ // ── Find OpenClaw workspace ────────────────────────────────────────
126
87
  function findOpenClawWorkspace() {
127
- // Check OPENCLAW_WORKSPACE env var first
128
88
  if (process.env.OPENCLAW_WORKSPACE) {
129
89
  return process.env.OPENCLAW_WORKSPACE;
130
90
  }
131
-
132
- // Try to find workspace from clawhub or common locations
133
- // Prefer directories that already have a skills/ directory
134
91
  const home = os.homedir();
135
92
  const candidates = [
136
- // Current working directory (most common for clawhub)
137
93
  process.cwd(),
138
- // Common workspace locations
139
94
  path.join(home, 'my-project'),
140
95
  path.join(home, 'openclaw'),
141
96
  path.join(home, '.openclaw'),
142
97
  ];
143
-
144
- // Check if any candidate has a skills/ directory (existing)
145
98
  for (const dir of candidates) {
146
99
  const skillsDir = path.join(dir, 'skills');
147
100
  if (fs.existsSync(skillsDir)) {
148
101
  return dir;
149
102
  }
150
103
  }
151
-
152
- // Also check parent directories of the current working dir
153
104
  let current = process.cwd();
154
105
  for (let i = 0; i < 3; i++) {
155
106
  const skillsDir = path.join(current, 'skills');
@@ -158,15 +109,10 @@ function findOpenClawWorkspace() {
158
109
  }
159
110
  current = path.dirname(current);
160
111
  }
161
-
162
- // If no existing skills/ found, fall back to the OpenClaw directory itself.
163
- // This handles the case where ~/.openclaw/ exists but has no skills/ yet.
164
- // We'll auto-create skills/ inside it during installation.
165
112
  const openclawDir = findOpenClawDir();
166
113
  if (openclawDir) {
167
114
  return openclawDir;
168
115
  }
169
-
170
116
  return null;
171
117
  }
172
118
 
@@ -188,19 +134,16 @@ function copyDirRecursive(src, dest) {
188
134
 
189
135
  // ── Install plugin files to a target directory ─────────────────────
190
136
  function installToDir(sourceDir, targetDir, version) {
191
- // Files and dirs to copy
192
137
  const filesToCopy = [
193
- 'index.js', 'cli.js', 'postinstall.js',
138
+ 'index.js', 'setup-entry.js', 'cli.js', 'postinstall.js',
194
139
  'openclaw.plugin.json', 'package.json', 'README.md',
195
140
  ];
196
- const dirsToCopy = ['lib', 'public'];
141
+ const dirsToCopy = ['lib', 'src', 'public'];
197
142
 
198
- // Create target directory if needed
199
143
  if (!fs.existsSync(targetDir)) {
200
144
  fs.mkdirSync(targetDir, { recursive: true });
201
145
  }
202
146
 
203
- // Copy files
204
147
  for (const file of filesToCopy) {
205
148
  const src = path.join(sourceDir, file);
206
149
  const dest = path.join(targetDir, file);
@@ -209,7 +152,6 @@ function installToDir(sourceDir, targetDir, version) {
209
152
  }
210
153
  }
211
154
 
212
- // Copy directories
213
155
  for (const dir of dirsToCopy) {
214
156
  const src = path.join(sourceDir, dir);
215
157
  const dest = path.join(targetDir, dir);
@@ -221,133 +163,91 @@ function installToDir(sourceDir, targetDir, version) {
221
163
  }
222
164
  }
223
165
 
224
- // Generate SKILL.md with current version
225
166
  const skillMd = SKILL_MD_TEMPLATE.replace('{VERSION}', version);
226
167
  fs.writeFileSync(path.join(targetDir, 'SKILL.md'), skillMd, 'utf8');
227
168
  }
228
169
 
229
- // ── Copy plugin files to OpenClaw (skills + plugins) ──────────────
170
+ // ── Install to OpenClaw ────────────────────────────────────────────
230
171
  function installToOpenClaw() {
231
172
  const PLUGIN_ID = 'aicq-chat';
232
173
  const sourceDir = path.resolve(__dirname);
233
- let version = '2.5.5';
174
+ let version = '3.0.0';
234
175
 
235
- // Read version from package.json
236
176
  try {
237
177
  const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, 'package.json'), 'utf8'));
238
178
  version = pkg.version;
239
179
  } catch (e) {}
240
180
 
241
- // ── Step 1: Install to OpenClaw skills/ directory ──────────────
242
- // This is the primary install location — OpenClaw dashboard discovers
243
- // skills by scanning skills/ directories for SKILL.md marker files.
244
181
  let skillsInstalled = false;
245
182
  const workspace = findOpenClawWorkspace();
246
183
  if (workspace) {
247
184
  const skillsDir = path.join(workspace, 'skills');
248
185
  const skillTargetDir = path.join(skillsDir, PLUGIN_ID);
186
+ console.log(`[AICQ] Found workspace at: ${workspace}`);
187
+ console.log(`[AICQ] Installing skill to ${skillTargetDir}...`);
249
188
 
250
- // Check if already installed and up-to-date
251
- const targetSkillJson = path.join(skillTargetDir, 'openclaw.plugin.json');
252
- if (fs.existsSync(targetSkillJson)) {
253
- try {
254
- const existing = JSON.parse(fs.readFileSync(targetSkillJson, 'utf8'));
255
- if (existing.version === version) {
256
- console.log(`[AICQ] Skill already installed at ${skillTargetDir} (v${version})`);
257
- skillsInstalled = true;
258
- }
259
- } catch (e) {}
189
+ if (!fs.existsSync(skillsDir)) {
190
+ fs.mkdirSync(skillsDir, { recursive: true });
260
191
  }
261
192
 
262
- if (!skillsInstalled) {
263
- console.log(`[AICQ] Found workspace at: ${workspace}`);
264
- console.log(`[AICQ] Installing skill to ${skillTargetDir}...`);
265
-
266
- if (!fs.existsSync(skillsDir)) {
267
- fs.mkdirSync(skillsDir, { recursive: true });
268
- }
269
-
270
- installToDir(sourceDir, skillTargetDir, version);
271
-
272
- // Install npm dependencies in target
273
- console.log('[AICQ] Installing skill dependencies...');
274
- try {
275
- execSync('npm install --omit=dev', {
276
- cwd: skillTargetDir,
277
- stdio: 'pipe',
278
- timeout: 120000,
279
- });
280
- console.log('[AICQ] Skill dependencies installed.');
281
- } catch (e) {
282
- console.log('[AICQ] Warning: npm install failed. You may need to run manually:');
283
- console.log(` cd ${skillTargetDir} && npm install`);
284
- }
285
-
286
- console.log(`[AICQ] Skill installed to: ${skillTargetDir}`);
287
- skillsInstalled = true;
193
+ installToDir(sourceDir, skillTargetDir, version);
194
+
195
+ console.log('[AICQ] Installing skill dependencies...');
196
+ try {
197
+ execSync('npm install --omit=dev', {
198
+ cwd: skillTargetDir,
199
+ stdio: 'pipe',
200
+ timeout: 120000,
201
+ });
202
+ console.log('[AICQ] Skill dependencies installed.');
203
+ } catch (e) {
204
+ console.log('[AICQ] Warning: npm install failed. You may need to run manually:');
205
+ console.log(` cd ${skillTargetDir} && npm install`);
288
206
  }
207
+
208
+ console.log(`[AICQ] Skill installed to: ${skillTargetDir}`);
209
+ skillsInstalled = true;
289
210
  }
290
211
 
291
- // ── Step 2: Install to OpenClaw plugins/ directory ─────────────
292
- // This is the secondary install location for sidecar startup.
293
- // OpenClaw reads openclaw.plugin.json from plugins/ to launch sidecar.
294
212
  let pluginInstalled = false;
295
213
  const openclawDir = findOpenClawDir();
296
214
  if (openclawDir) {
297
215
  const pluginsDir = path.join(openclawDir, 'plugins');
298
216
  const pluginTargetDir = path.join(pluginsDir, PLUGIN_ID);
299
217
 
300
- // Check if already installed and up-to-date
301
- const targetPluginJson = path.join(pluginTargetDir, 'openclaw.plugin.json');
302
- if (fs.existsSync(targetPluginJson)) {
303
- try {
304
- const existing = JSON.parse(fs.readFileSync(targetPluginJson, 'utf8'));
305
- if (existing.version === version) {
306
- console.log(`[AICQ] Plugin already installed at ${pluginTargetDir} (v${version})`);
307
- pluginInstalled = true;
308
- }
309
- } catch (e) {}
310
- }
311
-
312
- if (!pluginInstalled) {
313
- console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
314
- console.log(`[AICQ] Installing plugin to ${pluginTargetDir}...`);
315
-
316
- if (!fs.existsSync(pluginsDir)) {
317
- fs.mkdirSync(pluginsDir, { recursive: true });
318
- }
218
+ console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
219
+ console.log(`[AICQ] Installing plugin to ${pluginTargetDir}...`);
319
220
 
320
- installToDir(sourceDir, pluginTargetDir, version);
321
-
322
- // Install npm dependencies in target
323
- console.log('[AICQ] Installing plugin dependencies...');
324
- try {
325
- execSync('npm install --omit=dev', {
326
- cwd: pluginTargetDir,
327
- stdio: 'pipe',
328
- timeout: 120000,
329
- });
330
- console.log('[AICQ] Plugin dependencies installed.');
331
- } catch (e) {
332
- console.log('[AICQ] Warning: npm install failed. You may need to run manually:');
333
- console.log(` cd ${pluginTargetDir} && npm install`);
334
- }
221
+ if (!fs.existsSync(pluginsDir)) {
222
+ fs.mkdirSync(pluginsDir, { recursive: true });
223
+ }
335
224
 
336
- console.log(`[AICQ] Plugin installed to: ${pluginTargetDir}`);
337
- pluginInstalled = true;
225
+ installToDir(sourceDir, pluginTargetDir, version);
226
+
227
+ console.log('[AICQ] Installing plugin dependencies...');
228
+ try {
229
+ execSync('npm install --omit=dev', {
230
+ cwd: pluginTargetDir,
231
+ stdio: 'pipe',
232
+ timeout: 120000,
233
+ });
234
+ console.log('[AICQ] Plugin dependencies installed.');
235
+ } catch (e) {
236
+ console.log('[AICQ] Warning: npm install failed. You may need to run manually:');
237
+ console.log(` cd ${pluginTargetDir} && npm install`);
338
238
  }
239
+
240
+ console.log(`[AICQ] Plugin installed to: ${pluginTargetDir}`);
241
+ pluginInstalled = true;
339
242
  }
340
243
 
341
- // ── Summary ────────────────────────────────────────────────────
342
244
  if (!skillsInstalled && !pluginInstalled) {
343
245
  console.log('[AICQ] OpenClaw not found, skipping auto-install.');
344
- console.log('[AICQ] If you have OpenClaw installed, set OPENCLAW_HOME or OPENCLAW_WORKSPACE environment variable.');
345
- console.log('[AICQ] OPENCLAW_HOME=<openclaw-root-dir> (for plugins/ directory)');
346
- console.log('[AICQ] OPENCLAW_WORKSPACE=<workspace-dir> (for skills/ directory)');
246
+ console.log('[AICQ] Set OPENCLAW_HOME or OPENCLAW_WORKSPACE environment variable.');
347
247
  return false;
348
248
  }
349
249
 
350
- console.log('[AICQ] Restart OpenClaw to activate the plugin.');
250
+ console.log('[AICQ] Restart OpenClaw to activate the plugin (Channel mode).');
351
251
  return true;
352
252
  }
353
253
 
@@ -356,44 +256,38 @@ function uninstallFromOpenClaw() {
356
256
  const PLUGIN_ID = 'aicq-chat';
357
257
  let removed = false;
358
258
 
359
- // Remove from skills/ directory
360
259
  const workspace = findOpenClawWorkspace();
361
260
  if (workspace) {
362
261
  const skillDir = path.join(workspace, 'skills', PLUGIN_ID);
363
262
  if (fs.existsSync(skillDir)) {
364
263
  console.log(`[AICQ] Removing skill from ${skillDir}...`);
365
264
  fs.rmSync(skillDir, { recursive: true, force: true });
366
- console.log('[AICQ] Skill removed.');
367
265
  removed = true;
368
266
  }
369
267
  }
370
268
 
371
- // Remove from plugins/ directory
372
269
  const openclawDir = findOpenClawDir();
373
270
  if (openclawDir) {
374
271
  const pluginDir = path.join(openclawDir, 'plugins', PLUGIN_ID);
375
272
  if (fs.existsSync(pluginDir)) {
376
273
  console.log(`[AICQ] Removing plugin from ${pluginDir}...`);
377
274
  fs.rmSync(pluginDir, { recursive: true, force: true });
378
- console.log('[AICQ] Plugin removed.');
379
275
  removed = true;
380
276
  }
381
277
  }
382
278
 
383
279
  if (!removed) {
384
280
  console.log('[AICQ] AICQ plugin not found in any OpenClaw directory.');
385
- console.log('[AICQ] Nothing to uninstall.');
386
281
  } else {
387
282
  console.log('[AICQ] Restart OpenClaw to complete the uninstall.');
388
283
  }
389
-
390
284
  return removed;
391
285
  }
392
286
 
393
287
  // ── Help ────────────────────────────────────────────────────────────
394
288
  if (command === '--help' || command === '-h') {
395
289
  console.log(`
396
- AICQ Chat Plugin — End-to-End Encrypted Chat for OpenClaw
290
+ AICQ Chat Plugin v3.0 — End-to-End Encrypted Chat for OpenClaw (Channel)
397
291
 
398
292
  Usage:
399
293
  openclaw plugins install npm:aicq-chat-plugin Install plugin via openclaw CLI
@@ -402,63 +296,35 @@ Usage:
402
296
  aicq-plugin [command] [options] Advanced usage
403
297
 
404
298
  Commands:
405
- start Install to OpenClaw (if needed) and start plugin server (default)
299
+ start Install to OpenClaw (if needed) and start in standalone mode (default)
406
300
  install Install plugin to OpenClaw only (don't start server)
407
301
  uninstall Remove plugin from OpenClaw (skills/ and plugins/)
408
302
  status Check if the plugin is running
409
303
 
410
304
  Options:
411
- --port, -p <port> Plugin server port (default: 6109)
412
305
  --server, -s <url> AICQ server URL (default: https://aicq.online)
413
306
  --help, -h Show this help message
414
307
 
415
308
  Environment Variables:
416
- AICQ_PORT Plugin server port
417
309
  AICQ_SERVER_URL AICQ server URL
418
310
  AICQ_DATA_DIR Data directory (default: ~/.aicq-plugin)
419
311
  OPENCLAW_HOME OpenClaw installation directory (for plugins/)
420
312
  OPENCLAW_WORKSPACE OpenClaw workspace directory (for skills/)
421
313
 
422
- Examples:
423
- openclaw plugins install npm:aicq-chat-plugin # Install via openclaw CLI
424
- openclaw gateway restart # Restart gateway
425
- aicq-plugin # Start on default port
426
- aicq-plugin install # Install to OpenClaw only
427
- aicq-plugin uninstall # Remove from OpenClaw
428
- aicq-plugin --port 8080 # Start on port 8080
429
- aicq-plugin -s http://localhost # Connect to local server
314
+ Architecture:
315
+ v3.0 uses Channel architecture runs in-process with OpenClaw.
316
+ No independent port needed. UI served via Gateway HTTP routes.
317
+ - UI: /plugins/aicq-chat/ui/
318
+ - API: /plugins/aicq-chat/api/*
430
319
  `);
431
320
  process.exit(0);
432
321
  }
433
322
 
434
323
  // ── Status ──────────────────────────────────────────────────────────
435
324
  if (command === 'status') {
436
- const http = require('http');
437
- const req = http.get(`http://localhost:${port}/api/status`, (res) => {
438
- let data = '';
439
- res.on('data', chunk => data += chunk);
440
- res.on('end', () => {
441
- try {
442
- const status = JSON.parse(data);
443
- console.log('AICQ Plugin Status:');
444
- console.log(` Version: ${status.version}`);
445
- console.log(` Status: ${status.status}`);
446
- console.log(` Connected: ${status.connected ? 'Yes' : 'No'}`);
447
- console.log(` Agent: ${status.currentAgent || 'None'}`);
448
- console.log(` Server: ${status.serverUrl}`);
449
- } catch (e) {
450
- console.log('Plugin is running but returned invalid status.');
451
- }
452
- });
453
- });
454
- req.on('error', () => {
455
- console.log(`AICQ Plugin is not running on port ${port}.`);
456
- console.log(`Start it with: openclaw plugins install npm:aicq-chat-plugin`);
457
- });
458
- req.setTimeout(3000, () => {
459
- req.destroy();
460
- console.log(`AICQ Plugin is not responding on port ${port}.`);
461
- });
325
+ console.log('AICQ Plugin v3.0 (Channel architecture)');
326
+ console.log('In Channel mode, the plugin runs inside OpenClaw process.');
327
+ console.log('Check OpenClaw gateway status for plugin health.');
462
328
  process.exit(0);
463
329
  }
464
330
 
@@ -474,17 +340,17 @@ if (command === 'uninstall' || command === 'remove') {
474
340
  process.exit(0);
475
341
  }
476
342
 
477
- // ── Start (default) — auto-install then run ─────────────────────────
343
+ // ── Start (default) — auto-install then run in standalone mode ──────
478
344
  installToOpenClaw();
479
345
 
480
- console.log(`[AICQ] Starting plugin on port ${port}`);
346
+ console.log(`[AICQ] Starting plugin in standalone mode`);
481
347
  console.log(`[AICQ] Server: ${serverUrl}`);
482
348
 
483
- const env = { ...process.env, AICQ_PORT: port, AICQ_SERVER_URL: serverUrl };
349
+ const env = { ...process.env, AICQ_SERVER_URL: serverUrl };
484
350
  const child = spawn('node', [path.join(__dirname, 'index.js')], {
485
351
  env,
486
352
  stdio: 'inherit',
487
- detached: false
353
+ detached: false,
488
354
  });
489
355
 
490
356
  child.on('error', (err) => {