AGENTS.md@packages/electron/src/lib · git:20260805.54c32e5 · 2026-08-05 · sha256 94423c795024213e
AGENTS.md@packages/electron/src/lib git:20260805.54c32e5A
Immutable. This exact content is served forever at /api/v1/blob/94423c795024213e.
# DOX — packages/electron/src/lib
Files in this directory. One row per source file.
| File | Purpose |
|------|---------|
| `app-menu.ts` | App menu with About dialog + Doctor on all platforms. Doctor menu entry opens `openDoctorWindow()` (replaces native `dialog.showMessageBox`). About dialog shows bundled-git GPLv2 license link (win32). See change: embed-git-bash-on-windows. Adds "Check for Updates…" (hidden in dev) + "View Update Log" items. See change: fix-electron-auto-update-pipeline. Adds "Connect to Remote Dashboard…" → `openRemoteConnectWindow()` + "Use Local Dashboard" → `useLocalDashboard()` (replaces the deleted wizard's remote-attach path). See change: auto-launch-first-run-skip-welcome. |
| `app-updater.ts` | electron-updater wrapper. `initAutoUpdater()` 60s + 24h check, dev-skip. `autoDownload=false`. `classifyUpdateError()` severity debug/warn/error. `logUpdate()` → `app.getPath('logs')/electron-main.log`. `checkForUpdatesNow()` → ManualCheckResult. `getUpdateLogPath()`. `downloadAndInstall()`/`quitAndInstall()`. `__setTestAutoUpdater()` test seam. See change: fix-electron-auto-update-pipeline. |
| `bridge-register.ts` | Registers bundled bridge extension into `~/.pi/agent/settings.json`. Exports `registerBundledBridgeExtension()`. `findBundledExtension()` locates extension dir via `process.resourcesPath` (packaged) or dev path; rejects AppImage `/tmp/.mount_` paths. Delegates to shared `registerBridgeExtension`. |
| `build-version.ts` | Windows-safe build version helper. Exports `deriveWindowsBuildVersion(pkgVersion, runNumber)` → 4-integer `MAJOR.MINOR.PATCH.RUN_NUMBER` for PE VERSIONINFO resource (packager `resedit.js` rejects SemVer prerelease slugs). Pure function, env reads in caller. |
| `bundled-node.ts` | Resolves bundled Node.js/npm paths in Electron resources |
| `dependency-detector.ts` | Detects pi, openspec, Node.js on system PATH + managed install. **AppImage self-recursion guard** (change: fix-electron-appimage-cli-self-detection): `detectPiDashboardCli` rejects candidates matching `isAppImageSelfHit(path)` (plus existing `_npx` filter) so power-user mode falls through to standalone tsx + `cli.ts` when only `pi-dashboard` on PATH is AppImage's own launcher (`packagerConfig.executableName: "pi-dashboard"` collides by design). `detectPi` + `detectSystemNode` apply same guard symmetrically on registry-resolved path — belt-and-braces beyond `whereStrategy` filter. **Windows extension filter** (change: fix-electron-windows-installer-and-server-bootstrap, Defect 3): pure helper `pickSpawnableShim(rawWhereOutput, platform)` on `win32` prefers candidates ending `.cmd`/`.exe`/`.bat`/`.ps1` over extensionless POSIX shim from npm-global. `spawn()` without `shell:true` cannot invoke extensionless shim on Windows, so pre-fix `lines[0]` produced `ENOENT`. POSIX (single-line `which`) unchanged. Locked by `dependency-detector-windows-extensions.test.ts`. `detectSystemNode`/`scanForUsableNodeOnDisk` gate on shared `isUsableNodeVersion`; removed drifted inline `isVersionAffected`; doctor reports system Node usable iff server accepts (22.19+, 24, 25). See change: unify-node-version-gate. |
| `doctor-bridge-contract.ts` | `DoctorBridge` TypeScript interface. Imported by preload + renderer. Channel-name-drift breaks type-check. |
| `doctor-window.ts` | `openDoctorWindow()` factory. BrowserWindow 1000×720. Single-instance focus-reuse. Registers IPC handlers `doctor:run` (concurrency-serialized), `doctor:open-log`, `doctor:open-doctor-log` (returns `{exists:false}` when absent), `doctor:copy`, `doctor:open-managed-dir`. `doctor:run-setup` + "Run setup wizard" button removed with the wizard; reset-to-local now via app-menu "Use Local Dashboard". See change: auto-launch-first-run-skip-welcome. Wires `closed` → null reference. |
| `doctor.ts` | Electron-only doctor checks (Electron version, bundled Node, bundled npm, server-code path, server-launch sanity test). Delegates portable checks to `packages/shared/src/doctor-core.ts:runSharedChecks`. Legacy `~/.pi-dashboard/` advisory now owned by shared `runSharedChecks` — duplicate emission block removed. Reinstall affordance + audit panel + safe-wipe removed. Routes spawns through `safeExec` (`timeoutMs: 15000`). See changes: eliminate-electron-runtime-install, fix-doctor-stale-managed-install-check. Exports pure helper `buildServerLaunchTestCmd({nodeBin,jitiUrl,testCli})` building Server launch test probe via `pathToFileURL(testCli).href` (`file://` URL, Windows-safe). See change: fix-doctor-windows-launch-test. |
| `ensure-windows-path.ts` | `ensureWindowsSystemPath(env = process.env)` — prepends `%SystemRoot%\System32`, `%SystemRoot%`, `%SystemRoot%\System32\Wbem`, `%SystemRoot%\System32\WindowsPowerShell\v1.0`, `%APPDATA%\npm`, + Git-for-Windows install dirs; pins `env.ComSpec`. No-op on POSIX. Idempotent. Called at `main.ts` startup. See change: ensure-windows-system-path. |
| `health-check.ts` | Thin re-export of `@blackbelt-technology/pi-dashboard-shared/server-identity.js` (`isDashboardRunning`, `DashboardStatus`, `DashboardCheckOpts`). Collapsed from duplicate impl. See change: harvest-bootstrap-survivor-fixes. |
| `launch-source.ts` | `selectLaunchSource()` resolver. 3 strategies: `attach` (health probe) → `devMonorepo` (`!app.isPackaged AND existsSync(cwd/packages/server/src/cli.ts)`) → `bundled` (`<resourcesPath>/server/node_modules/@blackbelt-technology/pi-dashboard-server/src/cli.ts`). `BundledServerMissingError` on missing. Pre-R3 strategies (`piExtension`, `npmGlobal`, `extracted`) removed. `parsePreferOverride` accepts `attach \| bundled \| devMonorepo`. `getBundledCliPath(resourcesPath)` returns fixed bundled path. See change: eliminate-electron-runtime-install. Canonical home of `SERVER_READY_DEADLINE_MS = 15_000` + new `SERVER_READY_DEADLINE_DEV_MS = 60_000` + `getServerReadyDeadlineMs(sourceKind)` (returns dev value for `"devMonorepo"`, prod otherwise). `spawnFromSource` passes `healthTimeoutMs: getServerReadyDeadlineMs(source.kind)` instead of hard-coded 15s; dev-monorepo jiti cold boot of ~400 TS files reaches 25–60s. See change: fix-mode-aware-server-ready-deadlines. Bundled-Node-dir contract: uses `getBundledNodeDir()` for PATH prepend; never `path.dirname`-chain on `getBundledNodePath()`. dirname-chain yields wrong dir on Windows (parent of `node.exe` ≠ POSIX `bin/` parent) — works by accident on POSIX, breaks npm-shim resolution on Win. See change: fix-electron-launch-source-bundled-node-dir. |
| `link-handling.ts` | Pure helpers used by Electron shell. **`isSameOriginUrl(href, serverOrigin)`** — classifies URLs as same-origin vs external (handles absolute, relative, fragment-only, malformed inputs; see change: harden-external-link-handling, #13). **`decideWillNavigate(serverOrigin, currentUrl, targetUrl) → "allow" | "open-external" | "cancel"`** — OAuth-aware decision wrapper for `will-navigate` callback: while BrowserWindow shows non-dashboard origin (mid-OAuth) returns `"allow"` so provider login flows proceed; on dashboard composes `isSameOriginUrl` to decide between `"allow"` (same-origin) + `"open-external"` (external trap-guard); fail-closes to `"cancel"` on unparseable `serverOrigin` (see change: fix-oauth-blocked-by-external-link-guard). No Electron imports; 23 unit tests in `packages/electron/src/__tests__/link-handling.test.ts`. |
| `lock-metadata.ts` | Read per-HOME dashboard lock sidecar. Exports `DashboardLockMetadata` interface, `getLockMetaPath()`, `readDashboardLockMetadata()` (null on missing/corrupt). Inlined `canonicalHomedir()` mirrors `packages/server/src/lifecycle/home-lock.ts` (`os.userInfo().homedir` + `realpathSync`). |
| `managed-paths.ts` | Managed install paths, mirrored locally (no shared-pkg import for ESM resolution in packaged app). Exports `MANAGED_DIR` (`~/.pi-dashboard`), `MANAGED_BIN` (`.bin`), `PI_SETTINGS_PATH` (`~/.pi/agent/settings.json`). |
| `pick-node.ts` | `pickNodeForServer(PickNodeInput): PickNodeResult` — discriminated union `{kind:"bundled"\|"execpath-fallback"}`. 2 strategies: bundled `Resources/node/bin/node` (POSIX) / `Resources/node/node.exe` (Win) → `process.execPath` + `ELECTRON_RUN_AS_NODE=1`. Fallback is corrupted-install signal, not normal mode. nodejs/node#58515 skip logic removed; bundled Node always pinned safe. See change: eliminate-electron-runtime-install. |
| `remote-probe.ts` | Main-process remote-dashboard reachability probe, extracted from `remote-connect-window.ts` so `main.ts` can back the `dashboard:probe-server` IPC without importing the BrowserWindow module. `probeRemote(url)` (Node fetch `${url}/api/health`, 5s timeout, no browser Origin → not CORS-bound; ok/`HTTP <n>`/`Timed out`/`Connection refused`), `normalizeRemoteUrl(input)`, `RemoteProbeResult`. `remote-connect-window.ts` re-exports both for back-compat. See change: fix-remote-connect-cors-gates. |
| `resolve-cdp-activation.ts` | Pure `resolveCdpActivation(argv, env): {enabled, port?}`. Parses `--debug-cdp[=<port>]` flag + `PI_DEBUG_CDP` env. CLI flag wins. Default port 9222. Invalid port → falls back to default (still enabled). Never returns address-binding info. See change: ship-browser-skill-and-electron-cdp. |
| `server-error-classification.ts` | Pure `isDeadlineOrChildExitError(message)` returns true for messages starting with "Server did not respond within" or "Server child process exited prematurely". Drives main.ts routing: terminal errors → Setup/Retry/Quit dialog; deadline/child-exit → fall through to loading page. Drift-guarded against `buildServerStartupError` prefixes. See change: tighten-electron-server-startup-deadline. |
| `server-lifecycle.ts` | Thin `selectLaunchSource + spawnFromSource` shim under R3. Exports `requestServerLaunch({force?})` (idempotent, shared inflight Promise, failure-as-value), `isManagedServerRunning()`, `readServerLogTail(lines)`, `onLaunchStatus(cb)`, `SERVER_READY_DEADLINE_MS = 15_000` (now re-exported from `./launch-source.js`; canonical home moved), `SERVER_READY_DEADLINE_DEV_MS = 60_000` (re-export), `getServerReadyDeadlineMs(sourceKind)` (re-export), `buildServerStartupError`, `setGracefulShutdownInProgress`, `isGracefulShutdownInProgress`, `makeServerWatchdog`, `setSpawnedPid`, `decideShutdownOnQuit`. `readServerLogTail` reads `getDashboardServerLogPath()`. See change: eliminate-electron-runtime-install. ensureServer() returns remoteUrl early in remote mode; no probe/spawn; serverStartedByUs stays false. See change: docker-packaging. Adds pure ownership classifiers + accessor: `decideOwnership({healthLaunchSource,healthPid,storedSpawnedPid})`→`electron\|foreign\|none` (consumes `launchSourceEffective`); `decideIsZombie({...,healthBootParentAlive,platform})`→bool (platform-branched: POSIX `ppid!==bootParentPid && !bootParentAlive`, win32 `!bootParentAlive`; all liveness precomputed server-side, no injected isPidAlive); `getStoredSpawnedPid()` read-only accessor; `HealthLaunchSourceEffective` local type. See change: electron-attach-ownership-fixes. `stopServerIfNeeded` posts `/api/shutdown` with `USER_QUIT_SHUTDOWN_INIT` (`{userQuit:true}`) so a user quit is recorded as `user-quit` (recovery allowed, liveness decides) rather than the reload-shaped `shutdown` (recovery suppressed). See change: fix-recovery-exit-intent. |
| `tray.ts` | System tray with platform-specific icons (template on macOS, ico/png on Win/Linux). See change: electron-server-launch-controls. Ownership-aware menu: pure `buildTrayMenuTemplate({ownership, onLaunch, onShow, onQuit})` (exported for tests) keyed on `TrayOwnership` (`electron\|foreign\|none\|unknown`) — `electron`→Restart, `none`→Start, `foreign`→disabled "Server managed externally" row, `unknown`→omit. `createTray` hook takes `getServerOwnership(): Promise<TrayOwnership>` polled every 3s (rebuild on change via `lastOwnership`). See change: electron-attach-ownership-fixes. |
| `update-checker.ts` | Checks outdated pi/openspec versions. Exports `OutdatedPackage`, `checkOutdated(starter)` (Standalone→global, Electron/unknown→managed `~/.pi-dashboard`), `updatePackage()`, `startUpdateChecker()` (30s initial + 24h interval). Starter from `/api/health`; Bridge skips. Delegates npm calls to shared `platform/npm.ts`. |
| `update-notifier.ts` | Native OS update notifications via Electron `Notification`. Exports `notifyUpdatesAvailable(packages, starter)` — shows once per cycle, "Update" action triggers `updatePackage` loop + confirm dialog. `dismissed` flag resets after 24h. |
| `window-state.ts` | Persists BrowserWindow size/position to `~/.pi/dashboard/window-state.json`. Exports `WindowState` interface, `loadWindowState()` (clamps off-screen coords via `isPointOnAnyDisplay`), `saveWindowState(win)`. `migrateLegacyStateFile()` moves legacy `~/.pi-dashboard/window-state.json` one-shot. |
| `remote-connect-window.ts` | `openRemoteConnectWindow()` factory (520×640) + IPC for `remote-connect.html`. Handlers `remote-connect:get-state`/`probe`/`connect`/`use-local`/`forget`/`close`. `connect` writes `dashboard-settings.json` remote mode + `addRecentRemote` + `app.relaunch()`; `use-local` resets to standalone. `probeRemote` + `normalizeRemoteUrl` now defined in `remote-probe.ts` and re-exported here (back-compat). `useLocalDashboard()` exported. Replaces the deleted wizard's remote-attach path (CONTRACT #3/#6 preserved). See change: auto-launch-first-run-skip-welcome, fix-remote-connect-cors-gates. |
| `wizard-state.ts` | Dashboard settings persistence: `~/.pi-dashboard/dashboard-settings.json` (renamed from `mode.json`; `readModeFile` migrates legacy on read). `DashboardSettings` (alias `ModeConfig`) = mode + remoteUrl + `recentRemotes[]` (MRU cap 8). `readModeFile`/`writeModeFile` (names kept), `listRecentRemotes`/`addRecentRemote`/`removeRecentRemote`, `isFirstRun`, API-key + recommended-state helpers. See change: auto-launch-first-run-skip-welcome, docker-packaging. |
| `zombie-adoption-dialog.ts` | Zombie-server adoption modal + stop sequence. `promptZombieAdoption({pid}): Promise<"adopt"\|"leave"\|"stop">` (Electron `dialog.showMessageBox`, 3 buttons, default+cancel = "leave"). `stopZombieServer(pid, {kill,isRunning,sleep,timeoutMs?,pollMs?})` — SIGTERM→poll→SIGKILL, injectable deps for unit tests. Wired in `main.ts` attach arm behind `decideIsZombie` + `--no-zombie-prompt` switch + in-memory `zombieAskedThisSession`. See change: electron-attach-ownership-fixes. |