pi-webman

A futuristic web cockpit for Pi — live transcript, telemetry, models, tools and command uplink.

Packages

Package details

extension

Install pi-webman from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-webman
Package
pi-webman
Version
0.2.1
Published
Oct 24, 2026
Downloads
974/mo · 974/wk
Author
steelburn
License
MIT
Types
extension
Size
431.7 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "image": "https://raw.githubusercontent.com/steelburn/pi-webman/main/docs/cockpit.png",
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

π Webman — Pi // Cockpit

A futuristic browser cockpit for the Pi coding agent. It puts a live flight deck around the Pi session already running in your terminal: streaming transcript, token flux, context pressure, model core, thinking level, tool loadout, activity log, command uplink and a keyboard-driven command palette.

Inspired by the terminal flight deck and telemetry of its sibling HUD extension pi-cockpit-hud, rebuilt as a zero-dependency local web UI served by a Pi extension.

Pi // Cockpit demo

A real run: directive in, tool calls, a streamed answer and live telemetry — full-resolution MP4.

Highlights

  • Live stream — user directives, assistant text, hidden reasoning, tool calls and tool output arrive over Server-Sent Events. Reconnects hydrate from a full session snapshot.
  • Flight telemetry — context gauge, input/output/cache tokens, reasoning tokens, cost, throughput, message and tool-call counters, mission uptime and session leaf.
  • Tool operations — expandable HUD cards for every tool call, including input, output, status, duration and coloured diffs when a tool returns a patch.
  • Control surface — send, steer or follow up; abort; switch model; change thinking level; enable/disable tools; rename the session; compact context; start a new session (interactive mode); switch between light and dark mode.
  • Command uplink — Ctrl/Cmd+K palette for actions, models, Pi slash commands and tools.
  • / completion — type / in the composer for fuzzy command search with argument completion for models and thinking levels, matching Pi's terminal editor.
  • Extension popups — dialogs an extension opens in the terminal are mirrored as a native modal in the cockpit, and extensions that opt in through the popup bridge get the answer back (see Extension popups).
  • Light & dark modes — toggle from the top bar or the command uplink. The cockpit follows the OS preference on first visit, remembers your choice, and recolours the HUD, transcript and background canvases together.
  • Retro-futurist HUD — neon grid, starfield, scanlines, context ring, neural pulse and status LEDs. Reduced-motion preferences are respected.
  • No runtime dependencies — only Node built-ins. The UI is bundled into the extension.

Install

From the Pi package catalog:

pi install npm:pi-webman

From a checkout of this repo:

pi install "$(pwd)"

Or load it for a single run while developing:

pi -e ./src/index.ts

Pi loads TypeScript extensions directly through jiti; there is no compile step.

Start the cockpit

Inside Pi's terminal UI:

/web                 start on http://127.0.0.1:4747/
/web 5000            start on a custom port
/web open            start and open the default browser
/web status          show the URL and connected clients
/web restart         restart the server
/web stop            stop the server
/webman              alias for /web

The URL is also printed as a Pi notification and shown in the footer as ◈ WEB :4747. Use Ctrl+C in the browser or leave the tab open; the server follows the Pi session.

Start automatically

pi --web
pi --web --web-port 5000
pi --web --web-open

The cockpit is restored automatically after /reload when it was running.

Flags

Flag Default Meaning
--web off Start the cockpit when a session starts
--web-port 4747 Listening port (0 picks a free port)
--web-host 127.0.0.1 Bind interface
--web-token empty Require a bearer token / cookie for access
--web-open off Open the default browser on start

Environment equivalents: PI_COCKPIT_PORT, PI_COCKPIT_HOST, PI_COCKPIT_TOKEN.

Cockpit guide

Area What it does
Top bar Brand, session callsign, working directory, uplink state, wall clock
Context Matrix Context-window ring, used/window tokens, segment bar
Telemetry Input, output, cache read/write, reasoning, cost, messages, tool calls
Neural Pulse Live waveform fed by streamed text/thinking deltas
Activity Log Running and recently completed tools with duration
Transcript Markdown messages, collapsible thoughts, expandable tool cards
Composer AUTO / STEER / FOLLOW delivery modes, prompt history, quick actions
Model Core Current model and a searchable model palette
Thinking Supported thinking levels for the active model
Tool Loadout Toggle active tools on the next model request
Uplink Host, SSE clients, uptime, branch leaf

Keyboard

Key Action
Enter Transmit directive, or run the highlighted command
Shift+Enter Newline
Tab Accept the highlighted command or argument
↑ / ↓ Move through the / menu, or the command uplink
Esc Dismiss the / menu or palette, otherwise abort the active run
Ctrl/Cmd+K Open or close the command uplink
↑ (empty composer) Recall previous directive

AUTO sends immediately when idle and queues a follow-up when Pi is busy. STEER enters after the current assistant turn and its tool calls. FOLLOW waits until the run settles.

Commands

Typing / in the composer opens the same command surface as Pi's terminal editor:

Command completion

Each entry is tagged with where it runs:

Badge Meaning
COCKPIT Executed by the browser against the cockpit API — never sent to the model
EXTENSION, PROMPT, SKILL Real Pi commands, forwarded to the session
TERMINAL A Pi builtin that only the interactive editor can run

Cockpit commands:

Command Effect
/compact [instructions] Compact the context, optionally with custom instructions
/model [provider/model] Switch the model core (argument completion)
/thinking <level> Set the thinking level (argument completion)
/name <callsign> Rename the session
/new Start a new session
/abort Abort the active run
/theme [light|dark] Toggle or set the cockpit theme
/copy Copy the last assistant response
/status One-line session summary
/help Open the command uplink

TERMINAL commands are listed so the surface is honest, but they are refused rather than forwarded: Pi handles its builtins in the interactive editor, so sending /quit or /thinking low as a prompt would otherwise be billed to the model as text.

Argument completion

Extension popups

Pi renders extension dialogs in the terminal, and the extension API does not let one extension read or answer another's dialog — it only reports that a prompt is waiting. The cockpit therefore covers both cases:

  1. Nothing to install. While any extension dialog is open, the composer shows a TERMINAL PROMPT banner with the prompt kind and title, so the browser never looks frozen and you know to answer in the terminal.
  2. Popup bridge. Extensions can render their popup in the browser instead and receive the answer, over the shared pi.events bus:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";

export default function (pi: ExtensionAPI) {
  pi.events.on("webman:popup:answer", (answer) => {
    console.log(answer); // { id, value } or { id, cancelled: true }
  });

  pi.registerCommand("hud", {
    description: "Open the HUD",
    handler: async (_args, ctx) => {
      pi.events.emit("webman:popup:open", {
        id: `hud-${Date.now()}`,
        kind: "select", // select | confirm | input | notice
        title: "DOCKER HUD",
        message: "3 containers running",
        options: [
          { value: "api", label: "api-7f9c", description: "up 3 hours · :8080" },
          { value: "db", label: "postgres-1a2b", description: "up 3 hours · :5432" },
        ],
        source: "my-extension",
      });
    },
  });
}

Channels:

Channel Direction Payload
webman:popup:open extension → cockpit popup spec (id, kind, title, message?, options?, placeholder?, allowCancel?, source?)
webman:popup:answer cockpit → extension { id, value? , cancelled? }
webman:popup:close either way { id } — dismiss without an answer
webman:popup:ping / pong extension ⇄ cockpit { id } → { id, url, version, clients }, to check whether a browser is listening
webman:popup:ready cockpit → extensions { url, version, clients } when the server starts

A runnable extension that implements all of it — including falling back to a terminal dialog when no browser is connected — lives in examples/popup-bridge-demo.ts:

pi -e ./examples/popup-bridge-demo.ts --web
/hud-demo      # select popup, answered in the browser
/hud-notice    # notice popup
/hud-terminal  # counter-example: terminal-only dialog, mirrored as a banner

Security

  • The server binds to 127.0.0.1 by default.
  • Binding to a non-loopback host automatically generates a random token. Open the URL that Pi prints; it contains the token and sets an HttpOnly, SameSite=Strict cookie.
  • --web-token forces token auth even on loopback.
  • Responses carry a restrictive CSP, nosniff, X-Frame-Options: DENY and no-referrer.
  • POST endpoints check the request origin.
  • For remote access prefer an SSH tunnel or Tailscale instead of exposing plain HTTP:
ssh -L 4747:127.0.0.1:4747 your-host

The cockpit has the same operating-system permissions as the Pi process. Anyone who can reach it can send prompts and change the active model/tools, so protect the token.

How it works

src/index.ts     extension entry: flags, /web command, lifecycle events, runtime
src/server.ts    HTTP server, auth, JSON API, SSE endpoint, static assets
src/hub.ts       SSE fan-out with a hydrate-before-stream barrier
src/state.ts     branch-aware session snapshot, telemetry, command catalog
src/popups.ts    extension popup bridge: registry, validation, channels
src/serialize.ts cycle-safe JSON with bounded string sizes
src/assets.ts    generated UI bundle (do not edit)
ui/              source for the HTML/CSS/JS cockpit
scripts/         UI asset bundler
examples/        runnable popup-bridge extension

The extension subscribes to Pi lifecycle events (message_*, tool_execution_*, agent_*, model_select, compaction, session changes) and forwards them to the browser. The browser receives a full snapshot in the SSE hello frame, then applies live events. Session state is persisted as a branch-scoped custom entry (pi-webman) so the cockpit can come back after a runtime reload without polluting model context.

Coexistence with pi-cockpit-hud

pi-cockpit-hud (npm:pi-cockpit-hud) is a separate terminal HUD extension and owns the /cockpit command. Webman uses /web, /webman and /web-session, its own footer status key (webman) and its own custom session entry type (pi-webman), so both extensions can be installed at once.

Note: the unrelated pi-cockpit package on npm is not part of this project.

Development

npm install
npm run build       # bundle ui/* -> src/assets.ts
npm run typecheck
npm run check       # build + typecheck
npm run watch       # rebuild UI on change

After changing UI files, rebuild the bundle and restart Pi (or run /web restart). In a separate terminal, a minimal smoke test:

curl -s http://127.0.0.1:4747/api/health
curl -s http://127.0.0.1:4747/api/state | head -c 400

Releasing

Publishing runs in CI (.github/workflows/publish.yml) through npm Trusted Publishing, so there is no NPM_TOKEN secret to store or rotate and every tarball is published with a provenance attestation.

# 1. bump the version
npm version patch --no-git-tag-version   # or minor / major
git commit -am "chore(release): $(node -p "require('./package.json').version")"
git push

# 2. cut the release: GitHub → Releases → Draft a new release → tag v<version> → Publish

Publishing the GitHub release triggers the workflow, which checks out the tag, verifies that it matches package.json, runs npm run check, and publishes. The job is idempotent — if the version is already on npm it reports that and exits cleanly, so re-running a release or a failed job is safe. workflow_dispatch publishes an existing tag by hand.

One-time setup, on https://www.npmjs.com/package/pi-webman → Settings → Trusted publishing: publisher GitHub Actions, organization steelburn, repository pi-webman, workflow publish.yml.

ci.yml runs the same checks on every push and pull request, and fails if src/assets.ts was not rebuilt after a UI change.

Limitations

  • New sessions are only available when Pi runs in interactive (TUI) mode; RPC/JSON/print hosts own their session lifecycle.
  • Image attachments and file uploads are not implemented yet.
  • The cockpit exposes the current session only; use Pi's session picker to switch sessions.
  • Throughput is estimated from streamed deltas; authoritative usage comes from provider responses.

License

MIT