svamp-cli 0.2.51 → 0.2.53
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/bin/skills/html-output/SKILL.md +253 -109
- package/dist/{agentCommands-CAFgfQ4n.mjs → agentCommands-CmaybHHk.mjs} +2 -2
- package/dist/cli.mjs +33 -33
- package/dist/{commands-oleDMsAo.mjs → commands-BXNmL7Vw.mjs} +2 -2
- package/dist/{commands-Cvnsb6dT.mjs → commands-CY_yS6CZ.mjs} +1 -1
- package/dist/{commands-DacnFYI4.mjs → commands-ErfU1raO.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-DRkMQ0BC.mjs → package-CdGJdYjj.mjs} +1 -1
- package/dist/{run-vAhBIn3O.mjs → run-CPIINXWC.mjs} +2 -2
- package/dist/{run-w2M-elyq.mjs → run-D2qdA8Hw.mjs} +1 -1
- package/dist/{serveCommands-C8CXxLXP.mjs → serveCommands-ThiCkJKF.mjs} +5 -5
- package/dist/{serveManager-WHmYr8v-.mjs → serveManager-Blsg_Pa9.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: html-output
|
|
3
|
-
version: 0.
|
|
4
|
-
description: Render rich inline visual output in chat
|
|
3
|
+
version: 0.4.0
|
|
4
|
+
description: Render rich inline visual output in chat. Write an HTML file (or point at a URL) and emit a single self-closing `<html-output src="..." />` tag — the Svamp client renders it as a sandboxed iframe.
|
|
5
5
|
tags:
|
|
6
6
|
- svamp
|
|
7
7
|
- html
|
|
@@ -13,93 +13,209 @@ license: MIT
|
|
|
13
13
|
|
|
14
14
|
# HTML Output
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Render rich visual output by writing an HTML file (or pointing at a running server's URL) and emitting a single self-closing tag:
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
```
|
|
19
|
+
<html-output src="./outputs/dashboard.html" title="Dashboard" />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The client reads the file, inlines relative refs (`./image.png` etc.) as data URIs, and renders it in a sandboxed iframe — like a Jupyter cell output. **Default to plain markdown for ordinary answers.**
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
- Comparing options visually (cards, side-by-side panels)
|
|
22
|
-
- Showing structured data with rich formatting (timelines, dashboards, leaderboards)
|
|
23
|
-
- Demonstrating an idea with animation or interaction (prototypes, motion sketches)
|
|
24
|
-
- Explaining a concept where a diagram beats a paragraph
|
|
25
|
-
- Producing a polished one-shot artifact (a poster, a card, a decision tree)
|
|
24
|
+
## Strategy: which mechanism?
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
- A markdown table or list is enough
|
|
29
|
-
- The user just wants prose, code, or a quick answer
|
|
30
|
-
- The content is plain text — wrapping it in HTML adds no value
|
|
26
|
+
Three options, pick the lightest that works:
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
| Mechanism | When |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Inline `<html-output src="./viz.html" />`** | Self-contained visualization. Cards, charts, animations, prototypes. Files live on disk; the chat marker is tiny. |
|
|
31
|
+
| **URL `<html-output src="https://my-app.example.com/" height="540" />`** | Embed a live server you're running, or any external site. Like a Canvas panel inline. |
|
|
32
|
+
| **Standalone server (`svamp serve` / `svamp service expose`)** | Real apps with backend, database, multi-user state. Post the URL as a normal markdown link. |
|
|
33
33
|
|
|
34
|
+
**Decision tree:**
|
|
35
|
+
1. Need a backend / database / persistent state? → standalone server.
|
|
36
|
+
2. Iterating on a single artifact (HTML file, dashboard, dataviz)? → write to file, emit `<html-output src="..." />`.
|
|
37
|
+
3. Want to embed a running server inline (preview a dev server, show a deployed app)? → URL src.
|
|
38
|
+
|
|
39
|
+
## Attributes
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<html-output
|
|
43
|
+
src="./outputs/viz.html" <!-- REQUIRED: relative file path or absolute URL -->
|
|
44
|
+
title="Dashboard" <!-- header label; defaults to filename -->
|
|
45
|
+
height="540" <!-- fixed pixel height (10..4000); default = auto-size for files -->
|
|
46
|
+
bare <!-- no border/header (controls float on hover) -->
|
|
47
|
+
wide <!-- extend beyond bubble width -->
|
|
48
|
+
mode="card" <!-- show as a preview card; click → open in new tab -->
|
|
49
|
+
description="..." <!-- card summary -->
|
|
50
|
+
poster="./outputs/thumb.png" <!-- card thumbnail -->
|
|
51
|
+
/>
|
|
34
52
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
|
|
54
|
+
All except `src` are optional.
|
|
55
|
+
|
|
56
|
+
**When to set each:**
|
|
57
|
+
- `title` — short name for the artifact ("3D scene", "Sales dashboard"). Helps when iterating (the deprecation pill names it).
|
|
58
|
+
- `height` — fixed when the content has no natural document height (Three.js scenes, video, fullscreen apps, URL embeds). For URL src, defaults to 540px since auto-resize doesn't work cross-origin.
|
|
59
|
+
- `bare` — for posters, hero animations, or fullscreen apps where the chrome is distracting.
|
|
60
|
+
- `wide` — for content that needs more horizontal space than a chat bubble.
|
|
61
|
+
- `mode="card"` — for fullscreen-only outputs that look bad squeezed into chat. Renders as a click-to-open card.
|
|
62
|
+
- `description`, `poster` — only used in card mode.
|
|
63
|
+
|
|
64
|
+
## Writing the file
|
|
65
|
+
|
|
66
|
+
Use the standard Write tool. Two storage conventions:
|
|
67
|
+
|
|
68
|
+
**Disposable (chat-related, ephemeral):** save under `.svamp/<sessionId>/outputs/`. Not tracked by git. Cleaned up with the session.
|
|
69
|
+
```
|
|
70
|
+
.svamp/<sessionId>/outputs/dashboard.html
|
|
71
|
+
.svamp/<sessionId>/outputs/3d-scene.html
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Persistent (project artifact):** save under `./outputs/`, `./reports/`, or wherever the project keeps these. Tracked in git if relevant.
|
|
75
|
+
```
|
|
76
|
+
./outputs/sales-report.html
|
|
77
|
+
./reports/quarterly.html
|
|
47
78
|
```
|
|
48
79
|
|
|
49
|
-
|
|
80
|
+
Pick disposable when the output is throwaway (one-off charts, scratch demos). Pick persistent when the artifact has lasting value (final reports, deliverables).
|
|
81
|
+
|
|
82
|
+
## Iteration
|
|
83
|
+
|
|
84
|
+
When you iterate on the same artifact, **edit the file in place** and emit a new `<html-output src="..." />` tag. The client auto-detects that two blocks share a `src` and:
|
|
85
|
+
- Renders only the **latest** block inline.
|
|
86
|
+
- Collapses older ones into a "Newer version below ↓" pill that scrolls to the latest on click.
|
|
87
|
+
|
|
88
|
+
This means you can iterate freely without polluting the chat with stale renders.
|
|
89
|
+
|
|
90
|
+
## Sharing
|
|
91
|
+
|
|
92
|
+
To share with a colleague, run `svamp serve`:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Owner-only (default — they need to log in via Hypha)
|
|
96
|
+
svamp serve dashboard ./outputs
|
|
97
|
+
|
|
98
|
+
# Public link (anyone with the URL)
|
|
99
|
+
svamp serve dashboard ./outputs --public
|
|
100
|
+
|
|
101
|
+
# Restrict to specific emails
|
|
102
|
+
svamp serve dashboard ./outputs --access alice@x.com,bob@y.com
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Post the resulting URL as a normal markdown link. The svamp client doesn't manage shares — `svamp serve` is the dedicated tool.
|
|
50
106
|
|
|
51
107
|
## Rendering environment
|
|
52
108
|
|
|
53
|
-
- **Sandbox**: `allow-scripts allow-popups`. Scripts run
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
109
|
+
- **Sandbox**: `allow-scripts allow-popups`. Scripts run; the iframe is null-origin and cannot read parent cookies/storage/DOM.
|
|
110
|
+
- **Fullscreen**: enabled — both the parent's fullscreen button and `document.documentElement.requestFullscreen()` work.
|
|
111
|
+
- **Network**: `connect-src https: wss:` — `fetch()` to HTTPS URLs and WebSocket to wss:// work. HTTP is blocked.
|
|
112
|
+
- **Nested iframes**: forbidden (`frame-src 'none'`) — anti-phishing.
|
|
113
|
+
- **Relative refs**: in file mode, `src=`/`href=` paths inside the HTML resolve against the HTML file's directory and are inlined as `data:` URIs (5 MB cap total). In URL mode, the iframe loads the URL directly and resolves refs natively.
|
|
114
|
+
- **Auto-resize**: file mode injects a ResizeObserver that posts height to the parent. URL mode can't auto-resize (cross-origin) — set `height="..."`.
|
|
115
|
+
- **Removed files**: if a file is deleted, the renderer shows a "Removed: ./path" pill.
|
|
116
|
+
|
|
117
|
+
## Theming
|
|
118
|
+
|
|
119
|
+
The Svamp client injects the user's current theme into file-mode iframes as CSS custom properties on `<html>`, plus a `data-theme="dark|light"` attribute. Reference these to adapt automatically:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
:root {
|
|
123
|
+
--svamp-bg /* base background */
|
|
124
|
+
--svamp-surface /* card / elevated */
|
|
125
|
+
--svamp-surface-high /* highest elevation */
|
|
126
|
+
--svamp-text /* primary text */
|
|
127
|
+
--svamp-text-secondary
|
|
128
|
+
--svamp-accent /* link / accent */
|
|
129
|
+
--svamp-divider /* borders */
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
body { background: var(--svamp-bg); color: var(--svamp-text); }
|
|
133
|
+
[data-theme="dark"] .hero { background: linear-gradient(...dark...); }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Theme updates push live via `postMessage` — no reload. URL mode does not get theme injection (we don't own the document).
|
|
137
|
+
|
|
138
|
+
For a branded visualization with its own deliberate palette, hardcode your colors and ignore the variables.
|
|
139
|
+
|
|
140
|
+
## Talking to agent-spawned servers
|
|
141
|
+
|
|
142
|
+
If your iframe `fetch()`es from a server you've run via `svamp service expose`, the server **must** include CORS headers (the iframe origin is `null`):
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
Access-Control-Allow-Origin: *
|
|
146
|
+
Access-Control-Allow-Methods: *
|
|
147
|
+
Access-Control-Allow-Headers: *
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### FastAPI
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from fastapi import FastAPI
|
|
154
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
155
|
+
app = FastAPI()
|
|
156
|
+
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Express
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
import express from 'express';
|
|
163
|
+
import cors from 'cors';
|
|
164
|
+
const app = express();
|
|
165
|
+
app.use(cors());
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Plain Node http
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
172
|
+
res.setHeader('Access-Control-Allow-Methods', '*');
|
|
173
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
174
|
+
if (req.method === 'OPTIONS') return res.end();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
If the iframe console shows `CORS error`, your server is missing one of these headers.
|
|
59
178
|
|
|
60
|
-
## Style philosophy
|
|
179
|
+
## Style philosophy
|
|
61
180
|
|
|
62
|
-
|
|
181
|
+
Lean on what HTML does well — flow layout, typography, animation — without overengineering.
|
|
63
182
|
|
|
64
|
-
- **Restrained palette.**
|
|
65
|
-
- **Type first.**
|
|
66
|
-
- **Spacing rhythm.**
|
|
67
|
-
- **Borders or shadows, not both.**
|
|
68
|
-
- **Animation conveys, not decorates.**
|
|
69
|
-
- **Mobile-first.**
|
|
183
|
+
- **Restrained palette.** 1–2 accent colors, lots of neutrals. Slate / zinc / stone ramps work well.
|
|
184
|
+
- **Type first.** 14–16px base, 1.5 line-height, system-ui font.
|
|
185
|
+
- **Spacing rhythm.** 4 / 8 / 12 / 16 / 24 / 32 / 48 (Tailwind's scale).
|
|
186
|
+
- **Borders or shadows, not both.**
|
|
187
|
+
- **Animation conveys, not decorates.** 200–400 ms ease-out on entry; longer is noise.
|
|
188
|
+
- **Mobile-first.** Single column by default; widen at `md:` only when columns add information.
|
|
70
189
|
|
|
71
190
|
## Templates
|
|
72
191
|
|
|
73
|
-
### 1. Comparison card (Tailwind)
|
|
192
|
+
### 1. Comparison card (Tailwind via CDN)
|
|
74
193
|
|
|
75
194
|
```html
|
|
76
195
|
<!doctype html>
|
|
77
196
|
<html><head>
|
|
78
197
|
<meta charset="utf-8">
|
|
79
198
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
80
|
-
</head><body class="p-6
|
|
199
|
+
</head><body class="p-6">
|
|
81
200
|
<div class="grid md:grid-cols-2 gap-4 max-w-3xl mx-auto">
|
|
82
|
-
<div class="
|
|
83
|
-
<h2 class="font-semibold
|
|
84
|
-
<p class="text-sm
|
|
85
|
-
<ul class="mt-3 text-sm text-slate-700 space-y-1">
|
|
86
|
-
<li>• Lower setup cost</li>
|
|
87
|
-
<li>• Easy to reverse</li>
|
|
88
|
-
</ul>
|
|
201
|
+
<div class="rounded-2xl p-5" style="background: var(--svamp-surface); border: 1px solid var(--svamp-divider); color: var(--svamp-text);">
|
|
202
|
+
<h2 class="font-semibold">Option A</h2>
|
|
203
|
+
<p class="text-sm mt-1" style="color: var(--svamp-text-secondary);">Short, fast, fewer features.</p>
|
|
89
204
|
</div>
|
|
90
|
-
<div class="
|
|
91
|
-
<span class="absolute -top-2 right-4
|
|
92
|
-
<h2 class="font-semibold
|
|
93
|
-
<p class="text-sm
|
|
94
|
-
<ul class="mt-3 text-sm text-slate-700 space-y-1">
|
|
95
|
-
<li>• Sustainable</li>
|
|
96
|
-
<li>• Composable</li>
|
|
97
|
-
</ul>
|
|
205
|
+
<div class="rounded-2xl p-5 relative" style="background: var(--svamp-surface); border: 2px solid var(--svamp-accent); color: var(--svamp-text);">
|
|
206
|
+
<span class="absolute -top-2 right-4 text-white text-xs px-2 py-0.5 rounded" style="background: var(--svamp-accent);">Recommended</span>
|
|
207
|
+
<h2 class="font-semibold">Option B</h2>
|
|
208
|
+
<p class="text-sm mt-1" style="color: var(--svamp-text-secondary);">More work upfront; better long-term.</p>
|
|
98
209
|
</div>
|
|
99
210
|
</div>
|
|
100
211
|
</body></html>
|
|
101
212
|
```
|
|
102
213
|
|
|
214
|
+
Then write it to `./outputs/comparison.html` and emit:
|
|
215
|
+
```
|
|
216
|
+
<html-output src="./outputs/comparison.html" title="Comparison" />
|
|
217
|
+
```
|
|
218
|
+
|
|
103
219
|
### 2. Animated entry (no JS framework)
|
|
104
220
|
|
|
105
221
|
```html
|
|
@@ -107,80 +223,108 @@ The goal is to lean on what HTML already does well — flow layout, typography,
|
|
|
107
223
|
<html><head>
|
|
108
224
|
<meta charset="utf-8">
|
|
109
225
|
<style>
|
|
110
|
-
body { margin: 0; padding: 32px; font-family: system-ui; background:
|
|
111
|
-
.card { opacity: 0; transform: translateY(8px); animation: fade .4s ease-out forwards; }
|
|
226
|
+
body { margin: 0; padding: 32px; font-family: system-ui; background: var(--svamp-bg); color: var(--svamp-text); }
|
|
227
|
+
.card { opacity: 0; transform: translateY(8px); animation: fade .4s ease-out forwards; background: var(--svamp-surface); border-radius: 12px; padding: 16px 20px; margin-bottom: 12px; }
|
|
112
228
|
.card:nth-child(2) { animation-delay: .1s; }
|
|
113
229
|
.card:nth-child(3) { animation-delay: .2s; }
|
|
114
230
|
@keyframes fade { to { opacity: 1; transform: none; } }
|
|
115
|
-
.card {
|
|
116
|
-
.card h3 { margin: 0
|
|
117
|
-
.card p { margin: 0; color: #94a3b8; font-size: 13px; }
|
|
231
|
+
.card p { margin: 4px 0 0; color: var(--svamp-text-secondary); font-size: 13px; }
|
|
232
|
+
.card h3 { margin: 0; font-size: 15px; }
|
|
118
233
|
</style>
|
|
119
234
|
</head><body>
|
|
120
|
-
<div class="card"><h3>Step 1</h3><p>
|
|
121
|
-
<div class="card"><h3>Step 2</h3><p>
|
|
122
|
-
<div class="card"><h3>Step 3</h3><p>
|
|
235
|
+
<div class="card"><h3>Step 1</h3><p>Gather inputs.</p></div>
|
|
236
|
+
<div class="card"><h3>Step 2</h3><p>Transform them.</p></div>
|
|
237
|
+
<div class="card"><h3>Step 3</h3><p>Persist the result.</p></div>
|
|
123
238
|
</body></html>
|
|
124
239
|
```
|
|
125
240
|
|
|
126
|
-
### 3.
|
|
241
|
+
### 3. Full-bleed 3D scene (fixed height, immersive)
|
|
127
242
|
|
|
128
243
|
```html
|
|
129
244
|
<!doctype html>
|
|
130
|
-
<html><head>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
</head><body
|
|
134
|
-
<canvas id="c" height="240"></canvas>
|
|
245
|
+
<html><head><meta charset="utf-8">
|
|
246
|
+
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
|
|
247
|
+
<style>html,body{margin:0;height:100%;overflow:hidden;background:#0f172a;}canvas{display:block;}</style>
|
|
248
|
+
</head><body>
|
|
135
249
|
<script>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
});
|
|
250
|
+
const w = innerWidth, h = innerHeight;
|
|
251
|
+
const scene = new THREE.Scene();
|
|
252
|
+
const camera = new THREE.PerspectiveCamera(45, w/h, 0.1, 100);
|
|
253
|
+
camera.position.z = 4;
|
|
254
|
+
const renderer = new THREE.WebGLRenderer({antialias:true});
|
|
255
|
+
renderer.setSize(w, h);
|
|
256
|
+
document.body.appendChild(renderer.domElement);
|
|
257
|
+
scene.add(new THREE.AmbientLight(0xffffff, .6));
|
|
258
|
+
const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(2,2,2); scene.add(light);
|
|
259
|
+
const cube = new THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.MeshStandardMaterial({color: 0x5eead4}));
|
|
260
|
+
scene.add(cube);
|
|
261
|
+
function loop(t){ cube.rotation.y = t*0.0006; cube.rotation.x = t*0.0004; renderer.render(scene, camera); requestAnimationFrame(loop); }
|
|
262
|
+
requestAnimationFrame(loop);
|
|
141
263
|
</script>
|
|
142
264
|
</body></html>
|
|
143
265
|
```
|
|
144
266
|
|
|
145
|
-
|
|
267
|
+
Emit:
|
|
268
|
+
```
|
|
269
|
+
<html-output src="./outputs/3d.html" title="3D viewer" height="540" mode="card" poster="./outputs/3d-thumb.png" description="Click to launch the rotating cube demo" />
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 4. Live data from agent-spawned API
|
|
146
273
|
|
|
147
274
|
```html
|
|
148
275
|
<!doctype html>
|
|
149
276
|
<html><head>
|
|
150
277
|
<meta charset="utf-8">
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
</div>
|
|
278
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
279
|
+
</head><body style="font-family:system-ui;padding:16px;background:var(--svamp-bg);color:var(--svamp-text)">
|
|
280
|
+
<h2 style="margin:0 0 12px">Live sales</h2>
|
|
281
|
+
<canvas id="c" height="240"></canvas>
|
|
282
|
+
<script>
|
|
283
|
+
fetch('https://sales-api-xxx.svamp.dev/last7days')
|
|
284
|
+
.then(r => r.json())
|
|
285
|
+
.then(rows => new Chart(document.getElementById('c'), {
|
|
286
|
+
type: 'line',
|
|
287
|
+
data: { labels: rows.map(r => r.day), datasets: [{label: 'Revenue', data: rows.map(r => r.usd), borderColor: '#0ea5e9'}] },
|
|
288
|
+
}));
|
|
289
|
+
</script>
|
|
164
290
|
</body></html>
|
|
165
291
|
```
|
|
166
292
|
|
|
167
|
-
|
|
293
|
+
Run server first:
|
|
294
|
+
```bash
|
|
295
|
+
# Server must include CORS headers — see "Talking to agent-spawned servers" above
|
|
296
|
+
svamp service expose sales-api --port 8000
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Then emit:
|
|
300
|
+
```
|
|
301
|
+
<html-output src="./outputs/sales-dashboard.html" title="Sales (live)" />
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 5. URL embed (CanvasPanel-style)
|
|
305
|
+
|
|
306
|
+
For a running dev server, deployed app, or external site:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
<html-output src="https://my-app.example.com/" title="My app" height="640" />
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
No file needed. Auto-resize doesn't work for URL embeds — set `height` explicitly.
|
|
168
313
|
|
|
169
314
|
## Anti-patterns
|
|
170
315
|
|
|
171
|
-
- **Don't
|
|
172
|
-
- **Don't
|
|
173
|
-
- **Don't
|
|
174
|
-
- **Don't open multiple `<html-output>`
|
|
175
|
-
- **Don't
|
|
176
|
-
- **Don't
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
5. Is the visual restrained — purposeful color, consistent spacing, one font?
|
|
316
|
+
- **Don't put HTML inline** — `<html-output>...HTML body...</html-output>` is not supported. Always use `src="./file.html"`.
|
|
317
|
+
- **Don't fetch from non-CORS endpoints.** If you don't control the server, the iframe can't reach it. Pre-compute and embed.
|
|
318
|
+
- **Don't omit `<!doctype html>`** in your HTML file. Quirks mode breaks CSS.
|
|
319
|
+
- **Don't open multiple `<html-output>`s back-to-back** for content that could be one. One artifact = one file = one tag.
|
|
320
|
+
- **Don't write huge files** (>5 MB total inlined assets). The block won't render past the cap.
|
|
321
|
+
- **Don't put visualization assets in git** when they're disposable — use `.svamp/<sessionId>/outputs/`.
|
|
322
|
+
|
|
323
|
+
## Checklist
|
|
324
|
+
|
|
325
|
+
Before emitting `<html-output src="..." />`:
|
|
326
|
+
1. Did you actually write the file? (Use the Write tool.)
|
|
327
|
+
2. Is the path relative to the session cwd, OR an absolute `https://` URL?
|
|
328
|
+
3. For fullscreen-only content, set `mode="card"` with a `poster`.
|
|
329
|
+
4. For URL embeds or canvas-heavy content, set `height="..."`.
|
|
330
|
+
5. If iterating, reuse the same `src` so older blocks collapse.
|
|
@@ -148,7 +148,7 @@ async function sessionBroadcast(action, args) {
|
|
|
148
148
|
console.log(`Broadcast sent: ${action}`);
|
|
149
149
|
}
|
|
150
150
|
async function connectToMachineService() {
|
|
151
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
151
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
152
152
|
return connectAndGetMachine();
|
|
153
153
|
}
|
|
154
154
|
async function inboxSend(targetSessionId, opts) {
|
|
@@ -165,7 +165,7 @@ async function inboxSend(targetSessionId, opts) {
|
|
|
165
165
|
}
|
|
166
166
|
const { server, machine } = await connectToMachineService();
|
|
167
167
|
try {
|
|
168
|
-
const { resolveSessionId } = await import('./commands-
|
|
168
|
+
const { resolveSessionId } = await import('./commands-CY_yS6CZ.mjs');
|
|
169
169
|
const sessions = await machine.listSessions();
|
|
170
170
|
const match = resolveSessionId(sessions, targetSessionId);
|
|
171
171
|
const fullTargetId = match.sessionId;
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-
|
|
1
|
+
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-CPIINXWC.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -43,7 +43,7 @@ async function main() {
|
|
|
43
43
|
console.error(`svamp daemon restart: ${err.message || err}`);
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
|
-
const { restartDaemon } = await import('./run-
|
|
46
|
+
const { restartDaemon } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.u; });
|
|
47
47
|
await restartDaemon();
|
|
48
48
|
process.exit(0);
|
|
49
49
|
}
|
|
@@ -279,7 +279,7 @@ async function main() {
|
|
|
279
279
|
console.error("svamp service: Service commands are not available in sandboxed sessions.");
|
|
280
280
|
process.exit(1);
|
|
281
281
|
}
|
|
282
|
-
const { handleServiceCommand } = await import('./commands-
|
|
282
|
+
const { handleServiceCommand } = await import('./commands-ErfU1raO.mjs');
|
|
283
283
|
await handleServiceCommand();
|
|
284
284
|
} else if (subcommand === "serve") {
|
|
285
285
|
const { isSandboxed: isSandboxedServe } = await import('./sandboxDetect-DNTcbgWD.mjs');
|
|
@@ -287,7 +287,7 @@ async function main() {
|
|
|
287
287
|
console.error("svamp serve: Serve commands are not available in sandboxed sessions.");
|
|
288
288
|
process.exit(1);
|
|
289
289
|
}
|
|
290
|
-
const { handleServeCommand } = await import('./serveCommands-
|
|
290
|
+
const { handleServeCommand } = await import('./serveCommands-ThiCkJKF.mjs');
|
|
291
291
|
await handleServeCommand();
|
|
292
292
|
process.exit(0);
|
|
293
293
|
} else if (subcommand === "process" || subcommand === "proc") {
|
|
@@ -296,7 +296,7 @@ async function main() {
|
|
|
296
296
|
console.error("svamp process: Process commands are not available in sandboxed sessions.");
|
|
297
297
|
process.exit(1);
|
|
298
298
|
}
|
|
299
|
-
const { processCommand } = await import('./commands-
|
|
299
|
+
const { processCommand } = await import('./commands-BXNmL7Vw.mjs');
|
|
300
300
|
let machineId;
|
|
301
301
|
const processArgs = args.slice(1);
|
|
302
302
|
const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
|
|
@@ -314,7 +314,7 @@ async function main() {
|
|
|
314
314
|
} else if (!subcommand || subcommand === "start") {
|
|
315
315
|
await handleInteractiveCommand();
|
|
316
316
|
} else if (subcommand === "--version" || subcommand === "-v") {
|
|
317
|
-
const pkg = await import('./package-
|
|
317
|
+
const pkg = await import('./package-CdGJdYjj.mjs').catch(() => ({ default: { version: "unknown" } }));
|
|
318
318
|
console.log(`svamp version: ${pkg.default.version}`);
|
|
319
319
|
} else {
|
|
320
320
|
console.error(`Unknown command: ${subcommand}`);
|
|
@@ -323,7 +323,7 @@ async function main() {
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
async function handleInteractiveCommand() {
|
|
326
|
-
const { runInteractive } = await import('./run-
|
|
326
|
+
const { runInteractive } = await import('./run-D2qdA8Hw.mjs');
|
|
327
327
|
const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
|
|
328
328
|
let directory = process.cwd();
|
|
329
329
|
let resumeSessionId;
|
|
@@ -368,7 +368,7 @@ async function handleAgentCommand() {
|
|
|
368
368
|
return;
|
|
369
369
|
}
|
|
370
370
|
if (agentArgs[0] === "list") {
|
|
371
|
-
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-
|
|
371
|
+
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.p; });
|
|
372
372
|
console.log("Known agents:");
|
|
373
373
|
for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
|
|
374
374
|
console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
|
|
@@ -380,7 +380,7 @@ async function handleAgentCommand() {
|
|
|
380
380
|
console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
|
|
381
381
|
return;
|
|
382
382
|
}
|
|
383
|
-
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-
|
|
383
|
+
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.p; });
|
|
384
384
|
let cwd = process.cwd();
|
|
385
385
|
const filteredArgs = [];
|
|
386
386
|
for (let i = 0; i < agentArgs.length; i++) {
|
|
@@ -404,12 +404,12 @@ async function handleAgentCommand() {
|
|
|
404
404
|
console.log(`Starting ${config.agentName} agent in ${cwd}...`);
|
|
405
405
|
let backend;
|
|
406
406
|
if (KNOWN_MCP_AGENTS[config.agentName]) {
|
|
407
|
-
const { CodexMcpBackend } = await import('./run-
|
|
407
|
+
const { CodexMcpBackend } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.q; });
|
|
408
408
|
backend = new CodexMcpBackend({ cwd, log: logFn });
|
|
409
409
|
} else {
|
|
410
|
-
const { AcpBackend } = await import('./run-
|
|
411
|
-
const { GeminiTransport } = await import('./run-
|
|
412
|
-
const { DefaultTransport } = await import('./run-
|
|
410
|
+
const { AcpBackend } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.o; });
|
|
411
|
+
const { GeminiTransport } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.G; });
|
|
412
|
+
const { DefaultTransport } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.D; });
|
|
413
413
|
const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
|
|
414
414
|
backend = new AcpBackend({
|
|
415
415
|
agentName: config.agentName,
|
|
@@ -536,7 +536,7 @@ async function handleSessionCommand() {
|
|
|
536
536
|
process.exit(1);
|
|
537
537
|
}
|
|
538
538
|
}
|
|
539
|
-
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-
|
|
539
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-CY_yS6CZ.mjs');
|
|
540
540
|
const parseFlagStr = (flag, shortFlag) => {
|
|
541
541
|
for (let i = 1; i < sessionArgs.length; i++) {
|
|
542
542
|
if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
|
|
@@ -596,7 +596,7 @@ async function handleSessionCommand() {
|
|
|
596
596
|
allowDomain.push(sessionArgs[++i]);
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
|
-
const { parseShareArg } = await import('./commands-
|
|
599
|
+
const { parseShareArg } = await import('./commands-CY_yS6CZ.mjs');
|
|
600
600
|
const shareEntries = share.map((s) => parseShareArg(s));
|
|
601
601
|
await sessionSpawn(agent, dir, targetMachineId, {
|
|
602
602
|
message,
|
|
@@ -682,7 +682,7 @@ async function handleSessionCommand() {
|
|
|
682
682
|
console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
|
|
683
683
|
process.exit(1);
|
|
684
684
|
}
|
|
685
|
-
const { sessionApprove } = await import('./commands-
|
|
685
|
+
const { sessionApprove } = await import('./commands-CY_yS6CZ.mjs');
|
|
686
686
|
const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
687
687
|
await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
|
|
688
688
|
json: hasFlag("--json")
|
|
@@ -692,7 +692,7 @@ async function handleSessionCommand() {
|
|
|
692
692
|
console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
|
|
693
693
|
process.exit(1);
|
|
694
694
|
}
|
|
695
|
-
const { sessionDeny } = await import('./commands-
|
|
695
|
+
const { sessionDeny } = await import('./commands-CY_yS6CZ.mjs');
|
|
696
696
|
const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
697
697
|
await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
|
|
698
698
|
json: hasFlag("--json")
|
|
@@ -728,7 +728,7 @@ async function handleSessionCommand() {
|
|
|
728
728
|
console.error("Usage: svamp session set-title <title>");
|
|
729
729
|
process.exit(1);
|
|
730
730
|
}
|
|
731
|
-
const { sessionSetTitle } = await import('./agentCommands-
|
|
731
|
+
const { sessionSetTitle } = await import('./agentCommands-CmaybHHk.mjs');
|
|
732
732
|
await sessionSetTitle(title);
|
|
733
733
|
} else if (sessionSubcommand === "set-link") {
|
|
734
734
|
const url = sessionArgs[1];
|
|
@@ -737,7 +737,7 @@ async function handleSessionCommand() {
|
|
|
737
737
|
process.exit(1);
|
|
738
738
|
}
|
|
739
739
|
const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
|
|
740
|
-
const { sessionSetLink } = await import('./agentCommands-
|
|
740
|
+
const { sessionSetLink } = await import('./agentCommands-CmaybHHk.mjs');
|
|
741
741
|
await sessionSetLink(url, label);
|
|
742
742
|
} else if (sessionSubcommand === "notify") {
|
|
743
743
|
const message = sessionArgs[1];
|
|
@@ -746,7 +746,7 @@ async function handleSessionCommand() {
|
|
|
746
746
|
process.exit(1);
|
|
747
747
|
}
|
|
748
748
|
const level = parseFlagStr("--level") || "info";
|
|
749
|
-
const { sessionNotify } = await import('./agentCommands-
|
|
749
|
+
const { sessionNotify } = await import('./agentCommands-CmaybHHk.mjs');
|
|
750
750
|
await sessionNotify(message, level);
|
|
751
751
|
} else if (sessionSubcommand === "broadcast") {
|
|
752
752
|
const action = sessionArgs[1];
|
|
@@ -754,7 +754,7 @@ async function handleSessionCommand() {
|
|
|
754
754
|
console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
|
|
755
755
|
process.exit(1);
|
|
756
756
|
}
|
|
757
|
-
const { sessionBroadcast } = await import('./agentCommands-
|
|
757
|
+
const { sessionBroadcast } = await import('./agentCommands-CmaybHHk.mjs');
|
|
758
758
|
await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
|
|
759
759
|
} else if (sessionSubcommand === "inbox") {
|
|
760
760
|
const inboxSubcmd = sessionArgs[1];
|
|
@@ -765,7 +765,7 @@ async function handleSessionCommand() {
|
|
|
765
765
|
process.exit(1);
|
|
766
766
|
}
|
|
767
767
|
if (agentSessionId) {
|
|
768
|
-
const { inboxSend } = await import('./agentCommands-
|
|
768
|
+
const { inboxSend } = await import('./agentCommands-CmaybHHk.mjs');
|
|
769
769
|
await inboxSend(sessionArgs[2], {
|
|
770
770
|
body: sessionArgs[3],
|
|
771
771
|
subject: parseFlagStr("--subject"),
|
|
@@ -780,7 +780,7 @@ async function handleSessionCommand() {
|
|
|
780
780
|
}
|
|
781
781
|
} else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
|
|
782
782
|
if (agentSessionId && !sessionArgs[2]) {
|
|
783
|
-
const { inboxList } = await import('./agentCommands-
|
|
783
|
+
const { inboxList } = await import('./agentCommands-CmaybHHk.mjs');
|
|
784
784
|
await inboxList({
|
|
785
785
|
unread: hasFlag("--unread"),
|
|
786
786
|
limit: parseFlagInt("--limit"),
|
|
@@ -802,7 +802,7 @@ async function handleSessionCommand() {
|
|
|
802
802
|
process.exit(1);
|
|
803
803
|
}
|
|
804
804
|
if (agentSessionId && !sessionArgs[3]) {
|
|
805
|
-
const { inboxList } = await import('./agentCommands-
|
|
805
|
+
const { inboxList } = await import('./agentCommands-CmaybHHk.mjs');
|
|
806
806
|
await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
|
|
807
807
|
} else if (sessionArgs[3]) {
|
|
808
808
|
await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
|
|
@@ -812,7 +812,7 @@ async function handleSessionCommand() {
|
|
|
812
812
|
}
|
|
813
813
|
} else if (inboxSubcmd === "reply") {
|
|
814
814
|
if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
|
|
815
|
-
const { inboxReply } = await import('./agentCommands-
|
|
815
|
+
const { inboxReply } = await import('./agentCommands-CmaybHHk.mjs');
|
|
816
816
|
await inboxReply(sessionArgs[2], sessionArgs[3]);
|
|
817
817
|
} else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
|
|
818
818
|
await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
|
|
@@ -848,7 +848,7 @@ async function handleMachineCommand() {
|
|
|
848
848
|
return;
|
|
849
849
|
}
|
|
850
850
|
if (machineSubcommand === "share") {
|
|
851
|
-
const { machineShare } = await import('./commands-
|
|
851
|
+
const { machineShare } = await import('./commands-CY_yS6CZ.mjs');
|
|
852
852
|
let machineId;
|
|
853
853
|
const shareArgs = [];
|
|
854
854
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
@@ -878,7 +878,7 @@ async function handleMachineCommand() {
|
|
|
878
878
|
}
|
|
879
879
|
await machineShare(machineId, { add, remove, list, configPath, showConfig });
|
|
880
880
|
} else if (machineSubcommand === "exec") {
|
|
881
|
-
const { machineExec } = await import('./commands-
|
|
881
|
+
const { machineExec } = await import('./commands-CY_yS6CZ.mjs');
|
|
882
882
|
let machineId;
|
|
883
883
|
let cwd;
|
|
884
884
|
const cmdParts = [];
|
|
@@ -898,7 +898,7 @@ async function handleMachineCommand() {
|
|
|
898
898
|
}
|
|
899
899
|
await machineExec(machineId, command, cwd);
|
|
900
900
|
} else if (machineSubcommand === "info") {
|
|
901
|
-
const { machineInfo } = await import('./commands-
|
|
901
|
+
const { machineInfo } = await import('./commands-CY_yS6CZ.mjs');
|
|
902
902
|
let machineId;
|
|
903
903
|
for (let i = 1; i < machineArgs.length; i++) {
|
|
904
904
|
if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
|
|
@@ -918,10 +918,10 @@ async function handleMachineCommand() {
|
|
|
918
918
|
level = machineArgs[++i];
|
|
919
919
|
}
|
|
920
920
|
}
|
|
921
|
-
const { machineNotify } = await import('./agentCommands-
|
|
921
|
+
const { machineNotify } = await import('./agentCommands-CmaybHHk.mjs');
|
|
922
922
|
await machineNotify(message, level);
|
|
923
923
|
} else if (machineSubcommand === "ls") {
|
|
924
|
-
const { machineLs } = await import('./commands-
|
|
924
|
+
const { machineLs } = await import('./commands-CY_yS6CZ.mjs');
|
|
925
925
|
let machineId;
|
|
926
926
|
let showHidden = false;
|
|
927
927
|
let path;
|
|
@@ -1388,7 +1388,7 @@ async function applyClaudeAuthFlags(argv) {
|
|
|
1388
1388
|
"--use-hypha-proxy, --use-claude-login, and --anthropic-base-url/--anthropic-api-key are mutually exclusive"
|
|
1389
1389
|
);
|
|
1390
1390
|
}
|
|
1391
|
-
const mod = await import('./run-
|
|
1391
|
+
const mod = await import('./run-CPIINXWC.mjs').then(function (n) { return n.t; });
|
|
1392
1392
|
if (hasHypha) {
|
|
1393
1393
|
mod.setClaudeAuthHyphaProxy();
|
|
1394
1394
|
console.log("Claude auth configured: hypha-proxy (uses HYPHA_TOKEN live at each spawn).");
|
|
@@ -1426,7 +1426,7 @@ async function applyDaemonShareFlag(argv) {
|
|
|
1426
1426
|
}
|
|
1427
1427
|
}
|
|
1428
1428
|
if (collected.length === 0) return;
|
|
1429
|
-
const { updateEnvFile } = await import('./run-
|
|
1429
|
+
const { updateEnvFile } = await import('./run-CPIINXWC.mjs').then(function (n) { return n.t; });
|
|
1430
1430
|
const seen = /* @__PURE__ */ new Set();
|
|
1431
1431
|
const deduped = collected.filter((e) => {
|
|
1432
1432
|
const k = e.toLowerCase();
|
|
@@ -1439,7 +1439,7 @@ async function applyDaemonShareFlag(argv) {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
async function handleDaemonAuthCommand(argv) {
|
|
1441
1441
|
const sub = (argv[0] || "status").toLowerCase();
|
|
1442
|
-
const mod = await import('./run-
|
|
1442
|
+
const mod = await import('./run-CPIINXWC.mjs').then(function (n) { return n.t; });
|
|
1443
1443
|
if (sub === "--help" || sub === "-h" || sub === "help") {
|
|
1444
1444
|
console.log(`
|
|
1445
1445
|
svamp daemon auth \u2014 Configure how Claude subprocesses authenticate
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
import { connectAndGetMachine } from './commands-
|
|
3
|
+
import { connectAndGetMachine } from './commands-CY_yS6CZ.mjs';
|
|
4
4
|
import 'node:fs';
|
|
5
5
|
import 'node:child_process';
|
|
6
6
|
import 'node:path';
|
|
7
7
|
import 'node:os';
|
|
8
|
-
import './run-
|
|
8
|
+
import './run-CPIINXWC.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { n as normalizeAllowedUser, l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha, i as buildSessionShareUrl, j as buildMachineShareUrl } from './run-
|
|
5
|
+
import { n as normalizeAllowedUser, l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha, i as buildSessionShareUrl, j as buildMachineShareUrl } from './run-CPIINXWC.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -97,7 +97,7 @@ async function serviceServe(args) {
|
|
|
97
97
|
}
|
|
98
98
|
async function serviceList(_args) {
|
|
99
99
|
try {
|
|
100
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
100
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
101
101
|
const { server, machine } = await connectAndGetMachine();
|
|
102
102
|
try {
|
|
103
103
|
const tunnels = await machine.tunnelList({});
|
|
@@ -126,7 +126,7 @@ async function serviceDelete(args) {
|
|
|
126
126
|
process.exit(1);
|
|
127
127
|
}
|
|
128
128
|
try {
|
|
129
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
129
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
130
130
|
const { server, machine } = await connectAndGetMachine();
|
|
131
131
|
try {
|
|
132
132
|
await machine.tunnelStop({ name });
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-CPIINXWC.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -6102,7 +6102,7 @@ You are running inside a Svamp session (id: ${sessionId}) on Hypha Cloud. Use th
|
|
|
6102
6102
|
- \`svamp session set-link "<url>" "<label>"\` \u2014 surface any viewable artifact as a button
|
|
6103
6103
|
- \`svamp session notify "<msg>" [--level info|warning|error]\` \u2014 send a user notification
|
|
6104
6104
|
|
|
6105
|
-
**Rich HTML output:**
|
|
6105
|
+
**Rich HTML output:** Write HTML to a file (\`.svamp/<sessionId>/outputs/\` for disposable, \`./outputs/\` for persistent) and emit \`<html-output src="./outputs/viz.html" title="..." />\` \u2014 the Svamp client renders it inline as a sandboxed iframe with theme CSS vars, auto-resize, and file inlining. Add \`mode="card" poster="./thumb.png" description="..."\` for fullscreen-only outputs (renders as click-to-open card). Use \`<html-output src="https://..." height="540" />\` to embed a live server. Run \`svamp serve <name> <dir>\` to share. Use a real backend server (\`svamp service expose\`) when you need persistence/auth. See the \`html-output\` skill.
|
|
6106
6106
|
|
|
6107
6107
|
## Parallel Agents
|
|
6108
6108
|
|
|
@@ -7351,7 +7351,7 @@ async function startDaemon(options) {
|
|
|
7351
7351
|
const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
|
|
7352
7352
|
await supervisor.init();
|
|
7353
7353
|
const tunnels = /* @__PURE__ */ new Map();
|
|
7354
|
-
const { ServeManager } = await import('./serveManager-
|
|
7354
|
+
const { ServeManager } = await import('./serveManager-Blsg_Pa9.mjs');
|
|
7355
7355
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
7356
7356
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
7357
7357
|
});
|
|
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
5
|
-
import { c as connectToHypha, a as registerSessionService, k as generateHookSettings } from './run-
|
|
5
|
+
import { c as connectToHypha, a as registerSessionService, k as generateHookSettings } from './run-CPIINXWC.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -86,7 +86,7 @@ async function serveAdd(args, machineId) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
async function serveApply(args, machineId) {
|
|
89
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
89
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
90
90
|
const fs = await import('fs');
|
|
91
91
|
const yaml = await import('yaml');
|
|
92
92
|
const file = positionalArgs(args)[0];
|
|
@@ -171,7 +171,7 @@ async function serveApply(args, machineId) {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
async function serveRemove(args, machineId) {
|
|
174
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
174
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
175
175
|
const pos = positionalArgs(args);
|
|
176
176
|
const name = pos[0];
|
|
177
177
|
if (!name) {
|
|
@@ -191,7 +191,7 @@ async function serveRemove(args, machineId) {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
async function serveList(args, machineId) {
|
|
194
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
194
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
195
195
|
const all = hasFlag(args, "--all", "-a");
|
|
196
196
|
const json = hasFlag(args, "--json");
|
|
197
197
|
const sessionId = getFlag(args, "--session");
|
|
@@ -224,7 +224,7 @@ async function serveList(args, machineId) {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
async function serveInfo(machineId) {
|
|
227
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
227
|
+
const { connectAndGetMachine } = await import('./commands-CY_yS6CZ.mjs');
|
|
228
228
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
229
229
|
try {
|
|
230
230
|
const info = await machine.serveInfo();
|
|
@@ -3,7 +3,7 @@ import * as fs from 'fs';
|
|
|
3
3
|
import * as http from 'http';
|
|
4
4
|
import * as net from 'net';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
-
import { S as ServeAuth, h as hasCookieToken } from './run-
|
|
6
|
+
import { S as ServeAuth, h as hasCookieToken } from './run-CPIINXWC.mjs';
|
|
7
7
|
import 'os';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|