๐Ÿ’ฌ pi-web-ui

August 31, 2026 ยท View on GitHub

๐Ÿ’ฌ pi-web-ui

English | Chinese (Simplified)

The polished browser cockpit for the pi coding agent.

npm version Node.js License npm downloads CI status GitHub stars PRs welcome

Stream conversations, inspect tool calls, manage files, and run your workspace โ€” all from one place.

Git source control panel

A web chat interface for the pi coding agent. The agent runs in-process via the pi SDK and streams events to the browser over WebSocket: thinking blocks, tool calls, file trees, a built-in terminal, model management, theme switching, and a full settings panel โ€” tuned for daily development.

Requirements โ€” Node.js โ‰ฅ 22.19 and a configured pi install.

More from the author

Building with DSH?

dsh-ui-tools is the author's companion project for building and extending UI tools in the DSH ecosystem.

โœจ Highlights

๐Ÿ’ฌ Chat that works like you do๐Ÿ–ผ๏ธ Files & images๐Ÿงฉ Extensible by design๐Ÿ”’ Private by default
Streaming replies, steer & follow-up queueing, slash commands, multiple conversations per project, edit-&-re-ask.Attach files, paste images, ask about pictures (vision bridge), preview anything with GBK fallback.Drop-in UI plugins (extra top-bar tabs + agent tools) and standalone themes โ€” no rebuild, no restart.Loopback-only, credential-safe: provider keys & headers never reach the browser.

๐Ÿ“š Table of Contents

Features

๐Ÿ’ฌ Chat

  • Streaming agent chat over WebSocket โ€” the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
  • Thinking blocks, tool-call cards and bash outputs with live status (running โ†’ finished ยท waiting for the model ยท duration).
  • Steer (follow-up queueing) โ€” send a follow-up while the agent is replying; it is queued and injected as soon as the current turn's tool calls settle (the "Interrupt" equivalent of the pi CLI).
  • Slash commands โ€” / opens a command picker (built-in / extension / template / skill); built-ins include /new /model /compact /cwd /thinking /resume, plus /help (command list) and /copy (copy last reply).
  • Multiple conversations per project โ€” each conversation gets its own agent runtime and keeps running in the background after you switch away; the "Running conversations" list shows stream progress and lets you switch back.
  • Edit & re-ask โ€” fork any past question into a new branch and re-prompt; the original conversation stays untouched.
  • Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
  • Question navigation โ€” a floating rail plus per-question tags to jump between questions.

๐Ÿ–ผ๏ธ Files, images & attachments

  • Three attachment modes: inline (โ‰ค12 KB), reference (path only), lines (selected ranges) โ€” over-limit ones degrade automatically.
  • Paste / drag-drop / upload images โ€” resized client-side and sent as image content when the model supports vision (warning otherwise).
  • Vision bridge โ€” when the current model is text-only, images are transcribed into text evidence by an auto-discovered vision model (cached per batch; model & on/off configurable in Settings).
  • Attach arbitrary files without a workspace path โ€” stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
  • File preview โ€” line numbers, click/drag/Shift selection (add to chat as lines), GBK fallback decoding, binary hex view, media preview over HTTP with Range support, and a download button.
  • Live file tree โ€” the server watches the listed directory (fs.watch) and re-lists on change; oversized directories show a truncation warning.

๐Ÿ–ฅ๏ธ Terminal & Git

  • Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
  • Source control (Git) panel โ€” status / branch / diff / untracked files via a hidden query terminal; commit, switch branch, push and pull run in the visible terminal and auto-switch to the terminal view.

๐ŸŽ›๏ธ Models & settings

  • Theme switching โ€” pick a theme in the top bar; themes are pure :root palette overrides on top of the single layout stylesheet (default dark + bundled light/dark palettes). See Themes for how to add your own or contribute one.
  • Model management โ€” edit models.json in the UI and set per-provider API keys (keys/headers never leave the server).
  • Thinking level per model (only the levels the model actually supports are shown).
  • First-run setup wizard.
  • Settings panel โ€” system prompt (append or replace), toggle skills/extensions on/off with immediate effect, save/apply/delete settings presets, and vision-bridge model & switch.

๐ŸŽฏ Goal mode

  • Goal bar โ€” set a target with a review model, max rounds and a lock switch.
  • Goal wizard (AI Refine) โ€” turns a raw request into a concrete goal through a guided questionnaire.
  • Automatic review loop โ€” after each turn an independent review session checks the goal against the final text and git diff HEAD; on fail the feedback is injected as steer until it passes (or the round cap is hit).

โš™๏ธ Background tasks

  • Background-task panel โ€” servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
  • Tool watchdog โ€” a tool call running over 20 minutes is aborted automatically.
  • Stop bash command only โ€” abort a running bash tool without killing the conversation.

๐Ÿ›ก๏ธ Safety & operations

  • Loopback-only by default; set PI_WEB_HOST=0.0.0.0 for LAN / containers.
  • WebSocket Origin/Host same-authority check โ€” cross-origin pages are rejected (403); PI_WEB_ALLOW_ORIGINS whitelist for reverse proxies.
  • Quiesce drain mode via a local control socket (server status|quiesce|unquiesce).
  • Credentials stay server-side โ€” provider headers are never sent to the browser.
  • Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).

๐Ÿšข Deploy & update

  • Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (server shortcut).
  • In-app self-update โ€” checks the npm registry, installs and auto-restarts the service.

Screenshots

Settings panel
Settings panel
Built-in terminal
Built-in terminal
Chat interface
Chat interface
Git source control panel
Git source control panel

Install

npm i -g pi-web-ui            # global install (recommended)
npx pi-web-ui                 # or run without installing (latest, starts on :8787)
npm i -g .                    # or install the local checkout

npm โ‰ฅ 12? npm 12+ blocks dependency install scripts by default (you'll see npm warn install-scripts โ€ฆ blocked). node-pty is a native module, so allow its script (the other two packages it lists are harmless no-ops โ€” allowing them just silences the warning):

npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest

Quick start

Start

pi-web-ui                                           # foreground, http://localhost:8787
PI_WEB_PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui     # custom port / workspace

Stop

  • Foreground: press Ctrl+C in the terminal running it.
  • As a service: pi-web-ui server stop (stops the instance; auto-start stays until server uninstall).

Update

npm i -g pi-web-ui@latest     # upgrade to the latest published version
pi-web-ui server restart      # restart the service to apply it (foreground: restart manually)

Uninstall

npm uninstall -g pi-web-ui

Uninstalling does not delete your chats โ€” session data lives in <cwd>/.pi-web (or PI_WEB_DATA_DIR) and survives uninstall/upgrade.

System service

pi-web-ui server install --port 9000 --cwd /path/to/project   # install + start
pi-web-ui server status                     # running? auto-start?
pi-web-ui server restart                    # restart (applies config/version changes)
pi-web-ui server stop                       # stop (auto-start stays)
pi-web-ui server start                      # start again
pi-web-ui server uninstall                  # remove the service entirely
pi-web-ui server shortcut                   # desktop one-click launch icon
pi-web-ui server quiesce                    # drain: refuse NEW chats/messages, let running ones finish
pi-web-ui server unquiesce                  # reopen admission

server status also shows live stats via a local control socket (version, PID, quiesce state, connected browsers, running conversations) โ€” the same socket drives quiesce/unquiesce.

  • macOS โ†’ launchd agent (no sudo), logs to /tmp/pi-web-ui.log / .err
  • Linux โ†’ systemd unit (systemctl enable --now), logs via journalctl -u pi-web-ui -f
  • Windows โ†’ Task Scheduler logon task (hidden PowerShell window, no black console)

Options: --port (default 8787), --cwd (workspace), --data-dir (sessions), --name (custom service name). Rerunning server install with new options regenerates the config and restarts the service โ€” that's how you change its port/cwd.

Plugins

Plugins are optional UI components (extra top-bar tabs backed by their own client view, optionally with a server-side entry and agent tools). They live in your data-dir plugins folder (<dataDir>/plugins/<id>/, default ~/.pi-web/plugins/) โ€” a plugin is simply a directory containing manifest.json, an optional server entry (index.mjs) and an optional view entry (client/entry.mjs). No plugin directories = no plugins, nothing shows up in the UI.

Plugin catalog

These plugins ship in this repository (plugins/<id>/) and can be installed straight from GitHub:

PluginWhat it does
๐Ÿ“ฌ webmailIMAP inbox browsing / search / read / mark / delete + SMTP sending, new-mail notifications, and an optional "allow AI to manage my mailbox" switch (six mail_* agent tools). Auto-installs its npm deps on first activation.
๐Ÿ—„๏ธ db-clientDatabase workbench: connection manager + schema tree for MySQL / PostgreSQL / SQLite / SQL Server / MongoDB / Redis โ€” table structure, paginated data with sorting, SQL editor, and row editing. Drivers auto-install on first use.
๐Ÿ“ vscode-editorVS Code-like workbench: multi-root file tree (local + SSH hosts), CodeMirror multi-tab editor, Remote-SSH remote file browsing/editing, draggable multi-terminal panel (xterm.js), SFTP sync & upload/download to your computer. Auto-installs ssh2.
๐Ÿ“ฌ demo-mailboxMinimal example plugin demonstrating the server entry + client view + two-way message protocol. Doubles as the plugin test fixture โ€” start here if you want to write your own.

Example โ€” install the webmail plugin:

pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail

Each plugin's directory in the repo has its own README.md with full feature lists, configuration and per-plugin caveats.

Installing

From GitHub (any of these source forms):

pi-web-ui install owner/repo                                  # shorthand
pi-web-ui install https://github.com/owner/repo               # full URL (.git optional)
pi-web-ui install https://github.com/o/r/tree/dev/sub/dir     # branch + subdirectory inside the repo
pi-web-ui install owner/repo#v1.2                             # pin a branch/tag (#suffix works on any form above)
pi-web-ui install /path/to/plugin-dir                         # local directory (for development)

Useful options:

  • --name <id> โ€” custom plugin id / directory name (defaults to the repo or subdirectory name; letters/digits/-/_ only).
  • --force โ€” overwrite an existing installation. Your plugin's local config.json (credentials etc.) is preserved across upgrades.
  • --data-dir <dir> โ€” override the data dir (default ~/.pi-web).

The CLI clones the repo (shallow; falls back to a tarball download without git), locates the manifest.json (including inside subdirectories) and copies the plugin into <dataDir>/plugins/<id>/.

No git? No network? You can also just copy a plugin directory into ~/.pi-web/plugins/ by hand โ€” same result.

Updating

Re-run install against the same source with --force:

# example: update the webmail plugin to the latest version in the repo
pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail --force
  • The upgrade preserves the plugin's local config.json automatically.
  • Plugins that store other local state inside their directory (e.g. db-client's db-connections.json, vscode-editor's ssh-hosts.json) are not covered by that preservation โ€” back those up before a forced reinstall.
  • Refresh the browser afterwards; no server restart needed.

Activating

If the server is running, just refresh the browser โ€” new plugins are picked up on attach without a restart. If it isn't, they load on next start. Each plugin appears as a tab (๐Ÿงฉ or its own icon) in the top bar.

Listing / disabling / uninstalling

pi-web-ui plugins             # list installed plugins (id / name / version / description)
pi-web-ui uninstall <id>      # remove a plugin
  • To temporarily hide a plugin without uninstalling, use the Settings panel (โš™) โ†’ UI plugins switches โ€” stored per client, purely visual, no restart needed. Re-enable any time.
  • uninstall deletes the plugin directory; refresh the browser and its tab disappears. Plugin configuration written inside the plugin dir is removed too โ€” back up <dataDir>/plugins/<id>/config.json first if you need it.

Themes

Each theme is a pure :root palette override โ€” a small CSS file that only sets CSS variables (see the :root block in web/src/styles.css for the full variable list: base colors --bg/--accent/--term-* plus derived colors like --tooltip-bg/--code-bg/--notice-*). The layout lives ONLY in the bundled web/src/styles.css; picking a theme overrides the variables, so every theme works with every build and layout changes never touch themes. Built-in themes are generated by node make-light-theme.mjs.

Built-in themes ship in the npm package (themes/, e.g. the bundled light theme). The theme picker lives in the top bar (๐ŸŒž icon); the current choice is stored per browser in localStorage.

Using a theme

Just pick it in the top bar โ€” built-in and user themes are merged in the same menu. User themes win over built-ins on the same id.

Providing a theme locally (no GitHub needed)

Any CSS file dropped into your data-dir themes folder shows up in the theme menu automatically โ€” no restart, no rebuild:

  1. Find your data dir (default ~/.pi-web, override with PI_WEB_DATA_DIR).
  2. Create <dataDir>/themes/ and drop your stylesheet in: e.g. ~/.pi-web/themes/my-theme.css.
  3. Reload the page and pick it in the top bar. The file name (without .css) is the theme id shown in the menu.
~/.pi-web/
โ””โ”€โ”€ themes/
    โ””โ”€โ”€ my-theme.css          # appears in the menu as "my-theme"

Easiest way to write one: copy a built-in palette (e.g. themes/white.css from the source repo) and change the :root colors โ€” list every variable you want to override; unlisted ones fall back to the dark defaults in styles.css. Notes:

  • The terminal follows the theme โ€” set the --term-* variables (terminal ANSI palette + --term-bg) in your :root and both the xterm canvas and its padded container adapt automatically (see the defaults in styles.css).
  • Syntax-highlight colors (highlight.js's github-dark.css is bundled) must be overridden in your theme file or code will be unreadable on light themes โ€” see the .hljs overrides at the bottom of themes/white.css for the pattern (dark themes can skip it).
  • Theme ids must match ^[A-Za-z0-9_-]+$ (no dots/slashes โ€” path-traversal guard on the server).

Contributing a theme to the repository (GitHub)

Want your theme shipped to everyone? Open a pull request at github.com/xing-shuyin/pi-web-ui:

  1. Fork the repo and clone it.
  2. Create your theme as themes/<id>.css โ€” a pure :root palette. Copy themes/white.css (or themes/cyberpunk.css for a dark palette) as the starting template.
  3. Verify locally: run npm run dev, then use the top bar theme picker โ€” your theme must be listed and render correctly (chat cards, code blocks, tool-call cards, git/terminal panels).
  4. Regenerate all built-in themes with node make-light-theme.mjs when you changed the variable list in styles.css.
  5. Commit (git add themes/<id>.css) and open the PR. The themes/ folder is already in the npm package files whitelist, so once merged and released, npm i -g pi-web-ui will ship your theme to everyone.

Rules for merged themes: the file must be a single CSS file, set the --term-* variables for a readable terminal, and override .hljs syntax colors for readable code on light themes.

Security

  • Loopback-only by default โ€” the server binds 127.0.0.1 and is not reachable from the network unless you explicitly set PI_WEB_HOST=0.0.0.0 (e.g. LAN access, Docker port mapping โ€” the compose file sets it for you).
  • WebSocket origin check โ€” browser pages connecting to /ws must present an Origin whose hostname and port match the request Host; cross-origin pages are rejected with 403. Non-browser clients (no Origin) are unaffected. Add PI_WEB_ALLOW_ORIGINS=http://your-host:port for reverse-proxy setups.
  • Quiesce โ€” server quiesce refuses new prompts/forks/session resumes until you server unquiesce; in-flight runs finish cleanly (useful before upgrades/backups).
  • Credentials stay server-side โ€” provider headers (which may carry Authorization / API keys) are never sent to the browser; the model management UI edits everything else and the server preserves the headers.

Reverse proxy (nginx)

Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a same-machine reverse proxy is the supported remote-access path โ€” no PI_WEB_HOST=0.0.0.0 needed):

# pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
server {
    listen 443 ssl;
    server_name your-host;
    # ssl_certificate ... / ssl_certificate_key ...

    # App entry at a sub-path (strips the /pi/ prefix)
    location /pi/ {
        proxy_pass http://127.0.0.1:8787/;
        proxy_http_version 1.1;
        # $http_host keeps the port โ€” the server's origin check compares the
        # full authority (hostname AND port). $host would drop it and get 403.
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket โ€” MUST forward Host identically or the upgrade is 403'd
    # (page loads, but chat/terminal keep reconnecting)
    location /ws {
        proxy_pass http://127.0.0.1:8787;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # Absolute-path assets/API the built frontend requests (root, not /pi/)
    location /assets/  { proxy_pass http://127.0.0.1:8787; }
    location = /favicon.svg           { proxy_pass http://127.0.0.1:8787; }
    location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
    location = /api/file   { proxy_pass http://127.0.0.1:8787; }
    location = /api/health { proxy_pass http://127.0.0.1:8787; }
}

Key points:

  • Host must be $http_host (keeps the port) on both /pi/ and /ws โ€” the origin check compares hostname and port. proxy_set_header Host $host or leaving it unset (defaults to the upstream 127.0.0.1:8787) both fail with 403.
  • Same-origin works automatically: as long as the browser's Origin equals the forwarded Host (it does through a plain proxy), no PI_WEB_ALLOW_ORIGINS is needed. Only set it when the browser origin differs from the Host the server sees (e.g. a TLS-terminating proxy that changes the port).
  • No proxy_protocol unless you really need real client IPs: it makes nginx reject every connection that does not send a PROXY header, which breaks direct LAN access and any non-frp clients. With frp, drop transport.proxyProtocolVersion from the proxy config unless nginx listens with proxy_protocol too.
  • LAN access without a proxy: just set PI_WEB_HOST=0.0.0.0 (and a firewall rule) โ€” or put the whole server block above on port 80/443.

Full working example (with an frp tunnel): deploy/nginx-subpath.conf.

Contribute

pi-web-ui is a small open-source project โ€” your contributions are what make it grow. Code, plugins, themes, docs, translations, ideas: everything is welcome, and every merged PR ships to all users with the next npm publish. โค๏ธ

Way to contributeHow to get started
๐Ÿงฉ Write a pluginBuild your own UI tab + agent tools. Copy plugins/demo-mailbox as the minimal template (it doubles as the test fixture), develop locally, then either open a PR to ship it in the catalog or publish it standalone.
๐ŸŽจ Contribute a themeCopy themes/white.css (light) or themes/cyberpunk.css (dark) as a pure-palette template, tweak the :root palette + --term-* + .hljs, verify with npm run dev, then open a PR โ€” full walkthrough in Contributing a theme.
๐Ÿ’ป Fix a bug / add a featureLook for open issues or propose something new. Fork โ†’ branch โ†’ PR. Keep the code conventions in AGENTS.md (tabs, i18n keys in both languages, protocol changes in server/protocol.ts).
๐Ÿ“– Docs & translationsImprove the READMEs, write plugin docs, fix typos, or help translate the UI / docs into more languages.
๐Ÿ’ก Ideas & feedbackOpen an issue or start a discussion โ€” feature requests, bug reports, UI polish ideas, deployment experience reports.

Before opening a PR, a quick sanity pass keeps reviewers happy:

  • npm run check:protocol + npm test โ€” protocol sync and unit tests.
  • npm run typecheck โ€” no type errors.
  • npm run build โ€” both frontend and backend compile.
  • For protocol changes: add branches in both server/index.ts and web/src/use-chat.ts (see the "Protocol single source" note in AGENTS.md).

Enjoying pi-web-ui? Give the repo a โญ โ€” it helps others find it. And if you built something cool on top (plugin, theme, deployment recipe), tell us โ€” we love showcasing community work.

License

MIT