commanderclaw 1.1.3
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 +57 -0
- package/bin/commanderclaw.cjs +77 -0
- package/binaries/darwin-arm64/coordination-server +0 -0
- package/dist/cli/config.js +116 -0
- package/dist/cli/gateway.js +250 -0
- package/dist/cli/index.js +53 -0
- package/dist/cli/plugin.js +400 -0
- package/dist/cli/utils.js +121 -0
- package/dist/plugin/client.d.ts +76 -0
- package/dist/plugin/client.d.ts.map +1 -0
- package/dist/plugin/config.d.ts +25 -0
- package/dist/plugin/config.d.ts.map +1 -0
- package/dist/plugin/index.cjs +4538 -0
- package/dist/plugin/index.cjs.map +1 -0
- package/dist/plugin/index.d.ts +15 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.mjs +4536 -0
- package/dist/plugin/index.mjs.map +1 -0
- package/dist/plugin/skills/commanderclaw/SKILL.md +177 -0
- package/dist/plugin/tools/command.d.ts +7 -0
- package/dist/plugin/tools/command.d.ts.map +1 -0
- package/dist/plugin/tools/command.ts +136 -0
- package/dist/plugin/tools/nodes.d.ts +7 -0
- package/dist/plugin/tools/nodes.d.ts.map +1 -0
- package/dist/plugin/tools/nodes.ts +71 -0
- package/dist/plugin/tools/status.d.ts +7 -0
- package/dist/plugin/tools/status.d.ts.map +1 -0
- package/dist/plugin/tools/status.ts +59 -0
- package/dist/plugin/tools/task.d.ts +7 -0
- package/dist/plugin/tools/task.d.ts.map +1 -0
- package/dist/plugin/tools/task.ts +254 -0
- package/dist/plugin/types.d.ts +133 -0
- package/dist/plugin/types.d.ts.map +1 -0
- package/openclaw.plugin.json +51 -0
- package/package.json +86 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { getPlatforms, findPlatform, rlWithDefault, maskToken, getTokenFromServer, backupFile, restoreBackup, DEFAULT_SERVER_URL, DEFAULT_DEVICE_NAME, } from './utils.js';
|
|
6
|
+
export function handlePlugin(args) {
|
|
7
|
+
if (args.length === 0) {
|
|
8
|
+
printPluginUsage();
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const cmd = args[0];
|
|
12
|
+
const platform = args[1];
|
|
13
|
+
switch (cmd) {
|
|
14
|
+
case 'install':
|
|
15
|
+
if (!platform) {
|
|
16
|
+
console.log('Error: platform name required');
|
|
17
|
+
console.log('Usage: commanderclaw plugin install <platform>');
|
|
18
|
+
printAvailablePlatforms();
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
handlePluginInstall(platform);
|
|
22
|
+
break;
|
|
23
|
+
case 'update':
|
|
24
|
+
if (!platform) {
|
|
25
|
+
console.log('Error: platform name required');
|
|
26
|
+
console.log('Usage: commanderclaw plugin update <platform>');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
handlePluginUpdate(platform);
|
|
30
|
+
break;
|
|
31
|
+
case 'uninstall':
|
|
32
|
+
if (!platform) {
|
|
33
|
+
console.log('Error: platform name required');
|
|
34
|
+
console.log('Usage: commanderclaw plugin uninstall <platform>');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
handlePluginUninstall(platform);
|
|
38
|
+
break;
|
|
39
|
+
case 'status':
|
|
40
|
+
handlePluginStatus();
|
|
41
|
+
break;
|
|
42
|
+
case 'list-platforms':
|
|
43
|
+
handleListPlatforms();
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
console.log(`Unknown plugin command: ${cmd}`);
|
|
47
|
+
printPluginUsage();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function printPluginUsage() {
|
|
52
|
+
console.log('Plugin management commands:');
|
|
53
|
+
console.log();
|
|
54
|
+
console.log('Usage:');
|
|
55
|
+
console.log(' commanderclaw plugin <command> [arguments]');
|
|
56
|
+
console.log();
|
|
57
|
+
console.log('Commands:');
|
|
58
|
+
console.log(' install <platform> Install plugin to specified platform');
|
|
59
|
+
console.log(' update <platform> Update installed plugin');
|
|
60
|
+
console.log(' uninstall <platform> Uninstall plugin from platform');
|
|
61
|
+
console.log(' status Show installation status');
|
|
62
|
+
console.log(' list-platforms List supported platforms');
|
|
63
|
+
console.log();
|
|
64
|
+
console.log('Supported platforms: qclaw, openclaw');
|
|
65
|
+
}
|
|
66
|
+
function printAvailablePlatforms() {
|
|
67
|
+
console.log('Available platforms:');
|
|
68
|
+
for (const p of getPlatforms()) {
|
|
69
|
+
const status = p.detectFunc() ? ' (installed)' : '';
|
|
70
|
+
console.log(` ${p.name} ${p.displayName}${status}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function handleListPlatforms() {
|
|
74
|
+
console.log('Supported platforms:');
|
|
75
|
+
console.log();
|
|
76
|
+
for (const p of getPlatforms()) {
|
|
77
|
+
const installed = p.detectFunc() ? 'installed' : 'not installed';
|
|
78
|
+
console.log(` ${p.name.padEnd(12)} ${p.displayName} (${installed})`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function handlePluginStatus() {
|
|
82
|
+
console.log('CommanderClaw Plugin Status');
|
|
83
|
+
console.log();
|
|
84
|
+
for (const p of getPlatforms()) {
|
|
85
|
+
console.log(`${p.displayName} (${p.name}):`);
|
|
86
|
+
if (!p.detectFunc()) {
|
|
87
|
+
console.log(' Status: not installed');
|
|
88
|
+
console.log();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const targetPath = path.join(p.extDir, 'commanderclaw');
|
|
92
|
+
if (fs.existsSync(targetPath)) {
|
|
93
|
+
const pkgPath = path.join(targetPath, 'package.json');
|
|
94
|
+
let version = 'unknown';
|
|
95
|
+
if (fs.existsSync(pkgPath)) {
|
|
96
|
+
try {
|
|
97
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
98
|
+
version = pkg.version || 'unknown';
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
101
|
+
}
|
|
102
|
+
console.log(` Status: installed (v${version})`);
|
|
103
|
+
console.log(` Path: ${targetPath}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(' Status: not installed');
|
|
107
|
+
}
|
|
108
|
+
console.log();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function handlePluginInstall(platformName) {
|
|
112
|
+
const platform = findPlatform(platformName);
|
|
113
|
+
if (!platform) {
|
|
114
|
+
console.log(`Error: Unknown platform: ${platformName}`);
|
|
115
|
+
printAvailablePlatforms();
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
if (!platform.detectFunc()) {
|
|
119
|
+
console.log(`Error: ${platform.displayName} is not installed`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
console.log(`🦞 Installing CommanderClaw plugin to ${platform.displayName}`);
|
|
123
|
+
console.log();
|
|
124
|
+
// Collect configuration
|
|
125
|
+
const config = await collectInstallConfig(platform);
|
|
126
|
+
// Get token from server
|
|
127
|
+
try {
|
|
128
|
+
console.log('Fetching token from server...');
|
|
129
|
+
const token = await getTokenFromServer(config.serverUrl);
|
|
130
|
+
config.token = token;
|
|
131
|
+
console.log(`✓ Token obtained: ${maskToken(token)}`);
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
console.log(`Error: Could not get token from server: ${e.message}`);
|
|
136
|
+
console.log('Make sure the CommanderClaw server is running:');
|
|
137
|
+
console.log(' commanderclaw gateway start');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
// Backup config
|
|
141
|
+
const backupPath = platform.configPath + '.backup';
|
|
142
|
+
backupFile(platform.configPath, backupPath);
|
|
143
|
+
// Install
|
|
144
|
+
console.log(`Installing commanderclaw-openclaw-plugin@${config.version}...`);
|
|
145
|
+
const targetPath = path.join(platform.extDir, 'commanderclaw');
|
|
146
|
+
try {
|
|
147
|
+
installFromNpm(config.version, targetPath, platform);
|
|
148
|
+
console.log('✓ Plugin files installed');
|
|
149
|
+
updatePlatformConfig(platform, config);
|
|
150
|
+
console.log('✓ Configuration updated');
|
|
151
|
+
addToPath(platform);
|
|
152
|
+
// Remove backup on success
|
|
153
|
+
if (fs.existsSync(backupPath)) {
|
|
154
|
+
fs.unlinkSync(backupPath);
|
|
155
|
+
}
|
|
156
|
+
console.log();
|
|
157
|
+
console.log('✅ Installation complete!');
|
|
158
|
+
console.log();
|
|
159
|
+
console.log('Configuration:');
|
|
160
|
+
console.log(` Server URL: ${config.serverUrl}`);
|
|
161
|
+
console.log(` Device Name: ${config.deviceName}`);
|
|
162
|
+
console.log(` Version: ${config.version}`);
|
|
163
|
+
console.log();
|
|
164
|
+
console.log('Next steps:');
|
|
165
|
+
console.log(` Restart ${platform.displayName} to activate the plugin`);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
console.log(`❌ Installation failed: ${e.message}`);
|
|
169
|
+
restoreBackup(backupPath, platform.configPath);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function handlePluginUpdate(platformName) {
|
|
174
|
+
const platform = findPlatform(platformName);
|
|
175
|
+
if (!platform) {
|
|
176
|
+
console.log(`Error: Unknown platform: ${platformName}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const targetPath = path.join(platform.extDir, 'commanderclaw');
|
|
180
|
+
if (!fs.existsSync(targetPath)) {
|
|
181
|
+
console.log(`Error: Plugin not installed in ${platform.displayName}`);
|
|
182
|
+
console.log('Run: commanderclaw plugin install <platform>');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
console.log(`🦞 Updating CommanderClaw plugin in ${platform.displayName}`);
|
|
186
|
+
console.log();
|
|
187
|
+
const version = await rlWithDefault('Version to update to', 'latest');
|
|
188
|
+
console.log(`Updating to version ${version}...`);
|
|
189
|
+
try {
|
|
190
|
+
installFromNpm(version, targetPath, platform);
|
|
191
|
+
console.log('✅ Plugin updated successfully!');
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
console.log(`❌ Update failed: ${e.message}`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function handlePluginUninstall(platformName) {
|
|
199
|
+
const platform = findPlatform(platformName);
|
|
200
|
+
if (!platform) {
|
|
201
|
+
console.log(`Error: Unknown platform: ${platformName}`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
const targetPath = path.join(platform.extDir, 'commanderclaw');
|
|
205
|
+
console.log(`🦞 Uninstalling CommanderClaw plugin from ${platform.displayName}`);
|
|
206
|
+
console.log();
|
|
207
|
+
try {
|
|
208
|
+
if (platform.requiresSudo) {
|
|
209
|
+
execSync(`sudo rm -rf ${targetPath}`, { stdio: 'inherit' });
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if (fs.existsSync(targetPath)) {
|
|
213
|
+
fs.rmSync(targetPath, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
console.log('✅ Plugin uninstalled successfully!');
|
|
217
|
+
console.log();
|
|
218
|
+
console.log(`Note: Configuration in ${platform.configPath} was not modified`);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
console.log(`❌ Uninstall failed: ${e.message}`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function collectInstallConfig(platform) {
|
|
226
|
+
const version = await rlWithDefault('Plugin version', 'latest');
|
|
227
|
+
const serverUrl = await rlWithDefault('Server URL', DEFAULT_SERVER_URL);
|
|
228
|
+
const deviceName = await rlWithDefault('Device name', DEFAULT_DEVICE_NAME);
|
|
229
|
+
return {
|
|
230
|
+
version,
|
|
231
|
+
serverUrl,
|
|
232
|
+
token: '',
|
|
233
|
+
deviceName,
|
|
234
|
+
autoConnect: true,
|
|
235
|
+
reconnect: {
|
|
236
|
+
enabled: true,
|
|
237
|
+
maxAttempts: 10,
|
|
238
|
+
delayMs: 5000,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function installFromNpm(version, targetPath, platform) {
|
|
243
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'commanderclaw-plugin-'));
|
|
244
|
+
try {
|
|
245
|
+
const spec = version === 'latest'
|
|
246
|
+
? 'commanderclaw'
|
|
247
|
+
: `commanderclaw@${version}`;
|
|
248
|
+
// Download from npm
|
|
249
|
+
execSync(`npm pack ${spec}`, { cwd: tmpDir, stdio: 'pipe' });
|
|
250
|
+
// Find tarball
|
|
251
|
+
const files = fs.readdirSync(tmpDir).filter(f => f.endsWith('.tgz'));
|
|
252
|
+
if (files.length === 0) {
|
|
253
|
+
throw new Error('Could not find downloaded tarball');
|
|
254
|
+
}
|
|
255
|
+
const tarball = path.join(tmpDir, files[0]);
|
|
256
|
+
// Extract
|
|
257
|
+
const extractDir = path.join(tmpDir, 'extracted');
|
|
258
|
+
fs.mkdirSync(extractDir);
|
|
259
|
+
execSync(`tar -xzf ${tarball} -C ${extractDir}`, { stdio: 'pipe' });
|
|
260
|
+
const packageDir = path.join(extractDir, 'package');
|
|
261
|
+
// Remove existing
|
|
262
|
+
if (platform.requiresSudo) {
|
|
263
|
+
execSync(`sudo rm -rf ${targetPath}`, { stdio: 'pipe' });
|
|
264
|
+
execSync(`sudo mkdir -p ${path.dirname(targetPath)}`, { stdio: 'pipe' });
|
|
265
|
+
execSync(`sudo cp -r ${packageDir} ${targetPath}`, { stdio: 'pipe' });
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
if (fs.existsSync(targetPath)) {
|
|
269
|
+
fs.rmSync(targetPath, { recursive: true });
|
|
270
|
+
}
|
|
271
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
272
|
+
copyDir(packageDir, targetPath);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
// Cleanup
|
|
277
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function copyDir(src, dst) {
|
|
281
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
282
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
283
|
+
for (const entry of entries) {
|
|
284
|
+
const srcPath = path.join(src, entry.name);
|
|
285
|
+
const dstPath = path.join(dst, entry.name);
|
|
286
|
+
if (entry.isDirectory()) {
|
|
287
|
+
copyDir(srcPath, dstPath);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function updatePlatformConfig(platform, config) {
|
|
295
|
+
let cfg = {};
|
|
296
|
+
if (fs.existsSync(platform.configPath)) {
|
|
297
|
+
try {
|
|
298
|
+
cfg = JSON.parse(fs.readFileSync(platform.configPath, 'utf-8'));
|
|
299
|
+
}
|
|
300
|
+
catch { }
|
|
301
|
+
}
|
|
302
|
+
// Update channels
|
|
303
|
+
if (!cfg.channels)
|
|
304
|
+
cfg.channels = {};
|
|
305
|
+
cfg.channels.commanderclaw = {
|
|
306
|
+
enabled: true,
|
|
307
|
+
serverUrl: config.serverUrl,
|
|
308
|
+
token: config.token,
|
|
309
|
+
deviceName: config.deviceName,
|
|
310
|
+
autoConnect: config.autoConnect,
|
|
311
|
+
};
|
|
312
|
+
// Update plugins
|
|
313
|
+
if (!cfg.plugins)
|
|
314
|
+
cfg.plugins = {};
|
|
315
|
+
if (!cfg.plugins.allow)
|
|
316
|
+
cfg.plugins.allow = [];
|
|
317
|
+
if (!cfg.plugins.allow.includes('commanderclaw')) {
|
|
318
|
+
cfg.plugins.allow.push('commanderclaw');
|
|
319
|
+
}
|
|
320
|
+
if (!cfg.plugins.entries)
|
|
321
|
+
cfg.plugins.entries = {};
|
|
322
|
+
cfg.plugins.entries.commanderclaw = {
|
|
323
|
+
enabled: true,
|
|
324
|
+
config: {
|
|
325
|
+
serverUrl: config.serverUrl,
|
|
326
|
+
token: config.token,
|
|
327
|
+
deviceName: config.deviceName,
|
|
328
|
+
autoConnect: config.autoConnect,
|
|
329
|
+
reconnect: config.reconnect,
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
if (!cfg.plugins.installs)
|
|
333
|
+
cfg.plugins.installs = {};
|
|
334
|
+
cfg.plugins.installs.commanderclaw = {
|
|
335
|
+
source: 'npm',
|
|
336
|
+
spec: `commanderclaw@${config.version}`,
|
|
337
|
+
version: config.version,
|
|
338
|
+
installedAt: new Date().toISOString(),
|
|
339
|
+
};
|
|
340
|
+
// Write config
|
|
341
|
+
const output = JSON.stringify(cfg, null, 2);
|
|
342
|
+
if (platform.requiresSudo) {
|
|
343
|
+
const tmpFile = path.join(os.tmpdir(), 'openclaw.json');
|
|
344
|
+
fs.writeFileSync(tmpFile, output);
|
|
345
|
+
execSync(`sudo cp ${tmpFile} ${platform.configPath}`, { stdio: 'pipe' });
|
|
346
|
+
fs.unlinkSync(tmpFile);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
fs.mkdirSync(path.dirname(platform.configPath), { recursive: true });
|
|
350
|
+
fs.writeFileSync(platform.configPath, output);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function addToPath(platform) {
|
|
354
|
+
// Check if commanderclaw is already in PATH
|
|
355
|
+
try {
|
|
356
|
+
execSync('which commanderclaw', { stdio: 'pipe' });
|
|
357
|
+
return; // Already in PATH
|
|
358
|
+
}
|
|
359
|
+
catch { }
|
|
360
|
+
const homeDir = os.homedir();
|
|
361
|
+
const shellConfig = detectShellConfig(homeDir);
|
|
362
|
+
if (!shellConfig)
|
|
363
|
+
return;
|
|
364
|
+
// Get bin directory
|
|
365
|
+
const packageDir = path.dirname(path.dirname(__dirname));
|
|
366
|
+
const binDir = path.join(packageDir, 'bin');
|
|
367
|
+
// Check if already added
|
|
368
|
+
if (fs.existsSync(shellConfig)) {
|
|
369
|
+
const content = fs.readFileSync(shellConfig, 'utf-8');
|
|
370
|
+
if (content.includes('commanderclaw')) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Add to shell config
|
|
375
|
+
const line = `\n# Added by CommanderClaw\nexport PATH="$PATH:${binDir}"\n`;
|
|
376
|
+
try {
|
|
377
|
+
fs.appendFileSync(shellConfig, line);
|
|
378
|
+
console.log(`✓ Added commanderclaw to PATH in ${path.basename(shellConfig)}`);
|
|
379
|
+
console.log(" Run 'source ~/.zshrc' or restart your terminal to apply");
|
|
380
|
+
}
|
|
381
|
+
catch { }
|
|
382
|
+
}
|
|
383
|
+
function detectShellConfig(homeDir) {
|
|
384
|
+
const shell = process.env.SHELL || '';
|
|
385
|
+
// Check for zsh
|
|
386
|
+
if (shell.includes('zsh')) {
|
|
387
|
+
const zshrc = path.join(homeDir, '.zshrc');
|
|
388
|
+
if (fs.existsSync(zshrc))
|
|
389
|
+
return zshrc;
|
|
390
|
+
}
|
|
391
|
+
// Check for bash
|
|
392
|
+
const bashrc = path.join(homeDir, '.bashrc');
|
|
393
|
+
if (fs.existsSync(bashrc))
|
|
394
|
+
return bashrc;
|
|
395
|
+
const bashProfile = path.join(homeDir, '.bash_profile');
|
|
396
|
+
if (fs.existsSync(bashProfile))
|
|
397
|
+
return bashProfile;
|
|
398
|
+
// Default to .zshrc
|
|
399
|
+
return path.join(homeDir, '.zshrc');
|
|
400
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
export const DEFAULT_SERVER_URL = 'ws://127.0.0.1:19739/ws';
|
|
6
|
+
export const DEFAULT_DEVICE_NAME = 'OpenClaw Agent';
|
|
7
|
+
export function getPlatforms() {
|
|
8
|
+
const homeDir = os.homedir();
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
name: 'qclaw',
|
|
12
|
+
displayName: 'QClaw',
|
|
13
|
+
configPath: '/Applications/QClaw.app/Contents/Resources/openclaw/config/openclaw.json',
|
|
14
|
+
extDir: '/Applications/QClaw.app/Contents/Resources/openclaw/config/extensions',
|
|
15
|
+
detectFunc: () => fs.existsSync('/Applications/QClaw.app'),
|
|
16
|
+
requiresSudo: true,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'openclaw',
|
|
20
|
+
displayName: 'OpenClaw',
|
|
21
|
+
configPath: path.join(homeDir, '.openclaw', 'openclaw.json'),
|
|
22
|
+
extDir: path.join(homeDir, '.openclaw', 'extensions'),
|
|
23
|
+
detectFunc: () => {
|
|
24
|
+
try {
|
|
25
|
+
require('child_process').execSync('which openclaw', { stdio: 'ignore' });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
requiresSudo: false,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
export function findPlatform(name) {
|
|
37
|
+
return getPlatforms().find(p => p.name === name);
|
|
38
|
+
}
|
|
39
|
+
export function maskToken(token) {
|
|
40
|
+
if (token.length <= 8) {
|
|
41
|
+
return '****';
|
|
42
|
+
}
|
|
43
|
+
return token.substring(0, 4) + '****' + token.substring(token.length - 4);
|
|
44
|
+
}
|
|
45
|
+
export function rl(question) {
|
|
46
|
+
const readlineInterface = readline.createInterface({
|
|
47
|
+
input: process.stdin,
|
|
48
|
+
output: process.stdout,
|
|
49
|
+
});
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
readlineInterface.question(question, (answer) => {
|
|
52
|
+
readlineInterface.close();
|
|
53
|
+
resolve(answer.trim());
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export function rlWithDefault(question, defaultValue) {
|
|
58
|
+
return rl(`${question} [${defaultValue}]: `).then(answer => answer || defaultValue);
|
|
59
|
+
}
|
|
60
|
+
export function getProjectRoot() {
|
|
61
|
+
// Check environment variable
|
|
62
|
+
const envRoot = process.env.COMMANDERCLAW_ROOT;
|
|
63
|
+
if (envRoot && fs.existsSync(path.join(envRoot, 'package.json'))) {
|
|
64
|
+
return envRoot;
|
|
65
|
+
}
|
|
66
|
+
// Check current directory
|
|
67
|
+
let dir = process.cwd();
|
|
68
|
+
while (dir !== path.dirname(dir)) {
|
|
69
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
70
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
|
|
71
|
+
if (pkg.name === 'commanderclaw') {
|
|
72
|
+
return dir;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
dir = path.dirname(dir);
|
|
76
|
+
}
|
|
77
|
+
return process.cwd();
|
|
78
|
+
}
|
|
79
|
+
export async function getTokenFromServer(serverUrl) {
|
|
80
|
+
const http = require('http');
|
|
81
|
+
// Convert ws:// to http://
|
|
82
|
+
let apiUrl = serverUrl
|
|
83
|
+
.replace('ws://', 'http://')
|
|
84
|
+
.replace('wss://', 'https://')
|
|
85
|
+
.replace(/\/ws$/, '');
|
|
86
|
+
apiUrl += '/api/auth/token';
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const req = http.get(apiUrl, { timeout: 5000 }, (res) => {
|
|
89
|
+
let data = '';
|
|
90
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
91
|
+
res.on('end', () => {
|
|
92
|
+
if (res.statusCode !== 200) {
|
|
93
|
+
reject(new Error(`Server returned ${res.statusCode}`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const json = JSON.parse(data);
|
|
98
|
+
resolve(json.token);
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
reject(new Error('Invalid response'));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
req.on('error', reject);
|
|
106
|
+
req.on('timeout', () => {
|
|
107
|
+
req.destroy();
|
|
108
|
+
reject(new Error('Timeout'));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export function backupFile(src, dst) {
|
|
113
|
+
if (fs.existsSync(src)) {
|
|
114
|
+
fs.copyFileSync(src, dst);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function restoreBackup(backup, target) {
|
|
118
|
+
if (fs.existsSync(backup)) {
|
|
119
|
+
fs.copyFileSync(backup, target);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket client for CommanderClaw server
|
|
3
|
+
* Cross-platform: works in both Node.js and browser/Electron environments
|
|
4
|
+
*/
|
|
5
|
+
import type { Message, PeerInfo, CreateTaskInput, Task, TaskUpdatePayload, ClientEventType, ClientEvent } from "./types";
|
|
6
|
+
export interface ClientOptions {
|
|
7
|
+
url: string;
|
|
8
|
+
token?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
capabilities: string[];
|
|
11
|
+
reconnect?: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
maxAttempts: number;
|
|
14
|
+
delayMs: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare class CommanderClawClient {
|
|
18
|
+
private ws;
|
|
19
|
+
private options;
|
|
20
|
+
private nodeId;
|
|
21
|
+
private peers;
|
|
22
|
+
private commanderId;
|
|
23
|
+
private connected;
|
|
24
|
+
private reconnectAttempts;
|
|
25
|
+
private messageHandlers;
|
|
26
|
+
private eventHandlers;
|
|
27
|
+
private heartbeatInterval;
|
|
28
|
+
private connectionPromise;
|
|
29
|
+
constructor(options: ClientOptions);
|
|
30
|
+
connect(): Promise<void>;
|
|
31
|
+
private handleMessage;
|
|
32
|
+
private updatePeers;
|
|
33
|
+
private handleDisconnect;
|
|
34
|
+
private startHeartbeat;
|
|
35
|
+
private attemptReconnect;
|
|
36
|
+
disconnect(): Promise<void>;
|
|
37
|
+
send(type: string, payload?: unknown): void;
|
|
38
|
+
on(event: ClientEventType, handler: (event: ClientEvent) => void): void;
|
|
39
|
+
off(event: ClientEventType, handler: (event: ClientEvent) => void): void;
|
|
40
|
+
private emit;
|
|
41
|
+
onMessage(type: string, handler: (msg: Message) => void): void;
|
|
42
|
+
isConnected(): boolean;
|
|
43
|
+
getNodeId(): string | null;
|
|
44
|
+
getPeers(): PeerInfo[];
|
|
45
|
+
getCommanderId(): string | null;
|
|
46
|
+
private getHttpUrl;
|
|
47
|
+
private fetchApi;
|
|
48
|
+
listNodes(): Promise<PeerInfo[]>;
|
|
49
|
+
getStatus(): Promise<{
|
|
50
|
+
status: string;
|
|
51
|
+
nodes: number;
|
|
52
|
+
}>;
|
|
53
|
+
createTask(input: CreateTaskInput): Promise<Task>;
|
|
54
|
+
listTasks(): Promise<Task[]>;
|
|
55
|
+
getTask(taskId: string): Promise<Task>;
|
|
56
|
+
abortTask(taskId: string, reason?: string): Promise<{
|
|
57
|
+
status: string;
|
|
58
|
+
}>;
|
|
59
|
+
pauseTask(taskId: string): Promise<{
|
|
60
|
+
status: string;
|
|
61
|
+
}>;
|
|
62
|
+
resumeTask(taskId: string): Promise<{
|
|
63
|
+
status: string;
|
|
64
|
+
}>;
|
|
65
|
+
appointCommander(nodeId: string): Promise<{
|
|
66
|
+
status: string;
|
|
67
|
+
commanderId: string;
|
|
68
|
+
}>;
|
|
69
|
+
sendTaskUpdate(payload: TaskUpdatePayload): void;
|
|
70
|
+
sendTaskResult(payload: TaskUpdatePayload & {
|
|
71
|
+
output?: unknown;
|
|
72
|
+
error?: string;
|
|
73
|
+
duration: number;
|
|
74
|
+
}): void;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/plugin/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management for CommanderClaw plugin
|
|
3
|
+
*
|
|
4
|
+
* Config file location: ~/.openclaw/.commanderclaw.json
|
|
5
|
+
*/
|
|
6
|
+
export interface CommanderClawConfigFile {
|
|
7
|
+
nodeId?: string;
|
|
8
|
+
nodeName: string;
|
|
9
|
+
serverUrl: string;
|
|
10
|
+
token?: string;
|
|
11
|
+
autoConnect: boolean;
|
|
12
|
+
reconnect: {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
maxAttempts: number;
|
|
15
|
+
delayMs: number;
|
|
16
|
+
};
|
|
17
|
+
lastConnected?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function loadConfig(): CommanderClawConfigFile | null;
|
|
20
|
+
export declare function saveConfig(config: CommanderClawConfigFile): void;
|
|
21
|
+
export declare function updateConfig(updates: Partial<CommanderClawConfigFile>): CommanderClawConfigFile | null;
|
|
22
|
+
export declare function createDefaultConfig(serverUrl: string, nodeName: string, token?: string): CommanderClawConfigFile;
|
|
23
|
+
export declare function getNodeId(): string | undefined;
|
|
24
|
+
export declare function saveNodeId(nodeId: string): void;
|
|
25
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/plugin/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiBD,wBAAgB,UAAU,IAAI,uBAAuB,GAAG,IAAI,CAc3D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAWhE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAAG,uBAAuB,GAAG,IAAI,CAUtG;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,uBAAuB,CAYzB;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAM/C"}
|