@magpiecloud/mags 1.8.11 → 1.8.13
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 +16 -12
- package/bin/mags.js +31 -5
- package/index.js +7 -2
- package/nodejs/README.md +10 -4
- package/package.json +1 -1
- package/python/README.md +7 -3
- package/python/dist/magpie_mags-1.3.5-py3-none-any.whl +0 -0
- package/python/dist/magpie_mags-1.3.5.tar.gz +0 -0
- package/python/pyproject.toml +1 -1
- package/python/src/magpie_mags.egg-info/PKG-INFO +2 -2
- package/python/src/mags/client.py +13 -3
- package/website/api.html +137 -8
- package/website/cookbook.html +1 -1
- package/website/index.html +149 -95
- package/website/llms.txt +76 -31
- package/website/mags.md +3 -2
- package/python/dist/magpie_mags-1.3.4-py3-none-any.whl +0 -0
- package/python/dist/magpie_mags-1.3.4.tar.gz +0 -0
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ Execute scripts instantly on Magpie's microVM infrastructure. VMs boot in <100ms
|
|
|
6
6
|
|
|
7
7
|
Mags is a CLI and SDK for running scripts on ephemeral microVMs. Each execution gets its own isolated VM that:
|
|
8
8
|
|
|
9
|
-
- Boots in
|
|
10
|
-
-
|
|
11
|
-
- Syncs /root directory automatically
|
|
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
12
|
- Can expose public URLs for web services
|
|
13
13
|
- Provides SSH access through secure proxy
|
|
14
14
|
- Auto-sleeps when idle and wakes on request
|
|
@@ -51,16 +51,17 @@ export MAGS_API_TOKEN="your-token-here"
|
|
|
51
51
|
mags run 'echo Hello World'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
### Create a
|
|
54
|
+
### Create a sandbox
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
# Create a new VM
|
|
57
|
+
# Create a new VM (local disk only)
|
|
58
58
|
mags new myproject
|
|
59
59
|
|
|
60
|
+
# Create with S3 data persistence
|
|
61
|
+
mags new myproject -p
|
|
62
|
+
|
|
60
63
|
# SSH into it
|
|
61
64
|
mags ssh myproject
|
|
62
|
-
|
|
63
|
-
# Your /root directory persists to S3 automatically
|
|
64
65
|
```
|
|
65
66
|
|
|
66
67
|
### Run with a persistent workspace
|
|
@@ -91,7 +92,7 @@ mags run -p --url 'python3 -m http.server 8080'
|
|
|
91
92
|
| `mags login` | Authenticate with Magpie (saves token locally) |
|
|
92
93
|
| `mags logout` | Remove saved credentials |
|
|
93
94
|
| `mags whoami` | Show current authentication status |
|
|
94
|
-
| `mags new <workspace
|
|
95
|
+
| `mags new <workspace> [-p]` | Create a VM sandbox (add `-p` for S3 persistence) |
|
|
95
96
|
| `mags run [options] <script>` | Execute a script on a microVM |
|
|
96
97
|
| `mags ssh <workspace>` | Open interactive SSH session to a VM |
|
|
97
98
|
| `mags status <job-id>` | Get job status |
|
|
@@ -230,7 +231,7 @@ If an always-on VM's host becomes unhealthy, the orchestrator automatically re-p
|
|
|
230
231
|
### Interactive Development
|
|
231
232
|
|
|
232
233
|
```bash
|
|
233
|
-
# Create a
|
|
234
|
+
# Create a dev environment (local disk)
|
|
234
235
|
mags new dev-env
|
|
235
236
|
|
|
236
237
|
# SSH in and work
|
|
@@ -263,7 +264,7 @@ const mags = new Mags({
|
|
|
263
264
|
|--------|-------------|
|
|
264
265
|
| `run(script, options)` | Submit a job. Options: `name`, `workspaceId`, `baseWorkspaceId`, `persistent`, `noSleep`, `ephemeral`, `startupCommand`, `environment`, `fileIds`, `diskGb` |
|
|
265
266
|
| `runAndWait(script, options)` | Submit and block until done. Extra options: `timeout`, `pollInterval` |
|
|
266
|
-
| `new(name, options)` | Create a
|
|
267
|
+
| `new(name, options)` | Create a VM sandbox and wait until running. Options: `persistent`, `baseWorkspaceId`, `diskGb`, `timeout` |
|
|
267
268
|
| `status(requestId)` | Get job status |
|
|
268
269
|
| `logs(requestId)` | Get job logs |
|
|
269
270
|
| `list({page, pageSize})` | List recent jobs (paginated) |
|
|
@@ -315,9 +316,12 @@ const mags = new Mags({
|
|
|
315
316
|
const result = await mags.runAndWait('echo Hello World');
|
|
316
317
|
console.log(result.logs);
|
|
317
318
|
|
|
318
|
-
// Create a
|
|
319
|
+
// Create a sandbox (local disk)
|
|
319
320
|
await mags.new('myproject');
|
|
320
321
|
|
|
322
|
+
// Create a sandbox with S3 persistence
|
|
323
|
+
await mags.new('myproject', { persistent: true });
|
|
324
|
+
|
|
321
325
|
// Execute command on existing VM
|
|
322
326
|
const { output } = await mags.exec('myproject', 'ls -la /root');
|
|
323
327
|
console.log(output);
|
|
@@ -415,7 +419,7 @@ mags run 'apk add ffmpeg imagemagick'
|
|
|
415
419
|
|
|
416
420
|
| Metric | Value |
|
|
417
421
|
|--------|-------|
|
|
418
|
-
| Warm start |
|
|
422
|
+
| Warm start | ~300ms |
|
|
419
423
|
| Cold start | ~4 seconds |
|
|
420
424
|
| Script overhead | ~50ms |
|
|
421
425
|
| Workspace sync | Every 30 seconds |
|
package/bin/mags.js
CHANGED
|
@@ -246,7 +246,8 @@ ${colors.bold}Cron Options:${colors.reset}
|
|
|
246
246
|
|
|
247
247
|
${colors.bold}Examples:${colors.reset}
|
|
248
248
|
mags login
|
|
249
|
-
mags new myvm # Create VM
|
|
249
|
+
mags new myvm # Create VM (local disk only)
|
|
250
|
+
mags new myvm -p # Create VM with S3 persistence
|
|
250
251
|
mags ssh myvm # SSH (auto-starts if needed)
|
|
251
252
|
mags exec myvm 'ls -la' # Run command on existing VM
|
|
252
253
|
mags run 'echo Hello World'
|
|
@@ -407,9 +408,12 @@ async function newVM(args) {
|
|
|
407
408
|
let name = null;
|
|
408
409
|
let baseWorkspace = null;
|
|
409
410
|
let diskGB = 0;
|
|
411
|
+
let persistent = false;
|
|
410
412
|
|
|
411
413
|
for (let i = 0; i < args.length; i++) {
|
|
412
|
-
if (args[i] === '
|
|
414
|
+
if (args[i] === '-p' || args[i] === '--persistent') {
|
|
415
|
+
persistent = true;
|
|
416
|
+
} else if (args[i] === '--base' && args[i + 1]) {
|
|
413
417
|
baseWorkspace = args[++i];
|
|
414
418
|
} else if (args[i] === '--disk' && args[i + 1]) {
|
|
415
419
|
diskGB = parseInt(args[++i]) || 0;
|
|
@@ -420,17 +424,36 @@ async function newVM(args) {
|
|
|
420
424
|
|
|
421
425
|
if (!name) {
|
|
422
426
|
log('red', 'Error: Name required');
|
|
423
|
-
console.log(`\nUsage: mags new <name> [--base <workspace>] [--disk <GB>]
|
|
427
|
+
console.log(`\nUsage: mags new <name> [-p] [--base <workspace>] [--disk <GB>]`);
|
|
428
|
+
console.log(` -p, --persistent Enable S3 data persistence\n`);
|
|
424
429
|
process.exit(1);
|
|
425
430
|
}
|
|
426
431
|
|
|
432
|
+
// Check if a VM with this name already exists (running or sleeping)
|
|
433
|
+
try {
|
|
434
|
+
const jobs = await request('GET', '/api/v1/mags-jobs');
|
|
435
|
+
const existing = (jobs.jobs || []).find(j =>
|
|
436
|
+
j.name === name && (j.status === 'running' || j.status === 'sleeping')
|
|
437
|
+
);
|
|
438
|
+
if (existing) {
|
|
439
|
+
log('yellow', `Sandbox '${name}' already exists (status: ${existing.status})`);
|
|
440
|
+
console.log(` SSH: mags ssh ${name}`);
|
|
441
|
+
console.log(` Exec: mags exec ${name} <command>`);
|
|
442
|
+
console.log(` Stop: mags stop ${name}`);
|
|
443
|
+
process.exit(0);
|
|
444
|
+
}
|
|
445
|
+
} catch (e) {
|
|
446
|
+
// Non-fatal — proceed with creation if list fails
|
|
447
|
+
}
|
|
448
|
+
|
|
427
449
|
const payload = {
|
|
428
450
|
script: 'sleep infinity',
|
|
429
451
|
type: 'inline',
|
|
430
452
|
persistent: true,
|
|
431
453
|
name: name,
|
|
432
454
|
workspace_id: name,
|
|
433
|
-
startup_command: 'sleep infinity'
|
|
455
|
+
startup_command: 'sleep infinity',
|
|
456
|
+
no_sync: !persistent
|
|
434
457
|
};
|
|
435
458
|
if (baseWorkspace) payload.base_workspace_id = baseWorkspace;
|
|
436
459
|
if (diskGB) payload.disk_gb = diskGB;
|
|
@@ -451,9 +474,12 @@ async function newVM(args) {
|
|
|
451
474
|
const status = await request('GET', `/api/v1/mags-jobs/${response.request_id}/status`);
|
|
452
475
|
|
|
453
476
|
if (status.status === 'running') {
|
|
454
|
-
log('green', `VM '${name}' created successfully`);
|
|
477
|
+
log('green', `VM '${name}' created successfully${persistent ? ' (persistent)' : ' (local disk)'}`);
|
|
455
478
|
console.log(` ID: ${response.request_id}`);
|
|
456
479
|
console.log(` SSH: mags ssh ${name}`);
|
|
480
|
+
if (!persistent) {
|
|
481
|
+
log('gray', ` Data is on local disk only. Use -p flag for S3 persistence.`);
|
|
482
|
+
}
|
|
457
483
|
return;
|
|
458
484
|
} else if (status.status === 'error') {
|
|
459
485
|
log('red', `VM creation failed: ${status.error_message || 'Unknown error'}`);
|
package/index.js
CHANGED
|
@@ -123,6 +123,7 @@ class Mags {
|
|
|
123
123
|
};
|
|
124
124
|
|
|
125
125
|
if (options.noSleep) payload.no_sleep = true;
|
|
126
|
+
if (options.noSync) payload.no_sync = true;
|
|
126
127
|
if (options.name) payload.name = options.name;
|
|
127
128
|
if (!options.ephemeral && options.workspaceId) payload.workspace_id = options.workspaceId;
|
|
128
129
|
if (options.baseWorkspaceId) payload.base_workspace_id = options.baseWorkspaceId;
|
|
@@ -262,9 +263,12 @@ class Mags {
|
|
|
262
263
|
}
|
|
263
264
|
|
|
264
265
|
/**
|
|
265
|
-
* Create a new
|
|
266
|
+
* Create a new VM sandbox and wait until it's running.
|
|
267
|
+
* By default, data lives on local disk only (no S3 sync).
|
|
268
|
+
* Pass persistent: true to enable S3 data persistence.
|
|
266
269
|
* @param {string} name - Workspace name
|
|
267
270
|
* @param {object} options - Options
|
|
271
|
+
* @param {boolean} options.persistent - Enable S3 data persistence (default: false)
|
|
268
272
|
* @param {string} options.baseWorkspaceId - Read-only base workspace to mount
|
|
269
273
|
* @param {number} options.diskGb - Custom disk size in GB
|
|
270
274
|
* @param {number} options.timeout - Timeout in ms (default: 30000)
|
|
@@ -278,6 +282,7 @@ class Mags {
|
|
|
278
282
|
const result = await this.run('sleep infinity', {
|
|
279
283
|
workspaceId: name,
|
|
280
284
|
persistent: true,
|
|
285
|
+
noSync: !options.persistent,
|
|
281
286
|
baseWorkspaceId: options.baseWorkspaceId,
|
|
282
287
|
diskGb: options.diskGb,
|
|
283
288
|
});
|
|
@@ -419,7 +424,7 @@ class Mags {
|
|
|
419
424
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
420
425
|
}
|
|
421
426
|
|
|
422
|
-
return this.new(workspace, { diskGb, timeout: options.timeout, pollInterval: options.pollInterval });
|
|
427
|
+
return this.new(workspace, { persistent: true, diskGb, timeout: options.timeout, pollInterval: options.pollInterval });
|
|
423
428
|
}
|
|
424
429
|
|
|
425
430
|
/**
|
package/nodejs/README.md
CHANGED
|
@@ -18,10 +18,11 @@ 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. Create a
|
|
21
|
+
### 2. Create a sandbox
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
mags new myproject
|
|
24
|
+
mags new myproject # Local disk only
|
|
25
|
+
mags new myproject -p # With S3 persistence
|
|
25
26
|
mags ssh myproject
|
|
26
27
|
```
|
|
27
28
|
|
|
@@ -59,9 +60,12 @@ export MAGS_API_TOKEN="your-token-here"
|
|
|
59
60
|
## CLI Commands
|
|
60
61
|
|
|
61
62
|
```bash
|
|
62
|
-
# Create a
|
|
63
|
+
# Create a sandbox (local disk)
|
|
63
64
|
mags new myproject
|
|
64
65
|
|
|
66
|
+
# Create with S3 persistence
|
|
67
|
+
mags new myproject -p
|
|
68
|
+
|
|
65
69
|
# SSH into it
|
|
66
70
|
mags ssh myproject
|
|
67
71
|
|
|
@@ -135,11 +139,13 @@ Features:
|
|
|
135
139
|
|
|
136
140
|
## Workspaces & Persistence
|
|
137
141
|
|
|
138
|
-
|
|
142
|
+
When using persistent mode (`-p`), your `/root` directory syncs to S3:
|
|
139
143
|
- **Auto-sync**: Every 30 seconds while running
|
|
140
144
|
- **On stop**: Full sync before VM terminates
|
|
141
145
|
- **On wake**: Previous state restored
|
|
142
146
|
|
|
147
|
+
Without `-p`, data lives on local disk only and is cleaned up when the VM is destroyed.
|
|
148
|
+
|
|
143
149
|
## Node.js SDK
|
|
144
150
|
|
|
145
151
|
```javascript
|
package/package.json
CHANGED
package/python/README.md
CHANGED
|
@@ -131,19 +131,23 @@ print(f"Jobs: {usage['total_jobs']}, VM seconds: {usage['vm_seconds']:.0f}")
|
|
|
131
131
|
|--------|-------------|
|
|
132
132
|
| `run(script, **opts)` | Submit a job (`persistent`, `no_sleep`, `workspace_id`, ...) |
|
|
133
133
|
| `run_and_wait(script, **opts)` | Submit and block until done |
|
|
134
|
-
| `new(name, **opts)` | Create a persistent
|
|
134
|
+
| `new(name, **opts)` | Create a VM sandbox (`persistent=True` for S3) |
|
|
135
135
|
| `find_job(name_or_id)` | Find a running/sleeping job by name or workspace |
|
|
136
136
|
| `exec(name_or_id, command)` | Run a command on an existing VM via SSH |
|
|
137
137
|
| `stop(name_or_id)` | Stop a running job |
|
|
138
|
+
| `url(name_or_id, port)` | Enable public URL and return full URL |
|
|
138
139
|
| `resize(workspace, disk_gb)` | Resize a workspace's disk |
|
|
139
140
|
| `status(request_id)` | Get job status |
|
|
140
141
|
| `logs(request_id)` | Get job logs |
|
|
141
142
|
| `list_jobs(page, page_size)` | List recent jobs |
|
|
142
|
-
| `update_job(request_id,
|
|
143
|
+
| `update_job(request_id, **opts)` | Update job config (`startup_command`, `no_sleep`) |
|
|
143
144
|
| `enable_access(request_id, port)` | Enable URL or SSH access |
|
|
144
145
|
| `usage(window_days)` | Get usage summary |
|
|
145
146
|
| `upload_file(path)` | Upload a file, returns file ID |
|
|
146
147
|
| `upload_files(paths)` | Upload multiple files |
|
|
148
|
+
| `url_alias_create(sub, ws_id)` | Create a stable URL alias |
|
|
149
|
+
| `url_alias_list()` | List URL aliases |
|
|
150
|
+
| `url_alias_delete(sub)` | Delete a URL alias |
|
|
147
151
|
| `cron_create(**opts)` | Create a cron job |
|
|
148
152
|
| `cron_list()` | List cron jobs |
|
|
149
153
|
| `cron_get(id)` | Get a cron job |
|
|
@@ -154,4 +158,4 @@ print(f"Jobs: {usage['total_jobs']}, VM seconds: {usage['vm_seconds']:.0f}")
|
|
|
154
158
|
|
|
155
159
|
- Website: [mags.run](https://mags.run)
|
|
156
160
|
- Node.js SDK: `npm install @magpiecloud/mags`
|
|
157
|
-
- CLI: `
|
|
161
|
+
- CLI: `npm install -g @magpiecloud/mags`
|
|
Binary file
|
|
Binary file
|
package/python/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: magpie-mags
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.5
|
|
4
4
|
Summary: Mags SDK - Execute scripts on Magpie's instant VM infrastructure
|
|
5
5
|
Author: Magpie Cloud
|
|
6
6
|
License: MIT
|
|
@@ -156,7 +156,7 @@ print(f"Jobs: {usage['total_jobs']}, VM seconds: {usage['vm_seconds']:.0f}")
|
|
|
156
156
|
|--------|-------------|
|
|
157
157
|
| `run(script, **opts)` | Submit a job (`persistent`, `no_sleep`, `workspace_id`, ...) |
|
|
158
158
|
| `run_and_wait(script, **opts)` | Submit and block until done |
|
|
159
|
-
| `new(name, **opts)` | Create a persistent
|
|
159
|
+
| `new(name, **opts)` | Create a VM sandbox (`persistent=True` for S3) |
|
|
160
160
|
| `find_job(name_or_id)` | Find a running/sleeping job by name or workspace |
|
|
161
161
|
| `exec(name_or_id, command)` | Run a command on an existing VM via SSH |
|
|
162
162
|
| `stop(name_or_id)` | Stop a running job |
|
|
@@ -106,6 +106,7 @@ class Mags:
|
|
|
106
106
|
environment: Dict[str, str] | None = None,
|
|
107
107
|
file_ids: List[str] | None = None,
|
|
108
108
|
disk_gb: int | None = None,
|
|
109
|
+
no_sync: bool = False,
|
|
109
110
|
) -> dict:
|
|
110
111
|
"""Submit a job for execution.
|
|
111
112
|
|
|
@@ -122,6 +123,7 @@ class Mags:
|
|
|
122
123
|
environment: Key-value env vars injected into the VM.
|
|
123
124
|
file_ids: File IDs to download into VM before script runs.
|
|
124
125
|
disk_gb: Custom disk size in GB (default 2GB).
|
|
126
|
+
no_sync: Skip S3 sync, use local disk only.
|
|
125
127
|
|
|
126
128
|
Returns ``{"request_id": ..., "status": "accepted"}``.
|
|
127
129
|
"""
|
|
@@ -139,6 +141,8 @@ class Mags:
|
|
|
139
141
|
}
|
|
140
142
|
if no_sleep:
|
|
141
143
|
payload["no_sleep"] = True
|
|
144
|
+
if no_sync:
|
|
145
|
+
payload["no_sync"] = True
|
|
142
146
|
if name:
|
|
143
147
|
payload["name"] = name
|
|
144
148
|
if not ephemeral and workspace_id:
|
|
@@ -262,20 +266,25 @@ class Mags:
|
|
|
262
266
|
self._request("POST", f"/mags-jobs/{existing['request_id']}/stop")
|
|
263
267
|
time.sleep(1)
|
|
264
268
|
|
|
265
|
-
return self.new(workspace,
|
|
269
|
+
return self.new(workspace, persistent=True, disk_gb=disk_gb,
|
|
270
|
+
timeout=timeout, poll_interval=poll_interval)
|
|
266
271
|
|
|
267
272
|
def new(
|
|
268
273
|
self,
|
|
269
274
|
name: str,
|
|
270
275
|
*,
|
|
276
|
+
persistent: bool = False,
|
|
271
277
|
base_workspace_id: str | None = None,
|
|
272
278
|
disk_gb: int | None = None,
|
|
273
279
|
timeout: float = 30.0,
|
|
274
280
|
poll_interval: float = 1.0,
|
|
275
281
|
) -> dict:
|
|
276
|
-
"""Create a new
|
|
282
|
+
"""Create a new VM sandbox and wait until it's running.
|
|
283
|
+
|
|
284
|
+
By default, data lives on local disk only (no S3 sync).
|
|
285
|
+
Pass ``persistent=True`` to enable S3 data persistence.
|
|
277
286
|
|
|
278
|
-
Equivalent to ``mags new <name
|
|
287
|
+
Equivalent to ``mags new <name>`` or ``mags new <name> -p``.
|
|
279
288
|
|
|
280
289
|
Returns ``{"request_id": ..., "status": "running"}``.
|
|
281
290
|
"""
|
|
@@ -283,6 +292,7 @@ class Mags:
|
|
|
283
292
|
"sleep infinity",
|
|
284
293
|
workspace_id=name,
|
|
285
294
|
persistent=True,
|
|
295
|
+
no_sync=not persistent,
|
|
286
296
|
base_workspace_id=base_workspace_id,
|
|
287
297
|
disk_gb=disk_gb,
|
|
288
298
|
)
|
package/website/api.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
/>
|
|
11
11
|
<meta name="api-base" content="https://api.magpiecloud.com" />
|
|
12
12
|
<meta name="auth-base" content="https://api.magpiecloud.com" />
|
|
13
|
-
<link rel="stylesheet" href="styles.css?v=
|
|
13
|
+
<link rel="stylesheet" href="styles.css?v=7" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
<style>
|
|
16
16
|
.endpoint { margin-bottom: 2.5rem; }
|
|
@@ -252,7 +252,7 @@ m = Mags(api_token="your-token")
|
|
|
252
252
|
<tr>
|
|
253
253
|
<td><code>"my-ws"</code></td>
|
|
254
254
|
<td><em>omit</em></td>
|
|
255
|
-
<td>
|
|
255
|
+
<td>Local workspace. Data stays on the VM only (not synced to S3). Add <code>persistent: true</code> to sync to S3.</td>
|
|
256
256
|
</tr>
|
|
257
257
|
<tr>
|
|
258
258
|
<td><em>omit</em></td>
|
|
@@ -283,11 +283,14 @@ m = Mags(api_token="your-token")
|
|
|
283
283
|
<a href="#get-status">Get Status</a>
|
|
284
284
|
<a href="#get-logs">Get Logs</a>
|
|
285
285
|
<a href="#list-jobs">List Jobs</a>
|
|
286
|
+
<a href="#stop-job">Stop Job</a>
|
|
286
287
|
<a href="#enable-access">Enable Access</a>
|
|
287
288
|
<a href="#update-job">Update Job</a>
|
|
289
|
+
<a href="#sync-workspace">Sync Workspace</a>
|
|
288
290
|
<a href="#upload-file">Upload File</a>
|
|
289
291
|
<a href="#list-workspaces">List Workspaces</a>
|
|
290
292
|
<a href="#delete-workspace">Delete Workspace</a>
|
|
293
|
+
<a href="#url-aliases">URL Aliases</a>
|
|
291
294
|
<a href="#cron-endpoints">Cron Jobs</a>
|
|
292
295
|
</div>
|
|
293
296
|
|
|
@@ -322,7 +325,7 @@ m = Mags(api_token="your-token")
|
|
|
322
325
|
<td><code>workspace_id</code></td>
|
|
323
326
|
<td>string</td>
|
|
324
327
|
<td>no</td>
|
|
325
|
-
<td>
|
|
328
|
+
<td>Workspace name. With <code>persistent: false</code>, data stays local to the VM only. With <code>persistent: true</code>, filesystem changes are synced to S3 and restored on next run. Omit for ephemeral.</td>
|
|
326
329
|
</tr>
|
|
327
330
|
<tr>
|
|
328
331
|
<td><code>base_workspace_id</code></td>
|
|
@@ -334,7 +337,7 @@ m = Mags(api_token="your-token")
|
|
|
334
337
|
<td><code>persistent</code></td>
|
|
335
338
|
<td>bool</td>
|
|
336
339
|
<td>no</td>
|
|
337
|
-
<td>If <code>true</code>, VM stays alive after script finishes.</td>
|
|
340
|
+
<td>If <code>true</code>, VM stays alive after script finishes and workspace is synced to S3. Required for cloud persistence.</td>
|
|
338
341
|
</tr>
|
|
339
342
|
<tr>
|
|
340
343
|
<td><code>no_sleep</code></td>
|
|
@@ -542,12 +545,48 @@ for job in resp["jobs"]:
|
|
|
542
545
|
print(job["request_id"], job["status"])</code></pre>
|
|
543
546
|
</div>
|
|
544
547
|
<div class="tab-content" data-tab="lj-js">
|
|
545
|
-
<pre><code>const resp = await mags.
|
|
548
|
+
<pre><code>const resp = await mags.list({ page: 1, pageSize: 10 });
|
|
546
549
|
resp.jobs.forEach(j => console.log(j.requestId, j.status));</code></pre>
|
|
547
550
|
</div>
|
|
548
551
|
</div>
|
|
549
552
|
</div>
|
|
550
553
|
|
|
554
|
+
<!-- Stop Job -->
|
|
555
|
+
<div class="endpoint card wide" id="stop-job" data-reveal>
|
|
556
|
+
<h3>Stop Job</h3>
|
|
557
|
+
<div class="method-url">
|
|
558
|
+
<span class="method post">POST</span>
|
|
559
|
+
<span class="url-path">/api/v1/mags-jobs/:id/stop</span>
|
|
560
|
+
</div>
|
|
561
|
+
<p>Stop a running or sleeping job. The VM is terminated and the workspace is synced to S3 if persistent.</p>
|
|
562
|
+
|
|
563
|
+
<p class="response-label">Response (200)</p>
|
|
564
|
+
<pre><code>{ "success": true, "message": "Job stopped" }</code></pre>
|
|
565
|
+
|
|
566
|
+
<div class="code-tabs tab-group">
|
|
567
|
+
<p class="response-label">SDK examples</p>
|
|
568
|
+
<div class="tab-bar">
|
|
569
|
+
<button class="tab active" data-tab="sj2-curl">curl</button>
|
|
570
|
+
<button class="tab" data-tab="sj2-py">Python</button>
|
|
571
|
+
<button class="tab" data-tab="sj2-js">Node.js</button>
|
|
572
|
+
<button class="tab" data-tab="sj2-cli">CLI</button>
|
|
573
|
+
</div>
|
|
574
|
+
<div class="tab-content active" data-tab="sj2-curl">
|
|
575
|
+
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs/$ID/stop \
|
|
576
|
+
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="tab-content" data-tab="sj2-py">
|
|
579
|
+
<pre><code>m.stop("myproject") # accepts name, workspace ID, or request ID</code></pre>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="tab-content" data-tab="sj2-js">
|
|
582
|
+
<pre><code>await mags.stop('myproject'); // accepts name, workspace ID, or request ID</code></pre>
|
|
583
|
+
</div>
|
|
584
|
+
<div class="tab-content" data-tab="sj2-cli">
|
|
585
|
+
<pre><code>mags stop myproject</code></pre>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
551
590
|
<!-- Enable Access -->
|
|
552
591
|
<div class="endpoint card wide" id="enable-access" data-reveal>
|
|
553
592
|
<h3>Enable Access (SSH or HTTP)</h3>
|
|
@@ -601,11 +640,11 @@ print(access.get("url"))</code></pre>
|
|
|
601
640
|
</div>
|
|
602
641
|
<div class="tab-content" data-tab="ea-js">
|
|
603
642
|
<pre><code>// SSH access
|
|
604
|
-
const ssh = await mags.enableAccess(requestId,
|
|
643
|
+
const ssh = await mags.enableAccess(requestId, 22);
|
|
605
644
|
console.log(`ssh root@${ssh.sshHost} -p ${ssh.sshPort}`);
|
|
606
645
|
|
|
607
646
|
// HTTP access
|
|
608
|
-
const access = await mags.enableAccess(requestId,
|
|
647
|
+
const access = await mags.enableAccess(requestId, 8080);</code></pre>
|
|
609
648
|
</div>
|
|
610
649
|
</div>
|
|
611
650
|
</div>
|
|
@@ -714,6 +753,37 @@ file_ids = m.upload_files(["script.py", "data.csv"])</code></pre>
|
|
|
714
753
|
</div>
|
|
715
754
|
</div>
|
|
716
755
|
|
|
756
|
+
<!-- Sync Workspace -->
|
|
757
|
+
<div class="endpoint card wide" id="sync-workspace" data-reveal>
|
|
758
|
+
<h3>Sync Workspace</h3>
|
|
759
|
+
<div class="method-url">
|
|
760
|
+
<span class="method post">POST</span>
|
|
761
|
+
<span class="url-path">/api/v1/mags-jobs/:id/sync</span>
|
|
762
|
+
</div>
|
|
763
|
+
<p>Trigger an immediate workspace sync to S3 without stopping the VM.</p>
|
|
764
|
+
|
|
765
|
+
<p class="response-label">Response (200)</p>
|
|
766
|
+
<pre><code>{ "success": true, "message": "Sync initiated" }</code></pre>
|
|
767
|
+
|
|
768
|
+
<div class="code-tabs tab-group">
|
|
769
|
+
<p class="response-label">SDK examples</p>
|
|
770
|
+
<div class="tab-bar">
|
|
771
|
+
<button class="tab active" data-tab="sw-py">Python</button>
|
|
772
|
+
<button class="tab" data-tab="sw-js">Node.js</button>
|
|
773
|
+
<button class="tab" data-tab="sw-cli">CLI</button>
|
|
774
|
+
</div>
|
|
775
|
+
<div class="tab-content active" data-tab="sw-py">
|
|
776
|
+
<pre><code>m.sync(request_id)</code></pre>
|
|
777
|
+
</div>
|
|
778
|
+
<div class="tab-content" data-tab="sw-js">
|
|
779
|
+
<pre><code>await mags.sync(requestId);</code></pre>
|
|
780
|
+
</div>
|
|
781
|
+
<div class="tab-content" data-tab="sw-cli">
|
|
782
|
+
<pre><code>mags sync myproject</code></pre>
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
</div>
|
|
786
|
+
|
|
717
787
|
<!-- List Workspaces -->
|
|
718
788
|
<div class="endpoint card wide" id="list-workspaces" data-reveal>
|
|
719
789
|
<h3>List Workspaces</h3>
|
|
@@ -794,6 +864,65 @@ mags workspace delete myproject --force # skip confirmation</code></pre>
|
|
|
794
864
|
</div>
|
|
795
865
|
</div>
|
|
796
866
|
|
|
867
|
+
<!-- URL Aliases -->
|
|
868
|
+
<div class="endpoint card wide" id="url-aliases" data-reveal>
|
|
869
|
+
<h3>URL Aliases</h3>
|
|
870
|
+
<div class="method-url">
|
|
871
|
+
<span class="method post">POST</span>
|
|
872
|
+
<span class="url-path">/api/v1/mags-url-aliases</span>
|
|
873
|
+
</div>
|
|
874
|
+
<div class="method-url" style="margin-left:0.5rem">
|
|
875
|
+
<span class="method get">GET</span>
|
|
876
|
+
<span class="url-path">/api/v1/mags-url-aliases</span>
|
|
877
|
+
</div>
|
|
878
|
+
<div class="method-url" style="margin-left:0.5rem">
|
|
879
|
+
<span class="method delete">DELETE</span>
|
|
880
|
+
<span class="url-path">/api/v1/mags-url-aliases/:subdomain</span>
|
|
881
|
+
</div>
|
|
882
|
+
<p>Create stable, human-readable URL aliases for your sandboxes. Aliases point to a workspace and automatically follow the active VM.</p>
|
|
883
|
+
|
|
884
|
+
<p class="response-label">Create request body</p>
|
|
885
|
+
<table class="field-table">
|
|
886
|
+
<thead>
|
|
887
|
+
<tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>
|
|
888
|
+
</thead>
|
|
889
|
+
<tbody>
|
|
890
|
+
<tr><td><code>subdomain</code></td><td>string</td><td><span class="required">YES</span></td><td>Subdomain for the alias (e.g. <code>my-api</code>).</td></tr>
|
|
891
|
+
<tr><td><code>workspace_id</code></td><td>string</td><td><span class="required">YES</span></td><td>Workspace to point the alias at.</td></tr>
|
|
892
|
+
<tr><td><code>domain</code></td><td>string</td><td>no</td><td>Custom domain (default: <code>apps.magpiecloud.com</code>).</td></tr>
|
|
893
|
+
</tbody>
|
|
894
|
+
</table>
|
|
895
|
+
|
|
896
|
+
<div class="code-tabs tab-group">
|
|
897
|
+
<p class="response-label">SDK examples</p>
|
|
898
|
+
<div class="tab-bar">
|
|
899
|
+
<button class="tab active" data-tab="ua-cli">CLI</button>
|
|
900
|
+
<button class="tab" data-tab="ua-py">Python</button>
|
|
901
|
+
<button class="tab" data-tab="ua-js">Node.js</button>
|
|
902
|
+
</div>
|
|
903
|
+
<div class="tab-content active" data-tab="ua-cli">
|
|
904
|
+
<pre><code># Create alias
|
|
905
|
+
mags url alias my-api myproject
|
|
906
|
+
|
|
907
|
+
# List aliases
|
|
908
|
+
mags url alias list
|
|
909
|
+
|
|
910
|
+
# Delete alias
|
|
911
|
+
mags url alias remove my-api</code></pre>
|
|
912
|
+
</div>
|
|
913
|
+
<div class="tab-content" data-tab="ua-py">
|
|
914
|
+
<pre><code>m.url_alias_create("my-api", "myproject")
|
|
915
|
+
aliases = m.url_alias_list()
|
|
916
|
+
m.url_alias_delete("my-api")</code></pre>
|
|
917
|
+
</div>
|
|
918
|
+
<div class="tab-content" data-tab="ua-js">
|
|
919
|
+
<pre><code>await mags.urlAliasCreate('my-api', 'myproject');
|
|
920
|
+
const aliases = await mags.urlAliasList();
|
|
921
|
+
await mags.urlAliasDelete('my-api');</code></pre>
|
|
922
|
+
</div>
|
|
923
|
+
</div>
|
|
924
|
+
</div>
|
|
925
|
+
|
|
797
926
|
<!-- Cron Endpoints -->
|
|
798
927
|
<div class="endpoint card wide" id="cron-endpoints" data-reveal>
|
|
799
928
|
<h3>Cron Jobs</h3>
|
|
@@ -961,6 +1090,6 @@ mags cron remove <id></code></pre>
|
|
|
961
1090
|
</div>
|
|
962
1091
|
</footer>
|
|
963
1092
|
|
|
964
|
-
<script src="script.js?v=
|
|
1093
|
+
<script src="script.js?v=8"></script>
|
|
965
1094
|
</body>
|
|
966
1095
|
</html>
|
package/website/cookbook.html
CHANGED
|
@@ -61,7 +61,7 @@ mags ssh dev-env
|
|
|
61
61
|
|
|
62
62
|
# inside the VM
|
|
63
63
|
apk add git nodejs npm</code></pre>
|
|
64
|
-
<p class="card-note">Create a
|
|
64
|
+
<p class="card-note">Create a dev sandbox with one command. Add <code>-p</code> for S3 persistence.</p>
|
|
65
65
|
</div>
|
|
66
66
|
</div>
|
|
67
67
|
</section>
|