import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'fs' import { homedir } from 'os' import { basename, dirname, join } from 'path' import { getPosixOmpShellWrapper } from '../main/pty/omp-shell-wrapper' const RELAY_SHELL_READY_DIR = '.orca-relay/shell-ready' const POSIX_LOGIN_ARGS = ['-l'] export type RelayShellLaunchConfig = { args: string[] env: Record } function quotePosixSingle(value: string): string { return `'${value.replace(/'/g, `'\\''`)}'` } function hasOverlayRestoreEnv(env: Record): boolean { return Boolean( env.ORCA_OPENCODE_CONFIG_DIR || env.ORCA_PI_CODING_AGENT_DIR || env.ORCA_OMP_CODING_AGENT_DIR || env.ORCA_REMOTE_CLI_BIN_DIR ) } function getWrapperRoot(env: Record): string { return join(env.HOME || process.env.HOME || homedir(), RELAY_SHELL_READY_DIR) } function normalizeOriginalZdotdirCandidate(value: string | undefined): string | null { if (!value) { return null } const normalized = value.replace(/\/+$/, '') if (!normalized || normalized.endsWith('/shell-ready/zsh')) { return null } return value } function resolveOriginalZdotdir(env: Record): string { return ( normalizeOriginalZdotdirCandidate(env.ZDOTDIR) || normalizeOriginalZdotdirCandidate(env.ORCA_ORIG_ZDOTDIR) || env.HOME || process.env.HOME || '' ) } function ensureOverlayRestoreWrappers(root: string): void { const zshDir = join(root, 'zsh') const bashDir = join(root, 'bash') const zshEnv = `# Orca relay zsh overlay wrapper export ORCA_ORIG_ZDOTDIR="\${ORCA_ORIG_ZDOTDIR:-$HOME}" case "\${ORCA_ORIG_ZDOTDIR%/}" in */shell-ready/zsh) export ORCA_ORIG_ZDOTDIR="$HOME" ;; esac [[ -f "$ORCA_ORIG_ZDOTDIR/.zshenv" ]] && source "$ORCA_ORIG_ZDOTDIR/.zshenv" export ORCA_USER_ZDOTDIR="\${ZDOTDIR:-\${ORCA_ORIG_ZDOTDIR:-$HOME}}" case "\${ORCA_USER_ZDOTDIR%/}" in */shell-ready/zsh) export ORCA_USER_ZDOTDIR="$HOME" ;; esac export ZDOTDIR=${quotePosixSingle(zshDir)} ` const zshProfile = `# Orca relay zsh overlay wrapper _orca_home="\${ORCA_USER_ZDOTDIR:-\${ORCA_ORIG_ZDOTDIR:-$HOME}}" case "\${_orca_home%/}" in */shell-ready/zsh) _orca_home="$HOME" ;; esac [[ -f "$_orca_home/.zprofile" ]] && source "$_orca_home/.zprofile" ` const zshRc = `# Orca relay zsh overlay wrapper _orca_home="\${ORCA_USER_ZDOTDIR:-\${ORCA_ORIG_ZDOTDIR:-$HOME}}" case "\${_orca_home%/}" in */shell-ready/zsh) _orca_home="$HOME" ;; esac if [[ -o interactive && -f "$_orca_home/.zshrc" ]]; then source "$_orca_home/.zshrc" fi if [[ ! -o login ]]; then # Why: remote startup files can re-export user defaults after relay spawn. [[ -n "\${ORCA_OPENCODE_CONFIG_DIR:-}" ]] && export OPENCODE_CONFIG_DIR="\${ORCA_OPENCODE_CONFIG_DIR}" [[ -n "\${ORCA_PI_CODING_AGENT_DIR:-}" ]] && export PI_CODING_AGENT_DIR="\${ORCA_PI_CODING_AGENT_DIR}" if [[ -z "\${ORCA_PI_CODING_AGENT_DIR:-}" && -n "\${ORCA_OMP_CODING_AGENT_DIR:-}" ]]; then export PI_CODING_AGENT_DIR="\${ORCA_OMP_CODING_AGENT_DIR}" fi [[ -n "\${ORCA_REMOTE_CLI_BIN_DIR:-}" ]] && case ":$PATH:" in *:"\${ORCA_REMOTE_CLI_BIN_DIR}":*) ;; *) export PATH="\${ORCA_REMOTE_CLI_BIN_DIR}:$PATH" ;; esac ${getPosixOmpShellWrapper()} fi ` const zshLogin = `# Orca relay zsh overlay wrapper _orca_home="\${ORCA_USER_ZDOTDIR:-\${ORCA_ORIG_ZDOTDIR:-$HOME}}" case "\${_orca_home%/}" in */shell-ready/zsh) _orca_home="$HOME" ;; esac if [[ -o interactive && -f "$_orca_home/.zlogin" ]]; then source "$_orca_home/.zlogin" fi # Why: .zlogin is the final zsh login startup file before the prompt. [[ -n "\${ORCA_OPENCODE_CONFIG_DIR:-}" ]] && export OPENCODE_CONFIG_DIR="\${ORCA_OPENCODE_CONFIG_DIR}" [[ -n "\${ORCA_PI_CODING_AGENT_DIR:-}" ]] && export PI_CODING_AGENT_DIR="\${ORCA_PI_CODING_AGENT_DIR}" if [[ -z "\${ORCA_PI_CODING_AGENT_DIR:-}" && -n "\${ORCA_OMP_CODING_AGENT_DIR:-}" ]]; then export PI_CODING_AGENT_DIR="\${ORCA_OMP_CODING_AGENT_DIR}" fi [[ -n "\${ORCA_REMOTE_CLI_BIN_DIR:-}" ]] && case ":$PATH:" in *:"\${ORCA_REMOTE_CLI_BIN_DIR}":*) ;; *) export PATH="\${ORCA_REMOTE_CLI_BIN_DIR}:$PATH" ;; esac ${getPosixOmpShellWrapper()} ` const bashRc = `# Orca relay bash overlay wrapper [[ -f /etc/profile ]] && source /etc/profile if [[ -f "$HOME/.bash_profile" ]]; then source "$HOME/.bash_profile" elif [[ -f "$HOME/.bash_login" ]]; then source "$HOME/.bash_login" elif [[ -f "$HOME/.profile" ]]; then source "$HOME/.profile" fi # Why: remote startup files can re-export user defaults after relay spawn. [[ -n "\${ORCA_OPENCODE_CONFIG_DIR:-}" ]] && export OPENCODE_CONFIG_DIR="\${ORCA_OPENCODE_CONFIG_DIR}" [[ -n "\${ORCA_PI_CODING_AGENT_DIR:-}" ]] && export PI_CODING_AGENT_DIR="\${ORCA_PI_CODING_AGENT_DIR}" if [[ -z "\${ORCA_PI_CODING_AGENT_DIR:-}" && -n "\${ORCA_OMP_CODING_AGENT_DIR:-}" ]]; then export PI_CODING_AGENT_DIR="\${ORCA_OMP_CODING_AGENT_DIR}" fi [[ -n "\${ORCA_REMOTE_CLI_BIN_DIR:-}" ]] && case ":$PATH:" in *:"\${ORCA_REMOTE_CLI_BIN_DIR}":*) ;; *) export PATH="\${ORCA_REMOTE_CLI_BIN_DIR}:$PATH" ;; esac ${getPosixOmpShellWrapper()} # Why: SSH bash sessions need the same command lifecycle markers as local # bash so agent rows stop showing "working" when the foreground command exits. __orca_osc133_precmd() { local exit_code=$? __orca_in_prompt_command=1 if [[ -n "\${__orca_in_command:-}" ]]; then printf "\\033]133;D;%s\\007" "$exit_code" unset __orca_in_command fi printf "\\033]133;A\\007" } __orca_osc133_prompt_done() { unset __orca_in_prompt_command } __orca_run_user_debug_trap() { if [[ -n "\${__orca_user_debug_trap:-}" ]]; then eval "$__orca_user_debug_trap" || true fi } __orca_osc133_preexec() { __orca_run_user_debug_trap [[ -z "\${__orca_in_prompt_command:-}" ]] || return case "$BASH_COMMAND" in *__orca_osc133_precmd*|*__orca_osc133_prompt_done*) return ;; esac printf "\\033]133;C\\007" __orca_in_command=1 } __orca_normalize_prompt_command() { local __orca_joined="" __orca_prompt_part if [[ "$(declare -p PROMPT_COMMAND 2>/dev/null)" == "declare -a"* ]]; then for __orca_prompt_part in "\${PROMPT_COMMAND[@]}"; do [[ -n "$__orca_prompt_part" ]] || continue if [[ -n "$__orca_joined" ]]; then __orca_joined="$__orca_joined;$__orca_prompt_part" else __orca_joined="$__orca_prompt_part" fi done PROMPT_COMMAND="$__orca_joined" fi } __orca_prepend_prompt_command() { __orca_normalize_prompt_command PROMPT_COMMAND="__orca_osc133_precmd\${PROMPT_COMMAND:+;\${PROMPT_COMMAND}}" } __orca_append_prompt_command() { local command="$1" __orca_normalize_prompt_command if [[ -n "\${PROMPT_COMMAND:-}" ]]; then PROMPT_COMMAND="\${PROMPT_COMMAND};$command" else PROMPT_COMMAND="$command" fi } __orca_prepend_prompt_command __orca_append_prompt_command "__orca_osc133_prompt_done" __orca_debug_trap_spec="$(trap -p DEBUG)" if [[ -n "$__orca_debug_trap_spec" ]]; then __orca_debug_trap_command="\${__orca_debug_trap_spec#trap -- }" __orca_debug_trap_command="\${__orca_debug_trap_command% DEBUG}" eval "__orca_user_debug_trap=$__orca_debug_trap_command" fi unset __orca_debug_trap_spec __orca_debug_trap_command unset -f __orca_normalize_prompt_command __orca_prepend_prompt_command __orca_append_prompt_command # Why: arm DEBUG after wrapper setup so the relay rcfile itself does not emit # fake command-start/end markers before the first prompt. trap '__orca_osc133_preexec' DEBUG ` const files = [ [join(zshDir, '.zshenv'), zshEnv], [join(zshDir, '.zprofile'), zshProfile], [join(zshDir, '.zshrc'), zshRc], [join(zshDir, '.zlogin'), zshLogin], [join(bashDir, 'rcfile'), bashRc] ] as const for (const [path, content] of files) { mkdirSync(dirname(path), { recursive: true }) let existing: string | null = null try { existing = readFileSync(path, 'utf8') } catch { existing = null } // Why: relay wrapper files persist under ~/.orca-relay across app // upgrades. Existence alone is not enough; stale wrappers would miss // later fixes such as preserving post-.zshenv ZDOTDIR. if (existing !== content) { writeFileSync(path, content, 'utf8') } chmodSync(path, 0o644) } } export function getRelayShellLaunchConfig( shellPath: string, env: Record ): RelayShellLaunchConfig { if (process.platform === 'win32') { return { args: POSIX_LOGIN_ARGS, env: {} } } const shellName = basename(shellPath).toLowerCase() if (shellName !== 'zsh' && shellName !== 'bash') { return { args: POSIX_LOGIN_ARGS, env: {} } } if (shellName === 'zsh' && !hasOverlayRestoreEnv(env)) { return { args: POSIX_LOGIN_ARGS, env: {} } } const root = getWrapperRoot(env) ensureOverlayRestoreWrappers(root) if (shellName === 'zsh') { return { args: POSIX_LOGIN_ARGS, env: { ORCA_ORIG_ZDOTDIR: resolveOriginalZdotdir(env), ZDOTDIR: join(root, 'zsh') } } } return { args: ['--rcfile', join(root, 'bash', 'rcfile')], env: {} } }