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