@jmfederico/pi-web 1.202607.2 → 1.202607.3
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/dist/client/assets/{CodeViewer-B4M13447.js → CodeViewer-CDVbMiN9.js} +1 -1
- package/dist/client/assets/{TerminalPanel-Bmvj-Ecn.js → TerminalPanel-CTS1CgqF.js} +2 -2
- package/dist/client/assets/{UnifiedDiffViewer-C0hygwRo.js → UnifiedDiffViewer-Cqke12ZX.js} +1 -1
- package/dist/client/assets/index-LME0LfPb.js +4068 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-web-plugins/info/infoInternals.js +208 -0
- package/dist/pi-web-plugins/info/pi-web-plugin.js +12 -15
- package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
- package/dist/pi-web-plugins/relays/package.json +9 -0
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
- package/dist/pi-web-plugins/relays/relayDiscovery.js +96 -0
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +393 -0
- package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
- package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
- package/dist/plugin-api.d.ts +7 -1
- package/dist/server/configRoutes.js +11 -0
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +32 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
- package/dist/server/sessiond.js +122 -114
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserTool.js +105 -0
- package/dist/server/sessions/askUserTool.js.map +1 -0
- package/dist/server/sessions/extensionDialogWaiters.js +76 -0
- package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +79 -10
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
- package/dist/server/sessions/modelCatalogRefresher.js +8 -0
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
- package/dist/server/sessions/parentSessionLocator.js +75 -0
- package/dist/server/sessions/parentSessionLocator.js.map +1 -0
- package/dist/server/sessions/pendingAskStore.js +278 -0
- package/dist/server/sessions/pendingAskStore.js.map +1 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +504 -44
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionFileHeader.js +45 -0
- package/dist/server/sessions/sessionFileHeader.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +99 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +3 -16
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +9 -0
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
- package/dist/server/workspaces/projectWorkspaceCwds.js +22 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
- package/dist/server/workspaces/workspaceService.js +15 -2
- package/dist/server/workspaces/workspaceService.js.map +1 -1
- package/dist/shared/activity.js +9 -1
- package/dist/shared/activity.js.map +1 -1
- package/dist/shared/apiTypes.d.ts +294 -1
- package/dist/shared/apiTypes.js +24 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +3 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +4 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/docs/config.md +56 -7
- package/docs/plugins.md +77 -11
- package/package.json +2 -1
- package/dist/client/assets/index-TvPsRyS1.js +0 -3545
- package/dist/server/sessiond/sessionDaemonStartup.js +0 -49
- package/dist/server/sessiond/sessionDaemonStartup.js.map +0 -1
- package/dist/server/sessions/sessionArchiveMigration.js +0 -590
- package/dist/server/sessions/sessionArchiveMigration.js.map +0 -1
package/docs/plugins.md
CHANGED
|
@@ -29,6 +29,20 @@ Use **Settings → PI WEB plugins** to enable or disable discovered PI WEB brows
|
|
|
29
29
|
|
|
30
30
|
After installing, removing, or updating a Pi package, type `/reload` in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for newly discovered or changed PI WEB browser plugins. A provider-registering Pi extension follows a separate daemon-start policy; see [Pi extension provider baseline](https://pi-web.dev/config#pi-extension-provider-baseline).
|
|
31
31
|
|
|
32
|
+
## Pi extension dialogs in PI WEB
|
|
33
|
+
|
|
34
|
+
Pi extensions running under PI WEB's session daemon can ask the user questions with `ctx.ui.confirm()`, `ctx.ui.select()`, and `ctx.ui.input()`. PI WEB reports `ctx.hasUI === true`, and for these three dialog methods that is true in fact: the call renders a dialog card inline in the session transcript and the returned Promise resolves with the user's actual answer — a boolean for confirm, the chosen option for select, the typed text for input.
|
|
35
|
+
|
|
36
|
+
- **Works from hooks, without the prompt queue.** Answers travel over a dedicated session-daemon channel, so a dialog opened inside an in-flight `tool_call` hook parks safely — the agent loop waits for the hook and the run continues with the answer. Consent-gating a tool from a `tool_call` hook is a supported pattern.
|
|
37
|
+
- **`session_start` dialogs are reachable.** A dialog opened from a `session_start` hook is answerable while the session is still starting, both when creating a session and when opening an existing one; startup completes once the dialog settles.
|
|
38
|
+
- **Survives browser reloads; first answer wins.** Reloading the browser re-renders open dialogs from the session status. With several tabs on the same session, the first answer settles the dialog and the other tabs re-render the settled card.
|
|
39
|
+
- **Settled cards stay until dismissed.** An answered or closed dialog leaves its outcome card in the transcript so the user can see what became of it — answers travel to the extension alone, so the card is the only record of the exchange. The card is browser-local: only a browser that saw the dialog open renders it, and switching sessions or reloading drops it.
|
|
40
|
+
- **Timeouts.** The extension's own `timeout` option applies, and the daemon adds an unattended-dialog safety valve, `extensionDialogsTimeoutMs` (default 5 minutes, `0` waits forever — see [Extension dialogs](https://pi-web.dev/config#extension-dialogs)). The effective deadline is the sooner of the two. A dialog that closes without an answer resolves with its kind's cancel value: `false` for confirm, `undefined` for select and input.
|
|
41
|
+
- **Abort and runtime replacement.** Aborting the current run settles a dialog opened during that run immediately, at abort-request time, with its cancel value. Replacing the session runtime (`/reload`, session disposal) settles any still-open dialog the same way; hooks on the new runtime open fresh dialogs. The extension's own `AbortSignal` is honored: aborting it dismisses the dialog and resolves with the cancel value.
|
|
42
|
+
- **Other UI surfaces are still no-ops.** `ExtensionUIContext` methods beyond the three dialogs (widgets, status, editor, `custom`) remain unimplemented under PI WEB even though `hasUI` is `true`; do not rely on `hasUI` alone to detect them.
|
|
43
|
+
|
|
44
|
+
One browser-local caveat: reloading the browser while a new session is still being created loses the browser-local pending-start row, so the dialog card disappears from view. The daemon-side dialog still settles at its deadline and the session appears in the sidebar once creation completes.
|
|
45
|
+
|
|
32
46
|
## Trust model
|
|
33
47
|
|
|
34
48
|
Plugins run as JavaScript in the browser app. Treat them as trusted code:
|
|
@@ -90,8 +104,11 @@ Source files:
|
|
|
90
104
|
```text
|
|
91
105
|
pi-web-plugins/info/package.json
|
|
92
106
|
pi-web-plugins/info/pi-web-plugin.ts
|
|
107
|
+
pi-web-plugins/info/infoInternals.ts
|
|
93
108
|
```
|
|
94
109
|
|
|
110
|
+
`pi-web-plugin.ts` is the plugin skeleton: metadata plus contribution definitions. `infoInternals.ts` holds everything the bundled panel and action actually render, so you can ignore or replace it when copying the plugin.
|
|
111
|
+
|
|
95
112
|
Built module:
|
|
96
113
|
|
|
97
114
|
```text
|
|
@@ -130,6 +147,8 @@ export default {
|
|
|
130
147
|
|
|
131
148
|
When copying the Info plugin, choose a new plugin id so it does not conflict with the bundled `info` plugin.
|
|
132
149
|
|
|
150
|
+
The Info panel doubles as an always-available PI WEB status view: it renders the host-provided `context.state.piWebStatus` (versions, installation, release state, machine, and workspace details) without issuing its own requests, and its action copies a plain-text diagnostics summary suitable for bug reports.
|
|
151
|
+
|
|
133
152
|
PI WEB also ships an `updates` plugin that demonstrates dynamic `visible` and `badge` callbacks for tabs that only appear when the host has status messages or needs extra install visibility.
|
|
134
153
|
|
|
135
154
|
## Local plugin usage
|
|
@@ -275,6 +294,25 @@ Task fields:
|
|
|
275
294
|
|
|
276
295
|
Review task configs before running them, especially in shared projects. Workspace Tasks runs trusted shell commands from your repositories.
|
|
277
296
|
|
|
297
|
+
### Relays
|
|
298
|
+
|
|
299
|
+
**Plugin id:** `relays`
|
|
300
|
+
**What it does:** adds a read-only **Relays** workspace tab for browsing the workspace's relays, plus an **Open Workspace Relays** action for the selected workspace that opens the same tab.
|
|
301
|
+
|
|
302
|
+
A relay is a directory of markdown notes under `.pi-web/relays/<name>/` in the workspace root — the convention used by the Relay method for chaining agent sessions. The tab lists each relay's documents with `status.md`, `charter.md`, and `log.md` first (in that order), followed by any other files alphabetically, and opens `status.md` by default. Markdown documents render as sanitized HTML; other files render as preformatted text, and binary files have no preview. Truncated documents show a notice, and **Refresh** re-scans the workspace and reloads the open document.
|
|
303
|
+
|
|
304
|
+
With several relays, a picker pre-selects the most recently modified one; a single relay opens directly. A workspace without `.pi-web/relays/` shows an empty state explaining the convention. The tab never creates, edits, or deletes relay files.
|
|
305
|
+
|
|
306
|
+
Relays is enabled by default. To hide it, disable `relays` in **Settings → PI WEB plugins** or set:
|
|
307
|
+
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"plugins": {
|
|
311
|
+
"relays": { "enabled": false }
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
278
316
|
## Discovery and packaging
|
|
279
317
|
|
|
280
318
|
PI WEB builds the gateway `/pi-web-plugins/manifest.json` from these sources:
|
|
@@ -433,14 +471,13 @@ Actions appear in the action palette. They can inspect app state and call UI/run
|
|
|
433
471
|
```js
|
|
434
472
|
actions: [
|
|
435
473
|
{
|
|
436
|
-
id: "
|
|
437
|
-
title: "
|
|
438
|
-
description: "
|
|
439
|
-
shortcut: "mod+shift+p",
|
|
474
|
+
id: "copy-diagnostics",
|
|
475
|
+
title: "Copy PI WEB Diagnostics",
|
|
476
|
+
description: "Copy version, installation, and status details for this machine",
|
|
440
477
|
group: "Info",
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
478
|
+
run: async (context) => {
|
|
479
|
+
const version = context.state.piWebStatus?.components.web.runtimeVersion ?? "unknown";
|
|
480
|
+
await navigator.clipboard.writeText(`PI WEB ${version}`);
|
|
444
481
|
},
|
|
445
482
|
},
|
|
446
483
|
]
|
|
@@ -468,6 +505,7 @@ Stable runtime context fields:
|
|
|
468
505
|
```ts
|
|
469
506
|
interface PluginRuntimeContext {
|
|
470
507
|
state: {
|
|
508
|
+
selectedMachine?: PluginMachine;
|
|
471
509
|
selectedWorkspace?: Workspace;
|
|
472
510
|
selectedSession?: unknown;
|
|
473
511
|
piWebStatus?: PiWebStatusResponse;
|
|
@@ -492,7 +530,7 @@ interface PluginRuntimeContext {
|
|
|
492
530
|
Notes:
|
|
493
531
|
|
|
494
532
|
- `state` is a snapshot of current UI state when actions are built.
|
|
495
|
-
- The stable state fields are `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
|
|
533
|
+
- The stable state fields are `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`. `state.selectedMachine` identifies the currently selected machine. `state.piWebStatus` describes the currently selected machine's PI WEB runtime, or the gateway/local runtime when the local machine is selected.
|
|
496
534
|
- Other `state` fields may exist at runtime, but they are private PI WEB internals that may graduate into stable helpers, change shape, or disappear.
|
|
497
535
|
- `enabled` is evaluated when the action palette asks for actions.
|
|
498
536
|
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
|
@@ -580,6 +618,7 @@ interface WorkspacePanelContext {
|
|
|
580
618
|
state?: PluginRuntimeState;
|
|
581
619
|
files: {
|
|
582
620
|
readFile(path: string): Promise<FileContentResponse>;
|
|
621
|
+
listFiles(path: string): Promise<FileTreeResponse>;
|
|
583
622
|
writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
|
|
584
623
|
deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
|
|
585
624
|
moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
|
|
@@ -602,7 +641,7 @@ interface WorkspacePanelContext {
|
|
|
602
641
|
|
|
603
642
|
`icon` is optional and is used in the compact mobile tab bar. Prefer an SVG rendered with the `svg` helper from `PluginActivationContext`; use `currentColor` so PI WEB themes can style it. If `icon` is omitted, mobile tabs fall back to initials from the panel title, or to the full title when initials collide.
|
|
604
643
|
|
|
605
|
-
`machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
|
|
644
|
+
`machine`, `workspace`, `files`, `prompt`, `terminal`, and `host` are documented as stable for panel callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). The `prompt` helper supports panel interactions that insert workspace context into the current prompt — see [Prompt editor API](#prompt-editor-api). Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
|
|
606
645
|
|
|
607
646
|
For compatibility, PI WEB still provides the old `context.openTerminal()` workspace-panel helper at runtime. It is deprecated, intentionally omitted from the public TypeScript declarations, and planned for removal in v2. Existing JavaScript plugins keep working, while typed plugins should migrate to `context.terminal.open()`.
|
|
608
647
|
|
|
@@ -670,6 +709,7 @@ interface WorkspaceLabelContext {
|
|
|
670
709
|
state?: PluginRuntimeState;
|
|
671
710
|
files: {
|
|
672
711
|
readFile(path: string): Promise<FileContentResponse>;
|
|
712
|
+
listFiles(path: string): Promise<FileTreeResponse>;
|
|
673
713
|
writeFile(path: string, content: string | Uint8Array, options?: WriteWorkspaceFileOptions): Promise<WriteWorkspaceFileResponse>;
|
|
674
714
|
deleteFile(path: string): Promise<DeleteWorkspaceFileResponse>;
|
|
675
715
|
moveFile(fromPath: string, toPath: string, options?: MoveWorkspaceFileOptions): Promise<MoveWorkspaceFileResponse>;
|
|
@@ -680,7 +720,7 @@ interface WorkspaceLabelContext {
|
|
|
680
720
|
}
|
|
681
721
|
```
|
|
682
722
|
|
|
683
|
-
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files) and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
|
|
723
|
+
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. The `files` helper supports `readFile`, `listFiles`, `writeFile`, `deleteFile`, and `moveFile` — see [Reading workspace files](#reading-workspace-files), [Listing workspace files](#listing-workspace-files), and [Writing workspace files](#writing-workspace-files). Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
|
|
684
724
|
|
|
685
725
|
Items are sorted by `order` and then id. Return an empty array to render nothing. Keep callbacks synchronous and lightweight; start async work from the callback, return cached items, then call `host.requestRender()` when the cache changes.
|
|
686
726
|
|
|
@@ -819,6 +859,32 @@ workspaceLabels: [
|
|
|
819
859
|
|
|
820
860
|
The file response includes fields such as `path`, `content`, `truncated`, and `binary`. Be careful with sensitive files such as `.env`: plugins are trusted browser code, and file contents are exposed to the plugin.
|
|
821
861
|
|
|
862
|
+
## Listing workspace files
|
|
863
|
+
|
|
864
|
+
`files.listFiles(path)` lists the entries of a workspace directory. Pass `""` for the workspace root. Like `readFile`, PI WEB binds the call to the callback's machine and workspace, so it works the same for local and federated machines.
|
|
865
|
+
|
|
866
|
+
```js
|
|
867
|
+
const listing = await context.files.listFiles("src");
|
|
868
|
+
for (const entry of listing.entries) {
|
|
869
|
+
// entry: { name, path, type: "file" | "directory" | "symlink", size?, modifiedAt? }
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
The listing response includes `path`, `entries`, `scannedAt`, and `truncated`. When `truncated` is true, the server cut the listing short, so treat the entries as partial.
|
|
874
|
+
|
|
875
|
+
`listFiles` rejects when the directory does not exist or cannot be read, matching `readFile` error behavior. When a directory is optional, catch the error and treat it as an empty listing:
|
|
876
|
+
|
|
877
|
+
```js
|
|
878
|
+
async function listSubdirectoryNames(context, path) {
|
|
879
|
+
try {
|
|
880
|
+
const listing = await context.files.listFiles(path);
|
|
881
|
+
return listing.entries.filter((entry) => entry.type === "directory").map((entry) => entry.name);
|
|
882
|
+
} catch {
|
|
883
|
+
return [];
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
```
|
|
887
|
+
|
|
822
888
|
## Writing, deleting, and moving workspace files
|
|
823
889
|
|
|
824
890
|
Workspace panels and workspace labels can write, delete, and move files through the documented `files` helper. Like `readFile`, PI WEB binds these helpers to the callback's machine and workspace, so they work the same for local and federated machines.
|
|
@@ -957,7 +1023,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
957
1023
|
9. Add workspace panels for larger workspace UI.
|
|
958
1024
|
10. Add workspace labels for compact inline metadata.
|
|
959
1025
|
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
|
|
960
|
-
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
|
|
1026
|
+
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedMachine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
|
|
961
1027
|
13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
|
|
962
1028
|
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
|
|
963
1029
|
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmfederico/pi-web",
|
|
3
|
-
"version": "1.202607.
|
|
3
|
+
"version": "1.202607.3",
|
|
4
4
|
"description": "Web UI for persistent Pi Coding Agent sessions in real workspaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Federico Jaramillo Martinez",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"@types/ws": "^8.18.1",
|
|
92
92
|
"eslint": "^10.6.0",
|
|
93
93
|
"globals": "^17.7.0",
|
|
94
|
+
"happy-dom": "^20.11.1",
|
|
94
95
|
"knip": "^6.25.0",
|
|
95
96
|
"tsx": "^4.23.0",
|
|
96
97
|
"typescript": "^6.0.3",
|