pi-web-ui 0.26.0 → 0.26.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 xing-shuyin
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xing-shuyin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,167 +1,246 @@
1
- # pi-web-ui
2
-
3
- **English** | [简体中文](https://github.com/xing-shuyin/pi-web-ui/blob/main/README.zh-CN.md)
4
-
5
- A web chat interface for the [pi coding agent](https://pi.dev) — the agent runs
6
- in-process via the pi SDK and streams events to the browser over WebSocket. Chat
7
- with thinking blocks and tool calls, attach files, ask about images, use a
8
- built-in terminal, manage models, tweak the system prompt, toggle skills and
9
- extensions on/off, and save/apply settings presets all from a settings panel.
10
- Requires Node.js 22.19 and a configured pi install.
11
-
12
- ## Features
13
-
14
- **Chat**
15
-
16
- - Streaming agent chat over WebSocketthe pi SDK runs in-process; events are pushed as snapshots (60 ms throttled) and the browser renders them.
17
- - Thinking blocks, tool-call cards and bash outputs with live status (running → finished · waiting for the model · duration).
18
- - **补充 (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).
19
- - **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).
20
- - **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.
21
- - **Edit & re-ask** — fork any past question into a new branch and re-prompt; the original conversation stays untouched.
22
- - Long threads auto-collapse messages older than 30 into lazy summary rows (click to expand).
23
- - Question navigation a floating rail plus per-question tags to jump between questions.
24
-
25
- **Files, images & attachments**
26
-
27
- - Three attachment modes: `inline` (≤12 KB), `reference` (path only), `lines` (selected ranges) — over-limit ones degrade automatically.
28
- - Paste / drag-drop / upload images — resized client-side and sent as image content when the model supports vision (warning otherwise).
29
- - **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).
30
- - Attach arbitrary files without a workspace path stored in a global uploads dir, inlined when small, referenced by absolute path otherwise.
31
- - File previewline numbers, click/drag/Shift selection (add to chat as `lines`), GBK fallback decoding, binary hex view, media preview over HTTP with Range support, and a download button.
32
- - Live file tree the server watches the listed directory (fs.watch) and re-lists on change; oversized directories show a truncation warning.
33
-
34
- **Terminal & Git**
35
-
36
- - Built-in terminal (xterm.js + node-pty) with per-client PTY management; Windows auto-selects Git Bash (busybox fallback).
37
- - **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.
38
-
39
- **Models & settings**
40
-
41
- - Model management edit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
42
- - Thinking level per model (only the levels the model actually supports are shown).
43
- - First-run setup wizard.
44
- - Settings panelsystem prompt (append or replace), toggle skills/extensions on/off with immediate effect, save/apply/delete settings presets, and vision-bridge model & switch.
45
-
46
- **Goal mode**
47
-
48
- - Goal bar — set a target with a review model, max rounds and a lock switch.
49
- - Goal wizard ("AI 提炼") turns a raw request into a concrete goal through a guided questionnaire.
50
- - Automatic review loopafter 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).
51
-
52
- **Background tasks**
53
-
54
- - Background-task panelservers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
55
- - Tool watchdog a tool call running over 20 minutes is aborted automatically.
56
- - **Stop bash command only** — abort a running bash tool without killing the conversation.
57
-
58
- **Safety & operations**
59
-
60
- - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
61
- - WebSocket Origin/Host same-authority check cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
62
- - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
63
- - Credentials stay server-sideprovider headers are never sent to the browser.
64
- - Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).
65
-
66
- **Deploy & update**
67
-
68
- - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
69
- - In-app self-updatechecks the npm registry, installs and auto-restarts the service.
70
-
71
- ## Screenshots
72
-
73
- ![pi-web-ui main interface](https://cdn.jsdelivr.net/gh/xing-shuyin/pi-web-ui@main/assets/shot.jpeg)
74
-
75
- ## Install
76
-
77
- ```bash
78
- npm i -g pi-web-ui # global install (recommended)
79
- npx pi-web-ui # or run without installing (latest, starts on :8787)
80
- npm i -g . # or install the local checkout
81
- ```
82
-
83
- **npm ≥ 12?** npm 12+ blocks dependency install scripts by default (you'll see
84
- `npm warn install-scripts … blocked`). node-pty is a native module, so allow its
85
- script (the other two packages it lists are harmless no-ops — allowing them just
86
- silences the warning):
87
-
88
- ```bash
89
- npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
90
- ```
91
-
92
- ## Start
93
-
94
- ```bash
95
- pi-web-ui # foreground, http://localhost:8787
96
- PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
97
- ```
98
-
99
- ## Stop
100
-
101
- - **Foreground**: press `Ctrl+C` in the terminal running it.
102
- - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
103
-
104
- ## Update
105
-
106
- ```bash
107
- npm i -g pi-web-ui@latest # upgrade to the latest published version
108
- pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
109
- ```
110
-
111
- ## Uninstall
112
-
113
- ```bash
114
- npm uninstall -g pi-web-ui
115
- ```
116
-
117
- Uninstalling does **not** delete your chats — session data lives in
118
- `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
119
-
120
- ## Run as a system service (auto-start on boot)
121
-
122
- ```bash
123
- pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
124
- pi-web-ui server status # running? auto-start?
125
- pi-web-ui server restart # restart (applies config/version changes)
126
- pi-web-ui server stop # stop (auto-start stays)
127
- pi-web-ui server start # start again
128
- pi-web-ui server uninstall # remove the service entirely
129
- pi-web-ui server shortcut # desktop one-click launch icon
130
- pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
131
- pi-web-ui server unquiesce # reopen admission
132
- ```
133
-
134
- `server status` also shows live stats via a local control socket (version,
135
- PID, quiesce state, connected browsers, running conversations) — the same
136
- socket drives `quiesce`/`unquiesce`.
137
-
138
- ## Security
139
-
140
- - **Loopback-only by default** — the server binds `127.0.0.1` and is not
141
- reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
142
- (e.g. LAN access, Docker port mapping — the compose file sets it for you).
143
- - **WebSocket origin check** browser pages connecting to `/ws` must present
144
- an `Origin` whose hostname **and port** match the request `Host`;
145
- cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
146
- are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
147
- reverse-proxy setups.
148
- - **Quiesce** `server quiesce` refuses new prompts/forks/session resumes
149
- until you `server unquiesce`; in-flight runs finish cleanly (useful before
150
- upgrades/backups).
151
- - **Credentials stay server-side** provider `headers` (which may carry
152
- `Authorization` / API keys) are never sent to the browser; the model
153
- management UI edits everything else and the server preserves the headers.
154
-
155
- - **macOS** launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
156
- - **Linux** → systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
157
- - **Windows** Task Scheduler logon task (hidden PowerShell window, no black console)
158
-
159
- Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
160
- `--name` (custom service name). Rerunning `server install` with new options
161
- regenerates the config and restarts the service — that's how you change its
162
- port/cwd.
163
-
164
- ## License
165
-
166
- MIT
167
-
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 presetsall 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 previewline numbers, click/drag/Shift selection (add to chat as `lines`), GBK fallback decoding, binary hex view, media preview over HTTP with Range support, and a download button.
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
+ - Model managementedit `models.json` in the UI and set per-provider API keys (keys/headers never leave the server).
55
+ - Thinking level per model (only the levels the model actually supports are shown).
56
+ - First-run setup wizard.
57
+ - 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.
58
+
59
+ **Goal mode**
60
+
61
+ - Goal bar set a target with a review model, max rounds and a lock switch.
62
+ - Goal wizard ("AI 提炼") — turns a raw request into a concrete goal through a guided questionnaire.
63
+ - Automatic review loopafter 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).
64
+
65
+ **Background tasks**
66
+
67
+ - Background-task panel — servers launched by the agent are detected via port snapshots and listed (port/pid/name); stop one or kill all.
68
+ - Tool watchdog a tool call running over 20 minutes is aborted automatically.
69
+ - **Stop bash command only** abort a running bash tool without killing the conversation.
70
+
71
+ **Safety & operations**
72
+
73
+ - Loopback-only by default; set `PI_WEB_HOST=0.0.0.0` for LAN / containers.
74
+ - WebSocket Origin/Host same-authority check — cross-origin pages are rejected (403); `PI_WEB_ALLOW_ORIGINS` whitelist for reverse proxies.
75
+ - Quiesce drain mode via a local control socket (`server status|quiesce|unquiesce`).
76
+ - Credentials stay server-side — provider headers are never sent to the browser.
77
+ - Sound alerts, Chinese/English UI, and a recent-projects list (click to switch workspace).
78
+
79
+ **Deploy & update**
80
+
81
+ - Foreground, global npm install, Docker (docker-compose), macOS launchd, Linux systemd, Windows Task Scheduler, and a desktop shortcut (`server shortcut`).
82
+ - In-app self-update — checks the npm registry, installs and auto-restarts the service.
83
+
84
+ ## Screenshots
85
+
86
+ ![pi-web-ui main interface](https://cdn.jsdelivr.net/gh/xing-shuyin/pi-web-ui@main/assets/shot.jpeg)
87
+
88
+ ## Install
89
+
90
+ ```bash
91
+ npm i -g pi-web-ui # global install (recommended)
92
+ npx pi-web-ui # or run without installing (latest, starts on :8787)
93
+ npm i -g . # or install the local checkout
94
+ ```
95
+
96
+ **npm 12?** npm 12+ blocks dependency install scripts by default (you'll see
97
+ `npm warn install-scripts … blocked`). node-pty is a native module, so allow its
98
+ script (the other two packages it lists are harmless no-ops — allowing them just
99
+ silences the warning):
100
+
101
+ ```bash
102
+ npm i -g --allow-scripts=node-pty,@google/genai,protobufjs pi-web-ui@latest
103
+ ```
104
+
105
+ ## Start
106
+
107
+ ```bash
108
+ pi-web-ui # foreground, http://localhost:8787
109
+ PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
110
+ ```
111
+
112
+ ## Stop
113
+
114
+ - **Foreground**: press `Ctrl+C` in the terminal running it.
115
+ - **As a service**: `pi-web-ui server stop` (stops the instance; auto-start stays until `server uninstall`).
116
+
117
+ ## Update
118
+
119
+ ```bash
120
+ npm i -g pi-web-ui@latest # upgrade to the latest published version
121
+ pi-web-ui server restart # restart the service to apply it (foreground: restart manually)
122
+ ```
123
+
124
+ ## Uninstall
125
+
126
+ ```bash
127
+ npm uninstall -g pi-web-ui
128
+ ```
129
+
130
+ Uninstalling does **not** delete your chats session data lives in
131
+ `<cwd>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
132
+
133
+ ## Run as a system service (auto-start on boot)
134
+
135
+ ```bash
136
+ pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
137
+ pi-web-ui server status # running? auto-start?
138
+ pi-web-ui server restart # restart (applies config/version changes)
139
+ pi-web-ui server stop # stop (auto-start stays)
140
+ pi-web-ui server start # start again
141
+ pi-web-ui server uninstall # remove the service entirely
142
+ pi-web-ui server shortcut # desktop one-click launch icon
143
+ pi-web-ui server quiesce # drain: refuse NEW chats/messages, let running ones finish
144
+ pi-web-ui server unquiesce # reopen admission
145
+ ```
146
+
147
+ `server status` also shows live stats via a local control socket (version,
148
+ PID, quiesce state, connected browsers, running conversations) the same
149
+ socket drives `quiesce`/`unquiesce`.
150
+
151
+ - **macOS** launchd agent (no sudo), logs to `/tmp/pi-web-ui.log` / `.err`
152
+ - **Linux** systemd unit (`systemctl enable --now`), logs via `journalctl -u pi-web-ui -f`
153
+ - **Windows** Task Scheduler logon task (hidden PowerShell window, no black console)
154
+
155
+ Options: `--port` (default 8787), `--cwd` (workspace), `--data-dir` (sessions),
156
+ `--name` (custom service name). Rerunning `server install` with new options
157
+ regenerates the config and restarts the service that's how you change its
158
+ port/cwd.
159
+
160
+ ## Security
161
+
162
+ - **Loopback-only by default** — the server binds `127.0.0.1` and is not
163
+ reachable from the network unless you explicitly set `PI_WEB_HOST=0.0.0.0`
164
+ (e.g. LAN access, Docker port mapping — the compose file sets it for you).
165
+ - **WebSocket origin check** — browser pages connecting to `/ws` must present
166
+ an `Origin` whose hostname **and port** match the request `Host`;
167
+ cross-origin pages are rejected with 403. Non-browser clients (no `Origin`)
168
+ are unaffected. Add `PI_WEB_ALLOW_ORIGINS=http://your-host:port` for
169
+ reverse-proxy setups.
170
+ - **Quiesce** — `server quiesce` refuses new prompts/forks/session resumes
171
+ until you `server unquiesce`; in-flight runs finish cleanly (useful before
172
+ upgrades/backups).
173
+ - **Credentials stay server-side** — provider `headers` (which may carry
174
+ `Authorization` / API keys) are never sent to the browser; the model
175
+ management UI edits everything else and the server preserves the headers.
176
+
177
+ ## Reverse proxy (nginx)
178
+
179
+ Serve pi-web-ui behind nginx on the same host (it binds loopback only, so a
180
+ same-machine reverse proxy is the supported remote-access path — no
181
+ `PI_WEB_HOST=0.0.0.0` needed):
182
+
183
+ ```nginx
184
+ # pi-web-ui on 127.0.0.1:8787, exposed as https://your-host/pi/
185
+ server {
186
+ listen 443 ssl;
187
+ server_name your-host;
188
+ # ssl_certificate ... / ssl_certificate_key ...
189
+
190
+ # App entry at a sub-path (strips the /pi/ prefix)
191
+ location /pi/ {
192
+ proxy_pass http://127.0.0.1:8787/;
193
+ proxy_http_version 1.1;
194
+ # $http_host keeps the port — the server's origin check compares the
195
+ # full authority (hostname AND port). $host would drop it and get 403.
196
+ proxy_set_header Host $http_host;
197
+ proxy_set_header X-Real-IP $remote_addr;
198
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
199
+ proxy_set_header X-Forwarded-Proto $scheme;
200
+ }
201
+
202
+ # WebSocket — MUST forward Host identically or the upgrade is 403'd
203
+ # (page loads, but chat/terminal keep reconnecting)
204
+ location /ws {
205
+ proxy_pass http://127.0.0.1:8787;
206
+ proxy_http_version 1.1;
207
+ proxy_set_header Host $http_host;
208
+ proxy_set_header Upgrade $http_upgrade;
209
+ proxy_set_header Connection "upgrade";
210
+ proxy_read_timeout 3600s;
211
+ proxy_send_timeout 3600s;
212
+ }
213
+
214
+ # Absolute-path assets/API the built frontend requests (root, not /pi/)
215
+ location /assets/ { proxy_pass http://127.0.0.1:8787; }
216
+ location = /favicon.svg { proxy_pass http://127.0.0.1:8787; }
217
+ location = /favicon-streaming.svg { proxy_pass http://127.0.0.1:8787; }
218
+ location = /api/file { proxy_pass http://127.0.0.1:8787; }
219
+ location = /api/health { proxy_pass http://127.0.0.1:8787; }
220
+ }
221
+ ```
222
+
223
+ Key points:
224
+
225
+ - **`Host` must be `$http_host`** (keeps the port) on both `/pi/` and `/ws` —
226
+ the origin check compares hostname **and** port. `proxy_set_header Host $host`
227
+ or leaving it unset (defaults to the upstream `127.0.0.1:8787`) both fail with 403.
228
+ - **Same-origin works automatically**: as long as the browser's `Origin` equals
229
+ the forwarded `Host` (it does through a plain proxy), no
230
+ `PI_WEB_ALLOW_ORIGINS` is needed. Only set it when the browser origin differs
231
+ from the Host the server sees (e.g. a TLS-terminating proxy that changes the
232
+ port).
233
+ - **No `proxy_protocol` unless you really need real client IPs**: it makes
234
+ nginx reject every connection that does not send a PROXY header, which
235
+ breaks direct LAN access and any non-frp clients. With frp, drop
236
+ `transport.proxyProtocolVersion` from the proxy config unless nginx listens
237
+ with `proxy_protocol` too.
238
+ - **LAN access without a proxy**: just set `PI_WEB_HOST=0.0.0.0` (and a
239
+ firewall rule) — or put the whole server block above on port 80/443.
240
+
241
+ Full working example (with an frp tunnel): `deploy/nginx-subpath.conf`.
242
+
243
+ ## License
244
+
245
+ MIT
246
+