@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/API.md ADDED
@@ -0,0 +1,388 @@
1
+ # Mags API Reference
2
+
3
+ Base URL: `https://api.magpiecloud.com`
4
+
5
+ All endpoints require authentication via `Authorization: Bearer <token>` header.
6
+
7
+ ---
8
+
9
+ ## Workflow: Base Image + Ephemeral Workers
10
+
11
+ ```
12
+ 1. Create a workspace, install dependencies, save it
13
+ POST /api/v1/mags-jobs (workspace_id: "my-base", script: "apk add python3 nodejs ...")
14
+
15
+ 2. Use that workspace as a read-only base for ephemeral workers
16
+ POST /api/v1/mags-jobs (base_workspace_id: "my-base", script: "npm test")
17
+ POST /api/v1/mags-jobs (base_workspace_id: "my-base", script: "python3 run.py")
18
+ ...workers run in parallel, base is never modified
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Endpoints
24
+
25
+ ### Submit Job
26
+
27
+ ```
28
+ POST /api/v1/mags-jobs
29
+ ```
30
+
31
+ Creates a VM, runs a script, and returns. The VM boots in ~300ms from a pool.
32
+
33
+ **Request:**
34
+
35
+ ```json
36
+ {
37
+ "script": "echo hello world",
38
+ "type": "inline",
39
+ "workspace_id": "my-project",
40
+ "base_workspace_id": "my-base",
41
+ "persistent": false,
42
+ "no_sleep": false,
43
+ "startup_command": "python3 -m http.server 8080",
44
+ "environment": { "FOO": "bar" },
45
+ "file_ids": ["file-uuid-1", "file-uuid-2"]
46
+ }
47
+ ```
48
+
49
+ | Field | Type | Required | Description |
50
+ |-------|------|----------|-------------|
51
+ | `script` | string | yes | Shell script to execute inside the VM |
52
+ | `type` | string | yes | Always `"inline"` |
53
+ | `workspace_id` | string | no | Persistent workspace name. Filesystem changes (apt install, files, configs) are synced to S3 and restored on next run. Omit for truly ephemeral (no sync). |
54
+ | `base_workspace_id` | string | no | Mount an existing workspace **read-only** as the starting filesystem. Changes are discarded unless `workspace_id` is also set (fork pattern). |
55
+ | `persistent` | bool | no | If `true`, VM stays alive after script finishes. Use for long-running servers, SSH access, or URL-exposed services. |
56
+ | `no_sleep` | bool | no | If `true` with `persistent`, VM never auto-sleeps. Stays running 24/7 and auto-recovers if the host goes down. |
57
+ | `startup_command` | string | no | Command to run when a sleeping persistent VM wakes up (on URL access). |
58
+ | `environment` | object | no | Key-value env vars injected into the VM. |
59
+ | `file_ids` | string[] | no | File IDs from the upload endpoint. Files are downloaded into `/root/` before script runs. |
60
+
61
+ **Response (202):**
62
+
63
+ ```json
64
+ {
65
+ "request_id": "eab1e214-39c8-4ecc-b941-75551976e0fa",
66
+ "status": "accepted",
67
+ "message": "Mags job submitted successfully"
68
+ }
69
+ ```
70
+
71
+ **Workspace modes:**
72
+
73
+ | `workspace_id` | `base_workspace_id` | Behavior |
74
+ |-----------------|----------------------|----------|
75
+ | omit | omit | Fully ephemeral. No S3 sync. Fastest. |
76
+ | `"my-ws"` | omit | Read+write workspace. Changes persist across runs. |
77
+ | omit | `"my-base"` | Base mounted read-only. Changes discarded after run. |
78
+ | `"fork-1"` | `"my-base"` | Fork: starts from base, saves changes to `fork-1`. |
79
+
80
+ ---
81
+
82
+ ### Get Job Status
83
+
84
+ ```
85
+ GET /api/v1/mags-jobs/:id/status
86
+ ```
87
+
88
+ **Response (200):**
89
+
90
+ ```json
91
+ {
92
+ "request_id": "eab1e214-...",
93
+ "status": "running",
94
+ "vm_id": "a488ceef",
95
+ "persistent": true,
96
+ "subdomain": "28dfed70c32e",
97
+ "url": "https://28dfed70c32e.apps.magpiecloud.com",
98
+ "sleeping": false,
99
+ "exit_code": 0,
100
+ "queue_duration_ms": 2,
101
+ "acquire_duration_ms": 45,
102
+ "script_duration_ms": 1200,
103
+ "started_at": "2026-02-04T22:30:00Z",
104
+ "completed_at": null
105
+ }
106
+ ```
107
+
108
+ **Status values:** `pending` → `running` → `completed` | `error` | `sleeping`
109
+
110
+ Poll this endpoint to wait for job completion. For persistent jobs, `running` is the final state (VM stays alive).
111
+
112
+ ---
113
+
114
+ ### Get Job Logs
115
+
116
+ ```
117
+ GET /api/v1/mags-jobs/:id/logs
118
+ ```
119
+
120
+ **Response (200):**
121
+
122
+ ```json
123
+ {
124
+ "logs": [
125
+ { "timestamp": "2026-02-04T22:30:01Z", "level": "info", "message": "hello world", "source": "stdout" },
126
+ { "timestamp": "2026-02-04T22:30:01Z", "level": "error", "message": "warning: ...", "source": "stderr" }
127
+ ]
128
+ }
129
+ ```
130
+
131
+ Filter by `source`: `"stdout"`, `"stderr"`, or `"system"` (internal events).
132
+
133
+ ---
134
+
135
+ ### List Jobs
136
+
137
+ ```
138
+ GET /api/v1/mags-jobs?page=1&page_size=20
139
+ ```
140
+
141
+ **Response (200):**
142
+
143
+ ```json
144
+ {
145
+ "jobs": [
146
+ {
147
+ "request_id": "eab1e214-...",
148
+ "job_id": "372525f4-...",
149
+ "name": "mags-job-1738703400",
150
+ "status": "completed",
151
+ "workspace_id": "my-project",
152
+ "exit_code": 0,
153
+ "created_at": "2026-02-04T22:30:00Z"
154
+ }
155
+ ],
156
+ "total": 42,
157
+ "page": 1,
158
+ "page_size": 20
159
+ }
160
+ ```
161
+
162
+ ---
163
+
164
+ ### Enable Access (SSH or HTTP)
165
+
166
+ ```
167
+ POST /api/v1/mags-jobs/:id/access
168
+ ```
169
+
170
+ Enables external access to a running VM. Use `port: 22` for SSH, any other port for HTTP proxy.
171
+
172
+ **Request:**
173
+
174
+ ```json
175
+ { "port": 22 }
176
+ ```
177
+
178
+ **Response (200) — SSH (port 22):**
179
+
180
+ ```json
181
+ {
182
+ "success": true,
183
+ "message": "SSH proxy enabled",
184
+ "ssh_host": "api.magpiecloud.com",
185
+ "ssh_port": 40000,
186
+ "ssh_private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----\n",
187
+ "access_type": "ssh_proxy"
188
+ }
189
+ ```
190
+
191
+ Connect with: `ssh -i <key_file> -p <ssh_port> root@<ssh_host>`
192
+
193
+ **Response (200) — HTTP (other ports):**
194
+
195
+ ```json
196
+ {
197
+ "success": true,
198
+ "message": "External access enabled",
199
+ "ipv6_address": "2a01:4f9:...",
200
+ "port": 8080,
201
+ "access_type": "http_proxy"
202
+ }
203
+ ```
204
+
205
+ HTTP services are also available via subdomain: `https://<subdomain>.apps.magpiecloud.com`
206
+
207
+ ---
208
+
209
+ ### Update Job
210
+
211
+ ```
212
+ PATCH /api/v1/mags-jobs/:id
213
+ ```
214
+
215
+ Update a job's settings. All fields are optional — only include the ones you want to change.
216
+
217
+ **Request:**
218
+
219
+ | Field | Type | Description |
220
+ |-------|------|-------------|
221
+ | `startup_command` | string | Command to run when VM wakes from sleep |
222
+ | `no_sleep` | bool | If `true`, VM never auto-sleeps. If `false`, re-enables auto-sleep. Requires persistent VM. |
223
+
224
+ ```json
225
+ { "no_sleep": true }
226
+ ```
227
+
228
+ **Response (200):**
229
+
230
+ ```json
231
+ { "success": true, "message": "Job updated" }
232
+ ```
233
+
234
+ ---
235
+
236
+ ### Upload File
237
+
238
+ ```
239
+ POST /api/v1/mags-files
240
+ Content-Type: multipart/form-data
241
+ ```
242
+
243
+ Upload a file (max 100MB) that can be attached to jobs via `file_ids`.
244
+
245
+ **Request:** Multipart form with field name `file`.
246
+
247
+ **Response (201):**
248
+
249
+ ```json
250
+ {
251
+ "file_id": "a1b2c3d4-...",
252
+ "file_name": "script.py",
253
+ "size": 4096
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Examples
260
+
261
+ ### 1. Set up a base workspace with dependencies
262
+
263
+ ```bash
264
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
265
+ -H "Authorization: Bearer $TOKEN" \
266
+ -H "Content-Type: application/json" \
267
+ -d '{
268
+ "script": "apk add python3 py3-pip nodejs npm && pip install requests flask",
269
+ "type": "inline",
270
+ "workspace_id": "python-base"
271
+ }'
272
+ # Returns request_id — poll /status until completed
273
+ ```
274
+
275
+ ### 2. Spawn ephemeral workers from the base
276
+
277
+ ```bash
278
+ # Worker 1 — changes discarded, base untouched
279
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
280
+ -H "Authorization: Bearer $TOKEN" \
281
+ -H "Content-Type: application/json" \
282
+ -d '{
283
+ "script": "python3 -c \"import requests; print(requests.get('https://httpbin.org/ip').text)\"",
284
+ "type": "inline",
285
+ "base_workspace_id": "python-base"
286
+ }'
287
+
288
+ # Worker 2 — runs in parallel, isolated from worker 1
289
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
290
+ -H "Authorization: Bearer $TOKEN" \
291
+ -H "Content-Type: application/json" \
292
+ -d '{
293
+ "script": "node -e \"console.log(process.version)\"",
294
+ "type": "inline",
295
+ "base_workspace_id": "python-base"
296
+ }'
297
+ ```
298
+
299
+ ### 3. Fork a base into a new workspace
300
+
301
+ ```bash
302
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
303
+ -H "Authorization: Bearer $TOKEN" \
304
+ -H "Content-Type: application/json" \
305
+ -d '{
306
+ "script": "pip install pandas && python3 -c \"import pandas; print(pandas.__version__)\"",
307
+ "type": "inline",
308
+ "base_workspace_id": "python-base",
309
+ "workspace_id": "python-data"
310
+ }'
311
+ # python-data now has everything from python-base + pandas
312
+ ```
313
+
314
+ ### 4. Run a persistent server with URL
315
+
316
+ ```bash
317
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
318
+ -H "Authorization: Bearer $TOKEN" \
319
+ -H "Content-Type: application/json" \
320
+ -d '{
321
+ "script": "echo \"<h1>Hello</h1>\" > index.html && python3 -m http.server 8080",
322
+ "type": "inline",
323
+ "workspace_id": "my-server",
324
+ "persistent": true,
325
+ "startup_command": "python3 -m http.server 8080"
326
+ }'
327
+ # After status=running, enable URL access:
328
+ curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs/$REQUEST_ID/access \
329
+ -H "Authorization: Bearer $TOKEN" \
330
+ -H "Content-Type: application/json" \
331
+ -d '{"port": 8080}'
332
+ ```
333
+
334
+ ### 5. Execute a command on a running VM via SSH
335
+
336
+ ```bash
337
+ # Enable SSH proxy
338
+ RESP=$(curl -s -X POST https://api.magpiecloud.com/api/v1/mags-jobs/$REQUEST_ID/access \
339
+ -H "Authorization: Bearer $TOKEN" \
340
+ -H "Content-Type: application/json" \
341
+ -d '{"port": 22}')
342
+
343
+ # Extract connection info
344
+ SSH_HOST=$(echo $RESP | jq -r '.ssh_host')
345
+ SSH_PORT=$(echo $RESP | jq -r '.ssh_port')
346
+ echo "$RESP" | jq -r '.ssh_private_key' > /tmp/mags_key && chmod 600 /tmp/mags_key
347
+
348
+ # Connect
349
+ ssh -i /tmp/mags_key -p $SSH_PORT -o StrictHostKeyChecking=no root@$SSH_HOST "echo hello"
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Error Responses
355
+
356
+ All errors return:
357
+
358
+ ```json
359
+ { "error": "description of what went wrong" }
360
+ ```
361
+
362
+ | Status | Meaning |
363
+ |--------|---------|
364
+ | 400 | Invalid request (missing fields, bad JSON) |
365
+ | 401 | Missing or invalid auth token |
366
+ | 404 | Job not found |
367
+ | 500 | Server error (includes workspace conflict messages) |
368
+
369
+ **Workspace conflict (500):**
370
+
371
+ ```json
372
+ {
373
+ "error": "workspace 'my-ws' is already in use by job eab1e214-... (status: running); use 'mags exec my-ws <command>' to run commands on it, or 'mags ssh my-ws' to connect interactively"
374
+ }
375
+ ```
376
+
377
+ ---
378
+
379
+ ## VM Environment
380
+
381
+ - **OS:** Alpine Linux (lightweight, ~50MB rootfs)
382
+ - **Shell:** `/bin/sh` (ash)
383
+ - **Package manager:** `apk add <package>`
384
+ - **User:** `root`
385
+ - **Working directory:** `/root`
386
+ - **Filesystem:** OverlayFS on top of base rootfs. Workspace changes sync to S3.
387
+ - **Boot time:** ~300ms from pool
388
+ - **Default timeout:** 300 seconds (configurable per job)