mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 16:02:03 +00:00
fix(wsl): harden shell identity and PTY teardown
This commit is contained in:
@@ -2,7 +2,34 @@
|
||||
_orca_shell_features=",${ORCA_SHELL_FEATURES:-},"
|
||||
builtin unset ORCA_SHELL_FEATURES
|
||||
__orca_has_feature() { [[ "$_orca_shell_features" == *",$1,"* ]]; }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
# Why a plain variable: the channel is consumed and destroyed in these first
|
||||
# lines, so nothing this shell later spawns can see or inherit the selection.
|
||||
__orca_ready_marker=""
|
||||
|
||||
@@ -30,7 +30,34 @@ builtin unset ORCA_SHELL_FEATURES
|
||||
builtin typeset -g _orca_histfile="${ORCA_HISTFILE:-}"
|
||||
builtin unset ORCA_HISTFILE
|
||||
__orca_has_feature() { (( ${_orca_shell_features[(Ie)$1]} )) }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
__orca_osc133_precmd() {
|
||||
local exit_code=$?
|
||||
if [[ -n "${__orca_in_command:-}" ]]; then
|
||||
|
||||
@@ -2,7 +2,34 @@
|
||||
_orca_shell_features=",${ORCA_SHELL_FEATURES:-},"
|
||||
builtin unset ORCA_SHELL_FEATURES
|
||||
__orca_has_feature() { [[ "$_orca_shell_features" == *",$1,"* ]]; }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
# Why a plain variable: the channel is consumed and destroyed in these first
|
||||
# lines, so nothing this shell later spawns can see or inherit the selection.
|
||||
__orca_ready_marker=""
|
||||
|
||||
@@ -30,7 +30,34 @@ builtin unset ORCA_SHELL_FEATURES
|
||||
builtin typeset -g _orca_histfile="${ORCA_HISTFILE:-}"
|
||||
builtin unset ORCA_HISTFILE
|
||||
__orca_has_feature() { (( ${_orca_shell_features[(Ie)$1]} )) }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
__orca_osc133_precmd() {
|
||||
local exit_code=$?
|
||||
if [[ -n "${__orca_in_command:-}" ]]; then
|
||||
|
||||
@@ -2,7 +2,34 @@
|
||||
_orca_shell_features=",${ORCA_SHELL_FEATURES:-},"
|
||||
builtin unset ORCA_SHELL_FEATURES
|
||||
__orca_has_feature() { [[ "$_orca_shell_features" == *",$1,"* ]]; }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
# Why a plain variable: the channel is consumed and destroyed in these first
|
||||
# lines, so nothing this shell later spawns can see or inherit the selection.
|
||||
__orca_ready_marker=""
|
||||
|
||||
@@ -30,7 +30,34 @@ builtin unset ORCA_SHELL_FEATURES
|
||||
builtin typeset -g _orca_histfile="${ORCA_HISTFILE:-}"
|
||||
builtin unset ORCA_HISTFILE
|
||||
__orca_has_feature() { (( ${_orca_shell_features[(Ie)$1]} )) }
|
||||
__orca_has_feature identity && printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
if __orca_has_feature identity; then
|
||||
if [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=${_orca_stat##*) }
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
case "$_orca_distro" in ""|*[!A-Za-z0-9._-]*) return 0 ;; esac
|
||||
case "$_orca_start" in ""|*[!0-9]*) return 0 ;; esac
|
||||
case "$_orca_tty" in /dev/pts/[0-9]*) ;; *) return 0 ;; esac
|
||||
printf "\033]777;orca-shell-start:v2:%s:%s:%s:%s:%s\007" "$_orca_distro" "$_orca_boot" "$$" "$_orca_start" "$_orca_tty"
|
||||
}
|
||||
__orca_emit_shell_identity
|
||||
unset -f __orca_emit_shell_identity
|
||||
else
|
||||
printf "\033]777;orca-shell-start:%s\007" "$$"
|
||||
fi
|
||||
fi
|
||||
__orca_deferred_init() {
|
||||
# Why first: this body runs after the user's own config, so it would otherwise
|
||||
# inherit whatever options that config left set. Under NO_UNSET an unset
|
||||
|
||||
@@ -57,6 +57,7 @@ export abstract class DaemonPtySessionInventory extends DaemonPtyProcessInspecti
|
||||
const distros = new Set(
|
||||
result.sessions
|
||||
.filter((session) => session.isAlive && session.wslDistro)
|
||||
.filter((session) => session.wslShellAnchor)
|
||||
.map((session) => session.wslDistro as string)
|
||||
)
|
||||
await Promise.all(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
createShellStartupIdentityScanState,
|
||||
drainShellStartupIdentityHeldBytes,
|
||||
scanForShellStartupIdentity,
|
||||
type ShellStartupIdentityScanState
|
||||
} from '../shell-startup-identity-scanner'
|
||||
@@ -17,6 +18,22 @@ export class SessionWslShellAnchorTracker {
|
||||
return this._anchor
|
||||
}
|
||||
|
||||
drainHeldBytes(): string {
|
||||
if (!this.scanState) {
|
||||
return ''
|
||||
}
|
||||
const held = drainShellStartupIdentityHeldBytes(this.scanState)
|
||||
this.scanState = null
|
||||
return held
|
||||
}
|
||||
|
||||
drainInto(accept: (data: string) => void): void {
|
||||
const held = this.drainHeldBytes()
|
||||
if (held.length > 0) {
|
||||
accept(held)
|
||||
}
|
||||
}
|
||||
|
||||
scan(data: string): string {
|
||||
if (!this.scanState) {
|
||||
return data
|
||||
|
||||
@@ -184,6 +184,18 @@ describe('Session', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('drains a partial WSL identity marker when the subprocess exits', () => {
|
||||
createSession({ wslDistro: 'Ubuntu' })
|
||||
const received: string[] = []
|
||||
session.attachClient({ onData: (data) => received.push(data), onExit: () => {} })
|
||||
const partial = '\x1b]777;orca-shell-start:v2:Ubuntu:01234567'
|
||||
|
||||
subprocess.simulateData(partial)
|
||||
subprocess.simulateExit(0)
|
||||
|
||||
expect(received.join('')).toContain(partial)
|
||||
})
|
||||
|
||||
it('does not confirm shell ownership from historical replay bytes', () => {
|
||||
createSession({
|
||||
historySeedChunks: ['\x1b[?1049hOLD-TUI\x1b]133;D;137\x07old-shell-marker']
|
||||
|
||||
@@ -291,6 +291,7 @@ export class Session {
|
||||
// Why: `wasTerminating` below must be read BEFORE the `_state = 'exited'` flip — it guards the
|
||||
// "dispose while kill() in flight" case and the invariant needs the pre-flip `_state`; do NOT move it down.
|
||||
this.shellReady.releaseDeviceAttributes()
|
||||
this.wslShellAnchorTracker.drainInto((data) => this.shellReady.ingestSubprocessData(data))
|
||||
this.shellReady.releaseHeldBytes()
|
||||
this.startupIngress.drainAndClose()
|
||||
// Why after drainAndClose (and before clearClients below): a dispose
|
||||
@@ -362,6 +363,7 @@ export class Session {
|
||||
|
||||
this.shellReady.releaseDeviceAttributes()
|
||||
this.shellReady.disposePromptReadinessProbe()
|
||||
this.wslShellAnchorTracker.drainInto((data) => this.shellReady.ingestSubprocessData(data))
|
||||
this.shellReady.releaseHeldBytes()
|
||||
this.startupIngress.drainAndClose()
|
||||
// Why after drainAndClose: drained ingress bytes re-enter the barrier and can
|
||||
|
||||
@@ -36,6 +36,7 @@ import { destroyPtyProcess, createPtyPhysicalExit } from './local-pty-terminatio
|
||||
import { writeStartupCommandWhenShellReady } from './local-pty-shell-ready-startup-command'
|
||||
import {
|
||||
createShellStartupIdentityScanState,
|
||||
drainShellStartupIdentityHeldBytes,
|
||||
scanForShellStartupIdentity,
|
||||
type ShellStartupIdentityScanState
|
||||
} from '../shell-startup-identity-scanner'
|
||||
@@ -165,6 +166,15 @@ export function activateLocalPtySession(args: {
|
||||
if (process.platform !== 'win32') {
|
||||
;(proc as unknown as { kill: (sig?: string) => void }).kill = () => {}
|
||||
}
|
||||
// If the PTY dies mid-marker, return the withheld OSC bytes to the normal
|
||||
// ingress path so teardown cannot silently eat user-visible output.
|
||||
if (wslIdentityScanState) {
|
||||
const heldIdentityBytes = drainShellStartupIdentityHeldBytes(wslIdentityScanState)
|
||||
wslIdentityScanState = null
|
||||
if (heldIdentityBytes.length > 0) {
|
||||
readiness.acceptData(heldIdentityBytes)
|
||||
}
|
||||
}
|
||||
readiness.prepareForExit()
|
||||
clearPtyState(id)
|
||||
startupIngress.drainAndClose()
|
||||
|
||||
@@ -128,7 +128,9 @@ export async function listLocalPtyProcesses(): Promise<PtyProcessInfo[]> {
|
||||
const wslByDistro = new Map<string, string[]>()
|
||||
for (const [id] of entries) {
|
||||
const distro = ptyWslDistroById.get(id)
|
||||
if (distro) {
|
||||
// Without a shell anchor the inventory cannot resolve a foreground process;
|
||||
// avoid paying for a guest scan whose verdict is necessarily unverifiable.
|
||||
if (distro && ptyWslShellAnchors.has(id)) {
|
||||
const ids = wslByDistro.get(distro) ?? []
|
||||
ids.push(id)
|
||||
wslByDistro.set(distro, ids)
|
||||
@@ -141,7 +143,12 @@ export async function listLocalPtyProcesses(): Promise<PtyProcessInfo[]> {
|
||||
})
|
||||
)
|
||||
|
||||
return entries.map(([id, proc]) => {
|
||||
return entries.flatMap(([id, proc]) => {
|
||||
// Inventory reads are asynchronous; a PTY may have exited while they ran.
|
||||
// Do not publish a row for an incarnation that is no longer authoritative.
|
||||
if (ptyProcesses.get(id) !== proc) {
|
||||
return []
|
||||
}
|
||||
const distro = ptyWslDistroById.get(id)
|
||||
let title = proc.process || ptyShellName.get(id) || 'shell'
|
||||
let foregroundProcessEvidence: ForegroundProcessEvidence | undefined
|
||||
@@ -178,16 +185,18 @@ export async function listLocalPtyProcesses(): Promise<PtyProcessInfo[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id,
|
||||
...(ptyIncarnations.get(id) ? { incarnationId: ptyIncarnations.get(id) } : {}),
|
||||
cwd: ptyInitialCwd.get(id) ?? '',
|
||||
title,
|
||||
...(ptyWorktreeId.get(id) ? { worktreeId: ptyWorktreeId.get(id) } : {}),
|
||||
...(ptyTerminalHandle.get(id) ? { terminalHandle: ptyTerminalHandle.get(id) } : {}),
|
||||
...(ptyWslDistroById.has(id) ? { wslDistro: ptyWslDistroById.get(id) ?? null } : {}),
|
||||
...(foregroundProcessEvidence ? { foregroundProcessEvidence } : {})
|
||||
}
|
||||
return [
|
||||
{
|
||||
id,
|
||||
...(ptyIncarnations.get(id) ? { incarnationId: ptyIncarnations.get(id) } : {}),
|
||||
cwd: ptyInitialCwd.get(id) ?? '',
|
||||
title,
|
||||
...(ptyWorktreeId.get(id) ? { worktreeId: ptyWorktreeId.get(id) } : {}),
|
||||
...(ptyTerminalHandle.get(id) ? { terminalHandle: ptyTerminalHandle.get(id) } : {}),
|
||||
...(ptyWslDistroById.has(id) ? { wslDistro: ptyWslDistroById.get(id) ?? null } : {}),
|
||||
...(foregroundProcessEvidence ? { foregroundProcessEvidence } : {})
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,16 @@ describe('addOrcaWslInteropEnv', () => {
|
||||
expect(env.WSLENV).toBe('ORCA_TERMINAL_HANDLE/u:ORCA_SHELL_READY_ROOT/p:ORCA_SHELL_FEATURES/u')
|
||||
})
|
||||
|
||||
it('drops unknown inherited shell features before adding canonical WSL features', () => {
|
||||
const env: Record<string, string> = {
|
||||
ORCA_SHELL_FEATURES: 'overlay,evil, markers,overlay, identity '
|
||||
}
|
||||
|
||||
addOrcaWslInteropEnv(env)
|
||||
|
||||
expect(env.ORCA_SHELL_FEATURES).toBe('overlay,markers,identity')
|
||||
})
|
||||
|
||||
// Why this is published at all: the wrapper tree is content-addressed, so the
|
||||
// in-guest login script cannot rebuild its path from ORCA_USER_DATA_PATH -- it
|
||||
// cannot derive the hash segment. Without this the guest finds no wrapper and
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SETUP_AGENT_SEQUENCE_STARTUP_SCRIPT_ENV
|
||||
} from '../../shared/setup-agent-sequencing'
|
||||
import { getShellReadyWrapperRoot } from '../providers/local-pty-shell-ready-wrapper-root'
|
||||
import { SHELL_STARTUP_FEATURES, type ShellStartupFeature } from '../shell-startup-features'
|
||||
|
||||
const WSLENV_ENTRY_SEPARATOR = ':'
|
||||
|
||||
@@ -61,7 +62,13 @@ export function addOrcaWslInteropEnv(env: Record<string, string>): void {
|
||||
// WSL's host-side process is always wsl.exe. Ask the guest wrapper to emit
|
||||
// its shell identity marker so process evidence can anchor to the guest PID.
|
||||
// The feature selection crosses through WSLENV, never the wsl.exe argv.
|
||||
const existingFeatures = env.ORCA_SHELL_FEATURES?.split(',').filter(Boolean) ?? []
|
||||
// A WSL pane can inherit this channel from an Orca shell launched earlier;
|
||||
// retain only canonical feature names before adding this launch's overlay.
|
||||
const existingFeatures = (env.ORCA_SHELL_FEATURES?.split(',') ?? [])
|
||||
.map((feature) => feature.trim())
|
||||
.filter((feature): feature is ShellStartupFeature =>
|
||||
(SHELL_STARTUP_FEATURES as readonly string[]).includes(feature)
|
||||
)
|
||||
const overlayFeatures = [
|
||||
'ORCA_OPENCODE_CONFIG_DIR',
|
||||
'ORCA_MIMOCODE_HOME',
|
||||
|
||||
@@ -45,11 +45,16 @@ export const SHELL_STARTUP_IDENTITY_MARKER_BLOCK = `if __orca_has_feature identi
|
||||
if [ -n "\${WSL_DISTRO_NAME:-}" ]; then
|
||||
__orca_emit_shell_identity() {
|
||||
local _orca_boot _orca_stat _orca_tail _orca_start _orca_tty _orca_distro
|
||||
local IFS=' '
|
||||
_orca_boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || return 0
|
||||
_orca_stat=$(cat /proc/$$/stat 2>/dev/null) || return 0
|
||||
_orca_tail=\${_orca_stat##*) }
|
||||
set -- $_orca_tail
|
||||
_orca_start=\${20:-}
|
||||
# zsh does not perform implicit word splitting for unquoted parameters;
|
||||
# read the proc-stat fields explicitly so field 22 (starttime) is parsed
|
||||
# correctly in both zsh and bash.
|
||||
builtin read -r _orca_state _orca_f2 _orca_f3 _orca_f4 _orca_f5 _orca_f6 _orca_f7 _orca_f8 _orca_f9 _orca_f10 _orca_f11 _orca_f12 _orca_f13 _orca_f14 _orca_f15 _orca_f16 _orca_f17 _orca_f18 _orca_f19 _orca_start _orca_rest <<EOF
|
||||
$_orca_tail
|
||||
EOF
|
||||
_orca_tty=$(tty 2>/dev/null) || return 0
|
||||
_orca_distro=\${WSL_DISTRO_NAME:-}
|
||||
case "$_orca_boot" in *[!A-Fa-f0-9-]*|"") return 0 ;; esac
|
||||
|
||||
Reference in New Issue
Block a user