Commit Graph

9 Commits

Author SHA1 Message Date
kshitijk4poor 9b199246e5 fix(desktop): cancelAndWait resolves only once the composed drain settles
Every caller tears the SSH connection down right after `await
cancelAndWait(scope)`, so resolving on this call's own barrier alone let a
connection-apply teardown overlap the pool stop's still-running afterStop
teardown for the same key. Wait for the composed barrier instead; chain the
barriers (drain promises never reject, so this equals allSettled). The test
now waits a macrotask and asserts that neither the apply nor the new
bootstrap runs before the first teardown completes.
2026-09-14 20:22:49 +05:30
kshitijk4poor 2aca48b416 fix(desktop): same-scope SSH drains compose instead of replacing the barrier
A pool stop blocked in teardownSshConnection() holds drains[scope]; a
concurrent connection apply calling cancelAndWait() for the same scope
replaced that barrier with its own and, having nothing to drain, cleared the
map entry as soon as it finished. start() then saw no drain and began a new
bootstrap while the first SSH teardown was still running.

cancelAndWait() now composes with the drain already in flight and the entry
is cleared only when the composed barrier settles, so start() waits for
every active teardown. Regression test reproduces the pool-stop / apply
race; it fails on the previous coordinator.

Reported by ehz0ah on #110025 (follow-up to #106935).
2026-09-14 20:22:49 +05:30
KoNit-K 134c5226af fix(desktop): hold the pool-stop fence through SSH teardown
Process-less SSH entries finish child exit immediately. Keep inFlight
and the bootstrap drain up until keepalive teardown completes, and
stop reading main.ts from tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 19:56:15 +05:30
Teknium 65335549a6 feat(desktop): wire managed SSH update engine into main process
Re-implements the #93042 main.ts wiring against current main (post-#94724
drift), making the extracted managed-ssh-update engine reachable:

- ManagedConnectionUpdateGate instance + owner-only recovery journal at
  DESKTOP_MANAGED_SSH_RECOVERY_PATH (read/write/persist/mark/clear with
  strict record validation).
- IPC: hermes:connections:update-managed (requestManagedSshUpdate with
  correlation-id claim + in-flight dedupe); update-all's ssh rows now route
  through the transactional drain/update/restore lifecycle instead of
  POSTing the remote backend updater.
- Gate enforcement at every dial/mutate seam: bootstrapSshConnectionInner
  (pre-dial + publication fence with exact-serve rollback via
  rollbackSshBootstrapResult), resolveRemoteBackend, ensureRegistryBackend,
  saveRegistryConnection dial-field edits, connections:remove, and
  primary-routing mutations (set-primary, set-launch-mode,
  connection-config save/apply, profile:set) via
  assertCanMutateManagedPrimaryRouting.
- Scope capture/drain/restore drivers: captureManagedSshScopes (pool +
  primary discovery, bootstrap fence join), drainManagedSshScope (exact
  identity-re-proof termination, no-kill forward recovery),
  ensureManagedSshBackend(AtKey)/restoreManagedPrimarySshBackend restores,
  openManagedSshUpdateTransport for serve-less connections.
- Startup recovery (resumeManagedSshRecoveries before createWindow) and
  before-quit join of in-flight update/recovery operations BEFORE the SSH
  coordinator is sealed, so restore dials are not refused during quit.
- Extended sshConnections state (spawnNonce/creationTime(Ns)/startedAt/
  hermesPath/hermesHome/pythonPath/remoteProfile/registryConnectionId/
  primaryRegistryScope) so drain can prove the exact serve it owns;
  bootstrap coordinator entries carry metadata for the update fence;
  persistSshConnectionToken mirrors tokens per managedSshTokenPersistencePlan.
- preload/global.d.ts: connections.updateManaged +
  DesktopManagedConnectionUpdateResult/Receipt types.

Renderer UI (fleet-updates store, about-settings, system.ts ProfileScope
plumbing, i18n) intentionally NOT wired — it belongs to the deferred fleet
rollout UI and follows separately.

Wiring re-implemented against current main; design from #93042 by @andrexibiza

tsc -p apps/desktop clean; electron project 1924/1924 passed (133 files,
incl. 131/131 across the three engine suites); eslint clean on touched files.
2026-08-26 17:42:44 -07:00
686f6c61 0c69cac48a fix(desktop): terminate owned SSH serve backends on quit
teardownSshConnection closed the tunnel and SSH transport but never
killed the detached serve --isolated process. Spawn uses setsid/nohup,
so the backend reparents to pid 1, keeps state.db open, and accumulates
across Cmd+Q. Reuse cleanupStale via disconnect while SSH can still
exec, sequence remote kill before close, and seal the bootstrap
coordinator so reconnect during a prevented first quit cannot respawn.
The quit race is 6s to cover cleanupStale's 5s wait-for-exit loop.
2026-08-26 00:40:35 -07:00
addel c76b7e6343 fix(desktop): harden plugin route lifecycle 2026-08-16 16:26:50 -07:00
Cad from Arca d6be88fbc8 fix(desktop): map SSH profiles to remote profiles 2026-08-01 14:30:16 -07:00
yoniebans faa3bce6dc style(desktop): satisfy merged eslint/prettier config
The SSH modules predate the stricter lint config that landed on main (curly, no-empty, perfectionist sorting, prettier). Mechanical lint:fix + fmt pass, empty catch blocks filled with the codebase's void-0 convention, and inline no-control-regex disables on the three deliberate control-char patterns (same pattern as lib/ansi.ts).
2026-07-20 23:01:49 +02:00
yoniebans a340d0adc5 feat(desktop): add isolated SSH transport primitives
Add OpenSSH config discovery, scoped ControlMaster/no-mux transport, durable installation ownership, serialized bootstrap coordination, and transactional remote backend lifecycle with focused Vitest coverage.
2026-07-16 14:43:14 +02:00