@blackbelt-technology/pi-agent-dashboard 0.4.6 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/AGENTS.md +339 -190
  2. package/README.md +50 -7
  3. package/docs/architecture.md +238 -23
  4. package/package.json +23 -13
  5. package/packages/extension/package.json +11 -3
  6. package/packages/extension/src/__tests__/ask-user-tool.test.ts +1 -1
  7. package/packages/extension/src/__tests__/build-provider-catalogue.test.ts +176 -0
  8. package/packages/extension/src/__tests__/command-handler.test.ts +68 -0
  9. package/packages/extension/src/__tests__/enrich-model-metadata.test.ts +1 -1
  10. package/packages/extension/src/__tests__/markdown-image-inliner.test.ts +355 -0
  11. package/packages/extension/src/__tests__/no-tui-multiselect-arm-regression.test.ts +1 -1
  12. package/packages/extension/src/__tests__/openspec-activity-detector.test.ts +68 -0
  13. package/packages/extension/src/__tests__/prompt-expander.test.ts +45 -0
  14. package/packages/extension/src/__tests__/provider-register-reload.test.ts +74 -0
  15. package/packages/extension/src/__tests__/retry-tracker.test.ts +147 -0
  16. package/packages/extension/src/__tests__/server-launcher.test.ts +24 -1
  17. package/packages/extension/src/__tests__/session-sync.test.ts +72 -0
  18. package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +105 -0
  19. package/packages/extension/src/ask-user-tool.ts +1 -1
  20. package/packages/extension/src/bridge-context.ts +1 -1
  21. package/packages/extension/src/bridge.ts +169 -4
  22. package/packages/extension/src/command-handler.ts +65 -2
  23. package/packages/extension/src/flow-event-wiring.ts +1 -1
  24. package/packages/extension/src/markdown-image-inliner.ts +268 -0
  25. package/packages/extension/src/multiselect-list.ts +1 -1
  26. package/packages/extension/src/pi-env.d.ts +16 -9
  27. package/packages/extension/src/prompt-expander.ts +50 -2
  28. package/packages/extension/src/provider-register.ts +132 -8
  29. package/packages/extension/src/retry-tracker.ts +123 -0
  30. package/packages/extension/src/server-launcher.ts +18 -1
  31. package/packages/extension/src/session-sync.ts +15 -1
  32. package/packages/extension/src/usage-limit-orderer.ts +76 -0
  33. package/packages/server/package.json +6 -6
  34. package/packages/server/src/__tests__/auto-attach-slug-defense.test.ts +104 -0
  35. package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +263 -0
  36. package/packages/server/src/__tests__/browser-gateway-snapshot-on-connect.test.ts +143 -0
  37. package/packages/server/src/__tests__/build-auth-status.test.ts +190 -0
  38. package/packages/server/src/__tests__/changelog-fs.test.ts +171 -0
  39. package/packages/server/src/__tests__/changelog-parser.test.ts +220 -0
  40. package/packages/server/src/__tests__/changelog-remote.test.ts +193 -0
  41. package/packages/server/src/__tests__/cli-parse.test.ts +22 -4
  42. package/packages/server/src/__tests__/cold-boot-openspec-broadcast.test.ts +161 -0
  43. package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +1 -1
  44. package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +1 -1
  45. package/packages/server/src/__tests__/directory-service-toctou.test.ts +1 -1
  46. package/packages/server/src/__tests__/directory-service.test.ts +1 -1
  47. package/packages/server/src/__tests__/doctor-route.test.ts +132 -0
  48. package/packages/server/src/__tests__/event-wiring-providers-list.test.ts +154 -0
  49. package/packages/server/src/__tests__/fixtures/pi-changelog-slice.md +180 -0
  50. package/packages/server/src/__tests__/fork-empty-session-preflight.test.ts +268 -0
  51. package/packages/server/src/__tests__/has-openspec-dir.test.ts +64 -0
  52. package/packages/server/src/__tests__/headless-pid-registry.test.ts +83 -0
  53. package/packages/server/src/__tests__/health-shape.test.ts +43 -0
  54. package/packages/server/src/__tests__/idle-timer-respects-terminals.test.ts +115 -0
  55. package/packages/server/src/__tests__/is-pi-process.test.ts +1 -1
  56. package/packages/server/src/__tests__/openspec-connect-snapshot.test.ts +92 -0
  57. package/packages/server/src/__tests__/package-manager-wrapper-resolve.test.ts +4 -4
  58. package/packages/server/src/__tests__/package-routes.test.ts +1 -1
  59. package/packages/server/src/__tests__/pending-fork-registry.test.ts +48 -24
  60. package/packages/server/src/__tests__/pi-changelog-integration.test.ts +165 -0
  61. package/packages/server/src/__tests__/pi-changelog-routes.test.ts +409 -0
  62. package/packages/server/src/__tests__/pi-core-checker.test.ts +155 -13
  63. package/packages/server/src/__tests__/pi-core-updater-managed-path.test.ts +236 -0
  64. package/packages/server/src/__tests__/pi-core-updater.test.ts +1 -1
  65. package/packages/server/src/__tests__/pi-dev-version-check.test.ts +184 -0
  66. package/packages/server/src/__tests__/pi-version-skew.test.ts +4 -4
  67. package/packages/server/src/__tests__/process-manager-codes.test.ts +80 -0
  68. package/packages/server/src/__tests__/process-manager-managed-path.test.ts +73 -0
  69. package/packages/server/src/__tests__/provider-auth-routes.test.ts +12 -4
  70. package/packages/server/src/__tests__/provider-auth-storage.test.ts +42 -11
  71. package/packages/server/src/__tests__/provider-catalogue-cache.test.ts +44 -0
  72. package/packages/server/src/__tests__/recommended-routes.test.ts +1 -1
  73. package/packages/server/src/__tests__/session-action-handler-spawn-error.test.ts +17 -2
  74. package/packages/server/src/__tests__/session-action-handler-spawn.test.ts +150 -0
  75. package/packages/server/src/__tests__/session-discovery-skill-firstmessage.test.ts +95 -0
  76. package/packages/server/src/__tests__/spawn-correlation-token-integration.test.ts +91 -0
  77. package/packages/server/src/__tests__/spawn-failure-log.test.ts +118 -0
  78. package/packages/server/src/__tests__/spawn-preflight.test.ts +91 -0
  79. package/packages/server/src/__tests__/spawn-register-watchdog.test.ts +250 -0
  80. package/packages/server/src/__tests__/spawn-token.test.ts +57 -0
  81. package/packages/server/src/__tests__/subscription-handler.test.ts +98 -6
  82. package/packages/server/src/__tests__/system-routes-reextract.test.ts +91 -0
  83. package/packages/server/src/__tests__/system-routes-spawn-failures.test.ts +84 -0
  84. package/packages/server/src/__tests__/terminal-manager.test.ts +45 -0
  85. package/packages/server/src/bootstrap-install-from-list.ts +232 -0
  86. package/packages/server/src/bootstrap-state.ts +18 -0
  87. package/packages/server/src/browser-gateway.ts +70 -24
  88. package/packages/server/src/browser-handlers/directory-handler.ts +4 -0
  89. package/packages/server/src/browser-handlers/handler-context.ts +9 -0
  90. package/packages/server/src/browser-handlers/session-action-handler.ts +159 -18
  91. package/packages/server/src/browser-handlers/subscription-handler.ts +50 -3
  92. package/packages/server/src/changelog-fs.ts +167 -0
  93. package/packages/server/src/changelog-parser.ts +321 -0
  94. package/packages/server/src/changelog-remote.ts +134 -0
  95. package/packages/server/src/cli.ts +23 -2
  96. package/packages/server/src/directory-service.ts +31 -0
  97. package/packages/server/src/event-wiring.ts +105 -5
  98. package/packages/server/src/headless-pid-registry.ts +54 -5
  99. package/packages/server/src/home-lock.d.ts +124 -0
  100. package/packages/server/src/home-lock.js +330 -0
  101. package/packages/server/src/home-lock.js.map +1 -0
  102. package/packages/server/src/idle-timer.ts +15 -1
  103. package/packages/server/src/pending-client-correlations.ts +73 -0
  104. package/packages/server/src/pending-fork-registry.ts +24 -12
  105. package/packages/server/src/pi-core-checker.ts +77 -17
  106. package/packages/server/src/pi-core-updater.ts +81 -15
  107. package/packages/server/src/pi-dev-version-check.ts +145 -0
  108. package/packages/server/src/pi-gateway.ts +10 -0
  109. package/packages/server/src/pi-version-skew.ts +12 -4
  110. package/packages/server/src/process-manager.ts +115 -21
  111. package/packages/server/src/provider-auth-handlers.ts +9 -0
  112. package/packages/server/src/provider-auth-storage.ts +83 -51
  113. package/packages/server/src/provider-catalogue-cache.ts +47 -0
  114. package/packages/server/src/routes/doctor-routes.ts +140 -0
  115. package/packages/server/src/routes/pi-changelog-routes.ts +194 -0
  116. package/packages/server/src/routes/pi-core-routes.ts +1 -1
  117. package/packages/server/src/routes/provider-auth-routes.ts +14 -1
  118. package/packages/server/src/routes/provider-routes.ts +4 -4
  119. package/packages/server/src/routes/system-routes.ts +38 -1
  120. package/packages/server/src/server.ts +85 -66
  121. package/packages/server/src/session-api.ts +54 -3
  122. package/packages/server/src/session-bootstrap.ts +27 -12
  123. package/packages/server/src/session-discovery.ts +11 -4
  124. package/packages/server/src/session-file-reader.ts +1 -1
  125. package/packages/server/src/session-scanner.ts +4 -2
  126. package/packages/server/src/spawn-failure-log.ts +130 -0
  127. package/packages/server/src/spawn-preflight.ts +82 -0
  128. package/packages/server/src/spawn-register-watchdog.ts +291 -0
  129. package/packages/server/src/spawn-token.ts +20 -0
  130. package/packages/server/src/terminal-manager.ts +12 -1
  131. package/packages/shared/package.json +1 -1
  132. package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +25 -17
  133. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +5 -4
  134. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +6 -5
  135. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +1 -0
  136. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +5 -4
  137. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +2 -1
  138. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +6 -3
  139. package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +1 -1
  140. package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +72 -0
  141. package/packages/shared/src/__tests__/browser-protocol-types.test.ts +47 -1
  142. package/packages/shared/src/__tests__/changelog-types.test.ts +78 -0
  143. package/packages/shared/src/__tests__/config.test.ts +48 -0
  144. package/packages/shared/src/__tests__/dashboard-starter.test.ts +40 -0
  145. package/packages/shared/src/__tests__/detached-spawn.test.ts +24 -0
  146. package/packages/shared/src/__tests__/doctor-core.test.ts +134 -0
  147. package/packages/shared/src/__tests__/doctor-fault-tolerance.test.ts +218 -0
  148. package/packages/shared/src/__tests__/doctor-format.test.ts +121 -0
  149. package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +68 -0
  150. package/packages/shared/src/__tests__/install-managed-node.test.ts +192 -0
  151. package/packages/shared/src/__tests__/installable-list.test.ts +130 -0
  152. package/packages/shared/src/__tests__/managed-node-path.test.ts +122 -0
  153. package/packages/shared/src/__tests__/managed-runtime-strategy.test.ts +74 -0
  154. package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +52 -0
  155. package/packages/shared/src/__tests__/no-raw-openspec-status-in-skills.test.ts +6 -1
  156. package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +56 -20
  157. package/packages/shared/src/__tests__/resolve-jiti.test.ts +140 -9
  158. package/packages/shared/src/__tests__/skill-block-parser.test.ts +153 -0
  159. package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +1 -1
  160. package/packages/shared/src/bootstrap-install.ts +197 -3
  161. package/packages/shared/src/browser-protocol.ts +155 -1
  162. package/packages/shared/src/changelog-types.ts +111 -0
  163. package/packages/shared/src/config.ts +15 -0
  164. package/packages/shared/src/dashboard-starter.ts +33 -0
  165. package/packages/shared/src/doctor-core.ts +821 -0
  166. package/packages/shared/src/index.ts +9 -0
  167. package/packages/shared/src/installable-list.ts +152 -0
  168. package/packages/shared/src/launch-source-flag.ts +14 -0
  169. package/packages/shared/src/launch-source-types.ts +18 -0
  170. package/packages/shared/src/openspec-activity-detector.ts +25 -7
  171. package/packages/shared/src/platform/detached-spawn.ts +13 -2
  172. package/packages/shared/src/platform/managed-node-path.ts +77 -0
  173. package/packages/shared/src/platform/node-spawn.ts +29 -21
  174. package/packages/shared/src/protocol.ts +54 -2
  175. package/packages/shared/src/resolve-jiti.ts +62 -9
  176. package/packages/shared/src/rest-api.ts +4 -0
  177. package/packages/shared/src/skill-block-parser.ts +115 -0
  178. package/packages/shared/src/tool-registry/__tests__/managed-runtime-strategy.test.ts +166 -0
  179. package/packages/shared/src/tool-registry/definitions.ts +33 -8
  180. package/packages/shared/src/tool-registry/strategies.ts +42 -0
  181. package/packages/shared/src/types.ts +64 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Synchronous spawn preflight check.
3
+ *
4
+ * Runs before every `spawnPiSession` invocation to catch fast-fail conditions
5
+ * (bad cwd, missing binaries) without racing the spawn itself. All checks run
6
+ * regardless of earlier failures so the caller gets all reasons in one pass.
7
+ *
8
+ * The ToolResolver passed in MUST have `useLoginShell: false` — preflight
9
+ * must never spawn a login shell on the spawn-click hot path. If a resolver
10
+ * with `useLoginShell: true` is passed, the check still runs but a one-time
11
+ * warning is emitted.
12
+ *
13
+ * See change: spawn-failure-diagnostics.
14
+ */
15
+ import { existsSync, accessSync, statSync, constants } from "node:fs";
16
+ import { ToolResolver } from "@blackbelt-technology/pi-dashboard-shared/platform/binary-lookup.js";
17
+
18
+ export interface PreflightReason {
19
+ code: string;
20
+ message: string;
21
+ }
22
+
23
+ export interface PreflightResult {
24
+ ok: boolean;
25
+ reasons: PreflightReason[];
26
+ }
27
+
28
+ /**
29
+ * Run all preflight checks for `cwd` and return the accumulated reasons.
30
+ * `ok` is `true` iff `reasons.length === 0`.
31
+ *
32
+ * @param deps.resolver - Must be constructed with `useLoginShell: false`.
33
+ * If omitted, a login-shell-disabled resolver is created automatically.
34
+ * Passing a resolver with `useLoginShell: true` violates the preflight
35
+ * contract; the function still runs but may call into the login shell.
36
+ */
37
+ export function preflightSpawn(
38
+ cwd: string,
39
+ deps?: { resolver?: ToolResolver },
40
+ ): PreflightResult {
41
+ const resolver = deps?.resolver ?? new ToolResolver({ processExecPath: process.execPath, useLoginShell: false });
42
+
43
+ const reasons: PreflightReason[] = [];
44
+
45
+ // 1. cwd exists
46
+ const cwdExists = existsSync(cwd);
47
+ if (!cwdExists) {
48
+ reasons.push({ code: "DIR_MISSING", message: `Directory does not exist: ${cwd}` });
49
+ // No point checking isDirectory / writable if it doesn't exist.
50
+ } else {
51
+ // 2. cwd is a directory
52
+ try {
53
+ const stat = statSync(cwd);
54
+ if (!stat.isDirectory()) {
55
+ reasons.push({ code: "DIR_NOT_DIRECTORY", message: `Path is not a directory: ${cwd}` });
56
+ }
57
+ } catch (err: any) {
58
+ reasons.push({ code: "DIR_NOT_DIRECTORY", message: `Cannot stat path: ${err.message}` });
59
+ }
60
+
61
+ // 3. cwd is writable
62
+ try {
63
+ accessSync(cwd, constants.W_OK);
64
+ } catch {
65
+ reasons.push({ code: "DIR_NOT_WRITABLE", message: `Directory is not writable: ${cwd}` });
66
+ }
67
+ }
68
+
69
+ // 4. pi resolves
70
+ const piCmd = resolver.resolvePi();
71
+ if (piCmd === null) {
72
+ reasons.push({ code: "PI_NOT_FOUND", message: "pi binary not found via managed install or system PATH" });
73
+ }
74
+
75
+ // 5. node resolves
76
+ const nodeCmd = resolver.resolveNode();
77
+ if (nodeCmd === null) {
78
+ reasons.push({ code: "NODE_NOT_FOUND", message: "node binary not found via managed install or system PATH" });
79
+ }
80
+
81
+ return { ok: reasons.length === 0, reasons };
82
+ }
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Spawn-register watchdog.
3
+ *
4
+ * Arms a per-spawn timer after every successful `spawnPiSession`. If the
5
+ * spawned pi session never sends `session_register` within the timeout
6
+ * window, emits `spawn_register_timeout` to the originating WebSocket.
7
+ *
8
+ * Two index maps handle the two spawn families:
9
+ * - `byPid` — headless spawns where the dashboard owns the PID.
10
+ * - `byCwd` — tmux/wt/wsl-tmux spawns where any `session_register` from
11
+ * that directory clears the watch.
12
+ *
13
+ * Late registrations (pi finally registers after the watchdog fired) are
14
+ * detected via `recentlyFired` (60 s TTL) and cause a `spawn_register_recovered`
15
+ * message to auto-clear the timeout banner.
16
+ *
17
+ * See change: spawn-failure-diagnostics.
18
+ */
19
+ import WebSocket from "ws";
20
+ import { readFileSync } from "node:fs";
21
+ import type { SpawnMechanism } from "@blackbelt-technology/pi-dashboard-shared/platform/spawn-mechanism.js";
22
+ import type {
23
+ SpawnRegisterTimeoutMessage,
24
+ SpawnRegisterRecoveredMessage,
25
+ } from "@blackbelt-technology/pi-dashboard-shared/browser-protocol.js";
26
+ import { clampSpawnRegisterTimeoutMs, loadConfig } from "@blackbelt-technology/pi-dashboard-shared/config.js";
27
+ import { appendSpawnFailure } from "./spawn-failure-log.js";
28
+
29
+ export interface WatchdogArmOptions {
30
+ pid?: number;
31
+ cwd: string;
32
+ mechanism: SpawnMechanism;
33
+ logPath?: string;
34
+ ws: WebSocket;
35
+ /**
36
+ * Server-minted spawn correlation token. When provided, the entry is
37
+ * indexed in `byToken` for strong-identity clearing via `clearByToken`.
38
+ * See change: spawn-correlation-token.
39
+ */
40
+ spawnToken?: string;
41
+ }
42
+
43
+ interface Entry {
44
+ timer: ReturnType<typeof setTimeout>;
45
+ cwd: string;
46
+ pid?: number;
47
+ mechanism: SpawnMechanism;
48
+ logPath?: string;
49
+ ws: WebSocket;
50
+ timeoutMs: number;
51
+ spawnToken?: string;
52
+ }
53
+
54
+ interface RecentlyFiredEntry {
55
+ firedAt: number;
56
+ pid?: number;
57
+ ws: WebSocket;
58
+ spawnToken?: string;
59
+ }
60
+
61
+ const RECENTLY_FIRED_TTL_MS = 60_000;
62
+
63
+ export class SpawnRegisterWatchdog {
64
+ /** Default timeout used when arm() callers do not supply one. */
65
+ readonly timeoutMs: number;
66
+ private readonly byPid = new Map<number, Entry>();
67
+ private readonly byCwd = new Map<string, Entry>();
68
+ private readonly byToken = new Map<string, Entry>();
69
+ private readonly recentlyFired = new Map<string, RecentlyFiredEntry>();
70
+
71
+ constructor(timeoutMs: number) {
72
+ this.timeoutMs = clampSpawnRegisterTimeoutMs(timeoutMs);
73
+ }
74
+
75
+ arm(opts: WatchdogArmOptions & { timeoutMs?: number }): void {
76
+ // Read-on-arm: caller passes the current config value so a Settings change
77
+ // takes effect on the next spawn without a server restart.
78
+ // See change: spawn-failure-diagnostics (fix W1).
79
+ const effectiveTimeout = clampSpawnRegisterTimeoutMs(opts.timeoutMs ?? this.timeoutMs);
80
+ const { pid, cwd, mechanism, logPath, ws, spawnToken } = opts;
81
+ const entry: Entry = {
82
+ timer: null as unknown as ReturnType<typeof setTimeout>,
83
+ cwd, pid, mechanism, logPath, ws,
84
+ timeoutMs: effectiveTimeout,
85
+ spawnToken,
86
+ };
87
+ entry.timer = setTimeout(() => this._fireEntry(entry), effectiveTimeout);
88
+ // Always index by cwd so a `session_register` clears the watchdog even
89
+ // when the bridge's reported pid differs from the spawner's pid (e.g.
90
+ // Unix headless wraps pi in `sh -c "tail -f /dev/null | pi …"`, so
91
+ // spawnResult.pid is the sh wrapper, not pi). Index by pid additionally
92
+ // for late-recovery lookup. Index by token (when provided) for
93
+ // strong-identity clearing. See change: spawn-correlation-token.
94
+ // Replace any prior entry for the same cwd/pid/token to avoid leaking timers.
95
+ const priorCwd = this.byCwd.get(cwd);
96
+ if (priorCwd) clearTimeout(priorCwd.timer);
97
+ this.byCwd.set(cwd, entry);
98
+ if (pid !== undefined) {
99
+ const priorPid = this.byPid.get(pid);
100
+ if (priorPid && priorPid !== priorCwd) clearTimeout(priorPid.timer);
101
+ this.byPid.set(pid, entry);
102
+ }
103
+ if (spawnToken) {
104
+ const priorTok = this.byToken.get(spawnToken);
105
+ if (priorTok && priorTok !== priorCwd && priorTok !== entry) clearTimeout(priorTok.timer);
106
+ this.byToken.set(spawnToken, entry);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Strong-identity clear: cancel watchdog for this exact spawn invocation.
112
+ * Tier 1 of the three-tier match in `event-wiring.ts`. Removes the entry
113
+ * from all three indices. See change: spawn-correlation-token.
114
+ */
115
+ clearByToken(spawnToken: string): void {
116
+ const entry = this.byToken.get(spawnToken);
117
+ if (entry) {
118
+ clearTimeout(entry.timer);
119
+ this.byToken.delete(spawnToken);
120
+ const cwdEntry = this.byCwd.get(entry.cwd);
121
+ if (cwdEntry === entry) this.byCwd.delete(entry.cwd);
122
+ if (entry.pid !== undefined) {
123
+ const pidEntry = this.byPid.get(entry.pid);
124
+ if (pidEntry === entry) this.byPid.delete(entry.pid);
125
+ }
126
+ return;
127
+ }
128
+ // Check for late recovery: scan recentlyFired for matching token.
129
+ for (const [cwd, fired] of this.recentlyFired) {
130
+ if (fired.spawnToken === spawnToken) {
131
+ this._emitRecovery(cwd, fired);
132
+ return;
133
+ }
134
+ }
135
+ }
136
+
137
+ clearByPid(pid: number): void {
138
+ const entry = this.byPid.get(pid);
139
+ if (entry) {
140
+ clearTimeout(entry.timer);
141
+ this.byPid.delete(pid);
142
+ // Also clear cwd / token entries if they point at the same arm.
143
+ const cwdEntry = this.byCwd.get(entry.cwd);
144
+ if (cwdEntry === entry) this.byCwd.delete(entry.cwd);
145
+ if (entry.spawnToken) {
146
+ const tokEntry = this.byToken.get(entry.spawnToken);
147
+ if (tokEntry === entry) this.byToken.delete(entry.spawnToken);
148
+ }
149
+ return;
150
+ }
151
+ // Check for late recovery.
152
+ this._checkRecoveryByPid(pid);
153
+ }
154
+
155
+ clearByCwd(cwd: string): void {
156
+ const entry = this.byCwd.get(cwd);
157
+ if (entry) {
158
+ clearTimeout(entry.timer);
159
+ this.byCwd.delete(cwd);
160
+ // Also clear pid / token entries if they point at the same arm.
161
+ if (entry.pid !== undefined) {
162
+ const pidEntry = this.byPid.get(entry.pid);
163
+ if (pidEntry === entry) this.byPid.delete(entry.pid);
164
+ }
165
+ if (entry.spawnToken) {
166
+ const tokEntry = this.byToken.get(entry.spawnToken);
167
+ if (tokEntry === entry) this.byToken.delete(entry.spawnToken);
168
+ }
169
+ return;
170
+ }
171
+ // Check for late recovery.
172
+ this._checkRecoveryByCwd(cwd);
173
+ }
174
+
175
+ private _fireEntry(entry: Entry): void {
176
+ const { cwd, pid, logPath, ws, timeoutMs: entryTimeoutMs } = entry;
177
+ // Remove from active maps.
178
+ if (pid !== undefined) {
179
+ const pidEntry = this.byPid.get(pid);
180
+ if (pidEntry === entry) this.byPid.delete(pid);
181
+ }
182
+ const cwdEntry = this.byCwd.get(cwd);
183
+ if (cwdEntry === entry) this.byCwd.delete(cwd);
184
+
185
+ // Record in recentlyFired for late-recovery detection (also drop token entry).
186
+ if (entry.spawnToken) {
187
+ const tokEntry = this.byToken.get(entry.spawnToken);
188
+ if (tokEntry === entry) this.byToken.delete(entry.spawnToken);
189
+ }
190
+ this.recentlyFired.set(cwd, { firedAt: Date.now(), pid, ws, spawnToken: entry.spawnToken });
191
+
192
+ // Read stderr tail if logPath available.
193
+ let stderrTail: string | undefined;
194
+ if (logPath) {
195
+ stderrTail = readLogTail(logPath);
196
+ }
197
+
198
+ // Persist the timeout to the rolling failure log. See change: spawn-failure-diagnostics.
199
+ appendSpawnFailure({
200
+ ts: new Date().toISOString(),
201
+ cwd,
202
+ strategy: entry.mechanism,
203
+ code: "REGISTER_TIMEOUT",
204
+ message: `Pi session spawned but never registered (timeout ${this.timeoutMs}ms)`,
205
+ ...(pid !== undefined ? { pid } : {}),
206
+ ...(stderrTail ? { stderrTail } : {}),
207
+ });
208
+
209
+ if (ws.readyState !== WebSocket.OPEN) return;
210
+
211
+ const msg: SpawnRegisterTimeoutMessage = {
212
+ type: "spawn_register_timeout",
213
+ cwd,
214
+ timeoutMs: entryTimeoutMs,
215
+ ...(pid !== undefined ? { pid } : {}),
216
+ ...(stderrTail ? { stderrTail } : {}),
217
+ };
218
+ ws.send(JSON.stringify(msg));
219
+ }
220
+
221
+ private _checkRecoveryByPid(pid: number): void {
222
+ // recentlyFired is keyed by cwd; scan to find matching pid.
223
+ for (const [cwd, fired] of this.recentlyFired) {
224
+ if (fired.pid === pid) {
225
+ this._emitRecovery(cwd, fired);
226
+ return;
227
+ }
228
+ }
229
+ }
230
+
231
+ private _checkRecoveryByCwd(cwd: string): void {
232
+ const fired = this.recentlyFired.get(cwd);
233
+ if (!fired) return;
234
+ this._emitRecovery(cwd, fired);
235
+ }
236
+
237
+ private _emitRecovery(cwd: string, fired: RecentlyFiredEntry): void {
238
+ // TTL check.
239
+ if (Date.now() - fired.firedAt > RECENTLY_FIRED_TTL_MS) {
240
+ this.recentlyFired.delete(cwd);
241
+ return;
242
+ }
243
+
244
+ this.recentlyFired.delete(cwd);
245
+
246
+ if (fired.ws.readyState !== WebSocket.OPEN) return;
247
+
248
+ const msg: SpawnRegisterRecoveredMessage = {
249
+ type: "spawn_register_recovered",
250
+ cwd,
251
+ ...(fired.pid !== undefined ? { pid: fired.pid } : {}),
252
+ };
253
+ fired.ws.send(JSON.stringify(msg));
254
+ }
255
+ }
256
+
257
+ // ── Singleton ────────────────────────────────────────────────────────────────
258
+
259
+ let _instance: SpawnRegisterWatchdog | null = null;
260
+
261
+ /**
262
+ * Lazy singleton. On first call, reads `spawnRegisterTimeoutMs` from config.
263
+ * Tests can swap the instance via `_setSpawnRegisterWatchdogForTests`.
264
+ */
265
+ export function getSpawnRegisterWatchdog(): SpawnRegisterWatchdog {
266
+ if (!_instance) {
267
+ const config = loadConfig();
268
+ _instance = new SpawnRegisterWatchdog(config.spawnRegisterTimeoutMs);
269
+ }
270
+ return _instance;
271
+ }
272
+
273
+ /** Swap the singleton for tests. Pass `null` to reset. */
274
+ export function _setSpawnRegisterWatchdogForTests(w: SpawnRegisterWatchdog | null): void {
275
+ _instance = w;
276
+ }
277
+
278
+ // ── Helpers ──────────────────────────────────────────────────────────────────
279
+
280
+ function readLogTail(filePath: string, maxBytes = 4096): string | undefined {
281
+ try {
282
+ const buf = readFileSync(filePath);
283
+ if (!buf.length) return undefined;
284
+ const slice = buf.length <= maxBytes ? buf : buf.slice(buf.length - maxBytes);
285
+ let start = 0;
286
+ while (start < slice.length && (slice[start]! & 0xC0) === 0x80) start++;
287
+ return slice.slice(start).toString("utf-8");
288
+ } catch {
289
+ return undefined;
290
+ }
291
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Spawn correlation token: server-minted UUIDv4 per `spawnPiSession` call.
3
+ *
4
+ * Flow: `mintSpawnToken()` → injected into spawned process env as
5
+ * `PI_DASHBOARD_SPAWN_TOKEN` → bridge reads it → bridge echoes back in
6
+ * `session_register.spawnToken` → server links via `linkByToken`.
7
+ *
8
+ * In-memory only; no persistence. TTL aligned to spawn-register-watchdog.
9
+ *
10
+ * See change: spawn-correlation-token.
11
+ */
12
+ import { randomUUID } from "node:crypto";
13
+
14
+ /** Mint a fresh UUIDv4 spawn correlation token. */
15
+ export function mintSpawnToken(): string {
16
+ return randomUUID();
17
+ }
18
+
19
+ /** Env-var name used for the correlation token in spawned pi processes. */
20
+ export const SPAWN_TOKEN_ENV_VAR = "PI_DASHBOARD_SPAWN_TOKEN";
@@ -185,7 +185,18 @@ export function createTerminalManager(options?: TerminalManagerOptions): Termina
185
185
  try {
186
186
  const msg: TerminalControlMessage = JSON.parse(str);
187
187
  if (msg.type === "resize") {
188
- entry.pty.resize(msg.cols, msg.rows);
188
+ // Defense in depth: reject degenerate resize messages.
189
+ // A PTY at <2 cols/rows is non-functional for every supported
190
+ // shell binding; no legitimate user intent maps there. xterm's
191
+ // FitAddon is supposed to guard against zero, but a transient
192
+ // display:none container measured during a route transition
193
+ // can leak a 1 through. See change:
194
+ // fix-terminal-half-height-dual-mount.
195
+ if (msg.cols < 2 || msg.rows < 2) {
196
+ // ignore — keep previous PTY dimensions
197
+ } else {
198
+ entry.pty.resize(msg.cols, msg.rows);
199
+ }
189
200
  } else if (msg.type === "title") {
190
201
  // title control message — handled elsewhere
191
202
  } else {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackbelt-technology/pi-dashboard-shared",
3
- "version": "0.4.6",
3
+ "version": "0.5.1",
4
4
  "description": "Shared types and utilities for pi-dashboard",
5
5
  "type": "module",
6
6
  "repository": {
@@ -29,12 +29,12 @@ source: —
29
29
  path: —
30
30
  tried:
31
31
  override no override set
32
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
32
33
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
33
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
34
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
34
35
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
35
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
36
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
36
37
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
37
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
38
38
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
39
39
  where not found on PATH"
40
40
  `;
@@ -66,8 +66,9 @@ source: managed
66
66
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
67
67
  tried:
68
68
  override no override set
69
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
69
70
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
70
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
71
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
71
72
  managed ok"
72
73
  `;
73
74
 
@@ -111,12 +112,12 @@ source: —
111
112
  path: —
112
113
  tried:
113
114
  override no override set
115
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
114
116
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
115
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
117
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
116
118
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
117
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
119
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
118
120
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
119
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
120
121
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
121
122
  where not found on PATH"
122
123
  `;
@@ -150,10 +151,11 @@ source: npm-global
150
151
  path: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
151
152
  tried:
152
153
  override no override set
154
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
153
155
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
154
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
156
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
155
157
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
156
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
158
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
157
159
  npm-global ok
158
160
  argv:
159
161
  - C:/Program Files/nodejs/node.exe
@@ -199,6 +201,7 @@ source: bare-import
199
201
  path: dist/cli.js
200
202
  tried:
201
203
  override no override set
204
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
202
205
  bare-import ok"
203
206
  `;
204
207
 
@@ -248,8 +251,9 @@ source: managed
248
251
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
249
252
  tried:
250
253
  override no override set
254
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
251
255
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
252
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
256
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
253
257
  managed ok"
254
258
  `;
255
259
 
@@ -271,12 +275,12 @@ source: —
271
275
  path: —
272
276
  tried:
273
277
  override no override set
278
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
274
279
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
275
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
280
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
276
281
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
277
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
282
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
278
283
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
279
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
280
284
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
281
285
  where not found on PATH"
282
286
  `;
@@ -288,8 +292,9 @@ source: managed
288
292
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
289
293
  tried:
290
294
  override no override set
295
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
291
296
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
292
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
297
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
293
298
  managed ok"
294
299
  `;
295
300
 
@@ -320,8 +325,9 @@ source: managed
320
325
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
321
326
  tried:
322
327
  override no override set
328
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
323
329
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
324
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
330
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
325
331
  managed ok
326
332
  argv:
327
333
  - C:/Program Files/nodejs/node.exe
@@ -335,10 +341,11 @@ source: npm-global
335
341
  path: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
336
342
  tried:
337
343
  override no override set
344
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
338
345
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
339
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
346
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
340
347
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
341
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
348
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
342
349
  npm-global ok
343
350
  argv:
344
351
  - C:/Program Files/nodejs/node.exe
@@ -352,6 +359,7 @@ source: system
352
359
  path: C:/Program Files/nodejs/node.exe
353
360
  tried:
354
361
  override no override set
362
+ managed missing: <HOME>/.pi-dashboard/node/node.exe
355
363
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/node.cmd
356
364
  where ok
357
365
  argv:
@@ -29,12 +29,12 @@ source: —
29
29
  path: —
30
30
  tried:
31
31
  override no override set
32
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
32
33
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
33
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
34
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
34
35
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
35
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
36
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
36
37
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
37
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
38
38
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
39
39
  where not found on PATH"
40
40
  `;
@@ -66,8 +66,9 @@ source: managed
66
66
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
67
67
  tried:
68
68
  override no override set
69
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
69
70
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
70
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
71
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
71
72
  managed ok"
72
73
  `;
73
74
 
@@ -29,12 +29,12 @@ source: —
29
29
  path: —
30
30
  tried:
31
31
  override no override set
32
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
32
33
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
33
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
34
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
34
35
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
35
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
36
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
36
37
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
37
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
38
38
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
39
39
  where not found on PATH"
40
40
  `;
@@ -68,10 +68,11 @@ source: npm-global
68
68
  path: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
69
69
  tried:
70
70
  override no override set
71
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
71
72
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
72
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
73
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
73
74
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
74
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
75
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
75
76
  npm-global ok
76
77
  argv:
77
78
  - C:/Program Files/nodejs/node.exe
@@ -29,5 +29,6 @@ source: bare-import
29
29
  path: dist/cli.js
30
30
  tried:
31
31
  override no override set
32
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
32
33
  bare-import ok"
33
34
  `;
@@ -27,8 +27,9 @@ source: managed
27
27
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
28
28
  tried:
29
29
  override no override set
30
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
30
31
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
31
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
32
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
32
33
  managed ok"
33
34
  `;
34
35
 
@@ -50,12 +51,12 @@ source: —
50
51
  path: —
51
52
  tried:
52
53
  override no override set
54
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
53
55
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
54
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
56
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
55
57
  managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
56
- managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
58
+ npm-global missing: <NPM_ROOT>/@earendil-works/pi-coding-agent/dist/cli.js
57
59
  npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
58
- npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
59
60
  managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
60
61
  where not found on PATH"
61
62
  `;
@@ -7,8 +7,9 @@ source: managed
7
7
  path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
8
8
  tried:
9
9
  override no override set
10
+ bare-import cannot resolve @earendil-works/pi-coding-agent/package.json
10
11
  bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
11
- bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
12
+ managed missing: <HOME>/.pi-dashboard/node_modules/@earendil-works/pi-coding-agent/dist/cli.js
12
13
  managed ok"
13
14
  `;
14
15