@magpiecloud/mags 1.3.0 → 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/README.md +55 -4
- package/bin/mags.js +100 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -18,7 +18,14 @@ mags login
|
|
|
18
18
|
|
|
19
19
|
This will open your browser to create an API token. Paste the token when prompted, and it will be saved for future use.
|
|
20
20
|
|
|
21
|
-
### 2.
|
|
21
|
+
### 2. Create a persistent VM
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mags new myproject
|
|
25
|
+
mags ssh myproject
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 3. Or run a script
|
|
22
29
|
|
|
23
30
|
```bash
|
|
24
31
|
mags run 'echo Hello World'
|
|
@@ -26,8 +33,6 @@ mags run 'echo Hello World'
|
|
|
26
33
|
|
|
27
34
|
## Authentication
|
|
28
35
|
|
|
29
|
-
The CLI supports multiple authentication methods:
|
|
30
|
-
|
|
31
36
|
### Interactive Login (Recommended)
|
|
32
37
|
|
|
33
38
|
```bash
|
|
@@ -54,7 +59,13 @@ export MAGS_API_TOKEN="your-token-here"
|
|
|
54
59
|
## CLI Commands
|
|
55
60
|
|
|
56
61
|
```bash
|
|
57
|
-
#
|
|
62
|
+
# Create a persistent VM
|
|
63
|
+
mags new myproject
|
|
64
|
+
|
|
65
|
+
# SSH into it
|
|
66
|
+
mags ssh myproject
|
|
67
|
+
|
|
68
|
+
# Run a simple command
|
|
58
69
|
mags run 'echo Hello'
|
|
59
70
|
|
|
60
71
|
# With persistent workspace (S3 sync)
|
|
@@ -78,8 +89,26 @@ mags status <job-id>
|
|
|
78
89
|
mags logs <job-id>
|
|
79
90
|
mags list
|
|
80
91
|
mags stop <job-id>
|
|
92
|
+
|
|
93
|
+
# Install Claude Code skill
|
|
94
|
+
mags setup-claude
|
|
81
95
|
```
|
|
82
96
|
|
|
97
|
+
## Claude Code Integration
|
|
98
|
+
|
|
99
|
+
Install the Mags skill for Claude Code to run scripts directly from Claude:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
mags setup-claude
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This lets you use natural language commands like:
|
|
106
|
+
- `/mags run echo Hello World`
|
|
107
|
+
- `/mags create a python environment with numpy and pandas`
|
|
108
|
+
- `/mags run a flask server and give me the public URL`
|
|
109
|
+
|
|
110
|
+
See [mags.run/claude-skill.html](https://mags.run/claude-skill.html) for full documentation.
|
|
111
|
+
|
|
83
112
|
## CLI Flags
|
|
84
113
|
|
|
85
114
|
| Flag | Description | Default |
|
|
@@ -90,6 +119,27 @@ mags stop <job-id>
|
|
|
90
119
|
| `--port` | Port to expose for URL access | 8080 |
|
|
91
120
|
| `--startup-command` | Command when VM wakes from sleep | none |
|
|
92
121
|
|
|
122
|
+
## SSH Access
|
|
123
|
+
|
|
124
|
+
Connect to any running VM:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
mags ssh myproject
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Features:
|
|
131
|
+
- Connects via secure proxy (`api.magpiecloud.com:PORT`)
|
|
132
|
+
- Full PTY support for interactive terminals
|
|
133
|
+
- Automatic SSH key management
|
|
134
|
+
- Hostname: `mags-vm`
|
|
135
|
+
|
|
136
|
+
## Workspaces & Persistence
|
|
137
|
+
|
|
138
|
+
Your `/root` directory automatically syncs to S3:
|
|
139
|
+
- **Auto-sync**: Every 30 seconds while running
|
|
140
|
+
- **On stop**: Full sync before VM terminates
|
|
141
|
+
- **On wake**: Previous state restored
|
|
142
|
+
|
|
93
143
|
## Node.js SDK
|
|
94
144
|
|
|
95
145
|
```javascript
|
|
@@ -134,6 +184,7 @@ await mags.stop(requestId);
|
|
|
134
184
|
- **Warm start**: <100ms (VM from pool)
|
|
135
185
|
- **Cold start**: ~4 seconds (new VM boot)
|
|
136
186
|
- **Script overhead**: ~50ms
|
|
187
|
+
- **Workspace sync**: Every 30 seconds
|
|
137
188
|
|
|
138
189
|
## License
|
|
139
190
|
|
package/bin/mags.js
CHANGED
|
@@ -14,6 +14,10 @@ const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.mags
|
|
|
14
14
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
15
15
|
const SSH_KEY_FILE = path.join(CONFIG_DIR, 'ssh_key');
|
|
16
16
|
|
|
17
|
+
// Claude skill paths
|
|
18
|
+
const CLAUDE_GLOBAL_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.claude', 'commands');
|
|
19
|
+
const SKILL_URL = 'https://mags.run/mags.md';
|
|
20
|
+
|
|
17
21
|
// Load saved config
|
|
18
22
|
function loadConfig() {
|
|
19
23
|
try {
|
|
@@ -182,6 +186,7 @@ ${colors.bold}Commands:${colors.reset}
|
|
|
182
186
|
list List recent jobs
|
|
183
187
|
url <name|id> [port] Enable URL access for a job
|
|
184
188
|
stop <name|id> Stop a running job
|
|
189
|
+
setup-claude Install Mags skill for Claude Code
|
|
185
190
|
|
|
186
191
|
${colors.bold}Run Options:${colors.reset}
|
|
187
192
|
-w, --workspace <id> Use persistent workspace (S3 sync)
|
|
@@ -202,6 +207,7 @@ ${colors.bold}Examples:${colors.reset}
|
|
|
202
207
|
mags status myvm
|
|
203
208
|
mags logs myvm
|
|
204
209
|
mags url myvm 8080
|
|
210
|
+
mags setup-claude # Install Claude Code skill
|
|
205
211
|
`);
|
|
206
212
|
process.exit(1);
|
|
207
213
|
}
|
|
@@ -586,6 +592,95 @@ async function stopJob(nameOrId) {
|
|
|
586
592
|
}
|
|
587
593
|
}
|
|
588
594
|
|
|
595
|
+
// Download a file from URL
|
|
596
|
+
function downloadFile(url) {
|
|
597
|
+
return new Promise((resolve, reject) => {
|
|
598
|
+
const urlObj = new URL(url);
|
|
599
|
+
const lib = urlObj.protocol === 'https:' ? https : http;
|
|
600
|
+
|
|
601
|
+
lib.get(url, (res) => {
|
|
602
|
+
// Handle redirects
|
|
603
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
604
|
+
downloadFile(res.headers.location).then(resolve).catch(reject);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (res.statusCode !== 200) {
|
|
609
|
+
reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
let data = '';
|
|
614
|
+
res.on('data', chunk => data += chunk);
|
|
615
|
+
res.on('end', () => resolve(data));
|
|
616
|
+
}).on('error', reject);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function setupClaude() {
|
|
621
|
+
console.log(`
|
|
622
|
+
${colors.cyan}${colors.bold}Mags Claude Code Skill Setup${colors.reset}
|
|
623
|
+
|
|
624
|
+
This will install the Mags skill for Claude Code, allowing you to run
|
|
625
|
+
scripts on instant VMs directly from Claude.
|
|
626
|
+
`);
|
|
627
|
+
|
|
628
|
+
// Ask where to install
|
|
629
|
+
console.log(`${colors.bold}Installation options:${colors.reset}`);
|
|
630
|
+
console.log(` 1. Global (all projects) - ~/.claude/commands/`);
|
|
631
|
+
console.log(` 2. Current project only - ./.claude/commands/`);
|
|
632
|
+
console.log('');
|
|
633
|
+
|
|
634
|
+
const choice = await prompt(`${colors.bold}Choose installation type [1/2]: ${colors.reset}`);
|
|
635
|
+
|
|
636
|
+
let targetDir;
|
|
637
|
+
if (choice === '2') {
|
|
638
|
+
targetDir = path.join(process.cwd(), '.claude', 'commands');
|
|
639
|
+
log('blue', 'Installing to current project...');
|
|
640
|
+
} else {
|
|
641
|
+
targetDir = CLAUDE_GLOBAL_DIR;
|
|
642
|
+
log('blue', 'Installing globally...');
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Create directory if it doesn't exist
|
|
646
|
+
try {
|
|
647
|
+
if (!fs.existsSync(targetDir)) {
|
|
648
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
649
|
+
}
|
|
650
|
+
} catch (err) {
|
|
651
|
+
log('red', `Failed to create directory: ${err.message}`);
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Download skill file
|
|
656
|
+
log('blue', `Downloading skill from ${SKILL_URL}...`);
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
const skillContent = await downloadFile(SKILL_URL);
|
|
660
|
+
|
|
661
|
+
const targetFile = path.join(targetDir, 'mags.md');
|
|
662
|
+
fs.writeFileSync(targetFile, skillContent);
|
|
663
|
+
|
|
664
|
+
console.log('');
|
|
665
|
+
log('green', 'Mags skill installed successfully!');
|
|
666
|
+
log('gray', `Location: ${targetFile}`);
|
|
667
|
+
console.log('');
|
|
668
|
+
log('cyan', 'Next steps:');
|
|
669
|
+
console.log(` 1. Make sure you're logged in: ${colors.bold}mags login${colors.reset}`);
|
|
670
|
+
console.log(` 2. In Claude Code, use: ${colors.bold}/mags <your request>${colors.reset}`);
|
|
671
|
+
console.log('');
|
|
672
|
+
log('cyan', 'Example prompts:');
|
|
673
|
+
console.log(' /mags run echo Hello World');
|
|
674
|
+
console.log(' /mags create a python environment with numpy');
|
|
675
|
+
console.log(' /mags run a flask server and give me the URL');
|
|
676
|
+
|
|
677
|
+
} catch (err) {
|
|
678
|
+
log('red', `Failed to download skill: ${err.message}`);
|
|
679
|
+
log('gray', `You can manually download from: ${SKILL_URL}`);
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
589
684
|
async function sshToJob(nameOrId) {
|
|
590
685
|
if (!nameOrId) {
|
|
591
686
|
log('red', 'Error: Job name or ID required');
|
|
@@ -643,6 +738,7 @@ async function sshToJob(nameOrId) {
|
|
|
643
738
|
|
|
644
739
|
// Build SSH arguments
|
|
645
740
|
const sshArgs = [
|
|
741
|
+
'-tt', // Force TTY allocation even when stdin isn't a terminal
|
|
646
742
|
'-o', 'StrictHostKeyChecking=no',
|
|
647
743
|
'-o', 'UserKnownHostsFile=/dev/null',
|
|
648
744
|
'-o', 'LogLevel=ERROR',
|
|
@@ -715,7 +811,7 @@ async function main() {
|
|
|
715
811
|
break;
|
|
716
812
|
case '--version':
|
|
717
813
|
case '-v':
|
|
718
|
-
console.log('mags v1.
|
|
814
|
+
console.log('mags v1.4.0');
|
|
719
815
|
process.exit(0);
|
|
720
816
|
break;
|
|
721
817
|
case 'new':
|
|
@@ -750,6 +846,9 @@ async function main() {
|
|
|
750
846
|
await requireAuth();
|
|
751
847
|
await stopJob(args[1]);
|
|
752
848
|
break;
|
|
849
|
+
case 'setup-claude':
|
|
850
|
+
await setupClaude();
|
|
851
|
+
break;
|
|
753
852
|
default:
|
|
754
853
|
if (!command) {
|
|
755
854
|
// No command - check if logged in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpiecloud/mags",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Mags CLI - Execute scripts on Magpie's instant VM infrastructure",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
"cloud",
|
|
18
18
|
"serverless",
|
|
19
19
|
"execution",
|
|
20
|
-
"cli"
|
|
20
|
+
"cli",
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code"
|
|
21
23
|
],
|
|
22
24
|
"author": "Magpie Cloud",
|
|
23
25
|
"license": "MIT",
|
|
@@ -25,7 +27,7 @@
|
|
|
25
27
|
"type": "git",
|
|
26
28
|
"url": "https://github.com/magpiecloud/mags"
|
|
27
29
|
},
|
|
28
|
-
"homepage": "https://
|
|
30
|
+
"homepage": "https://mags.run",
|
|
29
31
|
"bugs": {
|
|
30
32
|
"url": "https://github.com/magpiecloud/mags/issues"
|
|
31
33
|
},
|