@magpiecloud/mags 1.8.5 → 1.8.7
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/API.md +7 -2
- package/QUICKSTART.md +1 -0
- package/README.md +1 -0
- package/bin/mags.js +53 -6
- package/package.json +1 -1
- package/python/dist/magpie_mags-1.3.3-py3-none-any.whl +0 -0
- package/python/dist/magpie_mags-1.3.3.tar.gz +0 -0
- package/python/pyproject.toml +1 -1
- package/python/src/magpie_mags.egg-info/PKG-INFO +20 -2
- package/python/src/mags/client.py +20 -5
- package/website/api.html +40 -13
- package/website/claude-skill.html +8 -10
- package/website/cookbook/hn-marketing.sh +25 -33
- package/website/cookbook.html +24 -20
- package/website/index.html +52 -18
- package/website/llms.txt +2 -0
- package/website/login.html +50 -30
- package/website/mags.md +6 -0
- package/website/script.js +31 -3
- package/website/tokens.html +10 -12
- package/website/usage.html +9 -11
package/API.md
CHANGED
|
@@ -212,12 +212,17 @@ HTTP services are also available via subdomain: `https://<subdomain>.apps.magpie
|
|
|
212
212
|
PATCH /api/v1/mags-jobs/:id
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
Update a job's
|
|
215
|
+
Update a job's settings. All fields are optional — only include the ones you want to change.
|
|
216
216
|
|
|
217
217
|
**Request:**
|
|
218
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
|
+
|
|
219
224
|
```json
|
|
220
|
-
{ "
|
|
225
|
+
{ "no_sleep": true }
|
|
221
226
|
```
|
|
222
227
|
|
|
223
228
|
**Response (200):**
|
package/QUICKSTART.md
CHANGED
|
@@ -139,6 +139,7 @@ mags run -w webapp-prod "..."
|
|
|
139
139
|
| `mags list` | List jobs | `mags list -n 20` |
|
|
140
140
|
| `mags logs ID` | View job logs | `mags logs abc123` |
|
|
141
141
|
| `mags status ID` | Job status | `mags status abc123` |
|
|
142
|
+
| `mags set ID` | Update VM settings | `mags set myvm --no-sleep` |
|
|
142
143
|
|
|
143
144
|
## CLI Flags
|
|
144
145
|
|
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ mags run -p --url 'python3 -m http.server 8080'
|
|
|
107
107
|
| `mags list` | List recent jobs |
|
|
108
108
|
| `mags url <job-id> [port]` | Enable URL access for a job |
|
|
109
109
|
| `mags stop <job-id>` | Stop a running job |
|
|
110
|
+
| `mags set <name\|id> [options]` | Update VM settings (`--no-sleep`, `--sleep`) |
|
|
110
111
|
|
|
111
112
|
### Run Options
|
|
112
113
|
|
package/bin/mags.js
CHANGED
|
@@ -211,6 +211,7 @@ ${colors.bold}Commands:${colors.reset}
|
|
|
211
211
|
url alias <sub> <workspace> Create a stable URL alias for a workspace
|
|
212
212
|
url alias list List your URL aliases
|
|
213
213
|
url alias remove <subdomain> Delete a URL alias
|
|
214
|
+
set <name|id> [options] Update VM settings
|
|
214
215
|
stop <name|id> Stop a running job
|
|
215
216
|
resize <workspace> --disk <GB> Resize a workspace's disk (restarts VM)
|
|
216
217
|
sync <workspace|id> Sync workspace to S3 (without stopping)
|
|
@@ -595,8 +596,8 @@ async function runJob(args) {
|
|
|
595
596
|
if (baseWorkspace) log('blue', `Base workspace: ${baseWorkspace} (read-only)`);
|
|
596
597
|
if (persistent) log('yellow', 'Persistent: VM will stay alive');
|
|
597
598
|
|
|
598
|
-
// Poll for completion
|
|
599
|
-
const maxAttempts =
|
|
599
|
+
// Poll for completion (200ms intervals, 600 attempts = 2 min timeout)
|
|
600
|
+
const maxAttempts = 600;
|
|
600
601
|
let attempt = 0;
|
|
601
602
|
|
|
602
603
|
while (attempt < maxAttempts) {
|
|
@@ -609,7 +610,7 @@ async function runJob(args) {
|
|
|
609
610
|
// If --url requested, wait until VM is actually assigned (vm_id populated)
|
|
610
611
|
if (enableUrl && !status.vm_id) {
|
|
611
612
|
process.stdout.write('.');
|
|
612
|
-
await sleep(
|
|
613
|
+
await sleep(200);
|
|
613
614
|
attempt++;
|
|
614
615
|
continue;
|
|
615
616
|
}
|
|
@@ -633,7 +634,7 @@ async function runJob(args) {
|
|
|
633
634
|
}
|
|
634
635
|
|
|
635
636
|
process.stdout.write('.');
|
|
636
|
-
await sleep(
|
|
637
|
+
await sleep(200);
|
|
637
638
|
attempt++;
|
|
638
639
|
}
|
|
639
640
|
|
|
@@ -829,6 +830,48 @@ async function stopJob(nameOrId) {
|
|
|
829
830
|
}
|
|
830
831
|
}
|
|
831
832
|
|
|
833
|
+
async function setJobSettings(args) {
|
|
834
|
+
let nameOrId = null;
|
|
835
|
+
let noSleep = null;
|
|
836
|
+
|
|
837
|
+
for (let i = 0; i < args.length; i++) {
|
|
838
|
+
if (args[i] === '--no-sleep') {
|
|
839
|
+
noSleep = true;
|
|
840
|
+
} else if (args[i] === '--sleep') {
|
|
841
|
+
noSleep = false;
|
|
842
|
+
} else if (!nameOrId) {
|
|
843
|
+
nameOrId = args[i];
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (!nameOrId) {
|
|
848
|
+
log('red', 'Error: Job name or ID required');
|
|
849
|
+
console.log(`\nUsage: mags set <name|id> [options]\n`);
|
|
850
|
+
console.log('Options:');
|
|
851
|
+
console.log(' --no-sleep Never auto-sleep this VM');
|
|
852
|
+
console.log(' --sleep Re-enable auto-sleep');
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const payload = {};
|
|
857
|
+
if (noSleep !== null) payload.no_sleep = noSleep;
|
|
858
|
+
|
|
859
|
+
if (Object.keys(payload).length === 0) {
|
|
860
|
+
log('red', 'Error: No settings to update. Use --no-sleep or --sleep');
|
|
861
|
+
process.exit(1);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const requestId = await resolveJobId(nameOrId);
|
|
865
|
+
log('blue', `Updating settings for ${requestId}...`);
|
|
866
|
+
const resp = await request('PATCH', `/api/v1/mags-jobs/${requestId}`, payload);
|
|
867
|
+
if (resp.success) {
|
|
868
|
+
if (noSleep === true) log('green', 'VM set to never auto-sleep');
|
|
869
|
+
if (noSleep === false) log('green', 'VM set to auto-sleep when idle');
|
|
870
|
+
} else {
|
|
871
|
+
log('red', `Failed: ${resp.error || 'unknown error'}`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
832
875
|
async function resizeVM(args) {
|
|
833
876
|
let name = null;
|
|
834
877
|
let diskGB = 0;
|
|
@@ -1452,7 +1495,7 @@ async function sshToJob(nameOrId) {
|
|
|
1452
1495
|
process.exit(1);
|
|
1453
1496
|
}
|
|
1454
1497
|
process.stdout.write('.');
|
|
1455
|
-
await sleep(
|
|
1498
|
+
await sleep(300);
|
|
1456
1499
|
}
|
|
1457
1500
|
console.log('');
|
|
1458
1501
|
}
|
|
@@ -1628,7 +1671,7 @@ async function main() {
|
|
|
1628
1671
|
break;
|
|
1629
1672
|
case '--version':
|
|
1630
1673
|
case '-v':
|
|
1631
|
-
console.log('mags v1.8.
|
|
1674
|
+
console.log('mags v1.8.7');
|
|
1632
1675
|
process.exit(0);
|
|
1633
1676
|
break;
|
|
1634
1677
|
case 'new':
|
|
@@ -1671,6 +1714,10 @@ async function main() {
|
|
|
1671
1714
|
await requireAuth();
|
|
1672
1715
|
await listJobs();
|
|
1673
1716
|
break;
|
|
1717
|
+
case 'set':
|
|
1718
|
+
await requireAuth();
|
|
1719
|
+
await setJobSettings(args.slice(1));
|
|
1720
|
+
break;
|
|
1674
1721
|
case 'stop':
|
|
1675
1722
|
await requireAuth();
|
|
1676
1723
|
await stopJob(args[1]);
|
package/package.json
CHANGED
|
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.3
|
|
4
4
|
Summary: Mags SDK - Execute scripts on Magpie's instant VM infrastructure
|
|
5
5
|
Author: Magpie Cloud
|
|
6
6
|
License: MIT
|
|
@@ -91,6 +91,19 @@ m.run_and_wait(
|
|
|
91
91
|
)
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
### Always-On VMs
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# VM that never auto-sleeps — stays running 24/7
|
|
98
|
+
job = m.run(
|
|
99
|
+
"python3 server.py",
|
|
100
|
+
workspace_id="my-api",
|
|
101
|
+
persistent=True,
|
|
102
|
+
no_sleep=True,
|
|
103
|
+
)
|
|
104
|
+
# Auto-recovers if the host goes down
|
|
105
|
+
```
|
|
106
|
+
|
|
94
107
|
### Enable URL / SSH Access
|
|
95
108
|
|
|
96
109
|
```python
|
|
@@ -141,8 +154,13 @@ print(f"Jobs: {usage['total_jobs']}, VM seconds: {usage['vm_seconds']:.0f}")
|
|
|
141
154
|
|
|
142
155
|
| Method | Description |
|
|
143
156
|
|--------|-------------|
|
|
144
|
-
| `run(script, **opts)` | Submit a job (
|
|
157
|
+
| `run(script, **opts)` | Submit a job (`persistent`, `no_sleep`, `workspace_id`, ...) |
|
|
145
158
|
| `run_and_wait(script, **opts)` | Submit and block until done |
|
|
159
|
+
| `new(name, **opts)` | Create a persistent VM workspace |
|
|
160
|
+
| `find_job(name_or_id)` | Find a running/sleeping job by name or workspace |
|
|
161
|
+
| `exec(name_or_id, command)` | Run a command on an existing VM via SSH |
|
|
162
|
+
| `stop(name_or_id)` | Stop a running job |
|
|
163
|
+
| `resize(workspace, disk_gb)` | Resize a workspace's disk |
|
|
146
164
|
| `status(request_id)` | Get job status |
|
|
147
165
|
| `logs(request_id)` | Get job logs |
|
|
148
166
|
| `list_jobs(page, page_size)` | List recent jobs |
|
|
@@ -202,11 +202,26 @@ class Mags:
|
|
|
202
202
|
"GET", "/mags-jobs", params={"page": page, "page_size": page_size}
|
|
203
203
|
)
|
|
204
204
|
|
|
205
|
-
def update_job(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
205
|
+
def update_job(
|
|
206
|
+
self,
|
|
207
|
+
request_id: str,
|
|
208
|
+
*,
|
|
209
|
+
startup_command: str | None = None,
|
|
210
|
+
no_sleep: bool | None = None,
|
|
211
|
+
) -> dict:
|
|
212
|
+
"""Update a job's settings.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
request_id: The job/workspace ID to update.
|
|
216
|
+
startup_command: Command to run when VM wakes from sleep.
|
|
217
|
+
no_sleep: If True, VM never auto-sleeps. If False, re-enables auto-sleep.
|
|
218
|
+
"""
|
|
219
|
+
payload: dict = {}
|
|
220
|
+
if startup_command is not None:
|
|
221
|
+
payload["startup_command"] = startup_command
|
|
222
|
+
if no_sleep is not None:
|
|
223
|
+
payload["no_sleep"] = no_sleep
|
|
224
|
+
return self._request("PATCH", f"/mags-jobs/{request_id}", json=payload)
|
|
210
225
|
|
|
211
226
|
def enable_access(self, request_id: str, *, port: int = 8080) -> dict:
|
|
212
227
|
"""Enable external access (URL or SSH) for a persistent job's VM.
|
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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
<style>
|
|
16
16
|
.endpoint { margin-bottom: 2.5rem; }
|
|
@@ -109,11 +109,11 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
<nav class="nav-links">
|
|
111
111
|
<a href="index.html">Home</a>
|
|
112
|
-
<a href="index.html#quickstart">
|
|
113
|
-
<a href="index.html#sdk">SDKs</a>
|
|
112
|
+
<a href="index.html#quickstart">Docs</a>
|
|
114
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>
|
|
115
116
|
<a href="login.html">Login</a>
|
|
116
|
-
<a href="tokens.html">Tokens</a>
|
|
117
117
|
</nav>
|
|
118
118
|
<div class="nav-cta">
|
|
119
119
|
<a class="button ghost" href="tokens.html">Get API token</a>
|
|
@@ -617,29 +617,57 @@ const access = await mags.enableAccess(requestId, { port: 8080 });</code></pre>
|
|
|
617
617
|
<span class="method patch">PATCH</span>
|
|
618
618
|
<span class="url-path">/api/v1/mags-jobs/:id</span>
|
|
619
619
|
</div>
|
|
620
|
-
<p>Update a job's
|
|
620
|
+
<p>Update a job's settings. All fields are optional — only include the ones you want to change.</p>
|
|
621
621
|
|
|
622
622
|
<p class="response-label">Request body</p>
|
|
623
|
-
<
|
|
623
|
+
<div class="ref-table-wrap">
|
|
624
|
+
<table class="ref-table">
|
|
625
|
+
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
626
|
+
<tbody>
|
|
627
|
+
<tr><td><code>startup_command</code></td><td>string</td><td>Command to run when VM wakes from sleep</td></tr>
|
|
628
|
+
<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>
|
|
629
|
+
</tbody>
|
|
630
|
+
</table>
|
|
631
|
+
</div>
|
|
632
|
+
<pre><code>{ "no_sleep": true }</code></pre>
|
|
624
633
|
|
|
625
634
|
<div class="code-tabs tab-group">
|
|
626
635
|
<p class="response-label">SDK examples</p>
|
|
627
636
|
<div class="tab-bar">
|
|
628
637
|
<button class="tab active" data-tab="uj-curl">curl</button>
|
|
638
|
+
<button class="tab" data-tab="uj-cli">CLI</button>
|
|
629
639
|
<button class="tab" data-tab="uj-py">Python</button>
|
|
630
640
|
<button class="tab" data-tab="uj-js">Node.js</button>
|
|
631
641
|
</div>
|
|
632
642
|
<div class="tab-content active" data-tab="uj-curl">
|
|
633
|
-
<pre><code
|
|
643
|
+
<pre><code># Enable no-sleep
|
|
644
|
+
curl -X PATCH https://api.magpiecloud.com/api/v1/mags-jobs/$ID \
|
|
645
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
646
|
+
-H "Content-Type: application/json" \
|
|
647
|
+
-d '{ "no_sleep": true }'
|
|
648
|
+
|
|
649
|
+
# Set startup command
|
|
650
|
+
curl -X PATCH https://api.magpiecloud.com/api/v1/mags-jobs/$ID \
|
|
634
651
|
-H "Authorization: Bearer $TOKEN" \
|
|
635
652
|
-H "Content-Type: application/json" \
|
|
636
653
|
-d '{ "startup_command": "python3 -m http.server 8080" }'</code></pre>
|
|
654
|
+
</div>
|
|
655
|
+
<div class="tab-content" data-tab="uj-cli">
|
|
656
|
+
<pre><code># Enable no-sleep on an existing VM
|
|
657
|
+
mags set myvm --no-sleep
|
|
658
|
+
|
|
659
|
+
# Re-enable auto-sleep
|
|
660
|
+
mags set myvm --sleep</code></pre>
|
|
637
661
|
</div>
|
|
638
662
|
<div class="tab-content" data-tab="uj-py">
|
|
639
|
-
<pre><code
|
|
663
|
+
<pre><code># Enable no-sleep
|
|
664
|
+
m.update_job(request_id, no_sleep=True)
|
|
665
|
+
|
|
666
|
+
# Set startup command
|
|
667
|
+
m.update_job(request_id, startup_command="python3 -m http.server 8080")</code></pre>
|
|
640
668
|
</div>
|
|
641
669
|
<div class="tab-content" data-tab="uj-js">
|
|
642
|
-
<pre><code>await mags.updateJob(requestId, {
|
|
670
|
+
<pre><code>await mags.updateJob(requestId, { noSleep: true });</code></pre>
|
|
643
671
|
</div>
|
|
644
672
|
</div>
|
|
645
673
|
</div>
|
|
@@ -924,16 +952,15 @@ mags cron remove <id></code></pre>
|
|
|
924
952
|
</div>
|
|
925
953
|
<div class="footer-links">
|
|
926
954
|
<a href="index.html">Home</a>
|
|
955
|
+
<a href="cookbook.html">Cookbook</a>
|
|
927
956
|
<a href="https://pypi.org/project/magpie-mags/" rel="noreferrer">Python SDK</a>
|
|
928
957
|
<a href="https://www.npmjs.com/package/@magpiecloud/mags" rel="noreferrer">Node.js SDK</a>
|
|
929
|
-
<a href="
|
|
930
|
-
<a href="tokens.html">Tokens</a>
|
|
931
|
-
<a href="usage.html">Usage</a>
|
|
958
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
932
959
|
<a href="login.html">Login</a>
|
|
933
960
|
</div>
|
|
934
961
|
</div>
|
|
935
962
|
</footer>
|
|
936
963
|
|
|
937
|
-
<script src="script.js?v=
|
|
964
|
+
<script src="script.js?v=7"></script>
|
|
938
965
|
</body>
|
|
939
966
|
</html>
|
|
@@ -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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
<nav class="nav-links">
|
|
26
26
|
<a href="index.html">Home</a>
|
|
27
|
-
<a href="index.html#
|
|
28
|
-
<a href="api.html">API
|
|
27
|
+
<a href="index.html#quickstart">Docs</a>
|
|
28
|
+
<a href="api.html">API</a>
|
|
29
29
|
<a href="cookbook.html">Cookbook</a>
|
|
30
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
30
31
|
<a href="login.html">Login</a>
|
|
31
32
|
</nav>
|
|
32
33
|
<div class="nav-cta">
|
|
@@ -467,17 +468,14 @@ Use workspace "monitors" so the log persists</code></pre>
|
|
|
467
468
|
</div>
|
|
468
469
|
<div class="footer-links">
|
|
469
470
|
<a href="index.html">Home</a>
|
|
470
|
-
<a href="
|
|
471
|
-
<a href="usage.html">Usage</a>
|
|
472
|
-
<a href="tokens.html">Tokens</a>
|
|
471
|
+
<a href="api.html">API</a>
|
|
473
472
|
<a href="cookbook.html">Cookbook</a>
|
|
474
|
-
<a href="https://
|
|
475
|
-
<a href="
|
|
476
|
-
<a href="https://github.com/magpiecloud/mags" rel="noreferrer">GitHub</a>
|
|
473
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
474
|
+
<a href="login.html">Login</a>
|
|
477
475
|
</div>
|
|
478
476
|
</div>
|
|
479
477
|
</footer>
|
|
480
478
|
|
|
481
|
-
<script src="script.js"></script>
|
|
479
|
+
<script src="script.js?v=7"></script>
|
|
482
480
|
</body>
|
|
483
481
|
</html>
|
|
@@ -1,50 +1,42 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# hn-marketing.sh — Fetch top HN stories and
|
|
2
|
+
# hn-marketing.sh — Fetch top 10 HN stories and save to CSV
|
|
3
3
|
# Dependencies: curl, jq (install with: apk add -q curl jq)
|
|
4
4
|
|
|
5
5
|
set -e
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
CSV="${HN_CSV:-./hn-top10.csv}"
|
|
8
|
+
TIMESTAMP=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
echo ""
|
|
12
|
-
|
|
13
|
-
IDS=$(curl -sf "https://hacker-news.firebaseio.com/v0/topstories.json" | jq -r '.[0:100] | .[]')
|
|
14
|
-
|
|
15
|
-
if [ -z "$IDS" ]; then
|
|
16
|
-
echo "Error: could not fetch story IDs"
|
|
17
|
-
exit 1
|
|
10
|
+
# Write CSV header if file doesn't exist
|
|
11
|
+
if [ ! -f "$CSV" ]; then
|
|
12
|
+
echo "timestamp,rank,score,title,url,hn_link" > "$CSV"
|
|
18
13
|
fi
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
echo "=== HN Top 10 ==="
|
|
16
|
+
echo "Fetched at: $TIMESTAMP"
|
|
17
|
+
echo ""
|
|
18
|
+
|
|
19
|
+
# Get top 10 story IDs
|
|
20
|
+
IDS=$(curl -sf "https://hacker-news.firebaseio.com/v0/topstories.json" | jq -r '.[0:10] | .[]')
|
|
21
21
|
|
|
22
|
+
RANK=0
|
|
22
23
|
for ID in $IDS; do
|
|
24
|
+
RANK=$((RANK + 1))
|
|
23
25
|
ITEM=$(curl -sf "https://hacker-news.firebaseio.com/v0/item/${ID}.json")
|
|
24
|
-
TITLE=$(echo "$ITEM" | jq -r '.title // empty')
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
TITLE=$(echo "$ITEM" | jq -r '.title // "untitled"' | tr -d '\n\r' | sed 's/,/;/g')
|
|
28
|
+
URL=$(echo "$ITEM" | jq -r '.url // ""' | tr -d '\n\r')
|
|
29
|
+
SCORE=$(echo "$ITEM" | jq -r '.score // 0')
|
|
30
|
+
HN_LINK="https://news.ycombinator.com/item?id=$ID"
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
[ -z "$URL" ] && URL="$HN_LINK"
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
TIME=$(echo "$ITEM" | jq -r '.time // 0')
|
|
36
|
-
COUNT=$((COUNT + 1))
|
|
34
|
+
echo "[$RANK] $TITLE"
|
|
35
|
+
echo " Score: $SCORE | $URL"
|
|
36
|
+
echo ""
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
echo " Score: $SCORE | Link: $URL"
|
|
40
|
-
echo " HN: https://news.ycombinator.com/item?id=$ID"
|
|
41
|
-
echo ""
|
|
42
|
-
fi
|
|
38
|
+
echo "$TIMESTAMP,$RANK,$SCORE,$TITLE,$URL,$HN_LINK" >> "$CSV"
|
|
43
39
|
done
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
else
|
|
48
|
-
echo "---"
|
|
49
|
-
echo "Found $COUNT marketing-related stories."
|
|
50
|
-
fi
|
|
41
|
+
echo "---"
|
|
42
|
+
echo "Saved to $CSV"
|
package/website/cookbook.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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -24,13 +24,11 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
<nav class="nav-links">
|
|
26
26
|
<a href="index.html">Home</a>
|
|
27
|
-
<a href="index.html#quickstart">
|
|
28
|
-
<a href="
|
|
29
|
-
<a href="index.html#api">API</a>
|
|
30
|
-
<a href="login.html">Login</a>
|
|
31
|
-
<a href="usage.html">Usage</a>
|
|
32
|
-
<a href="tokens.html">Tokens</a>
|
|
27
|
+
<a href="index.html#quickstart">Docs</a>
|
|
28
|
+
<a href="api.html">API</a>
|
|
33
29
|
<a href="claude-skill.html">Claude Skill</a>
|
|
30
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
31
|
+
<a href="login.html">Login</a>
|
|
34
32
|
</nav>
|
|
35
33
|
<div class="nav-cta">
|
|
36
34
|
<a class="button ghost" href="index.html#quickstart">Get started</a>
|
|
@@ -171,18 +169,25 @@ mags logs <job-id></code></pre>
|
|
|
171
169
|
</article>
|
|
172
170
|
<article class="recipe-item" data-reveal>
|
|
173
171
|
<div class="recipe-header">
|
|
174
|
-
<h3>HN
|
|
172
|
+
<h3>HN Top 10 Digest</h3>
|
|
175
173
|
<span class="pill">Cron</span>
|
|
176
174
|
</div>
|
|
177
|
-
<pre><code># Upload script + install deps
|
|
178
|
-
mags run -f
|
|
179
|
-
'
|
|
175
|
+
<pre><code># Upload script + install deps (file lands in /root/)
|
|
176
|
+
mags run -f hn-marketing.sh -w hn-digest \
|
|
177
|
+
'chmod +x /root/hn-marketing.sh && apk add -q curl jq'
|
|
178
|
+
|
|
179
|
+
# Run it once (saves to /root/hn-top10.csv)
|
|
180
|
+
mags run -w hn-digest 'HN_CSV=/root/hn-top10.csv sh /root/hn-marketing.sh'
|
|
180
181
|
|
|
181
|
-
#
|
|
182
|
-
mags cron add --name "hn-
|
|
182
|
+
# Schedule it (every 2 hours, results append to CSV)
|
|
183
|
+
mags cron add --name "hn-top10" \
|
|
183
184
|
--schedule "0 */2 * * *" -w hn-digest \
|
|
184
|
-
'sh /root/hn-marketing.sh'</code></pre>
|
|
185
|
-
<p class="label"
|
|
185
|
+
'HN_CSV=/root/hn-top10.csv sh /root/hn-marketing.sh'</code></pre>
|
|
186
|
+
<p class="label">
|
|
187
|
+
<a href="cookbook/hn-marketing.sh" download>Download hn-marketing.sh</a>
|
|
188
|
+
·
|
|
189
|
+
<a href="cookbook/hn-marketing.html">Full recipe + live demo →</a>
|
|
190
|
+
</p>
|
|
186
191
|
</article>
|
|
187
192
|
</div>
|
|
188
193
|
</div>
|
|
@@ -264,15 +269,14 @@ curl -X POST https://api.magpiecloud.com/api/v1/mags-cron \
|
|
|
264
269
|
</div>
|
|
265
270
|
<div class="footer-links">
|
|
266
271
|
<a href="index.html">Home</a>
|
|
267
|
-
<a href="
|
|
268
|
-
<a href="usage.html">Usage</a>
|
|
269
|
-
<a href="tokens.html">Tokens</a>
|
|
272
|
+
<a href="api.html">API</a>
|
|
270
273
|
<a href="claude-skill.html">Claude Skill</a>
|
|
271
|
-
<a href="https://
|
|
274
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
275
|
+
<a href="login.html">Login</a>
|
|
272
276
|
</div>
|
|
273
277
|
</div>
|
|
274
278
|
</footer>
|
|
275
279
|
|
|
276
|
-
<script src="script.js"></script>
|
|
280
|
+
<script src="script.js?v=7"></script>
|
|
277
281
|
</body>
|
|
278
282
|
</html>
|
package/website/index.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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -23,11 +23,10 @@
|
|
|
23
23
|
<span class="tag">Secure cloud sandboxes for the AI age</span>
|
|
24
24
|
</div>
|
|
25
25
|
<nav class="nav-links">
|
|
26
|
-
<a href="#
|
|
27
|
-
<a href="
|
|
28
|
-
<a href="
|
|
29
|
-
<a href="
|
|
30
|
-
<a href="api.html">API Docs</a>
|
|
26
|
+
<a href="#quickstart">Docs</a>
|
|
27
|
+
<a href="api.html">API</a>
|
|
28
|
+
<a href="cookbook.html">Cookbook</a>
|
|
29
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
31
30
|
<a href="login.html" id="nav-auth-link">Login</a>
|
|
32
31
|
</nav>
|
|
33
32
|
<div class="nav-cta">
|
|
@@ -41,22 +40,16 @@
|
|
|
41
40
|
<section class="hero" id="overview">
|
|
42
41
|
<div class="container hero-grid">
|
|
43
42
|
<div class="hero-copy">
|
|
44
|
-
<span class="pill">New: Python SDK</span>
|
|
45
43
|
<h1>Secure sandboxes that boot in milliseconds. Your data stays.</h1>
|
|
46
44
|
<p class="lead">
|
|
47
|
-
Give your AI agents
|
|
48
|
-
completely isolated, boots
|
|
49
|
-
automatically.
|
|
50
|
-
needs to run without risk.
|
|
45
|
+
Give your AI agents an instant home. Every sandbox is
|
|
46
|
+
completely isolated, boots in ~300ms, and syncs your files to the cloud
|
|
47
|
+
automatically. CLI, Python, and Node.js — pick your tool.
|
|
51
48
|
</p>
|
|
52
49
|
<div class="hero-actions">
|
|
53
50
|
<a class="button" href="#quickstart">Quickstart</a>
|
|
54
51
|
<a class="button ghost" href="api.html">API Reference</a>
|
|
55
52
|
</div>
|
|
56
|
-
<div class="hero-meta">
|
|
57
|
-
<span>CLI, Python, and Node.js — pick your tool.</span>
|
|
58
|
-
<span>Sandboxes in ~300ms. Files persist to the cloud between runs.</span>
|
|
59
|
-
</div>
|
|
60
53
|
</div>
|
|
61
54
|
<div class="hero-card tab-group" aria-label="Mags example">
|
|
62
55
|
<div class="tab-bar">
|
|
@@ -205,6 +198,7 @@ mags login</code></pre>
|
|
|
205
198
|
<tr><td><code>mags status <id></code></td><td>Get job status</td></tr>
|
|
206
199
|
<tr><td><code>mags logs <id></code></td><td>Get job output</td></tr>
|
|
207
200
|
<tr><td><code>mags stop <id></code></td><td>Stop a running job</td></tr>
|
|
201
|
+
<tr><td><code>mags set <id> [options]</code></td><td>Update VM settings (e.g. <code>--no-sleep</code>, <code>--sleep</code>)</td></tr>
|
|
208
202
|
<tr><td><code>mags sync <workspace></code></td><td>Sync workspace to the cloud now</td></tr>
|
|
209
203
|
<tr><td><code>mags url <id> [port]</code></td><td>Enable public URL access</td></tr>
|
|
210
204
|
<tr><td><code>mags workspace list</code></td><td>List persistent workspaces</td></tr>
|
|
@@ -513,6 +507,45 @@ await mags.cronCreate({
|
|
|
513
507
|
</div>
|
|
514
508
|
</section>
|
|
515
509
|
|
|
510
|
+
<!-- ── Always-On Servers ──────────────────────────── -->
|
|
511
|
+
<section class="section" id="always-on">
|
|
512
|
+
<div class="container">
|
|
513
|
+
<div class="section-title">
|
|
514
|
+
<p>Always-On Servers</p>
|
|
515
|
+
<h2>Keep your sandboxes running forever.</h2>
|
|
516
|
+
</div>
|
|
517
|
+
<div class="grid split">
|
|
518
|
+
<article class="panel" data-reveal>
|
|
519
|
+
<h3>Never auto-sleep</h3>
|
|
520
|
+
<p>By default, persistent sandboxes auto-sleep after 10 minutes of inactivity to save resources. With the <code>--no-sleep</code> flag, your VM stays running 24/7 — perfect for web servers, workers, and background processes.</p>
|
|
521
|
+
<pre><code># CLI
|
|
522
|
+
mags run -w my-api -p --no-sleep --url --port 3000 'node server.js'
|
|
523
|
+
|
|
524
|
+
# Python
|
|
525
|
+
m.run("node server.js",
|
|
526
|
+
workspace_id="my-api", persistent=True, no_sleep=True)
|
|
527
|
+
|
|
528
|
+
# Node.js
|
|
529
|
+
await mags.run('node server.js', {
|
|
530
|
+
workspaceId: 'my-api', persistent: true, noSleep: true,
|
|
531
|
+
});</code></pre>
|
|
532
|
+
</article>
|
|
533
|
+
<article class="panel" data-reveal>
|
|
534
|
+
<h3>Auto-recovery</h3>
|
|
535
|
+
<p>Always-on sandboxes are automatically monitored. If the host goes down, your VM is re-provisioned on a healthy server within ~60 seconds — no manual intervention needed.</p>
|
|
536
|
+
<h3 style="margin-top:1.2rem">How it works</h3>
|
|
537
|
+
<ul class="list">
|
|
538
|
+
<li>Requires <code>-p</code> (persistent) flag</li>
|
|
539
|
+
<li>VM stays in <code>running</code> state indefinitely</li>
|
|
540
|
+
<li>Combine with <code>--url</code> to expose a public HTTPS endpoint</li>
|
|
541
|
+
<li>Use <code>--startup-command</code> to auto-restart your process if the VM recovers</li>
|
|
542
|
+
<li>Files persist to the cloud via workspace sync</li>
|
|
543
|
+
</ul>
|
|
544
|
+
</article>
|
|
545
|
+
</div>
|
|
546
|
+
</div>
|
|
547
|
+
</section>
|
|
548
|
+
|
|
516
549
|
<!-- ── SDKs + API ──────────────────────────────────── -->
|
|
517
550
|
<section class="section" id="sdk">
|
|
518
551
|
<div class="container">
|
|
@@ -692,20 +725,21 @@ console.log(result.logs);</code></pre>
|
|
|
692
725
|
<a href="api.html">API Reference</a>
|
|
693
726
|
<a href="claude-skill.html">Claude Skill</a>
|
|
694
727
|
<a href="cookbook.html">Cookbook</a>
|
|
728
|
+
<a href="https://discord.gg/3avpC2nS" rel="noreferrer" target="_blank">Discord</a>
|
|
695
729
|
<a href="https://github.com/magpiecloud/mags/issues" rel="noreferrer">Issues</a>
|
|
696
730
|
</div>
|
|
697
731
|
</div>
|
|
698
732
|
</footer>
|
|
699
733
|
|
|
700
|
-
<script src="script.js?v=
|
|
734
|
+
<script src="script.js?v=7"></script>
|
|
701
735
|
<script>
|
|
702
736
|
(function() {
|
|
703
737
|
var token = localStorage.getItem('microvm-access-token');
|
|
704
738
|
if (token) {
|
|
705
739
|
var nav = document.getElementById('nav-auth-link');
|
|
706
740
|
var cta = document.getElementById('cta-auth-link');
|
|
707
|
-
if (nav) { nav.textContent = '
|
|
708
|
-
if (cta) { cta.textContent = 'Dashboard'; cta.href = '
|
|
741
|
+
if (nav) { nav.textContent = 'Usage'; nav.href = 'usage.html'; }
|
|
742
|
+
if (cta) { cta.textContent = 'Dashboard'; cta.href = 'usage.html'; }
|
|
709
743
|
}
|
|
710
744
|
})();
|
|
711
745
|
</script>
|
package/website/llms.txt
CHANGED
|
@@ -35,6 +35,8 @@ mags run 'echo Hello World'
|
|
|
35
35
|
- `mags logs <job-id>` — View job logs
|
|
36
36
|
- `mags status <job-id>` — Check job status
|
|
37
37
|
- `mags stop <job-id>` — Stop a running job
|
|
38
|
+
- `mags set <name|id> --no-sleep` — Never auto-sleep this VM
|
|
39
|
+
- `mags set <name|id> --sleep` — Re-enable auto-sleep
|
|
38
40
|
|
|
39
41
|
## Run Options
|
|
40
42
|
|
package/website/login.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Mags Login</title>
|
|
7
|
-
<meta name="description" content="Sign in to Mags with Google
|
|
7
|
+
<meta name="description" content="Sign in to Mags with Google." />
|
|
8
8
|
<meta name="api-base" content="https://api.magpiecloud.com" />
|
|
9
9
|
<meta name="auth-base" content="https://api.magpiecloud.com" />
|
|
10
|
-
<link rel="stylesheet" href="styles.css?v=
|
|
10
|
+
<link rel="stylesheet" href="styles.css?v=6" />
|
|
11
11
|
<script src="env.js"></script>
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
<nav class="nav-links">
|
|
23
23
|
<a href="index.html">Home</a>
|
|
24
|
-
<a href="index.html#quickstart">
|
|
25
|
-
<a href="
|
|
26
|
-
<a href="
|
|
24
|
+
<a href="index.html#quickstart">Docs</a>
|
|
25
|
+
<a href="api.html">API</a>
|
|
26
|
+
<a href="cookbook.html">Cookbook</a>
|
|
27
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
27
28
|
</nav>
|
|
28
29
|
<div class="nav-cta">
|
|
29
30
|
<a class="button ghost" href="index.html">Back to home</a>
|
|
@@ -37,30 +38,10 @@
|
|
|
37
38
|
<div>
|
|
38
39
|
<span class="pill">Login</span>
|
|
39
40
|
<h1 class="auth-title">Sign in to Mags.</h1>
|
|
40
|
-
<p class="auth-subtitle">
|
|
41
|
+
<p class="auth-subtitle">Sign in with your Google account to get started.</p>
|
|
41
42
|
</div>
|
|
42
43
|
|
|
43
44
|
<button class="button full" type="button" data-google-login>Continue with Google</button>
|
|
44
|
-
|
|
45
|
-
<div class="auth-divider"><span>or</span></div>
|
|
46
|
-
|
|
47
|
-
<form class="login-form" data-login-form>
|
|
48
|
-
<div class="form-group">
|
|
49
|
-
<label class="form-label" for="login-email">Email</label>
|
|
50
|
-
<input class="input" type="email" id="login-email" name="email" required />
|
|
51
|
-
</div>
|
|
52
|
-
<div class="form-group">
|
|
53
|
-
<label class="form-label" for="login-password">Password</label>
|
|
54
|
-
<input class="input" type="password" id="login-password" name="password" required />
|
|
55
|
-
</div>
|
|
56
|
-
<button class="button full" type="submit">Sign in</button>
|
|
57
|
-
<p class="form-message" data-login-message></p>
|
|
58
|
-
</form>
|
|
59
|
-
|
|
60
|
-
<div class="form-links">
|
|
61
|
-
<a data-auth-link="/auth/forgot-password" href="#">Forgot password?</a>
|
|
62
|
-
<a data-auth-link="/auth/register" href="#">Create an account</a>
|
|
63
|
-
</div>
|
|
64
45
|
</div>
|
|
65
46
|
</div>
|
|
66
47
|
</main>
|
|
@@ -76,13 +57,52 @@
|
|
|
76
57
|
</div>
|
|
77
58
|
<div class="footer-links">
|
|
78
59
|
<a href="index.html">Home</a>
|
|
79
|
-
<a href="
|
|
80
|
-
<a href="
|
|
81
|
-
<a href="
|
|
60
|
+
<a href="api.html">API</a>
|
|
61
|
+
<a href="cookbook.html">Cookbook</a>
|
|
62
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
82
63
|
</div>
|
|
83
64
|
</div>
|
|
84
65
|
</footer>
|
|
85
66
|
|
|
86
|
-
<script src="script.js"></script>
|
|
67
|
+
<script src="script.js?v=7"></script>
|
|
68
|
+
<script>
|
|
69
|
+
(function() {
|
|
70
|
+
// Handle logout: clear tokens when ?logout=1 is present
|
|
71
|
+
var params = new URLSearchParams(window.location.search);
|
|
72
|
+
if (params.get('logout') === '1') {
|
|
73
|
+
localStorage.removeItem('microvm-access-token');
|
|
74
|
+
localStorage.removeItem('microvm-refresh-token');
|
|
75
|
+
// Clean up the URL
|
|
76
|
+
window.history.replaceState({}, '', 'login.html');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If already logged in, redirect to next or show dashboard link
|
|
81
|
+
var token = localStorage.getItem('microvm-access-token');
|
|
82
|
+
if (token) {
|
|
83
|
+
var next = params.get('next');
|
|
84
|
+
if (next && next.startsWith('/')) {
|
|
85
|
+
window.location.replace(next);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
var shell = document.querySelector('.auth-shell');
|
|
89
|
+
if (shell) {
|
|
90
|
+
shell.innerHTML =
|
|
91
|
+
'<div>' +
|
|
92
|
+
'<span class="pill">Logged in</span>' +
|
|
93
|
+
'<h1 class="auth-title">You\'re signed in.</h1>' +
|
|
94
|
+
'<p class="auth-subtitle">You\'re already logged in to Mags.</p>' +
|
|
95
|
+
'</div>' +
|
|
96
|
+
'<a class="button full" href="usage.html">Go to Dashboard</a>' +
|
|
97
|
+
'<button class="button ghost full" type="button" id="logout-btn" style="margin-top:0.75rem">Sign out</button>';
|
|
98
|
+
document.getElementById('logout-btn').addEventListener('click', function() {
|
|
99
|
+
localStorage.removeItem('microvm-access-token');
|
|
100
|
+
localStorage.removeItem('microvm-refresh-token');
|
|
101
|
+
window.location.reload();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
</script>
|
|
87
107
|
</body>
|
|
88
108
|
</html>
|
package/website/mags.md
CHANGED
|
@@ -71,6 +71,12 @@ mags url <name-or-id> [port]
|
|
|
71
71
|
mags stop <name-or-id>
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
### Update VM settings
|
|
75
|
+
```bash
|
|
76
|
+
mags set <name-or-id> --no-sleep # Never auto-sleep
|
|
77
|
+
mags set <name-or-id> --sleep # Re-enable auto-sleep
|
|
78
|
+
```
|
|
79
|
+
|
|
74
80
|
### Run without workspace (ephemeral, fastest)
|
|
75
81
|
```bash
|
|
76
82
|
mags run -e '<script>'
|
package/website/script.js
CHANGED
|
@@ -87,6 +87,30 @@ const tokenStore = {
|
|
|
87
87
|
},
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
+
/* ── Extract tokens from URL after OAuth callback redirect ── */
|
|
91
|
+
(function () {
|
|
92
|
+
var params = new URLSearchParams(window.location.search);
|
|
93
|
+
var token = params.get('token');
|
|
94
|
+
var refresh = params.get('refresh');
|
|
95
|
+
if (token && refresh) {
|
|
96
|
+
tokenStore.setTokens({ accessToken: token, refreshToken: refresh });
|
|
97
|
+
params.delete('token');
|
|
98
|
+
params.delete('refresh');
|
|
99
|
+
var clean = window.location.pathname;
|
|
100
|
+
var remaining = params.toString();
|
|
101
|
+
if (remaining) clean += '?' + remaining;
|
|
102
|
+
window.history.replaceState({}, '', clean);
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
|
|
106
|
+
/* ── Redirect to login for protected pages when not authed ── */
|
|
107
|
+
(function () {
|
|
108
|
+
if (!document.querySelector('[data-auth-required]')) return;
|
|
109
|
+
if (tokenStore.getAccessToken()) return;
|
|
110
|
+
var next = window.location.pathname + window.location.search;
|
|
111
|
+
window.location.replace('login.html?next=' + encodeURIComponent(next));
|
|
112
|
+
})();
|
|
113
|
+
|
|
90
114
|
const refreshTokens = async () => {
|
|
91
115
|
const refreshToken = tokenStore.getRefreshToken();
|
|
92
116
|
if (!refreshToken) return null;
|
|
@@ -183,7 +207,10 @@ copyButtons.forEach((button) => {
|
|
|
183
207
|
const googleLogin = document.querySelector('[data-google-login]');
|
|
184
208
|
if (googleLogin) {
|
|
185
209
|
googleLogin.addEventListener('click', () => {
|
|
186
|
-
|
|
210
|
+
var url = withBase('/auth/google', AUTH_BASE);
|
|
211
|
+
var next = new URLSearchParams(window.location.search).get('next');
|
|
212
|
+
if (next) url += (url.includes('?') ? '&' : '?') + 'next=' + encodeURIComponent(next);
|
|
213
|
+
window.location.href = url;
|
|
187
214
|
});
|
|
188
215
|
}
|
|
189
216
|
|
|
@@ -223,11 +250,12 @@ if (loginForm) {
|
|
|
223
250
|
refreshToken: data.refresh_token,
|
|
224
251
|
});
|
|
225
252
|
if (message) {
|
|
226
|
-
message.textContent = 'Signed in. Redirecting
|
|
253
|
+
message.textContent = 'Signed in. Redirecting...';
|
|
227
254
|
}
|
|
228
255
|
loginForm.reset();
|
|
256
|
+
var dest = new URLSearchParams(window.location.search).get('next') || 'usage.html';
|
|
229
257
|
setTimeout(() => {
|
|
230
|
-
window.location.href =
|
|
258
|
+
window.location.href = dest;
|
|
231
259
|
}, 600);
|
|
232
260
|
} catch (error) {
|
|
233
261
|
if (message) {
|
package/website/tokens.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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -24,16 +24,14 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
<nav class="nav-links">
|
|
26
26
|
<a href="index.html">Home</a>
|
|
27
|
-
<a href="index.html#quickstart">Quickstart</a>
|
|
28
|
-
<a href="index.html#cli">CLI</a>
|
|
29
|
-
<a href="index.html#api">API</a>
|
|
30
|
-
<a href="login.html">Login</a>
|
|
31
27
|
<a href="usage.html">Usage</a>
|
|
32
|
-
<a href="
|
|
28
|
+
<a href="api.html">API</a>
|
|
33
29
|
<a href="cookbook.html">Cookbook</a>
|
|
30
|
+
<a href="claude-skill.html">Claude Skill</a>
|
|
31
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
34
32
|
</nav>
|
|
35
33
|
<div class="nav-cta">
|
|
36
|
-
<a class="button ghost" href="
|
|
34
|
+
<a class="button ghost" href="usage.html">Usage</a>
|
|
37
35
|
</div>
|
|
38
36
|
</div>
|
|
39
37
|
</header>
|
|
@@ -49,7 +47,7 @@
|
|
|
49
47
|
the dashboard and store them securely.
|
|
50
48
|
</p>
|
|
51
49
|
<div class="hero-actions">
|
|
52
|
-
<a class="button" href="
|
|
50
|
+
<a class="button" href="#tokens">Create a token</a>
|
|
53
51
|
<a class="button ghost" href="#usage">Usage in CLI</a>
|
|
54
52
|
</div>
|
|
55
53
|
</div>
|
|
@@ -93,7 +91,7 @@ mags whoami</code></pre>
|
|
|
93
91
|
<h2>Manage active tokens.</h2>
|
|
94
92
|
</div>
|
|
95
93
|
<div class="callout" data-auth-required>
|
|
96
|
-
<p>Sign in to view and create tokens.
|
|
94
|
+
<p>Sign in to view and create tokens. <a href="login.html?next=/tokens.html">Sign in with Google</a>.</p>
|
|
97
95
|
</div>
|
|
98
96
|
<div class="grid split" style="margin-bottom: 1.6rem;">
|
|
99
97
|
<article class="panel" data-reveal>
|
|
@@ -158,14 +156,14 @@ mags whoami</code></pre>
|
|
|
158
156
|
</div>
|
|
159
157
|
<div class="footer-links">
|
|
160
158
|
<a href="index.html">Home</a>
|
|
161
|
-
<a href="login.html">Login</a>
|
|
162
159
|
<a href="usage.html">Usage</a>
|
|
163
|
-
<a href="
|
|
160
|
+
<a href="api.html">API</a>
|
|
164
161
|
<a href="cookbook.html">Cookbook</a>
|
|
162
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
165
163
|
</div>
|
|
166
164
|
</div>
|
|
167
165
|
</footer>
|
|
168
166
|
|
|
169
|
-
<script src="script.js"></script>
|
|
167
|
+
<script src="script.js?v=7"></script>
|
|
170
168
|
</body>
|
|
171
169
|
</html>
|
package/website/usage.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=6" />
|
|
14
14
|
<script src="env.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -24,16 +24,14 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
<nav class="nav-links">
|
|
26
26
|
<a href="index.html">Home</a>
|
|
27
|
-
<a href="index.html#quickstart">Quickstart</a>
|
|
28
|
-
<a href="index.html#cli">CLI</a>
|
|
29
|
-
<a href="index.html#api">API</a>
|
|
30
|
-
<a href="login.html">Login</a>
|
|
31
27
|
<a href="tokens.html">Tokens</a>
|
|
32
|
-
<a href="
|
|
28
|
+
<a href="api.html">API</a>
|
|
33
29
|
<a href="cookbook.html">Cookbook</a>
|
|
30
|
+
<a href="claude-skill.html">Claude Skill</a>
|
|
31
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
34
32
|
</nav>
|
|
35
33
|
<div class="nav-cta">
|
|
36
|
-
<a class="button ghost" href="
|
|
34
|
+
<a class="button ghost" href="tokens.html">Tokens</a>
|
|
37
35
|
</div>
|
|
38
36
|
</div>
|
|
39
37
|
</header>
|
|
@@ -74,7 +72,7 @@ mags logs <job-id></code></pre>
|
|
|
74
72
|
<h2>Your most recent activity.</h2>
|
|
75
73
|
</div>
|
|
76
74
|
<div class="callout" data-auth-required>
|
|
77
|
-
<p>Sign in to see usage data.
|
|
75
|
+
<p>Sign in to see usage data. <a href="login.html?next=/usage.html">Sign in with Google</a>.</p>
|
|
78
76
|
</div>
|
|
79
77
|
<div class="stats-grid" data-reveal>
|
|
80
78
|
<div class="stat-card">
|
|
@@ -174,14 +172,14 @@ mags cron add --name "backup" \
|
|
|
174
172
|
</div>
|
|
175
173
|
<div class="footer-links">
|
|
176
174
|
<a href="index.html">Home</a>
|
|
177
|
-
<a href="login.html">Login</a>
|
|
178
175
|
<a href="tokens.html">Tokens</a>
|
|
179
|
-
<a href="
|
|
176
|
+
<a href="api.html">API</a>
|
|
180
177
|
<a href="cookbook.html">Cookbook</a>
|
|
178
|
+
<a href="https://discord.gg/3avpC2nS" target="_blank" rel="noreferrer">Discord</a>
|
|
181
179
|
</div>
|
|
182
180
|
</div>
|
|
183
181
|
</footer>
|
|
184
182
|
|
|
185
|
-
<script src="script.js"></script>
|
|
183
|
+
<script src="script.js?v=7"></script>
|
|
186
184
|
</body>
|
|
187
185
|
</html>
|