@blackbelt-technology/pi-agent-dashboard 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +19 -30
- package/README.md +69 -1
- package/docs/architecture.md +89 -165
- package/package.json +10 -7
- package/packages/extension/package.json +2 -2
- package/packages/extension/src/__tests__/bridge-default-model-gate.test.ts +47 -0
- package/packages/extension/src/__tests__/bridge-followup-chat-order.test.ts +215 -0
- package/packages/extension/src/__tests__/bridge-followup-multi-entry.test.ts +202 -0
- package/packages/extension/src/__tests__/bridge-queue-update-forward.test.ts +77 -0
- package/packages/extension/src/__tests__/bridge-retry-ordering.test.ts +148 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-drain.test.ts +221 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-gate.test.ts +299 -0
- package/packages/extension/src/__tests__/bridge-shutdown-reset.test.ts +238 -0
- package/packages/extension/src/__tests__/bridge-slash-command-routing.test.ts +127 -31
- package/packages/extension/src/__tests__/command-handler.test.ts +105 -3
- package/packages/extension/src/__tests__/fixtures/usage-limit-error-strings.ts +127 -0
- package/packages/extension/src/__tests__/source-detector.test.ts +15 -0
- package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +12 -0
- package/packages/extension/src/bridge-default-model-gate.ts +32 -0
- package/packages/extension/src/bridge.ts +299 -20
- package/packages/extension/src/command-handler.ts +53 -7
- package/packages/extension/src/dashboard-default-adapter.ts +5 -0
- package/packages/extension/src/prompt-bus.ts +15 -0
- package/packages/extension/src/slash-dispatch.ts +30 -15
- package/packages/extension/src/source-detector.ts +13 -5
- package/packages/extension/src/usage-limit-orderer.ts +18 -1
- package/packages/server/bin/pi-dashboard.mjs +62 -14
- package/packages/server/package.json +9 -5
- package/packages/server/src/__tests__/browser-gateway-register-handler.test.ts +69 -0
- package/packages/server/src/__tests__/cli-env-no-clobber.test.ts +46 -0
- package/packages/server/src/__tests__/cli-no-bootstrap-references.test.ts +69 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +9 -10
- package/packages/server/src/__tests__/cli-version.test.ts +151 -0
- package/packages/server/src/__tests__/directory-service-openspec-enabled.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service.test.ts +9 -0
- package/packages/server/src/__tests__/doctor-route.test.ts +53 -0
- package/packages/server/src/__tests__/event-wiring-queue-state.test.ts +156 -0
- package/packages/server/src/__tests__/event-wiring-resume-clear.test.ts +105 -0
- package/packages/server/src/__tests__/health-shape.test.ts +35 -12
- package/packages/server/src/__tests__/installed-package-enricher.test.ts +12 -12
- package/packages/server/src/__tests__/is-activity-event.test.ts +4 -7
- package/packages/server/src/__tests__/package-routes.test.ts +6 -2
- package/packages/server/src/__tests__/pi-changelog-routes.test.ts +10 -13
- package/packages/server/src/__tests__/pi-core-checker.test.ts +2 -2
- package/packages/server/src/__tests__/pi-version-skew.test.ts +3 -2
- package/packages/server/src/__tests__/plugin-activation-routes.test.ts +267 -0
- package/packages/server/src/__tests__/plugin-intent-cache.test.ts +75 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +196 -0
- package/packages/server/src/__tests__/reattach-placement.test.ts +9 -0
- package/packages/server/src/__tests__/recommended-routes.test.ts +2 -2
- package/packages/server/src/__tests__/recovery-server.test.ts +203 -0
- package/packages/server/src/__tests__/session-action-handler-clear-queue.test.ts +153 -0
- package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +43 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +9 -0
- package/packages/server/src/__tests__/session-order-reboot.test.ts +9 -0
- package/packages/server/src/__tests__/session-ordering-integration.test.ts +9 -0
- package/packages/server/src/browser-gateway.ts +83 -5
- package/packages/server/src/browser-handlers/directory-handler.ts +69 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +89 -0
- package/packages/server/src/browser-handlers/subscription-handler.ts +23 -0
- package/packages/server/src/changelog-parser.ts +1 -1
- package/packages/server/src/cli.ts +68 -250
- package/packages/server/src/event-status-extraction.ts +14 -62
- package/packages/server/src/event-wiring.ts +23 -10
- package/packages/server/src/memory-session-manager.ts +4 -0
- package/packages/server/src/pi-core-checker.ts +1 -1
- package/packages/server/src/pi-dev-version-check.ts +1 -1
- package/packages/server/src/pi-version-skew.ts +24 -46
- package/packages/server/src/plugin-intent-cache.ts +67 -0
- package/packages/server/src/preferences-store.ts +199 -13
- package/packages/server/src/recovery-server.ts +366 -0
- package/packages/server/src/routes/__tests__/manifest-route.test.ts +138 -0
- package/packages/server/src/routes/doctor-routes.ts +26 -21
- package/packages/server/src/routes/manifest-route.ts +162 -0
- package/packages/server/src/routes/openspec-routes.ts +4 -25
- package/packages/server/src/routes/pi-changelog-routes.ts +5 -24
- package/packages/server/src/routes/pi-core-routes.ts +3 -23
- package/packages/server/src/routes/plugin-activation-routes.ts +193 -0
- package/packages/server/src/routes/recommended-routes.ts +21 -0
- package/packages/server/src/routes/system-routes.ts +73 -11
- package/packages/server/src/server.ts +105 -307
- package/packages/server/src/session-api.ts +5 -63
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/binary-lookup-resolveJiti.test.ts +28 -0
- package/packages/shared/src/__tests__/binary-lookup-spawn-env.test.ts +61 -0
- package/packages/shared/src/__tests__/binary-lookup.test.ts +16 -0
- package/packages/shared/src/__tests__/bridge-register.test.ts +67 -0
- package/packages/shared/src/__tests__/ci-electron-no-side-effects.test.ts +129 -0
- package/packages/shared/src/__tests__/config.test.ts +40 -0
- package/packages/shared/src/__tests__/dashboard-paths.test.ts +81 -0
- package/packages/shared/src/__tests__/ensure-windows-path.test.ts +112 -0
- package/packages/shared/src/__tests__/intent-types.test.ts +120 -0
- package/packages/shared/src/__tests__/jiti-packages-parity.test.ts +85 -0
- package/packages/shared/src/__tests__/legacy-managed-dir.test.ts +59 -0
- package/packages/shared/src/__tests__/no-direct-child-process.test.ts +12 -0
- package/packages/shared/src/__tests__/no-electron-execpath-spawn.test.ts +149 -0
- package/packages/shared/src/__tests__/no-flow-command-route-claims.test.ts +71 -0
- package/packages/shared/src/__tests__/no-flow-references-in-shell.test.ts +221 -0
- package/packages/shared/src/__tests__/no-managed-dir-reference.test.ts +134 -0
- package/packages/shared/src/__tests__/no-pi-dashboard-version-jiti-gate.test.ts +41 -0
- package/packages/shared/src/__tests__/no-primitive-direct-import.test.ts +235 -0
- package/packages/shared/src/__tests__/no-server-imports-in-resolver.test.ts +53 -0
- package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +54 -101
- package/packages/shared/src/__tests__/node-spawn.test.ts +29 -13
- package/packages/shared/src/__tests__/pi-package-resolver.test.ts +300 -0
- package/packages/shared/src/__tests__/plugin-activation-contracts.test.ts +74 -0
- package/packages/shared/src/__tests__/plugin-bridge-classify-source.test.ts +73 -0
- package/packages/shared/src/__tests__/plugin-bridge-register-extended.test.ts +17 -5
- package/packages/shared/src/__tests__/plugin-bridge-register-packages.test.ts +233 -0
- package/packages/shared/src/__tests__/plugin-bridge-register.test.ts +19 -9
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +154 -15
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +28 -10
- package/packages/shared/src/__tests__/resolver-parity-with-scanner.test.ts +76 -0
- package/packages/shared/src/__tests__/server-identity.test.ts +127 -0
- package/packages/shared/src/__tests__/server-launcher.test.ts +35 -0
- package/packages/shared/src/__tests__/source-matching.test.ts +5 -5
- package/packages/shared/src/__tests__/sync-versions-spec.test.ts +76 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +50 -2
- package/packages/shared/src/bridge-register.ts +35 -2
- package/packages/shared/src/browser-protocol.ts +176 -2
- package/packages/shared/src/config.ts +12 -0
- package/packages/shared/src/dashboard-paths.ts +69 -0
- package/packages/shared/src/dashboard-plugin/index.ts +2 -0
- package/packages/shared/src/dashboard-plugin/intent-types.ts +93 -0
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +55 -1
- package/packages/shared/src/dashboard-plugin/plugin-status.ts +82 -0
- package/packages/shared/src/dashboard-plugin/slot-props.ts +11 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +16 -2
- package/packages/shared/src/dashboard-plugin/ui-primitives.ts +287 -0
- package/packages/shared/src/dashboard-starter.ts +22 -0
- package/packages/shared/src/doctor-core.ts +49 -27
- package/packages/shared/src/launch-source-types.ts +9 -9
- package/packages/shared/src/legacy-managed-dir.ts +97 -0
- package/packages/shared/src/mdns-discovery.ts +4 -1
- package/packages/shared/src/pi-package-resolver.ts +388 -0
- package/packages/shared/src/platform/binary-lookup.ts +27 -3
- package/packages/shared/src/platform/ensure-windows-path.ts +95 -0
- package/packages/shared/src/platform/exec.ts +22 -0
- package/packages/shared/src/platform/node-spawn.ts +42 -41
- package/packages/shared/src/plugin-bridge-register.ts +275 -18
- package/packages/shared/src/protocol.ts +94 -2
- package/packages/shared/src/recommended-extensions.ts +34 -10
- package/packages/shared/src/server-identity.ts +74 -5
- package/packages/shared/src/server-launcher.ts +20 -0
- package/packages/shared/src/source-matching.ts +1 -1
- package/packages/shared/src/tool-registry/__tests__/node-script-toargv-fallback.test.ts +84 -0
- package/packages/shared/src/tool-registry/definitions.ts +91 -7
- package/packages/shared/src/types.ts +12 -8
- package/scripts/maybe-patch-package.cjs +44 -0
- package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +0 -263
- package/packages/server/src/__tests__/bootstrap-queue.test.ts +0 -120
- package/packages/server/src/__tests__/bootstrap-routes.test.ts +0 -125
- package/packages/server/src/__tests__/bootstrap-state.test.ts +0 -119
- package/packages/server/src/__tests__/cli-bootstrap.test.ts +0 -36
- package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +0 -55
- package/packages/server/src/__tests__/legacy-pi-cleanup.test.ts +0 -149
- package/packages/server/src/__tests__/post-install-openspec-refresh.test.ts +0 -180
- package/packages/server/src/__tests__/post-install-rescan.test.ts +0 -134
- package/packages/server/src/__tests__/system-routes-reextract.test.ts +0 -91
- package/packages/server/src/bootstrap-install-from-list.ts +0 -232
- package/packages/server/src/bootstrap-queue.ts +0 -130
- package/packages/server/src/bootstrap-state.ts +0 -159
- package/packages/server/src/legacy-pi-cleanup.ts +0 -151
- package/packages/server/src/routes/bootstrap-routes.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/README.md +0 -133
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +0 -378
- package/packages/shared/src/__tests__/bootstrap/assertions.ts +0 -136
- package/packages/shared/src/__tests__/bootstrap/cube.test.ts +0 -47
- package/packages/shared/src/__tests__/bootstrap/cube.ts +0 -66
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +0 -84
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +0 -90
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +0 -20
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +0 -62
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +0 -49
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +0 -12
- package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +0 -156
- package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +0 -157
- package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +0 -102
- package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +0 -76
- package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +0 -94
- package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +0 -87
- package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +0 -143
- package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +0 -64
- package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +0 -77
- package/packages/shared/src/__tests__/bootstrap/families/index.ts +0 -19
- package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +0 -61
- package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +0 -50
- package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +0 -272
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +0 -58
- package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +0 -84
- package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +0 -9
- package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +0 -85
- package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +0 -122
- package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +0 -36
- package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +0 -39
- package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +0 -220
- package/packages/shared/src/__tests__/bootstrap/harness.ts +0 -413
- package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/scenarios.ts +0 -132
- package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +0 -72
- package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +0 -68
- package/packages/shared/src/__tests__/install-managed-node.test.ts +0 -192
- package/packages/shared/src/__tests__/installable-list.test.ts +0 -130
- package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +0 -52
- package/packages/shared/src/bootstrap-install.ts +0 -406
- package/packages/shared/src/installable-list.ts +0 -152
- package/packages/shared/src/launch-source-flag.ts +0 -14
|
@@ -61,6 +61,16 @@ export interface RecommendedExtension {
|
|
|
61
61
|
* start working.
|
|
62
62
|
*/
|
|
63
63
|
autowired?: boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Companion dashboard plugin id, if this extension is paired with one
|
|
67
|
+
* (e.g. `pi-memory-honcho` extension <-> `honcho` dashboard plugin).
|
|
68
|
+
* The recommended-extensions enricher carries this through alongside a
|
|
69
|
+
* computed `dashboardPluginInstalled: boolean` so the install browser
|
|
70
|
+
* can render a "+plugin: <id>" badge.
|
|
71
|
+
* See change: add-plugin-activation-ui (Layer 1.5).
|
|
72
|
+
*/
|
|
73
|
+
dashboardPlugin?: string;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
/** Enriched manifest entry returned by GET /api/packages/recommended. */
|
|
@@ -77,6 +87,12 @@ export interface EnrichedRecommendedExtension extends RecommendedExtension {
|
|
|
77
87
|
activeInPi: boolean;
|
|
78
88
|
/** True iff a newer version is available upstream. */
|
|
79
89
|
updateAvailable: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* True iff the entry declares a `dashboardPlugin` and the named plugin is
|
|
92
|
+
* present in the dashboard's plugin status store.
|
|
93
|
+
* See change: add-plugin-activation-ui.
|
|
94
|
+
*/
|
|
95
|
+
dashboardPluginInstalled?: boolean;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
export const RECOMMENDED_EXTENSIONS: readonly RecommendedExtension[] = [
|
|
@@ -96,21 +112,24 @@ export const RECOMMENDED_EXTENSIONS: readonly RecommendedExtension[] = [
|
|
|
96
112
|
autowired: true,
|
|
97
113
|
},
|
|
98
114
|
{
|
|
99
|
-
id: "
|
|
100
|
-
source: "
|
|
101
|
-
displayName: "
|
|
115
|
+
id: "pi-dashboard-subagents",
|
|
116
|
+
source: "https://github.com/BlackBeltTechnology/pi-dashboard-subagents.git",
|
|
117
|
+
displayName: "pi-dashboard-subagents",
|
|
102
118
|
fallbackDescription:
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
|
|
119
|
+
"Foreground in-memory subagents for pi with a streamed timeline " +
|
|
120
|
+
"(every tool call, reasoning step, and assistant text). Pairs with " +
|
|
121
|
+
"the dashboard's subagent-inspector plugin for inline-expand + popout " +
|
|
122
|
+
"card UI. Producer of the Agent tool; no background spawning.",
|
|
123
|
+
status: "optional",
|
|
107
124
|
unlocks: [
|
|
108
125
|
"Agent tool card UI",
|
|
109
|
-
"Subagent
|
|
110
|
-
"
|
|
126
|
+
"Subagent inspector (inline expand + popout)",
|
|
127
|
+
"agent-md path display",
|
|
111
128
|
],
|
|
112
|
-
toolsRegistered: ["Agent"
|
|
129
|
+
toolsRegistered: ["Agent"],
|
|
113
130
|
autowired: true,
|
|
131
|
+
// Companion dashboard plugin id. See change: add-subagent-inspector.
|
|
132
|
+
dashboardPlugin: "subagents",
|
|
114
133
|
},
|
|
115
134
|
{
|
|
116
135
|
id: "pi-flows",
|
|
@@ -182,6 +201,8 @@ export const RECOMMENDED_EXTENSIONS: readonly RecommendedExtension[] = [
|
|
|
182
201
|
],
|
|
183
202
|
toolsRegistered: ["honcho_search", "honcho_context", "honcho_profile"],
|
|
184
203
|
autowired: true,
|
|
204
|
+
// Companion dashboard plugin id. See change: add-plugin-activation-ui.
|
|
205
|
+
dashboardPlugin: "honcho",
|
|
185
206
|
},
|
|
186
207
|
];
|
|
187
208
|
|
|
@@ -198,6 +219,9 @@ export const RECOMMENDED_EXTENSIONS: readonly RecommendedExtension[] = [
|
|
|
198
219
|
*/
|
|
199
220
|
export const BUNDLED_EXTENSION_IDS: readonly string[] = [
|
|
200
221
|
"pi-anthropic-messages",
|
|
222
|
+
// Foreground subagent producer. Git source + MIT license = bundle eligible.
|
|
223
|
+
// See change: add-subagent-inspector §13.
|
|
224
|
+
"pi-dashboard-subagents",
|
|
201
225
|
// "pi-flows" is intentionally NOT bundled until the upstream repo declares
|
|
202
226
|
// an SPDX-conformant license (`LICENSE` file or `package.json#license`).
|
|
203
227
|
// The bundle-recommended-extensions.mjs license allowlist enforcement
|
|
@@ -1,26 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Server identity verification via HTTP health check.
|
|
3
3
|
* Replaces bare TCP port probes with identity-verified dashboard detection.
|
|
4
|
+
*
|
|
5
|
+
* Retry semantics (cherry-pick 2 of harvest-bootstrap-survivor-fixes):
|
|
6
|
+
* the pre-wizard probe in Electron's main process fires while a *previous*
|
|
7
|
+
* server instance may still be mid-bootstrap (jiti TypeScript transpile +
|
|
8
|
+
* cold-cache extraction can block the event loop for 5–15 s). The default
|
|
9
|
+
* 2 s timeout + 1 attempt produces false negatives in that window.
|
|
10
|
+
* Callers can opt into a bounded retry loop via `opts.retries` /
|
|
11
|
+
* `opts.timeoutMs` / `opts.retryDelayMs`. Defaults preserve legacy
|
|
12
|
+
* behaviour (single attempt, 2 s timeout) so existing call sites are
|
|
13
|
+
* unaffected.
|
|
4
14
|
*/
|
|
5
15
|
|
|
6
|
-
const
|
|
16
|
+
const DEFAULT_TIMEOUT_MS = 2000;
|
|
17
|
+
const DEFAULT_RETRIES = 0;
|
|
18
|
+
const DEFAULT_RETRY_DELAY_MS = 500;
|
|
7
19
|
|
|
8
20
|
export interface DashboardStatus {
|
|
9
21
|
/** Whether the dashboard server is running on this port */
|
|
10
22
|
running: boolean;
|
|
11
23
|
/** PID of the running server (if detected) */
|
|
12
24
|
pid?: number;
|
|
25
|
+
/** Server version from /api/health (when detected) */
|
|
26
|
+
version?: string;
|
|
13
27
|
/** Port is occupied by a non-dashboard service */
|
|
14
28
|
portConflict?: boolean;
|
|
15
29
|
}
|
|
16
30
|
|
|
31
|
+
export interface DashboardCheckOpts {
|
|
32
|
+
/**
|
|
33
|
+
* Per-attempt fetch timeout. Default 2000 ms — preserves legacy single-shot behaviour.
|
|
34
|
+
* Bootstrap-aware callers should pass ~8000 ms to absorb event-loop hiccups
|
|
35
|
+
* during cold-cache install.
|
|
36
|
+
*/
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Number of additional attempts after the first. Default 0 (no retries).
|
|
40
|
+
* On `AbortError` (timeout) or 5xx the loop sleeps `retryDelayMs` and
|
|
41
|
+
* retries. `portConflict: true` (HTTP 200 with foreign JSON shape)
|
|
42
|
+
* short-circuits — that's a deterministic conflict, not a transient
|
|
43
|
+
* fault, and retrying would mask a real port collision.
|
|
44
|
+
* ECONNREFUSED is *not* retried (no process to talk to).
|
|
45
|
+
*/
|
|
46
|
+
retries?: number;
|
|
47
|
+
/** Sleep between retries. Default 500 ms. */
|
|
48
|
+
retryDelayMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Test seam: replace `setTimeout`-based sleep. Receives the configured
|
|
51
|
+
* `retryDelayMs`. Must return a promise that resolves after the sleep.
|
|
52
|
+
*/
|
|
53
|
+
_sleep?: (ms: number) => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
17
56
|
/**
|
|
18
57
|
* Check if a dashboard server is running on the given port by hitting GET /api/health.
|
|
19
58
|
* Returns identity-verified status instead of just "port is open".
|
|
20
59
|
*/
|
|
21
|
-
export async function isDashboardRunning(
|
|
60
|
+
export async function isDashboardRunning(
|
|
61
|
+
port: number,
|
|
62
|
+
host: string = "localhost",
|
|
63
|
+
opts?: DashboardCheckOpts,
|
|
64
|
+
): Promise<DashboardStatus> {
|
|
65
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
66
|
+
const retries = opts?.retries ?? DEFAULT_RETRIES;
|
|
67
|
+
const retryDelayMs = opts?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
68
|
+
const sleep = opts?._sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)));
|
|
69
|
+
|
|
70
|
+
const attempts = retries + 1;
|
|
71
|
+
let lastResult: DashboardStatus = { running: false };
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < attempts; i++) {
|
|
74
|
+
const result = await probeOnce(port, host, timeoutMs);
|
|
75
|
+
// Success — return immediately.
|
|
76
|
+
if (result.running) return result;
|
|
77
|
+
// Deterministic conflict — short-circuit (retrying would mask it).
|
|
78
|
+
if (result.portConflict) return result;
|
|
79
|
+
lastResult = result;
|
|
80
|
+
if (i < attempts - 1) await sleep(retryDelayMs);
|
|
81
|
+
}
|
|
82
|
+
return lastResult;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function probeOnce(
|
|
86
|
+
port: number,
|
|
87
|
+
host: string,
|
|
88
|
+
timeoutMs: number,
|
|
89
|
+
): Promise<DashboardStatus> {
|
|
22
90
|
const controller = new AbortController();
|
|
23
|
-
const timer = setTimeout(() => controller.abort(),
|
|
91
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
24
92
|
|
|
25
93
|
try {
|
|
26
94
|
const res = await fetch(`http://${host}:${port}/api/health`, {
|
|
@@ -32,9 +100,10 @@ export async function isDashboardRunning(port: number, host = "localhost"): Prom
|
|
|
32
100
|
return { running: false, portConflict: true };
|
|
33
101
|
}
|
|
34
102
|
|
|
35
|
-
const data = await res.json() as Record<string, unknown>;
|
|
103
|
+
const data = (await res.json()) as Record<string, unknown>;
|
|
36
104
|
if (data && data.ok === true && typeof data.pid === "number") {
|
|
37
|
-
|
|
105
|
+
const version = typeof data.version === "string" ? data.version : undefined;
|
|
106
|
+
return { running: true, pid: data.pid, version };
|
|
38
107
|
}
|
|
39
108
|
|
|
40
109
|
// HTTP 200 but not our format — another service
|
|
@@ -136,6 +136,19 @@ export interface LaunchOpts {
|
|
|
136
136
|
_now?: () => number;
|
|
137
137
|
/** Override the sleep function used between polls. */
|
|
138
138
|
_sleep?: (ms: number) => Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Called once when the spawned child exits (any exit — crash or graceful).
|
|
141
|
+
* Attached via `child.on("exit", …)` before the readiness loop so the
|
|
142
|
+
* handler fires even if the child exits during the health-wait window.
|
|
143
|
+
* No-op when omitted — existing callers are unaffected.
|
|
144
|
+
*
|
|
145
|
+
* Callers that need to distinguish crash from graceful shutdown should
|
|
146
|
+
* maintain their own flag (see `setGracefulShutdownInProgress` in
|
|
147
|
+
* `electron/server-lifecycle.ts`) and consult it inside the callback.
|
|
148
|
+
*
|
|
149
|
+
* See change: harvest-bootstrap-survivor-fixes (cherry-pick 6a).
|
|
150
|
+
*/
|
|
151
|
+
onChildExit?: (code: number | null, signal: NodeJS.Signals | null) => void;
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
export interface LaunchResult {
|
|
@@ -242,6 +255,13 @@ export async function launchDashboardServer(opts: LaunchOpts): Promise<LaunchRes
|
|
|
242
255
|
|
|
243
256
|
try { child.unref(); } catch { /* ignore */ }
|
|
244
257
|
|
|
258
|
+
// Attach caller's exit handler before the readiness loop so it fires
|
|
259
|
+
// even for exits that happen during the health-wait window.
|
|
260
|
+
// See change: harvest-bootstrap-survivor-fixes (cherry-pick 6a).
|
|
261
|
+
if (opts.onChildExit) {
|
|
262
|
+
child.once("exit", opts.onChildExit);
|
|
263
|
+
}
|
|
264
|
+
|
|
245
265
|
if (!child.pid) {
|
|
246
266
|
throw new EarlyExitError(child.exitCode ?? null, child.signalCode ?? null);
|
|
247
267
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Input sources take one of these forms:
|
|
10
10
|
//
|
|
11
11
|
// npm:<name>[@<version>]
|
|
12
|
-
// e.g. "npm:pi-web-access", "npm:@
|
|
12
|
+
// e.g. "npm:pi-web-access", "npm:@scope/example-pkg@0.5.2"
|
|
13
13
|
//
|
|
14
14
|
// git@<host>:<owner>/<repo>[.git]
|
|
15
15
|
// e.g. "git@github.com:BlackBeltTechnology/pi-flows.git"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression: `nodeScriptToArgv` MUST always prepend a Node interpreter
|
|
3
|
+
* on Windows + `.js` paths.
|
|
4
|
+
*
|
|
5
|
+
* Live repro on Windows 11: when the registry's `node` strategy chain
|
|
6
|
+
* failed (no managed runtime, no PATH hit), `nodeScriptToArgv`
|
|
7
|
+
* previously returned `[cli.js]` and `spawn(cli.js)` crashed with
|
|
8
|
+
* `EFTYPE`. The fix falls back to `process.execPath` — the dashboard
|
|
9
|
+
* server's own Node — which is by definition spawn-able.
|
|
10
|
+
*
|
|
11
|
+
* See change: fix-windows-standalone-spawn.
|
|
12
|
+
*/
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { describe, expect, it } from "vitest";
|
|
16
|
+
import {
|
|
17
|
+
ToolRegistry,
|
|
18
|
+
registerDefaultTools,
|
|
19
|
+
OverridesStore,
|
|
20
|
+
} from "../index.js";
|
|
21
|
+
|
|
22
|
+
function freshRegistry(opts: {
|
|
23
|
+
platform: NodeJS.Platform;
|
|
24
|
+
exists?: (p: string) => boolean;
|
|
25
|
+
which?: (name: string) => string | null;
|
|
26
|
+
npmRootGlobal?: () => string;
|
|
27
|
+
overrides?: Record<string, string>;
|
|
28
|
+
}) {
|
|
29
|
+
const store = new OverridesStore({
|
|
30
|
+
filePath: path.join(os.tmpdir(), `node-script-toargv-${Math.random()}.json`),
|
|
31
|
+
warn: () => {},
|
|
32
|
+
});
|
|
33
|
+
for (const [k, v] of Object.entries(opts.overrides ?? {})) store.set(k, v);
|
|
34
|
+
|
|
35
|
+
const r = new ToolRegistry({
|
|
36
|
+
overrides: store,
|
|
37
|
+
platform: opts.platform,
|
|
38
|
+
});
|
|
39
|
+
registerDefaultTools(r, {
|
|
40
|
+
exists: opts.exists ?? (() => false),
|
|
41
|
+
which: opts.which ?? (() => null),
|
|
42
|
+
npmRootGlobal: opts.npmRootGlobal ?? (() => ""),
|
|
43
|
+
});
|
|
44
|
+
return r;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("nodeScriptToArgv — Windows fallback (Bug 3)", () => {
|
|
48
|
+
it("falls back to process.execPath when registry.resolve('node') returns ok:false", () => {
|
|
49
|
+
// Locate pi via an explicit override pointing at a fake cli.js, so
|
|
50
|
+
// the pi executor resolves successfully. The `node` chain has no
|
|
51
|
+
// sources (no managed runtime, no PATH hit) so it must fail —
|
|
52
|
+
// forcing nodeScriptToArgv into the process.execPath fallback.
|
|
53
|
+
const fakePiCli = "C:\\Users\\u\\.pi-dashboard\\node_modules\\@earendil-works\\pi-coding-agent\\dist\\cli.js";
|
|
54
|
+
const r = freshRegistry({
|
|
55
|
+
platform: "win32",
|
|
56
|
+
// Only the pi override path exists; node has no candidates.
|
|
57
|
+
exists: (p) => p === fakePiCli,
|
|
58
|
+
overrides: { pi: fakePiCli },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const nodeRes = r.resolve("node");
|
|
62
|
+
expect(nodeRes.ok).toBe(false);
|
|
63
|
+
|
|
64
|
+
const piExec = r.resolveExecutor("pi");
|
|
65
|
+
expect(piExec.ok).toBe(true);
|
|
66
|
+
expect(piExec.path).toBe(fakePiCli);
|
|
67
|
+
expect(piExec.argv).toEqual([process.execPath, fakePiCli]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("uses the registry node.path when registry.resolve('node') succeeds", () => {
|
|
71
|
+
const fakePiCli = "C:\\Users\\u\\.pi-dashboard\\node_modules\\@earendil-works\\pi-coding-agent\\dist\\cli.js";
|
|
72
|
+
const fakeNode = "C:\\Program Files\\nodejs\\node.exe";
|
|
73
|
+
const r = freshRegistry({
|
|
74
|
+
platform: "win32",
|
|
75
|
+
exists: (p) => p === fakePiCli || p === fakeNode,
|
|
76
|
+
overrides: { pi: fakePiCli, node: fakeNode },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const piExec = r.resolveExecutor("pi");
|
|
80
|
+
expect(piExec.ok).toBe(true);
|
|
81
|
+
expect(piExec.argv[0]).toBe(fakeNode);
|
|
82
|
+
expect(piExec.argv[1]).toBe(fakePiCli);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { existsSync } from "node:fs";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
import path from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
14
15
|
import type { ToolDefinition, Source } from "./types.js";
|
|
15
16
|
import type { ToolRegistry } from "./registry.js";
|
|
16
17
|
import {
|
|
@@ -110,15 +111,64 @@ function bareImportPackageDirStrategy(
|
|
|
110
111
|
return {
|
|
111
112
|
name: "bare-import",
|
|
112
113
|
run() {
|
|
113
|
-
const pkgJson =
|
|
114
|
+
const pkgJson =
|
|
115
|
+
resolveModule(`${pkgName}/package.json`, import.meta.url)
|
|
116
|
+
?? findPackageJsonByDirWalk(pkgName, import.meta.url, searchPaths, deps?.exists);
|
|
114
117
|
if (!pkgJson) {
|
|
115
|
-
return { ok: false, reason: `cannot resolve ${pkgName}
|
|
118
|
+
return { ok: false, reason: `cannot resolve ${pkgName} package directory` };
|
|
116
119
|
}
|
|
117
120
|
return { ok: true, path: path.dirname(pkgJson) };
|
|
118
121
|
},
|
|
119
122
|
};
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Helper: walks up from `fromUrl`'s directory looking for
|
|
127
|
+
* `node_modules/<pkgName>/package.json` directly on the filesystem.
|
|
128
|
+
*
|
|
129
|
+
* Exports-map-immune: required because both `@earendil-works/pi-coding-agent`
|
|
130
|
+
* and `@fission-ai/openspec` declare `exports` blocks that omit
|
|
131
|
+
* `./package.json`, so `createRequire(from).resolve("<pkg>/package.json")`
|
|
132
|
+
* returns `ERR_PACKAGE_PATH_NOT_EXPORTED` in modern Node. This walk is
|
|
133
|
+
* a deliberate end-run around the resolver — we already know the file
|
|
134
|
+
* we want and just need its absolute path.
|
|
135
|
+
*
|
|
136
|
+
* Honors the injected `exists` predicate so tests with mocked
|
|
137
|
+
* filesystems stay deterministic; falls back to `existsSync` when
|
|
138
|
+
* none is injected.
|
|
139
|
+
*
|
|
140
|
+
* See change: eliminate-electron-runtime-install (F9 follow-on).
|
|
141
|
+
*/
|
|
142
|
+
function findPackageJsonByDirWalk(
|
|
143
|
+
pkgName: string,
|
|
144
|
+
fromUrl: string,
|
|
145
|
+
searchPaths?: readonly string[],
|
|
146
|
+
exists?: StrategyDeps["exists"],
|
|
147
|
+
): string | null {
|
|
148
|
+
const check = exists ?? existsSync;
|
|
149
|
+
const candidates: string[] = [];
|
|
150
|
+
try {
|
|
151
|
+
candidates.push(path.dirname(fileURLToPath(fromUrl)));
|
|
152
|
+
} catch {
|
|
153
|
+
// fromUrl might not be a file: URL in synthetic test contexts.
|
|
154
|
+
}
|
|
155
|
+
for (const sp of searchPaths ?? []) candidates.push(sp);
|
|
156
|
+
for (const start of candidates) {
|
|
157
|
+
let dir = start;
|
|
158
|
+
// Bound the walk: stop at filesystem root or once dirname is
|
|
159
|
+
// unchanged. Defensive cap at 64 levels covers any plausible
|
|
160
|
+
// workspace nesting without an infinite-loop risk on broken paths.
|
|
161
|
+
for (let i = 0; i < 64; i += 1) {
|
|
162
|
+
const candidate = path.join(dir, "node_modules", pkgName, "package.json");
|
|
163
|
+
if (check(candidate)) return candidate;
|
|
164
|
+
const parent = path.dirname(dir);
|
|
165
|
+
if (parent === dir) break;
|
|
166
|
+
dir = parent;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
122
172
|
/** Module def that returns the package directory (containing package.json). */
|
|
123
173
|
function packageDirModuleDef(
|
|
124
174
|
toolName: string,
|
|
@@ -168,11 +218,24 @@ function packageDirModuleDef(
|
|
|
168
218
|
* registered with this `toArgv` so the spawn becomes
|
|
169
219
|
* `node.exe <script.js>` (pure console-subsystem inherit, no new
|
|
170
220
|
* window ever).
|
|
221
|
+
*
|
|
222
|
+
* Node resolution order on Windows (see change:
|
|
223
|
+
* fix-windows-standalone-spawn):
|
|
224
|
+
* 1. `registry.resolve("node")` when it returns ok with a non-null
|
|
225
|
+
* path (the strategy chain has already validated existence via
|
|
226
|
+
* its injected `exists` dep).
|
|
227
|
+
* 2. `process.execPath` — the dashboard server's own Node — as a
|
|
228
|
+
* guaranteed-working fallback. Live repro: Windows 11 standalone
|
|
229
|
+
* install where the registry chain failed to find node and the
|
|
230
|
+
* spawn argv became `[cli.js]` → `spawn EFTYPE`. Falling back to
|
|
231
|
+
* execPath keeps the spawn argv well-formed because the dashboard
|
|
232
|
+
* server is itself running on a compatible Node.
|
|
171
233
|
*/
|
|
172
234
|
const nodeScriptToArgv: ToolDefinition["toArgv"] = (resolvedPath, { platform, registry }) => {
|
|
173
235
|
if (platform === "win32" && /\.js$/i.test(resolvedPath)) {
|
|
174
236
|
const node = registry.resolve("node");
|
|
175
237
|
if (node.ok && node.path) return [node.path, resolvedPath];
|
|
238
|
+
return [process.execPath, resolvedPath];
|
|
176
239
|
}
|
|
177
240
|
return [resolvedPath];
|
|
178
241
|
};
|
|
@@ -185,7 +248,17 @@ const nodeScriptToArgv: ToolDefinition["toArgv"] = (resolvedPath, { platform, re
|
|
|
185
248
|
* `node.exe` to produce `[node.exe, cli.js]`. Falls back to `pi.cmd`
|
|
186
249
|
* on PATH when the cli.js is nowhere to be found.
|
|
187
250
|
*
|
|
188
|
-
* On Unix, the chain
|
|
251
|
+
* On Unix, the chain first tries `bare-import` so a bundled
|
|
252
|
+
* `<server>/node_modules/@earendil-works/pi-coding-agent/dist/cli.js`
|
|
253
|
+
* wins over a system install. This is load-bearing for the Electron
|
|
254
|
+
* immutable-bundle architecture (see openspec change
|
|
255
|
+
* `eliminate-electron-runtime-install` finding F9). On a clean machine
|
|
256
|
+
* with no system `pi` and no managed `~/.pi-dashboard/node/bin/`,
|
|
257
|
+
* bare-import resolves the bundled cli.js (`#!/usr/bin/env node`
|
|
258
|
+
* shebang, executable) and `nodeScriptToArgv` returns `[cli.js]`
|
|
259
|
+
* directly. Without this strategy, the server falls into
|
|
260
|
+
* `bootstrapInstall(...)` and writes to `~/.pi-dashboard/` — the
|
|
261
|
+
* exact failure mode the immutable-bundle architecture eliminates.
|
|
189
262
|
*/
|
|
190
263
|
function piExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
191
264
|
const piPkgAliases = ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
|
|
@@ -202,6 +275,7 @@ function piExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
|
202
275
|
|
|
203
276
|
const unixStrategies = [
|
|
204
277
|
overrideStrategy("pi", deps),
|
|
278
|
+
...piPkgAliases.map((pkg) => bareImportCliStrategy(pkg, cliEntry, deps)),
|
|
205
279
|
managedBinStrategy("pi", deps),
|
|
206
280
|
whereStrategy("pi", deps),
|
|
207
281
|
];
|
|
@@ -221,7 +295,10 @@ function piExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
|
221
295
|
*
|
|
222
296
|
* On Windows: finds `@fission-ai/openspec/bin/openspec.js` via managed
|
|
223
297
|
* → bare-import → npm-global. `toArgv` wraps with node.exe.
|
|
224
|
-
* On Unix:
|
|
298
|
+
* On Unix: tries bare-import first (bundled
|
|
299
|
+
* `<server>/node_modules/@fission-ai/openspec/bin/openspec.js`), then
|
|
300
|
+
* managed-bin, then PATH. Symmetric with pi; same Electron
|
|
301
|
+
* immutable-bundle rationale (F9).
|
|
225
302
|
*/
|
|
226
303
|
function openspecExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
227
304
|
const pkgName = "@fission-ai/openspec";
|
|
@@ -229,7 +306,7 @@ function openspecExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
|
229
306
|
|
|
230
307
|
const winStrategies = [
|
|
231
308
|
overrideStrategy("openspec", deps),
|
|
232
|
-
bareImportCliStrategy(pkgName, cliEntry),
|
|
309
|
+
bareImportCliStrategy(pkgName, cliEntry, deps),
|
|
233
310
|
managedModuleStrategy(pkgName, cliEntry, deps),
|
|
234
311
|
npmGlobalStrategy(pkgName, cliEntry, deps),
|
|
235
312
|
managedBinStrategy("openspec", deps),
|
|
@@ -238,6 +315,7 @@ function openspecExecutorDef(deps?: StrategyDeps): ToolDefinition {
|
|
|
238
315
|
|
|
239
316
|
const unixStrategies = [
|
|
240
317
|
overrideStrategy("openspec", deps),
|
|
318
|
+
bareImportCliStrategy(pkgName, cliEntry, deps),
|
|
241
319
|
managedBinStrategy("openspec", deps),
|
|
242
320
|
whereStrategy("openspec", deps),
|
|
243
321
|
];
|
|
@@ -330,6 +408,10 @@ function bareImportCliStrategy(
|
|
|
330
408
|
) {
|
|
331
409
|
// Default uses the real module resolver anchored to this file;
|
|
332
410
|
// tests inject a fake via deps.resolveModule.
|
|
411
|
+
// Fallback to a filesystem walk because both pi-coding-agent and
|
|
412
|
+
// openspec declare exports maps that omit ./package.json (modern Node
|
|
413
|
+
// resolver returns ERR_PACKAGE_PATH_NOT_EXPORTED). See change:
|
|
414
|
+
// eliminate-electron-runtime-install (F9 follow-on).
|
|
333
415
|
const resolveModule: NonNullable<StrategyDeps["resolveModule"]> =
|
|
334
416
|
deps?.resolveModule
|
|
335
417
|
?? ((id, from) => {
|
|
@@ -342,9 +424,11 @@ function bareImportCliStrategy(
|
|
|
342
424
|
return {
|
|
343
425
|
name: "bare-import",
|
|
344
426
|
run(): { ok: true; path: string } | { ok: false; reason: string } {
|
|
345
|
-
const pkgJson =
|
|
427
|
+
const pkgJson =
|
|
428
|
+
resolveModule(`${pkgName}/package.json`, import.meta.url)
|
|
429
|
+
?? findPackageJsonByDirWalk(pkgName, import.meta.url, undefined, deps?.exists);
|
|
346
430
|
if (!pkgJson) {
|
|
347
|
-
return { ok: false, reason: `cannot
|
|
431
|
+
return { ok: false, reason: `cannot locate ${pkgName} package directory` };
|
|
348
432
|
}
|
|
349
433
|
const entry = path.join(path.dirname(pkgJson), entryRelative);
|
|
350
434
|
return { ok: true, path: entry };
|
|
@@ -89,14 +89,6 @@ export interface DashboardSession {
|
|
|
89
89
|
firstMessage?: string;
|
|
90
90
|
dataUnavailable?: boolean;
|
|
91
91
|
resuming?: boolean;
|
|
92
|
-
/** Active flow name (set during flow execution) */
|
|
93
|
-
activeFlowName?: string;
|
|
94
|
-
/** Number of completed agents in the active flow */
|
|
95
|
-
flowAgentsDone?: number;
|
|
96
|
-
/** Total number of agents in the active flow */
|
|
97
|
-
flowAgentsTotal?: number;
|
|
98
|
-
/** Flow execution status */
|
|
99
|
-
flowStatus?: FlowStatus;
|
|
100
92
|
/** Last known bridge entry count (for skip-wipe comparison on reconnect) */
|
|
101
93
|
lastEntryCount?: number;
|
|
102
94
|
/** OS process ID of the pi agent — used for force-kill escalation */
|
|
@@ -134,6 +126,14 @@ export interface DashboardSession {
|
|
|
134
126
|
* See change: chat-markdown-local-images-and-math.
|
|
135
127
|
*/
|
|
136
128
|
assets?: Record<string, { data: string; mimeType: string }>;
|
|
129
|
+
/**
|
|
130
|
+
* Mirror of pi's native steering + follow-up queues for this session.
|
|
131
|
+
* Populated from pi's `queue_update` event, forwarded by the bridge.
|
|
132
|
+
* `steering[]` typically empties every turn boundary (1-15 s); `followUp`
|
|
133
|
+
* is dashboard-enforced capacity 1 and drains on `agent_end`.
|
|
134
|
+
* See capability `mid-turn-prompt-queue`. See change: add-followup-edit-and-steer-cancel.
|
|
135
|
+
*/
|
|
136
|
+
pendingQueues?: { steering: string[]; followUp: string[] };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// ── Extension UI System (Phase 1: management-modal slot) ───────────
|
|
@@ -316,6 +316,10 @@ export interface ImageContent {
|
|
|
316
316
|
mimeType: string;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
// PendingPrompt removed in change: add-followup-edit-and-steer-cancel.
|
|
320
|
+
// Pi's native queues are now the single source of truth; `Session.pendingQueues`
|
|
321
|
+
// holds `string[]` arrays directly from pi's `queue_update` event.
|
|
322
|
+
|
|
319
323
|
/** File entry from directory listing */
|
|
320
324
|
export interface FileEntry {
|
|
321
325
|
path: string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Conditionally runs `patch-package` only when both (a) the patch-package
|
|
3
|
+
// module is resolvable and (b) a `patches/` directory exists in cwd.
|
|
4
|
+
//
|
|
5
|
+
// Why: this repo wires `patch-package` into the root `postinstall` so dev
|
|
6
|
+
// installs (and CI `npm ci`) replay patches in node_modules. End-users who
|
|
7
|
+
// run `npm install --omit=dev` against the published tarball have neither
|
|
8
|
+
// `patch-package` in node_modules nor a `patches/` dir (it isn't published).
|
|
9
|
+
// Without this guard the postinstall fails with exit 127 (`patch-package:
|
|
10
|
+
// not found`), breaking standalone installs and Docker smoke tests.
|
|
11
|
+
//
|
|
12
|
+
// See change: fix-electron-appimage-maker (regression of CI standalone-install-smoke).
|
|
13
|
+
|
|
14
|
+
"use strict";
|
|
15
|
+
|
|
16
|
+
const fs = require("node:fs");
|
|
17
|
+
const path = require("node:path");
|
|
18
|
+
const { spawnSync } = require("node:child_process");
|
|
19
|
+
|
|
20
|
+
const patchesDir = path.resolve(process.cwd(), "patches");
|
|
21
|
+
if (!fs.existsSync(patchesDir)) {
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let patchPackageBin;
|
|
26
|
+
try {
|
|
27
|
+
// Resolve the patch-package binary path via its package.json's `bin` field
|
|
28
|
+
// without invoking npm; works on Windows where `which patch-package` is unreliable.
|
|
29
|
+
const pkgJsonPath = require.resolve("patch-package/package.json");
|
|
30
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
31
|
+
const binEntry = typeof pkgJson.bin === "string"
|
|
32
|
+
? pkgJson.bin
|
|
33
|
+
: (pkgJson.bin && pkgJson.bin["patch-package"]);
|
|
34
|
+
if (!binEntry) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
patchPackageBin = path.resolve(path.dirname(pkgJsonPath), binEntry);
|
|
38
|
+
} catch {
|
|
39
|
+
// patch-package not installed (e.g. `npm install --omit=dev`). No-op.
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = spawnSync(process.execPath, [patchPackageBin], { stdio: "inherit" });
|
|
44
|
+
process.exit(result.status ?? 1);
|