pi-web-ui 0.44.0 → 0.44.2

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,388 +1,388 @@
1
- # pi-web-ui
2
-
3
- **English** | [简体中文](https://github.com/xing-shuyin/pi-web-ui/blob/main/README.zh-CN.md)
4
-
5
- [![npm version](https://img.shields.io/npm/v/pi-web-ui?color=cb3837&logo=npm)](https://www.npmjs.com/package/pi-web-ui)
6
- [![Node.js](https://img.shields.io/node/v/pi-web-ui?logo=node.js&logoColor=white)](https://nodejs.org/)
7
- [![License](https://img.shields.io/github/license/xing-shuyin/pi-web-ui)](LICENSE)
8
-
9
- > A polished browser interface for pi: stream conversations, inspect tools, manage files,
10
- > and run your workspace from one place.
11
-
12
- A web chat interface for the [pi coding agent](https://pi.dev) — the agent runs
13
- in-process via the pi SDK and streams events to the browser over WebSocket. Chat
14
- with thinking blocks and tool calls, attach files, ask about images, use a
15
- built-in terminal, manage models, tweak the system prompt, toggle skills and
16
- extensions on/off, and save/apply settings presets — all from a settings panel.
17
- Requires Node.js ≥ 22.19 and a configured pi install.
18
-
19
- ## More from the author
20
-
21
- > **Building with DSH?**
22
- >
23
- > [**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.
24
-
25
- ## Features
26
-
27
- **Chat**
28
-
29
- - Streaming agent chat over WebSocket — the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
30
- - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
31
- - **补充 (steer)** — 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).
32
- - **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).
33
- - **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.
34
- - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
35
- - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
36
- - Question navigation — a floating rail plus per-question tags to jump between questions.
37
-
38
- **Files, images & attachments**
39
-
40
- - Three attachment modes: `inline` (≤12 KB), `reference` (path only), `lines` (selected ranges) — over-limit ones degrade automatically.
41
- - Paste / drag-drop / upload images — resized client-side and sent as image content when the model supports vision (warning otherwise).
42
- - **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).
43
- - Attach arbitrary files without a workspace path — stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
44
- - 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.
45
- - Live file tree — the server watches the listed directory (fs.watch) and re-lists on change; oversized directories show a truncation warning.
46
-
47
- **Terminal & Git**
48
-
49
- - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
50
- - **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.
51
-
52
- **Models & settings**
53
-
54
- - Theme switching — pick a theme in the top bar; each theme is a full standalone stylesheet (default dark + a bundled light). See [Themes](#themes) for how to add your own or contribute one.
55
- - Model management — edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
56
- - Thinking level per model (only the levels the model actually supports are shown).
57
- - First-run setup wizard.
58
- - 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.
59
-
60
- **Goal mode**
61
-
62
- - Goal bar — set a target with a review model, max rounds and a lock switch.
63
- - Goal wizard ("AI 提炼") — turns a raw request into a concrete goal through a guided questionnaire.
64
- - 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).
65
-
66
- **Background tasks**
67
-
68
- - Background-task panel — servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
69
- - Tool watchdog — a tool call running over 20 minutes is aborted automatically.
70
- - **Stop bash command only** — abort a running bash tool without killing the conversation.
71
-
72
- **Safety & operations**
73
-
74
- - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
75
- - WebSocket Origin/Host same-authority check — cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
76
- - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
77
- - Credentials stay server-side — provider headers are never sent to the browser.
78
- - Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).
79
-
80
- **Deploy & update**
81
-
82
- - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
83
- - In-app self-update — checks the npm registry, installs and auto-restarts the service.
84
-
85
- ## Screenshots
86
-
87
- ![Settings panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot1.png)
88
-
89
- ![Built-in terminal](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot2.jpeg)
90
-
91
- ![Chat interface](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot3.jpeg)
92
-
93
- ![Git source control panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot4.jpeg)
94
-
95
- ## Install
96
-
97
- ```bash
98
- npm i -g pi-web-ui # global install (recommended)
99
- npx pi-web-ui # or run without installing (latest, starts on :8787)
100
- npm i -g . # or install the local checkout
101
- ```
102
-
103
- **npm ≥ 12?** npm 12+ blocks dependency install scripts by default (you'll see
104
- `npm warn install-scripts … blocked`). node-pty is a native module, so allow its
105
- script (the other two packages it lists are harmless no-ops — allowing them just
106
- silences the warning):
107
-
108
- ```bash
109
- npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
110
- ```
111
-
112
- ## Start
113
-
114
- ```bash
115
- pi-web-ui # foreground, http://localhost:8787
116
- PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
117
- ```
118
-
119
- ## Stop
120
-
121
- - **Foreground**: press `Ctrl+C` in the terminal running it.
122
- - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
123
-
124
- ## Update
125
-
126
- ```bash
127
- npm i -g pi-web-ui@latest # upgrade to the latest published version
128
- pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
129
- ```
130
-
131
- ## Uninstall
132
-
133
- ```bash
134
- npm uninstall -g pi-web-ui
135
- ```
136
-
137
- Uninstalling does **not** delete your chats — session data lives in
138
- `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
139
-
140
- ## Run as a system service (auto-start on boot)
141
-
142
- ```bash
143
- pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
144
- pi-web-ui server status # running? auto-start?
145
- pi-web-ui server restart # restart (applies config/version changes)
146
- pi-web-ui server stop # stop (auto-start stays)
147
- pi-web-ui server start # start again
148
- pi-web-ui server uninstall # remove the service entirely
149
- pi-web-ui server shortcut # desktop one-click launch icon
150
- pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
151
- pi-web-ui server unquiesce # reopen admission
152
- ```
153
-
154
- `server status` also shows live stats via a local control socket (version,
155
- PID, quiesce state, connected browsers, running conversations) — the same
156
- socket drives `quiesce`/`unquiesce`.
157
-
158
- - **macOS** → launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
159
- - **Linux** → systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
160
- - **Windows** → Task Scheduler logon task (hidden PowerShell window, no black console)
161
-
162
- Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
163
- `--name` (custom service name). Rerunning `server install` with new options
164
- regenerates the config and restarts the service — that's how you change its
165
- port/cwd.
166
-
167
- ## Plugins (UI extensions)
168
-
169
- Plugins are optional UI components (extra top-bar tabs backed by their own
170
- client view, optionally with a server-side entry and agent tools). They live in
171
- your **data-dir plugins folder** (`<dataDir>/plugins/<id>/`, default
172
- `~/.pi-web/plugins/`) — a plugin is simply a directory containing
173
- `manifest.json`, an optional server entry (`index.mjs`) and an optional view
174
- entry (`client/entry.mjs`). No plugin directories = no plugins, nothing shows
175
- up in the UI.
176
-
177
- ### Plugin catalog
178
-
179
- These plugins ship in this repository (`dev/plugins/<id>/`) and can be installed
180
- straight from GitHub:
181
-
182
- | Plugin | What it does |
183
- | --- | --- |
184
- | 📬 [webmail](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
185
- | 🗄️ [db-client](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
186
- | 📝 [vscode-editor](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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`. |
187
- | 📬 [demo-mailbox](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
188
-
189
- Example — install the webmail plugin:
190
-
191
- ```bash
192
- pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/plugins/webmail
193
- ```
194
-
195
- Each plugin's directory in the repo has its own `README.md` with full feature
196
- lists, configuration and per-plugin caveats.
197
-
198
- ### Installing
199
-
200
- From GitHub (any of these source forms):
201
-
202
- ```bash
203
- pi-web-ui install owner/repo # shorthand
204
- pi-web-ui install https://github.com/owner/repo # full URL (.git optional)
205
- pi-web-ui install https://github.com/o/r/tree/dev/sub/dir # branch + subdirectory inside the repo
206
- pi-web-ui install owner/repo#v1.2 # pin a branch/tag (#suffix works on any form above)
207
- pi-web-ui install /path/to/plugin-dir # local directory (for development)
208
- ```
209
-
210
- Useful options:
211
-
212
- - `--name <id>` — custom plugin id / directory name (defaults to the repo or
213
- subdirectory name; letters/digits/`-`/`_` only).
214
- - `--force` — overwrite an existing installation. Your plugin's local
215
- `config.json` (credentials etc.) is preserved across upgrades.
216
- - `--data-dir <dir>` — override the data dir (default `~/.pi-web`).
217
-
218
- The CLI clones the repo (shallow; falls back to a tarball download without
219
- git), locates the `manifest.json` (including inside subdirectories) and copies
220
- the plugin into `<dataDir>/plugins/<id>/`.
221
-
222
- **No git? No network?** You can also just copy a plugin directory into
223
- `~/.pi-web/plugins/` by hand — same result.
224
-
225
- ### Updating
226
-
227
- Re-run `install` against the same source with `--force`:
228
-
229
- ```bash
230
- # example: update the webmail plugin to the latest version in the repo
231
- pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/plugins/webmail --force
232
- ```
233
-
234
- - The upgrade preserves the plugin's local `config.json` automatically.
235
- - Plugins that store other local state inside their directory (e.g. db-client's
236
- `db-connections.json`, vscode-editor's `ssh-hosts.json`) are **not** covered
237
- by that preservation — back those up before a forced reinstall.
238
- - Refresh the browser afterwards; no server restart needed.
239
-
240
- ### Activating
241
-
242
- If the server is running, just **refresh the browser** — new plugins are picked
243
- up on attach without a restart. If it isn't, they load on next start. Each
244
- plugin appears as a tab (🧩 or its own icon) in the top bar.
245
-
246
- ### Listing / disabling / uninstalling
247
-
248
- ```bash
249
- pi-web-ui plugins # list installed plugins (id / name / version / description)
250
- pi-web-ui uninstall <id> # remove a plugin
251
- ```
252
-
253
- - To temporarily hide a plugin without uninstalling, use the **Settings panel
254
- (⚙) → UI plugins** switches — stored per client, purely visual, no restart
255
- needed. Re-enable any time.
256
- - `uninstall` deletes the plugin directory; refresh the browser and its tab
257
- disappears. Plugin configuration written inside the plugin dir is removed
258
- too — back up `<dataDir>/plugins/<id>/config.json` first if you need it.
259
-
260
- ## Themes
261
-
262
- Each theme is a **complete standalone stylesheet** — a full copy of the bundled dark `web/src/styles.css` with a different palette (no CSS-variable extraction, no base file to include). Picking a theme swaps the whole file, so any theme works with every build.
263
-
264
- 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`.
265
-
266
- ### Using a theme
267
-
268
- 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.
269
-
270
- ### Providing a theme locally (no GitHub needed)
271
-
272
- Any CSS file dropped into your **data-dir themes folder** shows up in the theme menu automatically — no restart, no rebuild:
273
-
274
- 1. Find your data dir (default `~/.pi-web`, override with `PI_WEB_DATA_DIR`).
275
- 2. Create `<dataDir>/themes/` and drop your stylesheet in: e.g. `~/.pi-web/themes/my-theme.css`.
276
- 3. Reload the page and pick it in the top bar. The **file name (without `.css`)** is the theme id shown in the menu.
277
-
278
- ```
279
- ~/.pi-web/
280
- └── themes/
281
- └── my-theme.css # appears in the menu as "my-theme"
282
- ```
283
-
284
- Easiest way to write one: copy `themes/light.css` (or the bundled dark `web/src/styles.css` from the source repo) and change the `:root` colors plus any hardcoded values — the file must be **self-contained**. Notes:
285
-
286
- - 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` and the light values in `themes/light.css`).
287
- - Syntax-highlight colors (`highlight.js`'s `github-dark.css` is bundled) must be overridden in your theme file or code will be unreadable — see the `.hljs` overrides at the bottom of `themes/light.css` for the pattern.
288
- - Theme ids must match `^[A-Za-z0-9_-]+$` (no dots/slashes — path-traversal guard on the server).
289
-
290
- ### Contributing a theme to the repository (GitHub)
291
-
292
- 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):
293
-
294
- 1. Fork the repo and clone it.
295
- 2. Create your theme as `themes/<id>.css` — a **self-contained** stylesheet. Copy `themes/light.css` as the starting template (it's the generator output for a full standalone theme).
296
- 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).
297
- 4. If you only changed colors in `styles.css` and want the bundled light theme updated too, regenerate it with `node make-light-theme.mjs`.
298
- 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.
299
-
300
- Rules for merged themes: the file must be a single self-contained CSS file, be a full standalone theme (no imports of the base `styles.css`), set the `--term-*` variables for a readable terminal, and override `.hljs` syntax colors for readable code.
301
-
302
- ## Security
303
-
304
- - **Loopback-only by default** — the server binds `127.0.0.1` and is not
305
- reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
306
- (e.g. LAN access, Docker port mapping — the compose file sets it for you).
307
- - **WebSocket origin check** — browser pages connecting to `/ws` must present
308
- an `Origin` whose hostname **and port** match the request `Host`;
309
- cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
310
- are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
311
- reverse-proxy setups.
312
- - **Quiesce** — `server quiesce` refuses new prompts/forks/session resumes
313
- until you `server unquiesce`; in-flight runs finish cleanly (useful before
314
- upgrades/backups).
315
- - **Credentials stay server-side** — provider `headers` (which may carry
316
- `Authorization` / API keys) are never sent to the browser; the model
317
- management UI edits everything else and the server preserves the headers.
318
-
319
- ## Reverse proxy (nginx)
320
-
321
- Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a
322
- same-machine reverse proxy is the supported remote-access path — no
323
- `PI_WEB_HOST=0.0.0.0` needed):
324
-
325
- ```nginx
326
- # pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
327
- server {
328
- listen 443 ssl;
329
- server_name your-host;
330
- # ssl_certificate ... / ssl_certificate_key ...
331
-
332
- # App entry at a sub-path (strips the /pi/ prefix)
333
- location /pi/ {
334
- proxy_pass http://127.0.0.1:8787/;
335
- proxy_http_version 1.1;
336
- # $http_host keeps the port — the server's origin check compares the
337
- # full authority (hostname AND port). $host would drop it and get 403.
338
- proxy_set_header Host $http_host;
339
- proxy_set_header X-Real-IP $remote_addr;
340
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
341
- proxy_set_header X-Forwarded-Proto $scheme;
342
- }
343
-
344
- # WebSocket — MUST forward Host identically or the upgrade is 403'd
345
- # (page loads, but chat/terminal keep reconnecting)
346
- location /ws {
347
- proxy_pass http://127.0.0.1:8787;
348
- proxy_http_version 1.1;
349
- proxy_set_header Host $http_host;
350
- proxy_set_header Upgrade $http_upgrade;
351
- proxy_set_header Connection "upgrade";
352
- proxy_read_timeout 3600s;
353
- proxy_send_timeout 3600s;
354
- }
355
-
356
- # Absolute-path assets/API the built frontend requests (root, not /pi/)
357
- location /assets/ { proxy_pass http://127.0.0.1:8787; }
358
- location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
359
- location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
360
- location = /api/file { proxy_pass http://127.0.0.1:8787; }
361
- location = /api/health { proxy_pass http://127.0.0.1:8787; }
362
- }
363
- ```
364
-
365
- Key points:
366
-
367
- - **`Host` must be `$http_host`** (keeps the port) on both `/pi/` and `/ws` —
368
- the origin check compares hostname **and** port. `proxy_set_header Host $host`
369
- or leaving it unset (defaults to the upstream `127.0.0.1:8787`) both fail with 403.
370
- - **Same-origin works automatically**: as long as the browser's `Origin` equals
371
- the forwarded `Host` (it does through a plain proxy), no
372
- `PI_WEB_ALLOW_ORIGINS` is needed. Only set it when the browser origin differs
373
- from the Host the server sees (e.g. a TLS-terminating proxy that changes the
374
- port).
375
- - **No `proxy_protocol` unless you really need real client IPs**: it makes
376
- nginx reject every connection that does not send a PROXY header, which
377
- breaks direct LAN access and any non-frp clients. With frp, drop
378
- `transport.proxyProtocolVersion` from the proxy config unless nginx listens
379
- with `proxy_protocol` too.
380
- - **LAN access without a proxy**: just set `PI_WEB_HOST=0.0.0.0` (and a
381
- firewall rule) — or put the whole server block above on port 80/443.
382
-
383
- Full working example (with an frp tunnel): `deploy/nginx-subpath.conf`.
384
-
385
- ## License
386
-
387
- MIT
388
-
1
+ # pi-web-ui
2
+
3
+ **English** | [简体中文](https://github.com/xing-shuyin/pi-web-ui/blob/main/README.zh-CN.md)
4
+
5
+ [![npm version](https://img.shields.io/npm/v/pi-web-ui?color=cb3837&logo=npm)](https://www.npmjs.com/package/pi-web-ui)
6
+ [![Node.js](https://img.shields.io/node/v/pi-web-ui?logo=node.js&logoColor=white)](https://nodejs.org/)
7
+ [![License](https://img.shields.io/github/license/xing-shuyin/pi-web-ui)](LICENSE)
8
+
9
+ > A polished browser interface for pi: stream conversations, inspect tools, manage files,
10
+ > and run your workspace from one place.
11
+
12
+ A web chat interface for the [pi coding agent](https://pi.dev) — the agent runs
13
+ in-process via the pi SDK and streams events to the browser over WebSocket. Chat
14
+ with thinking blocks and tool calls, attach files, ask about images, use a
15
+ built-in terminal, manage models, tweak the system prompt, toggle skills and
16
+ extensions on/off, and save/apply settings presets — all from a settings panel.
17
+ Requires Node.js ≥ 22.19 and a configured pi install.
18
+
19
+ ## More from the author
20
+
21
+ > **Building with DSH?**
22
+ >
23
+ > [**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.
24
+
25
+ ## Features
26
+
27
+ **Chat**
28
+
29
+ - Streaming agent chat over WebSocket — the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
30
+ - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
31
+ - **补充 (steer)** — 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).
32
+ - **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).
33
+ - **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.
34
+ - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
35
+ - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
36
+ - Question navigation — a floating rail plus per-question tags to jump between questions.
37
+
38
+ **Files, images & attachments**
39
+
40
+ - Three attachment modes: `inline` (≤12 KB), `reference` (path only), `lines` (selected ranges) — over-limit ones degrade automatically.
41
+ - Paste / drag-drop / upload images — resized client-side and sent as image content when the model supports vision (warning otherwise).
42
+ - **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).
43
+ - Attach arbitrary files without a workspace path — stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
44
+ - 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.
45
+ - Live file tree — the server watches the listed directory (fs.watch) and re-lists on change; oversized directories show a truncation warning.
46
+
47
+ **Terminal & Git**
48
+
49
+ - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
50
+ - **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.
51
+
52
+ **Models & settings**
53
+
54
+ - Theme switching — pick a theme in the top bar; each theme is a full standalone stylesheet (default dark + a bundled light). See [Themes](#themes) for how to add your own or contribute one.
55
+ - Model management — edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
56
+ - Thinking level per model (only the levels the model actually supports are shown).
57
+ - First-run setup wizard.
58
+ - 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.
59
+
60
+ **Goal mode**
61
+
62
+ - Goal bar — set a target with a review model, max rounds and a lock switch.
63
+ - Goal wizard ("AI 提炼") — turns a raw request into a concrete goal through a guided questionnaire.
64
+ - 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).
65
+
66
+ **Background tasks**
67
+
68
+ - Background-task panel — servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
69
+ - Tool watchdog — a tool call running over 20 minutes is aborted automatically.
70
+ - **Stop bash command only** — abort a running bash tool without killing the conversation.
71
+
72
+ **Safety & operations**
73
+
74
+ - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
75
+ - WebSocket Origin/Host same-authority check — cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
76
+ - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
77
+ - Credentials stay server-side — provider headers are never sent to the browser.
78
+ - Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).
79
+
80
+ **Deploy & update**
81
+
82
+ - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
83
+ - In-app self-update — checks the npm registry, installs and auto-restarts the service.
84
+
85
+ ## Screenshots
86
+
87
+ ![Settings panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot1.png)
88
+
89
+ ![Built-in terminal](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot2.jpeg)
90
+
91
+ ![Chat interface](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot3.jpeg)
92
+
93
+ ![Git source control panel](https://raw.githubusercontent.com/xing-shuyin/pi-web-ui/main/assets/shot4.jpeg)
94
+
95
+ ## Install
96
+
97
+ ```bash
98
+ npm i -g pi-web-ui # global install (recommended)
99
+ npx pi-web-ui # or run without installing (latest, starts on :8787)
100
+ npm i -g . # or install the local checkout
101
+ ```
102
+
103
+ **npm ≥ 12?** npm 12+ blocks dependency install scripts by default (you'll see
104
+ `npm warn install-scripts … blocked`). node-pty is a native module, so allow its
105
+ script (the other two packages it lists are harmless no-ops — allowing them just
106
+ silences the warning):
107
+
108
+ ```bash
109
+ npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
110
+ ```
111
+
112
+ ## Start
113
+
114
+ ```bash
115
+ pi-web-ui # foreground, http://localhost:8787
116
+ PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
117
+ ```
118
+
119
+ ## Stop
120
+
121
+ - **Foreground**: press `Ctrl+C` in the terminal running it.
122
+ - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
123
+
124
+ ## Update
125
+
126
+ ```bash
127
+ npm i -g pi-web-ui@latest # upgrade to the latest published version
128
+ pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
129
+ ```
130
+
131
+ ## Uninstall
132
+
133
+ ```bash
134
+ npm uninstall -g pi-web-ui
135
+ ```
136
+
137
+ Uninstalling does **not** delete your chats — session data lives in
138
+ `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
139
+
140
+ ## Run as a system service (auto-start on boot)
141
+
142
+ ```bash
143
+ pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
144
+ pi-web-ui server status # running? auto-start?
145
+ pi-web-ui server restart # restart (applies config/version changes)
146
+ pi-web-ui server stop # stop (auto-start stays)
147
+ pi-web-ui server start # start again
148
+ pi-web-ui server uninstall # remove the service entirely
149
+ pi-web-ui server shortcut # desktop one-click launch icon
150
+ pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
151
+ pi-web-ui server unquiesce # reopen admission
152
+ ```
153
+
154
+ `server status` also shows live stats via a local control socket (version,
155
+ PID, quiesce state, connected browsers, running conversations) — the same
156
+ socket drives `quiesce`/`unquiesce`.
157
+
158
+ - **macOS** → launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
159
+ - **Linux** → systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
160
+ - **Windows** → Task Scheduler logon task (hidden PowerShell window, no black console)
161
+
162
+ Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
163
+ `--name` (custom service name). Rerunning `server install` with new options
164
+ regenerates the config and restarts the service — that's how you change its
165
+ port/cwd.
166
+
167
+ ## Plugins (UI extensions)
168
+
169
+ Plugins are optional UI components (extra top-bar tabs backed by their own
170
+ client view, optionally with a server-side entry and agent tools). They live in
171
+ your **data-dir plugins folder** (`<dataDir>/plugins/<id>/`, default
172
+ `~/.pi-web/plugins/`) — a plugin is simply a directory containing
173
+ `manifest.json`, an optional server entry (`index.mjs`) and an optional view
174
+ entry (`client/entry.mjs`). No plugin directories = no plugins, nothing shows
175
+ up in the UI.
176
+
177
+ ### Plugin catalog
178
+
179
+ These plugins ship in this repository (`dev/plugins/<id>/`) and can be installed
180
+ straight from GitHub:
181
+
182
+ | Plugin | What it does |
183
+ | --- | --- |
184
+ | 📬 [webmail](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
185
+ | 🗄️ [db-client](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
186
+ | 📝 [vscode-editor](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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`. |
187
+ | 📬 [demo-mailbox](https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/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. |
188
+
189
+ Example — install the webmail plugin:
190
+
191
+ ```bash
192
+ pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/plugins/webmail
193
+ ```
194
+
195
+ Each plugin's directory in the repo has its own `README.md` with full feature
196
+ lists, configuration and per-plugin caveats.
197
+
198
+ ### Installing
199
+
200
+ From GitHub (any of these source forms):
201
+
202
+ ```bash
203
+ pi-web-ui install owner/repo # shorthand
204
+ pi-web-ui install https://github.com/owner/repo # full URL (.git optional)
205
+ pi-web-ui install https://github.com/o/r/tree/dev/sub/dir # branch + subdirectory inside the repo
206
+ pi-web-ui install owner/repo#v1.2 # pin a branch/tag (#suffix works on any form above)
207
+ pi-web-ui install /path/to/plugin-dir # local directory (for development)
208
+ ```
209
+
210
+ Useful options:
211
+
212
+ - `--name <id>` — custom plugin id / directory name (defaults to the repo or
213
+ subdirectory name; letters/digits/`-`/`_` only).
214
+ - `--force` — overwrite an existing installation. Your plugin's local
215
+ `config.json` (credentials etc.) is preserved across upgrades.
216
+ - `--data-dir <dir>` — override the data dir (default `~/.pi-web`).
217
+
218
+ The CLI clones the repo (shallow; falls back to a tarball download without
219
+ git), locates the `manifest.json` (including inside subdirectories) and copies
220
+ the plugin into `<dataDir>/plugins/<id>/`.
221
+
222
+ **No git? No network?** You can also just copy a plugin directory into
223
+ `~/.pi-web/plugins/` by hand — same result.
224
+
225
+ ### Updating
226
+
227
+ Re-run `install` against the same source with `--force`:
228
+
229
+ ```bash
230
+ # example: update the webmail plugin to the latest version in the repo
231
+ pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/dev/plugins/webmail --force
232
+ ```
233
+
234
+ - The upgrade preserves the plugin's local `config.json` automatically.
235
+ - Plugins that store other local state inside their directory (e.g. db-client's
236
+ `db-connections.json`, vscode-editor's `ssh-hosts.json`) are **not** covered
237
+ by that preservation — back those up before a forced reinstall.
238
+ - Refresh the browser afterwards; no server restart needed.
239
+
240
+ ### Activating
241
+
242
+ If the server is running, just **refresh the browser** — new plugins are picked
243
+ up on attach without a restart. If it isn't, they load on next start. Each
244
+ plugin appears as a tab (🧩 or its own icon) in the top bar.
245
+
246
+ ### Listing / disabling / uninstalling
247
+
248
+ ```bash
249
+ pi-web-ui plugins # list installed plugins (id / name / version / description)
250
+ pi-web-ui uninstall <id> # remove a plugin
251
+ ```
252
+
253
+ - To temporarily hide a plugin without uninstalling, use the **Settings panel
254
+ (⚙) → UI plugins** switches — stored per client, purely visual, no restart
255
+ needed. Re-enable any time.
256
+ - `uninstall` deletes the plugin directory; refresh the browser and its tab
257
+ disappears. Plugin configuration written inside the plugin dir is removed
258
+ too — back up `<dataDir>/plugins/<id>/config.json` first if you need it.
259
+
260
+ ## Themes
261
+
262
+ Each theme is a **complete standalone stylesheet** — a full copy of the bundled dark `web/src/styles.css` with a different palette (no CSS-variable extraction, no base file to include). Picking a theme swaps the whole file, so any theme works with every build.
263
+
264
+ 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`.
265
+
266
+ ### Using a theme
267
+
268
+ 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.
269
+
270
+ ### Providing a theme locally (no GitHub needed)
271
+
272
+ Any CSS file dropped into your **data-dir themes folder** shows up in the theme menu automatically — no restart, no rebuild:
273
+
274
+ 1. Find your data dir (default `~/.pi-web`, override with `PI_WEB_DATA_DIR`).
275
+ 2. Create `<dataDir>/themes/` and drop your stylesheet in: e.g. `~/.pi-web/themes/my-theme.css`.
276
+ 3. Reload the page and pick it in the top bar. The **file name (without `.css`)** is the theme id shown in the menu.
277
+
278
+ ```
279
+ ~/.pi-web/
280
+ └── themes/
281
+ └── my-theme.css # appears in the menu as "my-theme"
282
+ ```
283
+
284
+ Easiest way to write one: copy `themes/light.css` (or the bundled dark `web/src/styles.css` from the source repo) and change the `:root` colors plus any hardcoded values — the file must be **self-contained**. Notes:
285
+
286
+ - 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` and the light values in `themes/light.css`).
287
+ - Syntax-highlight colors (`highlight.js`'s `github-dark.css` is bundled) must be overridden in your theme file or code will be unreadable — see the `.hljs` overrides at the bottom of `themes/light.css` for the pattern.
288
+ - Theme ids must match `^[A-Za-z0-9_-]+$` (no dots/slashes — path-traversal guard on the server).
289
+
290
+ ### Contributing a theme to the repository (GitHub)
291
+
292
+ 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):
293
+
294
+ 1. Fork the repo and clone it.
295
+ 2. Create your theme as `themes/<id>.css` — a **self-contained** stylesheet. Copy `themes/light.css` as the starting template (it's the generator output for a full standalone theme).
296
+ 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).
297
+ 4. If you only changed colors in `styles.css` and want the bundled light theme updated too, regenerate it with `node make-light-theme.mjs`.
298
+ 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.
299
+
300
+ Rules for merged themes: the file must be a single self-contained CSS file, be a full standalone theme (no imports of the base `styles.css`), set the `--term-*` variables for a readable terminal, and override `.hljs` syntax colors for readable code.
301
+
302
+ ## Security
303
+
304
+ - **Loopback-only by default** — the server binds `127.0.0.1` and is not
305
+ reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
306
+ (e.g. LAN access, Docker port mapping — the compose file sets it for you).
307
+ - **WebSocket origin check** — browser pages connecting to `/ws` must present
308
+ an `Origin` whose hostname **and port** match the request `Host`;
309
+ cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
310
+ are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
311
+ reverse-proxy setups.
312
+ - **Quiesce** — `server quiesce` refuses new prompts/forks/session resumes
313
+ until you `server unquiesce`; in-flight runs finish cleanly (useful before
314
+ upgrades/backups).
315
+ - **Credentials stay server-side** — provider `headers` (which may carry
316
+ `Authorization` / API keys) are never sent to the browser; the model
317
+ management UI edits everything else and the server preserves the headers.
318
+
319
+ ## Reverse proxy (nginx)
320
+
321
+ Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a
322
+ same-machine reverse proxy is the supported remote-access path — no
323
+ `PI_WEB_HOST=0.0.0.0` needed):
324
+
325
+ ```nginx
326
+ # pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
327
+ server {
328
+ listen 443 ssl;
329
+ server_name your-host;
330
+ # ssl_certificate ... / ssl_certificate_key ...
331
+
332
+ # App entry at a sub-path (strips the /pi/ prefix)
333
+ location /pi/ {
334
+ proxy_pass http://127.0.0.1:8787/;
335
+ proxy_http_version 1.1;
336
+ # $http_host keeps the port — the server's origin check compares the
337
+ # full authority (hostname AND port). $host would drop it and get 403.
338
+ proxy_set_header Host $http_host;
339
+ proxy_set_header X-Real-IP $remote_addr;
340
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
341
+ proxy_set_header X-Forwarded-Proto $scheme;
342
+ }
343
+
344
+ # WebSocket — MUST forward Host identically or the upgrade is 403'd
345
+ # (page loads, but chat/terminal keep reconnecting)
346
+ location /ws {
347
+ proxy_pass http://127.0.0.1:8787;
348
+ proxy_http_version 1.1;
349
+ proxy_set_header Host $http_host;
350
+ proxy_set_header Upgrade $http_upgrade;
351
+ proxy_set_header Connection "upgrade";
352
+ proxy_read_timeout 3600s;
353
+ proxy_send_timeout 3600s;
354
+ }
355
+
356
+ # Absolute-path assets/API the built frontend requests (root, not /pi/)
357
+ location /assets/ { proxy_pass http://127.0.0.1:8787; }
358
+ location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
359
+ location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
360
+ location = /api/file { proxy_pass http://127.0.0.1:8787; }
361
+ location = /api/health { proxy_pass http://127.0.0.1:8787; }
362
+ }
363
+ ```
364
+
365
+ Key points:
366
+
367
+ - **`Host` must be `$http_host`** (keeps the port) on both `/pi/` and `/ws` —
368
+ the origin check compares hostname **and** port. `proxy_set_header Host $host`
369
+ or leaving it unset (defaults to the upstream `127.0.0.1:8787`) both fail with 403.
370
+ - **Same-origin works automatically**: as long as the browser's `Origin` equals
371
+ the forwarded `Host` (it does through a plain proxy), no
372
+ `PI_WEB_ALLOW_ORIGINS` is needed. Only set it when the browser origin differs
373
+ from the Host the server sees (e.g. a TLS-terminating proxy that changes the
374
+ port).
375
+ - **No `proxy_protocol` unless you really need real client IPs**: it makes
376
+ nginx reject every connection that does not send a PROXY header, which
377
+ breaks direct LAN access and any non-frp clients. With frp, drop
378
+ `transport.proxyProtocolVersion` from the proxy config unless nginx listens
379
+ with `proxy_protocol` too.
380
+ - **LAN access without a proxy**: just set `PI_WEB_HOST=0.0.0.0` (and a
381
+ firewall rule) — or put the whole server block above on port 80/443.
382
+
383
+ Full working example (with an frp tunnel): `deploy/nginx-subpath.conf`.
384
+
385
+ ## License
386
+
387
+ MIT
388
+