spawn-skill 1.0.2 → 1.2.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.
Files changed (3) hide show
  1. package/bin/CLAUDE.md +1 -1
  2. package/bin/cli.js +137 -4
  3. package/package.json +10 -3
package/bin/CLAUDE.md CHANGED
@@ -7,5 +7,5 @@
7
7
 
8
8
  | ID | Time | T | Title | Read |
9
9
  |----|------|---|-------|------|
10
- | #794 | 11:11 AM | | Simplified spawn-skill CLI package.json generation to use direct ws dependency | ~353 |
10
+ | #913 | 10:14 PM | 🔵 | Spawn-skill CLI tool provides Python SDK implementation | ~343 |
11
11
  </claude-mem-context>
package/bin/cli.js CHANGED
@@ -473,7 +473,113 @@ class SpawnAgent:
473
473
  `;
474
474
  }
475
475
 
476
- async function createSkillFiles(token, agentName, language) {
476
+ function getMoltbotBridge(token, agentName) {
477
+ return `/**
478
+ * Spawn.wtf → Moltbot Bridge
479
+ * Routes messages from Spawn iOS app to your Moltbot backend
480
+ * Run with: node connect.js
481
+ */
482
+
483
+ import WebSocket from 'ws';
484
+ import { spawn } from 'child_process';
485
+
486
+ const TOKEN = '${token}';
487
+ const NAME = '${agentName}';
488
+ const RELAY = 'wss://spawn-relay.ngvsqdjj5r.workers.dev/v1/agent';
489
+
490
+ let ws;
491
+
492
+ function send(type, payload) {
493
+ if (ws?.readyState === WebSocket.OPEN) {
494
+ ws.send(JSON.stringify({
495
+ type,
496
+ id: \`msg_\${Date.now()}\`,
497
+ ts: Date.now(),
498
+ payload
499
+ }));
500
+ }
501
+ }
502
+
503
+ function sendText(text) {
504
+ send('message', { content_type: 'text', text, format: 'plain' });
505
+ }
506
+
507
+ function updateStatus(status, label) {
508
+ send('status_update', { status, label });
509
+ }
510
+
511
+ // Forward message to Moltbot and return response
512
+ async function askMoltbot(message) {
513
+ return new Promise((resolve, reject) => {
514
+ console.log('📨 Forwarding to Moltbot:', message);
515
+ updateStatus('thinking', 'Asking Moltbot...');
516
+
517
+ const moltbot = spawn('moltbot', ['agent', '--message', message, '--thinking', 'high']);
518
+ let response = '';
519
+ let error = '';
520
+
521
+ moltbot.stdout.on('data', (data) => response += data.toString());
522
+ moltbot.stderr.on('data', (data) => error += data.toString());
523
+
524
+ moltbot.on('close', (code) => {
525
+ if (code === 0) {
526
+ console.log('✓ Moltbot responded');
527
+ resolve(response.trim());
528
+ } else {
529
+ console.error('✗ Moltbot error:', error);
530
+ reject(new Error(error || \`Exit code \${code}\`));
531
+ }
532
+ });
533
+ });
534
+ }
535
+
536
+ function connect() {
537
+ ws = new WebSocket(RELAY, {
538
+ headers: { 'Authorization': \`Bearer \${TOKEN}\` }
539
+ });
540
+
541
+ ws.on('open', () => {
542
+ console.log('🦞 Connected to Spawn.wtf (Moltbot bridge mode)');
543
+ sendText(\`\${NAME} is online (powered by Moltbot 🦞)\`);
544
+ updateStatus('idle', 'Ready for commands');
545
+ });
546
+
547
+ ws.on('message', async (data) => {
548
+ const msg = JSON.parse(data.toString());
549
+
550
+ if (msg.type === 'message') {
551
+ const text = msg.payload?.text || '';
552
+ console.log('📱 From Spawn:', text);
553
+
554
+ try {
555
+ const response = await askMoltbot(text);
556
+ sendText(response);
557
+ updateStatus('idle', 'Ready');
558
+ } catch (err) {
559
+ sendText(\`Error: \${err.message}\`);
560
+ updateStatus('idle', 'Ready');
561
+ }
562
+ }
563
+ });
564
+
565
+ ws.on('close', () => {
566
+ console.log('Disconnected, reconnecting in 5s...');
567
+ setTimeout(connect, 5000);
568
+ });
569
+
570
+ ws.on('error', (err) => console.error('WebSocket error:', err.message));
571
+ }
572
+
573
+ setInterval(() => ws?.readyState === WebSocket.OPEN && send('ping', {}), 30000);
574
+
575
+ console.log('🦞 Starting Spawn → Moltbot bridge...');
576
+ connect();
577
+
578
+ process.on('SIGINT', () => { ws?.close(); process.exit(0); });
579
+ `;
580
+ }
581
+
582
+ async function createSkillFiles(token, agentName, language, useMoltbot = false) {
477
583
  const skillDir = path.join(process.cwd(), 'spawn');
478
584
 
479
585
  // Create spawn directory
@@ -495,7 +601,11 @@ async function createSkillFiles(token, agentName, language) {
495
601
 
496
602
  if (language === 'typescript') {
497
603
  // TypeScript/Node setup
498
- fs.writeFileSync(path.join(skillDir, 'connect.js'), getTypeScriptConnector(token, agentName));
604
+ if (useMoltbot) {
605
+ fs.writeFileSync(path.join(skillDir, 'connect.js'), getMoltbotBridge(token, agentName));
606
+ } else {
607
+ fs.writeFileSync(path.join(skillDir, 'connect.js'), getTypeScriptConnector(token, agentName));
608
+ }
499
609
 
500
610
  const packageJson = {
501
611
  name: "spawn-agent",
@@ -514,7 +624,7 @@ async function createSkillFiles(token, agentName, language) {
514
624
  fs.writeFileSync(path.join(skillDir, 'requirements.txt'), 'websockets>=12.0\n');
515
625
  }
516
626
 
517
- return { skillDir, language };
627
+ return { skillDir, language, useMoltbot };
518
628
  }
519
629
 
520
630
  async function main() {
@@ -578,18 +688,41 @@ async function main() {
578
688
  });
579
689
  }
580
690
 
691
+ questions.push({
692
+ type: 'confirm',
693
+ name: 'useMoltbot',
694
+ message: 'Use Moltbot as your AI backend? (recommended)',
695
+ default: false
696
+ });
697
+
581
698
  const answers = await inquirer.prompt(questions);
582
699
 
583
700
  token = token || answers.token;
584
701
  agentName = answers.agentName || agentName;
585
702
  language = language || answers.language;
703
+ const useMoltbot = answers.useMoltbot || false;
586
704
 
587
705
  console.log('');
588
706
 
707
+ // Check for Moltbot if needed
708
+ if (useMoltbot) {
709
+ const moltbotCheck = ora('Checking for Moltbot...').start();
710
+ try {
711
+ const { execSync } = await import('child_process');
712
+ execSync('which moltbot', { stdio: 'pipe' });
713
+ moltbotCheck.succeed('Moltbot found');
714
+ } catch {
715
+ moltbotCheck.warn('Moltbot not installed');
716
+ console.log(dim(' Install with: npm install -g moltbot@latest'));
717
+ console.log(dim(' Then run: moltbot onboard'));
718
+ console.log('');
719
+ }
720
+ }
721
+
589
722
  // Create files
590
723
  const createSpinner = ora('Creating skill files...').start();
591
724
  try {
592
- const { skillDir } = await createSkillFiles(token, agentName, language);
725
+ const { skillDir } = await createSkillFiles(token, agentName, language, useMoltbot);
593
726
  createSpinner.succeed('Created skill files');
594
727
  } catch (err) {
595
728
  createSpinner.fail('Failed to create skill files');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawn-skill",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Connect your AI agent to Spawn.wtf",
5
5
  "bin": {
6
6
  "spawn-skill": "./bin/cli.js"
@@ -9,7 +9,14 @@
9
9
  "files": [
10
10
  "bin"
11
11
  ],
12
- "keywords": ["spawn", "ai", "agent", "claude", "monitoring"],
12
+ "keywords": [
13
+ "spawn",
14
+ "ai",
15
+ "agent",
16
+ "claude",
17
+ "monitoring",
18
+ "moltbot"
19
+ ],
13
20
  "author": "Spawn.wtf",
14
21
  "license": "MIT",
15
22
  "repository": {
@@ -21,4 +28,4 @@
21
28
  "inquirer": "^9.2.12",
22
29
  "ora": "^8.0.1"
23
30
  }
24
- }
31
+ }