Files
orca/src/shared/posix-command-path-lookup.ts
T
Neil c3a1694b1d perf(preflight): read the WSL mount table once, and make launch agree with detection (#16053)
* perf(preflight): read the WSL mount table once per shell, not once per CLI

The prelude is embedded in the lookup script, and the caller wraps that in
`for cmd in <every agent>`, so the unconditional assignment forked awk once per
probed CLI -- 36 of them inside the distro against a 10s detection budget. The
comment claimed it was read once outside the loop; it was not.

`${x+set}` rather than `[ -n ... ]`: a host with no Windows mounts yields the
empty string, which must still count as read.

Pinned by counting real awk forks through /bin/sh with a stub on PATH, because
nothing covered this expression at all -- a wrong-field mutation shipped green.
Verified to bind: the unconditional form counts 4 for 4 commands.

* fix(wsl): make launch resolve the same binary detection reported

Agent detection skips Windows mounts during the PATH walk; the Codex WSL
command builder and the WSL branch of isCommandOnPath did not. So Orca could
report the guest codex as installed and then launch the Windows one sitting
ahead of it on PATH, or disagree with itself between preflight and detection
about the same distro.

Both now pass the same option.

Verified on a real Windows host against a real WSL2 distro, with a Windows
binary planted ahead of a guest one on PATH:

  plain `command -v orcaprobe` -> /mnt/c/Users/neil/orca-agree/orcaprobe
  this lookup                  -> /home/neil/.orca-agree/bin/orcaprobe

That host reports /mnt/c as 9p, which the mount expression matches, so the
fstype list is confirmed against hardware rather than fixtures.

* test(preflight): prove the memoised mount list applies past the first command

Counting awk forks with a stub that reports no mounts cannot see what the
hoist trades correctness for. A mutant that empties `_orca_win_mounts` inside
the walk keeps the fork count at 1 and keeps every existing test green, while
every agent after the first stops skipping /mnt.

This runs two commands behind a stubbed Windows mount and asserts both resolve
to the guest binary. Verified against that exact mutant.

Credit: review counsel.
2026-08-23 02:58:40 -07:00

114 lines
4.5 KiB
TypeScript

export type PosixCommandPathLookupTarget =
| { kind: 'literal'; value: string }
| { kind: 'shell-variable'; name: string }
const SHELL_VARIABLE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/
export type PosixCommandPathLookupOptions = {
/**
* Skip PATH components that are Windows drives mounted into the guest.
*
* WSL appends the Windows PATH to the guest PATH, so a Windows `claude` can
* sit ahead of a real guest install. Rejecting the result AFTER the walk is
* not equivalent: the walk stops at the first hit, so discarding it reports
* "not installed" for a user who has both. Skipping the component keeps the
* walk going and finds the guest binary behind it.
*
* Matched by mount metadata, not by a `/mnt` name: the automount root is
* configurable, and `/mnt` is an ordinary directory name on a Linux box.
*/
skipWindowsMountDirs?: boolean
}
export function buildPosixCommandPathLookupScript(
target: PosixCommandPathLookupTarget,
options: PosixCommandPathLookupOptions = {}
): string {
const commandAssignment = buildCommandAssignment(target)
// `drvfs` is what WSL mounts a Windows drive as, wherever the automount root
// is; 9p/virtiofs cover the WSL2 shapes.
//
// `${x+set}` so the table is read once per SHELL, not once per lookup: the
// caller embeds this script inside `for cmd in <every agent>`, so an
// unconditional assignment forked awk once per probed CLI -- 36 of them
// against a 10s budget. The variable outlives the iteration, so the second
// pass finds it set. Deliberately not `[ -n ... ]`: a host with no Windows
// mounts yields the empty string, which must still count as read.
const mountPrelude = options.skipWindowsMountDirs
? [
'[ "${_orca_win_mounts+set}" = set ] || _orca_win_mounts=$(awk \'$3 == "drvfs" || $3 == "9p" || $3 == "virtiofs" { print $2 }\' /proc/mounts 2>/dev/null)'
]
: []
const skipMountComponent = options.skipWindowsMountDirs
? [
' for _orca_win_mount in $_orca_win_mounts; do',
' case "$_orca_lookup_component/" in',
' "$_orca_win_mount"/*) _orca_lookup_component= ;;',
' esac',
' done',
' if [ -z "$_orca_lookup_component" ]; then',
' [ -n "$_orca_lookup_has_more" ] || break',
' continue',
' fi'
]
: []
// Shell command resolution can be masked by aliases, functions, and builtins, so inspect PATH.
return [
`_orca_lookup_command=${commandAssignment}`,
'resolved=',
...mountPrelude,
'case "$_orca_lookup_command" in',
' */*)',
' case "$_orca_lookup_command" in',
' /*) _orca_lookup_candidate=$_orca_lookup_command ;;',
' *) _orca_lookup_candidate=${PWD%/}/$_orca_lookup_command ;;',
' esac',
' if [ -x "$_orca_lookup_candidate" ] && [ ! -d "$_orca_lookup_candidate" ]; then',
' resolved=$_orca_lookup_candidate',
' fi',
' ;;',
' *)',
' _orca_lookup_remaining=${PATH-}',
' while :; do',
' case "$_orca_lookup_remaining" in',
' *:*)',
' _orca_lookup_component=${_orca_lookup_remaining%%:*}',
' _orca_lookup_remaining=${_orca_lookup_remaining#*:}',
' _orca_lookup_has_more=1',
' ;;',
' *)',
' _orca_lookup_component=$_orca_lookup_remaining',
' _orca_lookup_has_more=',
' ;;',
' esac',
...skipMountComponent,
' [ -n "$_orca_lookup_component" ] || _orca_lookup_component=.',
' case "$_orca_lookup_component" in',
' /*) _orca_lookup_candidate=$_orca_lookup_component/$_orca_lookup_command ;;',
' *) _orca_lookup_candidate=${PWD%/}/$_orca_lookup_component/$_orca_lookup_command ;;',
' esac',
' if [ -x "$_orca_lookup_candidate" ] && [ ! -d "$_orca_lookup_candidate" ]; then',
' resolved=$_orca_lookup_candidate',
' break',
' fi',
' [ -n "$_orca_lookup_has_more" ] || break',
' done',
' ;;',
'esac'
].join('\n')
}
function buildCommandAssignment(target: PosixCommandPathLookupTarget): string {
if (target.kind === 'literal') {
return shellQuote(target.value)
}
if (!SHELL_VARIABLE_NAME_PATTERN.test(target.name)) {
throw new Error(`Invalid shell variable name: ${target.name}`)
}
return `\${${target.name}-}`
}
function shellQuote(value: string): string {
return `'${value.replace(/'/g, "'\\''")}'`
}