mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 08:01:56 +00:00
Deliver local POSIX Codex startup commands through the shell wrapper at shell initialization, preventing duplicate PTY echo.
221 lines
11 KiB
TypeScript
221 lines
11 KiB
TypeScript
// Why: local PTYs and the daemon/SSH path must use identical ZDOTDIR discovery;
|
|
// small drift here breaks different terminal transports in different ways.
|
|
import { SHELL_STARTUP_FEATURE_ENV } from './shell-startup-features'
|
|
import { POSIX_SHELL_STARTUP_COMMAND_ENV } from './pty/posix-shell-startup-command'
|
|
|
|
/** Basename of the file every Orca-generated zsh wrapper dir is stamped with. */
|
|
export const ZSH_WRAPPER_DIR_MARKER_FILE = '.orca-shell-wrapper'
|
|
|
|
export const ZSH_WRAPPER_DIR_MARKER_CONTENT = `# Orca-generated zsh startup wrapper directory.
|
|
# Its presence is how Orca recognises its own wrapper dir instead of treating it
|
|
# as the user's ZDOTDIR. Do not edit; the whole directory is regenerated.
|
|
`
|
|
|
|
/**
|
|
* The first executable lines of every zsh wrapper: read the feature allowlist,
|
|
* then destroy the variable.
|
|
*
|
|
* Why destroy it here: `_orca_shell_features` is a plain (non-exported) shell
|
|
* variable, so it survives .zshenv -> .zprofile -> .zshrc -> .zlogin in this
|
|
* process but physically cannot reach a child. Unsetting before the user's own
|
|
* .zshenv is sourced means nothing the user's config spawns can see or inherit
|
|
* Orca's feature selection.
|
|
*/
|
|
export const ZSH_FEATURE_CHANNEL_BLOCK = `builtin typeset -ga _orca_shell_features
|
|
_orca_shell_features=(\${(s:,:)\${${SHELL_STARTUP_FEATURE_ENV}:-}})
|
|
builtin unset ${SHELL_STARTUP_FEATURE_ENV}
|
|
# Why ORCA_HISTFILE is consumed HERE and not in the deferred hook: a user config
|
|
# that replaces precmd_functions wholesale drops the hook, and an exported value
|
|
# nothing will ever consume is then inherited by every child of this pane,
|
|
# including a nested Orca (#11146). Captured non-exported, it cannot escape.
|
|
builtin typeset -g _orca_histfile="\${ORCA_HISTFILE:-}"
|
|
builtin unset ORCA_HISTFILE
|
|
__orca_has_feature() { (( \${_orca_shell_features[(Ie)$1]} )) }`
|
|
|
|
/** The bash rcfile equivalent of ZSH_FEATURE_CHANNEL_BLOCK. */
|
|
export const BASH_FEATURE_CHANNEL_BLOCK = `_orca_shell_features=",\${${SHELL_STARTUP_FEATURE_ENV}:-},"
|
|
builtin unset ${SHELL_STARTUP_FEATURE_ENV}
|
|
__orca_has_feature() { [[ "$_orca_shell_features" == *",$1,"* ]]; }`
|
|
|
|
// Why one line usable by both languages: __orca_has_feature is defined with the
|
|
// same name and semantics in the zsh and bash channel blocks above.
|
|
export const SHELL_STARTUP_IDENTITY_MARKER_BLOCK = `__orca_has_feature identity && printf "\\033]777;orca-shell-start:%s\\007" "$$"`
|
|
|
|
/**
|
|
* The first executable lines of the wrapper: give ZDOTDIR back to the user.
|
|
*
|
|
* Why before anything else: every later startup file — the user's .zprofile,
|
|
* the system /etc/zshrc, .zshrc, .zlogin — is found through ZDOTDIR. Handing it
|
|
* back here means zsh reads all of them from the user's own directory, exactly
|
|
* as it would with no wrapper at all. In particular /etc/zshrc's unguarded
|
|
* `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` then derives the user's own path
|
|
* instead of one inside Orca's wrapper dir, so #11044 cannot happen rather than
|
|
* having to be repaired afterwards.
|
|
*
|
|
* ORCA_ORIG_ZDOTDIR is consumed: it has done its job, and leaving it exported
|
|
* would hand a stale value to everything this pane launches.
|
|
*
|
|
* Why the value is vetted rather than trusted: the launch config only sets this
|
|
* when it resolved a usable dir, but a pane also inherits its parent's
|
|
* environment, so a stale ORCA_ORIG_ZDOTDIR written by an older build can arrive
|
|
* on its own. Handing that back would point ZDOTDIR at an Orca wrapper dir — the
|
|
* self-loop the Node-side ownership check exists to prevent, arriving by a route
|
|
* that check never sees. Identification stays positive, as it is in Node: a
|
|
* stamped marker file, or Orca's own path shape for wrappers older builds wrote.
|
|
*/
|
|
export const ZSH_ZDOTDIR_HANDBACK_BLOCK = `__orca_usable_zdotdir() {
|
|
[[ -n "\${1:-}" ]] || return 1
|
|
# Orca's own dir, by marker file or by the shape older builds wrote.
|
|
[[ "$1" != */shell-ready/zsh ]] || return 1
|
|
[[ ! -f "$1/${ZSH_WRAPPER_DIR_MARKER_FILE}" ]] || return 1
|
|
# A directory holding no zsh startup file at all is not a config root,
|
|
# whoever wrote it — and a stale value pointing at one would stop zsh from
|
|
# ever reading the user's real .zshenv.
|
|
local _orca_startup_file
|
|
for _orca_startup_file in .zshenv .zshrc .zprofile .zlogin; do
|
|
[[ -r "$1/$_orca_startup_file" ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
if __orca_usable_zdotdir "\${ORCA_ORIG_ZDOTDIR:-}"; then
|
|
builtin export ZDOTDIR="$ORCA_ORIG_ZDOTDIR"
|
|
else
|
|
builtin unset ZDOTDIR
|
|
fi
|
|
builtin unset ORCA_ORIG_ZDOTDIR ORCA_ZSHENV_SOURCE_DIR
|
|
builtin unfunction __orca_usable_zdotdir`
|
|
|
|
/**
|
|
* Sources the user's own .zshenv, then arms the deferred hook.
|
|
*
|
|
* Why `{ } always { }`: the whole compound command is parsed before any of it
|
|
* runs, so the registration below is already parsed as zsh even if the user's
|
|
* .zshenv switches the shell into sh emulation. Sourcing at wrapper top level
|
|
* (not in a function or subshell) keeps the user's exports, functions, fpath
|
|
* typesets and options in their normal scope.
|
|
*
|
|
* Why guarded on absence: the hook is idempotent, but appending it twice would
|
|
* still leave a dead name in the user's precmd_functions.
|
|
*/
|
|
export const ZSH_USER_ZSHENV_SOURCE_BLOCK = `{
|
|
builtin typeset _orca_user_zshenv="\${ZDOTDIR-$HOME}/.zshenv"
|
|
[[ ! -r "$_orca_user_zshenv" ]] || builtin source -- "$_orca_user_zshenv"
|
|
} always {
|
|
builtin unset _orca_user_zshenv
|
|
builtin typeset -ag precmd_functions
|
|
(( \${precmd_functions[(Ie)__orca_deferred_init]} )) || precmd_functions+=(__orca_deferred_init)
|
|
}`
|
|
|
|
// Why: daemon, local, and relay wrappers must preserve one Bash prompt-hook contract.
|
|
export { BASH_PROMPT_COMMAND_COMPOSITION_BLOCK } from './bash-prompt-command-composition'
|
|
|
|
/**
|
|
* Restores the worktree-scoped HISTFILE that macOS `/etc/zshrc` destroys.
|
|
*
|
|
* Bash only. The zsh wrapper no longer needs this: it hands ZDOTDIR back before
|
|
* /etc/zshrc runs, so the value that file derives is already the user's own, and
|
|
* the scoped path is re-applied from a non-exported variable in the deferred
|
|
* hook. The `elif` below is inert under bash, where ZDOTDIR is normally unset.
|
|
*
|
|
* That file assigns `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no
|
|
* check-before-set, and it runs before any wrapper file Orca controls — so the
|
|
* injected value is already gone, and because ZDOTDIR still points at Orca's
|
|
* wrapper dir the replacement lands INSIDE it. Per-worktree history was a
|
|
* silent no-op on the primary platform as a result (#11044).
|
|
*
|
|
* `builtin unset ORCA_HISTFILE` is the root-cause fix for #11146: the variable
|
|
* cannot be inherited by anything the shell later spawns if it no longer exists
|
|
* once it has been consumed. HISTFILE itself stays exported.
|
|
*/
|
|
export const BASH_HISTFILE_RESTORE_BLOCK = `if [[ -n "\${ORCA_HISTFILE:-}" ]]; then
|
|
HISTFILE="$ORCA_HISTFILE"
|
|
builtin unset ORCA_HISTFILE
|
|
elif [[ "\${HISTFILE:-}" == "$ZDOTDIR/.zsh_history" ]]; then
|
|
# Why also when Orca injected nothing: /etc/zshrc derived this from Orca's
|
|
# wrapper ZDOTDIR, so history would accumulate INSIDE the wrapper dir and the
|
|
# user's real history would be invisible — the plain #11044 bug, with no
|
|
# per-worktree scoping involved. Matching the exact clobbered value means a
|
|
# HISTFILE the user set deliberately is never touched.
|
|
HISTFILE="\${ORCA_ORIG_ZDOTDIR:-$HOME}/.zsh_history"
|
|
fi`
|
|
|
|
// Why: zsh precmd fires before zle switches the PTY into line-editing mode,
|
|
// so the marker must be emitted from zle-line-init. Registering it through
|
|
// add-zle-hook-widget is unsafe: the azhw dispatcher aborts its hook chain
|
|
// when an earlier hook exits non-zero, and a pre-existing raw user widget
|
|
// (e.g. oh-my-zsh vi-mode without VI_MODE_SET_CURSOR) is preserved as the
|
|
// first hook and fails — silently suppressing the marker and stalling every
|
|
// startup command on the pre-ready timeout. Instead, own zle-line-init: emit
|
|
// the marker first, then chain to whatever widget was installed before.
|
|
export function getZshShellReadyMarkerRegistrationBlock(
|
|
escapedMarker: string,
|
|
supportsStartupCommand = false
|
|
): string {
|
|
const markerBlock = supportsStartupCommand
|
|
? ` if [[ "\${__orca_emit_ready_marker-1}" == 1 ]]; then
|
|
printf "${escapedMarker}"
|
|
fi`
|
|
: ` printf "${escapedMarker}"`
|
|
const startupCommandBlock = supportsStartupCommand
|
|
? `
|
|
if (( \${+${POSIX_SHELL_STARTUP_COMMAND_ENV}} )); then
|
|
BUFFER="\$${POSIX_SHELL_STARTUP_COMMAND_ENV}"
|
|
CURSOR=\${#BUFFER}
|
|
builtin unset ${POSIX_SHELL_STARTUP_COMMAND_ENV}
|
|
zle accept-line
|
|
fi`
|
|
: ''
|
|
return `# Why: capture the prior zle-line-init so the marker chains to it. On a
|
|
# re-source we are already the bound widget, so keep the function captured
|
|
# the first time instead of clobbering it to empty (which would silently
|
|
# drop the user's widget on every prompt after the second source). Only
|
|
# user-defined widgets are chainable as plain functions; builtin/completion
|
|
# forms (rare for zle-line-init) are left unchained.
|
|
if [[ "\${widgets[zle-line-init]:-}" == "user:__orca_prompt_mark" ]]; then
|
|
:
|
|
elif (( \${+widgets[zle-line-init]} )) && [[ "\${widgets[zle-line-init]}" == user:* ]]; then
|
|
__orca_prev_line_init_fn="\${widgets[zle-line-init]#user:}"
|
|
else
|
|
__orca_prev_line_init_fn=""
|
|
fi
|
|
__orca_prompt_mark() {
|
|
${markerBlock}
|
|
# Why: call the prior hook as a plain function, not an aliased widget, so
|
|
# $WIDGET stays zle-line-init for add-zle-hook-widget dispatchers.
|
|
if [[ -n "\${__orca_prev_line_init_fn:-}" ]]; then
|
|
"\${__orca_prev_line_init_fn}" "$@"
|
|
fi${startupCommandBlock}
|
|
}
|
|
zle -N zle-line-init __orca_prompt_mark`.replace(/\n\n}\n$/, '\n}\n')
|
|
}
|
|
|
|
// Why: fish has no ZDOTDIR-style wrapper dir, so the marker rides `--init-command`,
|
|
// which fish runs AFTER config.fish (verified on 4.7.1) — the same last-word
|
|
// guarantee the zsh/bash wrapper files rely on. It fires on fish_prompt, the
|
|
// earliest event fish exposes (STA-3417). Unlike zsh's zle-line-init this lands
|
|
// just *before* fish arms `?2004h`, which PostReadyFlushGate absorbs. `builtin
|
|
// printf` so a user-defined printf can't silently swallow the marker and send
|
|
// every launch to the ready timeout.
|
|
//
|
|
// Why no feature variable: the init command is composed per pane, so the
|
|
// selection is already baked into the text and nothing needs to be exported.
|
|
export function getFishShellReadyInitCommand(
|
|
escapedMarker: string,
|
|
emitReadyMarker = true,
|
|
supportsStartupCommand = false
|
|
): string {
|
|
const readyMarkerBlock = emitReadyMarker ? ` builtin printf "${escapedMarker}"\n` : ''
|
|
const startupCommandBlock = supportsStartupCommand
|
|
? ` if set -q ${POSIX_SHELL_STARTUP_COMMAND_ENV}
|
|
set -l __orca_command "\$${POSIX_SHELL_STARTUP_COMMAND_ENV}"
|
|
set -e ${POSIX_SHELL_STARTUP_COMMAND_ENV}
|
|
builtin printf '%s\\n' "$__orca_command"
|
|
eval "$__orca_command"
|
|
return $status
|
|
end\n`
|
|
: ''
|
|
return `function __orca_shell_ready_marker --on-event fish_prompt
|
|
${readyMarkerBlock} functions -e __orca_shell_ready_marker
|
|
${startupCommandBlock}end`
|
|
}
|