pi-web-ui 0.11.5 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +441 -428
- package/README.zh-CN.md +367 -359
- package/dist/server/agent-service.js +168 -4
- package/package.json +1 -1
- package/web/dist/assets/index-BRdOlIsa.js +119 -0
- package/web/dist/assets/index-D5i4-IdY.css +41 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-BZvXBQFP.css +0 -41
- package/web/dist/assets/index-CFs6gldr.js +0 -119
package/README.md
CHANGED
|
@@ -1,428 +1,441 @@
|
|
|
1
|
-
# pi-web-ui
|
|
2
|
-
|
|
3
|
-
**English** | [简体中文](README.zh-CN.md)
|
|
4
|
-
|
|
5
|
-
A web chat interface for the [pi coding agent](https://pi.dev), built directly on
|
|
6
|
-
the **pi SDK** ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)) —
|
|
7
|
-
no subprocess, no JSON-RPC shim. The agent runs in the server process and streams
|
|
8
|
-
events to the browser over WebSocket.
|
|
9
|
-
|
|
10
|
-
Inspired by [Pintra (pi-vsc)](https://github.com/bilalbentoumi/pi-vsc), which does
|
|
11
|
-
the same thing inside VS Code by spawning `pi --mode rpc`. This project instead
|
|
12
|
-
uses the SDK's `createAgentSessionRuntime` API in-process (the SDK docs recommend
|
|
13
|
-
this over RPC for Node.js apps), so you get type safety, direct state access, and
|
|
14
|
-
your existing pi auth/config/extensions — nothing extra to install or configure.
|
|
15
|
-
|
|
16
|
-
## Features
|
|
17
|
-
|
|
18
|
-
- 🧠 Full agent loop with **thinking** blocks (collapsible) and streaming text
|
|
19
|
-
- 🛠 Tool execution cards with **live output streaming**, status (queued → running → done/error), and copyable arguments
|
|
20
|
-
- 💬 Session **history persisted per browser** (localStorage clientId + per-client session dirs) — refresh or restart and your chats come back. The conversation panel also lists the pi CLI/TUI sessions for the current folder (tagged `TUI`), so you can resume a terminal conversation from the web UI
|
|
21
|
-
- 📂 **Project memory**: the last workspace of each browser is remembered and restored on restart; a "Recent projects" list in the left panel switches workspaces in one click, and each project keeps its own sessions so you can always pick up an old conversation
|
|
22
|
-
- ✏️ **Edit & re-ask**: every past question has an edit button — change it and re-ask from that point. The server forks a new branch session (keeping the full history before that question) while the original conversation stays untouched in the session list
|
|
23
|
-
- ⚡ **Long chats stay fast**: past 30 messages, older messages collapse into summary rows (role + first-line preview + block counts — no Markdown/thinking/tool output rendered); click to expand the full content. The latest 15 messages always render in full
|
|
24
|
-
- ⬇️ **Self-update**: the top-right corner shows the running version; the update panel checks npm for the latest release and can run `npm i -g` in one click (a restart is required to take effect)
|
|
25
|
-
- 🔄 Model & thinking-level cycling (same as pi's TUI), new chat, abort/stop
|
|
26
|
-
- 📎 Markdown rendering with GFM tables, syntax-highlighted code blocks and copy buttons
|
|
27
|
-
- 📁 Workspace-aware: the agent reads/edits/runs code in a configurable directory using **your** `~/.pi/agent` auth, models, skills and extensions
|
|
28
|
-
- 🌐 Multiple browser clients each get an isolated session (private session dir per clientId)
|
|
29
|
-
- 🖥 Built-in **terminal** (xterm.js + node-pty, no VS Code needed): three panes — a
|
|
30
|
-
**command list** on the left (user-defined commands with `${pwd}` support, persisted in
|
|
31
|
-
the project's `.pi/commands.json`), the **terminal** in the middle, and a VSCode-style
|
|
32
|
-
**tab strip** on the right for multiple concurrent shells. Switch between chat and
|
|
33
|
-
terminal views with the toggle in the top bar.
|
|
34
|
-
|
|
35
|
-
## Quick start
|
|
36
|
-
|
|
37
|
-
Requires Node.js ≥ 22.19 (the pi SDK requires it; older Node fails with
|
|
38
|
-
`Unexpected token 'with'` when loading the SDK) and a configured pi install
|
|
39
|
-
(run `pi` once to log in).
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install
|
|
43
|
-
npm run dev # server on :8787, web UI on :5173 (auto-proxied)
|
|
44
|
-
# open http://localhost:5173
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Production:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm run build # compiles server (tsc) + frontend (vite)
|
|
51
|
-
npm start # serves everything on http://localhost:8787
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## npm package (install / start / stop / update / uninstall)
|
|
55
|
-
|
|
56
|
-
The package is published on npm as [`pi-web-ui`](https://www.npmjs.com/package/pi-web-ui).
|
|
57
|
-
|
|
58
|
-
### Install
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# install globally (recommended)
|
|
62
|
-
npm i -g pi-web-ui
|
|
63
|
-
|
|
64
|
-
# or run without installing (pulls the latest, starts on :8787)
|
|
65
|
-
npx pi-web-ui
|
|
66
|
-
|
|
67
|
-
# or install the local checkout (for testing changes before publishing)
|
|
68
|
-
npm i -g .
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
> **pi-managed npm?** If your `npm` is the pi wrapper that blocks dependency
|
|
72
|
-
> install scripts, approve node-pty's native build once after installing:
|
|
73
|
-
> `npm approve-scripts node-pty@1.1.0` (standard npm does this automatically).
|
|
74
|
-
|
|
75
|
-
### As a pi package (web UI inside pi)
|
|
76
|
-
|
|
77
|
-
`pi-web-ui` is also published as a **pi package** (`pi-package` on npm) so it
|
|
78
|
-
can be installed and used from within a pi session:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
pi install npm:pi-web-ui
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Once installed, a `/webui` command becomes available inside pi, launching the
|
|
85
|
-
local web UI against your current working directory:
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
/webui # start + open browser (current dir)
|
|
89
|
-
/webui --port 9000 # start on a specific port
|
|
90
|
-
/webui --no-browser # start without opening the browser
|
|
91
|
-
/webui stop # stop the running instance
|
|
92
|
-
/webui status # show URL / status
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
> **Note — `pi install` is NOT a global CLI install.**
|
|
96
|
-
>
|
|
97
|
-
> `pi install npm:pi-web-ui` only loads the package into pi's extension tree
|
|
98
|
-
> (`~/.pi/agent/npm/node_modules/`) and registers its extension for pi sessions.
|
|
99
|
-
> It does **not** put a `pi-web-ui` executable on your shell `PATH`, so you
|
|
100
|
-
> cannot run the `pi-web-ui` terminal command from that install. For the CLI you
|
|
101
|
-
> still need the global npm install above (`npm i -g pi-web-ui`), which is what
|
|
102
|
-
> `which pi-web-ui` resolves to. `pi install` ≠ `npm i -g`: one is for pi
|
|
103
|
-
> extensions, the other for a system-wide command. Both can coexist (the global
|
|
104
|
-
> 0.x CLI for terminal use, the pi package for the `/webui` in-pi entry).
|
|
105
|
-
|
|
106
|
-
### Start
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
pi-web-ui # foreground, http://localhost:8787
|
|
110
|
-
PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
To run it in the background or auto-start on boot, use a system service —
|
|
114
|
-
see [Deploy & auto-start on boot](#deploy--auto-start-on-boot) (systemd /
|
|
115
|
-
launchd / Docker).
|
|
116
|
-
|
|
117
|
-
The `pi-web-ui` command serves the built frontend and the WebSocket API from
|
|
118
|
-
wherever the package is installed — no repo checkout needed. It uses **your**
|
|
119
|
-
`~/.pi/agent` config (auth/models/skills) and stores per-client sessions under
|
|
120
|
-
`<PI_WEB_CWD>/.pi-web`.
|
|
121
|
-
|
|
122
|
-
### Stop
|
|
123
|
-
|
|
124
|
-
- **Foreground**: press `Ctrl+C` in the terminal running it.
|
|
125
|
-
- **systemd**: `sudo systemctl stop pi-web-ui`
|
|
126
|
-
- **launchd**: `launchctl bootout gui/$(id -u)/com.xingshuyin.pi-web-ui`
|
|
127
|
-
- **Windows (scheduled task)**: `pi-web-ui server stop` (or
|
|
128
|
-
`schtasks /End /TN pi-web-ui`; stops the running instance, auto-start stays
|
|
129
|
-
until `server uninstall`)
|
|
130
|
-
- **Docker**: `docker compose stop` (stop + remove the container: `docker compose down`)
|
|
131
|
-
|
|
132
|
-
(Background processes should be managed by a system service, not `nohup` —
|
|
133
|
-
service stop commands above also stop and disable auto-start.)
|
|
134
|
-
|
|
135
|
-
### Verify / version
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
pi-web-ui --version # CLI version
|
|
139
|
-
npm ls -g pi-web-ui # installed? which version?
|
|
140
|
-
which pi-web-ui # executable location
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Update
|
|
144
|
-
|
|
145
|
-
```bash
|
|
146
|
-
npm i -g pi-web-ui@latest # upgrade to the latest published version
|
|
147
|
-
# restart the server afterwards for the new version to take effect
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Uninstall
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
npm uninstall -g pi-web-ui
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Uninstalling does **not** delete your chats: session data lives in
|
|
157
|
-
`<PI_WEB_CWD>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
|
|
158
|
-
|
|
159
|
-
### Manage as a system service (auto-start)
|
|
160
|
-
|
|
161
|
-
Install the server as a system service that starts on boot, with a custom
|
|
162
|
-
port and workspace:
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
|
|
166
|
-
pi-web-ui server status # running? auto-start?
|
|
167
|
-
pi-web-ui server restart # restart (also applies config changes)
|
|
168
|
-
pi-web-ui server stop # stop + disable auto-start
|
|
169
|
-
pi-web-ui server start # start again
|
|
170
|
-
pi-web-ui server uninstall # remove the service entirely
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
- **macOS** → launchd agent (no sudo): writes and loads
|
|
174
|
-
`~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist`, restarts on crash
|
|
175
|
-
(`KeepAlive`), logs to `/tmp/pi-web-ui.log` / `/tmp/pi-web-ui.err`.
|
|
176
|
-
- **Linux** → systemd unit (auto-sudo): writes
|
|
177
|
-
`/etc/systemd/system/pi-web-ui.service` and runs `systemctl enable --now`,
|
|
178
|
-
logs via `journalctl -u pi-web-ui -f`.
|
|
179
|
-
- **Windows** → Task Scheduler: creates a user task that starts at logon
|
|
180
|
-
(same as a launchd agent; usually no admin needed, but on some machines
|
|
181
|
-
`schtasks /Create` requires an elevated PowerShell — if `install` fails
|
|
182
|
-
with `ERROR: Access is denied`, rerun it from an admin shell). It runs a
|
|
183
|
-
PowerShell launcher generated at
|
|
184
|
-
`%APPDATA%\pi-web-ui\pi-web-ui.ps1` via `powershell.exe -WindowStyle Hidden` — no black console window stays open, so there's nothing to
|
|
185
|
-
accidentally close/kill. The launcher sets env, cd's to the workspace,
|
|
186
|
-
launches node, appends logs to `%USERPROFILE%\pi-web-ui.log`. The task XML
|
|
187
|
-
is saved next to it; restarts on failure. **Always pass `--cwd`
|
|
188
|
-
explicitly** — the task inherits the installing shell's directory, and an
|
|
189
|
-
admin shell defaults to `C:\WINDOWS\system32`, which the non-elevated
|
|
190
|
-
task cannot write to (EPERM at startup). See
|
|
191
|
-
[Windows — Task Scheduler](#windows--task-scheduler) for details.
|
|
192
|
-
- Options: `--port` (default 8787 or `$PORT`), `--cwd` (default `$PI_WEB_CWD`
|
|
193
|
-
or the current directory), `--data-dir` (sessions), `--name` (custom service
|
|
194
|
-
name; on macOS the label is `com.xingshuyin.pi-web-ui`, custom names become
|
|
195
|
-
`com.<name>.server`). `--print` previews the generated unit/plist/task files
|
|
196
|
-
without applying it.
|
|
197
|
-
- Rerunning `install` with new options regenerates the config and restarts the
|
|
198
|
-
service — that's how you change the port/cwd of an installed service.
|
|
199
|
-
|
|
200
|
-
## Configuration
|
|
201
|
-
|
|
202
|
-
| Env var | Default | Description |
|
|
203
|
-
| -------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
-
| `PORT` | `8787` | HTTP/WebSocket port |
|
|
205
|
-
| `PI_WEB_CWD` | server's cwd | The workspace directory the agent operates in (read/edit/bash/write) |
|
|
206
|
-
| `PI_WEB_DATA_DIR` | `<cwd>/.pi-web` | Where per-client session dirs are stored |
|
|
207
|
-
| `PI_WEB_INLINE_FILE_MAX` | `12288` (12KB) | Text attachments at or below this size are inlined into the model context; larger files are passed as path references and the model reads them on demand (saves tokens for small edits) |
|
|
208
|
-
| `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models.json, skills, extensions) |
|
|
209
|
-
|
|
210
|
-
Example — point the agent at a project:
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
PI_WEB_CWD=/path/to/your/project npm run dev
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## Architecture
|
|
217
|
-
|
|
218
|
-
```text
|
|
219
|
-
Browser (React + Vite)
|
|
220
|
-
│ WebSocket JSON — snapshot-driven protocol (server/protocol.ts)
|
|
221
|
-
▼
|
|
222
|
-
server/index.ts express static + ws endpoint
|
|
223
|
-
│
|
|
224
|
-
server/agent-service.ts per-client ClientSession:
|
|
225
|
-
│ createAgentSessionRuntime({ sessionManager: SessionManager.continueRecent(cwd, sessionDir) })
|
|
226
|
-
│ session.subscribe(events) → throttled full-state snapshots + live tool deltas
|
|
227
|
-
▼
|
|
228
|
-
@earendil-works/pi-coding-agent (SDK, in-process)
|
|
229
|
-
│ ModelRuntime (auth from ~/.pi/agent) · tools · extensions · skills
|
|
230
|
-
▼
|
|
231
|
-
your LLM provider
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Key design points:
|
|
235
|
-
|
|
236
|
-
- **Snapshot-driven UI.** The server is the source of truth: after every SDK
|
|
237
|
-
event it schedules a throttled (60 ms) full-state snapshot, and the browser
|
|
238
|
-
renders purely from snapshots. Reconnects just re-request `get_state`. Large
|
|
239
|
-
payloads (tool output, text) are capped during serialization (`server/serialize.ts`).
|
|
240
|
-
- **Live assistant streaming.** The in-progress message (SDK
|
|
241
|
-
`agent.state.streamingMessage`) is serialized into every snapshot, so thinking
|
|
242
|
-
blocks and answer text appear in the browser as they are generated — with a
|
|
243
|
-
blinking cursor — instead of only after the turn finishes. The partial message
|
|
244
|
-
gets a stable `stream-<ts>` id so it stays mounted (open thinking/tool blocks
|
|
245
|
-
keep their state) across snapshots.
|
|
246
|
-
- **Size-aware attachments.** Clicking + on a file queues it as an attachment
|
|
247
|
-
(shown as chips above the input). On send, the server attaches each file as an
|
|
248
|
-
independent custom message (SDK `sendCustomMessage` + `nextTurn` asides) — the
|
|
249
|
-
user message stays clean, and each file renders as its own collapsible card:
|
|
250
|
-
small text files (≤ `PI_WEB_INLINE_FILE_MAX`, default 12KB) are inlined so the
|
|
251
|
-
model sees them immediately; larger files are passed as a `<file path=...>`
|
|
252
|
-
reference and the model reads them on demand with its `read` tool, so attaching
|
|
253
|
-
a 5 MB file costs only a few tokens until the model actually looks at it.
|
|
254
|
-
Images are always attached as image content.
|
|
255
|
-
- **
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- **
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
###
|
|
348
|
-
|
|
349
|
-
```bash
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
###
|
|
361
|
-
|
|
362
|
-
```bash
|
|
363
|
-
npm i -g pi-web-ui
|
|
364
|
-
cp deploy/
|
|
365
|
-
# edit
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
```
|
|
376
|
-
npm i -g pi-web-ui
|
|
377
|
-
pi-web-ui
|
|
378
|
-
pi-web-ui
|
|
379
|
-
pi-web-ui
|
|
380
|
-
pi-web-ui
|
|
381
|
-
pi-web-ui
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
>
|
|
413
|
-
>
|
|
414
|
-
>
|
|
415
|
-
>
|
|
416
|
-
> `
|
|
417
|
-
> `
|
|
418
|
-
>
|
|
419
|
-
|
|
420
|
-
>
|
|
421
|
-
>
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
1
|
+
# pi-web-ui
|
|
2
|
+
|
|
3
|
+
**English** | [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
A web chat interface for the [pi coding agent](https://pi.dev), built directly on
|
|
6
|
+
the **pi SDK** ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)) —
|
|
7
|
+
no subprocess, no JSON-RPC shim. The agent runs in the server process and streams
|
|
8
|
+
events to the browser over WebSocket.
|
|
9
|
+
|
|
10
|
+
Inspired by [Pintra (pi-vsc)](https://github.com/bilalbentoumi/pi-vsc), which does
|
|
11
|
+
the same thing inside VS Code by spawning `pi --mode rpc`. This project instead
|
|
12
|
+
uses the SDK's `createAgentSessionRuntime` API in-process (the SDK docs recommend
|
|
13
|
+
this over RPC for Node.js apps), so you get type safety, direct state access, and
|
|
14
|
+
your existing pi auth/config/extensions — nothing extra to install or configure.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 🧠 Full agent loop with **thinking** blocks (collapsible) and streaming text
|
|
19
|
+
- 🛠 Tool execution cards with **live output streaming**, status (queued → running → done/error), and copyable arguments
|
|
20
|
+
- 💬 Session **history persisted per browser** (localStorage clientId + per-client session dirs) — refresh or restart and your chats come back. The conversation panel also lists the pi CLI/TUI sessions for the current folder (tagged `TUI`), so you can resume a terminal conversation from the web UI
|
|
21
|
+
- 📂 **Project memory**: the last workspace of each browser is remembered and restored on restart; a "Recent projects" list in the left panel switches workspaces in one click, and each project keeps its own sessions so you can always pick up an old conversation
|
|
22
|
+
- ✏️ **Edit & re-ask**: every past question has an edit button — change it and re-ask from that point. The server forks a new branch session (keeping the full history before that question) while the original conversation stays untouched in the session list
|
|
23
|
+
- ⚡ **Long chats stay fast**: past 30 messages, older messages collapse into summary rows (role + first-line preview + block counts — no Markdown/thinking/tool output rendered); click to expand the full content. The latest 15 messages always render in full
|
|
24
|
+
- ⬇️ **Self-update**: the top-right corner shows the running version; the update panel checks npm for the latest release and can run `npm i -g` in one click (a restart is required to take effect)
|
|
25
|
+
- 🔄 Model & thinking-level cycling (same as pi's TUI), new chat, abort/stop
|
|
26
|
+
- 📎 Markdown rendering with GFM tables, syntax-highlighted code blocks and copy buttons
|
|
27
|
+
- 📁 Workspace-aware: the agent reads/edits/runs code in a configurable directory using **your** `~/.pi/agent` auth, models, skills and extensions
|
|
28
|
+
- 🌐 Multiple browser clients each get an isolated session (private session dir per clientId)
|
|
29
|
+
- 🖥 Built-in **terminal** (xterm.js + node-pty, no VS Code needed): three panes — a
|
|
30
|
+
**command list** on the left (user-defined commands with `${pwd}` support, persisted in
|
|
31
|
+
the project's `.pi/commands.json`), the **terminal** in the middle, and a VSCode-style
|
|
32
|
+
**tab strip** on the right for multiple concurrent shells. Switch between chat and
|
|
33
|
+
terminal views with the toggle in the top bar.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
Requires Node.js ≥ 22.19 (the pi SDK requires it; older Node fails with
|
|
38
|
+
`Unexpected token 'with'` when loading the SDK) and a configured pi install
|
|
39
|
+
(run `pi` once to log in).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install
|
|
43
|
+
npm run dev # server on :8787, web UI on :5173 (auto-proxied)
|
|
44
|
+
# open http://localhost:5173
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Production:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm run build # compiles server (tsc) + frontend (vite)
|
|
51
|
+
npm start # serves everything on http://localhost:8787
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## npm package (install / start / stop / update / uninstall)
|
|
55
|
+
|
|
56
|
+
The package is published on npm as [`pi-web-ui`](https://www.npmjs.com/package/pi-web-ui).
|
|
57
|
+
|
|
58
|
+
### Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# install globally (recommended)
|
|
62
|
+
npm i -g pi-web-ui
|
|
63
|
+
|
|
64
|
+
# or run without installing (pulls the latest, starts on :8787)
|
|
65
|
+
npx pi-web-ui
|
|
66
|
+
|
|
67
|
+
# or install the local checkout (for testing changes before publishing)
|
|
68
|
+
npm i -g .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
> **pi-managed npm?** If your `npm` is the pi wrapper that blocks dependency
|
|
72
|
+
> install scripts, approve node-pty's native build once after installing:
|
|
73
|
+
> `npm approve-scripts node-pty@1.1.0` (standard npm does this automatically).
|
|
74
|
+
|
|
75
|
+
### As a pi package (web UI inside pi)
|
|
76
|
+
|
|
77
|
+
`pi-web-ui` is also published as a **pi package** (`pi-package` on npm) so it
|
|
78
|
+
can be installed and used from within a pi session:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pi install npm:pi-web-ui
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Once installed, a `/webui` command becomes available inside pi, launching the
|
|
85
|
+
local web UI against your current working directory:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
/webui # start + open browser (current dir)
|
|
89
|
+
/webui --port 9000 # start on a specific port
|
|
90
|
+
/webui --no-browser # start without opening the browser
|
|
91
|
+
/webui stop # stop the running instance
|
|
92
|
+
/webui status # show URL / status
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> **Note — `pi install` is NOT a global CLI install.**
|
|
96
|
+
>
|
|
97
|
+
> `pi install npm:pi-web-ui` only loads the package into pi's extension tree
|
|
98
|
+
> (`~/.pi/agent/npm/node_modules/`) and registers its extension for pi sessions.
|
|
99
|
+
> It does **not** put a `pi-web-ui` executable on your shell `PATH`, so you
|
|
100
|
+
> cannot run the `pi-web-ui` terminal command from that install. For the CLI you
|
|
101
|
+
> still need the global npm install above (`npm i -g pi-web-ui`), which is what
|
|
102
|
+
> `which pi-web-ui` resolves to. `pi install` ≠ `npm i -g`: one is for pi
|
|
103
|
+
> extensions, the other for a system-wide command. Both can coexist (the global
|
|
104
|
+
> 0.x CLI for terminal use, the pi package for the `/webui` in-pi entry).
|
|
105
|
+
|
|
106
|
+
### Start
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pi-web-ui # foreground, http://localhost:8787
|
|
110
|
+
PORT=9000 PI_WEB_CWD=/path/to/project pi-web-ui # custom port / workspace
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
To run it in the background or auto-start on boot, use a system service —
|
|
114
|
+
see [Deploy & auto-start on boot](#deploy--auto-start-on-boot) (systemd /
|
|
115
|
+
launchd / Docker).
|
|
116
|
+
|
|
117
|
+
The `pi-web-ui` command serves the built frontend and the WebSocket API from
|
|
118
|
+
wherever the package is installed — no repo checkout needed. It uses **your**
|
|
119
|
+
`~/.pi/agent` config (auth/models/skills) and stores per-client sessions under
|
|
120
|
+
`<PI_WEB_CWD>/.pi-web`.
|
|
121
|
+
|
|
122
|
+
### Stop
|
|
123
|
+
|
|
124
|
+
- **Foreground**: press `Ctrl+C` in the terminal running it.
|
|
125
|
+
- **systemd**: `sudo systemctl stop pi-web-ui`
|
|
126
|
+
- **launchd**: `launchctl bootout gui/$(id -u)/com.xingshuyin.pi-web-ui`
|
|
127
|
+
- **Windows (scheduled task)**: `pi-web-ui server stop` (or
|
|
128
|
+
`schtasks /End /TN pi-web-ui`; stops the running instance, auto-start stays
|
|
129
|
+
until `server uninstall`)
|
|
130
|
+
- **Docker**: `docker compose stop` (stop + remove the container: `docker compose down`)
|
|
131
|
+
|
|
132
|
+
(Background processes should be managed by a system service, not `nohup` —
|
|
133
|
+
service stop commands above also stop and disable auto-start.)
|
|
134
|
+
|
|
135
|
+
### Verify / version
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pi-web-ui --version # CLI version
|
|
139
|
+
npm ls -g pi-web-ui # installed? which version?
|
|
140
|
+
which pi-web-ui # executable location
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Update
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm i -g pi-web-ui@latest # upgrade to the latest published version
|
|
147
|
+
# restart the server afterwards for the new version to take effect
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Uninstall
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm uninstall -g pi-web-ui
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Uninstalling does **not** delete your chats: session data lives in
|
|
157
|
+
`<PI_WEB_CWD>/.pi-web` (or `PI_WEB_DATA_DIR`) and survives uninstall/upgrade.
|
|
158
|
+
|
|
159
|
+
### Manage as a system service (auto-start)
|
|
160
|
+
|
|
161
|
+
Install the server as a system service that starts on boot, with a custom
|
|
162
|
+
port and workspace:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pi-web-ui server install --port 9000 --cwd /path/to/project # install + start
|
|
166
|
+
pi-web-ui server status # running? auto-start?
|
|
167
|
+
pi-web-ui server restart # restart (also applies config changes)
|
|
168
|
+
pi-web-ui server stop # stop + disable auto-start
|
|
169
|
+
pi-web-ui server start # start again
|
|
170
|
+
pi-web-ui server uninstall # remove the service entirely
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- **macOS** → launchd agent (no sudo): writes and loads
|
|
174
|
+
`~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist`, restarts on crash
|
|
175
|
+
(`KeepAlive`), logs to `/tmp/pi-web-ui.log` / `/tmp/pi-web-ui.err`.
|
|
176
|
+
- **Linux** → systemd unit (auto-sudo): writes
|
|
177
|
+
`/etc/systemd/system/pi-web-ui.service` and runs `systemctl enable --now`,
|
|
178
|
+
logs via `journalctl -u pi-web-ui -f`.
|
|
179
|
+
- **Windows** → Task Scheduler: creates a user task that starts at logon
|
|
180
|
+
(same as a launchd agent; usually no admin needed, but on some machines
|
|
181
|
+
`schtasks /Create` requires an elevated PowerShell — if `install` fails
|
|
182
|
+
with `ERROR: Access is denied`, rerun it from an admin shell). It runs a
|
|
183
|
+
PowerShell launcher generated at
|
|
184
|
+
`%APPDATA%\pi-web-ui\pi-web-ui.ps1` via `powershell.exe -WindowStyle Hidden` — no black console window stays open, so there's nothing to
|
|
185
|
+
accidentally close/kill. The launcher sets env, cd's to the workspace,
|
|
186
|
+
launches node, appends logs to `%USERPROFILE%\pi-web-ui.log`. The task XML
|
|
187
|
+
is saved next to it; restarts on failure. **Always pass `--cwd`
|
|
188
|
+
explicitly** — the task inherits the installing shell's directory, and an
|
|
189
|
+
admin shell defaults to `C:\WINDOWS\system32`, which the non-elevated
|
|
190
|
+
task cannot write to (EPERM at startup). See
|
|
191
|
+
[Windows — Task Scheduler](#windows--task-scheduler) for details.
|
|
192
|
+
- Options: `--port` (default 8787 or `$PORT`), `--cwd` (default `$PI_WEB_CWD`
|
|
193
|
+
or the current directory), `--data-dir` (sessions), `--name` (custom service
|
|
194
|
+
name; on macOS the label is `com.xingshuyin.pi-web-ui`, custom names become
|
|
195
|
+
`com.<name>.server`). `--print` previews the generated unit/plist/task files
|
|
196
|
+
without applying it.
|
|
197
|
+
- Rerunning `install` with new options regenerates the config and restarts the
|
|
198
|
+
service — that's how you change the port/cwd of an installed service.
|
|
199
|
+
|
|
200
|
+
## Configuration
|
|
201
|
+
|
|
202
|
+
| Env var | Default | Description |
|
|
203
|
+
| -------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `PORT` | `8787` | HTTP/WebSocket port |
|
|
205
|
+
| `PI_WEB_CWD` | server's cwd | The workspace directory the agent operates in (read/edit/bash/write) |
|
|
206
|
+
| `PI_WEB_DATA_DIR` | `<cwd>/.pi-web` | Where per-client session dirs are stored |
|
|
207
|
+
| `PI_WEB_INLINE_FILE_MAX` | `12288` (12KB) | Text attachments at or below this size are inlined into the model context; larger files are passed as path references and the model reads them on demand (saves tokens for small edits) |
|
|
208
|
+
| `PI_CODING_AGENT_DIR` | `~/.pi/agent` | pi config dir (auth.json, models.json, skills, extensions) |
|
|
209
|
+
|
|
210
|
+
Example — point the agent at a project:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
PI_WEB_CWD=/path/to/your/project npm run dev
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Architecture
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
Browser (React + Vite)
|
|
220
|
+
│ WebSocket JSON — snapshot-driven protocol (server/protocol.ts)
|
|
221
|
+
▼
|
|
222
|
+
server/index.ts express static + ws endpoint
|
|
223
|
+
│
|
|
224
|
+
server/agent-service.ts per-client ClientSession:
|
|
225
|
+
│ createAgentSessionRuntime({ sessionManager: SessionManager.continueRecent(cwd, sessionDir) })
|
|
226
|
+
│ session.subscribe(events) → throttled full-state snapshots + live tool deltas
|
|
227
|
+
▼
|
|
228
|
+
@earendil-works/pi-coding-agent (SDK, in-process)
|
|
229
|
+
│ ModelRuntime (auth from ~/.pi/agent) · tools · extensions · skills
|
|
230
|
+
▼
|
|
231
|
+
your LLM provider
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Key design points:
|
|
235
|
+
|
|
236
|
+
- **Snapshot-driven UI.** The server is the source of truth: after every SDK
|
|
237
|
+
event it schedules a throttled (60 ms) full-state snapshot, and the browser
|
|
238
|
+
renders purely from snapshots. Reconnects just re-request `get_state`. Large
|
|
239
|
+
payloads (tool output, text) are capped during serialization (`server/serialize.ts`).
|
|
240
|
+
- **Live assistant streaming.** The in-progress message (SDK
|
|
241
|
+
`agent.state.streamingMessage`) is serialized into every snapshot, so thinking
|
|
242
|
+
blocks and answer text appear in the browser as they are generated — with a
|
|
243
|
+
blinking cursor — instead of only after the turn finishes. The partial message
|
|
244
|
+
gets a stable `stream-<ts>` id so it stays mounted (open thinking/tool blocks
|
|
245
|
+
keep their state) across snapshots.
|
|
246
|
+
- **Size-aware attachments.** Clicking + on a file queues it as an attachment
|
|
247
|
+
(shown as chips above the input). On send, the server attaches each file as an
|
|
248
|
+
independent custom message (SDK `sendCustomMessage` + `nextTurn` asides) — the
|
|
249
|
+
user message stays clean, and each file renders as its own collapsible card:
|
|
250
|
+
small text files (≤ `PI_WEB_INLINE_FILE_MAX`, default 12KB) are inlined so the
|
|
251
|
+
model sees them immediately; larger files are passed as a `<file path=...>`
|
|
252
|
+
reference and the model reads them on demand with its `read` tool, so attaching
|
|
253
|
+
a 5 MB file costs only a few tokens until the model actually looks at it.
|
|
254
|
+
Images are always attached as image content.
|
|
255
|
+
- **Image Q&A.** Besides attaching workspace images from the right panel, you
|
|
256
|
+
can **paste a screenshot (Ctrl+V), drag an image onto the input bar, or use
|
|
257
|
+
the 🖼 upload button** — the browser downscales it to ≤1568px and encodes it,
|
|
258
|
+
and it travels with the message (`prompt.attachments[].imageData`, base64),
|
|
259
|
+
no workspace path needed. Attaching an image to a non-vision model shows a
|
|
260
|
+
warning (the image would be ignored).
|
|
261
|
+
- **File chat.** Any local file (text or binary) can be dropped onto the input
|
|
262
|
+
bar or picked via the 📎 button — the browser sends the bytes as base64
|
|
263
|
+
(`prompt.attachments[].fileData`), the server persists them under
|
|
264
|
+
`~/.pi-web/uploads/<clientId>/` and attaches: small text files are inlined so
|
|
265
|
+
the model sees them immediately; large/binary files become absolute-path
|
|
266
|
+
references the model reads on demand (its read tool accepts absolute paths).
|
|
267
|
+
Cap: 20MB.
|
|
268
|
+
- **File preview with line selection.** Click a file name (or its 👁 button) in
|
|
269
|
+
the right panel to open a preview modal with line numbers. Click / drag /
|
|
270
|
+
Shift+click to select a line range, then “添加到对话” to queue it as a `lines`
|
|
271
|
+
attachment — the server inlines only the selected range
|
|
272
|
+
(`<file path=... lines="2-3">`), so you can point the agent at exactly the
|
|
273
|
+
code you mean without dumping the whole file. Preview reads are capped at
|
|
274
|
+
512 KB and binary files are detected and refused.
|
|
275
|
+
- **Live tool output.** `bash_execution_update` / `tool_execution_update` events
|
|
276
|
+
are forwarded as lightweight `tool_delta` messages so terminal output streams
|
|
277
|
+
in real time; the final output arrives in the toolResult message on the next
|
|
278
|
+
snapshot, which supersedes the delta buffer.
|
|
279
|
+
- **Isolated sessions.** Each browser client gets `sessions/<clientId>/` under
|
|
280
|
+
the data dir, resumed on reconnect via `SessionManager.continueRecent`.
|
|
281
|
+
- **Everything you already have.** No separate auth step — the SDK reads
|
|
282
|
+
`~/.pi/agent/auth.json` and loads your global extensions/skills automatically.
|
|
283
|
+
|
|
284
|
+
## Terminal
|
|
285
|
+
|
|
286
|
+
Toggle the terminal view from the top bar (对话/终端). It replaces the chat layout
|
|
287
|
+
with three panes:
|
|
288
|
+
|
|
289
|
+
- **Left — 命令 (commands)**: click a command to open a terminal tab in its directory and
|
|
290
|
+
run it. Add/edit/delete commands in the panel; they are saved to
|
|
291
|
+
`<project>/.pi/commands.json` (committed to the repo, shared with teammates):
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"commands": [
|
|
296
|
+
{ "name": "dev", "command": "npm run dev", "cwd": "${pwd}" },
|
|
297
|
+
{ "name": "test", "command": "npm test", "cwd": "${pwd}/server" },
|
|
298
|
+
{ "name": "build", "command": "npm run build", "cwd": "~/other-project" }
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
`${pwd}` resolves to the agent's current working directory (the one shown in the chat
|
|
304
|
+
view's file panel, changeable via set_cwd); `~` and relative paths also work. The `+`
|
|
305
|
+
button at the top of the command panel creates a new entry.
|
|
306
|
+
- **Middle — the terminal**: each tab is a real PTY (your `$SHELL` on
|
|
307
|
+
macOS/Linux; PowerShell or cmd.exe — `$COMSPEC` — on Windows); output
|
|
308
|
+
streams live and you can type, Ctrl+C, resize, etc. exactly like a desktop
|
|
309
|
+
terminal. Git Bash users on Windows get their `$SHELL` automatically.
|
|
310
|
+
- **Right — 终端 (tabs)**: VSCode-style vertical tab strip. `+` opens a plain shell in the
|
|
311
|
+
current directory. Closing a tab kills its process.
|
|
312
|
+
|
|
313
|
+
Notes:
|
|
314
|
+
|
|
315
|
+
- Running commands keep running while you switch back to the chat view.
|
|
316
|
+
- Terminals are killed when the last browser tab for a client disconnects (no orphaned
|
|
317
|
+
dev servers), so a dropped connection resets the terminal view.
|
|
318
|
+
|
|
319
|
+
## Protocol
|
|
320
|
+
|
|
321
|
+
See `server/protocol.ts` for the full wire format. Client → server: `hello`,
|
|
322
|
+
`prompt`, `abort`, `new_chat`, `cycle_model`, `cycle_thinking`, `get_state`,
|
|
323
|
+
`list_sessions`, `switch_session`, `list_files`, `list_models`, `set_model`,
|
|
324
|
+
`set_thinking`, `set_cwd`, `complete_path`, `dialog_response`,
|
|
325
|
+
`terminal_create`, `terminal_input`, `terminal_resize`, `terminal_kill`,
|
|
326
|
+
`run_command`, `list_commands`, `save_commands`.
|
|
327
|
+
Server → client: `ready`, `snapshot` (full `UiState`), `tool_delta`, `notice`,
|
|
328
|
+
`terminal_output`, `terminal_exit`, `commands`.
|
|
329
|
+
|
|
330
|
+
## Scripts
|
|
331
|
+
|
|
332
|
+
| Script | What it does |
|
|
333
|
+
| ---------------------------------- | ------------------------------------------------------- |
|
|
334
|
+
| `npm run dev` | server (tsx watch) + Vite dev server with WS proxy |
|
|
335
|
+
| `npm run build` | type-check + build frontend and server |
|
|
336
|
+
| `npm start` | run the production server (serves`web/dist`) |
|
|
337
|
+
| `npm run typecheck` | `tsc --noEmit` for both server and web |
|
|
338
|
+
| `node terminal-smoke-test.mjs` | WS-level terminal/commands protocol test (build first) |
|
|
339
|
+
| `node terminal-browser-test.mjs` | headless-browser E2E of the terminal view (build first) |
|
|
340
|
+
|
|
341
|
+
## Deploy & auto-start on boot
|
|
342
|
+
|
|
343
|
+
Quickest path: `pi-web-ui server install --port 8787 --cwd /path` — installs
|
|
344
|
+
and starts the service on boot (see [Manage as a system service](#manage-as-a-system-service-auto-start)).
|
|
345
|
+
The manual alternatives below are kept for reference / non-standard setups.
|
|
346
|
+
|
|
347
|
+
### Docker (one command)
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
docker compose up -d # builds, starts on :8787, auto-restarts on boot
|
|
351
|
+
docker compose stop # stop (keeps the container)
|
|
352
|
+
docker compose down # stop and remove the container
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`restart: unless-stopped` in `docker-compose.yml` brings the server back up
|
|
356
|
+
whenever the Docker daemon starts (boot, crashes, reboots). Mount a volume for
|
|
357
|
+
`/app/.pi-web` (sessions persist) and, optionally, your `~/.pi/agent` config
|
|
358
|
+
and a workspace — see the comments in `docker-compose.yml`.
|
|
359
|
+
|
|
360
|
+
### Linux — systemd
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
sudo npm i -g pi-web-ui
|
|
364
|
+
sudo cp deploy/pi-web-ui.service /etc/systemd/system/
|
|
365
|
+
# edit User/WorkingDirectory/Environment in the unit first
|
|
366
|
+
sudo systemctl daemon-reload
|
|
367
|
+
sudo systemctl enable --now pi-web-ui # starts now + on every boot
|
|
368
|
+
sudo systemctl stop pi-web-ui # stop
|
|
369
|
+
sudo systemctl disable pi-web-ui # stop auto-start on boot
|
|
370
|
+
journalctl -u pi-web-ui -f # logs
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### macOS — launchd
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npm i -g pi-web-ui
|
|
377
|
+
cp deploy/com.xingshuyin.pi-web-ui.plist ~/Library/LaunchAgents/
|
|
378
|
+
# edit ProgramArguments / WorkingDirectory / PI_WEB_CWD (which pi-web-ui)
|
|
379
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist
|
|
380
|
+
launchctl bootout gui/$(id -u)/com.xingshuyin.pi-web-ui # stop + remove auto-start
|
|
381
|
+
# logs: /tmp/pi-web-ui.log, /tmp/pi-web-ui.err
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Windows — Task Scheduler
|
|
385
|
+
|
|
386
|
+
Easiest path (generates everything, no manual XML editing):
|
|
387
|
+
|
|
388
|
+
```bat
|
|
389
|
+
npm i -g pi-web-ui
|
|
390
|
+
pi-web-ui server install --port 8787 --cwd C:\path\to\project
|
|
391
|
+
pi-web-ui server status
|
|
392
|
+
pi-web-ui server restart
|
|
393
|
+
pi-web-ui server stop :: stop the running instance (auto-start stays)
|
|
394
|
+
pi-web-ui server uninstall :: remove the task entirely
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
What it does: writes `%APPDATA%\pi-web-ui\pi-web-ui.ps1` (a PowerShell launcher
|
|
398
|
+
that sets `PORT`/`PI_WEB_CWD`, cd's to the workspace, launches node and
|
|
399
|
+
appends output to `%USERPROFILE%\pi-web-ui.log`) plus the Task Scheduler XML,
|
|
400
|
+
then registers a **logon** task (`schtasks /Create /XML` — the task runs when
|
|
401
|
+
you log in, same as a launchd agent; usually no admin needed, but see the
|
|
402
|
+
troubleshooting note below if you get access denied). The task invokes
|
|
403
|
+
`powershell.exe -WindowStyle Hidden`, so the server runs with **no black
|
|
404
|
+
console window** — there is nothing to accidentally close or kill. Preview
|
|
405
|
+
both generated files without installing: `pi-web-ui server install --print`.
|
|
406
|
+
|
|
407
|
+
Manual alternative with `deploy/pi-web-ui-task.xml`: edit the paths, save the
|
|
408
|
+
file as **UTF-16 LE** (schtasks requires it), then
|
|
409
|
+
`schtasks /Create /TN "pi-web-ui" /XML pi-web-ui-task.xml /F` and
|
|
410
|
+
`schtasks /Run /TN "pi-web-ui"`.
|
|
411
|
+
|
|
412
|
+
> **Windows troubleshooting**
|
|
413
|
+
>
|
|
414
|
+
> - **`install` fails with `ERROR: Access is denied` (错误: 拒绝访问)** — on
|
|
415
|
+
> some machines Task Scheduler refuses to let a non-elevated token create
|
|
416
|
+
> tasks (deleting your own task with `schtasks /Delete` still works, which
|
|
417
|
+
> is why `server uninstall` succeeds). Fix: run
|
|
418
|
+
> `pi-web-ui server install` from an **elevated (admin) PowerShell**.
|
|
419
|
+
> - **Always pass `--cwd` explicitly, and point it at a user-writable
|
|
420
|
+
> directory.** The task inherits the installing shell's current directory
|
|
421
|
+
> as its working directory. Installing from an elevated shell without
|
|
422
|
+
> `--cwd` registers the task with `C:\WINDOWS\system32`, and the server
|
|
423
|
+
> then fails at startup with
|
|
424
|
+
> `EPERM: operation not permitted, mkdir 'C:\WINDOWS\system32\.pi-web\sessions\...'`
|
|
425
|
+
> because the logon task runs with a least-privilege token that cannot
|
|
426
|
+
> write under `system32`. Use e.g. `--cwd C:\Users\<you>` (sessions then
|
|
427
|
+
> go to `C:\Users\<you>\.pi-web`).
|
|
428
|
+
> - **Fix an already-broken task** (task created with the wrong directory):
|
|
429
|
+
> `pi-web-ui server uninstall`, then
|
|
430
|
+
> `pi-web-ui server install --cwd C:\Users\<you>` from an elevated shell.
|
|
431
|
+
> Rerunning `install` with new options also regenerates the task in place.
|
|
432
|
+
|
|
433
|
+
> **Boot-start without login?** A logon task needs an interactive session, just
|
|
434
|
+
> like a launchd agent. For headless/always-on Windows use Docker (see above).
|
|
435
|
+
|
|
436
|
+
The three templates use `KeepAlive` / `Restart=on-failure` / `RestartOnFailure`
|
|
437
|
+
so the server survives crashes, and start at login/boot automatically.
|
|
438
|
+
|
|
439
|
+
## License
|
|
440
|
+
|
|
441
|
+
MIT
|