spawn-skill 1.3.6 → 1.4.0
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/bin/cli.js +115 -3
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -201,12 +201,14 @@ function getTypeScriptConnector(token, agentName) {
|
|
|
201
201
|
*/
|
|
202
202
|
|
|
203
203
|
import WebSocket from 'ws';
|
|
204
|
+
import readline from 'readline';
|
|
204
205
|
|
|
205
206
|
const TOKEN = '${token}';
|
|
206
207
|
const NAME = '${agentName}';
|
|
207
208
|
const RELAY = 'wss://spawn-relay.ngvsqdjj5r.workers.dev/v1/agent';
|
|
208
209
|
|
|
209
210
|
let ws;
|
|
211
|
+
let pendingSpawnCallbacks = {};
|
|
210
212
|
|
|
211
213
|
function send(type, payload) {
|
|
212
214
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
@@ -227,6 +229,42 @@ function updateStatus(status, label) {
|
|
|
227
229
|
send('status_update', { status, label });
|
|
228
230
|
}
|
|
229
231
|
|
|
232
|
+
// Request to spawn a sub-agent (requires user approval in app)
|
|
233
|
+
function requestSpawn(name, role, description, permissions = [], reason = '') {
|
|
234
|
+
const requestId = \`spawn_\${Date.now()}\`;
|
|
235
|
+
|
|
236
|
+
send('agent_spawn_request', {
|
|
237
|
+
request_id: requestId,
|
|
238
|
+
parent_id: NAME,
|
|
239
|
+
proposed_agent: {
|
|
240
|
+
name: name,
|
|
241
|
+
role: role,
|
|
242
|
+
description: description,
|
|
243
|
+
icon: 'bolt',
|
|
244
|
+
permissions: permissions,
|
|
245
|
+
lifespan: 'task',
|
|
246
|
+
estimated_duration: '5-10 minutes',
|
|
247
|
+
capabilities: []
|
|
248
|
+
},
|
|
249
|
+
reason: reason,
|
|
250
|
+
urgency: 'normal',
|
|
251
|
+
auto_approve_eligible: false,
|
|
252
|
+
expires_at: Math.floor(Date.now() / 1000) + 600
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
console.log(\`📤 Sent spawn request for sub-agent: \${name}\`);
|
|
256
|
+
return requestId;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Notify that a sub-agent has finished
|
|
260
|
+
function terminateSubAgent(subAgentId) {
|
|
261
|
+
send('sub_agent_terminated', {
|
|
262
|
+
agent_id: subAgentId,
|
|
263
|
+
parent_id: NAME,
|
|
264
|
+
reason: 'Task completed'
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
230
268
|
function connect() {
|
|
231
269
|
ws = new WebSocket(RELAY, {
|
|
232
270
|
headers: { 'Authorization': \`Bearer \${TOKEN}\` }
|
|
@@ -234,18 +272,24 @@ function connect() {
|
|
|
234
272
|
|
|
235
273
|
ws.on('open', () => {
|
|
236
274
|
console.log('Connected to Spawn.wtf!');
|
|
237
|
-
// Auth happens via token in header - just start working
|
|
238
275
|
sendText(\`\${NAME} is online and ready.\`);
|
|
239
276
|
updateStatus('idle', 'Ready for commands');
|
|
277
|
+
console.log('');
|
|
278
|
+
console.log('Commands:');
|
|
279
|
+
console.log(' spawn <name> - Request to spawn a sub-agent');
|
|
280
|
+
console.log(' kill <name> - Terminate a sub-agent');
|
|
281
|
+
console.log(' say <text> - Send a message');
|
|
282
|
+
console.log(' status <s> - Update status (idle/working/thinking)');
|
|
283
|
+
console.log('');
|
|
240
284
|
});
|
|
241
285
|
|
|
242
286
|
ws.on('message', (data) => {
|
|
243
287
|
const msg = JSON.parse(data.toString());
|
|
244
|
-
console.log('Received:', msg.type);
|
|
288
|
+
console.log('📨 Received:', msg.type);
|
|
245
289
|
|
|
246
290
|
if (msg.type === 'message') {
|
|
247
|
-
console.log('Message from app:', msg.payload);
|
|
248
291
|
const text = msg.payload?.text || '';
|
|
292
|
+
console.log('📱 From app:', text);
|
|
249
293
|
|
|
250
294
|
updateStatus('thinking', 'Processing...');
|
|
251
295
|
setTimeout(() => {
|
|
@@ -253,6 +297,19 @@ function connect() {
|
|
|
253
297
|
updateStatus('idle', 'Ready');
|
|
254
298
|
}, 500);
|
|
255
299
|
}
|
|
300
|
+
|
|
301
|
+
// Handle spawn approval/rejection
|
|
302
|
+
if (msg.type === 'agent_spawn_response') {
|
|
303
|
+
const { request_id, decision } = msg.payload || {};
|
|
304
|
+
console.log(\`🔔 Spawn \${decision} for request \${request_id}\`);
|
|
305
|
+
|
|
306
|
+
if (decision === 'approved') {
|
|
307
|
+
// Spawn was approved - agent can now use the sub-agent
|
|
308
|
+
sendText('Sub-agent approved! Starting task...');
|
|
309
|
+
} else {
|
|
310
|
+
sendText('Sub-agent request was denied. Continuing without it.');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
256
313
|
});
|
|
257
314
|
|
|
258
315
|
ws.on('close', () => {
|
|
@@ -264,6 +321,60 @@ function connect() {
|
|
|
264
321
|
});
|
|
265
322
|
}
|
|
266
323
|
|
|
324
|
+
// Interactive CLI
|
|
325
|
+
const rl = readline.createInterface({
|
|
326
|
+
input: process.stdin,
|
|
327
|
+
output: process.stdout
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
rl.on('line', (line) => {
|
|
331
|
+
const [cmd, ...args] = line.trim().split(' ');
|
|
332
|
+
const arg = args.join(' ');
|
|
333
|
+
|
|
334
|
+
switch (cmd) {
|
|
335
|
+
case 'spawn':
|
|
336
|
+
if (!arg) {
|
|
337
|
+
console.log('Usage: spawn <name>');
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
requestSpawn(
|
|
341
|
+
arg,
|
|
342
|
+
'Helper',
|
|
343
|
+
\`Sub-agent to help with \${arg} tasks\`,
|
|
344
|
+
[{ scope: 'files.read', path: '**/*', reason: 'Read project files' }],
|
|
345
|
+
'Need parallel processing help'
|
|
346
|
+
);
|
|
347
|
+
break;
|
|
348
|
+
|
|
349
|
+
case 'kill':
|
|
350
|
+
if (!arg) {
|
|
351
|
+
console.log('Usage: kill <name>');
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
terminateSubAgent(arg.toLowerCase().replace(/\\s+/g, '-'));
|
|
355
|
+
console.log(\`Terminated sub-agent: \${arg}\`);
|
|
356
|
+
break;
|
|
357
|
+
|
|
358
|
+
case 'say':
|
|
359
|
+
if (!arg) {
|
|
360
|
+
console.log('Usage: say <message>');
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
sendText(arg);
|
|
364
|
+
break;
|
|
365
|
+
|
|
366
|
+
case 'status':
|
|
367
|
+
updateStatus(arg || 'idle', arg === 'working' ? 'Working...' : 'Ready');
|
|
368
|
+
console.log(\`Status updated to: \${arg || 'idle'}\`);
|
|
369
|
+
break;
|
|
370
|
+
|
|
371
|
+
default:
|
|
372
|
+
if (line.trim()) {
|
|
373
|
+
sendText(line.trim());
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
267
378
|
// Keep alive
|
|
268
379
|
setInterval(() => {
|
|
269
380
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
@@ -276,6 +387,7 @@ connect();
|
|
|
276
387
|
|
|
277
388
|
process.on('SIGINT', () => {
|
|
278
389
|
console.log('Shutting down...');
|
|
390
|
+
rl.close();
|
|
279
391
|
ws?.close();
|
|
280
392
|
process.exit(0);
|
|
281
393
|
});
|