@blackbelt-technology/pi-agent-dashboard 0.5.0 → 0.5.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/AGENTS.md +26 -5
- package/README.md +49 -7
- package/docs/architecture.md +129 -1
- package/package.json +15 -15
- package/packages/extension/package.json +11 -3
- package/packages/extension/src/__tests__/ask-user-tool.test.ts +1 -1
- package/packages/extension/src/__tests__/bridge-slash-command-routing.test.ts +362 -0
- package/packages/extension/src/__tests__/command-handler.test.ts +78 -8
- package/packages/extension/src/__tests__/enrich-model-metadata.test.ts +1 -1
- package/packages/extension/src/__tests__/extension-slash-command-detection.test.ts +107 -0
- package/packages/extension/src/__tests__/no-tui-multiselect-arm-regression.test.ts +1 -1
- package/packages/extension/src/__tests__/prompt-expander.test.ts +110 -1
- package/packages/extension/src/__tests__/provider-register-reload.test.ts +74 -0
- package/packages/extension/src/__tests__/retry-tracker.test.ts +147 -0
- package/packages/extension/src/__tests__/server-launcher-launch.test.ts +78 -0
- package/packages/extension/src/__tests__/session-sync.test.ts +72 -0
- package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +105 -0
- package/packages/extension/src/ask-user-tool.ts +1 -1
- package/packages/extension/src/bridge-context.ts +68 -4
- package/packages/extension/src/bridge.ts +79 -11
- package/packages/extension/src/command-handler.ts +95 -15
- package/packages/extension/src/flow-event-wiring.ts +1 -1
- package/packages/extension/src/multiselect-list.ts +1 -1
- package/packages/extension/src/pi-env.d.ts +16 -9
- package/packages/extension/src/prompt-expander.ts +74 -63
- package/packages/extension/src/provider-register.ts +16 -9
- package/packages/extension/src/retry-tracker.ts +123 -0
- package/packages/extension/src/server-launcher.ts +31 -70
- package/packages/extension/src/session-sync.ts +10 -1
- package/packages/extension/src/slash-dispatch.ts +123 -0
- package/packages/extension/src/usage-limit-orderer.ts +76 -0
- package/packages/server/bin/pi-dashboard.mjs +84 -0
- package/packages/server/package.json +8 -7
- package/packages/server/scripts/fix-pty-permissions.cjs +52 -0
- package/packages/server/src/__tests__/changelog-fs.test.ts +171 -0
- package/packages/server/src/__tests__/changelog-parser.test.ts +220 -0
- package/packages/server/src/__tests__/changelog-remote.test.ts +193 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +16 -4
- package/packages/server/src/__tests__/directory-service-openspec-enabled.test.ts +187 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +1 -1
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +1 -1
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +1 -1
- package/packages/server/src/__tests__/directory-service.test.ts +2 -2
- package/packages/server/src/__tests__/dispatch-extension-command-router.test.ts +178 -0
- package/packages/server/src/__tests__/e2e/model-proxy-google-flash.test.ts +184 -0
- package/packages/server/src/__tests__/event-wiring-providers-list.test.ts +68 -1
- package/packages/server/src/__tests__/fixtures/pi-changelog-slice.md +180 -0
- package/packages/server/src/__tests__/fork-empty-session-preflight.test.ts +268 -0
- package/packages/server/src/__tests__/headless-pid-registry.test.ts +316 -0
- package/packages/server/src/__tests__/is-pi-process.test.ts +1 -1
- package/packages/server/src/__tests__/keeper-manager.test.ts +298 -0
- package/packages/server/src/__tests__/legacy-pi-cleanup.test.ts +149 -0
- package/packages/server/src/__tests__/model-proxy-api-key-routes.test.ts +277 -0
- package/packages/server/src/__tests__/model-proxy-auth-gate.test.ts +263 -0
- package/packages/server/src/__tests__/model-proxy-multi-user.test.ts +169 -0
- package/packages/server/src/__tests__/model-proxy-routes.test.ts +286 -0
- package/packages/server/src/__tests__/model-proxy-second-port.test.ts +116 -0
- package/packages/server/src/__tests__/openspec-connect-snapshot.test.ts +64 -8
- package/packages/server/src/__tests__/openspec-group-broadcast.test.ts +97 -0
- package/packages/server/src/__tests__/openspec-group-join.test.ts +80 -0
- package/packages/server/src/__tests__/openspec-group-routes.test.ts +370 -0
- package/packages/server/src/__tests__/openspec-group-store.test.ts +496 -0
- package/packages/server/src/__tests__/package-manager-wrapper-resolve.test.ts +4 -4
- package/packages/server/src/__tests__/package-routes.test.ts +1 -1
- package/packages/server/src/__tests__/pending-fork-registry.test.ts +48 -24
- package/packages/server/src/__tests__/pi-ai-shape.test.ts +147 -0
- package/packages/server/src/__tests__/pi-changelog-integration.test.ts +165 -0
- package/packages/server/src/__tests__/pi-changelog-routes.test.ts +409 -0
- package/packages/server/src/__tests__/pi-core-checker.test.ts +155 -13
- package/packages/server/src/__tests__/pi-core-updater-managed-path.test.ts +62 -3
- package/packages/server/src/__tests__/pi-core-updater.test.ts +1 -1
- package/packages/server/src/__tests__/pi-dashboard-bin-wrapper.test.ts +84 -0
- package/packages/server/src/__tests__/pi-dev-version-check.test.ts +184 -0
- package/packages/server/src/__tests__/pi-version-skew.test.ts +4 -4
- package/packages/server/src/__tests__/process-manager-keeper-spawn.test.ts +206 -0
- package/packages/server/src/__tests__/provider-auth-routes.test.ts +12 -4
- package/packages/server/src/__tests__/provider-catalogue-cache.test.ts +13 -23
- package/packages/server/src/__tests__/provider-routes-recursion-guard.test.ts +131 -0
- package/packages/server/src/__tests__/recommended-routes.test.ts +3 -3
- package/packages/server/src/__tests__/spawn-correlation-token-integration.test.ts +91 -0
- package/packages/server/src/__tests__/spawn-register-watchdog.test.ts +84 -0
- package/packages/server/src/__tests__/spawn-token.test.ts +57 -0
- package/packages/server/src/__tests__/tunnel-watchdog.test.ts +139 -0
- package/packages/server/src/auth-plugin.ts +3 -0
- package/packages/server/src/bootstrap-state.ts +10 -0
- package/packages/server/src/browser-gateway.ts +27 -10
- package/packages/server/src/browser-handlers/handler-context.ts +9 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +128 -19
- package/packages/server/src/changelog-fs.ts +167 -0
- package/packages/server/src/changelog-parser.ts +321 -0
- package/packages/server/src/changelog-remote.ts +134 -0
- package/packages/server/src/cli.ts +62 -82
- package/packages/server/src/config-api.ts +14 -2
- package/packages/server/src/directory-service.ts +106 -4
- package/packages/server/src/event-wiring.ts +90 -6
- package/packages/server/src/headless-pid-registry.ts +344 -37
- package/packages/server/src/legacy-pi-cleanup.ts +151 -0
- package/packages/server/src/model-proxy/__tests__/api-key-store.test.ts +142 -0
- package/packages/server/src/model-proxy/__tests__/auth-json-contention.test.ts +98 -0
- package/packages/server/src/model-proxy/__tests__/concurrency.test.ts +107 -0
- package/packages/server/src/model-proxy/__tests__/failed-auth-backoff.test.ts +46 -0
- package/packages/server/src/model-proxy/__tests__/recursion-guard.test.ts +61 -0
- package/packages/server/src/model-proxy/__tests__/streamer.test.ts +139 -0
- package/packages/server/src/model-proxy/api-key-store.ts +87 -0
- package/packages/server/src/model-proxy/auth-gate.ts +116 -0
- package/packages/server/src/model-proxy/concurrency.ts +76 -0
- package/packages/server/src/model-proxy/convert/UPSTREAM.md +13 -0
- package/packages/server/src/model-proxy/convert/__tests__/anthropic-in.test.ts +137 -0
- package/packages/server/src/model-proxy/convert/__tests__/anthropic-out.test.ts +183 -0
- package/packages/server/src/model-proxy/convert/__tests__/openai-in.test.ts +134 -0
- package/packages/server/src/model-proxy/convert/__tests__/openai-out.test.ts +166 -0
- package/packages/server/src/model-proxy/convert/anthropic-in.ts +129 -0
- package/packages/server/src/model-proxy/convert/anthropic-out.ts +173 -0
- package/packages/server/src/model-proxy/convert/index.ts +8 -0
- package/packages/server/src/model-proxy/convert/openai-in.ts +119 -0
- package/packages/server/src/model-proxy/convert/openai-out.ts +151 -0
- package/packages/server/src/model-proxy/convert/types.ts +70 -0
- package/packages/server/src/model-proxy/failed-auth-backoff.ts +45 -0
- package/packages/server/src/model-proxy/internal-auth-storage.ts +146 -0
- package/packages/server/src/model-proxy/internal-registry.ts +157 -0
- package/packages/server/src/model-proxy/recursion-guard.ts +72 -0
- package/packages/server/src/model-proxy/registry-singleton.ts +109 -0
- package/packages/server/src/model-proxy/request-log.ts +53 -0
- package/packages/server/src/model-proxy/streamer.ts +59 -0
- package/packages/server/src/openspec-group-store.ts +490 -0
- package/packages/server/src/pending-client-correlations.ts +73 -0
- package/packages/server/src/pending-fork-registry.ts +24 -12
- package/packages/server/src/pi-core-checker.ts +77 -17
- package/packages/server/src/pi-core-updater.ts +16 -6
- package/packages/server/src/pi-dev-version-check.ts +145 -0
- package/packages/server/src/pi-gateway.ts +4 -0
- package/packages/server/src/pi-version-skew.ts +12 -4
- package/packages/server/src/process-manager.ts +182 -11
- package/packages/server/src/provider-auth-storage.ts +29 -47
- package/packages/server/src/provider-catalogue-cache.ts +24 -18
- package/packages/server/src/restart-helper.ts +17 -16
- package/packages/server/src/routes/bootstrap-routes.ts +37 -0
- package/packages/server/src/routes/jj-routes.ts +3 -0
- package/packages/server/src/routes/model-proxy-api-key-routes.ts +168 -0
- package/packages/server/src/routes/model-proxy-refresh-routes.ts +24 -0
- package/packages/server/src/routes/model-proxy-routes.ts +330 -0
- package/packages/server/src/routes/openspec-group-routes.ts +231 -0
- package/packages/server/src/routes/pi-changelog-routes.ts +194 -0
- package/packages/server/src/routes/pi-core-routes.ts +1 -1
- package/packages/server/src/routes/provider-auth-routes.ts +8 -1
- package/packages/server/src/routes/provider-routes.ts +28 -5
- package/packages/server/src/routes/system-routes.ts +44 -2
- package/packages/server/src/rpc-keeper/__tests__/fixtures/mock-pi-shim.sh +9 -0
- package/packages/server/src/rpc-keeper/__tests__/fixtures/mock-pi.cjs +50 -0
- package/packages/server/src/rpc-keeper/__tests__/keeper.test.ts +371 -0
- package/packages/server/src/rpc-keeper/dispatch-router.ts +85 -0
- package/packages/server/src/rpc-keeper/keeper-manager.ts +364 -0
- package/packages/server/src/rpc-keeper/keeper.cjs +313 -0
- package/packages/server/src/server.ts +254 -60
- package/packages/server/src/session-api.ts +63 -4
- package/packages/server/src/session-discovery.ts +1 -1
- package/packages/server/src/session-file-reader.ts +1 -1
- package/packages/server/src/spawn-register-watchdog.ts +62 -7
- package/packages/server/src/spawn-token.ts +20 -0
- package/packages/server/src/tunnel-watchdog.ts +230 -0
- package/packages/server/src/tunnel.ts +5 -1
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/binary-lookup-resolveJiti.test.ts +228 -0
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +24 -17
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +5 -4
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +6 -5
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +1 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +5 -4
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +2 -1
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +5 -3
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +1 -1
- package/packages/shared/src/__tests__/changelog-types.test.ts +78 -0
- package/packages/shared/src/__tests__/config-openspec.test.ts +74 -0
- package/packages/shared/src/__tests__/model-proxy-config.test.ts +146 -0
- package/packages/shared/src/__tests__/no-raw-node-import.test.ts +7 -5
- package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +56 -20
- package/packages/shared/src/__tests__/node-spawn.test.ts +51 -0
- package/packages/shared/src/__tests__/openspec-groups-types.test.ts +135 -0
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +96 -0
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +11 -3
- package/packages/shared/src/__tests__/server-launcher.test.ts +227 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +1 -1
- package/packages/shared/src/bootstrap-install.ts +1 -1
- package/packages/shared/src/browser-protocol.ts +70 -0
- package/packages/shared/src/changelog-types.ts +111 -0
- package/packages/shared/src/config.ts +172 -2
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +16 -1
- package/packages/shared/src/dashboard-plugin/slot-props.ts +8 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +57 -0
- package/packages/shared/src/platform/binary-lookup.ts +204 -0
- package/packages/shared/src/platform/node-spawn.ts +71 -26
- package/packages/shared/src/protocol.ts +27 -1
- package/packages/shared/src/recommended-extensions.ts +18 -0
- package/packages/shared/src/rest-api.ts +219 -1
- package/packages/shared/src/server-launcher.ts +277 -0
- package/packages/shared/src/skill-block-parser.ts +1 -1
- package/packages/shared/src/tool-registry/__tests__/pi-ai-registration.test.ts +124 -0
- package/packages/shared/src/tool-registry/definitions.ts +15 -3
- package/packages/shared/src/types.ts +62 -0
- package/packages/shared/src/__tests__/resolve-jiti.test.ts +0 -53
- package/packages/shared/src/resolve-jiti.ts +0 -102
package/AGENTS.md
CHANGED
|
@@ -227,8 +227,12 @@ This section lists only the **architectural backbone** — the files agents touc
|
|
|
227
227
|
| `src/shared/types.ts` | Data models (Session, Workspace, Event) |
|
|
228
228
|
| `src/shared/config.ts` | Shared config loader (`~/.pi/dashboard/config.json`) |
|
|
229
229
|
| `src/shared/semaphore.ts` | Tiny FIFO semaphore (`createSemaphore(max)`) |
|
|
230
|
-
| `src/extension/bridge.ts` | Main bridge extension entry; PromptBus patch site, sync/tracker/flow composition |
|
|
231
|
-
| `src/extension/bridge-context.ts` | Shared mutable state type + helpers for bridge modules |
|
|
230
|
+
| `src/extension/bridge.ts` | Main bridge extension entry; PromptBus patch site, sync/tracker/flow composition; sessionPrompt routes through slash-dispatch (extension-cmd dispatch + stopgap) before template fallback. See change: fix-extension-slash-commands-in-dashboard. |
|
|
231
|
+
| `src/extension/bridge-context.ts` | Shared mutable state type + helpers for bridge modules; hosts `isExtensionSlashCommand`, `hasDispatchCommand`, `DASHBOARD_NATIVE_COMMANDS` (= {"roles"}). See change: fix-extension-slash-commands-in-dashboard. |
|
|
232
|
+
| `src/extension/slash-dispatch.ts` | Shared `tryDispatchExtensionCommand` helper: routing-step 9 (three-way decision: pi.dispatchCommand 0.71+ → server-routed via RPC keeper UDS when headless → stopgap). See change: fix-extension-slash-commands-in-dashboard, add-rpc-stdin-dispatch-with-keeper-sidecar. |
|
|
233
|
+
| `packages/server/src/rpc-keeper/dispatch-router.ts` | Handles `dispatch_extension_command` extension→server message. Forwards pi RPC line via `headlessPidRegistry.writeRpc`; emits optimistic `command_feedback {completed}` or {error}. See change: add-rpc-stdin-dispatch-with-keeper-sidecar. |
|
|
234
|
+
| `packages/server/src/rpc-keeper/keeper-manager.ts` | Server-side helper: `spawnKeeperFor`, `writeRpc(sessionId)`, `writeRpcToSockPath`, `killKeeper`, `discoverExistingKeepers`. Singleton via `getKeeperManager()` in process-manager.ts. See change: add-rpc-stdin-dispatch-with-keeper-sidecar. |
|
|
235
|
+
| `packages/server/src/rpc-keeper/keeper.cjs` | CJS-pure RPC keeper sidecar; spawns pi as child, owns stdin pipe, listens on per-session UDS / named pipe; outlives dashboard server. See change: add-rpc-stdin-dispatch-with-keeper-sidecar. |
|
|
232
236
|
| `src/extension/session-sync.ts` | Session register, replay, and switch/fork handling |
|
|
233
237
|
| `src/extension/model-tracker.ts` | Model/thinking-level/git/name change detection |
|
|
234
238
|
| `src/extension/flow-event-wiring.ts` | Flow event listener registration (flow:* → event_forward) |
|
|
@@ -237,7 +241,7 @@ This section lists only the **architectural backbone** — the files agents touc
|
|
|
237
241
|
| `src/shared/server-identity.ts` | Identity-verified health check (`isDashboardRunning`) |
|
|
238
242
|
| `src/shared/mdns-discovery.ts` | mDNS advertise/discover/browse for `_pi-dashboard._tcp` |
|
|
239
243
|
| `src/extension/server-launcher.ts` | Auto-start server as detached process; logs to `~/.pi/dashboard/server.log` |
|
|
240
|
-
| `src/extension/command-handler.ts` | Command routing: `!`/`!!` bash, `/compact`, slash commands |
|
|
244
|
+
| `src/extension/command-handler.ts` | Command routing: `!`/`!!` bash, `/compact`, slash commands; slash else-arm now gates through extension-dispatch helper before `sendUserMessage`. See change: fix-extension-slash-commands-in-dashboard. |
|
|
241
245
|
| `src/extension/prompt-expander.ts` | Slash command → prompt template expansion |
|
|
242
246
|
| `src/extension/dev-build.ts` | Dev build-on-reload helper (client build + server shutdown) |
|
|
243
247
|
| `src/extension/server-auto-start.ts` | mDNS-first → health check → auto-start with concurrent launch detection |
|
|
@@ -390,6 +394,22 @@ This section lists only the **architectural backbone** — the files agents touc
|
|
|
390
394
|
| `src/server/provider-auth-storage.ts` | Read/write `~/.pi/agent/auth.json` with lockfile for pi provider credentials |
|
|
391
395
|
| `src/server/routes/provider-auth-routes.ts` | REST routes: provider OAuth authorize/exchange/callback, device-code, API key CRUD |
|
|
392
396
|
| `src/server/routes/provider-routes.ts` | REST routes: custom LLM provider CRUD + connection probe |
|
|
397
|
+
| `src/server/model-proxy/auth-gate.ts` | Fastify `onRequest` hook for `/v1/*`: uniform proxy-key auth, backoff, scope check |
|
|
398
|
+
| `src/server/model-proxy/api-key-store.ts` | Pure helpers: `hashKey`, `verifyKey`, `generateKey`, `findApiKey`, `recordKeyUsage`, `keyHasScope` |
|
|
399
|
+
| `src/server/model-proxy/concurrency.ts` | `ConcurrencyTracker`: server-wide + per-key + per-provider caps; throws `ConcurrencyError` |
|
|
400
|
+
| `src/server/model-proxy/failed-auth-backoff.ts` | Per-IP exponential backoff (10ms→10s cap) for failed proxy-key auth |
|
|
401
|
+
| `src/server/model-proxy/internal-registry.ts` | Server-resident model registry: reads auth/providers/models.json + pi-ai built-ins |
|
|
402
|
+
| `src/server/model-proxy/internal-auth-storage.ts` | Wraps `provider-auth-storage.ts`; handles OAuth-refresh-on-expiry via pi-ai per-provider helpers |
|
|
403
|
+
| `src/server/model-proxy/registry-singleton.ts` | Lazy singleton: `getModelRegistry()`, `refreshModelRegistry()`, `getModelProxyStatus()` |
|
|
404
|
+
| `src/server/model-proxy/recursion-guard.ts` | `isSelfPointing(baseUrl, origins)`: detects dashboard-pointing custom provider baseUrls |
|
|
405
|
+
| `src/server/model-proxy/request-log.ts` | Append-mode JSONL request log at `~/.pi/dashboard/model-proxy.jsonl`; 50MB rotation |
|
|
406
|
+
| `src/server/model-proxy/streamer.ts` | `streamCompletion(opts, streamSimple, registry?)`: resolves creds then streams via pi-ai |
|
|
407
|
+
| `src/server/model-proxy/convert/` | Lifted MIT converters: OpenAI↔pi-ai, Anthropic↔pi-ai (format conversions, SSE output) |
|
|
408
|
+
| `src/server/routes/model-proxy-routes.ts` | Route handlers: `GET /v1/models`, `POST /v1/chat/completions`, `POST /v1/messages` |
|
|
409
|
+
| `src/server/routes/model-proxy-api-key-routes.ts` | REST CRUD for proxy API keys: list, create, revoke, purge (JWT-gated) |
|
|
410
|
+
| `src/server/routes/model-proxy-refresh-routes.ts` | `POST /api/model-proxy/refresh`: JWT-gated manual registry refresh |
|
|
411
|
+
| `src/client/components/ModelProxySection.tsx` | Settings section: proxy toggle, second-port, API key table + reveal-once banner |
|
|
412
|
+
| `src/client/lib/model-proxy-api.ts` | Client fetch helpers: `listApiKeys`, `createApiKey`, `revokeApiKey`, `deleteApiKey`, `refreshRegistry` |
|
|
393
413
|
| `src/server/provider-probe.ts` | Pure per-API probe builders + I/O `probeProvider` (8s timeout, no apiKey echo) |
|
|
394
414
|
| `src/extension/provider-register.ts` | Reads `providers.json`, calls `pi.registerProvider`, hot-reload on credentials change |
|
|
395
415
|
| `src/client/lib/providers-api.ts` | Client fetch helper for `/api/providers/test` connection probe |
|
|
@@ -420,7 +440,7 @@ This section lists only the **architectural backbone** — the files agents touc
|
|
|
420
440
|
| `src/client/components/ZrokInstallGuide.tsx` | OS-aware zrok installation guide view |
|
|
421
441
|
| `src/server/cli.ts` | CLI entry: start/stop/restart/status; `cmdRestart` delegates to `/api/restart` when up |
|
|
422
442
|
| `src/server/restart-helper.ts` | Cross-platform `/api/restart` orchestrator (detached node-built-ins-only spawner) |
|
|
423
|
-
| `
|
|
443
|
+
| `packages/shared/src/server-launcher.ts` | `launchDashboardServer` — single shared spawn primitive (jiti loader, argv, env, log header, readiness) used by Bridge / Standalone / Electron starters |
|
|
424
444
|
| `src/shared/platform/paths.ts` | OS-aware path primitives (`normalizePath`, `samePath`, `parsePathInput`) |
|
|
425
445
|
| `src/client/lib/session-grouping.ts` | Sessions grouped by directory; `resolveSessionGroupPath` (pin > jjState.workspaceRoot > cwd) |
|
|
426
446
|
| `src/shared/platform/` | Unified cross-OS primitives barrel (exec/runner/git/openspec/npm/process/binary-lookup/...) |
|
|
@@ -482,7 +502,8 @@ This section lists only the **architectural backbone** — the files agents touc
|
|
|
482
502
|
| `packages/electron/scripts/test-electron-install.sh` | Full e2e Docker test: install, wizard, server launch, health check |
|
|
483
503
|
| `packages/electron/scripts/test-electron-install-inner.sh` | Inner test script run inside Docker container |
|
|
484
504
|
| `packages/electron/resources/icon.png` | Master 1024×1024 app icon |
|
|
485
|
-
| `.github/workflows/publish.yml` | CI: build matrix × 6 (platform,arch); idempotent ordered npm publish; no-bash-on-Windows |
|
|
505
|
+
| `.github/workflows/publish.yml` | CI: build matrix × 6 (platform,arch); idempotent ordered npm publish; lockfile regen + verify in prepare; no-bash-on-Windows |
|
|
506
|
+
| `scripts/verify-lockfile-versions.mjs` | Sanity gate: asserts every cross-ref in `package-lock.json` is `^<root.version>`; runs after `npm install --package-lock-only` in `prepare` |
|
|
486
507
|
| `packages/shared/src/__tests__/publish-workflow-contract.test.ts` | Repo-lint: pin electron job's `needs:` array and `fail-fast: false` |
|
|
487
508
|
| `packages/shared/src/__tests__/no-bash-on-windows.test.ts` | Repo-lint: forbid `shell: bash` on steps reachable on Windows runners |
|
|
488
509
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@blackbelt-technology/pi-agent-dashboard)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**One browser tab to command an army of [pi](https://github.com/badlogic/pi-mono) agents.** Spawn parallel sessions, watch reasoning live, attach OpenSpec changes, ship work — from your laptop or phone.
|
|
8
8
|
|
|
9
9
|
🌐 **Website & demo:** [blackbelttechnology.github.io/pi-agent-dashboard](https://blackbelttechnology.github.io/pi-agent-dashboard) — animated tour, screenshots, and install guide.
|
|
10
10
|
📝 **Changelog:** [`CHANGELOG.md`](CHANGELOG.md)
|
|
@@ -13,6 +13,18 @@ A web-based dashboard for monitoring and interacting with [pi](https://github.co
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## Screenshots
|
|
17
|
+
|
|
18
|
+
<table>
|
|
19
|
+
<tr>
|
|
20
|
+
<td width="33%" align="center"><a href="docs/screenshots/readme-overview.png"><img src="docs/screenshots/readme-overview.png" alt="Sessions overview — folders, branches, OpenSpec changes, live token spend" /></a><br/><sub><b>Overview</b> — sessions grouped by folder, branch & OpenSpec context, live cost</sub></td>
|
|
21
|
+
<td width="33%" align="center"><a href="docs/screenshots/readme-session.png"><img src="docs/screenshots/readme-session.png" alt="Active session — chat, attached OpenSpec change, ask_user prompt, token gauge" /></a><br/><sub><b>Session</b> — chat, OpenSpec apply, interactive <code>ask_user</code>, context gauge</sub></td>
|
|
22
|
+
<td width="33%" align="center"><a href="docs/screenshots/readme-settings.png"><img src="docs/screenshots/readme-settings.png" alt="Settings — ports, spawn strategy, tunnel, resolved tools table" /></a><br/><sub><b>Settings</b> — ports, spawn strategy, zrok tunnel, tool resolution</sub></td>
|
|
23
|
+
</tr>
|
|
24
|
+
</table>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
16
28
|
## Table of contents
|
|
17
29
|
|
|
18
30
|
- [Quickstart](#quickstart)
|
|
@@ -44,12 +56,21 @@ Download a pre-built installer from [GitHub Releases](https://github.com/BlackBe
|
|
|
44
56
|
|----------|----------|
|
|
45
57
|
| **macOS** (Apple Silicon / Intel) | `.dmg` (arm64 / x64) |
|
|
46
58
|
| **Linux** (x64 / ARM64) | `.deb` or `.AppImage` |
|
|
47
|
-
| **Windows** (x64 / ARM64) | `.
|
|
59
|
+
| **Windows** (x64 / ARM64) | `.zip` |
|
|
48
60
|
|
|
49
61
|
On first launch a setup wizard walks you through mode selection (standalone vs. power-user), API key / OAuth sign-in, and [recommended extensions](#recommended-extensions). The standalone mode bundles Node.js and auto-installs pi + dashboard + openspec into `~/.pi-dashboard/` — **no terminal, npm, or Node.js required**.
|
|
50
62
|
|
|
51
63
|
**Picking the right macOS DMG:** run `uname -m` in Terminal — `arm64` means Apple Silicon (M1/M2/M3/M4), `x86_64` means Intel. Or open Apple menu → About This Mac and read the chip name. Download the matching DMG; if you grab the wrong one macOS will refuse to launch the app with a "cannot be opened" error.
|
|
52
64
|
|
|
65
|
+
**First-run unblocking (unsigned binaries):**
|
|
66
|
+
|
|
67
|
+
- **macOS** — the DMGs are not yet notarized. Either right-click `PI-Dashboard.app` → *Open* the first time, or clear all extended attributes from Terminal:
|
|
68
|
+
```bash
|
|
69
|
+
xattr -cr /Applications/PI-Dashboard.app
|
|
70
|
+
```
|
|
71
|
+
Use `-cr` (clear, recursive) rather than `-d com.apple.quarantine` — it's idempotent and won't print `No such xattr: com.apple.quarantine` when the attribute isn't there. That message is harmless; it just means quarantine was never set or already cleared.
|
|
72
|
+
- **Windows** — SmartScreen warns on first launch. Click *More info → Run anyway*, or right-click the downloaded `.exe` / `.zip` → *Properties* → tick *Unblock* → *OK* before running. For ZIPs, unblock the archive before extracting.
|
|
73
|
+
|
|
53
74
|
> **Note:** A future release will rename the macOS DMGs to `PI-Dashboard-darwin-arm64-<ver>.dmg` and `PI-Dashboard-darwin-x64-<ver>.dmg` (previously a single `PI Dashboard.dmg` was produced and silently overwrote one arch on each release). Direct download links pointing at the unsuffixed filename will 404 from that release onward; please link to the [Releases page](https://github.com/BlackBeltTechnology/pi-agent-dashboard/releases) instead. See OpenSpec change `fix-darwin-dmg-arch-collision`.
|
|
54
75
|
|
|
55
76
|
### B — pi package (recommended for CLI users)
|
|
@@ -263,6 +284,27 @@ The file is deliberately separate from `config.json` so machine-specific paths d
|
|
|
263
284
|
|
|
264
285
|
---
|
|
265
286
|
|
|
287
|
+
## Using the model proxy
|
|
288
|
+
|
|
289
|
+
The dashboard exposes an OpenAI-compatible HTTP proxy on the same port as the dashboard UI (`/v1/...`). Any LLM client that accepts a custom `base_url` can use it.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Example: point an OpenAI-compatible client at the dashboard
|
|
293
|
+
export OPENAI_BASE_URL=http://localhost:8000/v1
|
|
294
|
+
export OPENAI_API_KEY=pi-proxy-<your-proxy-key>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Setup:** open Settings → API Proxy in the dashboard UI, enable the proxy, and create an API key.
|
|
298
|
+
|
|
299
|
+
**Endpoints:**
|
|
300
|
+
- `GET /v1/models` — list available models (requires `models:list` scope or `all`)
|
|
301
|
+
- `POST /v1/chat/completions` — OpenAI chat completions, streaming + non-streaming
|
|
302
|
+
- `POST /v1/messages` — Anthropic messages, streaming + non-streaming
|
|
303
|
+
|
|
304
|
+
**Auth:** proxy API keys only (`pi-proxy-*` prefix). Dashboard JWT is never accepted on `/v1/*`.
|
|
305
|
+
|
|
306
|
+
For migration from `@blackbelt-technology/pi-model-proxy`, see [`docs/migration/from-pi-model-proxy.md`](docs/migration/from-pi-model-proxy.md).
|
|
307
|
+
|
|
266
308
|
## Usage
|
|
267
309
|
|
|
268
310
|
### Auto-start (default)
|
|
@@ -640,7 +682,7 @@ Output by platform:
|
|
|
640
682
|
|----------|--------|----------|
|
|
641
683
|
| macOS | `.dmg` | `packages/electron/out/make/` |
|
|
642
684
|
| Linux | `.deb` + `.AppImage` | `packages/electron/out/make/` |
|
|
643
|
-
| Windows | `.
|
|
685
|
+
| Windows | `.zip` | `packages/electron/out/make/` |
|
|
644
686
|
|
|
645
687
|
### Cross-platform builds (Docker)
|
|
646
688
|
|
|
@@ -649,7 +691,7 @@ From macOS or Linux, build installers for all platforms:
|
|
|
649
691
|
```bash
|
|
650
692
|
npm run electron:build -- --all # macOS (native) + Linux + Windows (Docker)
|
|
651
693
|
npm run electron:build -- --linux # Linux .deb + .AppImage only
|
|
652
|
-
npm run electron:build -- --windows # Windows .
|
|
694
|
+
npm run electron:build -- --windows # Windows .zip only
|
|
653
695
|
npm run electron:build -- --linux --windows # Both, skip native
|
|
654
696
|
```
|
|
655
697
|
|
|
@@ -663,7 +705,7 @@ npm run electron:build -- --mac-both
|
|
|
663
705
|
|
|
664
706
|
Requires Rosetta 2 (`softwareupdate --install-rosetta --agree-to-license`) so node-pty's x64 prebuilt binary can be unpacked during the cross-arch run. The script wipes per-arch caches between the two builds (`resources/.last-arch` sentinel) so back-to-back runs don't accidentally ship arm64 binaries inside an x64 DMG. Intel macs cannot cross-build arm64 locally (Rosetta is one-way) — use CI for arm64 validation.
|
|
665
707
|
|
|
666
|
-
Docker builds use a Node 22 Debian container
|
|
708
|
+
Docker builds use a Node 22 Debian container for Windows cross-compilation. Output goes to `packages/electron/out/make/`.
|
|
667
709
|
|
|
668
710
|
### Electron dev mode
|
|
669
711
|
|
|
@@ -718,8 +760,8 @@ This runs CI, publishes to npm with `--provenance` for supply-chain transparency
|
|
|
718
760
|
| `macos-15-intel` | macOS x64 | `.dmg` (Intel; last GitHub-hosted x86_64 image, EOL 2027-08) |
|
|
719
761
|
| `ubuntu-latest` | Linux x64 | `.deb` + `.AppImage` |
|
|
720
762
|
| `ubuntu-24.04-arm` | Linux arm64 | `.deb` |
|
|
721
|
-
| `windows-latest` | Windows x64 | `.
|
|
722
|
-
| `windows-latest` | Windows arm64 | `.zip`
|
|
763
|
+
| `windows-latest` | Windows x64 | `.zip` |
|
|
764
|
+
| `windows-latest` | Windows arm64 | `.zip` (x64 Node.js via WoW64) |
|
|
723
765
|
|
|
724
766
|
All artifacts are uploaded to a **draft GitHub Release**. Release notes are extracted automatically from the matching `## [<version>]` section of [`CHANGELOG.md`](CHANGELOG.md).
|
|
725
767
|
|
package/docs/architecture.md
CHANGED
|
@@ -483,6 +483,24 @@ See changes: `unified-bootstrap-install`, `pi-zero-seventy-compat`, `warn-pi-ver
|
|
|
483
483
|
|
|
484
484
|
Electron resources ship bundled Node under `resources/node/` (Windows: `node.exe` + `npm.cmd` + `npx.cmd` at root; Unix: `bin/node` + `bin/npm` + `bin/npx`). `installManagedNode` (`packages/shared/src/bootstrap-install.ts`) `fs.cp`-copies bundle into `<managedDir>/node/` (default `~/.pi-dashboard/node/`), writes `.version` marker for idempotency. `installStandalone` calls it BEFORE first `bootstrapInstall` so npm shims exist when registry install runs; Doctor calls it unconditionally on every launch as a self-repair step. ToolRegistry `node` + `npm` strategy chains prefer `<managedDir>/node/` ahead of system PATH; `prependManagedNodeToPath(env, managedDir)` (`packages/shared/src/platform/managed-node-path.ts`) injects same dir at HEAD of every spawned child's `PATH` (pi-session, pi-core-updater, headless RPC, server-launcher) so `npm.cmd`/`npx.cmd` resolve without `where npm` returning empty on Windows. Standalone CLI / dev / builds without `resources/node/`: bundled source absent, both helpers no-op, resolver falls through to system PATH. See change: embed-managed-node-runtime.
|
|
485
485
|
|
|
486
|
+
#### Legacy pi detection & cleanup
|
|
487
|
+
|
|
488
|
+
Pi renamed `@mariozechner/pi-coding-agent` → `@earendil-works/pi-coding-agent` at v0.74. Old scope ships only up to v0.73.x; new scope's `bin/pi` symlink collides with legacy install on `npm install -g` (EEXIST), producing silent "no spawning" failures when both exist.
|
|
489
|
+
|
|
490
|
+
`packages/server/src/legacy-pi-cleanup.ts` scans 3 locations: npm-global (`npm root -g` → `<root>/@mariozechner/pi-coding-agent`), npx-cache (`~/.npm/_npx/*/node_modules/@mariozechner/pi-coding-agent`, all hashed entries), managed (`~/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent`). Detection cost: one `npm root -g` (~50ms) + `fs.statSync` per candidate. Read-only.
|
|
491
|
+
|
|
492
|
+
Startup scan: `server.ts` calls `detectLegacyPiInstalls()` once at boot, writes result to `bootstrapState.legacyPiInstalls`. Broadcast via `bootstrap_status_update` WS.
|
|
493
|
+
|
|
494
|
+
REST: `GET /api/bootstrap/legacy-pi` force-refreshes detection. `POST /api/bootstrap/legacy-pi/cleanup` removes all detected installs, re-scans, returns `{results, remaining}`. Both auth-gated.
|
|
495
|
+
|
|
496
|
+
UI: `BootstrapBanner` renders amber "Remove legacy pi (N)" sub-banner when `legacyPiInstalls.length > 0` AND `status === "ready"`. Takes precedence over upgrade-recommended hint (legacy install blocks `upgrade-pi`). Wired via `useBootstrapStatus().cleanupLegacyPi()`.
|
|
497
|
+
|
|
498
|
+
Cleanup actions: npm-global removed via `npm uninstall -g @mariozechner/pi-coding-agent --no-fund --no-audit` so bin symlinks unwound; npx-cache + managed via `fs.rmSync({recursive, force})`. Per-install try/catch — one failure does not abort siblings.
|
|
499
|
+
|
|
500
|
+
Idempotent: empty pre-scan short-circuits the POST without invoking npm; missing path under `fs.rmSync({force:true})` returns `removed: true`. Tests in `packages/server/src/__tests__/legacy-pi-cleanup.test.ts` (12 cases) cover pure helpers, fs-backed detection under HOME-tempdir isolation, and removal idempotency.
|
|
501
|
+
|
|
502
|
+
See change: legacy-pi-cleanup.
|
|
503
|
+
|
|
486
504
|
### Force Kill Escalation
|
|
487
505
|
The Stop button supports two-click escalation for stuck sessions:
|
|
488
506
|
1. **Click 1 (Abort)**: Sends `abort` → bridge → `ctx.abort()`. Button transitions to orange pulsing "Force Stop".
|
|
@@ -697,6 +715,12 @@ Fold-back is **a skill, not a server route**. The dashboard's `JjFoldBackDialog`
|
|
|
697
715
|
7. Session cards display processes with elapsed time and a kill button (sends SIGTERM to process group)
|
|
698
716
|
|
|
699
717
|
### OpenSpec Polling (Server-Side)
|
|
718
|
+
|
|
719
|
+
**Master gate**: `DashboardConfig.openspec.enabled` (boolean, default `true`).
|
|
720
|
+
- `false` disables all polling. Hides OPENSPEC subcards across dashboard.
|
|
721
|
+
- Tuning fields below (`pollIntervalSeconds`, `maxConcurrentSpawns`, `changeDetection`, `jitterSeconds`) ignored at runtime when `false`. Values preserved.
|
|
722
|
+
- Runtime-reconfigurable via `PUT /api/config`. Disable transition clears per-cwd `OpenSpecData` cache + broadcasts cleared payload `{ initialized: false, pending: false, changes: [] }` per cwd so client predicate `openspecInitialized === false && pending === false` collapses subcards uniformly. Same broadcast shape covers "no openspec/ dir" + "openspec.enabled === false".
|
|
723
|
+
|
|
700
724
|
1. Server's DirectoryService polls `openspec` CLI for each known directory (union of pinned dirs + session cwds) at a **configurable interval** (`DashboardConfig.openspec.pollIntervalSeconds`, default 30 s, range 5–3600 s).
|
|
701
725
|
2. OpenSpec data is keyed by directory (cwd), not by session — one poll per directory regardless of session count.
|
|
702
726
|
3. Changes are broadcast to all connected browsers via `openspec_update { cwd, data }`.
|
|
@@ -1012,6 +1036,24 @@ To disable: set `tunnel.enabled` to `false` in `~/.pi/dashboard/config.json` or
|
|
|
1012
1036
|
The client can query `GET /api/tunnel-status` which returns `{ status: "active"|"inactive"|"unavailable", url?, serverOs }`.
|
|
1013
1037
|
The client can connect/disconnect the tunnel via `POST /api/tunnel-connect` and `POST /api/tunnel-disconnect`.
|
|
1014
1038
|
|
|
1039
|
+
### Tunnel watchdog
|
|
1040
|
+
|
|
1041
|
+
Long-lived `zrok share` goes stale on edge. Watchdog detects + recycles.
|
|
1042
|
+
|
|
1043
|
+
- Probe target: `GET ${publicUrl}/api/health` through public zrok URL (real edge round-trip, not localhost).
|
|
1044
|
+
- Cadence: every `intervalMs` (default 60000).
|
|
1045
|
+
- Failure classes: HTTP 5xx, network error, timeout (`probeTimeoutMs`, default 10000). 4xx + 2xx count as healthy reachability.
|
|
1046
|
+
- Threshold: `failureThreshold` consecutive failures (default 2) triggers recycle.
|
|
1047
|
+
- Recycle action: `deleteTunnel()` then `createTunnel(port, reservedToken)`. Reserved token preserved — URL stable.
|
|
1048
|
+
- Counter resets to 0 on success or after successful recycle.
|
|
1049
|
+
- Recycle-failure backoff: next retry delay ×2 each consecutive recycle failure, capped ×8 `intervalMs`. Resets to base on first successful probe.
|
|
1050
|
+
- Config: `tunnel.watchdog.{enabled, intervalMs, failureThreshold, probeTimeoutMs}` in `~/.pi/dashboard/config.json`. Defaults: enabled true, 60s, 2 failures, 10s timeout.
|
|
1051
|
+
- Status: `GET /api/tunnel-status` active variant carries `watchdog: {lastProbeAt, lastSuccessAt, lastFailureAt, lastFailureReason, consecutiveFailures, lastRecycleAt, recycleCount}`.
|
|
1052
|
+
- Lifecycle: `startTunnelWatchdog` called in `server.ts` after `createTunnel` succeeds at startup + in `/api/tunnel-connect` after on-demand connect. `stopTunnelWatchdog` called before `deleteTunnel` in graceful shutdown + `/api/tunnel-disconnect`.
|
|
1053
|
+
- Module: `packages/server/src/tunnel-watchdog.ts`. Tests: `packages/server/src/__tests__/tunnel-watchdog.test.ts`.
|
|
1054
|
+
- Settings UI: Settings → Tunnel exposes watchdog enable + intervalMs + failureThreshold + probeTimeoutMs.
|
|
1055
|
+
- Live reload: `PUT /api/config` with `partial.tunnel` stops + restarts watchdog against new config when tunnel active. No server restart required for watchdog tweaks.
|
|
1056
|
+
- `writeConfigPartial` deep-merges `tunnel.watchdog` so partial UI saves preserve unspecified fields.
|
|
1015
1057
|
|
|
1016
1058
|
|
|
1017
1059
|
### CORS
|
|
@@ -1179,6 +1221,42 @@ Every mechanism branch forwards `sessionFile` + `mode` via the shared `sessionFl
|
|
|
1179
1221
|
|
|
1180
1222
|
On Windows, `spawnDetached` uses `detached: true` which (via libuv's `src/win/process.c`) emits `DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP` and critically does NOT call `AssignProcessToJobObject` on the parent's global Job Object. This excludes the child from the parent's `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` job, so pi sessions survive when the dashboard server exits — matching Unix PGID behavior. The `headlessPidRegistry` reconciles these survivors on server restart.
|
|
1181
1223
|
|
|
1224
|
+
### RPC keeper sidecar
|
|
1225
|
+
|
|
1226
|
+
Introduced by change `add-rpc-stdin-dispatch-with-keeper-sidecar`. Experimental, gated by `useRpcKeeper` config flag (default `false`). Resolves typed extension slash commands (`/ctx-stats`, `/curator`, `/agents`, `/flows:*`) in headless dashboard sessions despite pi 0.74 `ExtensionAPI` exposing no `dispatchCommand`.
|
|
1227
|
+
|
|
1228
|
+
Per-session keeper process owns pi's stdin pipe. Server writes RPC lines to keeper via UDS (Unix) or named pipe (Windows). Keeper forwards verbatim to pi's stdin. Pi's `--mode rpc` reader runs `session.prompt(text, {expandPromptTemplates: true})` which dispatches slash commands.
|
|
1229
|
+
|
|
1230
|
+
Keeper outlives dashboard server restarts. Replaces Unix `tail -f /dev/null | pi` wrapper. Brings Windows to durability parity (today Windows loses pi on server death).
|
|
1231
|
+
|
|
1232
|
+
Three-process topology + dual-channel boundary:
|
|
1233
|
+
|
|
1234
|
+
```mermaid
|
|
1235
|
+
flowchart LR
|
|
1236
|
+
S["dashboard server"]
|
|
1237
|
+
K["keeper.cjs<br/>(1 per session)"]
|
|
1238
|
+
P["pi --mode rpc"]
|
|
1239
|
+
B["bridge.ts<br/>(loaded inside pi)"]
|
|
1240
|
+
|
|
1241
|
+
S -->|"UDS /<sessionId>.rpc.sock<br/>(slash dispatch only)"| K
|
|
1242
|
+
K -->|"pi.stdin pipe<br/>(forward JSON lines)"| P
|
|
1243
|
+
P --- B
|
|
1244
|
+
B -->|"bridge WS<br/>(events, send_prompt non-slash, abort, model, etc.)"| S
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
UDS path: `~/.pi/dashboard/sessions/<sessionId>.rpc.sock`. Windows pipe: `\\.\pipe\pi-rpc-<sessionId>`. Keeper PID sidecar: `<sockPath>.pid`. Server scans on startup for orphan-cleanup + reattach.
|
|
1248
|
+
|
|
1249
|
+
Protocol: line-framed JSON, fire-and-forget. Server writes `{"type":"prompt","message":"/cmd","id":"<requestId>"}\n`. Keeper forwards raw line; no parsing, no response. Acknowledgement implicit (UDS write success).
|
|
1250
|
+
|
|
1251
|
+
Dual-channel boundary explicit:
|
|
1252
|
+
- **Bridge WS** owns: send_prompt non-slash, abort, model switch, thinking-level, compaction, rename, events, flow control.
|
|
1253
|
+
- **Server → keeper UDS** owns: extension slash dispatch only.
|
|
1254
|
+
- **headlessPidRegistry kill** owns: kill-by-pid for shutdown / force-kill / reload.
|
|
1255
|
+
|
|
1256
|
+
Bridge cannot reach `session.prompt` from inside pi 0.74. Server can (owns spawn + keeper). Routing slash dispatch through the channel that has the capability is correct given the constraint.
|
|
1257
|
+
|
|
1258
|
+
Lifecycle: pi exits → keeper exits 0, unlinks socket + pid sidecar. Keeper crashes → pi reads EOF on stdin → exits. Force-kill → server kills pi PID first, schedules 200 ms keeper-fallback SIGTERM. Tmux / Windows-Terminal sessions retain the existing `command_feedback {error}` stopgap (terminal owns pi's stdin, no UDS route).
|
|
1259
|
+
|
|
1182
1260
|
### Server Log Hygiene
|
|
1183
1261
|
|
|
1184
1262
|
The daemon log at `~/.pi/dashboard/server.log` is opened in **append mode** (`"a"`) so crash output from prior start attempts survives subsequent retries — essential for diagnosing silent failures. Each attempt writes a timestamped header to distinguish runs:
|
|
@@ -1522,7 +1600,7 @@ Every external binary, module, and directory the dashboard depends on is resolve
|
|
|
1522
1600
|
| Tool | Kind | Strategy chain |
|
|
1523
1601
|
|---|---|---|
|
|
1524
1602
|
| `pi` | binary | override → managed (`MANAGED_BIN/pi[.cmd]`) → where |
|
|
1525
|
-
| `pi-coding-agent` | module | override → bare-import → managed (`MANAGED_DIR/node_modules/.../dist/index.js`) → npm-global; probes
|
|
1603
|
+
| `pi-coding-agent` | module | override → bare-import → managed (`MANAGED_DIR/node_modules/.../dist/index.js`) → npm-global; probes `@earendil-works/*` (primary) and `@mariozechner/*` (legacy) aliases |
|
|
1526
1604
|
| `openspec`, `npm`, `node`, `tsx`, `git`, `zrok` | binary | override → managed → where |
|
|
1527
1605
|
| `pi-dashboard` | module | override → managed → npm-global (presence of `package.json` is enough) |
|
|
1528
1606
|
| `electron` | module | override → bare-import (`paths: ["packages/electron"]`) → managed; resolves the package directory containing `install.js`, hoist-aware. See change: register-build-time-tools |
|
|
@@ -1998,3 +2076,53 @@ Diagnostics MUST never crash the app. `doctor-core.ts` enforces this with three
|
|
|
1998
2076
|
- **`assumedMandatory(label, fn, deps)`** — wraps "should-never-fail" ops (e.g. reading bundled-Node version, listing `~/.pi-dashboard/`). On throw it appends a structured entry to `<managedDir>/doctor.log` (1MB ring rotation) AND surfaces a row in the `Diagnostics` section so the user sees something went wrong instead of a silent gap. The log is opened from the toolbar (`Open doctor log`); the IPC handler returns `{exists:false}` when the file is absent so the renderer can show "no entries yet" instead of erroring.
|
|
1999
2077
|
|
|
2000
2078
|
See change: `doctor-rich-output`.
|
|
2079
|
+
|
|
2080
|
+
## Model Proxy
|
|
2081
|
+
|
|
2082
|
+
Dashboard-resident LLM proxy: `GET /v1/models`, `POST /v1/chat/completions`, `POST /v1/messages`.
|
|
2083
|
+
|
|
2084
|
+
```mermaid
|
|
2085
|
+
sequenceDiagram
|
|
2086
|
+
participant C as External client<br/>(Honcho, LangChain, curl)
|
|
2087
|
+
participant D as Dashboard :8000/v1/*
|
|
2088
|
+
participant R as InternalRegistry
|
|
2089
|
+
participant P as Upstream provider<br/>(Anthropic, OpenAI, Google…)
|
|
2090
|
+
|
|
2091
|
+
C->>D: Authorization: Bearer pi-proxy-*
|
|
2092
|
+
D->>D: Auth gate: verify key, scope, backoff
|
|
2093
|
+
D->>R: getAvailable() / find(provider, model)
|
|
2094
|
+
R->>R: auth.json + providers.json + models.json
|
|
2095
|
+
D->>R: getApiKeyAndHeaders(model)
|
|
2096
|
+
R->>D: { apiKey, headers }
|
|
2097
|
+
D->>P: streamSimple(model, context, opts)
|
|
2098
|
+
P-->>D: SSE stream
|
|
2099
|
+
D-->>C: SSE stream (OpenAI or Anthropic shape)
|
|
2100
|
+
```
|
|
2101
|
+
|
|
2102
|
+
### API-key auth data flow
|
|
2103
|
+
|
|
2104
|
+
1. Client sends `Authorization: Bearer pi-proxy-<48-char-base64url>`.
|
|
2105
|
+
2. Auth gate (`model-proxy/auth-gate.ts`) prefix-checks `pi-proxy-`, looks up `sha256(token)` in `config.json#modelProxy.apiKeys[]`.
|
|
2106
|
+
3. On hit: checks `revokedAt`, `expiresAt`, scope vs. route path.
|
|
2107
|
+
4. On success: attaches `request.proxyApiKeyId`, resets per-IP backoff, debounced `lastUsedAt` write.
|
|
2108
|
+
|
|
2109
|
+
### Refresh trigger map
|
|
2110
|
+
|
|
2111
|
+
| Trigger | Site |
|
|
2112
|
+
|---|---|
|
|
2113
|
+
| `PUT /api/providers` | `routes/provider-routes.ts` → `refreshModelRegistry()` |
|
|
2114
|
+
| OAuth callback completes | `routes/provider-auth-routes.ts` → `refreshModelRegistry()` |
|
|
2115
|
+
| Config save | `config-api.ts#writeConfigPartial` → `refreshModelRegistry()` |
|
|
2116
|
+
| Bridge `credentials_updated` | `event-wiring.ts` → `refreshModelRegistry()` |
|
|
2117
|
+
| `POST /api/model-proxy/refresh` | manual trigger (JWT-gated) |
|
|
2118
|
+
|
|
2119
|
+
### auth.json write contract
|
|
2120
|
+
|
|
2121
|
+
Two writer processes for `~/.pi/agent/auth.json`:
|
|
2122
|
+
|
|
2123
|
+
- **Dashboard**: `provider-auth-storage.ts#writeCredential` (mkdir-based lock). Used by OAuth-flow completion routes AND `InternalAuthStorage` OAuth-refresh-on-expiry.
|
|
2124
|
+
- **Pi sessions**: `pi-coding-agent`'s `AuthStorage` (proper-lockfile). Runs in each connected pi session.
|
|
2125
|
+
|
|
2126
|
+
Last-writer-wins on overlapping provider keys; non-overlapping providers preserved by merge. Acceptable — both writers re-read before writing; churn only occurs during concurrent OAuth refreshes (rare in practice).
|
|
2127
|
+
|
|
2128
|
+
See change: `add-dashboard-model-proxy`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blackbelt-technology/pi-agent-dashboard",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Web dashboard for monitoring and interacting with pi agent sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"main": "packages/server/src/cli.ts",
|
|
21
21
|
"bin": {
|
|
22
|
-
"pi-dashboard": "packages/server/
|
|
22
|
+
"pi-dashboard": "packages/server/bin/pi-dashboard.mjs"
|
|
23
23
|
},
|
|
24
24
|
"pi": {
|
|
25
25
|
"extensions": [
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"packages/server/src/",
|
|
34
|
+
"packages/server/scripts/",
|
|
34
35
|
"packages/server/package.json",
|
|
35
36
|
"packages/server/tsconfig.json",
|
|
36
37
|
"packages/shared/src/",
|
|
@@ -73,45 +74,44 @@
|
|
|
73
74
|
"node": ">=22.12.0 <25"
|
|
74
75
|
},
|
|
75
76
|
"dependencies": {
|
|
76
|
-
"@blackbelt-technology/pi-dashboard-extension": "^0.5.
|
|
77
|
-
"@blackbelt-technology/pi-dashboard-server": "^0.5.
|
|
78
|
-
"@blackbelt-technology/pi-dashboard-web": "^0.5.
|
|
77
|
+
"@blackbelt-technology/pi-dashboard-extension": "^0.5.2",
|
|
78
|
+
"@blackbelt-technology/pi-dashboard-server": "^0.5.2",
|
|
79
|
+
"@blackbelt-technology/pi-dashboard-web": "^0.5.2"
|
|
79
80
|
},
|
|
80
81
|
"optionalDependencies": {
|
|
81
82
|
"appdmg": "^0.6.6"
|
|
82
83
|
},
|
|
83
84
|
"devDependencies": {
|
|
84
85
|
"jsdom": "^29.0.2",
|
|
85
|
-
"tsx": "^4.21.0",
|
|
86
86
|
"typescript": "^5.7.0",
|
|
87
87
|
"vitest": "^4.0.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
+
"@earendil-works/pi-ai": "*",
|
|
91
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
92
|
+
"@earendil-works/pi-tui": "*",
|
|
90
93
|
"@mariozechner/pi-ai": "*",
|
|
91
94
|
"@mariozechner/pi-coding-agent": "*",
|
|
92
95
|
"@mariozechner/pi-tui": "*",
|
|
93
|
-
"@oh-my-pi/pi-ai": "*",
|
|
94
|
-
"@oh-my-pi/pi-coding-agent": "*",
|
|
95
|
-
"@oh-my-pi/pi-tui": "*",
|
|
96
96
|
"typebox": "*"
|
|
97
97
|
},
|
|
98
98
|
"peerDependenciesMeta": {
|
|
99
|
-
"@
|
|
99
|
+
"@earendil-works/pi-coding-agent": {
|
|
100
100
|
"optional": true
|
|
101
101
|
},
|
|
102
|
-
"@
|
|
102
|
+
"@earendil-works/pi-ai": {
|
|
103
103
|
"optional": true
|
|
104
104
|
},
|
|
105
|
-
"@
|
|
105
|
+
"@earendil-works/pi-tui": {
|
|
106
106
|
"optional": true
|
|
107
107
|
},
|
|
108
|
-
"@
|
|
108
|
+
"@mariozechner/pi-coding-agent": {
|
|
109
109
|
"optional": true
|
|
110
110
|
},
|
|
111
|
-
"@
|
|
111
|
+
"@mariozechner/pi-ai": {
|
|
112
112
|
"optional": true
|
|
113
113
|
},
|
|
114
|
-
"@
|
|
114
|
+
"@mariozechner/pi-tui": {
|
|
115
115
|
"optional": true
|
|
116
116
|
},
|
|
117
117
|
"typebox": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blackbelt-technology/pi-dashboard-extension",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Pi bridge extension for pi-dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -24,15 +24,23 @@
|
|
|
24
24
|
".pi/skills/pi-dashboard/"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@blackbelt-technology/pi-dashboard-shared": "^0.5.
|
|
27
|
+
"@blackbelt-technology/pi-dashboard-shared": "^0.5.2",
|
|
28
28
|
"ws": "^8.18.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
32
|
+
"@earendil-works/pi-tui": "*",
|
|
31
33
|
"@mariozechner/pi-coding-agent": "*",
|
|
32
34
|
"@mariozechner/pi-tui": "*",
|
|
33
35
|
"typebox": "*"
|
|
34
36
|
},
|
|
35
37
|
"peerDependenciesMeta": {
|
|
38
|
+
"@earendil-works/pi-coding-agent": {
|
|
39
|
+
"optional": true
|
|
40
|
+
},
|
|
41
|
+
"@earendil-works/pi-tui": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
36
44
|
"@mariozechner/pi-coding-agent": {
|
|
37
45
|
"optional": true
|
|
38
46
|
},
|
|
@@ -41,7 +49,7 @@
|
|
|
41
49
|
}
|
|
42
50
|
},
|
|
43
51
|
"devDependencies": {
|
|
44
|
-
"@
|
|
52
|
+
"@earendil-works/pi-tui": "*",
|
|
45
53
|
"@types/ws": "^8.18.1",
|
|
46
54
|
"typebox": "^1.1.33"
|
|
47
55
|
}
|