aicq-chat-plugin 3.8.1 → 3.9.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.cjs CHANGED
@@ -1,356 +1,356 @@
1
- #!/usr/bin/env node
2
- /**
3
- * AICQ Chat Plugin — CLI Entry Point (v3.0 Channel)
4
- *
5
- * Usage:
6
- * openclaw plugins install npm:aicq-chat-plugin Install via openclaw CLI
7
- * openclaw plugins uninstall aicq-chat Uninstall old version
8
- * openclaw gateway restart Restart gateway
9
- * aicq-plugin Start plugin server (standalone mode)
10
- * aicq-plugin start Start the plugin server (standalone mode)
11
- * aicq-plugin install Install plugin to OpenClaw only
12
- * aicq-plugin uninstall Remove plugin from OpenClaw
13
- * aicq-plugin status Check plugin status
14
- * aicq-plugin --server <url> Specify AICQ server URL
15
- * aicq-plugin --help Show help
16
- */
17
- const { spawn, execSync } = require('child_process');
18
- const fs = require('fs');
19
- const path = require('path');
20
- const os = require('os');
21
-
22
- const args = process.argv.slice(2);
23
- const command = args[0] || 'start';
24
-
25
- // Parse options
26
- let serverUrl = process.env.AICQ_SERVER_URL || 'https://aicq.online';
27
-
28
- for (let i = 0; i < args.length; i++) {
29
- if ((args[i] === '--server' || args[i] === '-s') && args[i + 1]) {
30
- serverUrl = args[i + 1];
31
- i++;
32
- }
33
- }
34
-
35
- // ── SKILL.md template ─────────────────────────────────────────────
36
- const SKILL_MD_TEMPLATE = `---
37
- name: aicq-chat
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
39
- license: MIT
40
- metadata:
41
- author: AICQ
42
- version: "{VERSION}"
43
- ---
44
-
45
- # AICQ Encrypted Chat
46
-
47
- AICQ 是一个端到端加密聊天频道插件,基于 OpenClaw Channel 架构。支持好友管理、群组聊天、文件传输和 AI Agent 通信。
48
-
49
- ## 一键启动
50
-
51
- \`\`\`bash
52
- openclaw plugins install npm:aicq-chat-plugin
53
- openclaw gateway restart
54
- \`\`\`
55
-
56
- ## 功能特性
57
-
58
- - **端到端加密 (E2EE)** — 基于 NaCl (libsodium) 的加密体系
59
- - **Channel 架构** — 进程内运行,无独立端口
60
- - **好友管理** — 好友码添加、QR 码扫描
61
- - **群组聊天** — 创建群组、邀请成员
62
- - **流式消息** — 支持 AI 流式输出
63
-
64
- ## UI 路由
65
-
66
- - /plugins/aicq-chat/ui/ — 聊天界面
67
- - /plugins/aicq-chat/api/* — API 端点
68
- `;
69
-
70
- // ── Find OpenClaw installation ──────────────────────────────────────
71
- function findOpenClawDir() {
72
- const candidates = [
73
- path.join(os.homedir(), '.openclaw'),
74
- path.join(os.homedir(), 'openclaw'),
75
- path.join(os.homedir(), '.config', 'openclaw'),
76
- ];
77
- if (process.env.OPENCLAW_HOME) {
78
- candidates.unshift(process.env.OPENCLAW_HOME);
79
- }
80
- for (const dir of candidates) {
81
- if (fs.existsSync(dir)) return dir;
82
- }
83
- return null;
84
- }
85
-
86
- // ── Find OpenClaw workspace ────────────────────────────────────────
87
- function findOpenClawWorkspace() {
88
- if (process.env.OPENCLAW_WORKSPACE) {
89
- return process.env.OPENCLAW_WORKSPACE;
90
- }
91
- const home = os.homedir();
92
- const candidates = [
93
- process.cwd(),
94
- path.join(home, 'my-project'),
95
- path.join(home, 'openclaw'),
96
- path.join(home, '.openclaw'),
97
- ];
98
- for (const dir of candidates) {
99
- const skillsDir = path.join(dir, 'skills');
100
- if (fs.existsSync(skillsDir)) {
101
- return dir;
102
- }
103
- }
104
- let current = process.cwd();
105
- for (let i = 0; i < 3; i++) {
106
- const skillsDir = path.join(current, 'skills');
107
- if (fs.existsSync(skillsDir)) {
108
- return current;
109
- }
110
- current = path.dirname(current);
111
- }
112
- const openclawDir = findOpenClawDir();
113
- if (openclawDir) {
114
- return openclawDir;
115
- }
116
- return null;
117
- }
118
-
119
- // ── Recursively copy a directory ────────────────────────────────────
120
- function copyDirRecursive(src, dest) {
121
- fs.mkdirSync(dest, { recursive: true });
122
- const entries = fs.readdirSync(src, { withFileTypes: true });
123
- for (const entry of entries) {
124
- const srcPath = path.join(src, entry.name);
125
- const destPath = path.join(dest, entry.name);
126
- if (entry.isDirectory()) {
127
- if (entry.name === 'node_modules') continue;
128
- copyDirRecursive(srcPath, destPath);
129
- } else {
130
- fs.copyFileSync(srcPath, destPath);
131
- }
132
- }
133
- }
134
-
135
- // ── Install plugin files to a target directory ─────────────────────
136
- function installToDir(sourceDir, targetDir, version) {
137
- const filesToCopy = [
138
- 'index.js', 'setup-entry.js', 'cli.cjs', 'postinstall.cjs',
139
- 'openclaw.plugin.json', 'package.json', 'README.md',
140
- ];
141
- const dirsToCopy = ['lib', 'src', 'public'];
142
-
143
- if (!fs.existsSync(targetDir)) {
144
- fs.mkdirSync(targetDir, { recursive: true });
145
- }
146
-
147
- for (const file of filesToCopy) {
148
- const src = path.join(sourceDir, file);
149
- const dest = path.join(targetDir, file);
150
- if (fs.existsSync(src)) {
151
- fs.copyFileSync(src, dest);
152
- }
153
- }
154
-
155
- for (const dir of dirsToCopy) {
156
- const src = path.join(sourceDir, dir);
157
- const dest = path.join(targetDir, dir);
158
- if (fs.existsSync(src)) {
159
- if (fs.existsSync(dest)) {
160
- fs.rmSync(dest, { recursive: true, force: true });
161
- }
162
- copyDirRecursive(src, dest);
163
- }
164
- }
165
-
166
- const skillMd = SKILL_MD_TEMPLATE.replace('{VERSION}', version);
167
- fs.writeFileSync(path.join(targetDir, 'SKILL.md'), skillMd, 'utf8');
168
- }
169
-
170
- // ── Install to OpenClaw ────────────────────────────────────────────
171
- function installToOpenClaw() {
172
- const PLUGIN_ID = 'aicq-chat';
173
- const sourceDir = path.resolve(__dirname);
174
- let version = '3.0.0';
175
-
176
- try {
177
- const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, 'package.json'), 'utf8'));
178
- version = pkg.version;
179
- } catch (e) {}
180
-
181
- let skillsInstalled = false;
182
- const workspace = findOpenClawWorkspace();
183
- if (workspace) {
184
- const skillsDir = path.join(workspace, 'skills');
185
- const skillTargetDir = path.join(skillsDir, PLUGIN_ID);
186
- console.log(`[AICQ] Found workspace at: ${workspace}`);
187
- console.log(`[AICQ] Installing skill to ${skillTargetDir}...`);
188
-
189
- if (!fs.existsSync(skillsDir)) {
190
- fs.mkdirSync(skillsDir, { recursive: true });
191
- }
192
-
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`);
206
- }
207
-
208
- console.log(`[AICQ] Skill installed to: ${skillTargetDir}`);
209
- skillsInstalled = true;
210
- }
211
-
212
- let pluginInstalled = false;
213
- const openclawDir = findOpenClawDir();
214
- if (openclawDir) {
215
- const pluginsDir = path.join(openclawDir, 'plugins');
216
- const pluginTargetDir = path.join(pluginsDir, PLUGIN_ID);
217
-
218
- console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
219
- console.log(`[AICQ] Installing plugin to ${pluginTargetDir}...`);
220
-
221
- if (!fs.existsSync(pluginsDir)) {
222
- fs.mkdirSync(pluginsDir, { recursive: true });
223
- }
224
-
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`);
238
- }
239
-
240
- console.log(`[AICQ] Plugin installed to: ${pluginTargetDir}`);
241
- pluginInstalled = true;
242
- }
243
-
244
- if (!skillsInstalled && !pluginInstalled) {
245
- console.log('[AICQ] OpenClaw not found, skipping auto-install.');
246
- console.log('[AICQ] Set OPENCLAW_HOME or OPENCLAW_WORKSPACE environment variable.');
247
- return false;
248
- }
249
-
250
- console.log('[AICQ] Restart OpenClaw to activate the plugin (Channel mode).');
251
- return true;
252
- }
253
-
254
- // ── Uninstall from OpenClaw ─────────────────────────────────────────
255
- function uninstallFromOpenClaw() {
256
- const PLUGIN_ID = 'aicq-chat';
257
- let removed = false;
258
-
259
- const workspace = findOpenClawWorkspace();
260
- if (workspace) {
261
- const skillDir = path.join(workspace, 'skills', PLUGIN_ID);
262
- if (fs.existsSync(skillDir)) {
263
- console.log(`[AICQ] Removing skill from ${skillDir}...`);
264
- fs.rmSync(skillDir, { recursive: true, force: true });
265
- removed = true;
266
- }
267
- }
268
-
269
- const openclawDir = findOpenClawDir();
270
- if (openclawDir) {
271
- const pluginDir = path.join(openclawDir, 'plugins', PLUGIN_ID);
272
- if (fs.existsSync(pluginDir)) {
273
- console.log(`[AICQ] Removing plugin from ${pluginDir}...`);
274
- fs.rmSync(pluginDir, { recursive: true, force: true });
275
- removed = true;
276
- }
277
- }
278
-
279
- if (!removed) {
280
- console.log('[AICQ] AICQ plugin not found in any OpenClaw directory.');
281
- } else {
282
- console.log('[AICQ] Restart OpenClaw to complete the uninstall.');
283
- }
284
- return removed;
285
- }
286
-
287
- // ── Help ────────────────────────────────────────────────────────────
288
- if (command === '--help' || command === '-h') {
289
- console.log(`
290
- AICQ Chat Plugin v3.2 — End-to-End Encrypted Chat for OpenClaw (Channel SDK)
291
-
292
- Usage:
293
- openclaw plugins install npm:aicq-chat-plugin Install plugin via openclaw CLI
294
- openclaw plugins uninstall aicq-chat Uninstall old version
295
- openclaw gateway restart Restart gateway after install
296
- aicq-plugin [command] [options] Advanced usage
297
-
298
- Commands:
299
- start Install to OpenClaw (if needed) (default)
300
- install Install plugin to OpenClaw only
301
- uninstall Remove plugin from OpenClaw (skills/ and plugins/)
302
- status Check if the plugin is running
303
-
304
- Options:
305
- --server, -s <url> AICQ server URL (default: https://aicq.online)
306
- --help, -h Show this help message
307
-
308
- Environment Variables:
309
- AICQ_SERVER_URL AICQ server URL
310
- AICQ_DATA_DIR Data directory (default: ~/.aicq-plugin)
311
- OPENCLAW_HOME OpenClaw installation directory (for plugins/)
312
- OPENCLAW_WORKSPACE OpenClaw workspace directory (for skills/)
313
-
314
- Architecture:
315
- v3.2 uses official Channel Plugin SDK (defineChannelPluginEntry).
316
- Runs in-process with OpenClaw — no standalone server needed.
317
- UI served via Gateway HTTP routes.
318
- - UI: /plugins/aicq-chat/ui/
319
- - API: /plugins/aicq-chat/api/*
320
- `);
321
- process.exit(0);
322
- }
323
-
324
- // ── Status ──────────────────────────────────────────────────────────
325
- if (command === 'status') {
326
- console.log('AICQ Plugin v3.2 (Channel SDK architecture)');
327
- console.log('Uses defineChannelPluginEntry — runs in-process with OpenClaw.');
328
- console.log('Check OpenClaw gateway status for plugin health.');
329
- process.exit(0);
330
- }
331
-
332
- // ── Install only ────────────────────────────────────────────────────
333
- if (command === 'install') {
334
- installToOpenClaw();
335
- process.exit(0);
336
- }
337
-
338
- // ── Uninstall ───────────────────────────────────────────────────────
339
- if (command === 'uninstall' || command === 'remove') {
340
- uninstallFromOpenClaw();
341
- process.exit(0);
342
- }
343
-
344
- // ── Start (default) — auto-install then inform user ───────────────
345
- installToOpenClaw();
346
-
347
- console.log('');
348
- console.log('[AICQ] Channel Plugin uses OpenClaw Channel SDK (defineChannelPluginEntry).');
349
- console.log('[AICQ] It runs in-process with OpenClaw — no standalone server needed.');
350
- console.log('[AICQ] To activate:');
351
- console.log(' openclaw plugins install npm:aicq-chat-plugin');
352
- console.log(' openclaw gateway restart');
353
- console.log('');
354
- console.log(`[AICQ] Server: ${serverUrl}`);
355
- console.log('[AICQ] UI: /plugins/aicq-chat/ui/');
356
- console.log('[AICQ] API: /plugins/aicq-chat/api/*');
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AICQ Chat Plugin — CLI Entry Point (v3.0 Channel)
4
+ *
5
+ * Usage:
6
+ * openclaw plugins install npm:aicq-chat-plugin Install via openclaw CLI
7
+ * openclaw plugins uninstall aicq-chat Uninstall old version
8
+ * openclaw gateway restart Restart gateway
9
+ * aicq-plugin Start plugin server (standalone mode)
10
+ * aicq-plugin start Start the plugin server (standalone mode)
11
+ * aicq-plugin install Install plugin to OpenClaw only
12
+ * aicq-plugin uninstall Remove plugin from OpenClaw
13
+ * aicq-plugin status Check plugin status
14
+ * aicq-plugin --server <url> Specify AICQ server URL
15
+ * aicq-plugin --help Show help
16
+ */
17
+ const { spawn, execSync } = require('child_process');
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const os = require('os');
21
+
22
+ const args = process.argv.slice(2);
23
+ const command = args[0] || 'start';
24
+
25
+ // Parse options
26
+ let serverUrl = process.env.AICQ_SERVER_URL || 'https://aicq.online';
27
+
28
+ for (let i = 0; i < args.length; i++) {
29
+ if ((args[i] === '--server' || args[i] === '-s') && args[i + 1]) {
30
+ serverUrl = args[i + 1];
31
+ i++;
32
+ }
33
+ }
34
+
35
+ // ── SKILL.md template ─────────────────────────────────────────────
36
+ const SKILL_MD_TEMPLATE = `---
37
+ name: aicq-chat
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
39
+ license: MIT
40
+ metadata:
41
+ author: AICQ
42
+ version: "{VERSION}"
43
+ ---
44
+
45
+ # AICQ Encrypted Chat
46
+
47
+ AICQ 是一个端到端加密聊天频道插件,基于 OpenClaw Channel 架构。支持好友管理、群组聊天、文件传输和 AI Agent 通信。
48
+
49
+ ## 一键启动
50
+
51
+ \`\`\`bash
52
+ openclaw plugins install npm:aicq-chat-plugin
53
+ openclaw gateway restart
54
+ \`\`\`
55
+
56
+ ## 功能特性
57
+
58
+ - **端到端加密 (E2EE)** — 基于 NaCl (libsodium) 的加密体系
59
+ - **Channel 架构** — 进程内运行,无独立端口
60
+ - **好友管理** — 好友码添加、QR 码扫描
61
+ - **群组聊天** — 创建群组、邀请成员
62
+ - **流式消息** — 支持 AI 流式输出
63
+
64
+ ## UI 路由
65
+
66
+ - /plugins/aicq-chat/ui/ — 聊天界面
67
+ - /plugins/aicq-chat/api/* — API 端点
68
+ `;
69
+
70
+ // ── Find OpenClaw installation ──────────────────────────────────────
71
+ function findOpenClawDir() {
72
+ const candidates = [
73
+ path.join(os.homedir(), '.openclaw'),
74
+ path.join(os.homedir(), 'openclaw'),
75
+ path.join(os.homedir(), '.config', 'openclaw'),
76
+ ];
77
+ if (process.env.OPENCLAW_HOME) {
78
+ candidates.unshift(process.env.OPENCLAW_HOME);
79
+ }
80
+ for (const dir of candidates) {
81
+ if (fs.existsSync(dir)) return dir;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ // ── Find OpenClaw workspace ────────────────────────────────────────
87
+ function findOpenClawWorkspace() {
88
+ if (process.env.OPENCLAW_WORKSPACE) {
89
+ return process.env.OPENCLAW_WORKSPACE;
90
+ }
91
+ const home = os.homedir();
92
+ const candidates = [
93
+ process.cwd(),
94
+ path.join(home, 'my-project'),
95
+ path.join(home, 'openclaw'),
96
+ path.join(home, '.openclaw'),
97
+ ];
98
+ for (const dir of candidates) {
99
+ const skillsDir = path.join(dir, 'skills');
100
+ if (fs.existsSync(skillsDir)) {
101
+ return dir;
102
+ }
103
+ }
104
+ let current = process.cwd();
105
+ for (let i = 0; i < 3; i++) {
106
+ const skillsDir = path.join(current, 'skills');
107
+ if (fs.existsSync(skillsDir)) {
108
+ return current;
109
+ }
110
+ current = path.dirname(current);
111
+ }
112
+ const openclawDir = findOpenClawDir();
113
+ if (openclawDir) {
114
+ return openclawDir;
115
+ }
116
+ return null;
117
+ }
118
+
119
+ // ── Recursively copy a directory ────────────────────────────────────
120
+ function copyDirRecursive(src, dest) {
121
+ fs.mkdirSync(dest, { recursive: true });
122
+ const entries = fs.readdirSync(src, { withFileTypes: true });
123
+ for (const entry of entries) {
124
+ const srcPath = path.join(src, entry.name);
125
+ const destPath = path.join(dest, entry.name);
126
+ if (entry.isDirectory()) {
127
+ if (entry.name === 'node_modules') continue;
128
+ copyDirRecursive(srcPath, destPath);
129
+ } else {
130
+ fs.copyFileSync(srcPath, destPath);
131
+ }
132
+ }
133
+ }
134
+
135
+ // ── Install plugin files to a target directory ─────────────────────
136
+ function installToDir(sourceDir, targetDir, version) {
137
+ const filesToCopy = [
138
+ 'index.js', 'setup-entry.js', 'cli.cjs', 'postinstall.cjs',
139
+ 'openclaw.plugin.json', 'package.json', 'README.md',
140
+ ];
141
+ const dirsToCopy = ['lib', 'src', 'public'];
142
+
143
+ if (!fs.existsSync(targetDir)) {
144
+ fs.mkdirSync(targetDir, { recursive: true });
145
+ }
146
+
147
+ for (const file of filesToCopy) {
148
+ const src = path.join(sourceDir, file);
149
+ const dest = path.join(targetDir, file);
150
+ if (fs.existsSync(src)) {
151
+ fs.copyFileSync(src, dest);
152
+ }
153
+ }
154
+
155
+ for (const dir of dirsToCopy) {
156
+ const src = path.join(sourceDir, dir);
157
+ const dest = path.join(targetDir, dir);
158
+ if (fs.existsSync(src)) {
159
+ if (fs.existsSync(dest)) {
160
+ fs.rmSync(dest, { recursive: true, force: true });
161
+ }
162
+ copyDirRecursive(src, dest);
163
+ }
164
+ }
165
+
166
+ const skillMd = SKILL_MD_TEMPLATE.replace('{VERSION}', version);
167
+ fs.writeFileSync(path.join(targetDir, 'SKILL.md'), skillMd, 'utf8');
168
+ }
169
+
170
+ // ── Install to OpenClaw ────────────────────────────────────────────
171
+ function installToOpenClaw() {
172
+ const PLUGIN_ID = 'aicq-chat';
173
+ const sourceDir = path.resolve(__dirname);
174
+ let version = '3.0.0';
175
+
176
+ try {
177
+ const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, 'package.json'), 'utf8'));
178
+ version = pkg.version;
179
+ } catch (e) {}
180
+
181
+ let skillsInstalled = false;
182
+ const workspace = findOpenClawWorkspace();
183
+ if (workspace) {
184
+ const skillsDir = path.join(workspace, 'skills');
185
+ const skillTargetDir = path.join(skillsDir, PLUGIN_ID);
186
+ console.log(`[AICQ] Found workspace at: ${workspace}`);
187
+ console.log(`[AICQ] Installing skill to ${skillTargetDir}...`);
188
+
189
+ if (!fs.existsSync(skillsDir)) {
190
+ fs.mkdirSync(skillsDir, { recursive: true });
191
+ }
192
+
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`);
206
+ }
207
+
208
+ console.log(`[AICQ] Skill installed to: ${skillTargetDir}`);
209
+ skillsInstalled = true;
210
+ }
211
+
212
+ let pluginInstalled = false;
213
+ const openclawDir = findOpenClawDir();
214
+ if (openclawDir) {
215
+ const pluginsDir = path.join(openclawDir, 'plugins');
216
+ const pluginTargetDir = path.join(pluginsDir, PLUGIN_ID);
217
+
218
+ console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
219
+ console.log(`[AICQ] Installing plugin to ${pluginTargetDir}...`);
220
+
221
+ if (!fs.existsSync(pluginsDir)) {
222
+ fs.mkdirSync(pluginsDir, { recursive: true });
223
+ }
224
+
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`);
238
+ }
239
+
240
+ console.log(`[AICQ] Plugin installed to: ${pluginTargetDir}`);
241
+ pluginInstalled = true;
242
+ }
243
+
244
+ if (!skillsInstalled && !pluginInstalled) {
245
+ console.log('[AICQ] OpenClaw not found, skipping auto-install.');
246
+ console.log('[AICQ] Set OPENCLAW_HOME or OPENCLAW_WORKSPACE environment variable.');
247
+ return false;
248
+ }
249
+
250
+ console.log('[AICQ] Restart OpenClaw to activate the plugin (Channel mode).');
251
+ return true;
252
+ }
253
+
254
+ // ── Uninstall from OpenClaw ─────────────────────────────────────────
255
+ function uninstallFromOpenClaw() {
256
+ const PLUGIN_ID = 'aicq-chat';
257
+ let removed = false;
258
+
259
+ const workspace = findOpenClawWorkspace();
260
+ if (workspace) {
261
+ const skillDir = path.join(workspace, 'skills', PLUGIN_ID);
262
+ if (fs.existsSync(skillDir)) {
263
+ console.log(`[AICQ] Removing skill from ${skillDir}...`);
264
+ fs.rmSync(skillDir, { recursive: true, force: true });
265
+ removed = true;
266
+ }
267
+ }
268
+
269
+ const openclawDir = findOpenClawDir();
270
+ if (openclawDir) {
271
+ const pluginDir = path.join(openclawDir, 'plugins', PLUGIN_ID);
272
+ if (fs.existsSync(pluginDir)) {
273
+ console.log(`[AICQ] Removing plugin from ${pluginDir}...`);
274
+ fs.rmSync(pluginDir, { recursive: true, force: true });
275
+ removed = true;
276
+ }
277
+ }
278
+
279
+ if (!removed) {
280
+ console.log('[AICQ] AICQ plugin not found in any OpenClaw directory.');
281
+ } else {
282
+ console.log('[AICQ] Restart OpenClaw to complete the uninstall.');
283
+ }
284
+ return removed;
285
+ }
286
+
287
+ // ── Help ────────────────────────────────────────────────────────────
288
+ if (command === '--help' || command === '-h') {
289
+ console.log(`
290
+ AICQ Chat Plugin v3.2 — End-to-End Encrypted Chat for OpenClaw (Channel SDK)
291
+
292
+ Usage:
293
+ openclaw plugins install npm:aicq-chat-plugin Install plugin via openclaw CLI
294
+ openclaw plugins uninstall aicq-chat Uninstall old version
295
+ openclaw gateway restart Restart gateway after install
296
+ aicq-plugin [command] [options] Advanced usage
297
+
298
+ Commands:
299
+ start Install to OpenClaw (if needed) (default)
300
+ install Install plugin to OpenClaw only
301
+ uninstall Remove plugin from OpenClaw (skills/ and plugins/)
302
+ status Check if the plugin is running
303
+
304
+ Options:
305
+ --server, -s <url> AICQ server URL (default: https://aicq.online)
306
+ --help, -h Show this help message
307
+
308
+ Environment Variables:
309
+ AICQ_SERVER_URL AICQ server URL
310
+ AICQ_DATA_DIR Data directory (default: ~/.aicq-plugin)
311
+ OPENCLAW_HOME OpenClaw installation directory (for plugins/)
312
+ OPENCLAW_WORKSPACE OpenClaw workspace directory (for skills/)
313
+
314
+ Architecture:
315
+ v3.2 uses official Channel Plugin SDK (defineChannelPluginEntry).
316
+ Runs in-process with OpenClaw — no standalone server needed.
317
+ UI served via Gateway HTTP routes.
318
+ - UI: /plugins/aicq-chat/ui/
319
+ - API: /plugins/aicq-chat/api/*
320
+ `);
321
+ process.exit(0);
322
+ }
323
+
324
+ // ── Status ──────────────────────────────────────────────────────────
325
+ if (command === 'status') {
326
+ console.log('AICQ Plugin v3.2 (Channel SDK architecture)');
327
+ console.log('Uses defineChannelPluginEntry — runs in-process with OpenClaw.');
328
+ console.log('Check OpenClaw gateway status for plugin health.');
329
+ process.exit(0);
330
+ }
331
+
332
+ // ── Install only ────────────────────────────────────────────────────
333
+ if (command === 'install') {
334
+ installToOpenClaw();
335
+ process.exit(0);
336
+ }
337
+
338
+ // ── Uninstall ───────────────────────────────────────────────────────
339
+ if (command === 'uninstall' || command === 'remove') {
340
+ uninstallFromOpenClaw();
341
+ process.exit(0);
342
+ }
343
+
344
+ // ── Start (default) — auto-install then inform user ───────────────
345
+ installToOpenClaw();
346
+
347
+ console.log('');
348
+ console.log('[AICQ] Channel Plugin uses OpenClaw Channel SDK (defineChannelPluginEntry).');
349
+ console.log('[AICQ] It runs in-process with OpenClaw — no standalone server needed.');
350
+ console.log('[AICQ] To activate:');
351
+ console.log(' openclaw plugins install npm:aicq-chat-plugin');
352
+ console.log(' openclaw gateway restart');
353
+ console.log('');
354
+ console.log(`[AICQ] Server: ${serverUrl}`);
355
+ console.log('[AICQ] UI: /plugins/aicq-chat/ui/');
356
+ console.log('[AICQ] API: /plugins/aicq-chat/api/*');