@magpiecloud/mags 1.8.13 → 1.8.15
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 +95 -378
- package/bin/mags.js +196 -104
- package/index.js +6 -52
- package/package.json +22 -4
- package/API.md +0 -388
- package/Mags-API.postman_collection.json +0 -374
- package/QUICKSTART.md +0 -295
- package/deploy-page.sh +0 -171
- package/mags +0 -0
- package/mags.sh +0 -270
- package/nodejs/README.md +0 -197
- package/nodejs/bin/mags.js +0 -1146
- package/nodejs/index.js +0 -642
- package/nodejs/package.json +0 -42
- package/python/INTEGRATION.md +0 -800
- package/python/README.md +0 -161
- 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/examples/demo.py +0 -181
- package/python/pyproject.toml +0 -39
- package/python/src/magpie_mags.egg-info/PKG-INFO +0 -182
- package/python/src/magpie_mags.egg-info/SOURCES.txt +0 -9
- package/python/src/magpie_mags.egg-info/dependency_links.txt +0 -1
- package/python/src/magpie_mags.egg-info/requires.txt +0 -1
- package/python/src/magpie_mags.egg-info/top_level.txt +0 -1
- package/python/src/mags/__init__.py +0 -6
- package/python/src/mags/client.py +0 -573
- package/python/test_sdk.py +0 -78
- package/skill.md +0 -153
- package/website/api.html +0 -1095
- package/website/claude-skill.html +0 -481
- package/website/cookbook/hn-marketing.html +0 -410
- package/website/cookbook/hn-marketing.sh +0 -42
- package/website/cookbook.html +0 -282
- package/website/env.js +0 -4
- package/website/index.html +0 -801
- package/website/llms.txt +0 -334
- package/website/login.html +0 -108
- package/website/mags.md +0 -210
- package/website/script.js +0 -453
- package/website/styles.css +0 -908
- package/website/tokens.html +0 -169
- package/website/usage.html +0 -185
package/website/api.html
DELETED
|
@@ -1,1095 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Mags API Reference</title>
|
|
7
|
-
<meta
|
|
8
|
-
name="description"
|
|
9
|
-
content="Full API reference for the Mags REST API, Python SDK, and Node.js SDK. Submit jobs, manage workspaces, upload files, and enable URL/SSH access."
|
|
10
|
-
/>
|
|
11
|
-
<meta name="api-base" content="https://api.magpiecloud.com" />
|
|
12
|
-
<meta name="auth-base" content="https://api.magpiecloud.com" />
|
|
13
|
-
<link rel="stylesheet" href="styles.css?v=7" />
|
|
14
|
-
<script src="env.js"></script>
|
|
15
|
-
<style>
|
|
16
|
-
.endpoint { margin-bottom: 2.5rem; }
|
|
17
|
-
.endpoint h3 { margin-bottom: 0.3rem; }
|
|
18
|
-
.method-url {
|
|
19
|
-
display: inline-flex;
|
|
20
|
-
align-items: center;
|
|
21
|
-
gap: 0.5rem;
|
|
22
|
-
margin-bottom: 1rem;
|
|
23
|
-
}
|
|
24
|
-
.method {
|
|
25
|
-
display: inline-block;
|
|
26
|
-
padding: 0.2rem 0.6rem;
|
|
27
|
-
border-radius: 6px;
|
|
28
|
-
font-family: var(--mono);
|
|
29
|
-
font-size: 0.8rem;
|
|
30
|
-
font-weight: 600;
|
|
31
|
-
text-transform: uppercase;
|
|
32
|
-
}
|
|
33
|
-
.method.post { background: rgba(47, 143, 99, 0.15); color: #237c52; }
|
|
34
|
-
.method.get { background: rgba(59, 130, 246, 0.15); color: #2563eb; }
|
|
35
|
-
.method.patch { background: rgba(245, 158, 11, 0.15); color: #b45309; }
|
|
36
|
-
.method.delete { background: rgba(239, 68, 68, 0.15); color: #dc2626; }
|
|
37
|
-
.url-path {
|
|
38
|
-
font-family: var(--mono);
|
|
39
|
-
font-size: 0.95rem;
|
|
40
|
-
color: var(--text);
|
|
41
|
-
}
|
|
42
|
-
.field-table {
|
|
43
|
-
width: 100%;
|
|
44
|
-
border-collapse: collapse;
|
|
45
|
-
font-size: 0.9rem;
|
|
46
|
-
margin: 1rem 0;
|
|
47
|
-
}
|
|
48
|
-
.field-table th {
|
|
49
|
-
text-align: left;
|
|
50
|
-
padding: 0.6rem 0.8rem;
|
|
51
|
-
background: var(--surface-muted);
|
|
52
|
-
border: 1px solid var(--border);
|
|
53
|
-
font-size: 0.78rem;
|
|
54
|
-
text-transform: uppercase;
|
|
55
|
-
letter-spacing: 0.06em;
|
|
56
|
-
color: var(--text-muted);
|
|
57
|
-
}
|
|
58
|
-
.field-table td {
|
|
59
|
-
padding: 0.6rem 0.8rem;
|
|
60
|
-
border: 1px solid var(--border);
|
|
61
|
-
vertical-align: top;
|
|
62
|
-
}
|
|
63
|
-
.field-table code {
|
|
64
|
-
font-size: 0.85rem;
|
|
65
|
-
white-space: nowrap;
|
|
66
|
-
}
|
|
67
|
-
.field-table .required {
|
|
68
|
-
color: #dc2626;
|
|
69
|
-
font-size: 0.75rem;
|
|
70
|
-
font-weight: 600;
|
|
71
|
-
}
|
|
72
|
-
.response-label {
|
|
73
|
-
font-size: 0.8rem;
|
|
74
|
-
font-weight: 600;
|
|
75
|
-
color: var(--text-muted);
|
|
76
|
-
text-transform: uppercase;
|
|
77
|
-
letter-spacing: 0.06em;
|
|
78
|
-
margin: 1rem 0 0.4rem;
|
|
79
|
-
}
|
|
80
|
-
.toc {
|
|
81
|
-
display: flex;
|
|
82
|
-
flex-wrap: wrap;
|
|
83
|
-
gap: 0.5rem;
|
|
84
|
-
margin-bottom: 1.5rem;
|
|
85
|
-
}
|
|
86
|
-
.toc a {
|
|
87
|
-
display: inline-block;
|
|
88
|
-
padding: 0.35rem 0.8rem;
|
|
89
|
-
border-radius: 8px;
|
|
90
|
-
font-size: 0.85rem;
|
|
91
|
-
background: var(--surface-muted);
|
|
92
|
-
border: 1px solid var(--border);
|
|
93
|
-
transition: background 0.15s;
|
|
94
|
-
}
|
|
95
|
-
.toc a:hover {
|
|
96
|
-
background: rgba(47, 143, 99, 0.1);
|
|
97
|
-
border-color: rgba(47, 143, 99, 0.3);
|
|
98
|
-
}
|
|
99
|
-
</style>
|
|
100
|
-
</head>
|
|
101
|
-
<body>
|
|
102
|
-
<div class="backdrop"></div>
|
|
103
|
-
|
|
104
|
-
<header class="site-header">
|
|
105
|
-
<div class="container nav">
|
|
106
|
-
<div class="brand">
|
|
107
|
-
<span class="logo">Mags</span>
|
|
108
|
-
<span class="tag">API Reference</span>
|
|
109
|
-
</div>
|
|
110
|
-
<nav class="nav-links">
|
|
111
|
-
<a href="index.html">Home</a>
|
|
112
|
-
<a href="index.html#quickstart">Docs</a>
|
|
113
|
-
<a href="cookbook.html">Cookbook</a>
|
|
114
|
-
<a href="claude-skill.html">Claude Skill</a>
|
|
115
|
-
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
116
|
-
<a href="login.html">Login</a>
|
|
117
|
-
</nav>
|
|
118
|
-
<div class="nav-cta">
|
|
119
|
-
<a class="button ghost" href="tokens.html">Get API token</a>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</header>
|
|
123
|
-
|
|
124
|
-
<main>
|
|
125
|
-
<section class="hero">
|
|
126
|
-
<div class="container hero-grid">
|
|
127
|
-
<div class="hero-copy">
|
|
128
|
-
<span class="pill">REST API + SDKs</span>
|
|
129
|
-
<h1>Full API reference for Mags.</h1>
|
|
130
|
-
<p class="lead">
|
|
131
|
-
Submit jobs, poll for status, stream logs, manage workspaces, upload files, and enable URL or SSH access. Every endpoint includes curl, Python, and Node.js examples.
|
|
132
|
-
</p>
|
|
133
|
-
<div class="hero-actions">
|
|
134
|
-
<a class="button" href="#endpoints">Endpoints</a>
|
|
135
|
-
<a class="button ghost" href="#workflow">Workflow</a>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
<div class="hero-card tab-group">
|
|
139
|
-
<div class="tab-bar">
|
|
140
|
-
<button class="tab active" data-tab="hero-curl">curl</button>
|
|
141
|
-
<button class="tab" data-tab="hero-py">Python</button>
|
|
142
|
-
<button class="tab" data-tab="hero-js">Node.js</button>
|
|
143
|
-
</div>
|
|
144
|
-
<div class="tab-content active" data-tab="hero-curl">
|
|
145
|
-
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
|
|
146
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
147
|
-
-H "Content-Type: application/json" \
|
|
148
|
-
-d '{
|
|
149
|
-
"script": "echo hello world",
|
|
150
|
-
"type": "inline"
|
|
151
|
-
}'</code></pre>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="tab-content" data-tab="hero-py">
|
|
154
|
-
<pre><code>from mags import Mags
|
|
155
|
-
|
|
156
|
-
m = Mags()
|
|
157
|
-
result = m.run_and_wait("echo hello world")
|
|
158
|
-
print(result["status"])</code></pre>
|
|
159
|
-
</div>
|
|
160
|
-
<div class="tab-content" data-tab="hero-js">
|
|
161
|
-
<pre><code>const Mags = require('@magpiecloud/mags');
|
|
162
|
-
const mags = new Mags();
|
|
163
|
-
|
|
164
|
-
const result = await mags.runAndWait('echo hello world');
|
|
165
|
-
console.log(result.status);</code></pre>
|
|
166
|
-
</div>
|
|
167
|
-
<p class="card-note">Submit a job and get a request_id back in ~50ms.</p>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
</section>
|
|
171
|
-
|
|
172
|
-
<!-- Authentication -->
|
|
173
|
-
<section class="section" id="auth">
|
|
174
|
-
<div class="container">
|
|
175
|
-
<div class="section-title">
|
|
176
|
-
<p>Authentication</p>
|
|
177
|
-
<h2>All requests require a Bearer token.</h2>
|
|
178
|
-
</div>
|
|
179
|
-
<div class="card wide" data-reveal>
|
|
180
|
-
<h3>Authorization header</h3>
|
|
181
|
-
<pre><code>Authorization: Bearer <your-api-token></code></pre>
|
|
182
|
-
<p>Generate tokens at <a href="tokens.html" class="text-link">mags.run/tokens</a> or via <code>mags login</code>.</p>
|
|
183
|
-
<div class="code-tabs tab-group">
|
|
184
|
-
<div class="tab-bar">
|
|
185
|
-
<button class="tab active" data-tab="auth-py">Python</button>
|
|
186
|
-
<button class="tab" data-tab="auth-js">Node.js</button>
|
|
187
|
-
<button class="tab" data-tab="auth-env">Env var</button>
|
|
188
|
-
</div>
|
|
189
|
-
<div class="tab-content active" data-tab="auth-py">
|
|
190
|
-
<pre><code>from mags import Mags
|
|
191
|
-
m = Mags(api_token="your-token")
|
|
192
|
-
# or: export MAGS_API_TOKEN="your-token"</code></pre>
|
|
193
|
-
</div>
|
|
194
|
-
<div class="tab-content" data-tab="auth-js">
|
|
195
|
-
<pre><code>const mags = new Mags({ apiToken: 'your-token' });
|
|
196
|
-
// or: export MAGS_API_TOKEN="your-token"</code></pre>
|
|
197
|
-
</div>
|
|
198
|
-
<div class="tab-content" data-tab="auth-env">
|
|
199
|
-
<pre><code>export MAGS_API_TOKEN="your-token"
|
|
200
|
-
# Works with CLI, Python SDK, and Node.js SDK</code></pre>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</section>
|
|
206
|
-
|
|
207
|
-
<!-- Workflow -->
|
|
208
|
-
<section class="section" id="workflow">
|
|
209
|
-
<div class="container">
|
|
210
|
-
<div class="section-title">
|
|
211
|
-
<p>Workflow</p>
|
|
212
|
-
<h2>Base image + ephemeral workers.</h2>
|
|
213
|
-
</div>
|
|
214
|
-
<div class="grid split">
|
|
215
|
-
<article class="panel" data-reveal>
|
|
216
|
-
<h3>1. Create a base workspace</h3>
|
|
217
|
-
<p>Install dependencies once and save the workspace. This becomes your reusable base image.</p>
|
|
218
|
-
<pre><code>POST /api/v1/mags-jobs
|
|
219
|
-
{
|
|
220
|
-
"script": "apk add python3 py3-pip nodejs npm && pip install requests flask",
|
|
221
|
-
"type": "inline",
|
|
222
|
-
"workspace_id": "python-base"
|
|
223
|
-
}</code></pre>
|
|
224
|
-
</article>
|
|
225
|
-
<article class="panel" data-reveal>
|
|
226
|
-
<h3>2. Spawn ephemeral workers</h3>
|
|
227
|
-
<p>Use the base as a read-only starting point. Workers run in parallel, base is never modified.</p>
|
|
228
|
-
<pre><code>POST /api/v1/mags-jobs
|
|
229
|
-
{
|
|
230
|
-
"script": "python3 run.py",
|
|
231
|
-
"type": "inline",
|
|
232
|
-
"base_workspace_id": "python-base"
|
|
233
|
-
}</code></pre>
|
|
234
|
-
</article>
|
|
235
|
-
</div>
|
|
236
|
-
<div class="card wide" data-reveal>
|
|
237
|
-
<h3>Workspace modes</h3>
|
|
238
|
-
<table class="field-table">
|
|
239
|
-
<thead>
|
|
240
|
-
<tr>
|
|
241
|
-
<th>workspace_id</th>
|
|
242
|
-
<th>base_workspace_id</th>
|
|
243
|
-
<th>Behavior</th>
|
|
244
|
-
</tr>
|
|
245
|
-
</thead>
|
|
246
|
-
<tbody>
|
|
247
|
-
<tr>
|
|
248
|
-
<td><em>omit</em></td>
|
|
249
|
-
<td><em>omit</em></td>
|
|
250
|
-
<td>Fully ephemeral. No S3 sync. Fastest.</td>
|
|
251
|
-
</tr>
|
|
252
|
-
<tr>
|
|
253
|
-
<td><code>"my-ws"</code></td>
|
|
254
|
-
<td><em>omit</em></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
|
-
</tr>
|
|
257
|
-
<tr>
|
|
258
|
-
<td><em>omit</em></td>
|
|
259
|
-
<td><code>"my-base"</code></td>
|
|
260
|
-
<td>Base mounted read-only. Changes discarded after run.</td>
|
|
261
|
-
</tr>
|
|
262
|
-
<tr>
|
|
263
|
-
<td><code>"fork-1"</code></td>
|
|
264
|
-
<td><code>"my-base"</code></td>
|
|
265
|
-
<td>Fork: starts from base, saves changes to <code>fork-1</code>.</td>
|
|
266
|
-
</tr>
|
|
267
|
-
</tbody>
|
|
268
|
-
</table>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
</section>
|
|
272
|
-
|
|
273
|
-
<!-- Endpoints -->
|
|
274
|
-
<section class="section" id="endpoints">
|
|
275
|
-
<div class="container">
|
|
276
|
-
<div class="section-title">
|
|
277
|
-
<p>Endpoints</p>
|
|
278
|
-
<h2>Base URL: <code>https://api.magpiecloud.com/api/v1</code></h2>
|
|
279
|
-
</div>
|
|
280
|
-
|
|
281
|
-
<div class="toc">
|
|
282
|
-
<a href="#submit-job">Submit Job</a>
|
|
283
|
-
<a href="#get-status">Get Status</a>
|
|
284
|
-
<a href="#get-logs">Get Logs</a>
|
|
285
|
-
<a href="#list-jobs">List Jobs</a>
|
|
286
|
-
<a href="#stop-job">Stop Job</a>
|
|
287
|
-
<a href="#enable-access">Enable Access</a>
|
|
288
|
-
<a href="#update-job">Update Job</a>
|
|
289
|
-
<a href="#sync-workspace">Sync Workspace</a>
|
|
290
|
-
<a href="#upload-file">Upload File</a>
|
|
291
|
-
<a href="#list-workspaces">List Workspaces</a>
|
|
292
|
-
<a href="#delete-workspace">Delete Workspace</a>
|
|
293
|
-
<a href="#url-aliases">URL Aliases</a>
|
|
294
|
-
<a href="#cron-endpoints">Cron Jobs</a>
|
|
295
|
-
</div>
|
|
296
|
-
|
|
297
|
-
<!-- Submit Job -->
|
|
298
|
-
<div class="endpoint card wide" id="submit-job" data-reveal>
|
|
299
|
-
<h3>Submit Job</h3>
|
|
300
|
-
<div class="method-url">
|
|
301
|
-
<span class="method post">POST</span>
|
|
302
|
-
<span class="url-path">/api/v1/mags-jobs</span>
|
|
303
|
-
</div>
|
|
304
|
-
<p>Creates a VM, runs a script, and returns. The VM boots in ~300ms from a pool.</p>
|
|
305
|
-
|
|
306
|
-
<p class="response-label">Request body</p>
|
|
307
|
-
<table class="field-table">
|
|
308
|
-
<thead>
|
|
309
|
-
<tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>
|
|
310
|
-
</thead>
|
|
311
|
-
<tbody>
|
|
312
|
-
<tr>
|
|
313
|
-
<td><code>script</code></td>
|
|
314
|
-
<td>string</td>
|
|
315
|
-
<td><span class="required">YES</span></td>
|
|
316
|
-
<td>Shell script to execute inside the VM.</td>
|
|
317
|
-
</tr>
|
|
318
|
-
<tr>
|
|
319
|
-
<td><code>type</code></td>
|
|
320
|
-
<td>string</td>
|
|
321
|
-
<td><span class="required">YES</span></td>
|
|
322
|
-
<td>Always <code>"inline"</code>.</td>
|
|
323
|
-
</tr>
|
|
324
|
-
<tr>
|
|
325
|
-
<td><code>workspace_id</code></td>
|
|
326
|
-
<td>string</td>
|
|
327
|
-
<td>no</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>
|
|
329
|
-
</tr>
|
|
330
|
-
<tr>
|
|
331
|
-
<td><code>base_workspace_id</code></td>
|
|
332
|
-
<td>string</td>
|
|
333
|
-
<td>no</td>
|
|
334
|
-
<td>Mount an existing workspace <strong>read-only</strong> as the starting filesystem.</td>
|
|
335
|
-
</tr>
|
|
336
|
-
<tr>
|
|
337
|
-
<td><code>persistent</code></td>
|
|
338
|
-
<td>bool</td>
|
|
339
|
-
<td>no</td>
|
|
340
|
-
<td>If <code>true</code>, VM stays alive after script finishes and workspace is synced to S3. Required for cloud persistence.</td>
|
|
341
|
-
</tr>
|
|
342
|
-
<tr>
|
|
343
|
-
<td><code>no_sleep</code></td>
|
|
344
|
-
<td>bool</td>
|
|
345
|
-
<td>no</td>
|
|
346
|
-
<td>If <code>true</code>, VM will never be auto-slept by the idle timeout. Requires <code>persistent: true</code>.</td>
|
|
347
|
-
</tr>
|
|
348
|
-
<tr>
|
|
349
|
-
<td><code>startup_command</code></td>
|
|
350
|
-
<td>string</td>
|
|
351
|
-
<td>no</td>
|
|
352
|
-
<td>Command to run when a sleeping persistent VM wakes up.</td>
|
|
353
|
-
</tr>
|
|
354
|
-
<tr>
|
|
355
|
-
<td><code>environment</code></td>
|
|
356
|
-
<td>object</td>
|
|
357
|
-
<td>no</td>
|
|
358
|
-
<td>Key-value env vars injected into the VM.</td>
|
|
359
|
-
</tr>
|
|
360
|
-
<tr>
|
|
361
|
-
<td><code>file_ids</code></td>
|
|
362
|
-
<td>string[]</td>
|
|
363
|
-
<td>no</td>
|
|
364
|
-
<td>File IDs from the upload endpoint. Files are downloaded into <code>/root/</code> before script runs.</td>
|
|
365
|
-
</tr>
|
|
366
|
-
<tr>
|
|
367
|
-
<td><code>disk_gb</code></td>
|
|
368
|
-
<td>int</td>
|
|
369
|
-
<td>no</td>
|
|
370
|
-
<td>Custom disk size in GB. Default is 2GB. The rootfs is resized on-the-fly after VM boot.</td>
|
|
371
|
-
</tr>
|
|
372
|
-
</tbody>
|
|
373
|
-
</table>
|
|
374
|
-
|
|
375
|
-
<p class="response-label">Response (202)</p>
|
|
376
|
-
<pre><code>{
|
|
377
|
-
"request_id": "eab1e214-39c8-4ecc-b941-75551976e0fa",
|
|
378
|
-
"status": "accepted",
|
|
379
|
-
"message": "Mags job submitted successfully"
|
|
380
|
-
}</code></pre>
|
|
381
|
-
|
|
382
|
-
<div class="code-tabs tab-group">
|
|
383
|
-
<p class="response-label">SDK examples</p>
|
|
384
|
-
<div class="tab-bar">
|
|
385
|
-
<button class="tab active" data-tab="sj-curl">curl</button>
|
|
386
|
-
<button class="tab" data-tab="sj-py">Python</button>
|
|
387
|
-
<button class="tab" data-tab="sj-js">Node.js</button>
|
|
388
|
-
</div>
|
|
389
|
-
<div class="tab-content active" data-tab="sj-curl">
|
|
390
|
-
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
|
|
391
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
392
|
-
-H "Content-Type: application/json" \
|
|
393
|
-
-d '{
|
|
394
|
-
"script": "echo hello",
|
|
395
|
-
"type": "inline",
|
|
396
|
-
"workspace_id": "myproject"
|
|
397
|
-
}'</code></pre>
|
|
398
|
-
</div>
|
|
399
|
-
<div class="tab-content" data-tab="sj-py">
|
|
400
|
-
<pre><code>job = m.run("echo hello", workspace_id="myproject")
|
|
401
|
-
print(job["request_id"])
|
|
402
|
-
|
|
403
|
-
# Or run and wait for completion:
|
|
404
|
-
result = m.run_and_wait("echo hello", workspace_id="myproject")
|
|
405
|
-
print(result["status"]) # "completed"</code></pre>
|
|
406
|
-
</div>
|
|
407
|
-
<div class="tab-content" data-tab="sj-js">
|
|
408
|
-
<pre><code>const job = await mags.run('echo hello', { workspaceId: 'myproject' });
|
|
409
|
-
console.log(job.requestId);
|
|
410
|
-
|
|
411
|
-
// Or run and wait:
|
|
412
|
-
const result = await mags.runAndWait('echo hello', { workspaceId: 'myproject' });
|
|
413
|
-
console.log(result.status); // "completed"</code></pre>
|
|
414
|
-
</div>
|
|
415
|
-
</div>
|
|
416
|
-
</div>
|
|
417
|
-
|
|
418
|
-
<!-- Get Status -->
|
|
419
|
-
<div class="endpoint card wide" id="get-status" data-reveal>
|
|
420
|
-
<h3>Get Job Status</h3>
|
|
421
|
-
<div class="method-url">
|
|
422
|
-
<span class="method get">GET</span>
|
|
423
|
-
<span class="url-path">/api/v1/mags-jobs/:id/status</span>
|
|
424
|
-
</div>
|
|
425
|
-
<p>Poll this endpoint to wait for job completion.</p>
|
|
426
|
-
|
|
427
|
-
<p class="response-label">Response (200)</p>
|
|
428
|
-
<pre><code>{
|
|
429
|
-
"request_id": "eab1e214-...",
|
|
430
|
-
"status": "running",
|
|
431
|
-
"vm_id": "a488ceef",
|
|
432
|
-
"persistent": true,
|
|
433
|
-
"subdomain": "28dfed70c32e",
|
|
434
|
-
"url": "https://28dfed70c32e.apps.magpiecloud.com",
|
|
435
|
-
"sleeping": false,
|
|
436
|
-
"exit_code": 0,
|
|
437
|
-
"script_duration_ms": 1200
|
|
438
|
-
}</code></pre>
|
|
439
|
-
|
|
440
|
-
<p class="response-label">Status values</p>
|
|
441
|
-
<table class="field-table">
|
|
442
|
-
<thead>
|
|
443
|
-
<tr><th>Status</th><th>Description</th></tr>
|
|
444
|
-
</thead>
|
|
445
|
-
<tbody>
|
|
446
|
-
<tr><td><code>pending</code></td><td>Job queued, waiting for VM.</td></tr>
|
|
447
|
-
<tr><td><code>running</code></td><td>Script is executing.</td></tr>
|
|
448
|
-
<tr><td><code>completed</code></td><td>Script finished successfully.</td></tr>
|
|
449
|
-
<tr><td><code>error</code></td><td>Script failed or VM error.</td></tr>
|
|
450
|
-
<tr><td><code>sleeping</code></td><td>Persistent VM is idle. Auto-wakes on next request.</td></tr>
|
|
451
|
-
</tbody>
|
|
452
|
-
</table>
|
|
453
|
-
|
|
454
|
-
<div class="code-tabs tab-group">
|
|
455
|
-
<p class="response-label">SDK examples</p>
|
|
456
|
-
<div class="tab-bar">
|
|
457
|
-
<button class="tab active" data-tab="gs-curl">curl</button>
|
|
458
|
-
<button class="tab" data-tab="gs-py">Python</button>
|
|
459
|
-
<button class="tab" data-tab="gs-js">Node.js</button>
|
|
460
|
-
</div>
|
|
461
|
-
<div class="tab-content active" data-tab="gs-curl">
|
|
462
|
-
<pre><code>curl https://api.magpiecloud.com/api/v1/mags-jobs/$ID/status \
|
|
463
|
-
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
464
|
-
</div>
|
|
465
|
-
<div class="tab-content" data-tab="gs-py">
|
|
466
|
-
<pre><code>status = m.status("eab1e214-...")
|
|
467
|
-
print(status["status"]) # "running"</code></pre>
|
|
468
|
-
</div>
|
|
469
|
-
<div class="tab-content" data-tab="gs-js">
|
|
470
|
-
<pre><code>const status = await mags.status('eab1e214-...');
|
|
471
|
-
console.log(status.status); // "running"</code></pre>
|
|
472
|
-
</div>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
|
|
476
|
-
<!-- Get Logs -->
|
|
477
|
-
<div class="endpoint card wide" id="get-logs" data-reveal>
|
|
478
|
-
<h3>Get Job Logs</h3>
|
|
479
|
-
<div class="method-url">
|
|
480
|
-
<span class="method get">GET</span>
|
|
481
|
-
<span class="url-path">/api/v1/mags-jobs/:id/logs</span>
|
|
482
|
-
</div>
|
|
483
|
-
|
|
484
|
-
<p class="response-label">Response (200)</p>
|
|
485
|
-
<pre><code>{
|
|
486
|
-
"logs": [
|
|
487
|
-
{ "timestamp": "...", "level": "info", "message": "hello world", "source": "stdout" },
|
|
488
|
-
{ "timestamp": "...", "level": "error", "message": "warning: ...", "source": "stderr" }
|
|
489
|
-
]
|
|
490
|
-
}</code></pre>
|
|
491
|
-
|
|
492
|
-
<div class="code-tabs tab-group">
|
|
493
|
-
<p class="response-label">SDK examples</p>
|
|
494
|
-
<div class="tab-bar">
|
|
495
|
-
<button class="tab active" data-tab="gl-curl">curl</button>
|
|
496
|
-
<button class="tab" data-tab="gl-py">Python</button>
|
|
497
|
-
<button class="tab" data-tab="gl-js">Node.js</button>
|
|
498
|
-
</div>
|
|
499
|
-
<div class="tab-content active" data-tab="gl-curl">
|
|
500
|
-
<pre><code>curl https://api.magpiecloud.com/api/v1/mags-jobs/$ID/logs \
|
|
501
|
-
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
502
|
-
</div>
|
|
503
|
-
<div class="tab-content" data-tab="gl-py">
|
|
504
|
-
<pre><code>logs = m.logs("eab1e214-...")
|
|
505
|
-
for log in logs["logs"]:
|
|
506
|
-
print(f"[{log['source']}] {log['message']}")</code></pre>
|
|
507
|
-
</div>
|
|
508
|
-
<div class="tab-content" data-tab="gl-js">
|
|
509
|
-
<pre><code>const logs = await mags.logs('eab1e214-...');
|
|
510
|
-
logs.logs.forEach(l => console.log(`[${l.source}] ${l.message}`));</code></pre>
|
|
511
|
-
</div>
|
|
512
|
-
</div>
|
|
513
|
-
</div>
|
|
514
|
-
|
|
515
|
-
<!-- List Jobs -->
|
|
516
|
-
<div class="endpoint card wide" id="list-jobs" data-reveal>
|
|
517
|
-
<h3>List Jobs</h3>
|
|
518
|
-
<div class="method-url">
|
|
519
|
-
<span class="method get">GET</span>
|
|
520
|
-
<span class="url-path">/api/v1/mags-jobs?page=1&page_size=20</span>
|
|
521
|
-
</div>
|
|
522
|
-
|
|
523
|
-
<p class="response-label">Response (200)</p>
|
|
524
|
-
<pre><code>{
|
|
525
|
-
"jobs": [{ "request_id": "...", "status": "completed", "workspace_id": "my-project", ... }],
|
|
526
|
-
"total": 42,
|
|
527
|
-
"page": 1,
|
|
528
|
-
"page_size": 20
|
|
529
|
-
}</code></pre>
|
|
530
|
-
|
|
531
|
-
<div class="code-tabs tab-group">
|
|
532
|
-
<p class="response-label">SDK examples</p>
|
|
533
|
-
<div class="tab-bar">
|
|
534
|
-
<button class="tab active" data-tab="lj-curl">curl</button>
|
|
535
|
-
<button class="tab" data-tab="lj-py">Python</button>
|
|
536
|
-
<button class="tab" data-tab="lj-js">Node.js</button>
|
|
537
|
-
</div>
|
|
538
|
-
<div class="tab-content active" data-tab="lj-curl">
|
|
539
|
-
<pre><code>curl "https://api.magpiecloud.com/api/v1/mags-jobs?page=1&page_size=10" \
|
|
540
|
-
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
541
|
-
</div>
|
|
542
|
-
<div class="tab-content" data-tab="lj-py">
|
|
543
|
-
<pre><code>resp = m.list_jobs(page=1, page_size=10)
|
|
544
|
-
for job in resp["jobs"]:
|
|
545
|
-
print(job["request_id"], job["status"])</code></pre>
|
|
546
|
-
</div>
|
|
547
|
-
<div class="tab-content" data-tab="lj-js">
|
|
548
|
-
<pre><code>const resp = await mags.list({ page: 1, pageSize: 10 });
|
|
549
|
-
resp.jobs.forEach(j => console.log(j.requestId, j.status));</code></pre>
|
|
550
|
-
</div>
|
|
551
|
-
</div>
|
|
552
|
-
</div>
|
|
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
|
-
|
|
590
|
-
<!-- Enable Access -->
|
|
591
|
-
<div class="endpoint card wide" id="enable-access" data-reveal>
|
|
592
|
-
<h3>Enable Access (SSH or HTTP)</h3>
|
|
593
|
-
<div class="method-url">
|
|
594
|
-
<span class="method post">POST</span>
|
|
595
|
-
<span class="url-path">/api/v1/mags-jobs/:id/access</span>
|
|
596
|
-
</div>
|
|
597
|
-
<p>Enables external access to a running VM. Use <code>port: 22</code> for SSH, any other port for HTTP proxy.</p>
|
|
598
|
-
|
|
599
|
-
<p class="response-label">Request body</p>
|
|
600
|
-
<pre><code>{ "port": 22 }</code></pre>
|
|
601
|
-
|
|
602
|
-
<p class="response-label">Response — SSH (port 22)</p>
|
|
603
|
-
<pre><code>{
|
|
604
|
-
"success": true,
|
|
605
|
-
"ssh_host": "api.magpiecloud.com",
|
|
606
|
-
"ssh_port": 40000,
|
|
607
|
-
"ssh_private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...",
|
|
608
|
-
"access_type": "ssh_proxy"
|
|
609
|
-
}</code></pre>
|
|
610
|
-
|
|
611
|
-
<p class="response-label">Response — HTTP (other ports)</p>
|
|
612
|
-
<pre><code>{
|
|
613
|
-
"success": true,
|
|
614
|
-
"ipv6_address": "2a01:4f9:...",
|
|
615
|
-
"port": 8080,
|
|
616
|
-
"access_type": "http_proxy"
|
|
617
|
-
}</code></pre>
|
|
618
|
-
|
|
619
|
-
<div class="code-tabs tab-group">
|
|
620
|
-
<p class="response-label">SDK examples</p>
|
|
621
|
-
<div class="tab-bar">
|
|
622
|
-
<button class="tab active" data-tab="ea-curl">curl</button>
|
|
623
|
-
<button class="tab" data-tab="ea-py">Python</button>
|
|
624
|
-
<button class="tab" data-tab="ea-js">Node.js</button>
|
|
625
|
-
</div>
|
|
626
|
-
<div class="tab-content active" data-tab="ea-curl">
|
|
627
|
-
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs/$ID/access \
|
|
628
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
629
|
-
-H "Content-Type: application/json" \
|
|
630
|
-
-d '{ "port": 22 }'</code></pre>
|
|
631
|
-
</div>
|
|
632
|
-
<div class="tab-content" data-tab="ea-py">
|
|
633
|
-
<pre><code># SSH access
|
|
634
|
-
ssh = m.enable_access(request_id, port=22)
|
|
635
|
-
print(f"ssh root@{ssh['ssh_host']} -p {ssh['ssh_port']}")
|
|
636
|
-
|
|
637
|
-
# HTTP access
|
|
638
|
-
access = m.enable_access(request_id, port=8080)
|
|
639
|
-
print(access.get("url"))</code></pre>
|
|
640
|
-
</div>
|
|
641
|
-
<div class="tab-content" data-tab="ea-js">
|
|
642
|
-
<pre><code>// SSH access
|
|
643
|
-
const ssh = await mags.enableAccess(requestId, 22);
|
|
644
|
-
console.log(`ssh root@${ssh.sshHost} -p ${ssh.sshPort}`);
|
|
645
|
-
|
|
646
|
-
// HTTP access
|
|
647
|
-
const access = await mags.enableAccess(requestId, 8080);</code></pre>
|
|
648
|
-
</div>
|
|
649
|
-
</div>
|
|
650
|
-
</div>
|
|
651
|
-
|
|
652
|
-
<!-- Update Job -->
|
|
653
|
-
<div class="endpoint card wide" id="update-job" data-reveal>
|
|
654
|
-
<h3>Update Job</h3>
|
|
655
|
-
<div class="method-url">
|
|
656
|
-
<span class="method patch">PATCH</span>
|
|
657
|
-
<span class="url-path">/api/v1/mags-jobs/:id</span>
|
|
658
|
-
</div>
|
|
659
|
-
<p>Update a job's settings. All fields are optional — only include the ones you want to change.</p>
|
|
660
|
-
|
|
661
|
-
<p class="response-label">Request body</p>
|
|
662
|
-
<div class="ref-table-wrap">
|
|
663
|
-
<table class="ref-table">
|
|
664
|
-
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
665
|
-
<tbody>
|
|
666
|
-
<tr><td><code>startup_command</code></td><td>string</td><td>Command to run when VM wakes from sleep</td></tr>
|
|
667
|
-
<tr><td><code>no_sleep</code></td><td>bool</td><td>If <code>true</code>, VM never auto-sleeps. If <code>false</code>, re-enables auto-sleep. Requires persistent VM.</td></tr>
|
|
668
|
-
</tbody>
|
|
669
|
-
</table>
|
|
670
|
-
</div>
|
|
671
|
-
<pre><code>{ "no_sleep": true }</code></pre>
|
|
672
|
-
|
|
673
|
-
<div class="code-tabs tab-group">
|
|
674
|
-
<p class="response-label">SDK examples</p>
|
|
675
|
-
<div class="tab-bar">
|
|
676
|
-
<button class="tab active" data-tab="uj-curl">curl</button>
|
|
677
|
-
<button class="tab" data-tab="uj-cli">CLI</button>
|
|
678
|
-
<button class="tab" data-tab="uj-py">Python</button>
|
|
679
|
-
<button class="tab" data-tab="uj-js">Node.js</button>
|
|
680
|
-
</div>
|
|
681
|
-
<div class="tab-content active" data-tab="uj-curl">
|
|
682
|
-
<pre><code># Enable no-sleep
|
|
683
|
-
curl -X PATCH https://api.magpiecloud.com/api/v1/mags-jobs/$ID \
|
|
684
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
685
|
-
-H "Content-Type: application/json" \
|
|
686
|
-
-d '{ "no_sleep": true }'
|
|
687
|
-
|
|
688
|
-
# Set startup command
|
|
689
|
-
curl -X PATCH https://api.magpiecloud.com/api/v1/mags-jobs/$ID \
|
|
690
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
691
|
-
-H "Content-Type: application/json" \
|
|
692
|
-
-d '{ "startup_command": "python3 -m http.server 8080" }'</code></pre>
|
|
693
|
-
</div>
|
|
694
|
-
<div class="tab-content" data-tab="uj-cli">
|
|
695
|
-
<pre><code># Enable no-sleep on an existing VM
|
|
696
|
-
mags set myvm --no-sleep
|
|
697
|
-
|
|
698
|
-
# Re-enable auto-sleep
|
|
699
|
-
mags set myvm --sleep</code></pre>
|
|
700
|
-
</div>
|
|
701
|
-
<div class="tab-content" data-tab="uj-py">
|
|
702
|
-
<pre><code># Enable no-sleep
|
|
703
|
-
m.update_job(request_id, no_sleep=True)
|
|
704
|
-
|
|
705
|
-
# Set startup command
|
|
706
|
-
m.update_job(request_id, startup_command="python3 -m http.server 8080")</code></pre>
|
|
707
|
-
</div>
|
|
708
|
-
<div class="tab-content" data-tab="uj-js">
|
|
709
|
-
<pre><code>await mags.updateJob(requestId, { noSleep: true });</code></pre>
|
|
710
|
-
</div>
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
|
|
714
|
-
<!-- Upload File -->
|
|
715
|
-
<div class="endpoint card wide" id="upload-file" data-reveal>
|
|
716
|
-
<h3>Upload File</h3>
|
|
717
|
-
<div class="method-url">
|
|
718
|
-
<span class="method post">POST</span>
|
|
719
|
-
<span class="url-path">/api/v1/mags-files</span>
|
|
720
|
-
</div>
|
|
721
|
-
<p>Upload a file (max 100MB) that can be attached to jobs via <code>file_ids</code>.</p>
|
|
722
|
-
|
|
723
|
-
<p class="response-label">Request</p>
|
|
724
|
-
<pre><code>Content-Type: multipart/form-data
|
|
725
|
-
Field name: file</code></pre>
|
|
726
|
-
|
|
727
|
-
<p class="response-label">Response (201)</p>
|
|
728
|
-
<pre><code>{
|
|
729
|
-
"file_id": "a1b2c3d4-...",
|
|
730
|
-
"file_name": "script.py",
|
|
731
|
-
"size": 4096
|
|
732
|
-
}</code></pre>
|
|
733
|
-
|
|
734
|
-
<div class="code-tabs tab-group">
|
|
735
|
-
<p class="response-label">SDK examples</p>
|
|
736
|
-
<div class="tab-bar">
|
|
737
|
-
<button class="tab active" data-tab="uf-curl">curl</button>
|
|
738
|
-
<button class="tab" data-tab="uf-py">Python</button>
|
|
739
|
-
<button class="tab" data-tab="uf-js">Node.js</button>
|
|
740
|
-
</div>
|
|
741
|
-
<div class="tab-content active" data-tab="uf-curl">
|
|
742
|
-
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-files \
|
|
743
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
744
|
-
-F "file=@script.py"</code></pre>
|
|
745
|
-
</div>
|
|
746
|
-
<div class="tab-content" data-tab="uf-py">
|
|
747
|
-
<pre><code>file_id = m.upload_file("script.py")
|
|
748
|
-
file_ids = m.upload_files(["script.py", "data.csv"])</code></pre>
|
|
749
|
-
</div>
|
|
750
|
-
<div class="tab-content" data-tab="uf-js">
|
|
751
|
-
<pre><code>const fileId = await mags.uploadFile('script.py');</code></pre>
|
|
752
|
-
</div>
|
|
753
|
-
</div>
|
|
754
|
-
</div>
|
|
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
|
-
|
|
787
|
-
<!-- List Workspaces -->
|
|
788
|
-
<div class="endpoint card wide" id="list-workspaces" data-reveal>
|
|
789
|
-
<h3>List Workspaces</h3>
|
|
790
|
-
<div class="method-url">
|
|
791
|
-
<span class="method get">GET</span>
|
|
792
|
-
<span class="url-path">/api/v1/mags-workspaces</span>
|
|
793
|
-
</div>
|
|
794
|
-
<p>List all persistent workspaces for the authenticated user.</p>
|
|
795
|
-
|
|
796
|
-
<p class="response-label">Response (200)</p>
|
|
797
|
-
<pre><code>{
|
|
798
|
-
"workspaces": [
|
|
799
|
-
{ "workspace_id": "myproject", "job_count": 12, "last_used": "2026-02-04T...", "has_active": false }
|
|
800
|
-
],
|
|
801
|
-
"total": 3
|
|
802
|
-
}</code></pre>
|
|
803
|
-
|
|
804
|
-
<div class="code-tabs tab-group">
|
|
805
|
-
<p class="response-label">SDK examples</p>
|
|
806
|
-
<div class="tab-bar">
|
|
807
|
-
<button class="tab active" data-tab="lw-curl">curl</button>
|
|
808
|
-
<button class="tab" data-tab="lw-py">Python</button>
|
|
809
|
-
<button class="tab" data-tab="lw-js">Node.js</button>
|
|
810
|
-
<button class="tab" data-tab="lw-cli">CLI</button>
|
|
811
|
-
</div>
|
|
812
|
-
<div class="tab-content active" data-tab="lw-curl">
|
|
813
|
-
<pre><code>curl https://api.magpiecloud.com/api/v1/mags-workspaces \
|
|
814
|
-
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
815
|
-
</div>
|
|
816
|
-
<div class="tab-content" data-tab="lw-py">
|
|
817
|
-
<pre><code>ws = m.list_workspaces()
|
|
818
|
-
for w in ws["workspaces"]:
|
|
819
|
-
print(w["workspace_id"], w["job_count"])</code></pre>
|
|
820
|
-
</div>
|
|
821
|
-
<div class="tab-content" data-tab="lw-js">
|
|
822
|
-
<pre><code>const ws = await mags.listWorkspaces();
|
|
823
|
-
ws.workspaces.forEach(w => console.log(w.workspaceId));</code></pre>
|
|
824
|
-
</div>
|
|
825
|
-
<div class="tab-content" data-tab="lw-cli">
|
|
826
|
-
<pre><code>mags workspace list</code></pre>
|
|
827
|
-
</div>
|
|
828
|
-
</div>
|
|
829
|
-
</div>
|
|
830
|
-
|
|
831
|
-
<!-- Delete Workspace -->
|
|
832
|
-
<div class="endpoint card wide" id="delete-workspace" data-reveal>
|
|
833
|
-
<h3>Delete Workspace</h3>
|
|
834
|
-
<div class="method-url">
|
|
835
|
-
<span class="method delete">DELETE</span>
|
|
836
|
-
<span class="url-path">/api/v1/mags-workspaces/:id</span>
|
|
837
|
-
</div>
|
|
838
|
-
<p>Permanently delete a workspace and all its S3 data. Active jobs using this workspace must be stopped first.</p>
|
|
839
|
-
|
|
840
|
-
<p class="response-label">Response (200)</p>
|
|
841
|
-
<pre><code>{ "success": true, "message": "Workspace 'myproject' deleted" }</code></pre>
|
|
842
|
-
|
|
843
|
-
<p class="response-label">Error (409 Conflict)</p>
|
|
844
|
-
<pre><code>{ "error": "workspace 'myproject' has 1 active job(s); stop them first" }</code></pre>
|
|
845
|
-
|
|
846
|
-
<div class="code-tabs tab-group">
|
|
847
|
-
<p class="response-label">SDK examples</p>
|
|
848
|
-
<div class="tab-bar">
|
|
849
|
-
<button class="tab active" data-tab="dw-py">Python</button>
|
|
850
|
-
<button class="tab" data-tab="dw-cli">CLI</button>
|
|
851
|
-
<button class="tab" data-tab="dw-curl">curl</button>
|
|
852
|
-
</div>
|
|
853
|
-
<div class="tab-content active" data-tab="dw-py">
|
|
854
|
-
<pre><code>m.delete_workspace("myproject")</code></pre>
|
|
855
|
-
</div>
|
|
856
|
-
<div class="tab-content" data-tab="dw-cli">
|
|
857
|
-
<pre><code>mags workspace delete myproject
|
|
858
|
-
mags workspace delete myproject --force # skip confirmation</code></pre>
|
|
859
|
-
</div>
|
|
860
|
-
<div class="tab-content" data-tab="dw-curl">
|
|
861
|
-
<pre><code>curl -X DELETE https://api.magpiecloud.com/api/v1/mags-workspaces/myproject \
|
|
862
|
-
-H "Authorization: Bearer $TOKEN"</code></pre>
|
|
863
|
-
</div>
|
|
864
|
-
</div>
|
|
865
|
-
</div>
|
|
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
|
-
|
|
926
|
-
<!-- Cron Endpoints -->
|
|
927
|
-
<div class="endpoint card wide" id="cron-endpoints" data-reveal>
|
|
928
|
-
<h3>Cron Jobs</h3>
|
|
929
|
-
<div class="method-url">
|
|
930
|
-
<span class="method post">POST</span>
|
|
931
|
-
<span class="url-path">/api/v1/mags-cron</span>
|
|
932
|
-
</div>
|
|
933
|
-
<div class="method-url" style="margin-left:0.5rem">
|
|
934
|
-
<span class="method get">GET</span>
|
|
935
|
-
<span class="url-path">/api/v1/mags-cron</span>
|
|
936
|
-
</div>
|
|
937
|
-
<div class="method-url" style="margin-left:0.5rem">
|
|
938
|
-
<span class="method patch">PATCH</span>
|
|
939
|
-
<span class="url-path">/api/v1/mags-cron/:id</span>
|
|
940
|
-
</div>
|
|
941
|
-
<div class="method-url" style="margin-left:0.5rem">
|
|
942
|
-
<span class="method delete">DELETE</span>
|
|
943
|
-
<span class="url-path">/api/v1/mags-cron/:id</span>
|
|
944
|
-
</div>
|
|
945
|
-
<p>Create, list, update, and delete scheduled cron jobs.</p>
|
|
946
|
-
|
|
947
|
-
<p class="response-label">Create request body</p>
|
|
948
|
-
<table class="field-table">
|
|
949
|
-
<thead>
|
|
950
|
-
<tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>
|
|
951
|
-
</thead>
|
|
952
|
-
<tbody>
|
|
953
|
-
<tr><td><code>name</code></td><td>string</td><td><span class="required">YES</span></td><td>Name for the cron job.</td></tr>
|
|
954
|
-
<tr><td><code>cron_expression</code></td><td>string</td><td><span class="required">YES</span></td><td>Standard cron expression (e.g. <code>0 * * * *</code>).</td></tr>
|
|
955
|
-
<tr><td><code>script</code></td><td>string</td><td><span class="required">YES</span></td><td>Script to run on each trigger.</td></tr>
|
|
956
|
-
<tr><td><code>workspace_id</code></td><td>string</td><td>no</td><td>Workspace to use.</td></tr>
|
|
957
|
-
<tr><td><code>persistent</code></td><td>bool</td><td>no</td><td>Keep VM alive after script.</td></tr>
|
|
958
|
-
</tbody>
|
|
959
|
-
</table>
|
|
960
|
-
|
|
961
|
-
<div class="code-tabs tab-group">
|
|
962
|
-
<p class="response-label">SDK examples</p>
|
|
963
|
-
<div class="tab-bar">
|
|
964
|
-
<button class="tab active" data-tab="cr-curl">curl</button>
|
|
965
|
-
<button class="tab" data-tab="cr-py">Python</button>
|
|
966
|
-
<button class="tab" data-tab="cr-js">Node.js</button>
|
|
967
|
-
<button class="tab" data-tab="cr-cli">CLI</button>
|
|
968
|
-
</div>
|
|
969
|
-
<div class="tab-content active" data-tab="cr-curl">
|
|
970
|
-
<pre><code>curl -X POST https://api.magpiecloud.com/api/v1/mags-cron \
|
|
971
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
972
|
-
-H "Content-Type: application/json" \
|
|
973
|
-
-d '{
|
|
974
|
-
"name": "nightly-backup",
|
|
975
|
-
"cron_expression": "0 0 * * *",
|
|
976
|
-
"script": "tar czf backup.tar.gz /data",
|
|
977
|
-
"workspace_id": "backups"
|
|
978
|
-
}'</code></pre>
|
|
979
|
-
</div>
|
|
980
|
-
<div class="tab-content" data-tab="cr-py">
|
|
981
|
-
<pre><code>cron = m.cron_create(
|
|
982
|
-
name="nightly-backup",
|
|
983
|
-
cron_expression="0 0 * * *",
|
|
984
|
-
script="tar czf backup.tar.gz /data",
|
|
985
|
-
workspace_id="backups",
|
|
986
|
-
)
|
|
987
|
-
jobs = m.cron_list()
|
|
988
|
-
m.cron_update(cron["id"], enabled=False)
|
|
989
|
-
m.cron_delete(cron["id"])</code></pre>
|
|
990
|
-
</div>
|
|
991
|
-
<div class="tab-content" data-tab="cr-js">
|
|
992
|
-
<pre><code>const cron = await mags.cronCreate({
|
|
993
|
-
name: 'nightly-backup',
|
|
994
|
-
cronExpression: '0 0 * * *',
|
|
995
|
-
script: 'tar czf backup.tar.gz /data',
|
|
996
|
-
workspaceId: 'backups',
|
|
997
|
-
});
|
|
998
|
-
await mags.cronDelete(cron.id);</code></pre>
|
|
999
|
-
</div>
|
|
1000
|
-
<div class="tab-content" data-tab="cr-cli">
|
|
1001
|
-
<pre><code>mags cron add --name "nightly-backup" --schedule "0 0 * * *" -w backups \
|
|
1002
|
-
'tar czf backup.tar.gz /data'
|
|
1003
|
-
mags cron list
|
|
1004
|
-
mags cron remove <id></code></pre>
|
|
1005
|
-
</div>
|
|
1006
|
-
</div>
|
|
1007
|
-
</div>
|
|
1008
|
-
</div>
|
|
1009
|
-
</section>
|
|
1010
|
-
|
|
1011
|
-
<!-- Errors -->
|
|
1012
|
-
<section class="section" id="errors">
|
|
1013
|
-
<div class="container">
|
|
1014
|
-
<div class="section-title">
|
|
1015
|
-
<p>Errors</p>
|
|
1016
|
-
<h2>Error responses.</h2>
|
|
1017
|
-
</div>
|
|
1018
|
-
<div class="card wide" data-reveal>
|
|
1019
|
-
<p>All errors return:</p>
|
|
1020
|
-
<pre><code>{ "error": "description of what went wrong" }</code></pre>
|
|
1021
|
-
<table class="field-table">
|
|
1022
|
-
<thead>
|
|
1023
|
-
<tr><th>Status</th><th>Meaning</th></tr>
|
|
1024
|
-
</thead>
|
|
1025
|
-
<tbody>
|
|
1026
|
-
<tr><td><code>400</code></td><td>Invalid request (missing fields, bad JSON).</td></tr>
|
|
1027
|
-
<tr><td><code>401</code></td><td>Missing or invalid auth token.</td></tr>
|
|
1028
|
-
<tr><td><code>404</code></td><td>Job or workspace not found.</td></tr>
|
|
1029
|
-
<tr><td><code>409</code></td><td>Workspace has active jobs (delete conflict).</td></tr>
|
|
1030
|
-
<tr><td><code>500</code></td><td>Server error.</td></tr>
|
|
1031
|
-
</tbody>
|
|
1032
|
-
</table>
|
|
1033
|
-
</div>
|
|
1034
|
-
</div>
|
|
1035
|
-
</section>
|
|
1036
|
-
|
|
1037
|
-
<!-- VM Environment -->
|
|
1038
|
-
<section class="section" id="vm-env">
|
|
1039
|
-
<div class="container">
|
|
1040
|
-
<div class="section-title">
|
|
1041
|
-
<p>VM Environment</p>
|
|
1042
|
-
<h2>What runs inside the VM.</h2>
|
|
1043
|
-
</div>
|
|
1044
|
-
<div class="grid split">
|
|
1045
|
-
<article class="panel" data-reveal>
|
|
1046
|
-
<h3>System</h3>
|
|
1047
|
-
<table class="field-table">
|
|
1048
|
-
<tbody>
|
|
1049
|
-
<tr><td><strong>OS</strong></td><td>Alpine Linux (~50MB rootfs)</td></tr>
|
|
1050
|
-
<tr><td><strong>Shell</strong></td><td><code>/bin/sh</code> (ash)</td></tr>
|
|
1051
|
-
<tr><td><strong>Package manager</strong></td><td><code>apk add <package></code></td></tr>
|
|
1052
|
-
<tr><td><strong>User</strong></td><td><code>root</code></td></tr>
|
|
1053
|
-
<tr><td><strong>Working directory</strong></td><td><code>/root</code></td></tr>
|
|
1054
|
-
</tbody>
|
|
1055
|
-
</table>
|
|
1056
|
-
</article>
|
|
1057
|
-
<article class="panel" data-reveal>
|
|
1058
|
-
<h3>Performance</h3>
|
|
1059
|
-
<table class="field-table">
|
|
1060
|
-
<tbody>
|
|
1061
|
-
<tr><td><strong>Boot time</strong></td><td>~300ms from pool</td></tr>
|
|
1062
|
-
<tr><td><strong>Default timeout</strong></td><td>300 seconds</td></tr>
|
|
1063
|
-
<tr><td><strong>Filesystem</strong></td><td>OverlayFS on base rootfs</td></tr>
|
|
1064
|
-
<tr><td><strong>Workspace sync</strong></td><td>S3-backed persistence</td></tr>
|
|
1065
|
-
</tbody>
|
|
1066
|
-
</table>
|
|
1067
|
-
</article>
|
|
1068
|
-
</div>
|
|
1069
|
-
</div>
|
|
1070
|
-
</section>
|
|
1071
|
-
</main>
|
|
1072
|
-
|
|
1073
|
-
<footer class="site-footer">
|
|
1074
|
-
<div class="container footer-grid">
|
|
1075
|
-
<div>
|
|
1076
|
-
<div class="brand">
|
|
1077
|
-
<span class="logo">Mags</span>
|
|
1078
|
-
<span class="tag">Instant sandboxes and runtime environments</span>
|
|
1079
|
-
</div>
|
|
1080
|
-
<p>Build, test, and run from clean microVMs whenever you need them.</p>
|
|
1081
|
-
</div>
|
|
1082
|
-
<div class="footer-links">
|
|
1083
|
-
<a href="index.html">Home</a>
|
|
1084
|
-
<a href="cookbook.html">Cookbook</a>
|
|
1085
|
-
<a href="https://pypi.org/project/magpie-mags/" rel="noreferrer">Python SDK</a>
|
|
1086
|
-
<a href="https://www.npmjs.com/package/@magpiecloud/mags" rel="noreferrer">Node.js SDK</a>
|
|
1087
|
-
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
1088
|
-
<a href="login.html">Login</a>
|
|
1089
|
-
</div>
|
|
1090
|
-
</div>
|
|
1091
|
-
</footer>
|
|
1092
|
-
|
|
1093
|
-
<script src="script.js?v=8"></script>
|
|
1094
|
-
</body>
|
|
1095
|
-
</html>
|