@magpiecloud/mags 1.8.16 → 1.8.17

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 (44) hide show
  1. package/API.md +388 -0
  2. package/Mags-API.postman_collection.json +374 -0
  3. package/QUICKSTART.md +295 -0
  4. package/README.md +378 -95
  5. package/bin/mags.js +23 -2
  6. package/deploy-page.sh +171 -0
  7. package/index.js +0 -2
  8. package/mags +0 -0
  9. package/mags.sh +270 -0
  10. package/nodejs/README.md +197 -0
  11. package/nodejs/bin/mags.js +1882 -0
  12. package/nodejs/index.js +603 -0
  13. package/nodejs/package.json +45 -0
  14. package/package.json +3 -18
  15. package/python/INTEGRATION.md +800 -0
  16. package/python/README.md +161 -0
  17. package/python/dist/magpie_mags-1.3.8-py3-none-any.whl +0 -0
  18. package/python/dist/magpie_mags-1.3.8.tar.gz +0 -0
  19. package/python/examples/demo.py +181 -0
  20. package/python/pyproject.toml +39 -0
  21. package/python/src/magpie_mags.egg-info/PKG-INFO +186 -0
  22. package/python/src/magpie_mags.egg-info/SOURCES.txt +9 -0
  23. package/python/src/magpie_mags.egg-info/dependency_links.txt +1 -0
  24. package/python/src/magpie_mags.egg-info/requires.txt +1 -0
  25. package/python/src/magpie_mags.egg-info/top_level.txt +1 -0
  26. package/python/src/mags/__init__.py +6 -0
  27. package/python/src/mags/client.py +527 -0
  28. package/python/test_sdk.py +78 -0
  29. package/skill.md +153 -0
  30. package/website/api.html +1095 -0
  31. package/website/claude-skill.html +481 -0
  32. package/website/cookbook/hn-marketing.html +410 -0
  33. package/website/cookbook/hn-marketing.sh +42 -0
  34. package/website/cookbook.html +282 -0
  35. package/website/docs.html +677 -0
  36. package/website/env.js +4 -0
  37. package/website/index.html +801 -0
  38. package/website/llms.txt +334 -0
  39. package/website/login.html +108 -0
  40. package/website/mags.md +210 -0
  41. package/website/script.js +453 -0
  42. package/website/styles.css +1075 -0
  43. package/website/tokens.html +169 -0
  44. package/website/usage.html +185 -0
package/README.md CHANGED
@@ -1,6 +1,17 @@
1
- # @magpiecloud/mags
1
+ # Mags - Instant VM Execution
2
2
 
3
- Execute scripts instantly on Magpie's microVM infrastructure. VMs boot in <100ms from a warm pool.
3
+ Execute scripts instantly on Magpie's microVM infrastructure. VMs boot in <100ms from a warm pool, giving you instant access to isolated compute environments.
4
+
5
+ ## What is Mags?
6
+
7
+ Mags is a CLI and SDK for running scripts on ephemeral microVMs. Each execution gets its own isolated VM that:
8
+
9
+ - Boots in ~300ms (from warm pool)
10
+ - Supports optional S3-backed persistent workspaces (with `-p` flag)
11
+ - Syncs /root directory automatically when persistence is enabled
12
+ - Can expose public URLs for web services
13
+ - Provides SSH access through secure proxy
14
+ - Auto-sleeps when idle and wakes on request
4
15
 
5
16
  ## Installation
6
17
 
@@ -8,176 +19,411 @@ Execute scripts instantly on Magpie's microVM infrastructure. VMs boot in <100ms
8
19
  npm install -g @magpiecloud/mags
9
20
  ```
10
21
 
11
- ## Quick Start
22
+ ## Authentication
12
23
 
13
- ### 1. Login to Magpie
24
+ ### Interactive Login (Recommended)
14
25
 
15
26
  ```bash
16
27
  mags login
17
28
  ```
18
29
 
19
- This will open your browser to create an API token. Paste the token when prompted, and it will be saved for future use.
30
+ This opens your browser to the Magpie dashboard where you can create an API token. Paste the token when prompted, and it will be saved to `~/.mags/config.json` for all future commands.
20
31
 
21
- ### 2. Create a sandbox
32
+ ### Auth Commands
22
33
 
23
34
  ```bash
24
- mags new myproject # Local disk only
25
- mags new myproject -p # With S3 persistence
26
- mags ssh myproject
35
+ mags login # Authenticate with Magpie
36
+ mags whoami # Check current authentication status
37
+ mags logout # Remove saved credentials
27
38
  ```
28
39
 
29
- ### 3. Or run a script
40
+ ### Environment Variable (Alternative)
30
41
 
31
42
  ```bash
32
- mags run 'echo Hello World'
43
+ export MAGS_API_TOKEN="your-token-here"
33
44
  ```
34
45
 
35
- ## Authentication
46
+ ## Quick Start
36
47
 
37
- ### Interactive Login (Recommended)
48
+ ### Run a simple command
38
49
 
39
50
  ```bash
40
- mags login
51
+ mags run 'echo Hello World'
41
52
  ```
42
53
 
43
- Opens the Magpie dashboard in your browser where you can create an API token. The token is saved to `~/.mags/config.json` and used automatically for all future commands.
44
-
45
- ### Other Auth Commands
54
+ ### Create a sandbox
46
55
 
47
56
  ```bash
48
- mags whoami # Check current authentication status
49
- mags logout # Remove saved credentials
57
+ # Create a new VM (local disk only)
58
+ mags new myproject
59
+
60
+ # Create with S3 data persistence
61
+ mags new myproject -p
62
+
63
+ # SSH into it
64
+ mags ssh myproject
50
65
  ```
51
66
 
52
- ### Environment Variable
67
+ ### Run with a persistent workspace
53
68
 
54
- You can also set the token via environment variable (overrides saved config):
69
+ Workspaces persist data between runs using S3 sync:
55
70
 
56
71
  ```bash
57
- export MAGS_API_TOKEN="your-token-here"
72
+ # First run - install dependencies
73
+ mags run -w myproject 'apk add python3 py3-pip && pip install requests'
74
+
75
+ # Second run - dependencies are still there
76
+ mags run -w myproject 'python3 -c "import requests; print(requests.__version__)"'
58
77
  ```
59
78
 
60
- ## CLI Commands
79
+ ### Deploy a web service with public URL
61
80
 
62
81
  ```bash
63
- # Create a sandbox (local disk)
64
- mags new myproject
82
+ mags run -p --url 'python3 -m http.server 8080'
83
+ # Returns: https://abc123.apps.magpiecloud.com
84
+ ```
65
85
 
66
- # Create with S3 persistence
67
- mags new myproject -p
86
+ ## CLI Reference
68
87
 
69
- # SSH into it
70
- mags ssh myproject
88
+ ### Commands
71
89
 
72
- # Run a simple command
73
- mags run 'echo Hello'
90
+ | Command | Description |
91
+ |---------|-------------|
92
+ | `mags login` | Authenticate with Magpie (saves token locally) |
93
+ | `mags logout` | Remove saved credentials |
94
+ | `mags whoami` | Show current authentication status |
95
+ | `mags new <workspace> [-p]` | Create a VM sandbox (add `-p` for S3 persistence) |
96
+ | `mags run [options] <script>` | Execute a script on a microVM |
97
+ | `mags ssh <workspace>` | Open interactive SSH session to a VM |
98
+ | `mags status <job-id>` | Get job status |
99
+ | `mags logs <job-id>` | Get job logs |
100
+ | `mags list` | List recent jobs |
101
+ | `mags url <job-id> [port]` | Enable URL access for a job |
102
+ | `mags stop <job-id>` | Stop a running job |
103
+ | `mags set <name\|id> [options]` | Update VM settings (`--no-sleep`, `--sleep`) |
74
104
 
75
- # With persistent workspace (S3 sync)
76
- mags run -w my-project 'apk add nodejs && node --version'
105
+ ### Run Options
77
106
 
78
- # Persistent VM with public URL
79
- mags run -w webapp -p --url 'python3 -m http.server 8080'
107
+ | Flag | Description | Default |
108
+ |------|-------------|---------|
109
+ | `-w, --workspace <id>` | Use persistent workspace (S3 sync) | auto-generated |
110
+ | `-p, --persistent` | Keep VM alive after script completes | false |
111
+ | `--url` | Enable public URL access (requires -p) | false |
112
+ | `--port <port>` | Port to expose for URL access | 8080 |
113
+ | `--no-sleep` | Keep VM always running, never auto-sleep (requires -p) | false |
114
+ | `--startup-command <cmd>` | Command to run when VM wakes from sleep | none |
115
+
116
+ ## SSH Access
80
117
 
81
- # With startup command (for auto-wake)
82
- mags run -w webapp -p --url --startup-command 'npm start' 'npm install && npm start'
118
+ Connect to any running VM via SSH:
83
119
 
84
- # Custom port
85
- mags run -w webapp -p --url --port 3000 'npm start'
120
+ ```bash
121
+ mags ssh myproject
122
+ ```
86
123
 
87
- # Enable URL for existing job
88
- mags url <job-id>
89
- mags url <job-id> 8080
124
+ ### SSH Features
90
125
 
91
- # Other commands
92
- mags status <job-id>
93
- mags logs <job-id>
94
- mags list
95
- mags stop <job-id>
126
+ - **Secure proxy**: Connect via `api.magpiecloud.com:PORT` (agent IPs are hidden)
127
+ - **PTY support**: Full interactive terminal with colors and editors
128
+ - **Key-based auth**: Automatic SSH key management
129
+ - **Hostname**: VMs use `mags-vm` as hostname
96
130
 
97
- # Install Claude Code skill
98
- mags setup-claude
131
+ ### SSH Session Example
132
+
133
+ ```bash
134
+ $ mags ssh myproject
135
+ Connecting to api.magpiecloud.com:40006...
136
+ (Use Ctrl+D or 'exit' to disconnect)
137
+
138
+ mags-vm:~# ls
139
+ index.html test.txt
140
+
141
+ mags-vm:~# python3 --version
142
+ Python 3.11.6
143
+
144
+ mags-vm:~# exit
99
145
  ```
100
146
 
101
- ## Claude Code Integration
147
+ ## Workspaces & Persistence
148
+
149
+ ### How It Works
150
+
151
+ When you use a workspace, Mags:
102
152
 
103
- Install the Mags skill for Claude Code to run scripts directly from Claude:
153
+ 1. Mounts a JuiceFS filesystem backed by S3
154
+ 2. Creates an OverlayFS to capture all filesystem changes
155
+ 3. Syncs your `/root` directory to S3 every 30 seconds
156
+ 4. Restores your files when you reconnect
157
+
158
+ ### What Gets Persisted
159
+
160
+ - `/root` - Your home directory (synced every 30 seconds)
161
+ - `/jfs` - Direct JuiceFS mount (instant S3 sync)
162
+ - Installed packages and dependencies
163
+ - Configuration files and dotfiles
164
+
165
+ ### Sync Behavior
166
+
167
+ | Event | Behavior |
168
+ |-------|----------|
169
+ | While running | Auto-sync every 30 seconds |
170
+ | On stop | Full sync before VM terminates |
171
+ | On wake | Previous state restored instantly |
172
+
173
+ ## Usage Examples
174
+
175
+ ### Basic Execution
104
176
 
105
177
  ```bash
106
- mags setup-claude
178
+ # Simple command
179
+ mags run 'echo Hello World'
180
+
181
+ # Multi-line script
182
+ mags run 'apk add curl && curl -s https://api.github.com/users/octocat | head -5'
183
+
184
+ # With environment info
185
+ mags run 'uname -a && cat /etc/os-release'
107
186
  ```
108
187
 
109
- This lets you use natural language commands like:
110
- - `/mags run echo Hello World`
111
- - `/mags create a python environment with numpy and pandas`
112
- - `/mags run a flask server and give me the public URL`
188
+ ### Persistent Workspaces
113
189
 
114
- See [mags.run/claude-skill.html](https://mags.run/claude-skill.html) for full documentation.
190
+ ```bash
191
+ # Create a workspace with dependencies
192
+ mags run -w ml-project 'apk add python3 py3-pip && pip install numpy pandas scikit-learn'
115
193
 
116
- ## CLI Flags
194
+ # Run scripts using the workspace
195
+ mags run -w ml-project 'python3 train.py'
117
196
 
118
- | Flag | Description | Default |
119
- |------|-------------|---------|
120
- | `-w, --workspace` | Workspace ID for persistent storage | auto |
121
- | `-p, --persistent` | Keep VM alive for URL/SSH access | false |
122
- | `--url` | Enable public URL access (requires -p) | false |
123
- | `--port` | Port to expose for URL access | 8080 |
124
- | `--startup-command` | Command when VM wakes from sleep | none |
197
+ # All files in /root are persisted
198
+ mags run -w ml-project 'ls -la /root'
199
+ ```
125
200
 
126
- ## SSH Access
201
+ ### Web Services
202
+
203
+ ```bash
204
+ # Simple static server
205
+ mags run -p --url 'python3 -m http.server 8080'
206
+
207
+ # Node.js app
208
+ mags run -w myapp -p --url --port 3000 'npm install && npm start'
209
+
210
+ # Flask app
211
+ mags run -w flask-app -p --url 'pip install flask && python app.py'
212
+
213
+ # Custom startup command for auto-wake
214
+ mags run -w api -p --url --startup-command 'python server.py' 'pip install -r requirements.txt && python server.py'
215
+ ```
127
216
 
128
- Connect to any running VM:
217
+ ### Always-On VMs
218
+
219
+ By default, persistent VMs auto-sleep after 10 minutes of inactivity and wake on the next request. Use `--no-sleep` to keep a VM running 24/7:
129
220
 
130
221
  ```bash
131
- mags ssh myproject
222
+ # Always-on API server (never auto-sleeps)
223
+ mags run -w my-api -p --no-sleep --url --port 3000 'npm start'
224
+
225
+ # Always-on background worker
226
+ mags run -w worker -p --no-sleep 'python worker.py'
132
227
  ```
133
228
 
134
- Features:
135
- - Connects via secure proxy (`api.magpiecloud.com:PORT`)
136
- - Full PTY support for interactive terminals
137
- - Automatic SSH key management
138
- - Hostname: `mags-vm`
229
+ If an always-on VM's host becomes unhealthy, the orchestrator automatically re-provisions it on a healthy agent within ~60 seconds.
139
230
 
140
- ## Workspaces & Persistence
231
+ ### Interactive Development
141
232
 
142
- When using persistent mode (`-p`), your `/root` directory syncs to S3:
143
- - **Auto-sync**: Every 30 seconds while running
144
- - **On stop**: Full sync before VM terminates
145
- - **On wake**: Previous state restored
233
+ ```bash
234
+ # Create a dev environment (local disk)
235
+ mags new dev-env
236
+
237
+ # SSH in and work
238
+ mags ssh dev-env
146
239
 
147
- Without `-p`, data lives on local disk only and is cleaned up when the VM is destroyed.
240
+ # Inside the VM:
241
+ mags-vm:~# apk add git nodejs npm
242
+ mags-vm:~# git clone https://github.com/user/repo
243
+ mags-vm:~# cd repo && npm install
244
+ mags-vm:~# npm run dev
245
+ ```
148
246
 
149
247
  ## Node.js SDK
150
248
 
249
+ For programmatic access, use the SDK:
250
+
151
251
  ```javascript
152
252
  const Mags = require('@magpiecloud/mags');
153
253
 
154
254
  const mags = new Mags({
155
255
  apiToken: process.env.MAGS_API_TOKEN
156
256
  });
257
+ ```
258
+
259
+ ### SDK Methods
260
+
261
+ #### Jobs
262
+
263
+ | Method | Description |
264
+ |--------|-------------|
265
+ | `run(script, options)` | Submit a job. Options: `name`, `workspaceId`, `baseWorkspaceId`, `persistent`, `noSleep`, `ephemeral`, `startupCommand`, `environment`, `fileIds`, `diskGb` |
266
+ | `runAndWait(script, options)` | Submit and block until done. Extra options: `timeout`, `pollInterval` |
267
+ | `new(name, options)` | Create a VM sandbox and wait until running. Options: `persistent`, `baseWorkspaceId`, `diskGb`, `timeout` |
268
+ | `status(requestId)` | Get job status |
269
+ | `logs(requestId)` | Get job logs |
270
+ | `list({page, pageSize})` | List recent jobs (paginated) |
271
+ | `findJob(nameOrId)` | Find job by name, workspace ID, or request ID |
272
+ | `updateJob(requestId, {startupCommand, noSleep})` | Update job settings |
273
+ | `stop(nameOrId)` | Stop a job (accepts name, workspace ID, or request ID) |
274
+ | `exec(nameOrId, command, {timeout})` | Execute a command on a running/sleeping VM via SSH |
275
+ | `sync(requestId)` | Sync workspace to S3 without stopping |
276
+ | `resize(workspace, diskGb)` | Resize workspace disk (stops VM, creates new one) |
277
+ | `usage({windowDays})` | Get aggregated usage summary |
278
+
279
+ #### URL & Access
280
+
281
+ | Method | Description |
282
+ |--------|-------------|
283
+ | `enableAccess(requestId, port)` | Enable SSH (port 22) or HTTP access (default 8080) |
284
+ | `url(nameOrId, port)` | Enable public URL and return the full URL |
285
+ | `urlAliasCreate(subdomain, workspaceId, domain)` | Create a stable URL alias for a workspace |
286
+ | `urlAliasList()` | List all URL aliases |
287
+ | `urlAliasDelete(subdomain)` | Delete a URL alias |
288
+
289
+ #### Workspaces
290
+
291
+ | Method | Description |
292
+ |--------|-------------|
293
+ | `listWorkspaces()` | List all workspaces |
294
+ | `deleteWorkspace(workspaceId)` | Delete a workspace and its S3 data |
295
+
296
+ #### Files
297
+
298
+ | Method | Description |
299
+ |--------|-------------|
300
+ | `uploadFiles(filePaths)` | Upload local files, returns array of file IDs |
301
+
302
+ #### Cron Jobs
303
+
304
+ | Method | Description |
305
+ |--------|-------------|
306
+ | `cronCreate({name, cronExpression, script, workspaceId, environment, persistent})` | Create a scheduled job |
307
+ | `cronList()` | List all cron jobs |
308
+ | `cronGet(id)` | Get a cron job |
309
+ | `cronUpdate(id, updates)` | Update a cron job |
310
+ | `cronDelete(id)` | Delete a cron job |
311
+
312
+ ### SDK Examples
157
313
 
314
+ ```javascript
158
315
  // Run and wait for completion
159
316
  const result = await mags.runAndWait('echo Hello World');
160
317
  console.log(result.logs);
161
318
 
162
- // Run with workspace
163
- const { requestId } = await mags.run('python script.py', {
319
+ // Create a sandbox (local disk)
320
+ await mags.new('myproject');
321
+
322
+ // Create a sandbox with S3 persistence
323
+ await mags.new('myproject', { persistent: true });
324
+
325
+ // Execute command on existing VM
326
+ const { output } = await mags.exec('myproject', 'ls -la /root');
327
+ console.log(output);
328
+
329
+ // Run with workspace and options
330
+ const job = await mags.run('python script.py', {
164
331
  workspaceId: 'myproject',
165
- persistent: true
332
+ persistent: true,
333
+ diskGb: 5,
334
+ startupCommand: 'python server.py'
335
+ });
336
+
337
+ // Always-on VM (never auto-sleeps)
338
+ await mags.run('node server.js', {
339
+ workspaceId: 'my-api',
340
+ persistent: true,
341
+ noSleep: true
166
342
  });
167
343
 
168
- // Get status
169
- const status = await mags.status(requestId);
344
+ // Enable public URL
345
+ const { url } = await mags.url('my-api', 3000);
346
+ console.log(url); // https://abc123.apps.magpiecloud.com
347
+
348
+ // Create stable URL alias
349
+ await mags.urlAliasCreate('my-app', 'my-api');
350
+
351
+ // Resize workspace disk
352
+ await mags.resize('myproject', 10); // 10GB
353
+
354
+ // Stop a job by name
355
+ await mags.stop('myproject');
356
+ ```
357
+
358
+ ## API Endpoints
359
+
360
+ For direct API access:
170
361
 
171
- // Enable URL access
172
- await mags.enableUrl(requestId, 8080);
362
+ | Endpoint | Method | Description |
363
+ |----------|--------|-------------|
364
+ | `/api/v1/mags-jobs` | POST | Submit a new job |
365
+ | `/api/v1/mags-jobs` | GET | List jobs (paginated) |
366
+ | `/api/v1/mags-jobs/{id}/status` | GET | Get job status |
367
+ | `/api/v1/mags-jobs/{id}/logs` | GET | Get job logs |
368
+ | `/api/v1/mags-jobs/{id}/access` | POST | Enable SSH/URL access |
369
+ | `/api/v1/mags-jobs/{id}` | PATCH | Update job settings |
173
370
 
174
- // List jobs
175
- const jobs = await mags.list({ page: 1, pageSize: 10 });
371
+ ### Example API Request
176
372
 
177
- // Stop a job
178
- await mags.stop(requestId);
373
+ ```bash
374
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
375
+ -H "Authorization: Bearer $MAGS_API_TOKEN" \
376
+ -H "Content-Type: application/json" \
377
+ -d '{
378
+ "script": "echo Hello World",
379
+ "type": "inline",
380
+ "workspace_id": "myproject",
381
+ "persistent": true,
382
+ "no_sleep": true
383
+ }'
179
384
  ```
180
385
 
386
+ ## Job Status Values
387
+
388
+ | Status | Description |
389
+ |--------|-------------|
390
+ | `pending` | Job queued, waiting for VM |
391
+ | `running` | Script executing |
392
+ | `sleeping` | Persistent VM idle (wakes on request) |
393
+ | `completed` | Script finished successfully |
394
+ | `error` | Script failed |
395
+
396
+ ## VM Environment
397
+
398
+ Each VM runs Alpine Linux with hostname `mags-vm` and includes:
399
+
400
+ - `/root` - Persistent home directory (synced to S3)
401
+ - `/jfs` - Direct JuiceFS mount
402
+ - Common tools: curl, wget, git, python3, nodejs
403
+ - Package manager: `apk add <package>`
404
+
405
+ ### Installing Packages
406
+
407
+ ```bash
408
+ # Python packages
409
+ mags run 'apk add python3 py3-pip && pip install requests flask pandas'
410
+
411
+ # Node.js packages
412
+ mags run 'apk add nodejs npm && npm install -g express'
413
+
414
+ # System packages
415
+ mags run 'apk add ffmpeg imagemagick'
416
+ ```
417
+
418
+ ## Performance
419
+
420
+ | Metric | Value |
421
+ |--------|-------|
422
+ | Warm start | ~300ms |
423
+ | Cold start | ~4 seconds |
424
+ | Script overhead | ~50ms |
425
+ | Workspace sync | Every 30 seconds |
426
+
181
427
  ## Environment Variables
182
428
 
183
429
  | Variable | Description | Default |
@@ -185,12 +431,49 @@ await mags.stop(requestId);
185
431
  | `MAGS_API_TOKEN` | Your API token (required) | - |
186
432
  | `MAGS_API_URL` | API endpoint | https://api.magpiecloud.com |
187
433
 
188
- ## Performance
434
+ ## Troubleshooting
435
+
436
+ ### "API token required"
437
+
438
+ Set your API token:
439
+ ```bash
440
+ mags login
441
+ # or
442
+ export MAGS_API_TOKEN="your-token-here"
443
+ ```
444
+
445
+ ### "Job timed out"
446
+
447
+ Increase the timeout or check if your script is hanging:
448
+ ```bash
449
+ mags run --timeout 600 'long-running-script.sh'
450
+ ```
451
+
452
+ ### "Workspace not found"
453
+
454
+ Workspace IDs are case-sensitive. Check with:
455
+ ```bash
456
+ mags list
457
+ ```
458
+
459
+ ### URL not accessible
460
+
461
+ Make sure you're using both `-p` (persistent) and `--url` flags, and your app is listening on the correct port:
462
+ ```bash
463
+ mags run -p --url --port 3000 'node server.js'
464
+ ```
465
+
466
+ ### SSH connection issues
467
+
468
+ SSH connects through our proxy. If you have issues:
469
+ 1. Ensure the job is running: `mags status <id>`
470
+ 2. Try reconnecting: `mags ssh <workspace>`
471
+
472
+ ## Support
189
473
 
190
- - **Warm start**: <100ms (VM from pool)
191
- - **Cold start**: ~4 seconds (new VM boot)
192
- - **Script overhead**: ~50ms
193
- - **Workspace sync**: Every 30 seconds
474
+ - Documentation: https://mags.run
475
+ - API Reference: https://mags.run/api.html
476
+ - Dashboard: https://mags.run/tokens.html
194
477
 
195
478
  ## License
196
479
 
package/bin/mags.js CHANGED
@@ -230,6 +230,7 @@ ${colors.bold}Run Options:${colors.reset}
230
230
  --url Enable public URL access (requires -p)
231
231
  --port <port> Port to expose for URL (default: 8080)
232
232
  --startup-command <cmd> Command to run when VM wakes from sleep
233
+ -t, --type <type> VM rootfs type: standard, claude, pi
233
234
 
234
235
  ${colors.bold}Cron Commands:${colors.reset}
235
236
  cron add [options] <script> Create a scheduled cron job
@@ -409,6 +410,7 @@ async function newVM(args) {
409
410
  let baseWorkspace = null;
410
411
  let diskGB = 0;
411
412
  let persistent = false;
413
+ let rootfsType = '';
412
414
 
413
415
  for (let i = 0; i < args.length; i++) {
414
416
  if (args[i] === '-p' || args[i] === '--persistent') {
@@ -417,6 +419,8 @@ async function newVM(args) {
417
419
  baseWorkspace = args[++i];
418
420
  } else if (args[i] === '--disk' && args[i + 1]) {
419
421
  diskGB = parseInt(args[++i]) || 0;
422
+ } else if ((args[i] === '--type' || args[i] === '-t') && args[i + 1]) {
423
+ rootfsType = args[++i];
420
424
  } else if (!name) {
421
425
  name = args[i];
422
426
  }
@@ -424,8 +428,9 @@ async function newVM(args) {
424
428
 
425
429
  if (!name) {
426
430
  log('red', 'Error: Name required');
427
- console.log(`\nUsage: mags new <name> [-p] [--base <workspace>] [--disk <GB>]`);
428
- console.log(` -p, --persistent Enable S3 data persistence\n`);
431
+ console.log(`\nUsage: mags new <name> [-p] [--base <workspace>] [--disk <GB>] [--type <type>]`);
432
+ console.log(` -p, --persistent Enable S3 data persistence`);
433
+ console.log(` -t, --type VM type: standard (default), claude, pi\n`);
429
434
  process.exit(1);
430
435
  }
431
436
 
@@ -457,6 +462,7 @@ async function newVM(args) {
457
462
  };
458
463
  if (baseWorkspace) payload.base_workspace_id = baseWorkspace;
459
464
  if (diskGB) payload.disk_gb = diskGB;
465
+ if (rootfsType) payload.rootfs_type = rootfsType;
460
466
 
461
467
  const response = await request('POST', '/api/v1/mags-jobs', payload);
462
468
 
@@ -506,6 +512,7 @@ async function runJob(args) {
506
512
  let startupCommand = '';
507
513
  let diskGB = 0;
508
514
  let fileArgs = [];
515
+ let rootfsType = '';
509
516
 
510
517
  // Parse flags
511
518
  for (let i = 0; i < args.length; i++) {
@@ -546,6 +553,10 @@ async function runJob(args) {
546
553
  case '--startup-command':
547
554
  startupCommand = args[++i];
548
555
  break;
556
+ case '--type':
557
+ case '-t':
558
+ rootfsType = args[++i];
559
+ break;
549
560
  default:
550
561
  script = args.slice(i).join(' ');
551
562
  i = args.length;
@@ -608,6 +619,7 @@ async function runJob(args) {
608
619
  if (startupCommand) payload.startup_command = startupCommand;
609
620
  if (fileIds.length > 0) payload.file_ids = fileIds;
610
621
  if (diskGB) payload.disk_gb = diskGB;
622
+ if (rootfsType) payload.rootfs_type = rootfsType;
611
623
 
612
624
  const response = await request('POST', '/api/v1/mags-jobs', payload);
613
625
 
@@ -1508,6 +1520,11 @@ async function proxyTunnel(jobID) {
1508
1520
 
1509
1521
  ws.on('open', () => {
1510
1522
  // Don't flush yet — wait for first message (SSH key)
1523
+ // Send pings every 30s to keep the connection alive through proxies/CDNs
1524
+ const pingInterval = setInterval(() => {
1525
+ if (ws.readyState === WebSocket.OPEN) ws.ping();
1526
+ }, 30000);
1527
+ ws.on('close', () => clearInterval(pingInterval));
1511
1528
  });
1512
1529
 
1513
1530
  ws.on('message', (data, isBinary) => {
@@ -1672,6 +1689,8 @@ async function sshToJob(nameOrId) {
1672
1689
  '-o', 'StrictHostKeyChecking=no',
1673
1690
  '-o', 'UserKnownHostsFile=/dev/null',
1674
1691
  '-o', 'LogLevel=ERROR',
1692
+ '-o', 'ServerAliveInterval=15',
1693
+ '-o', 'ServerAliveCountMax=4',
1675
1694
  '-o', `ProxyCommand=${magsPath} proxy ${jobID}`,
1676
1695
  '-i', keyFile,
1677
1696
  'root@mags-vm',
@@ -1739,6 +1758,8 @@ async function execOnJob(nameOrId, command) {
1739
1758
  '-o', 'StrictHostKeyChecking=no',
1740
1759
  '-o', 'UserKnownHostsFile=/dev/null',
1741
1760
  '-o', 'LogLevel=ERROR',
1761
+ '-o', 'ServerAliveInterval=15',
1762
+ '-o', 'ServerAliveCountMax=4',
1742
1763
  '-o', `ProxyCommand=${magsPath} proxy ${jobID}`,
1743
1764
  '-i', keyFile,
1744
1765
  ];