pi-web-ui 0.80.0 → 0.80.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/CHANGELOG.md CHANGED
@@ -12,12 +12,53 @@
12
12
 
13
13
  暂无未发布内容。
14
14
 
15
+ ## [0.80.2] — 2026-09-12
16
+
17
+ ### Added
18
+
19
+ - **新官方插件 legado-web(📖 阅读)**:把 [Legado / 阅读](https://github.com/gedoor/legado) 的读书链路搬进 pi-web-ui——搜索 / 发现 / 详情 / 目录 / 正文,书源 JSON 与安卓版兼容,另带书源导入、废源检测与清理。插件自带内嵌前端需要的一切后端:跨域 + GBK 代理、本地存储(书源 / 书架 / 阅读进度只落数据目录 `<dataDir>/legado-web/`,不写浏览器 localStorage)、静态托管。安装 `pi-web-ui install xing-shuyin/pi-web-ui/plugins/legado-web`(插件市场里也可一键装),刷新后顶栏多一个 📖 tab。
20
+ - **顺带给 AI 配了修源接口**:四个 agent 工具 `legado_rules`(规则速查)/ `legado_book_sources`(读书源文件、只改坏掉的那几个字段)/ `legado_source_probe`(逐步跑链路,回报每步请求、HTTP 状态、用到的规则与失败明细)/ `legado_run_rule`(拿真实页体试一条规则再落盘);阅读页与书源页的「🤖 AI 修复源 / AI 新建书源」按钮把现场直接发给 AI 并开一个新对话(工作目录限定在插件数据目录)。规则引擎跑在 worker 里,同步 JS 规则(`java.ajax` 等)走 SharedArrayBuffer 桥。
21
+ - **插件 → 宿主动作桥 `window.__piWebUiHost`**:插件 client bundle 是裸 ESM,import 不到应用模块,之前只能往宿主发数据;现在也能让主应用**做事**——`setView("chat" | "terminal" | "git" | "plugin:<id>")` 切主视图,`startChat({ prompt, newChat?, cwd? })` 新建对话(可选切工作目录)并把 prompt 作为用户消息发出去。时序上 `startChat` 会串行等「cwd 切过去 → 对话换成新空白」才发 prompt(服务端 `new_chat` 是异步的,紧接着发会落到旧对话),每步都有超时,超时也发、不静默丢。定义见 `web/src/plugin-host.ts`。
22
+
23
+ ### Changed
24
+
25
+ - **升级 SDK `@earendil-works/pi-coding-agent` 0.84.4 → 0.85.1**(上游带来 `@earendil-works/chord`、Anthropic SDK 0.123.0、esbuild 0.28 等);本仓库代码无需跟着改。
26
+ - **README 中英双版按当前实况重写**:功能清单补全(快捷键、Docker、队列撤回、消息构成、项目与会话、搜索与导航、文件树、终端与 Git、模型与设置、Agent 工具与内联标记、声音与通知、PWA、调优用环境变量等章节),中英两边同步并修掉失效锚点。
27
+ - 插件运行期数据 `plugins/*/storage/` 加入 `.gitignore`;legado-web 的上游前端源码与构建产物加入 `.prettierignore`(保持上游风格,不被格式化重排)。
28
+
29
+ ### Fixed
30
+
31
+ - nginx 子路径示例配置删掉 `favicon-streaming.svg` 的那条 `location`:该图标早已不存在,留着只会让人以为得额外补一个文件。
32
+
33
+ <!-- auto-i18n:start -->
34
+ ### i18n
35
+
36
+ - 本版无文案增量(相对 v0.80.1,已核查)。
37
+ <!-- auto-i18n:end -->
38
+
39
+ ## [0.80.1] — 2026-09-12
40
+
41
+ ### Added
42
+
43
+ - **更新面板新增「重启服务」**:由 `pi-web-ui server start|install` 起的实例,更新面板底部多一个按钮,点一下服务就重启(等价于 `pi-web-ui server restart`)——更新完立即生效,不用回终端。服务端 `server/launch-origin.ts` 判定本实例是不是被平台服务托管(launchd / systemd / Windows watchdog),判定结果随 `ready.service` 下发,`pi-web-ui server status` 也会显示启动方式;认不出来(前台 `pi-web-ui`、`npm run dev`、Docker)就不画按钮、也拒绝 `restart_service`——那里没有 supervisor,退出就真的停了。已装好的服务不用重装(运行时靠 `XPC_SERVICE_NAME` / `INVOCATION_ID` / `%APPDATA%\pi-web-ui\<name>.pid` 对比 `process.ppid` 识别),新装的另外烘焙 `PI_WEB_LAUNCHED_BY=service` / `PI_WEB_SERVICE_NAME`。回归:`tests/restart-service-test.mjs`。
44
+
45
+ ### Fixed
46
+
47
+ - **终端接管 bash 修复:没有尾部管道的命令不再报 `Cannot read properties of null (reading 'segment')`(issue #121)**:`date`、`ls | head -5` 这类命令没有「尾部限输出管道」,`detectTrailingLimiter()` 返回 `null`,而 #91 v2 的取值重构把原本的可选链写成了非空断言 `limiter!.segment` —— 结果几乎每条一次性 bash 命令都在取值处直接 TypeError(只有以 `| tail` / `| less` / `| more` / `| cat` 结尾的命令能跑)。现已改回可选链(这几个值只在真的拆掉管道时才被取用)。回归:`tests/unit/terminal-bash-limiter.test.ts`(桩终端钉住取值路径,CI 必跑);`tests/terminal-bash-test.mjs` 同步恢复可跑(动态导入走 `pathToFileURL`,Windows 上也跑得起来;提示文案断言钉死中文;一次性终端退出改为轮询而非固定等待)。
48
+
49
+ <!-- auto-i18n:start -->
50
+ ### i18n
51
+
52
+ - 前端新增 key(3):`restartService`、`restartingService`、`restartServiceTip`
53
+ - 服务端新增 key(2):`terminals.headtail.omitted.below`、`terminals.headtail.omitted.above`
54
+ <!-- auto-i18n:end -->
55
+
15
56
  ## [0.80.0] — 2026-09-12
16
57
 
17
58
  ### Added
18
59
 
19
- - **桌面版(Electron 外壳)**:同一套服务端 + 前端装进一个原生窗口——主进程用随机空闲口起 `dist/server/index.js`(`ELECTRON_RUN_AS_NODE` 当纯 Node 用,不再额外捆一个 Node),`/api/health` 就绪后 `BrowserWindow` 直接加载该地址,因此前端 `appUrl("/ws")`、`server/protocol.ts` 全部零改动。可与网页版并存:不抢 `8787`(`PI_WEB_PORT` 被占用时自动退到随机空闲口)、独立数据目录(`<userData>/data`)、独立单实例锁;外链丢给系统浏览器,renderer 走 `contextIsolation + sandbox` 且无 Node。Windows 安装包(NSIS,可选安装目录)随每个 Release 由 CI 出包并附在 Release 页面(macOS dmg / Linux AppImage 已配好,CI 暂只出 Windows);开发用 `npm run desktop:dev`,本地打包用 `npm run desktop:dist`。
20
- - 当前**未签名**:首次运行 Windows 会提示「未知发布者」(进展见仓库 README 的 Code signing policy 一节)。
60
+ - **桌面版(Electron 外壳)**:同一套服务端 + 前端装进一个原生窗口——主进程用随机空闲口起 `dist/server/index.js`(`ELECTRON_RUN_AS_NODE` 当纯 Node 用,不再额外捆一个 Node),`/api/health` 就绪后 `BrowserWindow` 直接加载该地址,因此前端 `appUrl("/ws")`、`server/protocol.ts` 全部零改动。可与网页版并存:不抢 `8787`(`PI_WEB_PORT` 被占用时自动退到随机空闲口)、独立数据目录(`<userData>/data`)、独立单实例锁;外链丢给系统浏览器,renderer 走 `contextIsolation + sandbox` 且无 Node。WindowsNSIS,可选安装目录)/ macOS(dmg)/ Linux(AppImage)安装包随每个 Release 由 CI 并行出包并附在 Release 页面;开发用 `npm run desktop:dev`,本地打包用 `npm run desktop:dist`。
61
+ - 当前三平台产物都**未签名**:Windows 首启有 SmartScreen「未知发布者」提示,macOS 首次需右键 → 打开(Gatekeeper),进展见仓库 README 的 Code signing policy 一节。
21
62
 
22
63
  ### Changed
23
64
 
@@ -481,7 +522,8 @@
481
522
  - 0.35.1(2026-08-27):编辑重问保留附件(#18)+ 全窗口拖放(#19)。
482
523
  - 0.29.0(2026-08-23):全局搜索弹窗(Ctrl+K)+ 消息列表惰性窗口化。
483
524
 
484
- [Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.79.0...main
525
+ [Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.80.1...main
526
+ [0.80.1]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.80.1
485
527
  [0.80.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.80.0
486
528
  [0.79.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.79.0
487
529
  [0.78.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.78.0
package/README.md CHANGED
@@ -44,15 +44,18 @@ theme switching, and a full settings panel — tuned for daily development.
44
44
  ## 📚 Table of Contents
45
45
 
46
46
  - 🚀 [Features](#features)
47
+ - ⌨️ [Keyboard shortcuts](#keyboard-shortcuts)
47
48
  - 🖼️ [Screenshots](#screenshots)
48
49
  - 📦 [Install](#install)
49
50
  - ⚡ [Quick start](#quick-start)
50
51
  - 🖥️ [System service](#system-service)
52
+ - 🐳 [Docker](#docker)
51
53
  - 🧩 [Plugins](#plugins)
52
54
  - 🎨 [Themes](#themes)
55
+ - 🔧 [Tuning & advanced environment variables](#tuning--advanced-environment-variables)
53
56
  - 🔒 [Security](#security)
54
- - 🪪 [Code signing policy](#code-signing-policy)
55
- - 🔐 [Privacy](#privacy)
57
+ - 🪪 [Code signing policy](#-code-signing-policy)
58
+ - 🔐 [Privacy](#-privacy)
56
59
  - 🌐 [Reverse proxy (nginx)](#reverse-proxy-nginx)
57
60
  - 🤝 [Contribute](#contribute)
58
61
  - 📄 [License](#license)
@@ -64,13 +67,36 @@ theme switching, and a full settings panel — tuned for daily development.
64
67
  - **Streaming agent chat over WebSocket** — the pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
65
68
  - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
66
69
  - **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).
67
- - **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).
70
+ - **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). `/new` takes an optional first prompt (`/new fix the failing test`) and sends it as the new chat's first message.
68
71
  - **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.
69
72
  - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
70
73
  - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
71
74
  - Question navigation — a floating rail plus per-question tags to jump between questions.
72
75
  - **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.
73
- - **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.
76
+ - **Auto-retry on model errors** — configurable retry count per conversation (default 6, `0` = fail immediately); when retries run out the failed turn is marked red with a one-click Retry button.
77
+ - **Queue control** — a queued steer/follow-up bubble can be dropped (✕) or **recalled (↩)**, which pulls its text back into the composer (appended on a new line if you already typed something — it never overwrites your draft).
78
+ - **Message anatomy** — each message header shows the role, the model that produced it and a local `HH:MM` timestamp, and every text block has a copy button. Attachments render as their own collapsible card with a mode chip (`lines` / `ref` / `bridged` / `inline n lines`), a copy button and a vision-bridge “transcribed” note; a skill invocation becomes a skill card with the full `SKILL.md`, next to the arguments you typed.
79
+ - **Compaction, visible** — compacted context shows up as a card (“compacted from N tokens”) that auto-expands and jumps when it arrives, and a live banner counts up (“compacting context · 12s”) naming the trigger (manual / threshold / overflow).
80
+
81
+ ### 🗂️ Projects & sessions
82
+
83
+ - **Switching projects** — the workspace root (what the agent reads/writes and where the terminal starts) changes without a restart:
84
+ - **Bottom-right path in the status bar** — click `📁 <path>` to open the folder picker: type a path (`Tab` completes), `↑` goes up one level, `💻` jumps to the computer root so you can change drives, click a folder to enter it and hit **Select** — or **Select this folder** to take the folder you are browsing. **+ New folder** creates a directory on the spot; `Esc` or a click outside closes it.
85
+ - **Right panel file tree** — right-click any folder → **Open as project** (the same menu has **Upload files to this folder**).
86
+ - **Left panel → Recent projects**, or `/cwd <path>` from the input box (`/cwd` alone reports the current directory).
87
+ - The startup default comes from `--cwd <dir>` / `PI_WEB_CWD`.
88
+ - **Conversations run in parallel** — each conversation has its own agent runtime and keeps streaming after you switch away; up to 8 can be open per project (subagents don't count).
89
+ - **Running list** — grouped by project (the current one first), with subagent children indented under their parent, badges for subagent / error (the tooltip carries the reason) / streaming, inline rename (✎) and a scoped ✕ that offers “dismiss finished subagents only” or “force-dismiss everything” (a second confirmation while a run is streaming). Right-clicking a row scopes the menu to that conversation's subtree.
90
+ - **History** — sessions are read from `<agentDir>/sessions/--<cwd>--/`, i.e. the same transcripts the pi CLI/TUI writes, so the browser and a terminal `pi` session share one list per project. Rename (✎ — the same `session_info` entry pi's `/name` writes) and two-step delete.
91
+ - **Recent projects** — stored per browser, merged with every directory that has transcripts, minus the ones you removed (tombstones) and the ones that no longer exist, sorted by last use (20 shown, 30 stored).
92
+ - **Coming back** — reconnecting restores the last workspace the browser used (with a notice), the tab title can show the project folder, and each project remembers its own model + active provider key for *new* chats (a chat that already has messages keeps its own model).
93
+ - If the server was shut down mid-answer, the next attach reports it once (“last run was interrupted”) instead of leaving a silent gap in the history.
94
+
95
+ ### 🔎 Search & navigation
96
+
97
+ - **Global search (Ctrl/Cmd+K)** — one box, three sources: conversation transcripts (full text, assistant output included; up to 50 hits, each with a jump anchor), recent projects, and workspace file names (bounded walk: 50 results / 20 000 entries / 4 s / depth 24 — it tells you when it truncated instead of hanging). ↑/↓ then Enter to open, Esc to close. Clicking a conversation restores it and jumps to the matching message, a project switches the workspace and re-runs the search, a file opens its preview.
98
+ - **In-conversation search (Ctrl/Cmd+F)** — searches the text actually rendered in the open chat (case-insensitive, highlighted through the CSS Custom Highlight API), Enter / Shift+Enter to step through hits, Esc to close. Messages collapsed by the summary view are indexed from their message data, so long threads stay searchable without force-expanding everything — only the message you land on is expanded.
99
+ - **Long-thread ergonomics** — messages older than 30 collapse into one-line summary rows (with thinking / tool / bash / image counts and a 90-character preview), the question rail lists every question you asked, a “back to bottom” chip appears once you scroll up, the list follows streaming output only while you are pinned to the bottom (a real scroll-up is respected), and messages far outside the viewport are swapped for equal-height placeholders.
74
100
 
75
101
  ### 🤖 Subagents & templates
76
102
 
@@ -85,19 +111,68 @@ theme switching, and a full settings panel — tuned for daily development.
85
111
  - Attach arbitrary files without a workspace path — stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
86
112
  - 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.
87
113
  - Live file tree — the server watches the listed directory (`fs.watch`) and re-lists on change; oversized directories show a truncation warning.
114
+ - **Browse anywhere** — the tree climbs past the workspace root to a 💻 “This computer” level that lists every mounted drive (`/` on POSIX), the breadcrumb jumps straight to any level, `..` goes up, and a listing that vanished or lost its permissions degrades into an empty list plus a warning instead of an error page.
115
+ - **Row actions** — hover a file for download / attach inline (+) / attach as reference (🔗) / copy name / copy path; folders offer reference-attach, copy name and copy path (copying falls back to a hidden textarea on plain-HTTP origins where the clipboard API is unavailable).
116
+ - **Upload from the tree** — right-click a **folder row** → **Upload files to this folder** (that folder's menu also offers **Open as project**), or right-click a file row / the panel body → **Upload files to current directory** (the directory you are browsing). Dragging OS files onto a folder row uploads into exactly that folder (the row highlights), dropping them on the panel uploads into the browsed directory, and dragging a *folder* warns that folders aren't supported instead of doing nothing. Uploads accept one file up to 100 MB, refuse empty files, strip the name to a basename with Windows-illegal characters replaced (200-char clamp), create the target directory if needed, and refresh the listing afterwards even if you are browsing somewhere else.
117
+ - **Listings that stay honest** — on Windows/macOS a recursive watcher on the workspace root refreshes the tree for changes in *any* subdirectory (400 ms debounce), with a 10 s polling fallback — announced once per workspace — on network drives where watching isn't supported; POSIX hides build noise (`node_modules`, `.git`, `dist`, `.venv`, …) and caps at 500 entries, Windows hides only dependency/VCS/data directories and caps at 2000, and both say when they truncated.
118
+ - **The preview is an editor too** — text files can be edited in place and saved with Ctrl/Cmd+S (2 MB cap, dirty-guarded; closing with unsaved changes asks first), Markdown toggles between rendered and source, HTML renders in a sandboxed iframe through a directory-mapped URL so relative CSS/images resolve (with a per-file “enable scripts” opt-in that never grants same-origin), images and videos stream over HTTP Range, binaries get a hex dump, and text gets line numbers, selection by click/drag/Shift (add to chat as `lines`), zoom 50–200 %, a word-wrap toggle and fullscreen.
119
+ - **Download without Safe Browsing fights** — downloads fetch the bytes and use the browser's save picker where available (falling back to a blob link, and to native streaming above 200 MB), sanitize Windows-illegal file names and report a cancelled dialog as “not an error”.
88
120
 
89
121
  ### 🖥️ Terminal & Git
90
122
 
91
- - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
92
- - **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.
123
+ - Built-in terminal (xterm.js + node-pty) with per-client PTY management; on Windows it picks Git Bash, falls back to a bundled busybox download, then `cmd`. Up to 16 live terminals (agent-opened ones don't count), each keeping its own 8000-line scrollback while you switch tabs; tabs can be renamed inline, closed (which really kills the process) and show their exit code in the scrollback.
124
+ - **Saved commands** — the sidebar's upper half is the project's `.pi/commands.json` list (`name` + `command` + `cwd`, `${pwd}` expands to the workspace): click a row to run it (a same-titled tab is reused and restarted, VS Code task style), add/edit/delete entries, and reload the file from disk.
125
+ - **AI bash grouping** — terminals the agent opens through its bash takeover are folded into an “AI bash” group so they don't bury your own tabs.
126
+ - **Terminal-backed bash** (Settings → Tools, off by default) — the agent's `bash` tool then runs inside a visible persistent terminal instead of a hidden process, so shell state (`cd`, venv, ssh) survives between calls; a silence threshold (default 15 s, `0` = wait forever) moves a quiet command to the background, and `head`/`tail` trim what the model has to read.
127
+ - **Liveness detection** — when a terminal the agent is using goes silent while the chat is still streaming, the server steers the AI with the tail of its output (“read it / answer it / close it”) instead of letting the turn hang.
128
+ - **Source control (Git) panel** — status / branch / diff / history / untracked files via a hidden query terminal, plus per-file stage (+) and unstage (−), a commit box (Enter commits, IME-safe) with “Commit all” (`git add -A && git commit`), a branch picker that groups local and remote-tracking refs (picking a remote one creates a local branch tracking it), and detached-HEAD / `↑ahead ↓behind` badges. The “Commit tree” tab loads `git log --graph` with per-commit diffs. Writes (commit / branch switch / push / pull) run in the visible terminal and the view follows them there; the panel refreshes itself when the repository's real git dir changes (worktrees included) and via a 30 s fallback poll, so commits made outside the browser show up by themselves.
93
129
 
94
130
  ### 🎛️ Models & settings
95
131
 
96
132
  - 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.
97
133
  - Model management — edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
98
- - Thinking level per model (only the levels the model actually supports are shown).
99
- - First-run setup wizard.
100
- - Settings panelsystem 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.
134
+ - **Model picker** searchable by name/provider/id, with a provider sidebar once you have several providers; models you pick often float to the top with a “used N×” badge plus reasoning/vision badges, opening it scrolls to the active model, and the footer keeps **Refresh models** and **Manage models**.
135
+ - **Several keys per provider** — built-in providers can store multiple named keys (`<agentDir>/provider-keys.json`): add a second key without losing the first, activate another by name, remove one (dropping the active key promotes the next). The picker lists each key separately, so picking a model under a key switches to it — and only nicknames reach the browser.
136
+ - **Custom providers**add/edit/delete a provider (API type, `baseUrl`, key, optional auth header) with per-model metadata (context window, max output, text/text-image, reasoning); **Fetch models** probes `/models` *server-side* (so a LAN/loopback endpoint works despite CORS) and merges what it advertises, and an existing provider can be re-probed in place. Hand-edited `models.json` is picked up with **Reload models.json** (comments allowed, like the SDK).
137
+ - Thinking level per model — seven levels, but the ones the current model doesn't support are shown disabled rather than silently snapped to another.
138
+ - First-run setup wizard — installs the pi CLI for you when it's missing (with failure detail, Retry and Skip) and then takes a provider + API key so you can start immediately.
139
+ - Settings panel:
140
+ - **System prompt** — a `{{token}}` compose template over 11 sources (soul / tools / guidelines / pi docs / append / persona / terminal / markers / context / skills / cwd) with click-to-append token chips, per-source overrides (an `auto` badge, “seed from default”, per-source reset; environment-derived sources stay read-only), and two viewers showing the prompt actually in effect and the tool schema actually sent to the model.
141
+ - **Input history & quick phrases** — a bounded history (1–500 entries, optional per-entry character cap, two-step clear) that ↑/↓ walks through, and the chips above the composer (edit / reorder / delete / reset to defaults).
142
+ - **Skills** — per-skill switches plus a **Full** chip that injects a whole `SKILL.md` into the prompt instead of its catalog line (8 KB per file, 32 KB total).
143
+ - **Extensions** — per-extension switches, and one-click uninstall for `npm:`-installed ones (runs `pi remove npm:<pkg>` in a reusable terminal tab).
144
+ - **UI plugins**, **goal review**, **vision bridge** and **subagent templates** have their own pages — see [Plugins](#plugins).
145
+ - **Presets** — save the current combination (prompt template/mode/overrides, skill & extension switches, tool switches, terminal-bash settings, retry count, reviewer prompt, skill full-text list) under a name and re-apply or delete it; deliberately *not* captured (questionnaire, goal mode, display prefs, vision bridge, default subagent model, quick phrases) stay as they are.
146
+ - **Apply timing** — tool switches, retry count, display preferences, markers and the skill full-text list apply immediately; the prompt template/overrides and skill/extension switches need a session reload, and a change made mid-answer is deferred with a “takes effect after this reply” notice.
147
+ - **Display preferences** — thinking blocks expanded or collapsed by default, tool cards expanded by default, wide chat column (drops the 860 px cap on very wide viewports), project name in the browser tab title, and a chat wallpaper (image URL or upload, with dim and blur sliders).
148
+
149
+ ### 🧩 Agent tools & inline markers
150
+
151
+ - **Tool switches** — Settings → Tools lists every optional agent tool as its own switch: the 7 terminal tools (default **off**), the 7 `subagent_*` tools (default on), `edit_soft` (default off), `delegate_task`, `ask_user_question` and `todo_list` (default on). Toggling is live (no reload) and the tools stay registered so they can come back; `bash` and the SDK's own `edit`/`read` are deliberately outside the catalog and cannot be disabled.
152
+ - **Inline markers** — instead of a tool round-trip the AI writes state changes straight into its reply: `[[todo:new:<subject>]]` / `[[todo:set:<id>,in_progress]]` / `[[todo:remove:<id>]]` / `[[todo:dep:<id>,blocks=<id>]]` for the task list, `[[notify:<level>:<message>]]` for a non-interruptive notice, and `[[conv:rename:<title>]]` to retitle the chat. Markers are applied as soon as a reply bubble is final, a bad marker comes back as a browser notice, and the task list also renders as a live widget under the file tree (`N/M done` with ✓ / ◐ / ○) that follows the active conversation and survives a reload — it is stored in that conversation's own session branch. Settings → Tools has a master switch plus one switch per marker (these are global, shared by all browsers).
153
+ - **`edit_soft`** — a looser `edit` (default off) for when indentation or whitespace makes the built-in tool fail: exact substring first, then trimmed line-core matching, `newText` written verbatim with the file's line endings/BOM preserved, and a diff + unified patch in the result. It also tolerates sloppy input (a JSON string, a bare object, legacy top-level `oldText`/`newText`).
154
+ - **`delegate_task`** — hands a specialist template a six-section brief (TASK / EXPECTED OUTCOME / REQUIRED TOOLS / MUST DO / MUST NOT DO / CONTEXT) validated on the server: a missing template, a task under 20 characters or any empty section is rejected, and the error tells the model which templates it may use. Cards render the brief as labelled sections, and a finished delegation gets a button that jumps to the subagent's conversation.
155
+ - **`ask_user_question`** — the pi engine has no questionnaire tool, so pi-web-ui adds one: the model asks structured questions (single/multi-select with rich option previews, plus free text) which open as a dialog; answers go back as the tool result, cancelling returns a tool error, waiting for you is exempt from the tool watchdog, and an unanswered questionnaire survives a page refresh or reconnect.
156
+ - **MCP servers** — drop a `<dataDir>/mcp.json` (`{"servers":{"github":{"command":"node","args":["mcp.js"],"cwd":"/x"}}}`) and every tool a stdio MCP server advertises becomes an ordinary agent tool, executed server-side; one server failing to start only logs a line and leaves the others working. The file is read at startup, so restart pi-web-ui after editing it.
157
+ - **Extension UI bridge** — pi extensions can drive the browser: `setWidget` renders live panels under the file tree (click a title to open it centred), `setStatus` puts text in the status bar, `notify` raises a toast, and `select` / `confirm` / `input` open an inline request panel above the composer with Markdown-rendered options (Esc cancels). ANSI colour codes in widget text are stripped, so extension footers don't arrive as escape-sequence noise.
158
+ - **Plugin extras** — plugins can register `/commands` (badge “plugin”, executed server-side without spending tokens), register background tasks with their own stop button, declare a settings form, subscribe to run/tool/conversation events, and reach the host from their client bundle via `window.__piWebUiHost`. See [Plugins](#plugins).
159
+
160
+ ### 🔔 Sounds & notifications
161
+
162
+ - **Sound alerts** — a master switch plus one cue per event (question asked / run finished / run started / error), each with its own preview button, and a volume slider (0–100 %).
163
+ - **Desktop / OS notifications** — off by default; enabling them asks the browser for permission from the click itself and switches back off (persistently) if you deny it. Notifications go through the service worker, so they also work when the installed PWA is in the background, they cover run-finished / question-asked / error, and clicking one brings the app window back to the front. They are suppressed while you are demonstrably watching the page — including the Windows case where the browser still reports focus and visibility while the window is minimised (detected from the native window rectangle instead).
164
+
165
+ ### 📱 PWA & offline
166
+
167
+ - **Installable** — a web-app manifest (standalone window, 192/512/1024 + maskable icons, `./`-relative so sub-path deployments work) means “Install app” in Chrome/Edge or “Add to home screen” on mobile gives you its own window and icon.
168
+ - **Offline app shell** — the service worker serves navigations network-first with a cached shell fallback (the app reopens while your backend is down or restarting) and caches hashed assets cache-first, while never caching `/ws`, `/api`, `/themes` or `/plugins`; a new worker takes over already-open pages immediately.
169
+ - **Refresh prompts when they matter** — a persistent banner appears if the page was loaded from a build whose wire protocol differs from the server's (i.e. right after an update), and the browser tab title can show the current project folder.
170
+
171
+ ### 🌍 Languages & language packs
172
+
173
+ - The top-bar language menu lists every language by its native name, and **Get more languages** opens a manager showing each downloadable pack's version with Download / Remove buttons (and a Refresh button) — packs land in `<dataDir>/locales/`, so you can also drop a valid pack file there by hand for a fully offline install.
174
+ - A first-time visitor without a stored choice gets the language their browser asks for, then the instance default (`PI_WEB_LOCALE`), then English; once you pick one it sticks.
175
+ - The server renders AI-facing text (tool return values, prompt sections, notices) in the same language, so a Chinese UI also gets Chinese answers from tools like `subagent_list`.
101
176
 
102
177
  ### 🎯 Goal mode
103
178
 
@@ -114,27 +189,66 @@ theme switching, and a full settings panel — tuned for daily development.
114
189
  - **Question dialog** — the model's `ask_user_question` surfaces as a browser dialog (single/multi-select + free text) with queueing and a countdown.
115
190
  - **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).
116
191
  - **Skill enable/disable** — the DSH skill catalog is exposed in Settings; disabling a skill filters it out of the model's view at runtime.
117
- - **DSH user patches** — drop `.yml` Cordis patches into `<dataDir>/dsh-patches/` to extend the runtime and reload from Settings.
192
+ - **DSH user patches** — drop `.yml` Cordis patches into `<dataDir>/dsh-patches/` to extend the runtime and reload from Settings; the Settings → Plugins page lists them with size/mtime and the resolved path, and a broken file is skipped with its error on the runtime's stderr.
193
+
194
+ **How DSH differs from the pi engine** (worth knowing before you switch):
195
+
196
+ - Thinking intensity is fixed to `high` — asking for another level answers “DeepSeek V4 only supports high thinking”.
197
+ - The footer's token/cost/context figures come from DeepSeek's published per-million pricing against a 1M-token window.
198
+ - Opening a past session replays it read-only: sending a prompt starts a **new** branch with the old conversation injected as context, because the runtime has no in-place resume. The same applies to edit-&-re-ask.
199
+ - **Stop** kills the runtime process tree, so every running DSH conversation stops (you get a notice), and a half-finished goal is cleared first. Stopping only the bash tool isn't supported.
200
+ - Sessions live in `<dataDir>/dsh-sessions/` (separate from the pi engine's transcripts) and are swept after `PI_WEB_DSH_SESSION_RETENTION_DAYS` (90); open conversations are capped at 8 per project; a crashed runtime is restarted with 1 s/3 s/9 s back-off, at most twice per 60 s, then it stops and points you at the API key and the DSH dependencies.
201
+ - The tool runs in a `workspace-write` sandbox with approvals off — your Stop button is the control. Questionnaires are a one-question-at-a-time wizard with option previews and a countdown.
202
+ - pi-only features answer with an explicit notice (and their UI is hidden) instead of failing silently: session rename, `/compact`, `/reload`, extension hot-reload, subagent templates, custom providers / multiple keys, provider model probing, installing the pi CLI, the vision bridge and the per-tool switches.
118
203
 
119
204
  ### ⚙️ Background tasks
120
205
 
121
- - Background-task panel — servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
122
- - Tool watchdog a tool call running over 20 minutes is aborted automatically.
206
+ - Background-task panel — servers launched by the agent are detected by diffing the listening ports before and after a bash run and listed with port / pid / name / command (click the command to expand it fully); stop one or kill all, and the top-bar chip carries a live count badge.
207
+ - The list belongs to the browser client, not the conversation: it survives project switches, conversation switches and reconnects, and is refreshed server-side every 30 s with processes that exited pruned. Detection deliberately ignores known desktop apps and processes whose parent chain traces back to `explorer` rather than to the server, so a browser you opened yourself isn't reported as “started by the agent”.
208
+ - Plugin-registered tasks show a 🧩 marker and their live status text, and stop through the plugin's own callback (a mail-polling task, for instance).
209
+ - Tool watchdog — a tool call running over 20 minutes is aborted automatically (`PI_WEB_TOOL_TIMEOUT_MS`; questionnaires are exempt).
123
210
  - **Stop bash command only** — abort a running bash tool without killing the conversation.
211
+ - **Stall warning** — if a streaming run goes completely silent for 3 minutes (`PI_WEB_STALL_NOTIFY_MS`, `0` = off) you get a warning naming the conversation, without aborting it.
124
212
 
125
213
  ### 🛡️ Safety & operations
126
214
 
127
215
  - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
128
- - WebSocket Origin/Host same-authority check cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
129
- - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
130
- - Credentials stay server-sideprovider headers are never sent to the browser.
131
- - 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).
216
+ - **Token auth** `PI_WEB_TOKEN` accepts any of `Authorization: Bearer …`, `X-PI-Token: …`, `?token=…` or the `pi_web_token` cookie. A `?token=` link logs you in once, strips the token from the address bar and stores the cookie; every authorised request refreshes it and a stale cookie is expired on the 401 response, so after changing the password one correct `?token=` visit recovers permanently. `/api/health` stays open for probes.
217
+ - WebSocket Origin/Host same-authority check cross-origin pages are rejected (403), `Origin: null` (a `file://` page) is rejected outright, and when a token is configured a bad credential is refused *before* the upgrade; `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
218
+ - **Host allow-list**`PI_WEB_ALLOW_HOSTS=host1,host2` adds a strict hostname allow-list on top of the always-on same-authority check.
219
+ - **Instance scoping** — `PI_WEB_TABS=chat,terminal,git` exposes only those tabs: hidden tabs are also refused *server-side* (their messages answer with an explanation), and `chat` can never be switched off. `PI_WEB_MANAGED=1` declares the instance as deployed from outside: the server refuses self-update, pi-CLI installs and marketplace installs with a reason, and the UI hides those entry points (the version chip becomes a plain label saying the deployment owns updates).
220
+ - **File boundaries** — workspace-relative reads/writes reject `..` escapes (a path outside the workspace is only reachable through explicit absolute / machine browsing); inline `/api/file` streaming is limited to images, video and HTML, so a binary can never be smuggled through an `<img>` tag — anything else needs `?download=1` (attachment disposition). The HTML preview route is always served sandboxed.
221
+ - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`) — refuses new prompts/forks/resumes (and, on the DSH engine, brand-new client connections) while in-flight runs finish.
222
+ - Credentials stay server-side — provider headers (which may carry `Authorization`) are never sent to the browser, and provider API keys reach it only as nicknames.
223
+ - 9 UI languages — Chinese/English built in, plus 8 downloadable packs (German, Spanish, French, Italian, Japanese, Korean, Portuguese, Russian); the top-bar language menu installs or removes packs — see [Languages & language packs](#-languages--language-packs).
224
+ - **Retention** — uploaded files older than `PI_WEB_UPLOAD_RETENTION_DAYS` (14; `0` = never) are swept at startup and every 6 h; DSH sessions have their own 90-day sweep.
225
+ - **Operational watchdogs** — tool timeout, model-stall warning and terminal liveness are all tunable, see [Tuning & advanced environment variables](#tuning--advanced-environment-variables).
132
226
 
133
227
  ### 🚢 Deploy & update
134
228
 
135
- - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
136
- - In-app self-update checks the npm registry, installs and auto-restarts the service.
229
+ - Foreground, global npm install, Docker (see [Docker](#docker)), macOS launchd, Linux systemd, Windows autostart (a per-user `Run` key with a console-free launcher and a crash watchdog), and a desktop shortcut (`server shortcut`).
230
+ - `server install --print` prints the launchd plist / systemd unit / Windows launcher it *would* write and exits, so you can review it before installing.
231
+ - **Update panel** — the version chip shows an amber dot when a newer web UI exists and a badge with how many *other* components have updates. “Check all updates” compares the web UI, the globally installed pi core and the direct packages declared in `<agentDir>/npm/package.json`; each row has its own Update, plus “Update all” and “Re-check all”, and the commands run in a visible terminal (`pi update npm:<name>` for pi extensions — the only command that updates the copy pi actually loads — and `npm i -g <name>@latest` for the rest). A “just published (<30 min)” warning tells you npm's cached metadata may be stale. On an instance owned by launchd/systemd/the Windows watchdog there is also a **Restart service** button; on a foreground instance there isn't, because nothing would bring it back.
232
+ - **Plugin updates from the CLI** — `pi-web-ui plugins --check-updates` compares each installed plugin's recorded commit with the remote HEAD and prints the exact update command; every `install --force` snapshots the outgoing version into `<dataDir>/plugin-backups/` (newest 3 kept, and it auto-rolls back if the copy fails), so `pi-web-ui plugins --rollback <id>` can undo an upgrade.
233
+ - In the pi CLI there is also `/webui` (from the bundled `extensions/webui.ts`): `/webui` starts a server on the first free port from 8787, and `/webui --port 9000`, `--cwd <path>`, `--no-browser`, `status` and `stop` manage it — one subprocess per pi session, killed when the session shuts down so no orphan servers linger.
234
+
235
+
236
+ ## Keyboard shortcuts
137
237
 
238
+ | Keys | What it does |
239
+ | --- | --- |
240
+ | `Enter` | Send. On touch-first devices `Enter` inserts a newline instead and `Ctrl/Cmd+Enter` sends (Windows touch laptops are treated as desktops). |
241
+ | `Shift+Enter` | Newline in the composer. |
242
+ | `↑` / `↓` | Walk the global prompt history (persisted across conversations) when the caret is on the first/last line; `Esc` returns to your draft. |
243
+ | `Ctrl/Cmd+K` | Global search over conversations, projects and workspace file names. |
244
+ | `Ctrl/Cmd+F` | Search inside the open conversation — `Enter` next hit, `Shift+Enter` previous, `Esc` closes. |
245
+ | `/` | Open the slash-command picker (`↑`/`↓` to move, `Tab` or `Enter` to complete, `Esc` to dismiss; typing a space closes it). |
246
+ | `Ctrl/Cmd+S` | Save while editing a file in the preview. |
247
+ | `Ctrl/Cmd+A` | Select all lines in the preview (when the caret isn't in a text field). |
248
+ | `Ctrl/Cmd+Enter` | Submit the edit-&-re-ask editor. |
249
+ | `Ctrl/Cmd+C` / `Ctrl/Cmd+V` | In the terminal: copy the current selection (no selection = `^C` goes to the shell) / paste natively. |
250
+ | `Esc` | Close the preview, a dialog, the command picker, a questionnaire or an extension request panel — with unsaved preview edits it asks first. |
251
+ | Drag & drop | Dropping files anywhere in the window attaches them to the chat; over the file tree it uploads into the folder you dropped on; folders can't be dropped (expand and pick files). |
138
252
 
139
253
  ## Screenshots
140
254
 
@@ -178,20 +292,22 @@ npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
178
292
 
179
293
  ### 🖥️ Desktop app (Windows installer)
180
294
 
181
- Prefer a window over a browser tab? Every release ships a Windows installer built
182
- from this repository by GitHub Actions:
295
+ Prefer a window over a browser tab? Every release ships installers built from
296
+ this repository by GitHub Actions:
183
297
 
184
- **[⬇ Download the latest installer](https://github.com/xing-shuyin/pi-web-ui/releases/latest)**
185
- — `pi-web-ui-desktop Setup <version>.exe`
298
+ **[⬇ Download the latest installers](https://github.com/xing-shuyin/pi-web-ui/releases/latest)**
299
+ Windows (`pi-web-ui-desktop Setup <version>.exe`), macOS (`.dmg`) and Linux
300
+ (`.AppImage`).
186
301
 
187
302
  The desktop shell reuses this very server: it spawns `dist/server/index.js` on a
188
303
  random free loopback port and opens a window pointed at it (see
189
304
  [`desktop/README.md`](desktop/README.md)). The web version is untouched — no
190
305
  fight over port `8787`, separate data directory, both can run side by side.
191
306
 
192
- Currently Windows-only and **not code signed yet** Windows SmartScreen will
193
- show an “unknown publisher” prompt the first time you run it (see the
194
- [code signing policy](#code-signing-policy)). Build it yourself with
307
+ **Nothing is code signed yet**: on Windows SmartScreen shows an “unknown
308
+ publisher” prompt, and on macOS you have to right-click → **Open** the app the
309
+ first time (Gatekeeper is stricter than SmartScreen) see the
310
+ [code signing policy](#-code-signing-policy). Build it locally with
195
311
  `npm run desktop:dist`.
196
312
 
197
313
  ### Termux (Android)
@@ -252,10 +368,11 @@ line **or** set as an environment variable (flag wins). Pick whichever you prefe
252
368
  | --- | --- | --- | --- |
253
369
  | `--port <n>` | `PI_WEB_PORT` | `8787` | HTTP port |
254
370
  | `--cwd <dir>` | `PI_WEB_CWD` | current dir | workspace root (read/write/terminal) |
255
- | `--data-dir <dir>` | `PI_WEB_DATA_DIR` | `~/.pi-web` | data dir (sessions, plugins, uploads) |
371
+ | `--data-dir <dir>` | `PI_WEB_DATA_DIR` | `~/.pi-web` | data dir (UI state, plugins, uploads, themes, locales) |
256
372
  | `--engine <pi\|dsh>` | `PI_WEB_ENGINE` | `pi` | agent engine; `--engine dsh` = DeepSeek Harness |
257
373
  | `--host <addr>` | `PI_WEB_HOST` | `127.0.0.1` | listen address (`0.0.0.0` for LAN/Docker) |
258
- | `--agent-dir <dir>` | `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models, skills) |
374
+ | `--agent-dir <dir>` | `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models.json, sessions, skills) |
375
+ | `--no-browser` | — | off | start without auto-opening the browser |
259
376
  | _env only_ | `PI_WEB_TOKEN` | empty | optional shared auth token |
260
377
  | _env only_ | `PI_WEB_DSH_*` | — | dsh runtime, patches & debug settings |
261
378
 
@@ -272,7 +389,7 @@ DeepSeek API key (read from `~/.pi/agent/auth.json`, set in the provider/API-key
272
389
  **Stop**
273
390
 
274
391
  - **Foreground**: press `Ctrl+C` in the terminal running it.
275
- - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
392
+ - **As a service**: `pi-web-ui server stop`. On **Linux and Windows** auto-start is kept (the next login/boot brings it back, until `server uninstall`); on **macOS** `stop` unloads the launchd agent, so it no longer starts at login — `pi-web-ui server start` brings it back.
276
393
 
277
394
  **Update**
278
395
 
@@ -287,8 +404,7 @@ pi-web-ui server restart # restart the service to apply it (foreground: res
287
404
  npm uninstall -g pi-web-ui
288
405
  ```
289
406
 
290
- Uninstalling does **not** delete your chats — session data lives in
291
- `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
407
+ Uninstalling does **not** delete your chats: the transcripts you see in the history panel live in `<agentDir>/sessions/` (default `~/.pi/agent/sessions/`, per project), and the rest of your state UI settings, recent projects, plugins, uploads, themes, language packs — lives in `<dataDir>` (default `~/.pi-web/`). Both survive uninstall, upgrade and reinstall; rerunning `pi-web-ui server install` afterward picks them up again (and if you plan to delete them, back up `sessions/` and `plugins/` first — an uninstall never touches either).
292
408
 
293
409
 
294
410
  ## System service
@@ -325,6 +441,32 @@ pi-web-ui server install --engine dsh --port 9000 --cwd /path/to/project
325
441
  ```
326
442
 
327
443
 
444
+ ## Docker
445
+
446
+ The image builds the frontend and the server, keeps the compiler toolchain `node-pty` needs, pre-installs the DSH runtime (so `PI_WEB_ENGINE=dsh` works without extra steps), runs as the non-root `node` user and declares `/app/.pi-web` as a volume:
447
+
448
+ ```bash
449
+ docker compose up -d # then open http://localhost:8787
450
+ ```
451
+
452
+ `docker-compose.yml` already sets `PI_WEB_HOST=0.0.0.0` (required for port mapping) and persists the data dir in the named volume `pi-web-data`. The commented blocks in that file cover the usual container tweaks — switching to the DSH engine, mounting a `dsh-patches` directory, mounting your project as `PI_WEB_CWD`, and mounting `~/.pi/agent` read-only as `PI_CODING_AGENT_DIR` so the container sees your API keys and model config:
453
+
454
+ ```yaml
455
+ services:
456
+ pi-web-ui:
457
+ build: .
458
+ ports: ["8787:8787"]
459
+ environment:
460
+ PI_WEB_HOST: 0.0.0.0
461
+ # PI_WEB_ENGINE: dsh
462
+ volumes:
463
+ - pi-web-data:/app/.pi-web
464
+ # - ./my-project:/workspace:ro
465
+ # - ~/.pi/agent:/root/.pi/agent:ro
466
+ volumes:
467
+ pi-web-data:
468
+ ```
469
+
328
470
  ## Plugins
329
471
 
330
472
  Plugins are optional UI components (extra top-bar tabs backed by their own
@@ -347,6 +489,7 @@ straight from GitHub:
347
489
  | 📝 [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`. |
348
490
  | 📊 [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). |
349
491
  | 🧭 [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. |
492
+ | 📖 [legado-web](https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/legado-web) | Legado book reader (📖 阅读): search / discovery / book info / TOC / chapter reading on top of Android-compatible **book sources**, with source import, health checking and dead-source cleanup, and four agent tools (`legado_rules`, `legado_book_sources`, `legado_source_probe`, `legado_run_rule`) plus an “🤖 AI fix this source” button that opens a new chat with the failure context. Sources/shelf/progress persist under `<dataDir>/legado-web/`. |
350
493
 
351
494
  `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.
352
495
 
@@ -408,6 +551,7 @@ pi-web-ui install https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/web
408
551
  `db-connections.json`, vscode-editor's `ssh-hosts.json`) are **not** covered
409
552
  by that preservation — back those up before a forced reinstall.
410
553
  - Refresh the browser afterwards; no server restart needed.
554
+ - **Which plugins are outdated?** `pi-web-ui plugins --check-updates` compares each plugin's recorded commit against the remote HEAD and prints the exact update command. `pi-web-ui plugins --rollback <id>` restores the newest pre-upgrade snapshot (every `--force` install snapshots the old directory into `<dataDir>/plugin-backups/`, keeping the latest three, and rolls itself back if the copy fails).
411
555
 
412
556
  ### Activating
413
557
 
@@ -473,6 +617,29 @@ Want your theme shipped to everyone? Open a pull request at [github.com/xing-shu
473
617
  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.
474
618
 
475
619
 
620
+ ## Tuning & advanced environment variables
621
+
622
+ All optional — the defaults are what the app is developed against. Full reference: [`docs/env-vars.md`](docs/env-vars.md).
623
+
624
+ | Variable | Default | What it changes |
625
+ | --- | --- | --- |
626
+ | `PI_WEB_TOOL_TIMEOUT_MS` | `1200000` (20 min) | Per-tool-call watchdog; a tool still running is aborted (`ask_user_question` is exempt). |
627
+ | `PI_WEB_STALL_NOTIFY_MS` | `180000` (3 min) | Warn — without aborting — when a streaming run produces no event at all; `0` disables. |
628
+ | `PI_WEB_TERMINAL_IDLE_MS` | `15000` | Nudge the AI when a terminal it opened goes silent for this long; `0` disables. |
629
+ | `PI_WEB_TERMINAL_IDLE_LINES` | `10` | How many trailing terminal lines that nudge quotes back (1–500). |
630
+ | `PI_WEB_INLINE_FILE_MAX` | `12288` (12 KB) | Size under which a path-less uploaded file is inlined instead of referenced. |
631
+ | `PI_WEB_VISION_TIMEOUT_MS` | `90000` | Timeout for one whole vision-bridge transcription batch. |
632
+ | `PI_WEB_UPLOAD_RETENTION_DAYS` | `14` | Retention for `<dataDir>/uploads/`; `0` never sweeps. |
633
+ | `PI_WEB_SHELL` | auto | Windows only: which shell node-pty spawns (auto: `PI_WEB_SHELL` → `$SHELL` → Git Bash → bundled busybox → `%COMSPEC%` → PowerShell). |
634
+ | `PI_WEB_TABS` | all tabs | Comma-separated tab allow-list (`chat,terminal,git,search,tasks,settings,plugins`); hidden tabs are refused server-side, `chat` can't be turned off. |
635
+ | `PI_WEB_MANAGED` | off | `1`/`true` declares the instance externally deployed: self-update, pi-CLI install and plugin installs are refused with a reason and hidden in the UI. |
636
+ | `PI_WEB_ALLOW_HOSTS` | empty | Strict hostname allow-list for the HTTP/WS `Host` header, on top of the always-on same-authority check. |
637
+ | `PI_WEB_LOCALE` | empty | Fallback UI language for first-time visitors (an explicit choice and the browser's languages win over it). |
638
+ | `PI_WEB_LOCALE_BASE_URL` | GitHub raw | Where language packs are downloaded from — point it at a mirror for offline/intranet installs. |
639
+ | `PI_WEB_PKG_ROOT` | auto | Overrides where the server looks for `package.json`, `themes/`, `plugins/catalog.json` and `web/dist` (non-standard install layouts). |
640
+ | `PI_CODING_AGENT_SESSION_DIR` | empty | Flat session layout for pi instead of `<agentDir>/sessions/--<cwd>--/` (changes what the history list reads). |
641
+ | `DSH_*` | — | DSH runtime knobs: `PI_WEB_DSH_RUNTIME`, `PI_WEB_DSH_DATA_DIR`, `PI_WEB_DSH_PATCH_DIR`, `PI_WEB_DSH_QUESTION_TIMEOUT_MS`, `PI_WEB_DSH_TOOL_TIMEOUT_MS`, `PI_WEB_DSH_SESSION_RETENTION_DAYS`, `PI_WEB_DSH_DEBUG`. |
642
+
476
643
  ## Security
477
644
 
478
645
  - **Loopback-only by default** — the server binds `127.0.0.1` and is not
@@ -516,15 +683,17 @@ Our release artifacts contain no binaries we did not build ourselves, except
516
683
  upstream open-source components bundled by npm and electron-builder (see
517
684
  [License](#license)).
518
685
 
519
- **Privacy policy:** see [Privacy](#privacy) below.
686
+ **Privacy policy:** see [Privacy](#-privacy) below.
520
687
 
521
688
  ## 🔐 Privacy
522
689
 
523
690
  pi-web-ui runs entirely on your own machine and has **no telemetry, no analytics
524
691
  and no accounts of its own**. Conversations, attachments, settings and terminal
525
- history stay in your local data directory (`~/.pi-web/` by default, or
526
- `%APPDATA%\pi-web-ui\data` for the desktop app), and the HTTP server binds
527
- loopback unless you explicitly expose it.
692
+ history stay on your disk: chat transcripts in the pi agent dir
693
+ (`~/.pi/agent/sessions/` by default), everything else (UI settings, recent
694
+ projects, plugins, uploads, themes, language packs) in the data dir
695
+ (`~/.pi-web/`, or `%APPDATA%\pi-web-ui\data` for the desktop app). The HTTP
696
+ server binds loopback unless you explicitly expose it.
528
697
 
529
698
  Network requests happen only in these cases:
530
699
 
@@ -534,7 +703,7 @@ Network requests happen only in these cases:
534
703
  | Model catalog refresh (startup, then every 4 h) | `pi.dev` | nothing but the request itself |
535
704
  | You install or update a plugin, theme or language pack | `github.com` / `raw.githubusercontent.com` | nothing but the request itself |
536
705
  | You check for or install an update | `registry.npmjs.org` | nothing but the request itself |
537
- | Terminals on Windows, when neither Git Bash nor a `bash` on `PATH` exists | `frippery.org` | one download of `busybox64u.exe` into `<data dir>/bin/bash.exe`, reused offline afterwards |
706
+ | Terminals on Windows, when neither Git Bash nor a `bash` on `PATH` exists | `frippery.org` | one download of `busybox64u.exe` into `~/.pi-web/bin/bash.exe`, reused offline afterwards |
538
707
 
539
708
  Reverse-proxy setups, the optional `PI_WEB_TOKEN` password and Docker port
540
709
  mappings are under your control — see [Security](#security).
@@ -579,7 +748,6 @@ server {
579
748
  # Absolute-path assets/API the built frontend requests (root, not /pi/)
580
749
  location /assets/ { proxy_pass http://127.0.0.1:8787; }
581
750
  location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
582
- location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
583
751
  location = /api/file { proxy_pass http://127.0.0.1:8787; }
584
752
  location = /api/health { proxy_pass http://127.0.0.1:8787; }
585
753
  }