@jmfederico/pi-web 1.202606.0 → 1.202606.1
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 +21 -7
- package/dist/client/assets/{CodeViewer-N88Bx8F6.js → CodeViewer-BcQUZop_.js} +1 -1
- package/dist/client/assets/{TerminalPanel-DKiL51Xi.js → TerminalPanel-1o4j3QP_.js} +4 -4
- package/dist/client/assets/index-CM8WX5nf.js +1994 -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 +55 -25
- 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 +179 -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 +50 -0
- package/dist/server/machines/machineRoutes.js.map +1 -0
- package/dist/server/machines/machineService.js +168 -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 +22 -12
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/sessiond/sessionProxyRoutes.js +15 -13
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +19 -5
- package/dist/server/sessions/piSessionService.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 +1 -1
- 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 +2 -1
- 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 +464 -0
- package/dist/shared/federatedRoutes.js +59 -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/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 +276 -65
- 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 not fetch PI WEB API endpoints directly; use the documented context helpers instead.
|
|
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,130 @@ 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 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 already has an enabled plugin with the same original id, the gateway plugin wins and the remote duplicate stays hidden;
|
|
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
|
+
For portable plugin assets, prefer URLs relative to the plugin module, for example:
|
|
143
146
|
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
```js
|
|
148
|
+
const url = new URL("./asset.json", import.meta.url);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
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.
|
|
152
|
+
|
|
153
|
+
## Manage plugins
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
Plugin preferences are stored under the top-level `plugins` config key in the PI WEB config file:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"plugins": {
|
|
162
|
+
"workspace-tasks": {
|
|
163
|
+
"enabled": true,
|
|
164
|
+
"settings": {}
|
|
165
|
+
},
|
|
166
|
+
"info": {
|
|
167
|
+
"enabled": false
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
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.
|
|
174
|
+
|
|
175
|
+
After changing plugin enablement, reload the PI WEB browser tab. Already-loaded plugin JavaScript is not unloaded from the current page.
|
|
176
|
+
|
|
177
|
+
## Built-in plugins
|
|
178
|
+
|
|
179
|
+
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`.
|
|
180
|
+
|
|
181
|
+
Built-in plugins can be managed from **Settings → Plugins** or with the top-level `plugins` config key.
|
|
182
|
+
|
|
183
|
+
### Updates
|
|
184
|
+
|
|
185
|
+
**Plugin id:** `updates`
|
|
186
|
+
**What it does:** adds a conditional **Updates** workspace tab with PI WEB update, restart, and installed-service guidance.
|
|
187
|
+
|
|
188
|
+
Updates is enabled by default. To hide it, disable `updates` in **Settings → Plugins** or set:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"plugins": {
|
|
193
|
+
"updates": { "enabled": false }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Workspace Tasks
|
|
199
|
+
|
|
200
|
+
**Plugin id:** `workspace-tasks`
|
|
201
|
+
**Config file:** `.pi-web/tasks.json`
|
|
202
|
+
**What it does:** adds a **Tasks** workspace tab for running configured shell commands in dedicated PI WEB terminals.
|
|
203
|
+
|
|
204
|
+
Workspace Tasks is enabled by default. To hide it, disable `workspace-tasks` in **Settings → Plugins** or set:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"plugins": {
|
|
209
|
+
"workspace-tasks": { "enabled": false }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Configure workspace tasks in `.pi-web/tasks.json`:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"version": 1,
|
|
219
|
+
"tasks": [
|
|
220
|
+
{
|
|
221
|
+
"id": "docker.start",
|
|
222
|
+
"title": "Start Docker",
|
|
223
|
+
"group": "Docker",
|
|
224
|
+
"description": "Start the local Docker Compose environment.",
|
|
225
|
+
"command": "./docker/scripts/docker-compose-dev up -d"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"id": "db.reset",
|
|
229
|
+
"title": "Reset DB",
|
|
230
|
+
"group": "Database",
|
|
231
|
+
"command": "go -C klingit-go run ./cli db reset",
|
|
232
|
+
"confirm": true
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
147
236
|
```
|
|
148
237
|
|
|
149
|
-
|
|
238
|
+
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.
|
|
239
|
+
|
|
240
|
+
Task fields:
|
|
241
|
+
|
|
242
|
+
- `version`: must be `1`.
|
|
243
|
+
- `tasks`: array of task definitions.
|
|
244
|
+
- `id`: stable task id, matching `^[a-z][a-z0-9.-]*$`.
|
|
245
|
+
- `title`: button label.
|
|
246
|
+
- `command`: literal shell command sent to the terminal.
|
|
247
|
+
- `description`: optional explanatory text.
|
|
248
|
+
- `group`: optional group heading.
|
|
249
|
+
- `confirm`: optional boolean. When true, the browser asks before dispatching the command.
|
|
250
|
+
|
|
251
|
+
Review task configs before running them, especially in shared projects. Workspace Tasks runs trusted shell commands from your repositories.
|
|
150
252
|
|
|
151
253
|
## Discovery and packaging
|
|
152
254
|
|
|
153
|
-
PI WEB builds `/pi-web-plugins/manifest.json` from these sources:
|
|
255
|
+
PI WEB builds the gateway `/pi-web-plugins/manifest.json` from these sources:
|
|
154
256
|
|
|
155
257
|
1. Bundled plugins in the PI WEB package:
|
|
156
258
|
|
|
@@ -168,6 +270,8 @@ PI WEB builds `/pi-web-plugins/manifest.json` from these sources:
|
|
|
168
270
|
|
|
169
271
|
3. Installed Pi packages that expose PI WEB plugin metadata. Pi packages may be user or project scoped.
|
|
170
272
|
|
|
273
|
+
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.
|
|
274
|
+
|
|
171
275
|
Plugin package directory names and plugin ids must be valid identifiers:
|
|
172
276
|
|
|
173
277
|
```text
|
|
@@ -239,6 +343,7 @@ interface PluginActivationContext {
|
|
|
239
343
|
apiVersion: 1;
|
|
240
344
|
pluginId: string;
|
|
241
345
|
html: typeof import("lit").html;
|
|
346
|
+
svg: typeof import("lit").svg;
|
|
242
347
|
}
|
|
243
348
|
|
|
244
349
|
interface PluginActivationResult {
|
|
@@ -325,6 +430,7 @@ interface PluginRuntimeContext {
|
|
|
325
430
|
state: {
|
|
326
431
|
selectedWorkspace?: Workspace;
|
|
327
432
|
selectedSession?: unknown;
|
|
433
|
+
piWebStatus?: PiWebStatusResponse;
|
|
328
434
|
};
|
|
329
435
|
openActionPalette: () => void;
|
|
330
436
|
focusPrompt: () => void;
|
|
@@ -344,12 +450,12 @@ interface PluginRuntimeContext {
|
|
|
344
450
|
Notes:
|
|
345
451
|
|
|
346
452
|
- `state` is a snapshot of current UI state when actions are built.
|
|
347
|
-
-
|
|
453
|
+
- The stable state fields are `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`.
|
|
348
454
|
- Other `state` fields may exist at runtime, but they are PI WEB internals and can change quickly.
|
|
349
455
|
- `enabled` is evaluated when the action palette asks for actions.
|
|
350
456
|
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
|
351
457
|
- `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.
|
|
458
|
+
- Only fields documented here and declared in `plugin-api.d.ts` are stable public plugin API. Unstable runtime fields are intentionally omitted from these types; if a plugin author chooses to depend on them, they must explicitly import unstable types from `@jmfederico/pi-web/plugin-api/unstable` and type-assert the context in their own code.
|
|
353
459
|
|
|
354
460
|
#### Keyboard shortcuts
|
|
355
461
|
|
|
@@ -369,6 +475,13 @@ workspacePanels: [
|
|
|
369
475
|
{
|
|
370
476
|
id: "workspace.info",
|
|
371
477
|
title: "Info",
|
|
478
|
+
icon: svg`
|
|
479
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
480
|
+
<circle cx="12" cy="12" r="9"></circle>
|
|
481
|
+
<path d="M12 10v6"></path>
|
|
482
|
+
<path d="M12 7h.01"></path>
|
|
483
|
+
</svg>
|
|
484
|
+
`,
|
|
372
485
|
order: 100,
|
|
373
486
|
visible: ({ workspace }) => workspace.isGitRepo,
|
|
374
487
|
render: ({ workspace }) => html`
|
|
@@ -388,23 +501,50 @@ Panel type:
|
|
|
388
501
|
interface WorkspacePanelContribution {
|
|
389
502
|
id: string;
|
|
390
503
|
title: string;
|
|
504
|
+
icon?: TemplateResult;
|
|
391
505
|
order?: number;
|
|
392
|
-
visible?: (context:
|
|
506
|
+
visible?: (context: WorkspacePanelContext) => boolean;
|
|
393
507
|
badge?: (context: WorkspacePanelContext) => string | number | TemplateResult | undefined;
|
|
394
508
|
render: (context: WorkspacePanelContext) => TemplateResult;
|
|
395
509
|
}
|
|
396
510
|
|
|
397
511
|
interface WorkspacePanelContext {
|
|
512
|
+
machine: PluginMachine;
|
|
398
513
|
workspace: Workspace;
|
|
399
|
-
|
|
514
|
+
state?: PluginRuntimeState;
|
|
515
|
+
files: {
|
|
516
|
+
readFile(path: string): Promise<FileContentResponse>;
|
|
517
|
+
};
|
|
518
|
+
terminal: {
|
|
519
|
+
open(options?: { terminalId?: string }): void;
|
|
520
|
+
runCommand(input: {
|
|
521
|
+
title: string;
|
|
522
|
+
command: string;
|
|
523
|
+
metadata?: Record<string, string>;
|
|
524
|
+
open?: boolean;
|
|
525
|
+
}): Promise<TerminalCommandRunHandle>;
|
|
526
|
+
};
|
|
527
|
+
host: {
|
|
528
|
+
requestRender(): void;
|
|
529
|
+
};
|
|
400
530
|
}
|
|
401
531
|
```
|
|
402
532
|
|
|
403
|
-
`
|
|
533
|
+
`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.
|
|
534
|
+
|
|
535
|
+
`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
536
|
|
|
405
|
-
|
|
537
|
+
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()`.
|
|
538
|
+
|
|
539
|
+
Useful workspace and machine shapes:
|
|
406
540
|
|
|
407
541
|
```ts
|
|
542
|
+
interface PluginMachine {
|
|
543
|
+
id: string;
|
|
544
|
+
name: string;
|
|
545
|
+
kind: "local" | "remote";
|
|
546
|
+
}
|
|
547
|
+
|
|
408
548
|
interface Workspace {
|
|
409
549
|
id: string;
|
|
410
550
|
projectId: string;
|
|
@@ -417,6 +557,8 @@ interface Workspace {
|
|
|
417
557
|
}
|
|
418
558
|
```
|
|
419
559
|
|
|
560
|
+
`machine.id` is included in panel contexts so plugins can keep caches machine-scoped. Do not infer the selected machine from global browser state.
|
|
561
|
+
|
|
420
562
|
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
563
|
|
|
422
564
|
### Workspace labels
|
|
@@ -453,13 +595,21 @@ interface WorkspaceLabelContribution {
|
|
|
453
595
|
}
|
|
454
596
|
|
|
455
597
|
interface WorkspaceLabelContext {
|
|
598
|
+
machine: PluginMachine;
|
|
456
599
|
workspace: Workspace;
|
|
600
|
+
state?: PluginRuntimeState;
|
|
601
|
+
files: {
|
|
602
|
+
readFile(path: string): Promise<FileContentResponse>;
|
|
603
|
+
};
|
|
604
|
+
host: {
|
|
605
|
+
requestRender(): void;
|
|
606
|
+
};
|
|
457
607
|
}
|
|
458
608
|
```
|
|
459
609
|
|
|
460
|
-
|
|
610
|
+
`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
611
|
|
|
462
|
-
Items are sorted by `order` and then id. Return an empty array to render nothing.
|
|
612
|
+
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
613
|
|
|
464
614
|
#### Text items
|
|
465
615
|
|
|
@@ -519,55 +669,116 @@ export default {
|
|
|
519
669
|
|
|
520
670
|
## Reading workspace files
|
|
521
671
|
|
|
522
|
-
|
|
672
|
+
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
673
|
|
|
524
674
|
```js
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
675
|
+
workspacePanels: [
|
|
676
|
+
{
|
|
677
|
+
id: "workspace.env",
|
|
678
|
+
title: "Env",
|
|
679
|
+
render: ({ files }) => html`
|
|
680
|
+
<my-env-viewer .files=${files}></my-env-viewer>
|
|
681
|
+
`,
|
|
682
|
+
},
|
|
683
|
+
]
|
|
684
|
+
|
|
685
|
+
class MyEnvViewer extends HTMLElement {
|
|
686
|
+
set files(value) {
|
|
687
|
+
this._files = value;
|
|
688
|
+
void this.load();
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async load() {
|
|
692
|
+
try {
|
|
693
|
+
const file = await this._files.readFile(".env.example");
|
|
694
|
+
this.textContent = file.binary ? "Binary file" : file.content;
|
|
695
|
+
} catch (error) {
|
|
696
|
+
this.textContent = error instanceof Error ? error.message : String(error);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
534
699
|
}
|
|
535
700
|
```
|
|
536
701
|
|
|
537
|
-
|
|
702
|
+
Labels should use the same helper through a plugin-owned cache because `items()` itself must return synchronously:
|
|
538
703
|
|
|
539
|
-
|
|
704
|
+
```js
|
|
705
|
+
const envCache = new Map();
|
|
540
706
|
|
|
541
|
-
|
|
707
|
+
function envKey(machine, workspace) {
|
|
708
|
+
return `${machine.id}:${workspace.id}:docker/development.be-go.local.env`;
|
|
709
|
+
}
|
|
542
710
|
|
|
543
|
-
|
|
711
|
+
function loadEnvLabel(context) {
|
|
712
|
+
const key = envKey(context.machine, context.workspace);
|
|
713
|
+
const cached = envCache.get(key);
|
|
714
|
+
if (cached !== undefined) return cached;
|
|
715
|
+
|
|
716
|
+
const pending = { status: "loading", label: undefined };
|
|
717
|
+
envCache.set(key, pending);
|
|
718
|
+
context.files.readFile("docker/development.be-go.local.env")
|
|
719
|
+
.then((file) => {
|
|
720
|
+
pending.status = "ready";
|
|
721
|
+
pending.label = file.content.match(/^DEV_URL=(.+)$/m)?.[1];
|
|
722
|
+
context.host.requestRender();
|
|
723
|
+
})
|
|
724
|
+
.catch(() => {
|
|
725
|
+
pending.status = "missing";
|
|
726
|
+
context.host.requestRender();
|
|
727
|
+
});
|
|
728
|
+
return pending;
|
|
729
|
+
}
|
|
544
730
|
|
|
545
|
-
|
|
731
|
+
workspaceLabels: [
|
|
732
|
+
{
|
|
733
|
+
id: "dev-url",
|
|
734
|
+
items: (context) => {
|
|
735
|
+
const cached = loadEnvLabel(context);
|
|
736
|
+
return cached.label === undefined ? [] : [{
|
|
737
|
+
type: "link",
|
|
738
|
+
text: cached.label,
|
|
739
|
+
href: cached.label,
|
|
740
|
+
target: "_blank",
|
|
741
|
+
}];
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
]
|
|
745
|
+
```
|
|
546
746
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
747
|
+
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.
|
|
748
|
+
|
|
749
|
+
## Running workspace terminal commands
|
|
750
|
+
|
|
751
|
+
Workspace panels can start terminal commands through the documented `terminal` helper. Commands run in the current workspace on the panel's machine.
|
|
752
|
+
|
|
753
|
+
```js
|
|
754
|
+
render: ({ terminal }) => html`
|
|
755
|
+
<button @click=${() => terminal.runCommand({
|
|
756
|
+
title: "Build",
|
|
757
|
+
command: "npm run build",
|
|
758
|
+
open: true,
|
|
759
|
+
metadata: { "my-plugin.task": "build" },
|
|
760
|
+
})}>Build</button>
|
|
761
|
+
`
|
|
557
762
|
```
|
|
558
763
|
|
|
559
|
-
|
|
764
|
+
Review command strings carefully. They are trusted shell commands executed in the workspace terminal.
|
|
560
765
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
766
|
+
## Internal PI WEB APIs and explicit unstable opt-in
|
|
767
|
+
|
|
768
|
+
PI WEB's `/api/...` HTTP and WebSocket routes are private implementation details. Plugin code should not fetch PI WEB API endpoints directly because those URLs, response shapes, and machine-federation routing rules can change.
|
|
769
|
+
|
|
770
|
+
If a plugin author deliberately chooses to depend on an unstable runtime field while a public helper is still being designed, make that decision explicit in code with a type-only unstable import and a local type assertion:
|
|
771
|
+
|
|
772
|
+
```ts
|
|
773
|
+
import type { WorkspacePanelContext } from "@jmfederico/pi-web/plugin-api";
|
|
774
|
+
import type { UnstableWorkspacePanelContext } from "@jmfederico/pi-web/plugin-api/unstable";
|
|
775
|
+
|
|
776
|
+
function unstableContext(context: WorkspacePanelContext) {
|
|
777
|
+
return context as WorkspacePanelContext & UnstableWorkspacePanelContext;
|
|
778
|
+
}
|
|
568
779
|
```
|
|
569
780
|
|
|
570
|
-
|
|
781
|
+
Unstable APIs are not covered by the v1 compatibility promise. Prefer documented helpers whenever they exist.
|
|
571
782
|
|
|
572
783
|
## Async data and caching
|
|
573
784
|
|
|
@@ -576,7 +787,7 @@ PI WEB does not provide a plugin cache/invalidation framework. Keep host callbac
|
|
|
576
787
|
- simple contributions should be synchronous and cheap;
|
|
577
788
|
- expensive or async work should live inside the plugin;
|
|
578
789
|
- custom elements in `type: "render"` label items or panels are a good place to own async loading;
|
|
579
|
-
- dedupe
|
|
790
|
+
- dedupe async reads/commands and avoid unbounded polling;
|
|
580
791
|
- clean up intervals/event listeners in custom elements' `disconnectedCallback()`.
|
|
581
792
|
|
|
582
793
|
## Agent implementation checklist
|
|
@@ -594,8 +805,8 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
594
805
|
9. Add workspace panels for larger workspace UI.
|
|
595
806
|
10. Add workspace labels for compact inline metadata.
|
|
596
807
|
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
|
|
597
|
-
12. Use
|
|
598
|
-
13.
|
|
808
|
+
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`.
|
|
809
|
+
13. Do not fetch PI WEB `/api/...` endpoints directly. If an unstable runtime field is intentionally required, import the type from `@jmfederico/pi-web/plugin-api/unstable` and type-assert locally.
|
|
599
810
|
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
|
|
600
811
|
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
|
|
601
812
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmfederico/pi-web",
|
|
3
|
-
"version": "1.202606.
|
|
3
|
+
"version": "1.202606.1",
|
|
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";
|