aicq-chat-plugin 2.4.1 → 2.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/cli.js +150 -12
- package/lib/crypto.js +14 -2
- package/lib/server-client.js +13 -5
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/postinstall.js +178 -17
package/cli.js
CHANGED
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
* AICQ Chat Plugin — CLI Entry Point
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* aicq-plugin Start
|
|
7
|
-
* aicq-plugin
|
|
8
|
-
* aicq-plugin
|
|
9
|
-
* aicq-plugin
|
|
10
|
-
* aicq-plugin
|
|
6
|
+
* npx aicq-chat-plugin Start plugin + auto-install to OpenClaw
|
|
7
|
+
* aicq-plugin Same as above
|
|
8
|
+
* aicq-plugin start Start the plugin server
|
|
9
|
+
* aicq-plugin install Install plugin to OpenClaw only
|
|
10
|
+
* aicq-plugin status Check plugin status
|
|
11
|
+
* aicq-plugin --port <port> Specify port (default 6109)
|
|
12
|
+
* aicq-plugin --help Show help
|
|
11
13
|
*/
|
|
12
|
-
const { spawn } = require('child_process');
|
|
14
|
+
const { spawn, execSync } = require('child_process');
|
|
15
|
+
const fs = require('fs');
|
|
13
16
|
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
14
18
|
|
|
15
19
|
const args = process.argv.slice(2);
|
|
16
20
|
const command = args[0] || 'start';
|
|
@@ -30,15 +34,137 @@ for (let i = 0; i < args.length; i++) {
|
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
// ── Find OpenClaw installation ──────────────────────────────────────
|
|
38
|
+
function findOpenClawDir() {
|
|
39
|
+
const candidates = [
|
|
40
|
+
path.join(os.homedir(), '.openclaw'),
|
|
41
|
+
path.join(os.homedir(), 'openclaw'),
|
|
42
|
+
path.join(os.homedir(), '.config', 'openclaw'),
|
|
43
|
+
];
|
|
44
|
+
if (process.env.OPENCLAW_HOME) {
|
|
45
|
+
candidates.unshift(process.env.OPENCLAW_HOME);
|
|
46
|
+
}
|
|
47
|
+
for (const dir of candidates) {
|
|
48
|
+
if (fs.existsSync(dir)) return dir;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Copy plugin files to OpenClaw plugins directory ─────────────────
|
|
54
|
+
function installToOpenClaw() {
|
|
55
|
+
const openclawDir = findOpenClawDir();
|
|
56
|
+
if (!openclawDir) {
|
|
57
|
+
console.log('[AICQ] OpenClaw not found, skipping auto-install.');
|
|
58
|
+
console.log('[AICQ] If you have OpenClaw installed, set OPENCLAW_HOME environment variable.');
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const PLUGIN_ID = 'aicq-chat';
|
|
63
|
+
const pluginsDir = path.join(openclawDir, 'plugins');
|
|
64
|
+
const targetDir = path.join(pluginsDir, PLUGIN_ID);
|
|
65
|
+
const sourceDir = path.resolve(__dirname);
|
|
66
|
+
|
|
67
|
+
// Check if already installed and up-to-date
|
|
68
|
+
const targetPluginJson = path.join(targetDir, 'openclaw.plugin.json');
|
|
69
|
+
if (fs.existsSync(targetPluginJson)) {
|
|
70
|
+
try {
|
|
71
|
+
const existing = JSON.parse(fs.readFileSync(targetPluginJson, 'utf8'));
|
|
72
|
+
const current = JSON.parse(fs.readFileSync(path.join(sourceDir, 'openclaw.plugin.json'), 'utf8'));
|
|
73
|
+
if (existing.version === current.version) {
|
|
74
|
+
console.log(`[AICQ] Plugin already installed at ${targetDir} (v${current.version})`);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
|
|
81
|
+
console.log(`[AICQ] Installing plugin to ${targetDir}...`);
|
|
82
|
+
|
|
83
|
+
// Create plugins directory if needed
|
|
84
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
85
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Files and dirs to copy
|
|
89
|
+
const filesToCopy = [
|
|
90
|
+
'index.js', 'cli.js', 'postinstall.js',
|
|
91
|
+
'openclaw.plugin.json', 'package.json', 'README.md',
|
|
92
|
+
];
|
|
93
|
+
const dirsToCopy = ['lib', 'public'];
|
|
94
|
+
|
|
95
|
+
// Create target directory
|
|
96
|
+
if (!fs.existsSync(targetDir)) {
|
|
97
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Copy files
|
|
101
|
+
for (const file of filesToCopy) {
|
|
102
|
+
const src = path.join(sourceDir, file);
|
|
103
|
+
const dest = path.join(targetDir, file);
|
|
104
|
+
if (fs.existsSync(src)) {
|
|
105
|
+
fs.copyFileSync(src, dest);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Copy directories
|
|
110
|
+
for (const dir of dirsToCopy) {
|
|
111
|
+
const src = path.join(sourceDir, dir);
|
|
112
|
+
const dest = path.join(targetDir, dir);
|
|
113
|
+
if (fs.existsSync(src)) {
|
|
114
|
+
if (fs.existsSync(dest)) {
|
|
115
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
copyDirRecursive(src, dest);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Install npm dependencies in target
|
|
122
|
+
console.log('[AICQ] Installing plugin dependencies...');
|
|
123
|
+
try {
|
|
124
|
+
execSync('npm install --production', {
|
|
125
|
+
cwd: targetDir,
|
|
126
|
+
stdio: 'pipe',
|
|
127
|
+
timeout: 120000,
|
|
128
|
+
});
|
|
129
|
+
console.log('[AICQ] Dependencies installed.');
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.log('[AICQ] Warning: npm install failed. You may need to run manually:');
|
|
132
|
+
console.log(` cd ${targetDir} && npm install`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`[AICQ] Plugin installed to: ${targetDir}`);
|
|
136
|
+
console.log('[AICQ] Restart OpenClaw to activate the plugin.');
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Recursively copy a directory ────────────────────────────────────
|
|
141
|
+
function copyDirRecursive(src, dest) {
|
|
142
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
143
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
const srcPath = path.join(src, entry.name);
|
|
146
|
+
const destPath = path.join(dest, entry.name);
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
if (entry.name === 'node_modules') continue;
|
|
149
|
+
copyDirRecursive(srcPath, destPath);
|
|
150
|
+
} else {
|
|
151
|
+
fs.copyFileSync(srcPath, destPath);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Help ────────────────────────────────────────────────────────────
|
|
33
157
|
if (command === '--help' || command === '-h') {
|
|
34
158
|
console.log(`
|
|
35
159
|
AICQ Chat Plugin — End-to-End Encrypted Chat for OpenClaw
|
|
36
160
|
|
|
37
161
|
Usage:
|
|
162
|
+
npx aicq-chat-plugin Start plugin + auto-install to OpenClaw
|
|
38
163
|
aicq-plugin [command] [options]
|
|
39
164
|
|
|
40
165
|
Commands:
|
|
41
|
-
start
|
|
166
|
+
start Install to OpenClaw (if needed) and start plugin server (default)
|
|
167
|
+
install Install plugin to OpenClaw only (don't start server)
|
|
42
168
|
status Check if the plugin is running
|
|
43
169
|
|
|
44
170
|
Options:
|
|
@@ -50,15 +176,19 @@ Environment Variables:
|
|
|
50
176
|
AICQ_PORT Plugin server port
|
|
51
177
|
AICQ_SERVER_URL AICQ server URL
|
|
52
178
|
AICQ_DATA_DIR Data directory (default: ~/.aicq-plugin)
|
|
179
|
+
OPENCLAW_HOME OpenClaw installation directory
|
|
53
180
|
|
|
54
181
|
Examples:
|
|
55
|
-
aicq-plugin
|
|
56
|
-
aicq-plugin
|
|
57
|
-
aicq-plugin
|
|
182
|
+
npx aicq-chat-plugin # Install + start
|
|
183
|
+
aicq-plugin # Start on default port
|
|
184
|
+
aicq-plugin install # Install to OpenClaw only
|
|
185
|
+
aicq-plugin --port 8080 # Start on port 8080
|
|
186
|
+
aicq-plugin -s http://localhost # Connect to local server
|
|
58
187
|
`);
|
|
59
188
|
process.exit(0);
|
|
60
189
|
}
|
|
61
190
|
|
|
191
|
+
// ── Status ──────────────────────────────────────────────────────────
|
|
62
192
|
if (command === 'status') {
|
|
63
193
|
const http = require('http');
|
|
64
194
|
const req = http.get(`http://localhost:${port}/api/status`, (res) => {
|
|
@@ -80,7 +210,7 @@ if (command === 'status') {
|
|
|
80
210
|
});
|
|
81
211
|
req.on('error', () => {
|
|
82
212
|
console.log(`AICQ Plugin is not running on port ${port}.`);
|
|
83
|
-
console.log(`Start it with: aicq-plugin
|
|
213
|
+
console.log(`Start it with: npx aicq-chat-plugin`);
|
|
84
214
|
});
|
|
85
215
|
req.setTimeout(3000, () => {
|
|
86
216
|
req.destroy();
|
|
@@ -89,7 +219,15 @@ if (command === 'status') {
|
|
|
89
219
|
process.exit(0);
|
|
90
220
|
}
|
|
91
221
|
|
|
92
|
-
//
|
|
222
|
+
// ── Install only ────────────────────────────────────────────────────
|
|
223
|
+
if (command === 'install') {
|
|
224
|
+
installToOpenClaw();
|
|
225
|
+
process.exit(0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ── Start (default) — auto-install then run ─────────────────────────
|
|
229
|
+
installToOpenClaw();
|
|
230
|
+
|
|
93
231
|
console.log(`[AICQ] Starting plugin on port ${port}`);
|
|
94
232
|
console.log(`[AICQ] Server: ${serverUrl}`);
|
|
95
233
|
|
package/lib/crypto.js
CHANGED
|
@@ -31,7 +31,13 @@ function generateExchangeKeypair() {
|
|
|
31
31
|
|
|
32
32
|
function signMessage(message, secretKeyHex) {
|
|
33
33
|
const secretKey = Buffer.from(secretKeyHex, 'hex');
|
|
34
|
-
|
|
34
|
+
// If message looks like hex (64 chars), treat as raw bytes to match server's bytes.fromhex()
|
|
35
|
+
let messageBytes;
|
|
36
|
+
if (/^[0-9a-fA-F]{64}$/.test(message)) {
|
|
37
|
+
messageBytes = Buffer.from(message, 'hex');
|
|
38
|
+
} else {
|
|
39
|
+
messageBytes = naclUtil.decodeUTF8(message);
|
|
40
|
+
}
|
|
35
41
|
const signature = nacl.sign.detached(messageBytes, secretKey);
|
|
36
42
|
return Buffer.from(signature).toString('hex');
|
|
37
43
|
}
|
|
@@ -39,7 +45,13 @@ function signMessage(message, secretKeyHex) {
|
|
|
39
45
|
function verifySignature(message, signatureHex, publicKeyHex) {
|
|
40
46
|
try {
|
|
41
47
|
const publicKey = Buffer.from(publicKeyHex, 'hex');
|
|
42
|
-
|
|
48
|
+
// If message looks like hex (64 chars), treat as raw bytes to match server
|
|
49
|
+
let messageBytes;
|
|
50
|
+
if (/^[0-9a-fA-F]{64}$/.test(message)) {
|
|
51
|
+
messageBytes = Buffer.from(message, 'hex');
|
|
52
|
+
} else {
|
|
53
|
+
messageBytes = naclUtil.decodeUTF8(message);
|
|
54
|
+
}
|
|
43
55
|
const signature = Buffer.from(signatureHex, 'hex');
|
|
44
56
|
return nacl.sign.detached.verify(messageBytes, signature, publicKey);
|
|
45
57
|
} catch (e) {
|
package/lib/server-client.js
CHANGED
|
@@ -53,9 +53,13 @@ class ServerClient {
|
|
|
53
53
|
public_key: identity.signing_public_key,
|
|
54
54
|
agent_name: identity.nickname || agentId,
|
|
55
55
|
});
|
|
56
|
-
if (data.accessToken) {
|
|
57
|
-
this.jwtToken = data.accessToken;
|
|
56
|
+
if (data.access_token || data.accessToken) {
|
|
57
|
+
this.jwtToken = data.access_token || data.accessToken;
|
|
58
58
|
this.currentAgentId = agentId;
|
|
59
|
+
// Store server-side account ID for WS auth (nodeId must match JWT sub)
|
|
60
|
+
if (data.account && data.account.id) {
|
|
61
|
+
this.serverAccountId = data.account.id;
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
return data;
|
|
61
65
|
}
|
|
@@ -83,9 +87,13 @@ class ServerClient {
|
|
|
83
87
|
challenge,
|
|
84
88
|
});
|
|
85
89
|
|
|
86
|
-
if (loginData.accessToken) {
|
|
87
|
-
this.jwtToken = loginData.accessToken;
|
|
90
|
+
if (loginData.access_token || loginData.accessToken) {
|
|
91
|
+
this.jwtToken = loginData.access_token || loginData.accessToken;
|
|
88
92
|
this.currentAgentId = agentId;
|
|
93
|
+
// Store server-side account ID for WS auth (nodeId must match JWT sub)
|
|
94
|
+
if (loginData.account && loginData.account.id) {
|
|
95
|
+
this.serverAccountId = loginData.account.id;
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
return loginData;
|
|
91
99
|
}
|
|
@@ -198,7 +206,7 @@ class ServerClient {
|
|
|
198
206
|
console.log('[WS] Connected, sending auth...');
|
|
199
207
|
this.ws.send(JSON.stringify({
|
|
200
208
|
type: 'online',
|
|
201
|
-
nodeId: this.currentAgentId,
|
|
209
|
+
nodeId: this.serverAccountId || this.currentAgentId,
|
|
202
210
|
token: this.jwtToken,
|
|
203
211
|
}));
|
|
204
212
|
});
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/postinstall.js
CHANGED
|
@@ -2,26 +2,187 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* AICQ Chat Plugin — Post-install script
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Automatically installs the plugin into OpenClaw's plugins directory.
|
|
6
|
+
* OpenClaw requires plugins to be in ~/.openclaw/plugins/<plugin-id>/
|
|
7
|
+
* with an openclaw.plugin.json file to recognize them.
|
|
6
8
|
*/
|
|
7
9
|
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
const PLUGIN_ID = 'aicq-chat';
|
|
16
|
+
const PLUGIN_DIR = path.resolve(__dirname);
|
|
17
|
+
|
|
18
|
+
// ── Find OpenClaw installation ──────────────────────────────────────
|
|
19
|
+
function findOpenClawDir() {
|
|
20
|
+
const candidates = [
|
|
21
|
+
path.join(os.homedir(), '.openclaw'),
|
|
22
|
+
path.join(os.homedir(), 'openclaw'),
|
|
23
|
+
path.join(os.homedir(), '.config', 'openclaw'),
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Check environment variable
|
|
27
|
+
if (process.env.OPENCLAW_HOME) {
|
|
28
|
+
candidates.unshift(process.env.OPENCLAW_HOME);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const dir of candidates) {
|
|
32
|
+
if (fs.existsSync(dir)) {
|
|
33
|
+
return dir;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Copy plugin files to OpenClaw plugins directory ─────────────────
|
|
40
|
+
function installToOpenClaw(openclawDir) {
|
|
41
|
+
const pluginsDir = path.join(openclawDir, 'plugins');
|
|
42
|
+
const targetDir = path.join(pluginsDir, PLUGIN_ID);
|
|
43
|
+
|
|
44
|
+
// Create plugins directory if needed
|
|
45
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
46
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Files to copy (from package.json "files" field)
|
|
50
|
+
const filesToCopy = [
|
|
51
|
+
'index.js',
|
|
52
|
+
'cli.js',
|
|
53
|
+
'postinstall.js',
|
|
54
|
+
'openclaw.plugin.json',
|
|
55
|
+
'package.json',
|
|
56
|
+
'README.md',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const dirsToCopy = [
|
|
60
|
+
'lib',
|
|
61
|
+
'public',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// Remove old installation if exists
|
|
65
|
+
if (fs.existsSync(targetDir)) {
|
|
66
|
+
console.log(`[AICQ] Updating existing plugin at ${targetDir}`);
|
|
67
|
+
// Don't remove the entire dir — preserve node_modules and data
|
|
68
|
+
} else {
|
|
69
|
+
console.log(`[AICQ] Installing plugin to ${targetDir}`);
|
|
70
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Copy individual files
|
|
74
|
+
for (const file of filesToCopy) {
|
|
75
|
+
const src = path.join(PLUGIN_DIR, file);
|
|
76
|
+
const dest = path.join(targetDir, file);
|
|
77
|
+
if (fs.existsSync(src)) {
|
|
78
|
+
fs.copyFileSync(src, dest);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Copy directories
|
|
83
|
+
for (const dir of dirsToCopy) {
|
|
84
|
+
const src = path.join(PLUGIN_DIR, dir);
|
|
85
|
+
const dest = path.join(targetDir, dir);
|
|
86
|
+
if (fs.existsSync(src)) {
|
|
87
|
+
// Remove old and copy fresh
|
|
88
|
+
if (fs.existsSync(dest)) {
|
|
89
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
copyDirRecursive(src, dest);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Install npm dependencies in the target directory
|
|
96
|
+
console.log('[AICQ] Installing plugin dependencies...');
|
|
97
|
+
try {
|
|
98
|
+
execSync('npm install --production', {
|
|
99
|
+
cwd: targetDir,
|
|
100
|
+
stdio: 'pipe',
|
|
101
|
+
timeout: 120000,
|
|
102
|
+
});
|
|
103
|
+
console.log('[AICQ] Dependencies installed.');
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.log('[AICQ] Warning: npm install failed. You may need to run it manually:');
|
|
106
|
+
console.log(` cd ${targetDir} && npm install`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return targetDir;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Recursively copy a directory ────────────────────────────────────
|
|
113
|
+
function copyDirRecursive(src, dest) {
|
|
114
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
115
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
116
|
+
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const srcPath = path.join(src, entry.name);
|
|
119
|
+
const destPath = path.join(dest, entry.name);
|
|
120
|
+
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
// Skip node_modules — will be installed fresh
|
|
123
|
+
if (entry.name === 'node_modules') continue;
|
|
124
|
+
copyDirRecursive(srcPath, destPath);
|
|
125
|
+
} else {
|
|
126
|
+
fs.copyFileSync(srcPath, destPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── Main ────────────────────────────────────────────────────────────
|
|
8
132
|
console.log('');
|
|
9
133
|
console.log(' ╔══════════════════════════════════════════════╗');
|
|
10
|
-
console.log(' ║ AICQ Chat Plugin
|
|
11
|
-
console.log(' ╠══════════════════════════════════════════════╣');
|
|
12
|
-
console.log(' ║ ║');
|
|
13
|
-
console.log(' ║ Start the plugin: ║');
|
|
14
|
-
console.log(' ║ npx aicq-plugin ║');
|
|
15
|
-
console.log(' ║ ║');
|
|
16
|
-
console.log(' ║ Or install globally: ║');
|
|
17
|
-
console.log(' ║ npm install -g aicq-chat-plugin ║');
|
|
18
|
-
console.log(' ║ aicq-plugin ║');
|
|
19
|
-
console.log(' ║ ║');
|
|
20
|
-
console.log(' ║ Options: ║');
|
|
21
|
-
console.log(' ║ --port <port> Server port (6109) ║');
|
|
22
|
-
console.log(' ║ --server <url> AICQ server URL ║');
|
|
23
|
-
console.log(' ║ ║');
|
|
24
|
-
console.log(' ║ Chat UI: http://localhost:6109 ║');
|
|
25
|
-
console.log(' ║ Docs: https://aicq.online ║');
|
|
134
|
+
console.log(' ║ AICQ Chat Plugin — Installing... ║');
|
|
26
135
|
console.log(' ╚══════════════════════════════════════════════╝');
|
|
27
136
|
console.log('');
|
|
137
|
+
|
|
138
|
+
// Try to auto-install into OpenClaw
|
|
139
|
+
const openclawDir = findOpenClawDir();
|
|
140
|
+
|
|
141
|
+
if (openclawDir) {
|
|
142
|
+
console.log(`[AICQ] Found OpenClaw at: ${openclawDir}`);
|
|
143
|
+
try {
|
|
144
|
+
const installedDir = installToOpenClaw(openclawDir);
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log(' ╔══════════════════════════════════════════════╗');
|
|
147
|
+
console.log(' ║ AICQ Plugin Installed Successfully! ║');
|
|
148
|
+
console.log(' ╠══════════════════════════════════════════════╣');
|
|
149
|
+
console.log(' ║ ║');
|
|
150
|
+
console.log(' ║ Plugin installed to: ║');
|
|
151
|
+
console.log(` ║ ${installedDir}`);
|
|
152
|
+
console.log(' ║ ║');
|
|
153
|
+
console.log(' ║ Restart OpenClaw to activate the plugin. ║');
|
|
154
|
+
console.log(' ║ ║');
|
|
155
|
+
console.log(' ║ Chat UI: http://localhost:6109 ║');
|
|
156
|
+
console.log(' ║ Docs: https://aicq.online ║');
|
|
157
|
+
console.log(' ╚══════════════════════════════════════════════╝');
|
|
158
|
+
console.log('');
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.error('[AICQ] Auto-install failed:', e.message);
|
|
161
|
+
console.log('');
|
|
162
|
+
printManualInstructions();
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
console.log('[AICQ] OpenClaw not found — skipping auto-install.');
|
|
166
|
+
console.log('');
|
|
167
|
+
printManualInstructions();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printManualInstructions() {
|
|
171
|
+
console.log(' ╔══════════════════════════════════════════════╗');
|
|
172
|
+
console.log(' ║ AICQ Chat Plugin Installed! ║');
|
|
173
|
+
console.log(' ╠══════════════════════════════════════════════╣');
|
|
174
|
+
console.log(' ║ ║');
|
|
175
|
+
console.log(' ║ Manual install to OpenClaw: ║');
|
|
176
|
+
console.log(' ║ mkdir -p ~/.openclaw/plugins/aicq-chat ║');
|
|
177
|
+
console.log(' ║ cp -r ' + PLUGIN_DIR + '/* ~/.openclaw/plugins/aicq-chat/ ║');
|
|
178
|
+
console.log(' ║ cd ~/.openclaw/plugins/aicq-chat ║');
|
|
179
|
+
console.log(' ║ npm install ║');
|
|
180
|
+
console.log(' ║ ║');
|
|
181
|
+
console.log(' ║ Or start standalone: ║');
|
|
182
|
+
console.log(' ║ npx aicq-plugin ║');
|
|
183
|
+
console.log(' ║ ║');
|
|
184
|
+
console.log(' ║ Chat UI: http://localhost:6109 ║');
|
|
185
|
+
console.log(' ║ Docs: https://aicq.online ║');
|
|
186
|
+
console.log(' ╚══════════════════════════════════════════════╝');
|
|
187
|
+
console.log('');
|
|
188
|
+
}
|