@jmfederico/pi-web 1.202606.0 → 1.202606.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 +23 -7
- package/dist/client/assets/{CodeViewer-N88Bx8F6.js → CodeViewer-iYSv2-dg.js} +1 -1
- package/dist/client/assets/{TerminalPanel-DKiL51Xi.js → TerminalPanel-BpFW6j27.js} +4 -4
- package/dist/client/assets/index-D4dbiKXF.js +2210 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +63 -1
- package/dist/config.js.map +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +8 -1
- package/dist/pi-web-plugins/updates/package.json +9 -0
- package/dist/pi-web-plugins/{pi-web → updates}/pi-web-plugin.js +38 -30
- package/dist/pi-web-plugins/workspace-tasks/config.js +91 -0
- package/dist/pi-web-plugins/workspace-tasks/package.json +9 -0
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +48 -0
- package/dist/pi-web-plugins/workspace-tasks/taskRunner.js +12 -0
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +292 -0
- package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +47 -0
- package/dist/plugin-api/unstable.d.ts +22 -0
- package/dist/plugin-api.d.ts +169 -0
- package/dist/server/app.js +63 -26
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +123 -0
- package/dist/server/configRoutes.js.map +1 -0
- package/dist/server/git/gitEnv.js +15 -0
- package/dist/server/git/gitEnv.js.map +1 -0
- package/dist/server/git/gitService.js +2 -1
- package/dist/server/git/gitService.js.map +1 -1
- package/dist/server/gitRoutes.js +3 -3
- package/dist/server/gitRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +134 -0
- package/dist/server/machines/machineClient.js.map +1 -0
- package/dist/server/machines/machinePluginProxyRoutes.js +187 -0
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineProxyRoutes.js +92 -0
- package/dist/server/machines/machineProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineRoutes.js +56 -0
- package/dist/server/machines/machineRoutes.js.map +1 -0
- package/dist/server/machines/machineService.js +260 -0
- package/dist/server/machines/machineService.js.map +1 -0
- package/dist/server/machines/machineStore.js +128 -0
- package/dist/server/machines/machineStore.js.map +1 -0
- package/dist/server/piWebPluginService.js +45 -15
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +127 -36
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/piWebStatusCache.js +32 -0
- package/dist/server/piWebStatusCache.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +17 -14
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond.js +18 -7
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +87 -0
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +147 -66
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionArchiveStore.js +12 -0
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionNameGenerator.js +2 -0
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +126 -43
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/terminalProxyRoutes.js +20 -10
- package/dist/server/terminalProxyRoutes.js.map +1 -1
- package/dist/server/terminals/terminalRoutes.js +11 -0
- package/dist/server/terminals/terminalRoutes.js.map +1 -1
- package/dist/server/terminals/terminalService.js +6 -0
- package/dist/server/terminals/terminalService.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +4 -4
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/fileSuggestions.js +98 -17
- package/dist/server/workspaces/fileSuggestions.js.map +1 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +3 -2
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
- package/dist/server/workspaces/workspaceDeletionRoutes.js +113 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -0
- package/dist/shared/apiTypes.d.ts +498 -0
- package/dist/shared/apiTypes.js +3 -1
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +25 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/federatedRoutes.js +61 -0
- package/dist/shared/federatedRoutes.js.map +1 -0
- package/dist/shared/machinePluginIds.js +41 -0
- package/dist/shared/machinePluginIds.js.map +1 -0
- package/dist/shared/piWebStatusParsing.js +43 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/pluginIds.js +5 -0
- package/dist/shared/pluginIds.js.map +1 -0
- package/dist/shared/workspaceDeletion.js +12 -0
- package/dist/shared/workspaceDeletion.js.map +1 -0
- package/docs/plugins.md +278 -71
- package/package.json +8 -12
- package/plugin-api/unstable.d.ts +1 -0
- package/plugin-api.d.ts +1 -148
- package/dist/client/assets/index-DeSR1RUE.js +0 -1446
- package/dist/pi-web-plugins/pi-web/package.json +0 -9
package/docs/plugins.md
CHANGED
|
@@ -7,7 +7,8 @@ Plugins can currently:
|
|
|
7
7
|
- add action-palette commands;
|
|
8
8
|
- add workspace tools/panels next to Files, Git, and Terminal;
|
|
9
9
|
- add compact workspace-label items in the workspace list, panel header, and status bar;
|
|
10
|
-
- call browser APIs and PI WEB
|
|
10
|
+
- call browser APIs and documented PI WEB plugin context helpers;
|
|
11
|
+
- read workspace files and start workspace terminal commands through documented helpers;
|
|
11
12
|
- serve their own static assets from the plugin directory.
|
|
12
13
|
|
|
13
14
|
They do **not** run in the session daemon, do not get a server-side hook API, and are not sandboxed.
|
|
@@ -17,11 +18,12 @@ They do **not** run in the session daemon, do not get a server-side hook API, an
|
|
|
17
18
|
Plugins run as JavaScript in the browser app. Treat them as trusted code:
|
|
18
19
|
|
|
19
20
|
- they can call browser APIs;
|
|
20
|
-
- they can
|
|
21
|
-
- they can read workspace files through PI WEB's file endpoints if the UI can read them;
|
|
21
|
+
- they can read workspace files and start terminal commands through documented plugin helpers;
|
|
22
22
|
- they can render arbitrary Lit templates/custom elements in plugin contribution areas;
|
|
23
23
|
- they should not be installed from untrusted sources.
|
|
24
24
|
|
|
25
|
+
PI WEB's `/api/...` HTTP and WebSocket endpoints are internal implementation details. Plugin code should use the documented context helpers instead. Daring plugins can still reach private routes or runtime objects because they run in the browser, but those private surfaces are experimental: they may graduate into stable helpers, change shape, or disappear.
|
|
26
|
+
|
|
25
27
|
## What to ask AI to build
|
|
26
28
|
|
|
27
29
|
Humans should not need to hand-code plugins. Give an AI agent a concrete UI goal and ask it to create or modify a local plugin.
|
|
@@ -100,11 +102,11 @@ Module shape excerpt:
|
|
|
100
102
|
export default {
|
|
101
103
|
apiVersion: 1,
|
|
102
104
|
name: "Info Plugin",
|
|
103
|
-
activate: ({ html }) => ({
|
|
105
|
+
activate: ({ html, svg }) => ({
|
|
104
106
|
contributions: {
|
|
105
107
|
actions: [/* action definitions */],
|
|
106
108
|
workspaceLabels: [/* compact label definitions */],
|
|
107
|
-
workspacePanels: [/* panel definitions using html */],
|
|
109
|
+
workspacePanels: [/* panel definitions using html, optional icons using svg */],
|
|
108
110
|
},
|
|
109
111
|
}),
|
|
110
112
|
};
|
|
@@ -112,7 +114,7 @@ export default {
|
|
|
112
114
|
|
|
113
115
|
When copying the Info plugin, choose a new plugin id so it does not conflict with the bundled `info` plugin.
|
|
114
116
|
|
|
115
|
-
PI WEB also ships
|
|
117
|
+
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.
|
|
116
118
|
|
|
117
119
|
## Local plugin usage
|
|
118
120
|
|
|
@@ -127,30 +129,135 @@ ln -s /path/to/plugin-folder ~/.pi-web/plugins/plugin-id
|
|
|
127
129
|
|
|
128
130
|
Reload the PI WEB browser tab. PI WEB serves plugin modules with an mtime-based `?v=` cache buster. After editing a plugin, hard reload the browser if you do not see changes.
|
|
129
131
|
|
|
130
|
-
##
|
|
132
|
+
## Remote machine plugins
|
|
131
133
|
|
|
132
|
-
|
|
134
|
+
When [machine federation](https://pi-web.dev/machines.html) is enabled, PI WEB also loads discovered plugins from the selected remote machine. Remote plugins are trusted browser-side code like local plugins, but their contributions are machine-scoped:
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
- actions, workspace panels, and workspace labels only appear while that machine is selected;
|
|
137
|
+
- plugin file and terminal helpers run against that machine;
|
|
138
|
+
- plugin code is loaded best-effort through the current gateway and cached for the browser page lifetime;
|
|
139
|
+
- if the gateway and remote machine both have an enabled plugin with the same original id, `machineSpecific` metadata decides whether the gateway copy is reused or only the selected machine's copy can appear;
|
|
140
|
+
- remote theme contributions are ignored for now because themes are app-wide;
|
|
141
|
+
- mixed PI WEB versions across federated machines are best-effort and not guaranteed compatible.
|
|
135
142
|
|
|
136
|
-
|
|
137
|
-
- keep its PI WEB metadata in its own `package.json` with `piWeb.plugins` entries pointing at built JavaScript in `dist/`;
|
|
138
|
-
- include a package-level `build` script and `prepack` script so `npm pack --workspace <package>` and `npm publish --workspace <package>` produce a usable plugin package;
|
|
139
|
-
- use a local symlink into `~/.pi-web/plugins/<plugin-id>` while developing;
|
|
140
|
-
- document any private PI WEB APIs it dogfoods until those APIs become stable plugin runtime helpers.
|
|
143
|
+
Remote plugin enablement is controlled by the remote machine's PI WEB plugin config. To edit or disable a remote machine plugin, open that machine directly or update its config file.
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
Plugin package metadata may set `machineSpecific: true` when the plugin's meaning is tied to the selected PI WEB machine:
|
|
143
146
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
- Omitted or `false`: use the gateway copy when the same plugin id is also present on a remote machine. This is best for portable UI plugins whose helpers already route through the selected machine.
|
|
148
|
+
- `true`: the gateway copy only appears for the local machine. When a remote machine is selected, only that remote machine's copy can appear; if the remote machine does not expose the plugin, the plugin is hidden. This is best for plugins that report machine-local PI WEB status or depend on machine-local plugin code.
|
|
149
|
+
|
|
150
|
+
For portable plugin assets, prefer URLs relative to the plugin module, for example:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
const url = new URL("./asset.json", import.meta.url);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
If a remote plugin constructs absolute asset URLs, it should use the `pluginId` from `activate()` because PI WEB gives remote plugins a gateway-scoped runtime id. Hard-coded `/pi-web-plugins/<original-id>/...` URLs may point at the gateway instead of the remote machine.
|
|
157
|
+
|
|
158
|
+
## Manage plugins
|
|
159
|
+
|
|
160
|
+
Open **Settings → Plugins** to review discovered bundled, local, dev, and Pi package plugins for the PI WEB gateway you opened. PI WEB can disable any discovered gateway plugin before the browser imports it. Core app contributions such as the built-in command palette, base workspace tools, and themes are not managed through this plugin list.
|
|
161
|
+
|
|
162
|
+
Plugin preferences are stored under the top-level `plugins` config key in the PI WEB config file:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"plugins": {
|
|
167
|
+
"workspace-tasks": {
|
|
168
|
+
"enabled": true,
|
|
169
|
+
"settings": {}
|
|
170
|
+
},
|
|
171
|
+
"info": {
|
|
172
|
+
"enabled": false
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Plugins are enabled by default. Set `enabled` to `false` to remove a plugin from `/pi-web-plugins/manifest.json` so the browser will not import or activate it on the next page load. The optional `settings` object is reserved for plugin-specific settings.
|
|
179
|
+
|
|
180
|
+
After changing plugin enablement, reload the PI WEB browser tab. Already-loaded plugin JavaScript is not unloaded from the current page.
|
|
181
|
+
|
|
182
|
+
## Built-in plugins
|
|
183
|
+
|
|
184
|
+
PI WEB ships core, discoverable plugins in the main `@jmfederico/pi-web` npm package. No separate `pi install` step is required: update PI WEB, reload the browser tab, and the bundled plugins appear in `/pi-web-plugins/manifest.json`.
|
|
185
|
+
|
|
186
|
+
Built-in plugins can be managed from **Settings → Plugins** or with the top-level `plugins` config key.
|
|
187
|
+
|
|
188
|
+
### Updates
|
|
189
|
+
|
|
190
|
+
**Plugin id:** `updates`
|
|
191
|
+
**What it does:** adds a conditional **Updates** workspace tab with PI WEB update, restart, and installed-service guidance.
|
|
192
|
+
|
|
193
|
+
Updates is enabled by default. It declares `machineSpecific: true` so the gateway Updates tab only appears for the local machine; while a remote machine is selected, that remote machine's Updates plugin is used if available. To hide it, disable `updates` in **Settings → Plugins** or set:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"plugins": {
|
|
198
|
+
"updates": { "enabled": false }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
147
201
|
```
|
|
148
202
|
|
|
149
|
-
|
|
203
|
+
### Workspace Tasks
|
|
204
|
+
|
|
205
|
+
**Plugin id:** `workspace-tasks`
|
|
206
|
+
**Config file:** `.pi-web/tasks.json`
|
|
207
|
+
**What it does:** adds a **Tasks** workspace tab for running configured shell commands in dedicated PI WEB terminals.
|
|
208
|
+
|
|
209
|
+
Workspace Tasks is enabled by default. To hide it, disable `workspace-tasks` in **Settings → Plugins** or set:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"plugins": {
|
|
214
|
+
"workspace-tasks": { "enabled": false }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Configure workspace tasks in `.pi-web/tasks.json`:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"version": 1,
|
|
224
|
+
"tasks": [
|
|
225
|
+
{
|
|
226
|
+
"id": "docker.start",
|
|
227
|
+
"title": "Start Docker",
|
|
228
|
+
"group": "Docker",
|
|
229
|
+
"description": "Start the local Docker Compose environment.",
|
|
230
|
+
"command": "./docker/scripts/docker-compose-dev up -d"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"id": "db.reset",
|
|
234
|
+
"title": "Reset DB",
|
|
235
|
+
"group": "Database",
|
|
236
|
+
"command": "go -C klingit-go run ./cli db reset",
|
|
237
|
+
"confirm": true
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Open a workspace, choose the **Tasks** tab, and click **Run** next to a task. Commands run in the workspace root because PI WEB creates the terminal for that workspace.
|
|
244
|
+
|
|
245
|
+
Task fields:
|
|
246
|
+
|
|
247
|
+
- `version`: must be `1`.
|
|
248
|
+
- `tasks`: array of task definitions.
|
|
249
|
+
- `id`: stable task id, matching `^[a-z][a-z0-9.-]*$`.
|
|
250
|
+
- `title`: button label.
|
|
251
|
+
- `command`: literal shell command sent to the terminal.
|
|
252
|
+
- `description`: optional explanatory text.
|
|
253
|
+
- `group`: optional group heading.
|
|
254
|
+
- `confirm`: optional boolean. When true, the browser asks before dispatching the command.
|
|
255
|
+
|
|
256
|
+
Review task configs before running them, especially in shared projects. Workspace Tasks runs trusted shell commands from your repositories.
|
|
150
257
|
|
|
151
258
|
## Discovery and packaging
|
|
152
259
|
|
|
153
|
-
PI WEB builds `/pi-web-plugins/manifest.json` from these sources:
|
|
260
|
+
PI WEB builds the gateway `/pi-web-plugins/manifest.json` from these sources:
|
|
154
261
|
|
|
155
262
|
1. Bundled plugins in the PI WEB package:
|
|
156
263
|
|
|
@@ -168,6 +275,8 @@ PI WEB builds `/pi-web-plugins/manifest.json` from these sources:
|
|
|
168
275
|
|
|
169
276
|
3. Installed Pi packages that expose PI WEB plugin metadata. Pi packages may be user or project scoped.
|
|
170
277
|
|
|
278
|
+
Remote machines expose their own manifests through the gateway at `/api/machines/<machine-id>/pi-web-plugins/manifest.json`. Those plugin modules are rewritten to gateway-scoped asset URLs and registered under machine-scoped runtime ids so duplicate plugin ids on different machines do not collide.
|
|
279
|
+
|
|
171
280
|
Plugin package directory names and plugin ids must be valid identifiers:
|
|
172
281
|
|
|
173
282
|
```text
|
|
@@ -182,7 +291,7 @@ A package can expose one or more PI WEB plugin modules. There is exactly one sup
|
|
|
182
291
|
"piWeb": {
|
|
183
292
|
"plugins": [
|
|
184
293
|
{ "id": "review", "module": "dist/review.js" },
|
|
185
|
-
{ "id": "dashboard", "module": "dist/dashboard.js" }
|
|
294
|
+
{ "id": "dashboard", "module": "dist/dashboard.js", "machineSpecific": true }
|
|
186
295
|
]
|
|
187
296
|
}
|
|
188
297
|
}
|
|
@@ -194,6 +303,7 @@ Rules:
|
|
|
194
303
|
- Each entry must have an explicit `id` and `module`.
|
|
195
304
|
- `id` must match `^[a-z][a-z0-9.-]*$`.
|
|
196
305
|
- `module` must be a safe relative path inside the plugin package root.
|
|
306
|
+
- `machineSpecific` is optional and must be a boolean; omit it for the default portable gateway behavior.
|
|
197
307
|
- Duplicate plugin ids are not auto-renamed; later duplicates are skipped.
|
|
198
308
|
- Legacy shortcuts such as `piWeb.plugin`, string entries in `piWeb.plugins`, `piWeb.id` fallback ids, and no-`package.json` fallbacks are not supported.
|
|
199
309
|
|
|
@@ -208,13 +318,14 @@ The manifest contains each discovered plugin module:
|
|
|
208
318
|
"id": "my-plugin",
|
|
209
319
|
"module": "/pi-web-plugins/my-plugin/pi-web-plugin.js?v=1234567890",
|
|
210
320
|
"source": "local",
|
|
211
|
-
"scope": "local"
|
|
321
|
+
"scope": "local",
|
|
322
|
+
"machineSpecific": false
|
|
212
323
|
}
|
|
213
324
|
]
|
|
214
325
|
}
|
|
215
326
|
```
|
|
216
327
|
|
|
217
|
-
`source` describes where the plugin came from (`bundled`, `local`, or the Pi package source). `scope` is `bundled`, `local`, `user`, or `project`.
|
|
328
|
+
`source` describes where the plugin came from (`bundled`, `local`, or the Pi package source). `scope` is `bundled`, `local`, `user`, or `project`. `machineSpecific` controls whether the gateway copy is valid for remote machines or only each selected machine's own copy can appear.
|
|
218
329
|
|
|
219
330
|
A plugin can fetch its own static assets with URLs under:
|
|
220
331
|
|
|
@@ -239,6 +350,7 @@ interface PluginActivationContext {
|
|
|
239
350
|
apiVersion: 1;
|
|
240
351
|
pluginId: string;
|
|
241
352
|
html: typeof import("lit").html;
|
|
353
|
+
svg: typeof import("lit").svg;
|
|
242
354
|
}
|
|
243
355
|
|
|
244
356
|
interface PluginActivationResult {
|
|
@@ -325,6 +437,7 @@ interface PluginRuntimeContext {
|
|
|
325
437
|
state: {
|
|
326
438
|
selectedWorkspace?: Workspace;
|
|
327
439
|
selectedSession?: unknown;
|
|
440
|
+
piWebStatus?: PiWebStatusResponse;
|
|
328
441
|
};
|
|
329
442
|
openActionPalette: () => void;
|
|
330
443
|
focusPrompt: () => void;
|
|
@@ -344,12 +457,12 @@ interface PluginRuntimeContext {
|
|
|
344
457
|
Notes:
|
|
345
458
|
|
|
346
459
|
- `state` is a snapshot of current UI state when actions are built.
|
|
347
|
-
-
|
|
348
|
-
- Other `state` fields may exist at runtime, but they are PI WEB internals
|
|
460
|
+
- 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.
|
|
461
|
+
- Other `state` fields may exist at runtime, but they are private PI WEB internals that may graduate into stable helpers, change shape, or disappear.
|
|
349
462
|
- `enabled` is evaluated when the action palette asks for actions.
|
|
350
463
|
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
|
351
464
|
- `openTerminal()` switches to the built-in terminal panel. Pass `{ terminalId }` to deep-link to a specific terminal.
|
|
352
|
-
- Only fields documented here and declared in `plugin-api.d.ts` are stable public plugin API.
|
|
465
|
+
- Only fields documented here and declared in `plugin-api.d.ts` are stable public plugin API. Anything else is experimental: it may become public API later, change shape, or disappear.
|
|
353
466
|
|
|
354
467
|
#### Keyboard shortcuts
|
|
355
468
|
|
|
@@ -369,6 +482,13 @@ workspacePanels: [
|
|
|
369
482
|
{
|
|
370
483
|
id: "workspace.info",
|
|
371
484
|
title: "Info",
|
|
485
|
+
icon: svg`
|
|
486
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
487
|
+
<circle cx="12" cy="12" r="9"></circle>
|
|
488
|
+
<path d="M12 10v6"></path>
|
|
489
|
+
<path d="M12 7h.01"></path>
|
|
490
|
+
</svg>
|
|
491
|
+
`,
|
|
372
492
|
order: 100,
|
|
373
493
|
visible: ({ workspace }) => workspace.isGitRepo,
|
|
374
494
|
render: ({ workspace }) => html`
|
|
@@ -388,23 +508,50 @@ Panel type:
|
|
|
388
508
|
interface WorkspacePanelContribution {
|
|
389
509
|
id: string;
|
|
390
510
|
title: string;
|
|
511
|
+
icon?: TemplateResult;
|
|
391
512
|
order?: number;
|
|
392
|
-
visible?: (context:
|
|
513
|
+
visible?: (context: WorkspacePanelContext) => boolean;
|
|
393
514
|
badge?: (context: WorkspacePanelContext) => string | number | TemplateResult | undefined;
|
|
394
515
|
render: (context: WorkspacePanelContext) => TemplateResult;
|
|
395
516
|
}
|
|
396
517
|
|
|
397
518
|
interface WorkspacePanelContext {
|
|
519
|
+
machine: PluginMachine;
|
|
398
520
|
workspace: Workspace;
|
|
399
|
-
|
|
521
|
+
state?: PluginRuntimeState;
|
|
522
|
+
files: {
|
|
523
|
+
readFile(path: string): Promise<FileContentResponse>;
|
|
524
|
+
};
|
|
525
|
+
terminal: {
|
|
526
|
+
open(options?: { terminalId?: string }): void;
|
|
527
|
+
runCommand(input: {
|
|
528
|
+
title: string;
|
|
529
|
+
command: string;
|
|
530
|
+
metadata?: Record<string, string>;
|
|
531
|
+
open?: boolean;
|
|
532
|
+
}): Promise<TerminalCommandRunHandle>;
|
|
533
|
+
};
|
|
534
|
+
host: {
|
|
535
|
+
requestRender(): void;
|
|
536
|
+
};
|
|
400
537
|
}
|
|
401
538
|
```
|
|
402
539
|
|
|
403
|
-
`
|
|
540
|
+
`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.
|
|
541
|
+
|
|
542
|
+
`machine`, `workspace`, `files`, `terminal`, and `host` are documented as stable for panel callbacks. 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`.
|
|
404
543
|
|
|
405
|
-
|
|
544
|
+
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()`.
|
|
545
|
+
|
|
546
|
+
Useful workspace and machine shapes:
|
|
406
547
|
|
|
407
548
|
```ts
|
|
549
|
+
interface PluginMachine {
|
|
550
|
+
id: string;
|
|
551
|
+
name: string;
|
|
552
|
+
kind: "local" | "remote";
|
|
553
|
+
}
|
|
554
|
+
|
|
408
555
|
interface Workspace {
|
|
409
556
|
id: string;
|
|
410
557
|
projectId: string;
|
|
@@ -417,6 +564,8 @@ interface Workspace {
|
|
|
417
564
|
}
|
|
418
565
|
```
|
|
419
566
|
|
|
567
|
+
`machine.id` is included in panel contexts so plugins can keep caches machine-scoped. Do not infer the selected machine from global browser state.
|
|
568
|
+
|
|
420
569
|
Use existing classes such as `toolbar`, `viewer`, `empty`, and `muted` for panel content when possible. Do not assume a panel owns the whole page; keep layout contained.
|
|
421
570
|
|
|
422
571
|
### Workspace labels
|
|
@@ -453,13 +602,21 @@ interface WorkspaceLabelContribution {
|
|
|
453
602
|
}
|
|
454
603
|
|
|
455
604
|
interface WorkspaceLabelContext {
|
|
605
|
+
machine: PluginMachine;
|
|
456
606
|
workspace: Workspace;
|
|
607
|
+
state?: PluginRuntimeState;
|
|
608
|
+
files: {
|
|
609
|
+
readFile(path: string): Promise<FileContentResponse>;
|
|
610
|
+
};
|
|
611
|
+
host: {
|
|
612
|
+
requestRender(): void;
|
|
613
|
+
};
|
|
457
614
|
}
|
|
458
615
|
```
|
|
459
616
|
|
|
460
|
-
|
|
617
|
+
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. 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.
|
|
461
618
|
|
|
462
|
-
Items are sorted by `order` and then id. Return an empty array to render nothing.
|
|
619
|
+
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.
|
|
463
620
|
|
|
464
621
|
#### Text items
|
|
465
622
|
|
|
@@ -519,55 +676,105 @@ export default {
|
|
|
519
676
|
|
|
520
677
|
## Reading workspace files
|
|
521
678
|
|
|
522
|
-
|
|
679
|
+
Workspace panels and workspace labels can read files through the documented `files` helper. PI WEB binds this helper to the callback's machine and workspace, so it works the same for local and federated machines.
|
|
523
680
|
|
|
524
681
|
```js
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
682
|
+
workspacePanels: [
|
|
683
|
+
{
|
|
684
|
+
id: "workspace.env",
|
|
685
|
+
title: "Env",
|
|
686
|
+
render: ({ files }) => html`
|
|
687
|
+
<my-env-viewer .files=${files}></my-env-viewer>
|
|
688
|
+
`,
|
|
689
|
+
},
|
|
690
|
+
]
|
|
691
|
+
|
|
692
|
+
class MyEnvViewer extends HTMLElement {
|
|
693
|
+
set files(value) {
|
|
694
|
+
this._files = value;
|
|
695
|
+
void this.load();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
async load() {
|
|
699
|
+
try {
|
|
700
|
+
const file = await this._files.readFile(".env.example");
|
|
701
|
+
this.textContent = file.binary ? "Binary file" : file.content;
|
|
702
|
+
} catch (error) {
|
|
703
|
+
this.textContent = error instanceof Error ? error.message : String(error);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
534
706
|
}
|
|
535
707
|
```
|
|
536
708
|
|
|
537
|
-
|
|
709
|
+
Labels should use the same helper through a plugin-owned cache because `items()` itself must return synchronously:
|
|
538
710
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
## Other useful PI WEB APIs
|
|
711
|
+
```js
|
|
712
|
+
const envCache = new Map();
|
|
542
713
|
|
|
543
|
-
|
|
714
|
+
function envKey(machine, workspace) {
|
|
715
|
+
return `${machine.id}:${workspace.id}:docker/development.be-go.local.env`;
|
|
716
|
+
}
|
|
544
717
|
|
|
545
|
-
|
|
718
|
+
function loadEnvLabel(context) {
|
|
719
|
+
const key = envKey(context.machine, context.workspace);
|
|
720
|
+
const cached = envCache.get(key);
|
|
721
|
+
if (cached !== undefined) return cached;
|
|
722
|
+
|
|
723
|
+
const pending = { status: "loading", label: undefined };
|
|
724
|
+
envCache.set(key, pending);
|
|
725
|
+
context.files.readFile("docker/development.be-go.local.env")
|
|
726
|
+
.then((file) => {
|
|
727
|
+
pending.status = "ready";
|
|
728
|
+
pending.label = file.content.match(/^DEV_URL=(.+)$/m)?.[1];
|
|
729
|
+
context.host.requestRender();
|
|
730
|
+
})
|
|
731
|
+
.catch(() => {
|
|
732
|
+
pending.status = "missing";
|
|
733
|
+
context.host.requestRender();
|
|
734
|
+
});
|
|
735
|
+
return pending;
|
|
736
|
+
}
|
|
546
737
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
738
|
+
workspaceLabels: [
|
|
739
|
+
{
|
|
740
|
+
id: "dev-url",
|
|
741
|
+
items: (context) => {
|
|
742
|
+
const cached = loadEnvLabel(context);
|
|
743
|
+
return cached.label === undefined ? [] : [{
|
|
744
|
+
type: "link",
|
|
745
|
+
text: cached.label,
|
|
746
|
+
href: cached.label,
|
|
747
|
+
target: "_blank",
|
|
748
|
+
}];
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
]
|
|
557
752
|
```
|
|
558
753
|
|
|
559
|
-
|
|
754
|
+
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.
|
|
560
755
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
756
|
+
## Running workspace terminal commands
|
|
757
|
+
|
|
758
|
+
Workspace panels can start terminal commands through the documented `terminal` helper. Commands run in the current workspace on the panel's machine.
|
|
759
|
+
|
|
760
|
+
```js
|
|
761
|
+
render: ({ terminal }) => html`
|
|
762
|
+
<button @click=${() => terminal.runCommand({
|
|
763
|
+
title: "Build",
|
|
764
|
+
command: "npm run build",
|
|
765
|
+
open: true,
|
|
766
|
+
metadata: { "my-plugin.task": "build" },
|
|
767
|
+
})}>Build</button>
|
|
768
|
+
`
|
|
568
769
|
```
|
|
569
770
|
|
|
570
|
-
|
|
771
|
+
Review command strings carefully. They are trusted shell commands executed in the workspace terminal.
|
|
772
|
+
|
|
773
|
+
## Private and experimental PI WEB APIs
|
|
774
|
+
|
|
775
|
+
PI WEB's `/api/...` HTTP and WebSocket routes and runtime-only fields are private implementation details. They exist because plugins are trusted browser code, and because some capabilities may be evaluated there before they are designed as stable helpers.
|
|
776
|
+
|
|
777
|
+
That is allowed, but outside the v1 compatibility promise: URLs, response shapes, runtime fields, and machine-federation routing may graduate into stable APIs, change shape, or disappear. The stable public plugin API is only the documented helpers and declarations in `plugin-api.d.ts`. Prefer those whenever they exist; if you rely on private surfaces, keep the dependency local to the plugin and expect to revisit it after PI WEB upgrades.
|
|
571
778
|
|
|
572
779
|
## Async data and caching
|
|
573
780
|
|
|
@@ -576,7 +783,7 @@ PI WEB does not provide a plugin cache/invalidation framework. Keep host callbac
|
|
|
576
783
|
- simple contributions should be synchronous and cheap;
|
|
577
784
|
- expensive or async work should live inside the plugin;
|
|
578
785
|
- custom elements in `type: "render"` label items or panels are a good place to own async loading;
|
|
579
|
-
- dedupe
|
|
786
|
+
- dedupe async reads/commands and avoid unbounded polling;
|
|
580
787
|
- clean up intervals/event listeners in custom elements' `disconnectedCallback()`.
|
|
581
788
|
|
|
582
789
|
## Agent implementation checklist
|
|
@@ -584,7 +791,7 @@ PI WEB does not provide a plugin cache/invalidation framework. Keep host callbac
|
|
|
584
791
|
If you are an AI agent building or editing a PI WEB plugin, follow this checklist:
|
|
585
792
|
|
|
586
793
|
1. Create or update a plugin folder with `package.json` and a JavaScript module such as `pi-web-plugin.js`.
|
|
587
|
-
2. Use the single supported package metadata shape: `piWeb.plugins` array with `{ id, module }` entries.
|
|
794
|
+
2. Use the single supported package metadata shape: `piWeb.plugins` array with `{ id, module, machineSpecific? }` entries.
|
|
588
795
|
3. Default-export `{ apiVersion: 1, name, activate }` from the module.
|
|
589
796
|
4. Return `{ contributions: { actions, workspacePanels, workspaceLabels } }` from `activate()`.
|
|
590
797
|
5. Use ids matching `^[a-z][a-z0-9.-]*$`.
|
|
@@ -594,8 +801,8 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
594
801
|
9. Add workspace panels for larger workspace UI.
|
|
595
802
|
10. Add workspace labels for compact inline metadata.
|
|
596
803
|
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
|
|
597
|
-
12. Use
|
|
598
|
-
13.
|
|
804
|
+
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`.
|
|
805
|
+
13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers.
|
|
599
806
|
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
|
|
600
807
|
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
|
|
601
808
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmfederico/pi-web",
|
|
3
|
-
"version": "1.202606.
|
|
3
|
+
"version": "1.202606.2",
|
|
4
4
|
"description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Federico Jaramillo Martinez",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"workspaces": [
|
|
9
|
-
".",
|
|
10
|
-
"plugins/*"
|
|
11
|
-
],
|
|
12
8
|
"bin": {
|
|
13
9
|
"pi-web": "dist/cli.js",
|
|
14
10
|
"pi-web-server": "dist/server/index.js",
|
|
@@ -22,26 +18,26 @@
|
|
|
22
18
|
"extensions",
|
|
23
19
|
"docs/plugins.md",
|
|
24
20
|
"docs/assets",
|
|
25
|
-
"plugin-api.d.ts"
|
|
21
|
+
"plugin-api.d.ts",
|
|
22
|
+
"plugin-api/unstable.d.ts"
|
|
26
23
|
],
|
|
27
24
|
"scripts": {
|
|
28
25
|
"dev": "bash -c 'trap \"kill 0\" EXIT; npm run dev:sessiond & npm run dev:web & npm run dev:client & wait'",
|
|
29
26
|
"dev:sessiond": "tsx watch src/server/sessiond.ts",
|
|
30
|
-
"dev:web": "bash -c 'set -e; npm run build:plugins;
|
|
27
|
+
"dev:web": "bash -c 'set -e; npm run build:plugins; trap \"kill 0\" EXIT; npm run dev:plugins & tsx watch src/server/index.ts & wait'",
|
|
31
28
|
"dev:server": "npm run dev:web",
|
|
32
29
|
"dev:client": "vite --host 0.0.0.0",
|
|
33
30
|
"dev:plugins": "node scripts/build-plugins.mjs --watch",
|
|
34
|
-
"
|
|
35
|
-
"build": "tsc -p tsconfig.
|
|
31
|
+
"build": "tsc -p tsconfig.build.json && npm run build:plugin-api && npm run build:plugins && vite build",
|
|
32
|
+
"build:plugin-api": "tsc -p tsconfig.plugin-api.json",
|
|
36
33
|
"build:plugins": "tsc -p tsconfig.plugins.json && node scripts/build-plugins.mjs",
|
|
37
|
-
"build:plugin-packages": "bash -c 'set -e; shopt -s nullglob; for package in plugins/*/package.json; do dir=${package%/package.json}; (cd \"$dir\" && npm run build --if-present); done'",
|
|
38
34
|
"typecheck": "tsc --noEmit",
|
|
39
|
-
"lint": "eslint \"src/**/*.ts\" \"extensions/**/*.ts\" \"pi-web-plugins/**/*.ts\"
|
|
35
|
+
"lint": "eslint \"src/**/*.ts\" \"extensions/**/*.ts\" \"pi-web-plugins/**/*.ts\" vite.config.ts vitest.config.ts",
|
|
40
36
|
"test": "vitest run --config vitest.config.ts",
|
|
41
37
|
"verify": "npm run typecheck && npm run lint && npm test",
|
|
42
38
|
"start": "tsx src/server/index.ts",
|
|
43
39
|
"start:sessiond": "tsx src/server/sessiond.ts",
|
|
44
|
-
"clean": "rm -rf dist
|
|
40
|
+
"clean": "rm -rf dist",
|
|
45
41
|
"prepack": "npm run build",
|
|
46
42
|
"pack:dry": "npm pack --dry-run",
|
|
47
43
|
"prepublishOnly": "npm run verify",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../dist/plugin-api/unstable.js";
|