@firstpick/pi-package-webui 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,115 +1,59 @@
1
1
  # @firstpick/pi-package-webui
2
2
 
3
- Local browser companion for [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
3
+ Local browser UI for [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
4
4
 
5
5
  ![Pi Web UI main window showing multi-tab chat, controls, theme picker, and local status](https://unpkg.com/@firstpick/pi-package-webui/images/Main_Window_v0.1.7.png)
6
6
 
7
- This package provides:
7
+ Pi Web UI gives you a local browser companion for Pi: multi-tab chat, streaming output, model controls, uploads, slash-command helpers, workspace navigation, and optional extension widgets.
8
8
 
9
- - `pi-webui`: a local HTTP/SSE server that starts `pi --mode rpc`, serves the static browser UI, and proxies browser actions to Pi RPC commands.
10
- - `/webui-start`: a Pi slash command that launches `pi-webui` for the current Pi working directory and opens the browser.
11
- - `/webui-status`: a Pi slash command that reports the Web UI URL, online state, network exposure, and optional detailed runtime info.
12
- - A no-build web app in `public/` with no runtime frontend dependencies.
13
-
14
- > **Security:** Pi Web UI has no authentication. It can control the spawned Pi session, including any tools Pi is allowed to run. It binds to `127.0.0.1` by default; do not expose it on untrusted networks.
9
+ > **Security:** Pi Web UI has no authentication. It can control the spawned Pi session and run anything that session is allowed to run. It binds to `127.0.0.1` by default; only expose it on trusted networks.
15
10
 
16
11
  ## Requirements
17
12
 
18
13
  - Node.js `>=22.19.0`
19
- - Pi available through this package dependency or as `pi` on `PATH`
14
+ - Pi installed and configured
20
15
  - A modern browser with Server-Sent Events support
21
16
 
22
- ## Optional companion packages
23
-
24
- The Web UI declares its companion Pi packages as npm `optionalDependencies`. A normal npm/Pi install will install them, while minimal installs can skip them with npm's optional-dependency controls such as `npm install --omit=optional`.
25
-
26
- At startup, the browser checks loaded Pi capabilities directly through RPC-visible commands and live widget events; it does not inspect npm package folders. That means locally symlinked/dev packages and separately installed Pi packages work as long as their commands/widgets are loaded in the active Pi tab.
27
-
28
- The side panel shows each optional feature as enabled, disabled, or install-needed. Disabling a feature is Web UI-local and hides Web UI affordances/specialized renderers without uninstalling or unloading the underlying Pi package. Installing a missing feature is an explicit, warned action: the server runs npm install for the whitelisted package from localhost only, then prompts you to `/reload` the active Pi tab so newly installed resources can load.
29
-
30
- Optional companions:
31
-
32
- - `@firstpick/pi-prompts-git-pr` for the guided Git workflow's `/git-staged-msg` prompt.
33
- - `@firstpick/pi-extension-release-npm` and `@firstpick/pi-extension-release-aur` for Publish menu commands and live release widgets.
34
- - `@firstpick/pi-extension-todo-progress` for the specialized todo-progress widget.
35
- - `@firstpick/pi-extension-git-footer-status` and `@firstpick/pi-extension-stats` for richer Pi status/footer and stats commands.
36
- - `@firstpick/pi-themes-bundle` for theme resources used by the browser theme picker and Pi themes.
37
-
38
- ## Quick start
17
+ ## Install
39
18
 
40
- Install the package from npm into Pi, then restart Pi so `/webui-start` and `/webui-status` are loaded:
19
+ Install the package into Pi:
41
20
 
42
21
  ```bash
43
22
  pi install npm:@firstpick/pi-package-webui
44
23
  ```
45
24
 
46
- From inside terminal Pi:
47
-
48
- ```text
49
- /webui-start
50
- ```
25
+ Restart Pi after installation so the Web UI commands are loaded.
51
26
 
52
- Open the printed URL, usually <http://127.0.0.1:31415/>. The command opens it automatically unless `--no-open` is passed. Check a running instance with `/webui-status` or `/webui-status detailed`.
27
+ ## Start from Pi
53
28
 
54
- For direct development from this package directory:
29
+ Run this inside Pi:
55
30
 
56
- ```bash
57
- node bin/pi-webui.mjs --cwd /path/to/project
31
+ ```text
32
+ /webui-start
58
33
  ```
59
34
 
60
- Or use the helper script in dev mode. `--dev` bypasses any `pi-webui` on `PATH` and runs this checkout's `bin/pi-webui.mjs`:
61
-
62
- ```bash
63
- ./start-webui.sh --dev --cwd /path/to/project
64
- ```
35
+ Open the printed URL, usually <http://127.0.0.1:31415/>. The command opens your browser automatically unless you pass `--no-open`.
65
36
 
66
- After a global npm install:
37
+ Check a running Web UI with:
67
38
 
68
- ```bash
69
- npm install -g @firstpick/pi-package-webui
70
- pi-webui --cwd /path/to/project
39
+ ```text
40
+ /webui-status
41
+ /webui-status detailed
71
42
  ```
72
43
 
73
- ## Features
74
-
75
- - Local browser chat over Pi RPC with isolated terminal tabs; each tab has its own `pi --mode rpc` subprocess, event stream, session state, prompt draft, cwd, and activity indicator.
76
- - Automatic tab naming from the first prompt on default-named tabs, plus `/name <title>` to manually sync the Pi session and browser tab name.
77
- - Live transcript with streamed assistant text/thinking, Markdown output, active-run status, tool/bash cards, queue/compaction events, jump-to-latest, sticky last-prompt navigation, and abort by button, Esc, or long press.
78
- - Prompt composer for prompts, steer/follow-up, busy-session behavior, model/thinking controls, manual compact/new session, uploads by button/drag/drop/paste, slash-command autocomplete, and `@` file/path references with live suggestions.
79
- - Browser-native selector dialogs for `/model`, `/settings`, `/theme`, `/fork`, `/clone`, `/resume`, `/tree`, and `/scoped-models`; `/login`/`/logout` show non-secret guidance instead of accepting credentials in the browser.
80
- - Native TUI parity tracking through `WEBUI_TUI_NATIVE_PARITY.json`, used as the source of truth for native command discovery and exposed at `GET /api/native-parity`.
81
- - Initial `/export` parity: `/export` creates an HTML browser download through a short-lived opaque token, while explicit new `.html`/`.jsonl` paths write server-side from localhost only.
82
- - Initial `!` / `!!` bash parity: leading `!cmd` runs queued RPC bash and includes output in the next LLM context; `!!cmd` runs with `excludeFromContext`; the active bash can be aborted from the Web UI.
83
- - Initial native shortcut parity for Ctrl/Cmd+L model selector, Ctrl/Cmd+P and Shift+Ctrl/Cmd+P model cycling, Shift+Tab thinking cycling, Ctrl/Cmd+T thinking visibility, Ctrl/Cmd+O tool/bash expansion, Alt+Enter follow-up, degraded Alt+Up queue restore, and Ctrl/Cmd+C prompt clear when no text is selected.
84
- - Session/workspace helpers for per-tab cwd changes, a clickable footer cwd picker with server-persisted fast picks, fork/clone/resume/tree navigation, and restart-safe restoration of currently open tabs.
85
- - Collapsible side-panel control deck for model/thinking/settings, optional features, Codex usage, session/queue/commands/events, local-network exposure, browser notifications, and Web UI themes/custom backgrounds.
86
- - Pi-style footer with token/cache/context/cost/speed telemetry, estimated Pi-context tokens, cwd/git/runtime/model/thinking metadata, and a scoped-model picker.
87
- - Optional companion management with capability-based enabled/disabled/install-needed status, localhost-only warned installs, Side-panel theme picker backed by optional `@firstpick/pi-themes-bundle` themes when loaded, guided Git commit/push workflow, NPM/AUR Publish menu, todo-progress rendering, and richer git/status/stats widgets.
88
- - Extension UI bridge for `notify`, `setStatus`, `setWidget`, `setTitle`, `set_editor_text`, `select`, `confirm`, `input`, and `editor`, with browser notifications when a tab needs an extension UI response and an optional side-panel toggle for agent-done notifications.
89
- - Feedback reactions (`👍`, `👎`, `?`) on final assistant output plus tool/bash action cards, with queued post-run submission that asks Pi to create/update a LEARNING.
90
- - Mobile/PWA support: installable app shell, service worker/icons, backend-offline recovery panel, touch-friendly composer/tabs/footer, and a static frontend with no bundler or frontend install step.
91
-
92
- ## Mobile/PWA notes
93
-
94
- - The mobile composer starts as a one-line `Ask Pi…` input, grows with user-entered lines, and scrolls the transcript to the latest output when focused.
95
- - When Pi is idle, `Steer` and `Follow-up` live inside `Actions`; while a run is active, they move back into the main composer row for quick steering/follow-up.
96
- - PWA install support, blocked-tab browser notifications, and optional agent-done notifications require browser service-worker/notification support and usually HTTPS or `localhost`. Plain `http://<LAN-IP>` may show the app but may not offer install or notifications on Chrome/Safari.
97
-
98
- ## Pi slash commands
44
+ ### `/webui-start` options
99
45
 
100
46
  ```text
101
47
  /webui-start [port] [options] [-- <pi args...>]
102
48
  ```
103
49
 
104
- Options:
105
-
106
50
  ```text
107
- [port] Positional port shortcut
51
+ [port] Port shortcut
108
52
  --host <host> HTTP bind host (default: 127.0.0.1)
109
53
  --port <port> HTTP port (default: 31415)
110
54
  --no-open Do not open the browser automatically
111
55
  --no-session Start Pi RPC with --no-session
112
- --name <name> Initial Web UI tab display name
56
+ --name <name> Initial Web UI tab name
113
57
  -- <pi args...> Extra arguments forwarded to Pi RPC
114
58
  ```
115
59
 
@@ -122,33 +66,36 @@ Examples:
122
66
  /webui-start --name browser -- --model anthropic/claude-sonnet-4-5:high
123
67
  ```
124
68
 
125
- If a compatible Web UI is already running on the target URL, `/webui-start` captures its currently open terminal tabs, stops that instance, then starts a fresh server and reopens only those open tabs from their session files when available. Tabs you closed in the Web UI stay closed; use `/resume` if you want to reopen an older Pi session manually.
69
+ Running `/webui-start` again on the same URL restarts the server and restores currently open Web UI tabs from their session files when possible.
126
70
 
127
- Status commands:
71
+ ### `/webui-status` options
128
72
 
129
73
  ```text
130
- /webui-status
131
- /webui-status detailed
132
- /webui-status detailed --port 31500
74
+ /webui-status [detailed] [port] [--port N] [--host HOST]
133
75
  ```
134
76
 
135
- `/webui-status` reports the page URL, whether the server is online, and whether it is open to the local network. `detailed` adds Web UI/Pi PIDs, bind info, tabs, current session/model/thinking state, available providers, per-tab workspace/stats summaries, and recent backend events.
77
+ `/webui-status` reports the URL, online state, and network exposure. `detailed` adds tabs, sessions, models/providers, and recent backend events.
136
78
 
137
- ## CLI
79
+ ## Standalone CLI
80
+
81
+ Use the CLI when you want to start the Web UI without first opening terminal Pi:
82
+
83
+ ```bash
84
+ npm install -g @firstpick/pi-package-webui
85
+ pi-webui --cwd ~/src/my-project
86
+ ```
138
87
 
139
88
  ```text
140
89
  pi-webui [options] [-- <pi args...>]
141
90
  ```
142
91
 
143
- Options:
144
-
145
92
  ```text
146
93
  --host <host> HTTP bind host (default: 127.0.0.1)
147
94
  --port <port> HTTP port (default: 31415)
148
95
  --cwd <path> Default working directory for Pi tabs (default: current dir)
149
96
  --pi <command> Pi executable to spawn (default: bundled dependency, then "pi")
150
97
  --no-session Start Pi RPC with --no-session
151
- --name <name> Initial Web UI tab display name
98
+ --name <name> Initial Web UI tab name
152
99
  -h, --help Show help
153
100
  -v, --version Print version
154
101
  ```
@@ -165,66 +112,63 @@ Environment variables:
165
112
 
166
113
  - `PI_WEBUI_HOST`
167
114
  - `PI_WEBUI_PORT`
168
- - `PI_WEBUI_PI_BIN` (same purpose as `--pi`)
115
+ - `PI_WEBUI_PI_BIN`
169
116
 
170
- ## Guided Git workflow
117
+ ## Main features
171
118
 
172
- The browser button runs a native local workflow in the Web UI server process:
119
+ - Multi-tab Pi sessions with isolated processes, working directories, prompt drafts, and activity state.
120
+ - Streaming chat transcript with Markdown, thinking output, tool/bash cards, queue and compaction events, and abort controls.
121
+ - Prompt composer with uploads, drag/drop/paste, inline image support, slash-command autocomplete, and `@` file/path references.
122
+ - Browser dialogs for common Pi selectors such as `/model`, `/settings`, `/theme`, `/fork`, `/clone`, `/resume`, `/tree`, and `/scoped-models`.
123
+ - Model, thinking, session, workspace, theme, optional-feature, Codex usage, network, event, and notification controls in the side panel.
124
+ - Per-tab cwd changes, a clickable footer cwd picker, saved path fast picks, and restart-safe restoration of open tabs.
125
+ - Browser support for Pi extension UI prompts, widgets, status updates, and notifications.
126
+ - Feedback reactions (`👍`, `👎`, `?`) on assistant output and action cards, which can ask Pi to create or update a LEARNING.
127
+ - Mobile-friendly layout and PWA install support where the browser allows it.
173
128
 
174
- 1. `git add .` in the active Pi working directory.
175
- 2. Send `/git-staged-msg` to Pi.
176
- 3. Read `dev/COMMIT/staged-commit-short.txt` and `dev/COMMIT/staged-commit-long.txt` from the git root.
177
- 4. Commit with either the short message (`git commit -m ...`) or the long message (`git commit -F ...`).
178
- 5. Run `git push`.
129
+ ## Optional companion features
179
130
 
180
- This workflow requires `/git-staged-msg` from `@firstpick/pi-prompts-git-pr`, which writes the two `dev/COMMIT/` files above. The Web UI enables the Git workflow button only when that command is loaded in the active Pi tab. If package resources are filtered or optional dependencies were omitted, make sure `/git-staged-msg` remains enabled. The workflow can be cancelled between steps; active native git commands are terminated on cancel where possible.
131
+ A normal Pi/npm install includes the optional companion packages unless optional dependencies are disabled. If a feature is missing, the side panel shows it as install-needed. Installing from the side panel is localhost-only, limited to known packages, and requires reloading the active Pi tab after installation.
181
132
 
182
- ## How it works
133
+ Optional companions:
183
134
 
184
- `pi-webui` starts a Pi RPC subprocess for the initial browser terminal tab, and each additional Web UI tab starts another isolated subprocess:
135
+ - `@firstpick/pi-prompts-git-pr` guided Git commit/push workflow.
136
+ - `@firstpick/pi-extension-release-npm` — NPM publish menu and release widgets.
137
+ - `@firstpick/pi-extension-release-aur` — AUR publish menu and release widgets.
138
+ - `@firstpick/pi-extension-todo-progress` — todo-progress rendering.
139
+ - `@firstpick/pi-extension-git-footer-status` — richer git/footer status.
140
+ - `@firstpick/pi-extension-stats` — stats commands and status data.
141
+ - `@firstpick/pi-themes-bundle` — Web UI and Pi theme resources.
185
142
 
186
- ```bash
187
- pi --mode rpc
188
- ```
143
+ ## Guided Git workflow
189
144
 
190
- With options, each spawned command becomes:
145
+ The Git workflow button runs local git commands in the active Pi working directory:
191
146
 
192
- ```bash
193
- pi --mode rpc [--no-session] [...extra Pi args]
194
- ```
147
+ 1. `git add .`
148
+ 2. Send `/git-staged-msg` to Pi
149
+ 3. Read the generated commit message files from `dev/COMMIT/`
150
+ 4. Commit with the selected message
151
+ 5. Run `git push`
195
152
 
196
- Web UI tab titles are stored in Web UI metadata instead of being forwarded as Pi CLI `--name` flags, so multiple tabs remain compatible with bundled Pi CLI versions that do not support session naming.
197
-
198
- The local server exposes:
199
-
200
- - static files from `public/`
201
- - `GET /api/tabs`, `POST /api/tabs`, `PATCH /api/tabs/:id`, and `DELETE /api/tabs/:id` for isolated Web UI terminal tabs and per-tab cwd changes; default-named tabs are auto-renamed from the first conversation prompt
202
- - `GET /api/directories?tab=<tabId>&path=<path>` for the browser cwd picker
203
- - `GET /api/path-suggestions?tab=<tabId>&query=<path>` for `@` file/path reference autocomplete in the prompt composer
204
- - `GET /api/path-fast-picks` and `POST /api/path-fast-picks` for cwd picker fast picks persisted across browser tabs, Pi terminal tabs, and Web UI server restarts
205
- - `POST /api/attachments` for browser-selected, pasted, or dropped files; files are stored under the OS temp directory and referenced in the prompt, while supported images can also be sent inline via RPC `images`
206
- - `GET /api/themes` for optional theme data from `@firstpick/pi-themes-bundle` when available
207
- - `GET /api/native-parity` for the machine-readable native TUI parity matrix that drives native command discovery
208
- - `GET /api/native-download/<token>` for short-lived opaque native command artifacts such as browser `/export` downloads
209
- - `GET /api/fork-messages`, `POST /api/fork`, `POST /api/clone`, `GET /api/sessions`, `POST /api/switch-session`, `GET /api/session-tree`, and `POST /api/tree-navigate` for browser-native native slash selectors
210
- - localhost-only `POST /api/optional-feature-install` for explicit, warned installation of whitelisted optional feature packages
211
- - `GET /api/network` and localhost-only `POST /api/network/open` for local-network exposure status/control
212
- - `GET /api/webui-status?detailed=1` for slash-command status reporting
213
- - `POST /api/shutdown` for localhost-only graceful restarts from `/webui-start`; restart captures detailed open-tab status first so currently open tabs can be restored with their session files
214
- - HTTP endpoints for prompt/session/model/thinking/compact/git actions; tab-scoped calls use `?tab=<tabId>`
215
- - `POST /api/bash?tab=<tabId>` and `POST /api/abort-bash?tab=<tabId>` for leading `!` / `!!` user bash parity with server-side one-active-per-tab FIFO queuing
216
- - `POST /api/model-cycle?tab=<tabId>` and `POST /api/thinking-cycle?tab=<tabId>` for native shortcut model/thinking cycling
217
- - `POST /api/action-feedback?tab=<tabId>` to turn queued action/final-output reactions into a Pi prompt that creates/updates a LEARNING after the run is idle
218
- - `/api/events?tab=<tabId>` as a per-tab Server-Sent Events stream for Pi RPC events
219
- - `/api/extension-ui-response?tab=<tabId>` for browser responses to extension UI prompts
220
-
221
- Pi stdout is read as JSONL and split only on `\n`, matching Pi RPC framing.
222
-
223
- ## Network and safety notes
153
+ This requires `/git-staged-msg` from `@firstpick/pi-prompts-git-pr`. Review the generated commit message before committing or pushing.
154
+
155
+ ## Mobile and PWA notes
156
+
157
+ - The mobile composer starts as a compact `Ask Pi…` input and grows as you type.
158
+ - Installable PWA support and notifications depend on browser support and usually require `localhost` or HTTPS.
159
+ - Plain `http://<LAN-IP>` can show the app, but some browsers disable PWA install and notifications there.
160
+
161
+ ## Network safety
224
162
 
225
163
  - Default bind is localhost-only: `127.0.0.1:31415`.
226
- - The side-panel "Open to network" button rebinds the current server to `0.0.0.0`, shows LAN URLs when available, and toggles to "Close for network" to rebind back to localhost-only access.
227
- - `--host 0.0.0.0` also makes the UI reachable from the network and is unsafe unless the network is trusted.
228
- - The UI is intended as a local companion, not a hardened multi-user web service.
229
- - Browser actions can trigger Pi tools, shell commands, file edits, and git operations according to the spawned Pi session's permissions.
230
- - If the Web UI is exposed to a LAN, any connected browser client can use `!` / `!!` bash and the `/api/bash` endpoint to run shell commands as the Web UI process user.
164
+ - The side-panel **Open to network** button rebinds the server to `0.0.0.0`, shows LAN URLs when available, and toggles to "Close for network".
165
+ - `--host 0.0.0.0` also exposes the Web UI to the local network.
166
+ - Any connected browser client can control Pi and run Web UI bash actions as the Web UI process user.
167
+ - Treat Pi Web UI as a local companion, not a hardened multi-user web service.
168
+
169
+ ## Troubleshooting
170
+
171
+ - **`/webui-start` is missing:** restart Pi after installing the package.
172
+ - **Wrong port or existing server:** use `/webui-status detailed`, or start on another port with `/webui-start --port 31500`.
173
+ - **Optional feature is disabled or missing:** check the side panel, install the companion package if needed, then run `/reload` in the active Pi tab.
174
+ - **PWA install or notifications are unavailable:** use `localhost` or HTTPS; browser support varies on LAN HTTP URLs.
package/bin/pi-webui.mjs CHANGED
@@ -17,6 +17,7 @@ const packageRoot = path.resolve(__dirname, "..");
17
17
  const publicDir = path.join(packageRoot, "public");
18
18
  const packageJson = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
19
19
  const nativeParityMatrix = JSON.parse(await readFile(path.join(packageRoot, "WEBUI_TUI_NATIVE_PARITY.json"), "utf8"));
20
+ const webuiDevServer = isTruthyEnv(process.env.PI_WEBUI_DEV) || isSourceCheckout(packageRoot);
20
21
 
21
22
  const DEFAULT_HOST = "127.0.0.1";
22
23
  const DEFAULT_PORT = 31415;
@@ -100,6 +101,15 @@ const MIME_TYPES = new Map([
100
101
  [".webmanifest", "application/manifest+json; charset=utf-8"],
101
102
  ]);
102
103
 
104
+ function isTruthyEnv(value) {
105
+ return ["1", "true", "yes", "dev"].includes(String(value || "").trim().toLowerCase());
106
+ }
107
+
108
+ function isSourceCheckout(root) {
109
+ const normalized = String(root || "").replace(/\\/g, "/");
110
+ return normalized.includes("/npm-packages/") && !normalized.includes("/node_modules/");
111
+ }
112
+
103
113
  function nativeParitySurfaces(matrix = nativeParityMatrix) {
104
114
  return Array.isArray(matrix?.surfaces) ? matrix.surfaces : [];
105
115
  }
@@ -2004,6 +2014,12 @@ if (options.version) {
2004
2014
  process.exit(0);
2005
2015
  }
2006
2016
 
2017
+ const startupDelayMs = Number.parseInt(process.env.PI_WEBUI_START_DELAY_MS || "", 10);
2018
+ delete process.env.PI_WEBUI_START_DELAY_MS;
2019
+ if (Number.isFinite(startupDelayMs) && startupDelayMs > 0) {
2020
+ await delay(Math.min(startupDelayMs, 10_000));
2021
+ }
2022
+
2007
2023
  const restoreTabs = readRestoreTabsFromEnv();
2008
2024
 
2009
2025
  function normalizedRestoreString(value, maxLength) {
@@ -2547,6 +2563,33 @@ function mergeRestorableTabDescriptors(...sources) {
2547
2563
  .slice(0, RESTORE_TAB_LIMIT);
2548
2564
  }
2549
2565
 
2566
+ async function restorableTabsForRestart() {
2567
+ const liveDescriptors = await Promise.all([...tabs.values()].map(async (tab) => {
2568
+ const state = await currentSessionState(tab).catch(() => tab.lastState || null);
2569
+ return restorableTabDescriptor(tab, state);
2570
+ }));
2571
+ return mergeRestorableTabDescriptors(liveDescriptors, closedRestorableTabs);
2572
+ }
2573
+
2574
+ function spawnRestartServer(restorableTabs) {
2575
+ const env = {
2576
+ ...process.env,
2577
+ PI_WEBUI_RESTORE_TABS: JSON.stringify(restorableTabs || []),
2578
+ PI_WEBUI_START_DELAY_MS: "1200",
2579
+ };
2580
+ if (webuiDevServer) env.PI_WEBUI_DEV = "1";
2581
+ else delete env.PI_WEBUI_DEV;
2582
+ const child = spawn(process.execPath, process.argv.slice(1), {
2583
+ cwd: process.cwd(),
2584
+ env,
2585
+ detached: true,
2586
+ stdio: "ignore",
2587
+ windowsHide: true,
2588
+ });
2589
+ child.unref();
2590
+ return child;
2591
+ }
2592
+
2550
2593
  function rememberClosedRestorableTab(tab, state = null) {
2551
2594
  const descriptor = restorableTabDescriptor(tab, state);
2552
2595
  if (!descriptor) return;
@@ -3461,6 +3504,8 @@ async function webuiStatus({ detailed = false, eventLimit = 40 } = {}) {
3461
3504
  const data = {
3462
3505
  online: true,
3463
3506
  webuiVersion: packageJson.version,
3507
+ webuiDev: webuiDevServer,
3508
+ webuiMode: webuiDevServer ? "dev" : "production",
3464
3509
  webuiPid: process.pid,
3465
3510
  startedAt: serverStartedAt,
3466
3511
  cwd: options.cwd,
@@ -3537,6 +3582,8 @@ const server = createServer(async (req, res) => {
3537
3582
  sendSse(res, {
3538
3583
  type: "webui_connected",
3539
3584
  version: packageJson.version,
3585
+ webuiDev: webuiDevServer,
3586
+ webuiMode: webuiDevServer ? "dev" : "production",
3540
3587
  tabId: tab.id,
3541
3588
  tabTitle: tab.title,
3542
3589
  pid: tab.rpc.child?.pid,
@@ -3559,6 +3606,8 @@ const server = createServer(async (req, res) => {
3559
3606
  sendJson(res, 200, {
3560
3607
  ok: true,
3561
3608
  webuiVersion: status.webuiVersion,
3609
+ webuiDev: status.webuiDev,
3610
+ webuiMode: status.webuiMode,
3562
3611
  webuiPid: status.webuiPid,
3563
3612
  piPid: status.piPid,
3564
3613
  piRunning: status.piRunning,
@@ -3629,6 +3678,15 @@ const server = createServer(async (req, res) => {
3629
3678
  return;
3630
3679
  }
3631
3680
 
3681
+ if (url.pathname === "/api/restart" && req.method === "POST") {
3682
+ if (!isLocalAddress(req.socket.remoteAddress)) throw makeHttpError(403, "Restart is only allowed from localhost");
3683
+ const restorableTabs = await restorableTabsForRestart();
3684
+ const child = spawnRestartServer(restorableTabs);
3685
+ sendJson(res, 200, { ok: true, message: "Pi Web UI restarting", webuiPid: process.pid, nextWebuiPid: child.pid, restorableTabCount: restorableTabs.length });
3686
+ setTimeout(() => shutdown("api restart"), 20).unref();
3687
+ return;
3688
+ }
3689
+
3632
3690
  if (url.pathname === "/api/shutdown" && req.method === "POST") {
3633
3691
  if (!isLocalAddress(req.socket.remoteAddress)) throw makeHttpError(403, "Shutdown is only allowed from localhost");
3634
3692
  sendJson(res, 200, { ok: true, message: "Pi Web UI shutting down", webuiPid: process.pid });
@@ -3879,7 +3937,15 @@ server.listen(options.port, currentHost, () => {
3879
3937
 
3880
3938
  function shutdown(signal) {
3881
3939
  console.log(`\n${signal}: shutting down Pi Web UI...`);
3882
- server.close(() => process.exit(0));
3940
+ const forceCloseTimer = setTimeout(() => {
3941
+ server.closeAllConnections?.();
3942
+ }, NETWORK_REBIND_FORCE_CLOSE_MS);
3943
+ forceCloseTimer.unref?.();
3944
+ server.close(() => {
3945
+ clearTimeout(forceCloseTimer);
3946
+ process.exit(0);
3947
+ });
3948
+ server.closeIdleConnections?.();
3883
3949
  for (const tab of tabs.values()) tab.rpc.stop();
3884
3950
  setTimeout(() => process.exit(0), 4000).unref();
3885
3951
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firstpick/pi-package-webui",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Pi Web UI companion package with a local browser UI CLI plus /webui-start and /webui-status commands.",
5
5
  "license": "MIT",
6
6
  "type": "module",