Files
orca/src/main/runtime/runtime-file-command-target.ts
T
Neil 946627f2ce fix(runtime): route runtime filesystem commands by resolved execution host (#18325)
`ResolvedRuntimeFileTarget` carried `connectionId?: string` and no host id, so
`undefined` spelled three different answers at once — "runtime: host", "unresolved"
and "genuinely local". Its sole resolver read `store.getRepo(worktree.repoId)?.connectionId`
and never looked at `worktree.hostId`, which outranks every repo row, so one
arbitrarily chosen row decided the execution host for ~30 filesystem dispatches.
This is #18307's defect in the same file family; it was deliberately left out of
that PR rather than doubling an already-36-site diff.

The target now carries `executionHostId: ExecutionHostId` (never null, never
optional), resolved through `resolveWorktreeHostRouting` — the same adapter #18307
added — and dispatched through #18296's `resolveFilesystemRouteForHost`. Dispatch
sites call `requireRuntimeFileProvider`, where `null` means exactly one thing: the
host is `local` and the read happens here.

Four answers that used to collapse into one:

- `ssh:x` with a rival row on `ssh:y` — routes to x. Previously the first row won.
- `local` with a surviving `connectionId` — a row contradicting itself; no SSH
  connection is handed out.
- `runtime:<env>` — throws `ExecutionHostNotDispatchableError`. Its repo row's
  connection names a target in the *server's* namespace; reading it here reaches a
  same-named target on this client.
- rival rows disagreeing with no worktree host — `worktree_execution_host_unresolved`,
  matching the launch and Git paths rather than guessing a row.

Two further reads stop degrading. `assertRuntimeFileMutationExpectation` recomputed
the host from `connectionId`, so a client's host expectation could pass against a
host the workspace never named; it now compares the resolved host. And the
cross-workspace terminal tap coalesced `knownWorkspaceTarget?.connectionId ??
connectionId`, so a sibling workspace resolved as `local` inherited the origin
worktree's SSH target and statted a local path on the remote box; a non-optional
host id replaces rather than coalesces.

An unreachable SSH host still throws `SSH_FILESYSTEM_PROVIDER_UNAVAILABLE_MESSAGE`;
loss of contact is never evidence of locality (docs/reference/ssh-execution-boundary.md).
Quick-open listing and path search keep degrading to empty for an unreachable host —
that is a false negative, not a local answer — and now do so only for a host that
really is remote.

The whole `runtime-file-commands-*` family carries `@ts-nocheck` from a mechanical
class split, so removing the field could not raise the compile errors that made
#18307 safe. `runtime-file-command-target.ts` is deliberately checked, and a ratchet
test stands in for the errors the family cannot produce.

No wire change: `ResolvedRuntimeFileTarget` is main-process internal, and the SSH
watcher-release and grant keys are byte-identical to before.
2026-09-02 20:47:09 -07:00

91 lines
3.8 KiB
TypeScript

import type { ExecutionHostId } from '../../shared/execution-host'
import type { GitWorktreeInfo, Worktree } from '../../shared/worktree/types'
import {
ExecutionHostNotDispatchableError,
resolveFilesystemRouteForHost
} from '../providers/execution-host-provider-dispatch'
import { SSH_FILESYSTEM_PROVIDER_UNAVAILABLE_MESSAGE } from '../providers/ssh-filesystem-dispatch'
import type { IFilesystemProvider } from '../providers/types'
export type ResolvedRuntimeFileWorktree = Worktree & { git: GitWorktreeInfo }
export type ResolvedRuntimeFileTarget = {
worktree: ResolvedRuntimeFileWorktree
/**
* The host whose filesystem holds this workspace. Never optional and never null: the field it
* replaced (`connectionId?: string`) spelled "runtime host", "unresolved" and "genuinely local"
* all as `undefined`, so every path that could not resolve answered "local" and read remote
* paths on the client (#11163). Unresolved now fails at resolution time instead of arriving here
* as a silently-local target. Mirrors `RuntimeGitTarget.executionHostId`.
*/
executionHostId: ExecutionHostId
}
/** A workspace-relative path already joined onto its host's root; `executionHostId` routes it. */
export type RuntimeFileExplorerPath = {
worktree: ResolvedRuntimeFileWorktree
path: string
executionHostId: ExecutionHostId
}
/**
* The two hosts this process can itself run a runtime filesystem command on, narrowed from the
* shared host-keyed route in `src/main/providers/execution-host-provider-dispatch.ts`.
*
* `runtime:<env>` is deliberately not a variant, for the same reason it is not one for Git: the
* files live on that environment's own server, which normalizes the call to its own `local`, and
* the SSH target on its repo row is that server's *nested* one — addressable only as the pair
* (environmentId, targetId). Handing that id to this client's SSH table reads a same-named target
* in the wrong namespace, so it throws rather than routing.
*/
export type RuntimeFileRoute =
| { kind: 'local' }
/** `provider: null` is "remote and currently unreachable" — never "read it here". */
| { kind: 'ssh'; connectionId: string; provider: IFilesystemProvider | null }
/** The remote half of the route, for leaf helpers that only ever run against an SSH host. */
export type RuntimeFileSshRoute = Extract<RuntimeFileRoute, { kind: 'ssh' }>
export function runtimeFileRouteForTarget(target: {
executionHostId: ExecutionHostId
}): RuntimeFileRoute {
const route = resolveFilesystemRouteForHost(target.executionHostId)
switch (route.kind) {
case 'local':
return { kind: 'local' }
case 'ssh':
return { kind: 'ssh', connectionId: route.connectionId, provider: route.provider }
case 'runtime':
throw new ExecutionHostNotDispatchableError(route.hostId)
}
}
/**
* `null` means exactly one thing: the host is `local`, and this command reads and writes here. An
* unreachable SSH host and a `runtime:` host both throw.
*/
export function requireRuntimeFileProvider(target: {
executionHostId: ExecutionHostId
}): IFilesystemProvider | null {
const route = runtimeFileRouteForTarget(target)
if (route.kind === 'local') {
return null
}
if (!route.provider) {
throw new Error(SSH_FILESYSTEM_PROVIDER_UNAVAILABLE_MESSAGE)
}
return route.provider
}
/**
* The SSH target id for the leaf helpers that still address a connection by name — watcher release
* keys, re-arm registration, remote path stats. `undefined` is `local`; a `runtime:` host throws
* rather than surrendering its nested target id to this client's namespace.
*/
export function runtimeFileSshTargetId(target: {
executionHostId: ExecutionHostId
}): string | undefined {
const route = runtimeFileRouteForTarget(target)
return route.kind === 'ssh' ? route.connectionId : undefined
}