Files
orca/src/cli/worktree-handler-groups.ts
T
Neilandggbdpq a53c00219d fix(workspace): wake a sleeping recipe VM when a client opens its workspace (#39038)
A recipe VM workspace runs on its VM's own Orca server, so a phone, the CLI or
the desktop reaching it while the VM slept dialed a server that was not there
and failed. Only the host window's click ran the recipe's resume.

- The VM wake owner (WorkspaceSleepCoordinator) tracks the VM operations it
  was asked for and answers where the VM stands: a pending wake or a running
  suspend outranks the stored record.
- The host holds a request that opens the workspace (worktree.activate,
  defined once in shared code) until the VM is up: three minutes for the
  desktop and the CLI, and for a phone only as long as still lets the relay
  connect inside the phone's 30-second deadline. A VM still up keeps a sleep
  on its way from suspending it; a VM asleep or suspending wakes after what is
  running, joining a wake already pending. The open answers waking only when
  the hold runs out, wake_failed when the wake fails, and asleep when a Sleep
  asked for while it was held won; that open then wakes nothing more. Other
  requests answer waking or asleep instead of dialing nothing. A Sleep asked
  for during a wake keeps its place in the lifecycle queue and starts once the
  wake is done. A caller that gives up stops waiting and frees its queue
  place; the wake goes on.
- An open of a server the user disconnected is refused before anything wakes
  its VM, and cancelled if they disconnect while it waits. A wake carries the
  SSH host's user hold from when it was accepted and every Disconnect since,
  so its connect never lifts a hold; the VM still resumes and its new SSH
  address is saved for the user's own Connect.
- A wake re-pairs a paired VM's server. The request that waited for it is
  held to its caller's pairing as of before the wake (a stale caller is
  refused before it can wake anything), then to exactly the pairing revision
  the resume produced (including a re-pair its SSH reconnect made), whatever
  process now serves it; any other change still
  refuses it. A phone link still preparing for an open survives only the
  re-pair its own wake made; any other retirement, or a user Disconnect, ends
  it, and a link preparing for anything else is retired at once.
- The hold lives in the desktop's environment preparation, which its own calls
  and subscriptions, the phone relay and the CLI all go through. A phone open
  on a link to a server whose VM is not up gets a fresh link; the old link's
  requests are answered waking or asleep. Further opens join a link already
  waiting on the wake. A phone link's whole open (server preparation with any
  hold, grant sync and the socket) shares one deadline inside the phone's
  own, for every server.
- The CLI dials a VM directly, so `orca worktree activate` asks the desktop
  (runtimeEnvironment.wakeRecipeVm) to open the VM and reads the server's
  pairing again; the refusals (including runtime_manually_disconnected) keep
  their codes over RPC, and a host without recipe VMs answers that one RPC
  with method_not_found. Hosts that do this advertise
  host.recipe-vm-wake.v1.
- A workspace this host runs on a recipe VM over its SSH target wakes on this
  host's own activation, through the resume's explicit connect
  (wakeWorkspaceVm -> resumeVm -> connectRuntimeOwnedSshTarget). That connect
  runs whenever the VM is in SSH mode, even when its recipe had no resume step,
  since suspend tore down the transport.

Fixes #36923

Co-authored-by: ggbdpq <ggbdpq@gmail.com>
2026-11-06 06:51:09 -08:00

23 lines
592 B
TypeScript

import type { HandlerGroup } from './handler-group-manifest'
export const WORKTREE_HANDLER_GROUPS: readonly HandlerGroup[] = [
{
name: 'worktree',
keys: [
'worktree ps',
'worktree list',
'worktree show',
'worktree current',
'worktree create',
'worktree set',
'worktree rm'
],
load: async () => (await import('./handlers/worktree.js')).WORKTREE_HANDLERS
},
{
name: 'worktree-activate',
keys: ['worktree activate'],
load: async () => (await import('./handlers/worktree-activate.js')).WORKTREE_ACTIVATE_HANDLERS
}
]