pi-web-ui 0.72.0 → 0.74.0

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/README.md CHANGED
@@ -1,555 +1,569 @@
1
- <div align="center">
2
-
3
- # 💬 pi-web-ui
4
-
5
- **English** | [Chinese (Simplified)](https://github.com/xing-shuyin/pi-web-ui/blob/main/README.zh-CN.md)
6
-
7
- *The polished browser cockpit for the [pi coding agent](https://pi.dev).*
8
-
9
- <p>
10
- <a href="https://www.npmjs.com/package/pi-web-ui"><img src="https://img.shields.io/npm/v/pi-web-ui?color=cb3837&logo=npm&label=pi-web-ui" alt="npm version"></a>
11
- <a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/pi-web-ui?logo=node.js&logoColor=white" alt="Node.js"></a>
12
- <a href="LICENSE"><img src="https://img.shields.io/github/license/xing-shuyin/pi-web-ui" alt="License"></a>
13
- <a href="https://www.npmjs.com/package/pi-web-ui"><img src="https://img.shields.io/npm/dm/pi-web-ui?label=downloads" alt="npm downloads"></a>
14
- <a href="https://github.com/xing-shuyin/pi-web-ui/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/xing-shuyin/pi-web-ui/ci.yml?branch=main&label=CI" alt="CI status"></a>
15
- <a href="https://github.com/xing-shuyin/pi-web-ui/stargazers"><img src="https://img.shields.io/github/stars/xing-shuyin/pi-web-ui?style=social" alt="GitHub stars"></a>
16
- <a href="https://github.com/xing-shuyin/pi-web-ui/fork"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat" alt="PRs welcome"></a>
17
- </p>
18
-
19
- Stream conversations, inspect tool calls, manage files, and run your workspace — all from one place.
20
-
21
- ![Git source control panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot4.jpeg)
22
-
23
- </div>
24
-
25
- A web chat interface for the [pi coding agent](https://pi.dev). The agent runs
26
- **in-process** via the pi SDK and streams events to the browser over WebSocket:
27
- thinking blocks, tool calls, file trees, a built-in terminal, model management,
28
- theme switching, and a full settings panel — tuned for daily development.
29
-
30
- > **Requirements** — Node.js ≥ 22.19 and a configured pi install.
31
-
32
- ## More from the author
33
-
34
- > **Building with DSH?**
35
- >
36
- > [**dsh-ui-tools**](https://github.com/xing-shuyin/dsh-ui-tools) is the author's companion project for building and extending UI tools in the DSH ecosystem.
37
-
38
- ## ✨ Highlights
39
-
40
- | 💬 **Chat that works like you do** | 🖼️ **Files & images** | 🧩 **Extensible by design** | 🔒 **Private by default** |
41
- | --- | --- | --- | --- |
42
- | 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. |
43
-
44
- ## 📚 Table of Contents
45
-
46
- - 🚀 [Features](#features)
47
- - 🖼️ [Screenshots](#screenshots)
48
- - 📦 [Install](#install)
49
- - ⚡ [Quick start](#quick-start)
50
- - 🖥️ [System service](#system-service)
51
- - 🧩 [Plugins](#plugins)
52
- - 🎨 [Themes](#themes)
53
- - 🔒 [Security](#security)
54
- - 🌐 [Reverse proxy (nginx)](#reverse-proxy-nginx)
55
- - 🤝 [Contribute](#contribute)
56
- - 📄 [License](#license)
57
-
58
- ## Features
59
-
60
- ### 💬 Chat
61
-
62
- - **Streaming agent chat over WebSocket** — the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
63
- - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
64
- - **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).
65
- - **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).
66
- - **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.
67
- - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
68
- - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
69
- - Question navigation — a floating rail plus per-question tags to jump between questions.
70
-
71
- ### 🤖 Subagents & templates
72
-
73
- - **First-party subagents** spawn independent background conversations for parallel exploration / implementation / review (`subagent_spawn`, with optional `model` override or a template's model); collect results without polling via `subagent_wait_all` (blocks until every subagent finishes, then summarizes results/errors). Manage them like a chat right in the left panel: view live output, inject follow-ups (steer), abort, dismiss — failed runs surface a red dot in the running list and an error notice in the main chat. In-memory sessions — they never touch the history / resume list, and can be nested.
74
- - **Subagent templates** — configure reusable presets in Settings → Subagent templates: a role system prompt (append or replace), skills & extensions whitelists, and an optional per-template model. The AI picks one via the `subagent_templates` tool and `subagent_spawn(template="…")`, or spawns without one (default = follow the main conversation's current model, or the global default subagent model set in the same panel). Disabled templates stay in the panel for re-enabling but become invisible to the AI tools (can't be listed or picked). Templates are shared globally across browser clients (`<dataDir>/subagent-templates.json`). Six built-in templates (review / implement / research / scout / audit / delegate, adapted from the pi-subagents community projects) seed the list on first run — marked 「Built-in」, editable and deletable like any other.
75
-
76
- ### 🖼️ Files, images & attachments
77
-
78
- - Three attachment modes: `inline` (≤12 KB), `reference` (path only), `lines` (selected ranges) — over-limit ones degrade automatically.
79
- - Paste / drag-drop / upload images — resized client-side and sent as image content when the model supports vision (warning otherwise).
80
- - **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).
81
- - Attach arbitrary files without a workspace path stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
82
- - File previewline 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.
83
- - Live file tree the server watches the listed directory (`fs.watch`) and re-lists on change; oversized directories show a truncation warning.
84
-
85
- ### 🖥️ Terminal & Git
86
-
87
- - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
88
- - **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.
89
-
90
- ### 🎛️ Models & settings
91
-
92
- - 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](#themes) for how to add your own or contribute one.
93
- - Model management — edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
94
- - Thinking level per model (only the levels the model actually supports are shown).
95
- - First-run setup wizard.
96
- - 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.
97
-
98
- ### 🎯 Goal mode
99
-
100
- - Goal bar — set a target with a review model, max rounds and a lock switch.
101
- - Goal wizard (**AI Refine**) — turns a raw request into a concrete goal through a guided questionnaire.
102
- - 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).
103
-
104
- ### 🤖 DeepSeek Harness engine
105
-
106
- - **Switchable engine** — `PI_WEB_ENGINE=pi|dsh` (default `pi`). The pi engine runs the agent in-process via the pi SDK; the **DSH engine** runs the official [`@deepseek-ai/dsh`](https://github.com/deepseek-ai/dsh) (DeepSeek Harness) runtime as a subprocess. `/api/health` reports `engine`; the footer shows a DSH badge.
107
- - **Same wire protocol** — the DSH engine implements the same WebSocket protocol, so goal mode, SCM, background tasks, settings, plugins, terminals, message-delta & snapshots all work identically.
108
- - **Native goal machinery** — DSH's own goal state machine + round-driver auto-continues rounds; the model judges completion/blocked (no separate review session). The goal wizard drives it via the model's `ask_user_question`.
109
- - **Real image blocks** — photos are sent as true image content to vision-capable DeepSeek models (e.g. `deepseek-v4-flash-vision-exp`); text-only models get a text-transcription bridge instead.
110
- - **Question dialog** — the model's `ask_user_question` surfaces as a browser dialog (single/multi-select + free text) with queueing and a countdown.
111
- - **Tools & MCP bridge** — plugin AI tools and external MCP servers (`mcp.json`) are bridged into the DSH runtime, so the DSH model can call them (executed server-side).
112
- - **Skill enable/disable** — the DSH skill catalog is exposed in Settings; disabling a skill filters it out of the model's view at runtime.
113
- - **DSH user patches** — drop `.yml` Cordis patches into `<dataDir>/dsh-patches/` to extend the runtime and reload from Settings.
114
-
115
- ### ⚙️ Background tasks
116
-
117
- - Background-task panel — servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
118
- - Tool watchdog — a tool call running over 20 minutes is aborted automatically.
119
- - **Stop bash command only** abort a running bash tool without killing the conversation.
120
-
121
- ### 🛡️ Safety & operations
122
-
123
- - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
124
- - WebSocket Origin/Host same-authority check — cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
125
- - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
126
- - Credentials stay server-sideprovider headers are never sent to the browser.
127
- - Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).
128
-
129
- ### 🚢 Deploy & update
130
-
131
- - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
132
- - In-app self-update — checks the npm registry, installs and auto-restarts the service.
133
-
134
-
135
- ## Screenshots
136
-
137
- <table>
138
- <tr>
139
- <td align="center"><img src="https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot1.png" alt="Settings panel"><br><sub>Settings panel</sub></td>
140
- <td align="center"><img src="https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot2.jpeg" alt="Built-in terminal"><br><sub>Built-in terminal</sub></td>
141
- </tr>
142
- <tr>
143
- <td align="center"><img src="https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot3.jpeg" alt="Chat interface"><br><sub>Chat interface</sub></td>
144
- <td align="center"><img src="https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot4.jpeg" alt="Git source control panel"><br><sub>Git source control panel</sub></td>
145
- </tr>
146
- </table>
147
-
148
-
149
- ## Install
150
-
151
- ```bash
152
- npm i -g pi-web-ui # global install (recommended)
153
- npx pi-web-ui # or run without installing (latest, starts on :8787)
154
- npm i -g . # or install the local checkout
155
- ```
156
-
157
- **npm 12?** npm 12+ blocks dependency install scripts by default (you'll see
158
- `npm warn install-scripts … blocked`). node-pty is a native module, so allow its
159
- script (the other two packages it lists are harmless no-ops — allowing them just
160
- silences the warning):
161
-
162
- ```bash
163
- npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
164
- ```
165
-
166
- ### Termux (Android)
167
-
168
- pi-web-ui runs fine on Android via [Termux](https://termux.dev), but `node-pty`
169
- (the native dependency) needs a toolchain, and Android has a few quirks worth
170
- knowing:
171
-
172
- 1. **Install the build toolchain first** — `node-pty` needs Python and a C
173
- toolchain:
174
-
175
- ```bash
176
- pkg install python clang make binutils
177
- ```
178
-
179
- 2. **Point the node-pty build at a dummy NDK path.** On Android, gyp fails with
180
- `Undefined variable android_ndk_path` unless the variable is defined:
181
-
182
- ```bash
183
- GYP_DEFINES="android_ndk_path=' '" npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
184
- ```
185
-
186
- 3. **If `pi-web-ui` won't execute after install** (the exec bit and/or shebang
187
- can get mangled on Android): restore it:
188
-
189
- ```bash
190
- chmod +x "$(command -v pi-web-ui)"
191
- sed -i 's/\r$//' "$(command -v pi-web-ui)"
192
- ```
193
-
194
- 4. **Run it in the background** with `--no-browser` (there is no desktop
195
- browser to auto-open):
196
-
197
- ```bash
198
- setsid nohup pi-web-ui --no-browser --cwd /path/to/workspace >~/pi-web.log 2>&1 &
199
- ```
200
-
201
- `setsid` detaches the server from the launching shell's process group, so
202
- closing the Termux session doesn't take the server down — `nohup` alone is
203
- not enough when the parent process group gets killed.
204
-
205
- The `[control] socket error: EACCES …/.pi-web/pi-web-ui.sock` warning at startup
206
- is harmless on Android: `pi-web-ui server stop/restart` won't work over the
207
- control socket, but the web UI itself is unaffected.
208
-
209
- ## Quick start
210
-
211
- **Start (foreground)**
212
-
213
- ```bash
214
- pi-web-ui # foreground, http://localhost:8787
215
- ```
216
-
217
- **Start flags & environment variables** every setting can be passed as a `--flag` on the command
218
- line **or** set as an environment variable (flag wins). Pick whichever you prefer:
219
-
220
- | Flag | Env var | Default | Purpose |
221
- | --- | --- | --- | --- |
222
- | `--port <n>` | `PI_WEB_PORT` | `8787` | HTTP port |
223
- | `--cwd <dir>` | `PI_WEB_CWD` | current dir | workspace root (read/write/terminal) |
224
- | `--data-dir <dir>` | `PI_WEB_DATA_DIR` | `~/.pi-web` | data dir (sessions, plugins, uploads) |
225
- | `--engine <pi\|dsh>` | `PI_WEB_ENGINE` | `pi` | agent engine; `--engine dsh` = DeepSeek Harness |
226
- | `--host <addr>` | `PI_WEB_HOST` | `127.0.0.1` | listen address (`0.0.0.0` for LAN/Docker) |
227
- | `--agent-dir <dir>` | `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models, skills) |
228
- | _env only_ | `PI_WEB_TOKEN` | empty | optional shared auth token |
229
- | _env only_ | `PI_WEB_DSH_*` | | dsh runtime, patches & debug settings |
230
-
231
- The two are equivalent pick one:
232
-
233
- ```bash
234
- pi-web-ui --engine dsh --port 9000 --cwd /path/to/project
235
- PI_WEB_ENGINE=dsh PI_WEB_PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui
236
- ```
237
-
238
- For the DSH engine also install the runtime (`npm i -g @deepseek-ai/dsh@0.1.1-rc.2`) and set a
239
- DeepSeek API key (read from `~/.pi/agent/auth.json`, set in the provider/API-key panel).
240
-
241
- **Stop**
242
-
243
- - **Foreground**: press `Ctrl+C` in the terminal running it.
244
- - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
245
-
246
- **Update**
247
-
248
- ```bash
249
- npm i -g pi-web-ui@latest # upgrade to the latest published version
250
- pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
251
- ```
252
-
253
- **Uninstall**
254
-
255
- ```bash
256
- npm uninstall -g pi-web-ui
257
- ```
258
-
259
- Uninstalling does **not** delete your chats — session data lives in
260
- `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
261
-
262
-
263
- ## System service
264
-
265
- ```bash
266
- pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
267
- pi-web-ui server status # running? auto-start?
268
- pi-web-ui server restart # restart (applies config/version changes)
269
- pi-web-ui server stop # stop (auto-start stays)
270
- pi-web-ui server start # start again
271
- pi-web-ui server uninstall # remove the service entirely
272
- pi-web-ui server shortcut # desktop one-click launch icon
273
- pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
274
- pi-web-ui server unquiesce # reopen admission
275
- ```
276
-
277
- `server status` also shows live stats via a local control socket (version,
278
- PID, quiesce state, connected browsers, running conversations) — the same
279
- socket drives `quiesce`/`unquiesce`.
280
-
281
- - **macOS** → launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
282
- - **Linux** systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
283
- - **Windows** Task Scheduler logon task (hidden PowerShell window, no black console)
284
-
285
- Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
286
- `--engine <pi|dsh>`, `--host`, `--agent-dir`, `--name` (custom service name). Rerunning
287
- `server install` with new options regenerates the config and restarts the service — that's how
288
- you change its port/cwd/engine. `--engine` / `--host` / `--agent-dir` are baked into the service
289
- automatically; env-only vars (`PI_WEB_TOKEN`, `PI_WEB_DSH_*`) must be added to the service config
290
- by hand. See the [start flags table](#quick-start) above.
291
-
292
- ```bash
293
- pi-web-ui server install --engine dsh --port 9000 --cwd /path/to/project
294
- ```
295
-
296
-
297
- ## Plugins
298
-
299
- Plugins are optional UI components (extra top-bar tabs backed by their own
300
- client view, optionally with a server-side entry and agent tools). They live in
301
- your **data-dir plugins folder** (`<dataDir>/plugins/<id>/`, default
302
- `~/.pi-web/plugins/`) — a plugin is simply a directory containing
303
- `manifest.json`, an optional server entry (`index.mjs`) and an optional view
304
- entry (`client/entry.mjs`). No plugin directories = no plugins, nothing shows
305
- up in the UI.
306
-
307
- ### Plugin catalog
308
-
309
- These plugins ship in this repository (`plugins/<id>/`) and can be installed
310
- straight from GitHub:
311
-
312
- | Plugin | What it does |
313
- | --- | --- |
314
- | 📬 [webmail](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail) | IMAP 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. |
315
- | 🗄️ [db-client](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/db-client) | Database 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. |
316
- | 📝 [vscode-editor](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/vscode-editor) | VS 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`. |
317
- | 📬 [demo-mailbox](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/demo-mailbox) | Minimal 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. |
318
-
319
- Example — install the webmail plugin:
320
-
321
- ```bash
322
- pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail
323
- ```
324
-
325
- You can also install from inside the UI: **设置 界面插件 插件市场** lists
326
- maintainable plugins (the same set, shipped in `plugins/catalog.json`) with
327
- full **安装 / 更新 / 卸载** controls (updates keep `config.json`), and lets you
328
- drop any third-party plugin into the list via **添加插件** (paste `owner/repo`
329
- or an `owner/repo/subdir` source) your additions are stored in
330
- `<dataDir>/plugin-catalog.json`. A plugin author can contribute to the built-in
331
- list with a one-line PR to `plugins/catalog.json`.
332
-
333
- Each plugin's directory in the repo has its own `README.md` with full feature
334
- lists, configuration and per-plugin caveats.
335
-
336
- ### Installing
337
-
338
- From GitHub (any of these source forms):
339
-
340
- ```bash
341
- pi-web-ui install owner/repo # shorthand
342
- pi-web-ui install https://github.com/owner/repo # full URL (.git optional)
343
- pi-web-ui install https://github.com/o/r/tree/dev/sub/dir # branch + subdirectory inside the repo
344
- pi-web-ui install owner/repo#v1.2 # pin a branch/tag (#suffix works on any form above)
345
- pi-web-ui install /path/to/plugin-dir # local directory (for development)
346
- ```
347
-
348
- Useful options:
349
-
350
- - `--name <id>` — custom plugin id / directory name (defaults to the repo or
351
- subdirectory name; letters/digits/`-`/`_` only).
352
- - `--force` overwrite an existing installation. Your plugin's local
353
- `config.json` (credentials etc.) is preserved across upgrades.
354
- - `--data-dir <dir>` — override the data dir (default `~/.pi-web`).
355
-
356
- The CLI clones the repo (shallow; falls back to a tarball download without
357
- git), locates the `manifest.json` (including inside subdirectories) and copies
358
- the plugin into `<dataDir>/plugins/<id>/`.
359
-
360
- **No git? No network?** You can also just copy a plugin directory into
361
- `~/.pi-web/plugins/` by hand — same result.
362
-
363
- ### Updating
364
-
365
- Re-run `install` against the same source with `--force`:
366
-
367
- ```bash
368
- # example: update the webmail plugin to the latest version in the repo
369
- pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail --force
370
- ```
371
-
372
- - The upgrade preserves the plugin's local `config.json` automatically.
373
- - Plugins that store other local state inside their directory (e.g. db-client's
374
- `db-connections.json`, vscode-editor's `ssh-hosts.json`) are **not** covered
375
- by that preservation back those up before a forced reinstall.
376
- - Refresh the browser afterwards; no server restart needed.
377
-
378
- ### Activating
379
-
380
- If the server is running, just **refresh the browser** — new plugins are picked
381
- up on attach without a restart. If it isn't, they load on next start. Each
382
- plugin appears as a tab (🧩 or its own icon) in the top bar.
383
-
384
- ### Listing / disabling / uninstalling
385
-
386
- ```bash
387
- pi-web-ui plugins # list installed plugins (id / name / version / description)
388
- pi-web-ui uninstall <id> # remove a plugin
389
- ```
390
-
391
- - To temporarily hide a plugin without uninstalling, use the **Settings panel
392
- (⚙) → UI plugins** switches — stored per client, purely visual, no restart
393
- needed. Re-enable any time.
394
- - `uninstall` deletes the plugin directory; refresh the browser and its tab
395
- disappears. Plugin configuration written inside the plugin dir is removed
396
- too back up `<dataDir>/plugins/<id>/config.json` first if you need it.
397
-
398
-
399
- ## Themes
400
-
401
- 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`.
402
-
403
- 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`.
404
-
405
- ### Using a theme
406
-
407
- 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.
408
-
409
- ### Providing a theme locally (no GitHub needed)
410
-
411
- Any CSS file dropped into your **data-dir themes folder** shows up in the theme menu automatically — no restart, no rebuild:
412
-
413
- 1. Find your data dir (default `~/.pi-web`, override with `PI_WEB_DATA_DIR`).
414
- 2. Create `<dataDir>/themes/` and drop your stylesheet in: e.g. `~/.pi-web/themes/my-theme.css`.
415
- 3. Reload the page and pick it in the top bar. The **file name (without `.css`)** is the theme id shown in the menu.
416
-
417
- ```
418
- ~/.pi-web/
419
- └── themes/
420
- └── my-theme.css # appears in the menu as "my-theme"
421
- ```
422
-
423
- 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:
424
-
425
- - 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`).
426
- - 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).
427
- - Theme ids must match `^[A-Za-z0-9_-]+$` (no dots/slashes — path-traversal guard on the server).
428
-
429
- ### Contributing a theme to the repository (GitHub)
430
-
431
- Want your theme shipped to everyone? Open a pull request at [github.com/xing-shuyin/pi-web-ui](https://github.com/xing-shuyin/pi-web-ui):
432
-
433
- 1. Fork the repo and clone it.
434
- 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.
435
- 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).
436
- 4. Regenerate all built-in themes with `node make-light-theme.mjs` when you changed the variable list in `styles.css`.
437
- 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.
438
-
439
- 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.
440
-
441
-
442
- ## Security
443
-
444
- - **Loopback-only by default** — the server binds `127.0.0.1` and is not
445
- reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
446
- (e.g. LAN access, Docker port mapping — the compose file sets it for you).
447
- - **WebSocket origin check** browser pages connecting to `/ws` must present
448
- an `Origin` whose hostname **and port** match the request `Host`;
449
- cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
450
- are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
451
- reverse-proxy setups.
452
- - **Quiesce** — `server quiesce` refuses new prompts/forks/session resumes
453
- until you `server unquiesce`; in-flight runs finish cleanly (useful before
454
- upgrades/backups).
455
- - **Credentials stay server-side** — provider `headers` (which may carry
456
- `Authorization` / API keys) are never sent to the browser; the model
457
- management UI edits everything else and the server preserves the headers.
458
-
459
-
460
- ## Reverse proxy (nginx)
461
-
462
- Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a
463
- same-machine reverse proxy is the supported remote-access path no
464
- `PI_WEB_HOST=0.0.0.0` needed):
465
-
466
- ```nginx
467
- # pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
468
- server {
469
- listen 443 ssl;
470
- server_name your-host;
471
- # ssl_certificate ... / ssl_certificate_key ...
472
-
473
- # App entry at a sub-path (strips the /pi/ prefix)
474
- location /pi/ {
475
- proxy_pass http://127.0.0.1:8787/;
476
- proxy_http_version 1.1;
477
- # $http_host keeps the port — the server's origin check compares the
478
- # full authority (hostname AND port). $host would drop it and get 403.
479
- proxy_set_header Host $http_host;
480
- proxy_set_header X-Real-IP $remote_addr;
481
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
482
- proxy_set_header X-Forwarded-Proto $scheme;
483
- }
484
-
485
- # WebSocket MUST forward Host identically or the upgrade is 403'd
486
- # (page loads, but chat/terminal keep reconnecting)
487
- location /ws {
488
- proxy_pass http://127.0.0.1:8787;
489
- proxy_http_version 1.1;
490
- proxy_set_header Host $http_host;
491
- proxy_set_header Upgrade $http_upgrade;
492
- proxy_set_header Connection "upgrade";
493
- proxy_read_timeout 3600s;
494
- proxy_send_timeout 3600s;
495
- }
496
-
497
- # Absolute-path assets/API the built frontend requests (root, not /pi/)
498
- location /assets/ { proxy_pass http://127.0.0.1:8787; }
499
- location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
500
- location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
501
- location = /api/file { proxy_pass http://127.0.0.1:8787; }
502
- location = /api/health { proxy_pass http://127.0.0.1:8787; }
503
- }
504
- ```
505
-
506
- Key points:
507
-
508
- - **`Host` must be `$http_host`** (keeps the port) on both `/pi/` and `/ws` —
509
- the origin check compares hostname **and** port. `proxy_set_header Host $host`
510
- or leaving it unset (defaults to the upstream `127.0.0.1:8787`) both fail with 403.
511
- - **Same-origin works automatically**: as long as the browser's `Origin` equals
512
- the forwarded `Host` (it does through a plain proxy), no
513
- `PI_WEB_ALLOW_ORIGINS` is needed. Only set it when the browser origin differs
514
- from the Host the server sees (e.g. a TLS-terminating proxy that changes the
515
- port).
516
- - **No `proxy_protocol` unless you really need real client IPs**: it makes
517
- nginx reject every connection that does not send a PROXY header, which
518
- breaks direct LAN access and any non-frp clients. With frp, drop
519
- `transport.proxyProtocolVersion` from the proxy config unless nginx listens
520
- with `proxy_protocol` too.
521
- - **LAN access without a proxy**: just set `PI_WEB_HOST=0.0.0.0` (and a
522
- firewall rule) or put the whole server block above on port 80/443.
523
-
524
- Full working example (with an frp tunnel): `deploy/nginx-subpath.conf`.
525
-
526
-
527
- ## Contribute
528
-
529
- 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`. ❤️
530
-
531
- | Way to contribute | How to get started |
532
- | --- | --- |
533
- | 🧩 **Write a plugin** | Build 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](#plugin-catalog) or [publish it standalone](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins). |
534
- | 🎨 **Contribute a theme** | Copy `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](#contributing-a-theme-to-the-repository-github). |
535
- | 💻 **Fix a bug / add a feature** | Look for [open issues](https://github.com/xing-shuyin/pi-web-ui/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`). |
536
- | 📖 **Docs & translations** | Improve the READMEs, write plugin docs, fix typos, or help translate the UI / docs into more languages. |
537
- | 💡 **Ideas & feedback** | Open an [issue](https://github.com/xing-shuyin/pi-web-ui/issues) or start a [discussion](https://github.com/xing-shuyin/pi-web-ui/discussions) — feature requests, bug reports, UI polish ideas, deployment experience reports. |
538
-
539
- **Before opening a PR**, a quick sanity pass keeps reviewers happy:
540
-
541
- - `npm run format` — prettier formatting (tabs, width 120; CI checks it).
542
- - `npm run lint` — oxlint (unused vars, risky patterns; CI runs it).
543
- - `npm run check:protocol` + `npm test` protocol sync and unit tests.
544
- - `npm run typecheck` — no type errors.
545
- - `npm run build` both frontend and backend compile.
546
- - 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`).
547
-
548
- > Enjoying pi-web-ui? Give the repo a it helps others find it. And if you
549
- > built something cool on top (plugin, theme, deployment recipe), tell us we
550
- > love showcasing community work.
551
-
552
-
553
- ## License
554
-
1
+ <div align="center">
2
+
3
+ # 💬 pi-web-ui
4
+
5
+ **English** | [简体中文](https://github.com/xing-shuyin/pi-web-ui/blob/main/README.zh-CN.md)
6
+
7
+ *The polished browser cockpit for the [pi coding agent](https://pi.dev).*
8
+
9
+ <p>
10
+ <a href="https://www.npmjs.com/package/pi-web-ui"><img src="https://img.shields.io/npm/v/pi-web-ui?color=cb3837&logo=npm&label=pi-web-ui" alt="npm version"></a>
11
+ <a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/pi-web-ui?logo=node.js&logoColor=white" alt="Node.js"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/xing-shuyin/pi-web-ui" alt="License"></a>
13
+ <a href="https://www.npmjs.com/package/pi-web-ui"><img src="https://img.shields.io/npm/dm/pi-web-ui?label=downloads" alt="npm downloads"></a>
14
+ <a href="https://github.com/xing-shuyin/pi-web-ui/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/xing-shuyin/pi-web-ui/ci.yml?branch=main&label=CI" alt="CI status"></a>
15
+ <a href="https://github.com/xing-shuyin/pi-web-ui/stargazers"><img src="https://img.shields.io/github/stars/xing-shuyin/pi-web-ui?style=social" alt="GitHub stars"></a>
16
+ <a href="https://github.com/xing-shuyin/pi-web-ui/fork"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat" alt="PRs welcome"></a>
17
+ </p>
18
+
19
+ Stream conversations, inspect tool calls, manage files, and run your workspace — all from one place.
20
+
21
+ ![Chat with prompt templates](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/chat-prompts.jpeg)
22
+
23
+ </div>
24
+
25
+ A web chat interface for the [pi coding agent](https://pi.dev). The agent runs
26
+ **in-process** via the pi SDK and streams events to the browser over WebSocket:
27
+ thinking blocks, tool calls, file trees, a built-in terminal, model management,
28
+ theme switching, and a full settings panel — tuned for daily development.
29
+
30
+ > **Requirements** — Node.js ≥ 22.19 and a configured pi install.
31
+
32
+ ## More from the author
33
+
34
+ > **Building with DSH?**
35
+ >
36
+ > [**dsh-ui-tools**](https://github.com/xing-shuyin/dsh-ui-tools) is the author's companion project for building and extending UI tools in the DSH ecosystem.
37
+
38
+ ## ✨ Highlights
39
+
40
+ | 💬 **Chat that works like you do** | 🖼️ **Files & images** | 🧩 **Extensible by design** | 🔒 **Private by default** |
41
+ | --- | --- | --- | --- |
42
+ | 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. |
43
+
44
+ ## 📚 Table of Contents
45
+
46
+ - 🚀 [Features](#features)
47
+ - 🖼️ [Screenshots](#screenshots)
48
+ - 📦 [Install](#install)
49
+ - ⚡ [Quick start](#quick-start)
50
+ - 🖥️ [System service](#system-service)
51
+ - 🧩 [Plugins](#plugins)
52
+ - 🎨 [Themes](#themes)
53
+ - 🔒 [Security](#security)
54
+ - 🌐 [Reverse proxy (nginx)](#reverse-proxy-nginx)
55
+ - 🤝 [Contribute](#contribute)
56
+ - 📄 [License](#license)
57
+
58
+ ## Features
59
+
60
+ ### 💬 Chat
61
+
62
+ - **Streaming agent chat over WebSocket** — the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
63
+ - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
64
+ - **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).
65
+ - **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).
66
+ - **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.
67
+ - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
68
+ - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
69
+ - Question navigation — a floating rail plus per-question tags to jump between questions.
70
+ - **Prompt templates** — the empty chat state shows a one-click template gallery (repo init, code review, research, merge conflicts…); click a card to fill the input, or save the current draft as your own template.
71
+ - **Auto-retry on model errors** — configurable retry count per conversation; when retries run out the failed turn is marked red with a one-click Retry button.
72
+
73
+ ### 🤖 Subagents & templates
74
+
75
+ - **First-party subagents** — spawn independent background conversations for parallel exploration / implementation / review (`subagent_spawn`, with optional `model` override or a template's model); collect results without polling via `subagent_wait_all` (blocks until every subagent finishes, then summarizes results/errors). Manage them like a chat right in the left panel: view live output, inject follow-ups (steer), abort, dismiss — failed runs surface a red dot in the running list and an error notice in the main chat. In-memory sessions — they never touch the history / resume list, and can be nested.
76
+ - **Subagent templates** — configure reusable presets in Settings → Subagent templates: a role system prompt (append or replace), skills & extensions whitelists, and an optional per-template model. The AI picks one via the `subagent_templates` tool and `subagent_spawn(template="…")`, or spawns without one (default = follow the main conversation's current model, or the global default subagent model set in the same panel). Disabled templates stay in the panel for re-enabling but become invisible to the AI tools (can't be listed or picked). Templates are shared globally across browser clients (`<dataDir>/subagent-templates.json`). Six built-in templates (review / implement / research / scout / audit / delegate, adapted from the pi-subagents community projects) seed the list on first run — marked 「Built-in」, editable and deletable like any other.
77
+
78
+ ### 🖼️ Files, images & attachments
79
+
80
+ - Three attachment modes: `inline` (≤12 KB), `reference` (path only), `lines` (selected ranges) over-limit ones degrade automatically.
81
+ - Paste / drag-drop / upload imagesresized client-side and sent as image content when the model supports vision (warning otherwise).
82
+ - **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).
83
+ - Attach arbitrary files without a workspace path stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
84
+ - 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.
85
+ - Live file tree — the server watches the listed directory (`fs.watch`) and re-lists on change; oversized directories show a truncation warning.
86
+
87
+ ### 🖥️ Terminal & Git
88
+
89
+ - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
90
+ - **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.
91
+
92
+ ### 🎛️ Models & settings
93
+
94
+ - 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](#themes) for how to add your own or contribute one.
95
+ - Model management — edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
96
+ - Thinking level per model (only the levels the model actually supports are shown).
97
+ - First-run setup wizard.
98
+ - Settings panel — system prompt (append or replace), input history & quick phrases, shared markers (todo / notify), toggle skills/extensions on/off with immediate effect, save/apply/delete settings presets, vision-bridge model & switch, and subagent templates.
99
+
100
+ ### 🎯 Goal mode
101
+
102
+ - Goal barset a target with a review model, max rounds and a lock switch.
103
+ - Goal wizard (**AI Refine**) — turns a raw request into a concrete goal through a guided questionnaire.
104
+ - 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).
105
+
106
+ ### 🤖 DeepSeek Harness engine
107
+
108
+ - **Switchable engine** — `PI_WEB_ENGINE=pi|dsh` (default `pi`). The pi engine runs the agent in-process via the pi SDK; the **DSH engine** runs the official [`@deepseek-ai/dsh`](https://github.com/deepseek-ai/dsh) (DeepSeek Harness) runtime as a subprocess. `/api/health` reports `engine`; the footer shows a DSH badge.
109
+ - **Same wire protocol** — the DSH engine implements the same WebSocket protocol, so goal mode, SCM, background tasks, settings, plugins, terminals, message-delta & snapshots all work identically.
110
+ - **Native goal machinery** — DSH's own goal state machine + round-driver auto-continues rounds; the model judges completion/blocked (no separate review session). The goal wizard drives it via the model's `ask_user_question`.
111
+ - **Real image blocks** — photos are sent as true image content to vision-capable DeepSeek models (e.g. `deepseek-v4-flash-vision-exp`); text-only models get a text-transcription bridge instead.
112
+ - **Question dialog** — the model's `ask_user_question` surfaces as a browser dialog (single/multi-select + free text) with queueing and a countdown.
113
+ - **Tools & MCP bridge** — plugin AI tools and external MCP servers (`mcp.json`) are bridged into the DSH runtime, so the DSH model can call them (executed server-side).
114
+ - **Skill enable/disable** — the DSH skill catalog is exposed in Settings; disabling a skill filters it out of the model's view at runtime.
115
+ - **DSH user patches** — drop `.yml` Cordis patches into `<dataDir>/dsh-patches/` to extend the runtime and reload from Settings.
116
+
117
+ ### ⚙️ Background tasks
118
+
119
+ - Background-task panel servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
120
+ - Tool watchdog — a tool call running over 20 minutes is aborted automatically.
121
+ - **Stop bash command only** — abort a running bash tool without killing the conversation.
122
+
123
+ ### 🛡️ Safety & operations
124
+
125
+ - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
126
+ - WebSocket Origin/Host same-authority check cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
127
+ - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
128
+ - Credentials stay server-side — provider headers are never sent to the browser.
129
+ - Sound alerts, 9 UI languages (Chinese/English built in, plus 8 downloadable packs: German, Spanish, French, Italian, Japanese, Korean, Portuguese, Russian), chat wallpapers, and a recent-projects list (click to switch workspace).
130
+
131
+ ### 🚢 Deploy & update
132
+
133
+ - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
134
+ - In-app self-update — checks the npm registry, installs and auto-restarts the service.
135
+
136
+
137
+ ## Screenshots
138
+
139
+ ![Chat with prompt templates](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/chat-prompts.jpeg)
140
+
141
+ *Chat with prompt templates*
142
+
143
+ ![Run trajectory timeline](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/trajectory.jpeg)
144
+
145
+ *Run trajectory timeline (run-trace plugin)*
146
+
147
+ ![Settings panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/settings.jpeg)
148
+
149
+ *Settings panel*
150
+
151
+ ![Built-in terminal](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/terminal.jpeg)
152
+
153
+ *Built-in terminal*
154
+
155
+ ![Git source control panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/git.jpeg)
156
+
157
+ *Git source control panel*
158
+
159
+
160
+ ## Install
161
+
162
+ ```bash
163
+ npm i -g pi-web-ui # global install (recommended)
164
+ npx pi-web-ui # or run without installing (latest, starts on :8787)
165
+ npm i -g . # or install the local checkout
166
+ ```
167
+
168
+ **npm 12?** npm 12+ blocks dependency install scripts by default (you'll see
169
+ `npm warn install-scripts blocked`). node-pty is a native module, so allow its
170
+ script (the other two packages it lists are harmless no-ops — allowing them just
171
+ silences the warning):
172
+
173
+ ```bash
174
+ npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
175
+ ```
176
+
177
+ ### Termux (Android)
178
+
179
+ pi-web-ui runs fine on Android via [Termux](https://termux.dev), but `node-pty`
180
+ (the native dependency) needs a toolchain, and Android has a few quirks worth
181
+ knowing:
182
+
183
+ 1. **Install the build toolchain first** — `node-pty` needs Python and a C
184
+ toolchain:
185
+
186
+ ```bash
187
+ pkg install python clang make binutils
188
+ ```
189
+
190
+ 2. **Point the node-pty build at a dummy NDK path.** On Android, gyp fails with
191
+ `Undefined variable android_ndk_path` unless the variable is defined:
192
+
193
+ ```bash
194
+ GYP_DEFINES="android_ndk_path=' '" npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
195
+ ```
196
+
197
+ 3. **If `pi-web-ui` won't execute after install** (the exec bit and/or shebang
198
+ can get mangled on Android): restore it:
199
+
200
+ ```bash
201
+ chmod +x "$(command -v pi-web-ui)"
202
+ sed -i 's/\r$//' "$(command -v pi-web-ui)"
203
+ ```
204
+
205
+ 4. **Run it in the background** with `--no-browser` (there is no desktop
206
+ browser to auto-open):
207
+
208
+ ```bash
209
+ setsid nohup pi-web-ui --no-browser --cwd /path/to/workspace >~/pi-web.log 2>&1 &
210
+ ```
211
+
212
+ `setsid` detaches the server from the launching shell's process group, so
213
+ closing the Termux session doesn't take the server down — `nohup` alone is
214
+ not enough when the parent process group gets killed.
215
+
216
+ The `[control] socket error: EACCES …/.pi-web/pi-web-ui.sock` warning at startup
217
+ is harmless on Android: `pi-web-ui server stop/restart` won't work over the
218
+ control socket, but the web UI itself is unaffected.
219
+
220
+ ## Quick start
221
+
222
+ **Start (foreground)**
223
+
224
+ ```bash
225
+ pi-web-ui # foreground, http://localhost:8787
226
+ ```
227
+
228
+ **Start flags & environment variables** every setting can be passed as a `--flag` on the command
229
+ line **or** set as an environment variable (flag wins). Pick whichever you prefer:
230
+
231
+ | Flag | Env var | Default | Purpose |
232
+ | --- | --- | --- | --- |
233
+ | `--port <n>` | `PI_WEB_PORT` | `8787` | HTTP port |
234
+ | `--cwd <dir>` | `PI_WEB_CWD` | current dir | workspace root (read/write/terminal) |
235
+ | `--data-dir <dir>` | `PI_WEB_DATA_DIR` | `~/.pi-web` | data dir (sessions, plugins, uploads) |
236
+ | `--engine <pi\|dsh>` | `PI_WEB_ENGINE` | `pi` | agent engine; `--engine dsh` = DeepSeek Harness |
237
+ | `--host <addr>` | `PI_WEB_HOST` | `127.0.0.1` | listen address (`0.0.0.0` for LAN/Docker) |
238
+ | `--agent-dir <dir>` | `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models, skills) |
239
+ | _env only_ | `PI_WEB_TOKEN` | empty | optional shared auth token |
240
+ | _env only_ | `PI_WEB_DSH_*` | — | dsh runtime, patches & debug settings |
241
+
242
+ The two are equivalent — pick one:
243
+
244
+ ```bash
245
+ pi-web-ui --engine dsh --port 9000 --cwd /path/to/project
246
+ PI_WEB_ENGINE=dsh PI_WEB_PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui
247
+ ```
248
+
249
+ For the DSH engine also install the runtime (`npm i -g @deepseek-ai/dsh@0.1.1-rc.2`) and set a
250
+ DeepSeek API key (read from `~/.pi/agent/auth.json`, set in the provider/API-key panel).
251
+
252
+ **Stop**
253
+
254
+ - **Foreground**: press `Ctrl+C` in the terminal running it.
255
+ - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
256
+
257
+ **Update**
258
+
259
+ ```bash
260
+ npm i -g pi-web-ui@latest # upgrade to the latest published version
261
+ pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
262
+ ```
263
+
264
+ **Uninstall**
265
+
266
+ ```bash
267
+ npm uninstall -g pi-web-ui
268
+ ```
269
+
270
+ Uninstalling does **not** delete your chats — session data lives in
271
+ `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
272
+
273
+
274
+ ## System service
275
+
276
+ ```bash
277
+ pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
278
+ pi-web-ui server status # running? auto-start?
279
+ pi-web-ui server restart # restart (applies config/version changes)
280
+ pi-web-ui server stop # stop (auto-start stays)
281
+ pi-web-ui server start # start again
282
+ pi-web-ui server uninstall # remove the service entirely
283
+ pi-web-ui server shortcut # desktop one-click launch icon
284
+ pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
285
+ pi-web-ui server unquiesce # reopen admission
286
+ ```
287
+
288
+ `server status` also shows live stats via a local control socket (version,
289
+ PID, quiesce state, connected browsers, running conversations) the same
290
+ socket drives `quiesce`/`unquiesce`.
291
+
292
+ - **macOS** → launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
293
+ - **Linux** systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
294
+ - **Windows** → Task Scheduler logon task (hidden PowerShell window, no black console)
295
+
296
+ Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
297
+ `--engine <pi|dsh>`, `--host`, `--agent-dir`, `--name` (custom service name). Rerunning
298
+ `server install` with new options regenerates the config and restarts the service — that's how
299
+ you change its port/cwd/engine. `--engine` / `--host` / `--agent-dir` are baked into the service
300
+ automatically; env-only vars (`PI_WEB_TOKEN`, `PI_WEB_DSH_*`) must be added to the service config
301
+ by hand. See the [start flags table](#quick-start) above.
302
+
303
+ ```bash
304
+ pi-web-ui server install --engine dsh --port 9000 --cwd /path/to/project
305
+ ```
306
+
307
+
308
+ ## Plugins
309
+
310
+ Plugins are optional UI components (extra top-bar tabs backed by their own
311
+ client view, optionally with a server-side entry and agent tools). They live in
312
+ your **data-dir plugins folder** (`<dataDir>/plugins/<id>/`, default
313
+ `~/.pi-web/plugins/`) a plugin is simply a directory containing
314
+ `manifest.json`, an optional server entry (`index.mjs`) and an optional view
315
+ entry (`client/entry.mjs`). No plugin directories = no plugins, nothing shows
316
+ up in the UI.
317
+
318
+ ### Plugin catalog
319
+
320
+ These plugins ship in this repository (`plugins/<id>/`) and can be installed
321
+ straight from GitHub:
322
+
323
+ | Plugin | What it does |
324
+ | --- | --- |
325
+ | 📬 [webmail](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail) | IMAP 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. |
326
+ | 🗄️ [db-client](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/db-client) | Database 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. |
327
+ | 📝 [vscode-editor](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/vscode-editor) | VS 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`. |
328
+ | 📊 [mermaid](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/mermaid) | Renders ` ```mermaid ` fences in chat messages as SVG diagrams (fenced-code renderer plugin, offline-first local engine). |
329
+ | 🧭 [run-trace](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/run-trace) | Run trajectory: task thinking → tools → file changes → result timeline with replay and node details. |
330
+
331
+ `plugins/demo-mailbox` stays in the repo as the minimal plugin template (server entry + client view + two-way message protocol) and test fixture — start there if you want to write your own.
332
+
333
+ Example install the webmail plugin:
334
+
335
+ ```bash
336
+ pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail
337
+ ```
338
+
339
+ You can also install from inside the UI: **设置 → 界面插件 → 插件市场** lists
340
+ maintainable plugins (the same set, shipped in `plugins/catalog.json`) with
341
+ full **安装 / 更新 / 卸载** controls (updates keep `config.json`), and lets you
342
+ drop any third-party plugin into the list via **添加插件** (paste `owner/repo`
343
+ or an `owner/repo/subdir` source) your additions are stored in
344
+ `<dataDir>/plugin-catalog.json`. A plugin author can contribute to the built-in
345
+ list with a one-line PR to `plugins/catalog.json`.
346
+
347
+ Each plugin's directory in the repo has its own `README.md` with full feature
348
+ lists, configuration and per-plugin caveats.
349
+
350
+ ### Installing
351
+
352
+ From GitHub (any of these source forms):
353
+
354
+ ```bash
355
+ pi-web-ui install owner/repo # shorthand
356
+ pi-web-ui install https://github.com/owner/repo # full URL (.git optional)
357
+ pi-web-ui install https://github.com/o/r/tree/dev/sub/dir # branch + subdirectory inside the repo
358
+ pi-web-ui install owner/repo#v1.2 # pin a branch/tag (#suffix works on any form above)
359
+ pi-web-ui install /path/to/plugin-dir # local directory (for development)
360
+ ```
361
+
362
+ Useful options:
363
+
364
+ - `--name <id>` — custom plugin id / directory name (defaults to the repo or
365
+ subdirectory name; letters/digits/`-`/`_` only).
366
+ - `--force` — overwrite an existing installation. Your plugin's local
367
+ `config.json` (credentials etc.) is preserved across upgrades.
368
+ - `--data-dir <dir>` override the data dir (default `~/.pi-web`).
369
+
370
+ The CLI clones the repo (shallow; falls back to a tarball download without
371
+ git), locates the `manifest.json` (including inside subdirectories) and copies
372
+ the plugin into `<dataDir>/plugins/<id>/`.
373
+
374
+ **No git? No network?** You can also just copy a plugin directory into
375
+ `~/.pi-web/plugins/` by handsame result.
376
+
377
+ ### Updating
378
+
379
+ Re-run `install` against the same source with `--force`:
380
+
381
+ ```bash
382
+ # example: update the webmail plugin to the latest version in the repo
383
+ pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/webmail --force
384
+ ```
385
+
386
+ - The upgrade preserves the plugin's local `config.json` automatically.
387
+ - Plugins that store other local state inside their directory (e.g. db-client's
388
+ `db-connections.json`, vscode-editor's `ssh-hosts.json`) are **not** covered
389
+ by that preservation — back those up before a forced reinstall.
390
+ - Refresh the browser afterwards; no server restart needed.
391
+
392
+ ### Activating
393
+
394
+ If the server is running, just **refresh the browser** new plugins are picked
395
+ up on attach without a restart. If it isn't, they load on next start. Each
396
+ plugin appears as a tab (🧩 or its own icon) in the top bar.
397
+
398
+ ### Listing / disabling / uninstalling
399
+
400
+ ```bash
401
+ pi-web-ui plugins # list installed plugins (id / name / version / description)
402
+ pi-web-ui uninstall <id> # remove a plugin
403
+ ```
404
+
405
+ - To temporarily hide a plugin without uninstalling, use the **Settings panel
406
+ (⚙) → UI plugins** switches — stored per client, purely visual, no restart
407
+ needed. Re-enable any time.
408
+ - `uninstall` deletes the plugin directory; refresh the browser and its tab
409
+ disappears. Plugin configuration written inside the plugin dir is removed
410
+ too — back up `<dataDir>/plugins/<id>/config.json` first if you need it.
411
+
412
+
413
+ ## Themes
414
+
415
+ 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`.
416
+
417
+ Built-in themes ship in the npm package (`themes/`): `white` (light), `cyberpunk` / `dazzle` (dark), and `translucent` / `transparent` (wallpaper-friendly, pair with a chat wallpaper). The theme picker lives in the top bar (🌞 icon); the current choice is stored per browser in `localStorage`.
418
+
419
+ ### Using a theme
420
+
421
+ 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.
422
+
423
+ ### Providing a theme locally (no GitHub needed)
424
+
425
+ Any CSS file dropped into your **data-dir themes folder** shows up in the theme menu automatically no restart, no rebuild:
426
+
427
+ 1. Find your data dir (default `~/.pi-web`, override with `PI_WEB_DATA_DIR`).
428
+ 2. Create `<dataDir>/themes/` and drop your stylesheet in: e.g. `~/.pi-web/themes/my-theme.css`.
429
+ 3. Reload the page and pick it in the top bar. The **file name (without `.css`)** is the theme id shown in the menu.
430
+
431
+ ```
432
+ ~/.pi-web/
433
+ └── themes/
434
+ └── my-theme.css # appears in the menu as "my-theme"
435
+ ```
436
+
437
+ 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:
438
+
439
+ - 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`).
440
+ - 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).
441
+ - Theme ids must match `^[A-Za-z0-9_-]+$` (no dots/slashes — path-traversal guard on the server).
442
+
443
+ ### Contributing a theme to the repository (GitHub)
444
+
445
+ Want your theme shipped to everyone? Open a pull request at [github.com/xing-shuyin/pi-web-ui](https://github.com/xing-shuyin/pi-web-ui):
446
+
447
+ 1. Fork the repo and clone it.
448
+ 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.
449
+ 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).
450
+ 4. Regenerate all built-in themes with `node make-light-theme.mjs` when you changed the variable list in `styles.css`.
451
+ 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.
452
+
453
+ 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.
454
+
455
+
456
+ ## Security
457
+
458
+ - **Loopback-only by default** — the server binds `127.0.0.1` and is not
459
+ reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
460
+ (e.g. LAN access, Docker port mapping — the compose file sets it for you).
461
+ - **WebSocket origin check** — browser pages connecting to `/ws` must present
462
+ an `Origin` whose hostname **and port** match the request `Host`;
463
+ cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
464
+ are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
465
+ reverse-proxy setups.
466
+ - **Quiesce** — `server quiesce` refuses new prompts/forks/session resumes
467
+ until you `server unquiesce`; in-flight runs finish cleanly (useful before
468
+ upgrades/backups).
469
+ - **Credentials stay server-side** — provider `headers` (which may carry
470
+ `Authorization` / API keys) are never sent to the browser; the model
471
+ management UI edits everything else and the server preserves the headers.
472
+
473
+
474
+ ## Reverse proxy (nginx)
475
+
476
+ Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a
477
+ same-machine reverse proxy is the supported remote-access path no
478
+ `PI_WEB_HOST=0.0.0.0` needed):
479
+
480
+ ```nginx
481
+ # pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
482
+ server {
483
+ listen 443 ssl;
484
+ server_name your-host;
485
+ # ssl_certificate ... / ssl_certificate_key ...
486
+
487
+ # App entry at a sub-path (strips the /pi/ prefix)
488
+ location /pi/ {
489
+ proxy_pass http://127.0.0.1:8787/;
490
+ proxy_http_version 1.1;
491
+ # $http_host keeps the port — the server's origin check compares the
492
+ # full authority (hostname AND port). $host would drop it and get 403.
493
+ proxy_set_header Host $http_host;
494
+ proxy_set_header X-Real-IP $remote_addr;
495
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
496
+ proxy_set_header X-Forwarded-Proto $scheme;
497
+ }
498
+
499
+ # WebSocket MUST forward Host identically or the upgrade is 403'd
500
+ # (page loads, but chat/terminal keep reconnecting)
501
+ location /ws {
502
+ proxy_pass http://127.0.0.1:8787;
503
+ proxy_http_version 1.1;
504
+ proxy_set_header Host $http_host;
505
+ proxy_set_header Upgrade $http_upgrade;
506
+ proxy_set_header Connection "upgrade";
507
+ proxy_read_timeout 3600s;
508
+ proxy_send_timeout 3600s;
509
+ }
510
+
511
+ # Absolute-path assets/API the built frontend requests (root, not /pi/)
512
+ location /assets/ { proxy_pass http://127.0.0.1:8787; }
513
+ location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
514
+ location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
515
+ location = /api/file { proxy_pass http://127.0.0.1:8787; }
516
+ location = /api/health { proxy_pass http://127.0.0.1:8787; }
517
+ }
518
+ ```
519
+
520
+ Key points:
521
+
522
+ - **`Host` must be `$http_host`** (keeps the port) on both `/pi/` and `/ws`
523
+ the origin check compares hostname **and** port. `proxy_set_header Host $host`
524
+ or leaving it unset (defaults to the upstream `127.0.0.1:8787`) both fail with 403.
525
+ - **Same-origin works automatically**: as long as the browser's `Origin` equals
526
+ the forwarded `Host` (it does through a plain proxy), no
527
+ `PI_WEB_ALLOW_ORIGINS` is needed. Only set it when the browser origin differs
528
+ from the Host the server sees (e.g. a TLS-terminating proxy that changes the
529
+ port).
530
+ - **No `proxy_protocol` unless you really need real client IPs**: it makes
531
+ nginx reject every connection that does not send a PROXY header, which
532
+ breaks direct LAN access and any non-frp clients. With frp, drop
533
+ `transport.proxyProtocolVersion` from the proxy config unless nginx listens
534
+ with `proxy_protocol` too.
535
+ - **LAN access without a proxy**: just set `PI_WEB_HOST=0.0.0.0` (and a
536
+ firewall rule) or put the whole server block above on port 80/443.
537
+
538
+ Full working example (with an frp tunnel): `deploy/nginx-subpath.conf`.
539
+
540
+
541
+ ## Contribute
542
+
543
+ 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`. ❤️
544
+
545
+ | Way to contribute | How to get started |
546
+ | --- | --- |
547
+ | 🧩 **Write a plugin** | Build 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](#plugin-catalog) or [publish it standalone](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins). |
548
+ | 🎨 **Contribute a theme** | Copy `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 PRfull walkthrough in [Contributing a theme](#contributing-a-theme-to-the-repository-github). |
549
+ | 💻 **Fix a bug / add a feature** | Look for [open issues](https://github.com/xing-shuyin/pi-web-ui/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`). |
550
+ | 📖 **Docs & translations** | Improve the READMEs, write plugin docs, fix typos, or help translate the UI / docs into more languages. |
551
+ | 💡 **Ideas & feedback** | Open an [issue](https://github.com/xing-shuyin/pi-web-ui/issues) or start a [discussion](https://github.com/xing-shuyin/pi-web-ui/discussions) — feature requests, bug reports, UI polish ideas, deployment experience reports. |
552
+
553
+ **Before opening a PR**, a quick sanity pass keeps reviewers happy:
554
+
555
+ - `npm run format` — prettier formatting (tabs, width 120; CI checks it).
556
+ - `npm run lint` — oxlint (unused vars, risky patterns; CI runs it).
557
+ - `npm run check:protocol` + `npm test` — protocol sync and unit tests.
558
+ - `npm run typecheck` — no type errors.
559
+ - `npm run build` — both frontend and backend compile.
560
+ - 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`).
561
+
562
+ > Enjoying pi-web-ui? Give the repo a ⭐ — it helps others find it. And if you
563
+ > built something cool on top (plugin, theme, deployment recipe), tell us — we
564
+ > love showcasing community work.
565
+
566
+
567
+ ## License
568
+
555
569
  [MIT](LICENSE)