Files
orca/src/main/ipc/runtime-environment-usage-record.ts
T
841503152c fix(runtime-environments): don't crash when a server removed via the CLI still responds (#22517)
* fix(runtime-environments): don't crash when a server removed via the CLI still responds

orca environment rm edits the environment store behind the running app, so the
next ok response on a live socket called markEnvironmentUsed, which threw
'Unknown environment' out of an unguarded socket callback. Main-process callers
now use markEnvironmentUsedIfPresent, which skips a missing environment and
keeps every other store error; the status owner pauses shared control instead
of re-establishing it for a removed server.

* fix(runtime-environments): guard usage bookkeeping at the main-process boundary

Keep one strict store contract and move the leniency to the caller that cannot
report a failure to anyone.

- Revert markEnvironmentUsedIfPresent: drawing the line around one error string
  left corrupt, unreadable and oversized store files still fatal on the same
  unguarded socket callback.
- Add recordRuntimeEnvironmentUsage, a named main-process boundary that says
  lastUsedAt is advisory and swallows every store failure. Route only the three
  sites with no observer through it (subscription onResponse in transport- and
  support-routing, and the status owner's verified hook, where a throw skips
  settleWaiters and hangs refresh callers). Awaited request paths stay strict.
- Guard onResponse/onBinary in the subscription frame router the way the sibling
  request router already guards validateStatus, so no consumer throw can reach
  the ws 'message' emitter and become main_uncaught_exception.
- Drop the status-owner `capable && present` gate: pauseStandingRetry no-ops
  while subscriptions exist, and removal teardown belongs to #21048's watcher.

Co-authored-by: mmarabel <mmarabel@users.noreply.github.com>

* test(runtime-environments): cover the real socket path a consumer throw escapes

The existing tests invoke the captured onResponse directly, which never touches
the surface that actually kills the app. Drive a real WebSocket server through
subscribeRemoteRuntimeRequest so the throw travels ws 'message' -> handleFrame
-> consumer; without the frame-router guard vitest reports it as an unhandled
error, which is main_uncaught_exception in production.

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: mmarabel <mmarabel@users.noreply.github.com>
2026-09-25 14:48:54 -07:00

27 lines
1.1 KiB
TypeScript

import { markEnvironmentUsed } from '../../shared/runtime-environment-store'
/**
* Records `lastUsedAt` for callers that cannot report a failure to anyone.
*
* Why: subscription frames arrive on a websocket callback with no promise to reject and no
* caller to observe the result, so a throw from the store unwinds into the socket emitter and
* kills the main process. `orca environment rm` makes that routine — it edits the store behind
* the running app's back, so every later response resolves an environment that is gone. Usage
* bookkeeping has no consumer, so nothing here is worth an exception. Awaited request paths keep
* calling `markEnvironmentUsed` directly: there the rejection is observable and correct.
*/
export function recordRuntimeEnvironmentUsage(
userDataPath: string,
selector: string,
args: { runtimeId?: string | null; pairedDeviceId?: string } = {}
): void {
try {
markEnvironmentUsed(userDataPath, selector, args)
} catch (error) {
console.warn(
`Skipped last-used bookkeeping for runtime environment ${selector}:`,
error instanceof Error ? error.message : error
)
}
}