@blackbelt-technology/pi-agent-dashboard 0.4.5 → 0.5.0
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 +342 -267
- package/README.md +51 -2
- package/docs/architecture.md +266 -25
- package/package.json +14 -4
- package/packages/extension/package.json +2 -2
- package/packages/extension/src/__tests__/build-provider-catalogue.test.ts +176 -0
- package/packages/extension/src/__tests__/markdown-image-inliner.test.ts +355 -0
- package/packages/extension/src/__tests__/openspec-activity-detector.test.ts +68 -0
- package/packages/extension/src/__tests__/prompt-bus.test.ts +44 -0
- package/packages/extension/src/__tests__/prompt-expander.test.ts +45 -0
- package/packages/extension/src/__tests__/server-launcher.test.ts +24 -1
- package/packages/extension/src/__tests__/vcs-info-jj.test.ts +145 -0
- package/packages/extension/src/__tests__/{git-info.test.ts → vcs-info.test.ts} +6 -6
- package/packages/extension/src/bridge-context.ts +7 -0
- package/packages/extension/src/bridge.ts +142 -4
- package/packages/extension/src/command-handler.ts +6 -0
- package/packages/extension/src/markdown-image-inliner.ts +268 -0
- package/packages/extension/src/model-tracker.ts +35 -1
- package/packages/extension/src/prompt-bus.ts +4 -3
- package/packages/extension/src/prompt-expander.ts +50 -2
- package/packages/extension/src/provider-register.ts +117 -0
- package/packages/extension/src/server-launcher.ts +18 -1
- package/packages/extension/src/session-sync.ts +6 -1
- package/packages/extension/src/vcs-info.ts +184 -0
- package/packages/server/package.json +4 -4
- package/packages/server/src/__tests__/auto-attach-slug-defense.test.ts +104 -0
- package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +263 -0
- package/packages/server/src/__tests__/browser-gateway-snapshot-on-connect.test.ts +143 -0
- package/packages/server/src/__tests__/build-auth-status.test.ts +190 -0
- package/packages/server/src/__tests__/cold-boot-openspec-broadcast.test.ts +161 -0
- package/packages/server/src/__tests__/doctor-route.test.ts +132 -0
- package/packages/server/src/__tests__/event-wiring-providers-list.test.ts +87 -0
- package/packages/server/src/__tests__/has-openspec-dir.test.ts +64 -0
- package/packages/server/src/__tests__/health-shape.test.ts +43 -0
- package/packages/server/src/__tests__/idle-timer-respects-terminals.test.ts +115 -0
- package/packages/server/src/__tests__/is-unread-trigger.test.ts +4 -2
- package/packages/server/src/__tests__/jj-routes.test.ts +93 -0
- package/packages/server/src/__tests__/openspec-connect-snapshot.test.ts +92 -0
- package/packages/server/src/__tests__/openspec-tasks-parser.test.ts +114 -0
- package/packages/server/src/__tests__/pi-core-updater-managed-path.test.ts +177 -0
- package/packages/server/src/__tests__/process-manager-codes.test.ts +80 -0
- package/packages/server/src/__tests__/process-manager-managed-path.test.ts +73 -0
- package/packages/server/src/__tests__/provider-auth-storage.test.ts +42 -11
- package/packages/server/src/__tests__/provider-catalogue-cache.test.ts +54 -0
- package/packages/server/src/__tests__/session-action-handler-spawn-error.test.ts +17 -2
- package/packages/server/src/__tests__/session-action-handler-spawn.test.ts +150 -0
- package/packages/server/src/__tests__/session-diff-vcs.test.ts +61 -0
- package/packages/server/src/__tests__/session-discovery-skill-firstmessage.test.ts +95 -0
- package/packages/server/src/__tests__/spawn-failure-log.test.ts +118 -0
- package/packages/server/src/__tests__/spawn-preflight.test.ts +91 -0
- package/packages/server/src/__tests__/spawn-register-watchdog.test.ts +166 -0
- package/packages/server/src/__tests__/subscription-handler.test.ts +98 -6
- package/packages/server/src/__tests__/system-routes-reextract.test.ts +91 -0
- package/packages/server/src/__tests__/system-routes-restart.test.ts +4 -4
- package/packages/server/src/__tests__/system-routes-spawn-failures.test.ts +84 -0
- package/packages/server/src/__tests__/terminal-manager.test.ts +45 -0
- package/packages/server/src/bootstrap-install-from-list.ts +232 -0
- package/packages/server/src/bootstrap-state.ts +18 -0
- package/packages/server/src/browser-gateway.ts +58 -21
- package/packages/server/src/browser-handlers/directory-handler.ts +4 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +60 -2
- package/packages/server/src/browser-handlers/subscription-handler.ts +50 -3
- package/packages/server/src/cli.ts +22 -0
- package/packages/server/src/directory-service.ts +31 -0
- package/packages/server/src/event-wiring.ts +57 -2
- package/packages/server/src/home-lock.d.ts +124 -0
- package/packages/server/src/home-lock.js +330 -0
- package/packages/server/src/home-lock.js.map +1 -0
- package/packages/server/src/idle-timer.ts +15 -1
- package/packages/server/src/openspec-tasks.ts +50 -19
- package/packages/server/src/pi-core-updater.ts +65 -9
- package/packages/server/src/pi-gateway.ts +6 -0
- package/packages/server/src/process-manager.ts +62 -11
- package/packages/server/src/provider-auth-handlers.ts +9 -0
- package/packages/server/src/provider-auth-storage.ts +83 -51
- package/packages/server/src/provider-catalogue-cache.ts +41 -0
- package/packages/server/src/routes/doctor-routes.ts +140 -0
- package/packages/server/src/routes/jj-routes.ts +386 -0
- package/packages/server/src/routes/provider-auth-routes.ts +9 -0
- package/packages/server/src/routes/session-routes.ts +12 -3
- package/packages/server/src/routes/system-routes.ts +38 -1
- package/packages/server/src/server.ts +16 -9
- package/packages/server/src/session-bootstrap.ts +27 -12
- package/packages/server/src/session-diff.ts +118 -1
- package/packages/server/src/session-discovery.ts +10 -3
- package/packages/server/src/session-scanner.ts +4 -2
- package/packages/server/src/spawn-failure-log.ts +130 -0
- package/packages/server/src/spawn-preflight.ts +82 -0
- package/packages/server/src/spawn-register-watchdog.ts +236 -0
- package/packages/server/src/terminal-manager.ts +12 -1
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +1 -0
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +1 -0
- package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +72 -0
- package/packages/shared/src/__tests__/browser-protocol-types.test.ts +47 -1
- package/packages/shared/src/__tests__/config.test.ts +48 -0
- package/packages/shared/src/__tests__/dashboard-starter.test.ts +40 -0
- package/packages/shared/src/__tests__/detached-spawn.test.ts +24 -0
- package/packages/shared/src/__tests__/doctor-core.test.ts +134 -0
- package/packages/shared/src/__tests__/doctor-fault-tolerance.test.ts +218 -0
- package/packages/shared/src/__tests__/doctor-format.test.ts +121 -0
- package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +68 -0
- package/packages/shared/src/__tests__/install-managed-node.test.ts +192 -0
- package/packages/shared/src/__tests__/installable-list.test.ts +130 -0
- package/packages/shared/src/__tests__/managed-node-path.test.ts +122 -0
- package/packages/shared/src/__tests__/managed-runtime-strategy.test.ts +74 -0
- package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +52 -0
- package/packages/shared/src/__tests__/no-raw-openspec-status-in-skills.test.ts +6 -1
- package/packages/shared/src/__tests__/platform-jj.test.ts +339 -0
- package/packages/shared/src/__tests__/skill-block-parser.test.ts +153 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +18 -2
- package/packages/shared/src/bootstrap-install.ts +196 -2
- package/packages/shared/src/browser-protocol.ts +112 -1
- package/packages/shared/src/config.ts +29 -0
- package/packages/shared/src/dashboard-starter.ts +33 -0
- package/packages/shared/src/diff-types.ts +17 -0
- package/packages/shared/src/doctor-core.ts +821 -0
- package/packages/shared/src/index.ts +9 -0
- package/packages/shared/src/installable-list.ts +152 -0
- package/packages/shared/src/launch-source-flag.ts +14 -0
- package/packages/shared/src/launch-source-types.ts +18 -0
- package/packages/shared/src/openspec-activity-detector.ts +25 -7
- package/packages/shared/src/platform/detached-spawn.ts +13 -2
- package/packages/shared/src/platform/jj.ts +405 -0
- package/packages/shared/src/platform/managed-node-path.ts +77 -0
- package/packages/shared/src/protocol.ts +60 -2
- package/packages/shared/src/rest-api.ts +4 -0
- package/packages/shared/src/skill-block-parser.ts +115 -0
- package/packages/shared/src/tool-registry/__tests__/managed-runtime-strategy.test.ts +166 -0
- package/packages/shared/src/tool-registry/definitions.ts +19 -5
- package/packages/shared/src/tool-registry/strategies.ts +42 -0
- package/packages/shared/src/types.ts +91 -0
- package/packages/extension/src/git-info.ts +0 -55
|
@@ -12,8 +12,15 @@
|
|
|
12
12
|
*
|
|
13
13
|
* See change: unified-bootstrap-install.
|
|
14
14
|
*/
|
|
15
|
-
import { spawn as cpSpawn } from "./platform/exec.js";
|
|
16
|
-
import {
|
|
15
|
+
import { spawn as cpSpawn, spawnSync as cpSpawnSync } from "./platform/exec.js";
|
|
16
|
+
import {
|
|
17
|
+
cpSync,
|
|
18
|
+
existsSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
readFileSync,
|
|
21
|
+
rmSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
} from "node:fs";
|
|
17
24
|
import path from "node:path";
|
|
18
25
|
import { getManagedDir } from "./managed-paths.js";
|
|
19
26
|
import { getDefaultRegistry, type ToolRegistry } from "./tool-registry/index.js";
|
|
@@ -210,3 +217,190 @@ export async function bootstrapInstallDefaults(
|
|
|
210
217
|
progress,
|
|
211
218
|
});
|
|
212
219
|
}
|
|
220
|
+
|
|
221
|
+
// ── Managed Node runtime install ───────────────────────────────────────
|
|
222
|
+
//
|
|
223
|
+
// See change: embed-managed-node-runtime (spec: managed-node-runtime).
|
|
224
|
+
//
|
|
225
|
+
// `installManagedNode` copies a bundled Node distribution into
|
|
226
|
+
// `<managedDir>/node/` and writes a `<managedDir>/node/.version` marker.
|
|
227
|
+
// Idempotent: skip when marker matches the bundled version, replace on
|
|
228
|
+
// mismatch, no-op when the bundled source isn't available (standalone
|
|
229
|
+
// CLI install with no Electron resources).
|
|
230
|
+
|
|
231
|
+
export interface InstallManagedNodeOptions {
|
|
232
|
+
/**
|
|
233
|
+
* Source directory containing the bundled Node distribution.
|
|
234
|
+
* Layout matches the upstream Node zip/tar:
|
|
235
|
+
* Windows: `<dir>/node.exe`, `<dir>/npm.cmd`, `<dir>/npx.cmd`
|
|
236
|
+
* Unix: `<dir>/bin/node`, `<dir>/bin/npm`, `<dir>/bin/npx`
|
|
237
|
+
*
|
|
238
|
+
* Caller resolves this (Electron uses `path.dirname(getBundledNodePath())`
|
|
239
|
+
* after stripping the platform-specific suffix). Pass `null` /
|
|
240
|
+
* `undefined` for the standalone CLI install case — the function
|
|
241
|
+
* no-ops without error.
|
|
242
|
+
*/
|
|
243
|
+
bundledNodeDir?: string | null;
|
|
244
|
+
/** Root of the managed install. Defaults to `getManagedDir()`. */
|
|
245
|
+
managedDir?: string;
|
|
246
|
+
/** Called on every progress tick. Mirrors `bootstrapInstall` shape. */
|
|
247
|
+
progress?: ProgressCallback;
|
|
248
|
+
/**
|
|
249
|
+
* Test seam: override how the bundled Node version is read. Default
|
|
250
|
+
* spawns `<bundledNodeDir>/bin/node --version` (or `node.exe` on
|
|
251
|
+
* Windows) and trims stdout. Tests inject a fake to avoid real spawns.
|
|
252
|
+
*/
|
|
253
|
+
_readVersion?: (sourceBinary: string) => string | null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface InstallManagedNodeResult {
|
|
257
|
+
/** True iff the operation succeeded (including the no-op cases). */
|
|
258
|
+
ok: boolean;
|
|
259
|
+
/** Did we actually copy files? false when no-op or skipped. */
|
|
260
|
+
copied: boolean;
|
|
261
|
+
/** Resolved managed Node directory (`<managedDir>/node`). */
|
|
262
|
+
managedNodeDir: string;
|
|
263
|
+
/** Bundled Node version (e.g. `v22.12.0`) when known. */
|
|
264
|
+
version?: string;
|
|
265
|
+
/** Reason for skip / failure. Always set when `copied === false`. */
|
|
266
|
+
reason?: string;
|
|
267
|
+
/** Error message when `ok === false`. */
|
|
268
|
+
error?: string;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Path to the source `node` / `node.exe` binary inside `bundledNodeDir`. */
|
|
272
|
+
function sourceNodeBinary(bundledNodeDir: string): string {
|
|
273
|
+
return process.platform === "win32" // platform-branch-ok: Node distribution layout differs Windows vs Unix
|
|
274
|
+
? path.join(bundledNodeDir, "node.exe")
|
|
275
|
+
: path.join(bundledNodeDir, "bin", "node");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Spawn `<nodeBinary> --version` and return the trimmed stdout (e.g.
|
|
280
|
+
* `v22.12.0`). Returns null on failure or when the binary is missing.
|
|
281
|
+
* Synchronous because bootstrap is naturally serial.
|
|
282
|
+
*/
|
|
283
|
+
function readNodeVersion(nodeBinary: string): string | null {
|
|
284
|
+
if (!existsSync(nodeBinary)) return null;
|
|
285
|
+
try {
|
|
286
|
+
const r = cpSpawnSync(nodeBinary, ["--version"], {
|
|
287
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
288
|
+
timeout: 5_000,
|
|
289
|
+
encoding: "utf-8",
|
|
290
|
+
});
|
|
291
|
+
if (r.status !== 0) return null;
|
|
292
|
+
const out = (r.stdout ?? "").toString().trim();
|
|
293
|
+
return out || null;
|
|
294
|
+
} catch {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Read the `<managedNodeDir>/.version` marker if present, else null. */
|
|
300
|
+
function readManagedMarker(managedNodeDir: string): string | null {
|
|
301
|
+
const markerPath = path.join(managedNodeDir, ".version");
|
|
302
|
+
if (!existsSync(markerPath)) return null;
|
|
303
|
+
try {
|
|
304
|
+
return readFileSync(markerPath, "utf-8").trim() || null;
|
|
305
|
+
} catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Idempotently copy the bundled Node distribution into `<managedDir>/node/`.
|
|
312
|
+
*
|
|
313
|
+
* - First-run: full recursive copy + write `.version` marker.
|
|
314
|
+
* - Re-run with matching marker: no-op.
|
|
315
|
+
* - Mismatched marker (or missing marker with dir present): replace +
|
|
316
|
+
* rewrite marker.
|
|
317
|
+
* - Bundled source absent or `bundledNodeDir == null`: no-op.
|
|
318
|
+
* - Failed copy mid-flight: marker NOT written, so next call retries.
|
|
319
|
+
*/
|
|
320
|
+
export async function installManagedNode(
|
|
321
|
+
opts: InstallManagedNodeOptions = {},
|
|
322
|
+
): Promise<InstallManagedNodeResult> {
|
|
323
|
+
const managedDir = opts.managedDir ?? getManagedDir();
|
|
324
|
+
const managedNodeDir = path.join(managedDir, "node");
|
|
325
|
+
const step = "node-runtime";
|
|
326
|
+
|
|
327
|
+
const bundledDir = opts.bundledNodeDir ?? null;
|
|
328
|
+
if (!bundledDir) {
|
|
329
|
+
return {
|
|
330
|
+
ok: true,
|
|
331
|
+
copied: false,
|
|
332
|
+
managedNodeDir,
|
|
333
|
+
reason: "no bundled source",
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const sourceBinary = sourceNodeBinary(bundledDir);
|
|
338
|
+
const sourceVersion = (opts._readVersion ?? readNodeVersion)(sourceBinary);
|
|
339
|
+
if (!sourceVersion) {
|
|
340
|
+
return {
|
|
341
|
+
ok: true,
|
|
342
|
+
copied: false,
|
|
343
|
+
managedNodeDir,
|
|
344
|
+
reason: `bundled node binary missing or unreadable: ${sourceBinary}`,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const existingMarker = readManagedMarker(managedNodeDir);
|
|
349
|
+
if (existingMarker === sourceVersion) {
|
|
350
|
+
return {
|
|
351
|
+
ok: true,
|
|
352
|
+
copied: false,
|
|
353
|
+
managedNodeDir,
|
|
354
|
+
version: sourceVersion,
|
|
355
|
+
reason: "version matches bundled — no copy needed",
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
opts.progress?.({
|
|
360
|
+
step,
|
|
361
|
+
status: "running",
|
|
362
|
+
output: `Installing Node ${sourceVersion} runtime`,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
// Replace any existing dir (handles the mismatch + missing-marker
|
|
367
|
+
// cases) so the copy is from a clean slate.
|
|
368
|
+
if (existsSync(managedNodeDir)) {
|
|
369
|
+
rmSync(managedNodeDir, { recursive: true, force: true });
|
|
370
|
+
}
|
|
371
|
+
mkdirSync(path.dirname(managedNodeDir), { recursive: true });
|
|
372
|
+
|
|
373
|
+
cpSync(bundledDir, managedNodeDir, {
|
|
374
|
+
recursive: true,
|
|
375
|
+
force: true,
|
|
376
|
+
// dereference: false keeps symlinks-as-symlinks (Unix npm shim).
|
|
377
|
+
// verbatimSymlinks would also work in newer Node; default is fine.
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Marker last — partial copy on failure leaves no marker, so the
|
|
381
|
+
// next invocation treats the dir as missing and retries.
|
|
382
|
+
writeFileSync(
|
|
383
|
+
path.join(managedNodeDir, ".version"),
|
|
384
|
+
sourceVersion + "\n",
|
|
385
|
+
"utf-8",
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
opts.progress?.({ step, status: "done", output: `Node ${sourceVersion}` });
|
|
389
|
+
return {
|
|
390
|
+
ok: true,
|
|
391
|
+
copied: true,
|
|
392
|
+
managedNodeDir,
|
|
393
|
+
version: sourceVersion,
|
|
394
|
+
};
|
|
395
|
+
} catch (err) {
|
|
396
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
397
|
+
opts.progress?.({ step, status: "error", error: message });
|
|
398
|
+
return {
|
|
399
|
+
ok: false,
|
|
400
|
+
copied: false,
|
|
401
|
+
managedNodeDir,
|
|
402
|
+
version: sourceVersion,
|
|
403
|
+
error: message,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -127,6 +127,33 @@ export interface SpawnResultBrowserMessage {
|
|
|
127
127
|
message: string;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Failure classification codes for spawn errors.
|
|
132
|
+
* Set on every `{ success: false }` path inside process-manager and the
|
|
133
|
+
* session-action-handler. Additive — clients that do not know a code fall
|
|
134
|
+
* back to the `message` string.
|
|
135
|
+
* See change: spawn-failure-diagnostics.
|
|
136
|
+
*/
|
|
137
|
+
export type SpawnFailureCode =
|
|
138
|
+
| "DIR_MISSING"
|
|
139
|
+
| "PI_NOT_FOUND"
|
|
140
|
+
| "WIN_PI_CMD_ONLY"
|
|
141
|
+
| "WT_MISSING"
|
|
142
|
+
| "TMUX_MISSING"
|
|
143
|
+
| "PI_CRASHED"
|
|
144
|
+
| "SPAWN_ERRNO"
|
|
145
|
+
| "PREFLIGHT_FAILED"
|
|
146
|
+
| "REGISTER_TIMEOUT";
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A single reason from the synchronous spawn preflight check.
|
|
150
|
+
* See change: spawn-failure-diagnostics.
|
|
151
|
+
*/
|
|
152
|
+
export interface PreflightReason {
|
|
153
|
+
code: string;
|
|
154
|
+
message: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
130
157
|
/**
|
|
131
158
|
* Emitted when a session spawn fails — either because `spawnPiSession` threw,
|
|
132
159
|
* returned `{ success: false }`, or the spawned child crashed immediately.
|
|
@@ -140,6 +167,37 @@ export interface SpawnErrorMessage {
|
|
|
140
167
|
message: string;
|
|
141
168
|
/** Up to ~2 KB tail of stderr captured from the failed child, if any. */
|
|
142
169
|
stderr?: string;
|
|
170
|
+
/** Structured failure classifier. Additive — old clients ignore this field. See change: spawn-failure-diagnostics. */
|
|
171
|
+
code?: SpawnFailureCode;
|
|
172
|
+
/** Preflight failure reasons. Only set when code === "PREFLIGHT_FAILED". See change: spawn-failure-diagnostics. */
|
|
173
|
+
reasons?: PreflightReason[];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Emitted when a spawned pi session never sends `session_register` within
|
|
178
|
+
* the configured `spawnRegisterTimeoutMs` window.
|
|
179
|
+
* See change: spawn-failure-diagnostics.
|
|
180
|
+
*/
|
|
181
|
+
export interface SpawnRegisterTimeoutMessage {
|
|
182
|
+
type: "spawn_register_timeout";
|
|
183
|
+
cwd: string;
|
|
184
|
+
/** Present for headless spawns; absent for tmux/wt/wsl-tmux. */
|
|
185
|
+
pid?: number;
|
|
186
|
+
/** Last 4 KB of the per-session stderr log, if available. */
|
|
187
|
+
stderrTail?: string;
|
|
188
|
+
/** The effective watchdog timeout in ms — so the UI can render e.g. "30s". See change: spawn-failure-diagnostics (fix W2). */
|
|
189
|
+
timeoutMs?: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Emitted when pi finally registers AFTER the watchdog already fired.
|
|
194
|
+
* The UI uses it to auto-clear the timeout banner for the given cwd.
|
|
195
|
+
* See change: spawn-failure-diagnostics.
|
|
196
|
+
*/
|
|
197
|
+
export interface SpawnRegisterRecoveredMessage {
|
|
198
|
+
type: "spawn_register_recovered";
|
|
199
|
+
cwd: string;
|
|
200
|
+
pid?: number;
|
|
143
201
|
}
|
|
144
202
|
|
|
145
203
|
export interface SessionsReorderedMessage {
|
|
@@ -148,6 +206,28 @@ export interface SessionsReorderedMessage {
|
|
|
148
206
|
sessionIds: string[];
|
|
149
207
|
}
|
|
150
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Atomic on-connect snapshot of the server's full session registry and
|
|
211
|
+
* per-cwd ordering. Replaces the legacy per-session `session_added` loop
|
|
212
|
+
* + per-cwd `sessions_reordered` loop that the gateway used to emit on
|
|
213
|
+
* each browser WS connect. Client SHALL replace its `sessions` Map and
|
|
214
|
+
* `sessionOrderMap` with this payload (no merging) so stale ids from a
|
|
215
|
+
* previous server lifetime are dropped atomically.
|
|
216
|
+
*
|
|
217
|
+
* Live updates after the snapshot continue using the incremental
|
|
218
|
+
* `session_added` / `session_updated` / `session_removed` /
|
|
219
|
+
* `sessions_reordered` messages.
|
|
220
|
+
*
|
|
221
|
+
* See change: fix-stale-sessions-on-reconnect.
|
|
222
|
+
*/
|
|
223
|
+
export interface SessionsSnapshotMessage {
|
|
224
|
+
type: "sessions_snapshot";
|
|
225
|
+
/** Every session known to the server at construction time, alive AND ended. */
|
|
226
|
+
sessions: DashboardSession[];
|
|
227
|
+
/** cwd → ordered session ids. Only non-empty arrays are included. */
|
|
228
|
+
orders: Record<string, string[]>;
|
|
229
|
+
}
|
|
230
|
+
|
|
151
231
|
export interface PinnedDirsUpdatedMessage {
|
|
152
232
|
type: "pinned_dirs_updated";
|
|
153
233
|
paths: string[];
|
|
@@ -362,6 +442,23 @@ export interface BrowserExtUiDecoratorMessage {
|
|
|
362
442
|
removed?: boolean;
|
|
363
443
|
}
|
|
364
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Server → browser: register a base64-encoded image asset under a content
|
|
447
|
+
* hash for the given session. Forwarded verbatim from the bridge's
|
|
448
|
+
* `asset_register` message and replayed to reconnecting browsers (in
|
|
449
|
+
* chronological position relative to its referencing `message_update` /
|
|
450
|
+
* `message_end`). The client populates a per-session `Map<hash,{data,mime}>`
|
|
451
|
+
* consumed by the `MarkdownContent` `pi-asset:` resolver.
|
|
452
|
+
* See change: chat-markdown-local-images-and-math.
|
|
453
|
+
*/
|
|
454
|
+
export interface BrowserAssetRegisterMessage {
|
|
455
|
+
type: "asset_register";
|
|
456
|
+
sessionId: string;
|
|
457
|
+
hash: string;
|
|
458
|
+
mimeType: string;
|
|
459
|
+
data: string;
|
|
460
|
+
}
|
|
461
|
+
|
|
365
462
|
/** Sent when a plugin's config changes; carries only that plugin's namespace. */
|
|
366
463
|
export interface PluginConfigUpdateMessage {
|
|
367
464
|
type: "plugin_config_update";
|
|
@@ -392,7 +489,10 @@ export type ServerToBrowserMessage =
|
|
|
392
489
|
| ResumeResultBrowserMessage
|
|
393
490
|
| SpawnResultBrowserMessage
|
|
394
491
|
| SpawnErrorMessage
|
|
492
|
+
| SpawnRegisterTimeoutMessage
|
|
493
|
+
| SpawnRegisterRecoveredMessage
|
|
395
494
|
| SessionsReorderedMessage
|
|
495
|
+
| SessionsSnapshotMessage
|
|
396
496
|
| PinnedDirsUpdatedMessage
|
|
397
497
|
| TerminalAddedMessage
|
|
398
498
|
| TerminalRemovedMessage
|
|
@@ -416,7 +516,8 @@ export type ServerToBrowserMessage =
|
|
|
416
516
|
| BootstrapTicketCompleteMessage
|
|
417
517
|
| BrowserUiModulesListMessage
|
|
418
518
|
| BrowserUiDataListMessage
|
|
419
|
-
| BrowserExtUiDecoratorMessage
|
|
519
|
+
| BrowserExtUiDecoratorMessage
|
|
520
|
+
| BrowserAssetRegisterMessage;
|
|
420
521
|
|
|
421
522
|
// ── Browser → Server ────────────────────────────────────────────────
|
|
422
523
|
|
|
@@ -476,6 +577,15 @@ export interface RequestModelsBrowserMessage {
|
|
|
476
577
|
sessionId: string;
|
|
477
578
|
}
|
|
478
579
|
|
|
580
|
+
/**
|
|
581
|
+
* Browser asks the server to forward `request_providers` to the bridge.
|
|
582
|
+
* See change: replace-hardcoded-provider-lists.
|
|
583
|
+
*/
|
|
584
|
+
export interface RequestProvidersBrowserMessage {
|
|
585
|
+
type: "request_providers";
|
|
586
|
+
sessionId: string;
|
|
587
|
+
}
|
|
588
|
+
|
|
479
589
|
export interface SetThinkingLevelBrowserMessage {
|
|
480
590
|
type: "set_thinking_level";
|
|
481
591
|
sessionId: string;
|
|
@@ -769,6 +879,7 @@ export type BrowserToServerMessage =
|
|
|
769
879
|
| OpenSpecRefreshBrowserMessage
|
|
770
880
|
| RenameSessionBrowserMessage
|
|
771
881
|
| RequestModelsBrowserMessage
|
|
882
|
+
| RequestProvidersBrowserMessage
|
|
772
883
|
| SetThinkingLevelBrowserMessage
|
|
773
884
|
| SetModelBrowserMessage
|
|
774
885
|
| ShutdownBrowserMessage
|
|
@@ -134,6 +134,13 @@ export interface DashboardConfig {
|
|
|
134
134
|
editor: EditorConfig;
|
|
135
135
|
/** OpenSpec background polling behavior (interval, concurrency, change detection, jitter) */
|
|
136
136
|
openspec: OpenSpecPollConfig;
|
|
137
|
+
/**
|
|
138
|
+
* Timeout for ask_user prompts in seconds.
|
|
139
|
+
* Default: 300 (5 minutes).
|
|
140
|
+
* Set to -1 (or any value <= 0) for no timeout (waits indefinitely).
|
|
141
|
+
* If the key is absent from config.json the default of 300 s applies.
|
|
142
|
+
*/
|
|
143
|
+
askUserPromptTimeoutSeconds: number;
|
|
137
144
|
/** Networks trusted for full access without authentication (CIDR, wildcard, exact IP) */
|
|
138
145
|
trustedNetworks: string[];
|
|
139
146
|
/** Merged trustedNetworks + auth.bypassHosts (deduplicated). Computed at load time. */
|
|
@@ -152,6 +159,12 @@ export interface DashboardConfig {
|
|
|
152
159
|
reattachPlacement: ReattachPlacement;
|
|
153
160
|
/** Persisted list of known remote servers */
|
|
154
161
|
knownServers: KnownServer[];
|
|
162
|
+
/**
|
|
163
|
+
* How long (ms) to wait for a spawned pi session to send `session_register`
|
|
164
|
+
* before emitting a timeout warning. Default 30000 (30s). Clamped [5000, 120000].
|
|
165
|
+
* See change: spawn-failure-diagnostics.
|
|
166
|
+
*/
|
|
167
|
+
spawnRegisterTimeoutMs: number;
|
|
155
168
|
/**
|
|
156
169
|
* Per-plugin config namespaces. Reserved top-level key.
|
|
157
170
|
* Each plugin's config lives at plugins.<id>.*
|
|
@@ -168,6 +181,16 @@ export interface CorsConfig {
|
|
|
168
181
|
|
|
169
182
|
const VALID_SPAWN_STRATEGIES: SpawnStrategy[] = ["tmux", "headless"];
|
|
170
183
|
|
|
184
|
+
/** Default ask_user prompt timeout: 300 seconds (5 minutes). */
|
|
185
|
+
export const DEFAULT_ASK_USER_PROMPT_TIMEOUT_SECONDS = 300;
|
|
186
|
+
|
|
187
|
+
/** Default + clamp for spawnRegisterTimeoutMs. See change: spawn-failure-diagnostics. */
|
|
188
|
+
export const DEFAULT_SPAWN_REGISTER_TIMEOUT_MS = 30000;
|
|
189
|
+
export function clampSpawnRegisterTimeoutMs(v: unknown): number {
|
|
190
|
+
if (typeof v !== "number" || isNaN(v)) return DEFAULT_SPAWN_REGISTER_TIMEOUT_MS;
|
|
191
|
+
return Math.max(5000, Math.min(120000, v));
|
|
192
|
+
}
|
|
193
|
+
|
|
171
194
|
const DEFAULTS: DashboardConfig = {
|
|
172
195
|
plugins: {},
|
|
173
196
|
port: 8000,
|
|
@@ -187,7 +210,9 @@ const DEFAULTS: DashboardConfig = {
|
|
|
187
210
|
cors: { allowedOrigins: [] },
|
|
188
211
|
electronMode: false,
|
|
189
212
|
knownServers: [],
|
|
213
|
+
askUserPromptTimeoutSeconds: DEFAULT_ASK_USER_PROMPT_TIMEOUT_SECONDS,
|
|
190
214
|
reattachPlacement: DEFAULT_REATTACH_PLACEMENT,
|
|
215
|
+
spawnRegisterTimeoutMs: 30000,
|
|
191
216
|
};
|
|
192
217
|
|
|
193
218
|
/**
|
|
@@ -385,6 +410,10 @@ export function loadConfig(): DashboardConfig {
|
|
|
385
410
|
knownServers: parseKnownServers(parsed.knownServers),
|
|
386
411
|
reattachPlacement: parseReattachPlacement(parsed.reattachPlacement),
|
|
387
412
|
plugins: parsePluginsConfig(parsed.plugins),
|
|
413
|
+
askUserPromptTimeoutSeconds: typeof parsed.askUserPromptTimeoutSeconds === "number"
|
|
414
|
+
? parsed.askUserPromptTimeoutSeconds
|
|
415
|
+
: defaults.askUserPromptTimeoutSeconds,
|
|
416
|
+
spawnRegisterTimeoutMs: clampSpawnRegisterTimeoutMs(parsed.spawnRegisterTimeoutMs),
|
|
388
417
|
};
|
|
389
418
|
|
|
390
419
|
// Compute resolvedTrustedNetworks: merge trustedNetworks + auth.bypassHosts
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DashboardStarter — identifies who launched the dashboard server process.
|
|
3
|
+
*
|
|
4
|
+
* "Bridge" — spawned by the pi bridge extension (server-launcher.ts).
|
|
5
|
+
* "Standalone" — invoked directly via CLI (`pi-dashboard` or `pi-dashboard start`).
|
|
6
|
+
* "Electron" — spawned by the Electron main process.
|
|
7
|
+
*
|
|
8
|
+
* The value is injected via the DASHBOARD_STARTER env var at spawn time.
|
|
9
|
+
* When unset or empty the default is "Standalone" (direct CLI invocation).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type DashboardStarter = "Bridge" | "Standalone" | "Electron";
|
|
13
|
+
|
|
14
|
+
const VALID: ReadonlySet<string> = new Set<DashboardStarter>(["Bridge", "Standalone", "Electron"]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse `env.DASHBOARD_STARTER` into a `DashboardStarter`.
|
|
18
|
+
*
|
|
19
|
+
* - Unset or empty → `"Standalone"` (direct CLI invocation default).
|
|
20
|
+
* - Valid value → that value.
|
|
21
|
+
* - Invalid value → logs `console.warn`, returns `"Standalone"`.
|
|
22
|
+
*/
|
|
23
|
+
export function parseDashboardStarter(
|
|
24
|
+
env: Record<string, string | undefined>,
|
|
25
|
+
): DashboardStarter {
|
|
26
|
+
const raw = env["DASHBOARD_STARTER"];
|
|
27
|
+
if (!raw) return "Standalone";
|
|
28
|
+
if (VALID.has(raw)) return raw as DashboardStarter;
|
|
29
|
+
console.warn(
|
|
30
|
+
`[dashboard-starter] Unknown DASHBOARD_STARTER value "${raw}"; defaulting to "Standalone".`,
|
|
31
|
+
);
|
|
32
|
+
return "Standalone";
|
|
33
|
+
}
|
|
@@ -38,4 +38,21 @@ export interface SessionDiffResponse {
|
|
|
38
38
|
files: FileDiffEntry[];
|
|
39
39
|
/** Whether the session cwd is a git repository */
|
|
40
40
|
isGitRepo: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* VCS regime used to compute the per-file diffs. Optional for
|
|
43
|
+
* backwards compatibility — absent on responses produced before
|
|
44
|
+
* change `add-jj-workspace-plugin`.
|
|
45
|
+
*/
|
|
46
|
+
vcsKind?: "git" | "jj";
|
|
47
|
+
/**
|
|
48
|
+
* The literal revset / ref used as the diff base (e.g. "HEAD",
|
|
49
|
+
* "@-", "fork_point(@, trunk())"). Optional.
|
|
50
|
+
*/
|
|
51
|
+
diffBase?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Human-readable label for `diffBase` (e.g. "develop", "trunk()",
|
|
54
|
+
* "HEAD"). Optional. Renders as "Diffing against \<baseLabel\>"
|
|
55
|
+
* in the client when `vcsKind === "jj"`.
|
|
56
|
+
*/
|
|
57
|
+
baseLabel?: string;
|
|
41
58
|
}
|