@m64/nats-agent-dashboard
v0.1.5
Published
Static Vue 3 dashboard for the NATS AI agent network — discover, prompt and manage agents across pi-channel, pi-exec, claude-channel and OpenClaw runtimes from a single screen, with no backend. Ships as a single HTML file or via npx.
Maintainers
Readme
nats-agent-dashboard
A static Vue 3 web UI that connects directly to a NATS server over WebSocket and provides a unified dashboard for the entire NATS AI agent network. Discover, inspect, prompt and manage agents across all four runtimes from a single screen — with no backend server. NATS is the only data channel.
Supported runtimes (auto-detected from $SRV.PING + metadata):
pi-channel— interactive PI agents (agents.pi.[{org}.]{owner}.{session})pi-exec— headless PI bridge with control + per-session services (agents.pi-exec.{owner}[.{sessionId}])claude-channel— Claude Code MCP bridge (agents.ccc.{owner}.{session})nats-channel/ OpenClaw — OpenClaw plugin agents (agents.oc.[{org}.]{name})
Features
- Live agent grid — periodic
$SRV.PINGdiscovery, grouped by runtime, color-coded badges, status dots, automatic removal when agents disappear from the bus. - Streaming prompt panel — pick any agent, type a prompt, watch the response render token-by-token as live markdown with code-block syntax highlighting and copy buttons.
- Pi-exec session control plane — create new headless sessions with cwd / model / thinking-level / lifetime, stop them, and see live enrichment data (remaining lifetime, queue depth, active request) on every session card.
- Fan-out — run the same prompt across N working directories in parallel, with grouped streaming result cards.
- Connection settings — change the NATS WebSocket URL on the fly with auto-reconnect.
- Zero-setup demo — ships with
wss://demo.nats.io:8443as the default, so the dashboard works the instant you open it.
Quick start
npx @m64/nats-agent-dashboardThat's it. A small HTTP server spawns on http://localhost:5173, your browser opens, and the dashboard connects to the public Synadia demo server (wss://demo.nats.io:8443). Every AI agent currently running on that bus shows up in the grid.
To point at a different NATS server, open the Settings cog (top-right), paste in your own ws:// or wss:// URL, and the dashboard reconnects. The URL is persisted in localStorage, so next time you open it you land on the same server.
Flags:
npx @m64/nats-agent-dashboard --nats-url ws://localhost:8080 # override the NATS URL
npx @m64/nats-agent-dashboard --port 8000 # pick a port
npx @m64/nats-agent-dashboard --no-open # don't open the browser
npx @m64/nats-agent-dashboard --helpAlternative distributions
Single HTML file
npm run build produces exactly one file: dist/index.html (~450 KB, ~140 KB gzipped). Everything — Vue, the stores, the markdown renderer, the NATS client, the CSS — is inlined. No external resources. You can:
- open it directly from the filesystem (
file://) - host it on any static web server (GitHub Pages, S3, Netlify, nginx, plain object storage)
- distribute it as a single file attachment
Pre-seed the NATS URL via the query string if you don't want users to have to touch Settings:
file:///path/to/index.html?nats=ws://localhost:8080
https://your-host.example.com/dashboard.html?nats=wss://nats.example.com:443Local development
To work on the dashboard itself, with Vite's dev server, HMR and source maps:
git clone https://github.com/M64GitHub/nats-agent-dashboard.git
cd nats-agent-dashboard
npm install
npm run dev # Vite dev server at http://localhost:5173Running your own NATS server with WebSocket
The dashboard talks to NATS exclusively over WebSocket, so any server it connects to needs a websocket block. Minimal local setup:
# nats.conf
port: 4222
websocket {
port: 8080
no_tls: true
}nats-server -c nats.confFor production / remote access, terminate TLS on the WebSocket port:
websocket {
port: 443
tls {
cert_file: "/etc/letsencrypt/live/nats.example.com/fullchain.pem"
key_file: "/etc/letsencrypt/live/nats.example.com/privkey.pem"
}
}Then connect the dashboard to wss://nats.example.com:443.
Related packages
@m64/nats-channel— OpenClaw NATS channel plugin@m64/nats-pi-channel— PI interactive NATS channel@m64/nats-pi-bridge— PI headless session bridge
Tech stack
- Vue 3 (Composition API,
<script setup>) - Vite — dev server + bundler
@nats-io/nats-corev3 +wsconnect()— the only transport@nats-io/servicesv3 —Svcmdiscovery client- marked + DOMPurify + highlight.js — markdown rendering for streaming responses
No router, no Pinia, no backend, no REST, no SSE. State is plain reactive() modules.
Architecture
Browser
Vue 3 app (static)
┌─────────────────────────────────────┐
│ @nats-io/nats-core wsconnect() │ ←── single WebSocket connection
│ │
│ Discovery $SRV.INFO via Svcm │
│ Inspect {subject}.inspect │
│ Prompt agents.<type>.… │
│ Control agents.pi-exec.{owner} │
└─────────────────────────────────────┘
│
│ ws:// or wss://
▼
NATS Server (websocket port)State is split into plain reactive() modules in src/stores/:
| Store | Responsibility |
|---|---|
| natsConnection.js | wsconnect() lifecycle, status async iterator, persisted config |
| agentDiscovery.js | Periodic Svcm.client(nc).info(), categorisation into runtime buckets, reactive Map<id, AgentRecord> with grace-window removal |
| piexecState.js | Periodic sessionMode: "list" enrichment of pi-exec session records (lifetime, queue, active request) |
| promptState.js | Per-agent streaming chat sessions, in-memory only |
| fanoutState.js | Fan-out form + parallel run state |
| appState.js | Selected agent + UI flags |
The useNatsRequest.js composable wraps the streaming reply pattern: create an inbox, subscribe, publish with reply: inbox, accumulate chunks, and treat an empty payload as the end-of-stream sentinel. This pattern matches every sibling channel server (nats-channel, nats-pi-channel, nats-pi-bridge, nats-claude-channel).
Build from source
npm install
npm run build # produces dist/index.html (single self-contained file)
npm run preview # serves dist/ for local smoke-testing
npm start # runs the bundled launcher against dist/The build uses vite-plugin-singlefile with base: './' to inline everything into a single HTML file that's loadable from file://, any static host, or the bundled launcher. There is no server-side runtime and no backend.
Every third-party package bundled into dist/index.html has its copyright notice and full license text aggregated into dist/THIRD-PARTY-LICENSES.txt at build time (via rollup-plugin-license), so redistributing the single HTML file preserves the notice-retention clauses in MIT / BSD / Apache / MPL.
License
Apache-2.0. See LICENSE for the full text and dist/THIRD-PARTY-LICENSES.txt for the copyright notices of all bundled dependencies.
