fix(host-service): detect ports for terminals without an attached renderer
Problem
Ports were detected less completely in v2 than in v1. Both use the same shared @superset/port-scanner PortManager (2.5s periodic scan of registered terminal sessions, keyed by PID tree), so the gap was in what gets registered:
- v1 (desktop
daemon-manager.ts→reconcileOnStartup) pre-registers every alive daemon session withportManager.upsertSession(...)at startup, before any renderer attaches. - v2 host-service only called
upsertSessioninsidecreateTerminalSessionInternal— i.e. only when a renderer creates or attaches to a terminal. Terminals the daemon is running but nobody has opened (notably sessions adopted after a host-service restart) were never scanned, so their ports never appeared in the v2 sidebar.
Fix
Fold a port-scan sync into the existing terminal reaper (reaper.ts) instead of adding a parallel module. The reaper already calls daemon.list() and joins terminalSessions (for terminalId → workspaceId) on the same pass, immediately at startup and on its interval — so there's no extra daemon call, DB query, or timer.
planPortScanSync()— pure decision function:- register every alive daemon session that maps to an active workspace row and isn't already owned by a live in-memory session;
- unregister every watched terminal the daemon no longer reports and no live in-memory session owns (adopted sessions never get the daemon exit subscription that normally unregisters them).
applyPortScanSync()applies the plan toportManageron each reaper pass, before the empty-list short-circuit so an empty daemon clears stale scans.- Supporting helpers:
PortManager.getRegisteredTerminalIds()andterminal.ts isLiveTerminalSession()(the latter guards a renderer-attached session against being dropped during a racydaemon.list()).
The terminalId → workspaceId join is irreducible: daemon.list() returns {id, pid, alive} only, and the sidebar groups ports by workspace — that mapping lives in the host DB, which the reaper already reads.
End-to-end
- On start:
eventBus.start()(increateApp) wiresportManager→port:changedbroadcast; the reaper's immediate first pass registers live sessions → periodic scan begins. Bridge is wired before registration, so no early events drop. - Renderer reads:
ports.getAllreturnsportManager.getAllPorts(), now populated for unattached terminals. - Renderer subscribes: the event-bus
port:changedstream delivers live add/remove for all live terminals, attached or not.
Notes
- Self-heal cleanup of adopted-but-exited registrations rides the reaper's 5-min cadence; their ports still vanish within 2.5s via the empty-tree scan, only the no-op registration lingers. Attached terminals unregister instantly on the daemon exit event.
Testing
- New
reaper.test.tscoversplanPortScanSync(register/skip/unregister/empty-daemon/race-guard); verified each case fails under impl mutation. bun run lintexits 0; typecheck passes for@superset/host-serviceand@superset/port-scanner; existingport-scannertests pass.
Summary by cubic
Fixes missing port detection for terminals running without an attached renderer by syncing the port scanner with daemon sessions during reaper passes. Ports now appear for adopted/live terminals right after start and stay accurate.
- Bug Fixes
- Sync
portManagerwith alive daemon sessions on each reaper pass: register mapped active rows not owned by a live in-memory session; unregister watched terminals the daemon no longer reports; guard renderer-attached sessions viaisLiveTerminalSession. - Skip the DB join when the daemon reports no live sessions while still dropping stale scans; isolate the sync in try/catch so reaper cleanup can’t be blocked.
- Added
PortManager.getRegisteredTerminalIds(),planPortScanSync, and tests.
- Sync
Summary by CodeRabbit
-
New Features
- Improved port scan management by reconciling with the daemon’s active terminal sessions: newly live terminals are registered, stale ones are unregistered, and previously adopted scans are cleared when no live sessions are reported.
- Added a helper to treat only non-exited in-memory terminal sessions as “live” for reconciliation.
- Exposed a method to retrieve currently registered terminal IDs for port scanning.
-
Tests
- Added Bun coverage for port-scan synchronization scenarios, including adoption, skipping owned sessions, handling missing/inactive rows, unregistering removed terminals, clearing when none are live, and temporary daemon absence.