mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 00:03:47 +00:00
fix(terminal): preserve Bash exec edge cases
This commit is contained in:
@@ -181,6 +181,7 @@
|
||||
"assertions": [
|
||||
"local zsh and bash wrappers survive Fig-shaped exec replacements",
|
||||
"bash prompt rewrites are re-pinned before exec and temporary hooks are removed after normal startup",
|
||||
"bash nounset, redirection-only exec, and failed-exec prompt-array paths preserve user shell state",
|
||||
"zsh exec targets and redirections expand against the user ZDOTDIR before wrapper re-entry",
|
||||
"zsh bare exec redirections and user exec functions retain native behavior"
|
||||
]
|
||||
@@ -199,8 +200,8 @@
|
||||
"platform": "macos",
|
||||
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts src/main/providers/local-pty-shell-ready.test.ts src/relay/pty-shell-launch.test.ts",
|
||||
"result": "passed",
|
||||
"durationSeconds": 4.65,
|
||||
"summary": "All 107 focused local, daemon, and relay assertions passed, including real zsh/bash exec restoration, command modifiers, ZDOTDIR expansion ordering, and DEBUG trap invocation preservation when the user enables functrace during startup."
|
||||
"durationSeconds": 6.19,
|
||||
"summary": "All 110 focused local, daemon, and relay assertions passed, including real zsh/bash exec restoration, command modifiers, ZDOTDIR expansion ordering, nounset and prompt-array cleanup, redirection-only exec behavior, and DEBUG trap invocation preservation when the user enables functrace during startup."
|
||||
}
|
||||
],
|
||||
"runtimeBudget": {
|
||||
@@ -213,7 +214,7 @@
|
||||
},
|
||||
"redGreenEvidence": {
|
||||
"status": "complete",
|
||||
"evidence": "PR head 05c0ac91dd1 failed the exact exec \"$ZDOTDIR/zsh-replacement\" oracle with status 127 after expanding the target inside Orca's wrapper directory. The just-in-time shim candidate passed that oracle plus all 107 focused assertions. The pre-fix Bash guard reported USER_DEBUG_NESTED=0 after a profile enabled functrace; the reviewed guard reports USER_DEBUG_NESTED=1 while keeping the ordinary non-functrace trap unmultiplied. The existing #13767 red/green evidence independently proves the original replacement-shell readiness failure."
|
||||
"evidence": "PR head 05c0ac91dd1 failed the exact exec \"$ZDOTDIR/zsh-replacement\" oracle with status 127 after expanding the target inside Orca's wrapper directory. The just-in-time shim candidate passed that oracle. The pre-fix Bash guard reported USER_DEBUG_NESTED=0 after a profile enabled functrace; the reviewed guard reports USER_DEBUG_NESTED=1 while keeping the ordinary non-functrace trap unmultiplied. Exact head 205e63e4338 armed prompt hooks for redirection-only exec, failed with an unbound PROMPT_COMMAND under nounset, and ran a trailing prompt-array element five times; the reviewed candidate keeps the bridge unarmed, restores the replacement hooks, and runs each array element once. All 110 focused assertions pass. The existing #13767 red/green evidence independently proves the original replacement-shell readiness failure."
|
||||
},
|
||||
"performanceBudget": {
|
||||
"required": true,
|
||||
|
||||
@@ -63,6 +63,7 @@ __orca_install_exec_prompt_hooks() {
|
||||
unset PROMPT_COMMAND
|
||||
PROMPT_COMMAND="$_orca_exec_joined"
|
||||
fi
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND:-}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//__orca_exec_osc133_precmd;/}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//;__orca_exec_osc133_precmd/}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//__orca_exec_osc133_prompt_done;/}"
|
||||
@@ -90,7 +91,10 @@ __orca_startup_command_is_exec() {
|
||||
local _orca_exec_command="$1" _orca_exec_char _orca_exec_previous
|
||||
local _orca_exec_word="" _orca_exec_quote="" _orca_exec_name=""
|
||||
local _orca_exec_index=0 _orca_exec_nested_parens=0 _orca_exec_nested_braces=0
|
||||
local _orca_exec_modifier=0 _orca_exec_escaped=0
|
||||
local _orca_exec_modifier=0 _orca_exec_escaped=0 _orca_exec_found=0
|
||||
local _orca_exec_has_redirection=0 _orca_exec_skip_option_value=0
|
||||
local _orca_exec_skip_redirection_target=0 _orca_exec_options_done=0
|
||||
local _orca_exec_redirection_prefix=""
|
||||
while (( _orca_exec_index <= \${#_orca_exec_command} )); do
|
||||
if (( _orca_exec_index == \${#_orca_exec_command} )); then
|
||||
_orca_exec_char=" "
|
||||
@@ -126,23 +130,62 @@ __orca_startup_command_is_exec() {
|
||||
elif [[ "$_orca_exec_char" == "{" && "$_orca_exec_previous" == "$" ]]; then
|
||||
_orca_exec_nested_braces=1
|
||||
_orca_exec_word="\${_orca_exec_word}$_orca_exec_char"
|
||||
elif [[ "$_orca_exec_char" == "<" || "$_orca_exec_char" == ">" ]]; then
|
||||
if (( !_orca_exec_has_redirection )); then
|
||||
_orca_exec_redirection_prefix="$_orca_exec_word"
|
||||
_orca_exec_has_redirection=1
|
||||
fi
|
||||
_orca_exec_word="\${_orca_exec_word}$_orca_exec_char"
|
||||
elif [[ "$_orca_exec_char" == [[:space:]] ]]; then
|
||||
if [[ -n "$_orca_exec_word" ]]; then
|
||||
if [[ "$_orca_exec_word" == "exec" ]]; then
|
||||
return 0
|
||||
if (( !_orca_exec_found && _orca_exec_has_redirection )) &&
|
||||
[[ "$_orca_exec_redirection_prefix" == "exec" ]]; then
|
||||
_orca_exec_found=1
|
||||
_orca_exec_redirection_prefix=""
|
||||
fi
|
||||
_orca_exec_name="\${_orca_exec_word%%=*}"
|
||||
if (( !_orca_exec_modifier )) && [[ "$_orca_exec_word" == *=* &&
|
||||
"$_orca_exec_name" == [a-zA-Z_]* && "$_orca_exec_name" != *[!a-zA-Z0-9_]* ]]; then
|
||||
:
|
||||
elif (( !_orca_exec_modifier )) && [[ "$_orca_exec_word" == "builtin" || "$_orca_exec_word" == "command" ]]; then
|
||||
_orca_exec_modifier=1
|
||||
elif (( _orca_exec_modifier )) && [[ "$_orca_exec_word" == -* ]]; then
|
||||
:
|
||||
if (( _orca_exec_found )); then
|
||||
if (( _orca_exec_skip_redirection_target )); then
|
||||
_orca_exec_skip_redirection_target=0
|
||||
elif (( _orca_exec_has_redirection )); then
|
||||
if [[ -n "$_orca_exec_redirection_prefix" &&
|
||||
"$_orca_exec_redirection_prefix" == *[!0-9]* &&
|
||||
! "$_orca_exec_redirection_prefix" =~ ^[{][a-zA-Z_][a-zA-Z0-9_]*[}]$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$_orca_exec_word" in
|
||||
*">"|*"<"|*">&"|*"<&"|*"<<-"|*">|") _orca_exec_skip_redirection_target=1 ;;
|
||||
esac
|
||||
elif (( _orca_exec_skip_option_value )); then
|
||||
_orca_exec_skip_option_value=0
|
||||
elif (( _orca_exec_options_done )); then
|
||||
return 0
|
||||
else
|
||||
case "$_orca_exec_word" in
|
||||
-a) _orca_exec_skip_option_value=1 ;;
|
||||
-c|-l|-cl|-lc) ;;
|
||||
--) _orca_exec_options_done=1 ;;
|
||||
-*) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
fi
|
||||
elif [[ "$_orca_exec_word" == "exec" ]]; then
|
||||
_orca_exec_found=1
|
||||
else
|
||||
return 1
|
||||
_orca_exec_name="\${_orca_exec_word%%=*}"
|
||||
if (( !_orca_exec_modifier )) && [[ "$_orca_exec_word" == *=* &&
|
||||
"$_orca_exec_name" == [a-zA-Z_]* && "$_orca_exec_name" != *[!a-zA-Z0-9_]* ]]; then
|
||||
:
|
||||
elif (( !_orca_exec_modifier )) && [[ "$_orca_exec_word" == "builtin" || "$_orca_exec_word" == "command" ]]; then
|
||||
_orca_exec_modifier=1
|
||||
elif (( _orca_exec_modifier )) && [[ "$_orca_exec_word" == -* ]]; then
|
||||
:
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_orca_exec_word=""
|
||||
_orca_exec_has_redirection=0
|
||||
_orca_exec_redirection_prefix=""
|
||||
fi
|
||||
else
|
||||
_orca_exec_word="\${_orca_exec_word}$_orca_exec_char"
|
||||
@@ -203,8 +246,10 @@ __orca_remove_exec_prompt_hooks() {
|
||||
[[ -n "$_orca_exec_prompt_part" ]] || continue
|
||||
_orca_exec_joined="\${_orca_exec_joined}\${_orca_exec_joined:+;}$_orca_exec_prompt_part"
|
||||
done
|
||||
unset PROMPT_COMMAND
|
||||
PROMPT_COMMAND="$_orca_exec_joined"
|
||||
fi
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND:-}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//__orca_exec_osc133_precmd;/}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//;__orca_exec_osc133_precmd/}"
|
||||
PROMPT_COMMAND="\${PROMPT_COMMAND//__orca_exec_osc133_prompt_done;/}"
|
||||
|
||||
@@ -606,7 +606,7 @@ describePosix('local PTY shell-ready launch config', () => {
|
||||
'if [[ -z "${ORCA_EXEC_REPRO_DONE:-}" ]]; then',
|
||||
' export ORCA_EXEC_REPRO_DONE=1',
|
||||
' PROMPT_COMMAND=\'printf "USER_EXEC_PROMPT\\n"\'',
|
||||
' Q_START_TEXT="local seed" exec -a figterm-test bash --noprofile --norc -l -i',
|
||||
' Q_START_TEXT="local seed" exec -a 3> /dev/null figterm-test bash --noprofile --norc -l -i',
|
||||
'fi',
|
||||
''
|
||||
].join('\n')
|
||||
@@ -641,6 +641,73 @@ describePosix('local PTY shell-ready launch config', () => {
|
||||
expect(output).toContain('CHILD_TEMP_PC=unset')
|
||||
})
|
||||
|
||||
itWithBash('does not arm bash exec hooks for redirection-only commands', async () => {
|
||||
const { getBashShellReadyRcfileContent } = await importFreshLocalPtyShellReady()
|
||||
writeFileSync(
|
||||
join(userDataPath, '.bash_profile'),
|
||||
[
|
||||
'unset PROMPT_COMMAND',
|
||||
'exec 3>"$HOME/startup-redirection"',
|
||||
'printf "REDIRECTION_PC=%s:ARMED=%s\\n" "${PROMPT_COMMAND+set}" "$_orca_exec_prompt_hooks_installed"',
|
||||
'exec 3>&-',
|
||||
''
|
||||
].join('\n')
|
||||
)
|
||||
|
||||
const output = runInteractiveBashRcfile(getBashShellReadyRcfileContent(), userDataPath)
|
||||
|
||||
expect(output).toContain('REDIRECTION_PC=:ARMED=0')
|
||||
})
|
||||
|
||||
itWithBash('survives nounset before an exec replacement without PROMPT_COMMAND', async () => {
|
||||
const { getBashShellReadyRcfileContent } = await importFreshLocalPtyShellReady()
|
||||
writeFileSync(
|
||||
join(userDataPath, '.bash_profile'),
|
||||
[
|
||||
'if [[ -z "${ORCA_EXEC_REPRO_DONE:-}" ]]; then',
|
||||
' export ORCA_EXEC_REPRO_DONE=1',
|
||||
' unset PROMPT_COMMAND',
|
||||
' set -u',
|
||||
' exec -a figterm-test bash --noprofile --norc -l -i',
|
||||
'fi',
|
||||
''
|
||||
].join('\n')
|
||||
)
|
||||
|
||||
const output = runInteractiveBashRcfile(
|
||||
getBashShellReadyRcfileContent(),
|
||||
userDataPath,
|
||||
'printf \'NOUNSET_EXEC_PC=%s\\n\' "$PROMPT_COMMAND"\nexit 0\n'
|
||||
)
|
||||
|
||||
expect(output).toContain('NOUNSET_EXEC_PC=__orca_exec_osc133_precmd')
|
||||
expect(output).not.toContain('unbound variable')
|
||||
})
|
||||
|
||||
itWithBash('flattens a prompt array replaced after a failed exec', async () => {
|
||||
const { getBashShellReadyRcfileContent } = await importFreshLocalPtyShellReady()
|
||||
writeFileSync(
|
||||
join(userDataPath, '.bash_profile'),
|
||||
[
|
||||
'PROMPT_COMMAND=(\'printf "ARRAY_ZERO\\n"\' \'printf "ARRAY_ONE\\n"\')',
|
||||
'shopt -s execfail',
|
||||
'exec /orca-missing-replacement 2>/dev/null',
|
||||
'PROMPT_COMMAND=(\'printf "ARRAY_ZERO\\n"\' \'printf "ARRAY_ONE\\n"\')',
|
||||
''
|
||||
].join('\n')
|
||||
)
|
||||
|
||||
const output = runInteractiveBashRcfile(
|
||||
getBashShellReadyRcfileContent(),
|
||||
userDataPath,
|
||||
'exit 0\n'
|
||||
)
|
||||
|
||||
expect(output.split('ARRAY_ZERO')).toHaveLength(2)
|
||||
expect(output.split('ARRAY_ONE')).toHaveLength(2)
|
||||
expect(output).not.toContain('__orca_exec_osc133_precmd: command not found')
|
||||
})
|
||||
|
||||
itWithBash('preserves user-enabled functrace and nested DEBUG trap behavior', async () => {
|
||||
const { getBashShellReadyRcfileContent } = await importFreshLocalPtyShellReady()
|
||||
writeFileSync(
|
||||
|
||||
Reference in New Issue
Block a user