@firstpick/pi-package-webui 0.4.2 → 0.4.4
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 +10 -6
- package/bin/pi-webui.mjs +100 -15
- package/{WEBUI_TUI_NATIVE_PARITY.json → dev/docs/WEBUI_TUI_NATIVE_PARITY.json} +2 -2
- package/package.json +10 -6
- package/public/app.js +1000 -50
- package/public/index.html +20 -5
- package/public/styles.css +322 -56
- package/tests/http-endpoints-harness.test.mjs +2 -0
- package/tests/mobile-static.test.mjs +90 -19
- package/tests/native-parity-harness.test.mjs +1 -1
- package/tests/native-parity.test.mjs +2 -2
- package/tests/remote-auth-settings-harness.test.mjs +81 -0
- package/start-webui.ps1 +0 -368
- package/start-webui.sh +0 -472
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Local browser UI for [Pi coding agent](https://www.npmjs.com/package/@earendil-w
|
|
|
6
6
|
|
|
7
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
|
-
> **Security:** Pi Web UI can control the spawned Pi session and run anything that session is allowed to run. It binds to `127.0.0.1` by default. Remote PIN authentication is off by default;
|
|
9
|
+
> **Security:** Pi Web UI can control the spawned Pi session and run anything that session is allowed to run. It binds to `127.0.0.1` by default. Remote PIN authentication is off by default on first use; enabling it in **Controls → Network → Remote PIN auth** persists that preference for later Web UI starts.
|
|
10
10
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
|
@@ -123,6 +123,7 @@ Environment variables:
|
|
|
123
123
|
- `PI_WEBUI_PORT`
|
|
124
124
|
- `PI_WEBUI_PI_BIN`
|
|
125
125
|
- `PI_WEBUI_REMOTE_AUTH=1` to start with remote PIN authentication enabled
|
|
126
|
+
- `PI_WEBUI_SETTINGS_FILE=/path/to/settings.json` to override where Web UI stores persisted settings such as the Remote PIN auth preference
|
|
126
127
|
|
|
127
128
|
## Main features
|
|
128
129
|
|
|
@@ -130,7 +131,7 @@ Environment variables:
|
|
|
130
131
|
- Multi-tab Pi sessions with isolated processes, working directories, prompt drafts, activity state, and a workspace dashboard for common actions.
|
|
131
132
|
- Unified command palette (`Ctrl/Cmd+K`) for commands, tabs, models, sessions, settings, and frequent Web UI actions.
|
|
132
133
|
- Automatic tab naming from the first prompt, with `--name <name>` still available for an explicit initial tab name.
|
|
133
|
-
- Streaming chat transcript with Markdown, thinking output, tool/bash cards, queue and compaction events, edit-and-retry from user prompts, and abort controls.
|
|
134
|
+
- Streaming chat transcript with Markdown, thinking output, tool/bash cards, queue and compaction events, edit-and-retry from user prompts, and guarded abort controls that require holding Esc or the Abort button for 3 seconds.
|
|
134
135
|
- Prompt composer with uploads, drag/drop/paste, inline image support, slash-command autocomplete, and `@` file/path references with live suggestions.
|
|
135
136
|
- Browser dialogs for common Pi selectors such as `/model`, `/settings`, `/theme`, `/fork`, `/clone`, `/resume`, `/tree`, `/scoped-models`, `/tools`, and `/skills`.
|
|
136
137
|
- Model, thinking, session, workspace, theme, optional-feature, Codex usage, network, update/restart, event, and notification controls in the side panel.
|
|
@@ -138,7 +139,7 @@ Environment variables:
|
|
|
138
139
|
- Side-panel theme picker backed by optional `@firstpick/pi-themes-bundle` themes when loaded.
|
|
139
140
|
- Per-tab cwd changes, a clickable footer cwd picker, saved path fast picks, server-persisted fast picks, and restart-safe restoration of open tabs.
|
|
140
141
|
- Detected app runner dropdown for the active tab cwd, including Cargo, Bun, npm/npx/pnpm, Python/uv, Go/Golang, Zig, C/C++, Docker Compose, root/dev/scripts shell scripts, and other common project runners with live output pinned at the top of the terminal. Projects can add browseable custom runners in `.pi-webui-runners.json` with a command (default `./`) plus a relative path to the file to run.
|
|
141
|
-
- Browser support for Pi extension UI prompts, widgets, status updates, browser notifications when a tab needs an extension UI response and an optional side-panel toggle for agent-done notifications.
|
|
142
|
+
- Browser support for Pi extension UI prompts, widgets, status updates, `/btw` side-question output widgets with optional context transfer/live steering, browser notifications when a tab needs an extension UI response, and an optional side-panel toggle for agent-done notifications.
|
|
142
143
|
- Localhost-only Pi/Web UI update checks with a top-right update notification and a confirmed **Update & restart** action that runs `pi update` plus all detected local/global Web UI and Pi package-manager updates, then restarts the Web UI server.
|
|
143
144
|
- Feedback reactions (`👍`, `👎`, `?`) on final assistant output plus tool/bash action cards, which can ask Pi to create or update a LEARNING.
|
|
144
145
|
- Mobile-friendly layout and PWA install support where the browser allows it.
|
|
@@ -155,7 +156,7 @@ Useful browser endpoints exposed by the local server include:
|
|
|
155
156
|
For local development, run the checkout helper directly, for example:
|
|
156
157
|
|
|
157
158
|
```bash
|
|
158
|
-
./start-webui.sh --dev --cwd /path/to/project
|
|
159
|
+
./dev/scripts/start-webui.sh --dev --cwd /path/to/project
|
|
159
160
|
```
|
|
160
161
|
|
|
161
162
|
Run `../dev/scripts/sync-pi-package-symlinks.sh` first when developing companion packages from this workspace. The Web UI manifest loads companions through `node_modules/` paths, and the sync script links those paths to the top-level dev packages so only one copy is loaded.
|
|
@@ -168,13 +169,16 @@ When the standalone global `pi-webui` launcher is used, optional companion insta
|
|
|
168
169
|
|
|
169
170
|
Optional companions:
|
|
170
171
|
|
|
172
|
+
- `@firstpick/pi-extension-btw` — ephemeral `/btw` side-question command with a TUI overlay, Web UI live output widget, and Transfer Context action.
|
|
171
173
|
- `@firstpick/pi-prompts-git-pr` — guided Git commit/push workflow.
|
|
172
174
|
- `@firstpick/pi-extension-release-npm` — NPM publish menu and release widgets.
|
|
173
175
|
- `@firstpick/pi-extension-release-aur` — AUR publish menu and release widgets.
|
|
176
|
+
- `@firstpick/pi-extension-workflows` — `/workflow` runtime with non-blocking Web UI subprocess-output widgets.
|
|
174
177
|
- `@firstpick/pi-extension-safety-guard` — interactive guardrails for dangerous bash commands and protected file edits.
|
|
175
178
|
- `@firstpick/pi-extension-setup-skills` — TUI `/skills` setup command alongside WebUI-native skill toggles.
|
|
176
179
|
- `@firstpick/pi-extension-todo-progress` — todo-progress rendering.
|
|
177
180
|
- `@firstpick/pi-extension-tools` — TUI `/tools` active-tool manager alongside WebUI-native tool toggles.
|
|
181
|
+
- `@firstpick/pi-package-remote-webui` — `/remote` trusted-LAN QR helper for connecting mobile browsers to Web UI.
|
|
178
182
|
- `@firstpick/pi-extension-git-footer-status` — richer extension-owned git/footer status, including the structured Web UI footer payload.
|
|
179
183
|
- `@firstpick/pi-extension-stats` — stats commands and status data.
|
|
180
184
|
- `@firstpick/pi-themes-bundle` — Web UI and Pi theme resources.
|
|
@@ -205,8 +209,8 @@ This requires `/git-staged-msg` and `/pr` from `@firstpick/pi-prompts-git-pr`; b
|
|
|
205
209
|
|
|
206
210
|
- Default bind is localhost-only: `127.0.0.1:31415`.
|
|
207
211
|
- 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".
|
|
208
|
-
- The side-panel **Remote PIN auth** toggle is off by default. When enabled, the server generates a random 4-digit PIN, shows it in Controls and `/webui-status`, and requires it from non-local browser clients.
|
|
209
|
-
- Localhost clients stay frictionless and can toggle Remote PIN auth; changing the toggle disconnects existing event streams so remote clients must re-authenticate after enablement.
|
|
212
|
+
- The side-panel **Remote PIN auth** toggle is off by default on first use. When enabled, the server saves that preference, generates a fresh random 4-digit PIN for each server start, shows it in Controls and `/webui-status`, and requires it from non-local browser clients.
|
|
213
|
+
- Localhost clients stay frictionless and can toggle Remote PIN auth; changing the toggle persists the preference and disconnects existing event streams so remote clients must re-authenticate after enablement.
|
|
210
214
|
- `--host 0.0.0.0` also exposes the Web UI to the local network; pass `--remote-auth` to start with PIN auth already enabled.
|
|
211
215
|
- Any connected browser client with access (and the PIN, if enabled) can control Pi and run Web UI bash actions as the Web UI process user.
|
|
212
216
|
- Remote PIN auth is a simple trusted-LAN HTTP gate, not hardened multi-user authentication; do not expose it to untrusted networks.
|
package/bin/pi-webui.mjs
CHANGED
|
@@ -43,6 +43,7 @@ const publicDir = path.join(packageRoot, "public");
|
|
|
43
43
|
const webuiHelperExtensionPath = path.join(packageRoot, "webui-rpc-helper.mjs");
|
|
44
44
|
const agentDir = process.env.PI_CODING_AGENT_DIR || path.join(homedir(), ".pi", "agent");
|
|
45
45
|
const OPTIONAL_FEATURE_INSTALL_ROOT_ENV = "PI_WEBUI_OPTIONAL_FEATURE_INSTALL_ROOT";
|
|
46
|
+
const WEBUI_SETTINGS_FILE_ENV = "PI_WEBUI_SETTINGS_FILE";
|
|
46
47
|
const packageJson = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
47
48
|
let piPackageJson = {};
|
|
48
49
|
try {
|
|
@@ -51,7 +52,7 @@ try {
|
|
|
51
52
|
} catch {
|
|
52
53
|
piPackageJson = {};
|
|
53
54
|
}
|
|
54
|
-
const nativeParityMatrix = JSON.parse(await readFile(path.join(packageRoot, "WEBUI_TUI_NATIVE_PARITY.json"), "utf8"));
|
|
55
|
+
const nativeParityMatrix = JSON.parse(await readFile(path.join(packageRoot, "dev", "docs", "WEBUI_TUI_NATIVE_PARITY.json"), "utf8"));
|
|
55
56
|
const webuiDevServer = isTruthyEnv(process.env.PI_WEBUI_DEV) || isSourceCheckout(packageRoot);
|
|
56
57
|
|
|
57
58
|
const DEFAULT_HOST = "127.0.0.1";
|
|
@@ -220,13 +221,16 @@ function parseSlashCommand(message) {
|
|
|
220
221
|
return parseNativeSlashCommand(message, NATIVE_SLASH_COMMAND_NAMES);
|
|
221
222
|
}
|
|
222
223
|
const OPTIONAL_FEATURE_PACKAGES = new Map([
|
|
224
|
+
["btwCommand", "@firstpick/pi-extension-btw"],
|
|
223
225
|
["gitWorkflow", "@firstpick/pi-prompts-git-pr"],
|
|
224
226
|
["releaseNpm", "@firstpick/pi-extension-release-npm"],
|
|
225
227
|
["releaseAur", "@firstpick/pi-extension-release-aur"],
|
|
228
|
+
["workflows", "@firstpick/pi-extension-workflows"],
|
|
226
229
|
["safetyGuard", "@firstpick/pi-extension-safety-guard"],
|
|
227
230
|
["tuiSkillsCommand", "@firstpick/pi-extension-setup-skills"],
|
|
228
231
|
["todoProgressWidget", "@firstpick/pi-extension-todo-progress"],
|
|
229
232
|
["tuiToolsCommand", "@firstpick/pi-extension-tools"],
|
|
233
|
+
["remoteWebui", "@firstpick/pi-package-remote-webui"],
|
|
230
234
|
["gitFooterStatus", "@firstpick/pi-extension-git-footer-status"],
|
|
231
235
|
["statsCommand", "@firstpick/pi-extension-stats"],
|
|
232
236
|
["themeBundle", "@firstpick/pi-themes-bundle"],
|
|
@@ -265,8 +269,8 @@ Examples:
|
|
|
265
269
|
|
|
266
270
|
Security:
|
|
267
271
|
The web UI controls Pi tools. It binds to localhost by default. Remote PIN
|
|
268
|
-
authentication is off by default;
|
|
269
|
-
|
|
272
|
+
authentication is off by default on first use; enabling it in Controls saves
|
|
273
|
+
that preference for later starts.
|
|
270
274
|
`);
|
|
271
275
|
}
|
|
272
276
|
|
|
@@ -289,6 +293,7 @@ function parseArgs(argv) {
|
|
|
289
293
|
noSession: false,
|
|
290
294
|
name: undefined,
|
|
291
295
|
remoteAuth: isTruthyEnv(process.env.PI_WEBUI_REMOTE_AUTH),
|
|
296
|
+
remoteAuthExplicit: process.env.PI_WEBUI_REMOTE_AUTH !== undefined,
|
|
292
297
|
piArgs: [],
|
|
293
298
|
help: false,
|
|
294
299
|
version: false,
|
|
@@ -345,10 +350,12 @@ function parseArgs(argv) {
|
|
|
345
350
|
}
|
|
346
351
|
if (arg === "--remote-auth") {
|
|
347
352
|
options.remoteAuth = true;
|
|
353
|
+
options.remoteAuthExplicit = true;
|
|
348
354
|
continue;
|
|
349
355
|
}
|
|
350
356
|
if (arg === "--no-remote-auth") {
|
|
351
357
|
options.remoteAuth = false;
|
|
358
|
+
options.remoteAuthExplicit = true;
|
|
352
359
|
continue;
|
|
353
360
|
}
|
|
354
361
|
throw new Error(`Unknown option: ${arg}. Pass Pi CLI args after --.`);
|
|
@@ -739,7 +746,7 @@ function sendRemoteAuthPage(res, returnPath = "/") {
|
|
|
739
746
|
<body>
|
|
740
747
|
<main>
|
|
741
748
|
<h1>Remote PIN required</h1>
|
|
742
|
-
<p>
|
|
749
|
+
<p>Scan a trusted /remote QR code to unlock automatically, or enter the 4-digit PIN shown in the local Pi terminal or local Web UI.</p>
|
|
743
750
|
<form id="pinForm" autocomplete="off">
|
|
744
751
|
<label for="pin">PIN</label>
|
|
745
752
|
<input id="pin" name="pin" inputmode="numeric" pattern="[0-9]{4}" maxlength="4" autofocus required>
|
|
@@ -753,16 +760,19 @@ function sendRemoteAuthPage(res, returnPath = "/") {
|
|
|
753
760
|
const input = document.getElementById("pin");
|
|
754
761
|
const button = document.getElementById("submit");
|
|
755
762
|
const error = document.getElementById("error");
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
763
|
+
function pinFromHash() {
|
|
764
|
+
const params = new URLSearchParams(String(window.location.hash || "").replace(/^#/, ""));
|
|
765
|
+
const pin = String(params.get("pin") || "").trim();
|
|
766
|
+
return /^\\d{4}$/.test(pin) ? pin : "";
|
|
767
|
+
}
|
|
768
|
+
async function submitPin(pin) {
|
|
759
769
|
button.disabled = true;
|
|
760
770
|
error.textContent = "";
|
|
761
771
|
try {
|
|
762
772
|
const response = await fetch("/api/remote-auth", {
|
|
763
773
|
method: "POST",
|
|
764
774
|
headers: { "content-type": "application/json" },
|
|
765
|
-
body: JSON.stringify({ pin
|
|
775
|
+
body: JSON.stringify({ pin }),
|
|
766
776
|
});
|
|
767
777
|
const data = await response.json().catch(() => ({}));
|
|
768
778
|
if (!response.ok || data.ok !== true) throw new Error(data.error || "Incorrect PIN");
|
|
@@ -773,7 +783,18 @@ function sendRemoteAuthPage(res, returnPath = "/") {
|
|
|
773
783
|
} finally {
|
|
774
784
|
button.disabled = false;
|
|
775
785
|
}
|
|
786
|
+
}
|
|
787
|
+
input.addEventListener("input", () => { input.value = input.value.replace(/\\D/g, "").slice(0, 4); error.textContent = ""; });
|
|
788
|
+
form.addEventListener("submit", async (event) => {
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
await submitPin(input.value);
|
|
776
791
|
});
|
|
792
|
+
const autoPin = pinFromHash();
|
|
793
|
+
if (autoPin) {
|
|
794
|
+
input.value = autoPin;
|
|
795
|
+
window.history.replaceState(null, "", window.location.pathname + (window.location.search || ""));
|
|
796
|
+
submitPin(autoPin);
|
|
797
|
+
}
|
|
777
798
|
</script>
|
|
778
799
|
</body>
|
|
779
800
|
</html>`;
|
|
@@ -1332,6 +1353,50 @@ async function writePathFastPicks(picks) {
|
|
|
1332
1353
|
return normalized;
|
|
1333
1354
|
}
|
|
1334
1355
|
|
|
1356
|
+
function webuiSettingsFile() {
|
|
1357
|
+
if (process.env[WEBUI_SETTINGS_FILE_ENV]) return path.resolve(expandUserPath(process.env[WEBUI_SETTINGS_FILE_ENV]));
|
|
1358
|
+
const configRoot = process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
|
|
1359
|
+
return path.join(configRoot, "pi-webui", "settings.json");
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
function normalizeWebuiSettings(value) {
|
|
1363
|
+
return {
|
|
1364
|
+
version: 1,
|
|
1365
|
+
remoteAuthEnabled: value?.remoteAuthEnabled === true,
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
let webuiSettingsCache = null;
|
|
1370
|
+
|
|
1371
|
+
async function readWebuiSettings() {
|
|
1372
|
+
if (webuiSettingsCache) return webuiSettingsCache;
|
|
1373
|
+
webuiSettingsCache = normalizeWebuiSettings(await readJsonFileIfExists(webuiSettingsFile()));
|
|
1374
|
+
return webuiSettingsCache;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
async function writeWebuiSettings(patch) {
|
|
1378
|
+
const current = await readWebuiSettings();
|
|
1379
|
+
const next = normalizeWebuiSettings({ ...current, ...(patch || {}) });
|
|
1380
|
+
const storageFile = webuiSettingsFile();
|
|
1381
|
+
await mkdir(path.dirname(storageFile), { recursive: true });
|
|
1382
|
+
const tmpFile = `${storageFile}.${process.pid}.${Date.now()}.tmp`;
|
|
1383
|
+
await writeFile(tmpFile, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
1384
|
+
await rename(tmpFile, storageFile);
|
|
1385
|
+
webuiSettingsCache = next;
|
|
1386
|
+
return next;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
async function readPersistedRemoteAuthEnabled() {
|
|
1390
|
+
return (await readWebuiSettings()).remoteAuthEnabled === true;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
async function saveRemoteAuthPreference(enabled) {
|
|
1394
|
+
const nextEnabled = enabled === true;
|
|
1395
|
+
await writeWebuiSettings({ remoteAuthEnabled: nextEnabled });
|
|
1396
|
+
persistedRemoteAuthEnabled = nextEnabled;
|
|
1397
|
+
return persistedRemoteAuthEnabled;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1335
1400
|
function parseCliScopedModelPatterns() {
|
|
1336
1401
|
for (let index = 0; index < options.piArgs.length; index++) {
|
|
1337
1402
|
const arg = options.piArgs[index];
|
|
@@ -5317,8 +5382,10 @@ function renameTab(tab, title, { source = "explicit", maxLength, unique = source
|
|
|
5317
5382
|
}
|
|
5318
5383
|
|
|
5319
5384
|
function maybeNameTabForConversation(tab, command) {
|
|
5320
|
-
if (!tab || !commandStartsConversation(command)
|
|
5385
|
+
if (!tab || !commandStartsConversation(command)) return false;
|
|
5386
|
+
const shouldRename = !tab.conversationStarted && tab.titleSource !== "explicit";
|
|
5321
5387
|
tab.conversationStarted = true;
|
|
5388
|
+
if (!shouldRename) return false;
|
|
5322
5389
|
const title = generatedTabTitleFromPrompt(command.message) || `Conversation ${tab.index}`;
|
|
5323
5390
|
return renameTab(tab, title, { source: "auto", maxLength: AUTO_TAB_TITLE_MAX_LENGTH });
|
|
5324
5391
|
}
|
|
@@ -6606,6 +6673,7 @@ async function createInitialTabs() {
|
|
|
6606
6673
|
}
|
|
6607
6674
|
|
|
6608
6675
|
const serverStartedAt = new Date().toISOString();
|
|
6676
|
+
let persistedRemoteAuthEnabled = await readPersistedRemoteAuthEnabled();
|
|
6609
6677
|
const initialTabs = await createInitialTabs();
|
|
6610
6678
|
const initialTab = initialTabs[0];
|
|
6611
6679
|
let currentHost = options.host;
|
|
@@ -6650,6 +6718,19 @@ function resetRemoteAuth() {
|
|
|
6650
6718
|
remoteAuth.tokenExpiresAt = 0;
|
|
6651
6719
|
}
|
|
6652
6720
|
|
|
6721
|
+
function remoteAuthPreferenceEnabled() {
|
|
6722
|
+
return persistedRemoteAuthEnabled === true;
|
|
6723
|
+
}
|
|
6724
|
+
|
|
6725
|
+
function remoteAuthStartupEnabled() {
|
|
6726
|
+
return options.remoteAuthExplicit ? options.remoteAuth === true : remoteAuthPreferenceEnabled();
|
|
6727
|
+
}
|
|
6728
|
+
|
|
6729
|
+
function remoteAuthStartupReason() {
|
|
6730
|
+
if (options.remoteAuthExplicit) return "startup option";
|
|
6731
|
+
return "saved setting";
|
|
6732
|
+
}
|
|
6733
|
+
|
|
6653
6734
|
function remoteAuthStatus({ includePin = false } = {}) {
|
|
6654
6735
|
const enabled = !!remoteAuth.pin;
|
|
6655
6736
|
const status = {
|
|
@@ -6798,9 +6879,9 @@ async function closeNetworkAccess() {
|
|
|
6798
6879
|
await closeServerListener();
|
|
6799
6880
|
await listenOn(nextHost);
|
|
6800
6881
|
currentHost = nextHost;
|
|
6801
|
-
resetRemoteAuth();
|
|
6882
|
+
if (!remoteAuthPreferenceEnabled()) resetRemoteAuth();
|
|
6802
6883
|
console.warn("Web UI network access closed; listening on localhost only.");
|
|
6803
|
-
return networkStatus();
|
|
6884
|
+
return networkStatus({ includeAuthPin: true });
|
|
6804
6885
|
} catch (error) {
|
|
6805
6886
|
console.error("Failed to close Web UI network access:", sanitizeError(error));
|
|
6806
6887
|
if (!server.listening) {
|
|
@@ -6817,7 +6898,7 @@ async function closeNetworkAccess() {
|
|
|
6817
6898
|
}
|
|
6818
6899
|
}
|
|
6819
6900
|
|
|
6820
|
-
if (
|
|
6901
|
+
if (remoteAuthStartupEnabled()) enableRemoteAuth(remoteAuthStartupReason());
|
|
6821
6902
|
|
|
6822
6903
|
async function safeRpcData(tab, command, timeoutMs = STATUS_RPC_TIMEOUT_MS) {
|
|
6823
6904
|
try {
|
|
@@ -6961,9 +7042,13 @@ const server = createServer(async (req, res) => {
|
|
|
6961
7042
|
if (url.pathname === "/api/remote-auth/settings" && req.method === "POST") {
|
|
6962
7043
|
requireLocalhostRoute(req, url.pathname);
|
|
6963
7044
|
const body = await readJsonBody(req);
|
|
6964
|
-
if (body.enabled === true)
|
|
6965
|
-
|
|
6966
|
-
|
|
7045
|
+
if (body.enabled === true) {
|
|
7046
|
+
enableRemoteAuth("side panel toggle");
|
|
7047
|
+
await saveRemoteAuthPreference(true);
|
|
7048
|
+
} else if (body.enabled === false) {
|
|
7049
|
+
resetRemoteAuth();
|
|
7050
|
+
await saveRemoteAuthPreference(false);
|
|
7051
|
+
} else throw makeHttpError(400, "enabled must be true or false");
|
|
6967
7052
|
closeSseClientsForRemoteAuthChange();
|
|
6968
7053
|
const headers = body.enabled === false ? { "set-cookie": clearRemoteAuthCookie() } : {};
|
|
6969
7054
|
sendJson(res, 200, { ok: true, data: { auth: remoteAuthStatus({ includePin: true }), network: networkStatus({ includeAuthPin: true }) } }, headers);
|
|
@@ -445,8 +445,8 @@
|
|
|
445
445
|
"priority": "P1",
|
|
446
446
|
"sensitive": false,
|
|
447
447
|
"guards": ["confirmation"],
|
|
448
|
-
"currentBehavior": "Escape aborts active user bash first, then active agent runs, and closes UI surfaces with tab scoping.",
|
|
449
|
-
"targetBehavior": "Abort active bash first where applicable; keep agent/tab scoping and clear UI cancellation rules."
|
|
448
|
+
"currentBehavior": "Holding Escape for 3 seconds aborts active user bash first, then active agent runs, and closes UI surfaces with tab scoping before arming abort.",
|
|
449
|
+
"targetBehavior": "Abort active bash first where applicable; keep agent/tab scoping and clear UI cancellation rules with a guarded hold-to-abort affordance."
|
|
450
450
|
},
|
|
451
451
|
{
|
|
452
452
|
"id": "shortcut.editor.clear",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firstpick/pi-package-webui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
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
|
"homepage": "https://github.com/Firstp1ck/npm-packages/tree/main/pi-package-webui#readme",
|
|
@@ -23,14 +23,17 @@
|
|
|
23
23
|
"image": "https://unpkg.com/@firstpick/pi-package-webui/images/WebUI_v0.3.7.png",
|
|
24
24
|
"extensions": [
|
|
25
25
|
"./index.ts",
|
|
26
|
+
"node_modules/@firstpick/pi-extension-btw/index.ts",
|
|
26
27
|
"node_modules/@firstpick/pi-extension-git-footer-status/index.ts",
|
|
27
28
|
"node_modules/@firstpick/pi-extension-release-aur/index.ts",
|
|
28
29
|
"node_modules/@firstpick/pi-extension-release-npm/index.ts",
|
|
30
|
+
"node_modules/@firstpick/pi-extension-workflows/index.ts",
|
|
29
31
|
"node_modules/@firstpick/pi-extension-safety-guard/index.ts",
|
|
30
32
|
"node_modules/@firstpick/pi-extension-setup-skills/index.ts",
|
|
31
33
|
"node_modules/@firstpick/pi-extension-stats/index.ts",
|
|
32
34
|
"node_modules/@firstpick/pi-extension-todo-progress/index.ts",
|
|
33
|
-
"node_modules/@firstpick/pi-extension-tools/index.ts"
|
|
35
|
+
"node_modules/@firstpick/pi-extension-tools/index.ts",
|
|
36
|
+
"node_modules/@firstpick/pi-package-remote-webui/index.ts"
|
|
34
37
|
],
|
|
35
38
|
"skills": [
|
|
36
39
|
"node_modules/@firstpick/pi-extension-release-aur/skills"
|
|
@@ -50,17 +53,20 @@
|
|
|
50
53
|
"test": "node tests/run-all.mjs"
|
|
51
54
|
},
|
|
52
55
|
"dependencies": {
|
|
53
|
-
"@earendil-works/pi-coding-agent": "^0.79.
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^0.79.3"
|
|
54
57
|
},
|
|
55
58
|
"optionalDependencies": {
|
|
59
|
+
"@firstpick/pi-extension-btw": "^0.1.0",
|
|
56
60
|
"@firstpick/pi-extension-git-footer-status": "^0.3.3",
|
|
57
61
|
"@firstpick/pi-extension-release-aur": "^0.1.6",
|
|
58
62
|
"@firstpick/pi-extension-release-npm": "^0.4.0",
|
|
63
|
+
"@firstpick/pi-extension-workflows": "^0.1.0",
|
|
59
64
|
"@firstpick/pi-extension-safety-guard": "^0.2.3",
|
|
60
65
|
"@firstpick/pi-extension-setup-skills": "^0.1.8",
|
|
61
66
|
"@firstpick/pi-extension-stats": "^0.2.6",
|
|
62
67
|
"@firstpick/pi-extension-todo-progress": "^0.2.4",
|
|
63
68
|
"@firstpick/pi-extension-tools": "^0.1.6",
|
|
69
|
+
"@firstpick/pi-package-remote-webui": "^0.1.0",
|
|
64
70
|
"@firstpick/pi-prompts-git-pr": "^0.1.2",
|
|
65
71
|
"@firstpick/pi-themes-bundle": "^0.1.4"
|
|
66
72
|
},
|
|
@@ -72,9 +78,7 @@
|
|
|
72
78
|
"public",
|
|
73
79
|
"images",
|
|
74
80
|
"tests",
|
|
75
|
-
"
|
|
76
|
-
"start-webui.ps1",
|
|
77
|
-
"WEBUI_TUI_NATIVE_PARITY.json",
|
|
81
|
+
"dev/docs/WEBUI_TUI_NATIVE_PARITY.json",
|
|
78
82
|
"README.md",
|
|
79
83
|
"LICENSE"
|
|
80
84
|
],
|