streamboard
v0.0.7
Published
A CLI that gives AI agents a live browser canvas.
Readme
Streamboard
A CLI that gives AI agents a live browser canvas. Push data, UI appears. Beautiful, animated, real-time.
Install / Run
npx --yes streamboard serveThe published package vendors Bun as a dependency and ships a prebuilt browser UI, so npx --yes streamboard serve does not run a second hidden npm install for the frontend.
If you use Streamboard often, install it globally for faster startup on subsequent runs:
npm install -g streamboard
streamboard serveEnvironment variables
| Variable | Effect |
|----------|--------|
| STREAMBOARD_STATIC=1 | Serve the prebuilt web/dist UI (single port) even when a dev checkout has web/src — useful to preview a production build locally. Requires web/dist to exist. |
| STREAMBOARD_FORCE_VITE=1 | Force the Vite dev server + separate WebSocket port, even if web/dist is present. |
npm publish runs prepack, which builds web/ with node scripts/prepack-web.mjs (Bun if it is on PATH, otherwise npm).
Quick Start
# Start the canvas server (opens browser automatically)
npx --yes streamboard serve
# In another terminal, push components:
npx --yes streamboard push header '{"component":"header","title":"My Report","subtitle":"Generated by AI"}'For live multi-slot dashboards, stream JSONL so the canvas updates as each message is generated:
npx --yes streamboard stream --stdin <<'EOF'
{"type":"push","id":"header","data":{"component":"header","title":"Live report","subtitle":"Streaming from stdin"}}
{"type":"push","id":"notes","data":{"component":"markdown","content":"## Status\n\nFirst chunk arrived."}}
{"type":"notify","text":"Canvas ready","variant":"success"}
EOFCommands
serve
Starts the WebSocket server and browser UI. In a published install this serves the prebuilt static app on one port; in a local repo checkout with web/src, it uses Vite for hot reload. Then opens your browser unless --no-open is set.
Other commands auto-detect the running instance from per-project runtime state stored in your user temp/cache area, so normal usage does not create a repo-local .canvas.pid.
npx --yes streamboard serve # start on default port 3001
npx --yes streamboard serve --port 4000 # custom port
npx --yes streamboard serve --no-open # don't open browserpush <id> <json>
Push a component to the canvas. Each slot has a unique ID — pushing to the same ID replaces it.
npx --yes streamboard push myslot '{"component":"header","title":"Hello"}'
npx --yes streamboard push notes --md "# Markdown content here"
npx --yes streamboard push data --file ./results.json
echo '{"component":"table",...}' | npx --yes streamboard push tbl --stdin| Flag | Description |
|-----------|-------------------------------------|
| --md | Wrap text as a markdown component |
| --file | Read JSON from a file |
| --stdin | Read JSON from stdin |
| --port | Server port (default: 3001) |
stream
Send multiple canvas messages over one persistent WebSocket connection.
--stdinis the live path: send JSONL with one message object per line and Streamboard forwards each line as it arrives.--filereads a prepared JSON array or JSONL file.--messagesaccepts a short inline JSON array.--validateparses and validates messages without sending them.--delayadds a delay in milliseconds between messages.
# Progressive JSONL from stdin
npx --yes streamboard stream --delay 300 --stdin <<'EOF'
{"type":"push","id":"header","data":{"component":"header","title":"Agent Run"}}
{"type":"push","id":"notes","data":{"component":"markdown","content":"Streaming live updates."}}
EOF
# Prepared batch payload from a file
npx --yes streamboard stream --file ./dashboard.json
# Validate without sending
npx --yes streamboard stream --validate --stdin <<'EOF'
{"type":"notify","text":"Check me","variant":"success"}
EOFclear [id]
Remove slots from the canvas.
npx --yes streamboard clear header # remove one slot
npx --yes streamboard clear --all # clear everythingComponents
Every push payload needs a "component" field to tell the canvas what to render.
header
{
"component": "header",
"title": "Quarterly Report",
"subtitle": "Q1 2026 — Auto-generated",
"badges": [
{ "label": "Live", "variant": "default" },
{ "label": "Draft", "variant": "secondary" }
]
}kpi-cards
{
"component": "kpi-cards",
"cards": [
{ "label": "Revenue", "value": "$1.2M", "trend": "↑ 12%", "status": "green" },
{ "label": "Latency", "value": "142ms", "trend": "↓ 3%", "status": "yellow" },
{ "label": "Errors", "value": "0.02%", "status": "green" }
]
}Cards animate in with a staggered cascade. status shows a colored dot (green/yellow/red). trend is colored based on direction (↑/+ = green, ↓/- = red).
chart
Supports line, bar, area, and donut variants.
{
"component": "chart",
"variant": "area",
"title": "Revenue Over Time",
"xKey": "month",
"series": [
{ "key": "revenue", "label": "Revenue", "color": "#3b82f6" },
{ "key": "profit", "label": "Profit", "color": "#22c55e" }
],
"data": [
{ "month": "Jan", "revenue": 400, "profit": 240 },
{ "month": "Feb", "revenue": 300, "profit": 139 },
{ "month": "Mar", "revenue": 500, "profit": 380 }
]
}table
{
"component": "table",
"title": "Service Status",
"columns": [
{ "key": "service", "label": "Service" },
{ "key": "status", "label": "Status", "variant": "badge" },
{ "key": "latency", "label": "Latency", "align": "right" }
],
"rows": [
{ "service": "API Gateway", "status": "healthy", "latency": "12ms" },
{ "service": "Database", "status": "active", "latency": "3ms" }
]
}Columns with "variant": "badge" render values as colored badges. Status-like values (healthy, active, error, failed) get automatic coloring.
markdown
{
"component": "markdown",
"content": "# Hello World\n\nThis supports **GFM** including:\n- Lists\n- `code`\n- Tables\n- Blockquotes"
}Or use the --md shorthand:
npx --yes streamboard push notes --md "# Analysis Complete\n\nFound **3 issues**."progress
{
"component": "progress",
"title": "Pipeline",
"steps": [
{ "label": "Fetching data", "status": "done" },
{ "label": "Running analysis", "status": "active" },
{ "label": "Generating report", "status": "pending" }
]
}Steps show as a vertical list with animated checkmarks, a spinning indicator for active, and muted dots for pending.
code
{
"component": "code",
"title": "Query",
"language": "sql",
"code": "SELECT users.name, COUNT(orders.id)\nFROM users\nJOIN orders ON orders.user_id = users.id\nGROUP BY users.name\nORDER BY COUNT(orders.id) DESC;",
"highlights": [
{ "lines": [3, 4], "label": "Join clause", "color": "#3b82f6" }
]
}Highlighted lines get a colored left border and floating label.
Layout Control
Push a special _layout slot to control the grid:
# Two-column layout
npx --yes streamboard push _layout '{"component":"_layout","columns":2}'
# Control slot order
npx --yes streamboard push _layout '{"component":"_layout","columns":2,"order":["header","kpis","chart1","table1"]}'Architecture
Published: npx --yes streamboard serve Prebuilt UI + WebSocket on one port
─────────────────────────────────────── ─────────────────────────────────────
→ Bun.serve (static + /ws + /state) Browser uses same-origin ws://…/ws
Local dev: bun cli.ts serve (repo) Vite (:5173) + WebSocket (separate port)
─────────────────────────────────────── ─────────────────────────────────────
→ spawns Vite with VITE_WS_PORT React connects to ws://localhost:<ws>/ws
npx --yes streamboard stream --stdin On each JSONL message:
→ keeps one WS connection open → updates state Map incrementally
→ parses one JSON object per line → React re-renders as messages arrive
npx --yes streamboard push <id> <json> On WS "push" message:
→ connects to WS → updates state Map
→ sends message → React re-renders with animation
→ disconnects
Runtime discovery Per-project temp/cache record
→ written on serve start → lets later CLI calls auto-find the port
→ cleaned up on stop → avoids repo-local runtime files
npx --yes streamboard clear On WS "clear" message:
→ sends clear message → removes slot(s) with exit animationThe browser auto-reconnects if the server restarts — no data loss on reconnect since the server re-sends full state.
Development
# Install dependencies
bun install
cd web && bun install
# Start everything
bun cli.ts serve
# Build the web frontend
cd web && bun run buildTech Stack
- CLI/Server: Bun, citty, @clack/prompts
- Frontend: React 19, Vite, Tailwind v4, shadcn/ui, Framer Motion, Recharts
- Protocol: WebSocket (JSON messages)
