Fix zsh ZDOTDIR context in shell-ready wrappers (#4667)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo Hong 2026-06-04 20:17:47 -04:00 committed by GitHub
parent 1cd6df9137
commit 3600cab06d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 214 additions and 67 deletions

View File

@ -57,6 +57,17 @@ function expectBashOsc133Lifecycle(output: string): void {
expect(output.split(oscD)).toHaveLength(3)
}
function expectZdotdirSourceContext(content: string, fileName: '.zprofile' | '.zshrc' | '.zlogin') {
expect(content).toContain('export ZDOTDIR="$_orca_home"')
expect(content).toContain(`source "$_orca_home/${fileName}"`)
expect(content).toContain('export ZDOTDIR="$_orca_wrapper_zdotdir"')
}
function expectFinalZdotdirRestoreContext(content: string) {
expect(content).toContain("after Orca's last wrapper file has loaded")
expect(content).toContain('export ZDOTDIR="$_orca_home"')
}
describePosix('daemon shell-ready launch config', () => {
let previousUserDataPath: string | undefined
let previousOrcaOrigZdotdir: string | undefined
@ -213,9 +224,17 @@ describePosix('daemon shell-ready launch config', () => {
getShellReadyLaunchConfig('/bin/zsh')
const zshenv = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zshenv'), 'utf8')
const zprofile = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zprofile'), 'utf8')
const zshrc = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zshrc'), 'utf8')
const zlogin = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zlogin'), 'utf8')
expect(zshenv).toContain('_orca_user_zdotdir="${_orca_spawn_orig_zdotdir:-$HOME}"')
expect(zshenv).toContain('*/shell-ready/zsh) _orca_user_zdotdir="$HOME" ;;')
expect(zshenv).toContain('""|*/shell-ready/zsh) export ORCA_ORIG_ZDOTDIR="$HOME" ;;')
expectZdotdirSourceContext(zprofile, '.zprofile')
expectZdotdirSourceContext(zshrc, '.zshrc')
expectZdotdirSourceContext(zlogin, '.zlogin')
expectFinalZdotdirRestoreContext(zshrc)
expectFinalZdotdirRestoreContext(zlogin)
})
it('writes wrappers that restore OpenCode and Pi config after user startup files', async () => {

View File

@ -11,7 +11,11 @@ import {
isPowerShellExecutableName
} from '../powershell-osc133-bootstrap'
import { getPosixOmpShellWrapper } from '../pty/omp-shell-wrapper'
import { getZshEnvTemplate } from '../shell-templates'
import {
getZshEnvTemplate,
getZshFinalZdotdirRestoreBlock,
getZshStartupFileSourceBlock
} from '../shell-templates'
const ORCA_USER_DATA_PATH_ENV = 'ORCA_USER_DATA_PATH'
const SHELL_READY_MARKER = '\\033]777;orca-shell-ready\\007'
@ -195,10 +199,11 @@ trap '__orca_osc133_preexec' DEBUG
export function getDaemonZshShellReadyRcfileContent(): string {
return `# Orca daemon zsh shell-ready wrapper
_orca_home="\${ORCA_ORIG_ZDOTDIR:-$HOME}"
if [[ "$_orca_home" != "$ZDOTDIR" && -o interactive && -f "$_orca_home/.zshrc" ]]; then
source "$_orca_home/.zshrc"
fi
${getZshStartupFileSourceBlock({
fileName: '.zshrc',
interactiveOnly: true,
skipWhenHomeIsCurrentZdotdir: true
})}
__orca_restore_attribution_path() {
[[ -n "\${ORCA_ATTRIBUTION_SHIM_DIR:-}" ]] || return 0
case "$PATH" in
@ -234,6 +239,9 @@ __orca_osc133_preexec() {
# Why: prepend so Orca captures $? before user prompt hooks can overwrite it.
precmd_functions=(__orca_osc133_precmd \${precmd_functions[@]})
preexec_functions=(__orca_osc133_preexec \${preexec_functions[@]})
if [[ ! -o login ]]; then
${getZshFinalZdotdirRestoreBlock()}
fi
`
}
@ -252,21 +260,11 @@ function ensureShellReadyWrappers(): void {
const zshEnv = getZshEnvTemplate(zshDir, 'daemon')
const zshProfile = `# Orca daemon zsh shell-ready wrapper
_orca_home="\${ORCA_ORIG_ZDOTDIR:-$HOME}"
case "\${_orca_home%/}" in
*/shell-ready/zsh) _orca_home="$HOME" ;;
esac
[[ -f "$_orca_home/.zprofile" ]] && source "$_orca_home/.zprofile"
${getZshStartupFileSourceBlock({ fileName: '.zprofile' })}
`
const zshRc = getDaemonZshShellReadyRcfileContent()
const zshLogin = `# Orca daemon zsh shell-ready wrapper
_orca_home="\${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
${getZshStartupFileSourceBlock({ fileName: '.zlogin', interactiveOnly: true })}
__orca_restore_attribution_path() {
[[ -n "\${ORCA_ATTRIBUTION_SHIM_DIR:-}" ]] || return 0
case "$PATH" in
@ -293,6 +291,7 @@ if [[ "\${ORCA_SHELL_READY_MARKER:-0}" == "1" ]]; then
zle -N __orca_prompt_mark
add-zle-hook-widget line-init __orca_prompt_mark
fi
${getZshFinalZdotdirRestoreBlock()}
`
const bashRc = getDaemonBashShellReadyRcfileContent()

View File

@ -61,7 +61,7 @@ EOF
expect(stdout).toMatchInlineSnapshot(`
"HOME=<HOME>
ZDOTDIR=<WRAPPER_DIR>
ZDOTDIR=<HOME>/.config/zsh
"
`)
})
@ -85,8 +85,8 @@ EOF
)
expect(stdout).toMatchInlineSnapshot(`
"ORCA_ORIG_ZDOTDIR=<HOME>/.config/zsh-remote
ZDOTDIR=<WRAPPER_DIR>
"ORCA_ORIG_ZDOTDIR=<HOME>
ZDOTDIR=<HOME>/.config/zsh-remote
"
`)
})

View File

@ -25,8 +25,7 @@ EOF
expect(stdout).toMatchInlineSnapshot(`
"HOME=<HOME>
ORCA_ORIG_ZDOTDIR=<HOME>/.config/zsh
ZDOTDIR=<WRAPPER_DIR>
ZDOTDIR=<HOME>/.config/zsh
"
`)
})
@ -93,8 +92,7 @@ Use `toMatchInlineSnapshot()` to keep expected output visible in the test file:
```typescript
expect(stdout).toMatchInlineSnapshot(`
"HOME=<HOME>
ORCA_ORIG_ZDOTDIR=<HOME>/.config/zsh
ZDOTDIR=<WRAPPER_DIR>
ZDOTDIR=<HOME>/.config/zsh
"
`)
```

View File

@ -62,7 +62,11 @@ export async function shellScriptTest(
const env: Record<string, string> = {
...config.env,
HOME: testHome
// Why: the framework creates user startup files under testHome after
// computing the wrapper config; route wrapper discovery to that fixture.
HOME: testHome,
ORCA_ORIG_ZDOTDIR: testHome,
ORCA_ZSHENV_SOURCE_DIR: testHome
}
const spawnOptions = {

View File

@ -172,6 +172,17 @@ function expectBashOsc133Lifecycle(output: string): void {
expect(output.split(oscD)).toHaveLength(3)
}
function expectZdotdirSourceContext(content: string, fileName: '.zprofile' | '.zshrc' | '.zlogin') {
expect(content).toContain('export ZDOTDIR="$_orca_home"')
expect(content).toContain(`source "$_orca_home/${fileName}"`)
expect(content).toContain('export ZDOTDIR="$_orca_wrapper_zdotdir"')
}
function expectFinalZdotdirRestoreContext(content: string) {
expect(content).toContain("after Orca's last wrapper file has loaded")
expect(content).toContain('export ZDOTDIR="$_orca_home"')
}
describePosix('local PTY shell-ready launch config', () => {
let userDataPath: string
let previousOrcaOrigZdotdir: string | undefined
@ -288,9 +299,17 @@ describePosix('local PTY shell-ready launch config', () => {
getShellReadyLaunchConfig('/bin/zsh')
const zshenv = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zshenv'), 'utf8')
const zprofile = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zprofile'), 'utf8')
const zshrc = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zshrc'), 'utf8')
const zlogin = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zlogin'), 'utf8')
expect(zshenv).toContain('_orca_user_zdotdir="${_orca_spawn_orig_zdotdir:-$HOME}"')
expect(zshenv).toContain('*/shell-ready/zsh) _orca_user_zdotdir="$HOME" ;;')
expect(zshenv).toContain('""|*/shell-ready/zsh) export ORCA_ORIG_ZDOTDIR="$HOME" ;;')
expectZdotdirSourceContext(zprofile, '.zprofile')
expectZdotdirSourceContext(zshrc, '.zshrc')
expectZdotdirSourceContext(zlogin, '.zlogin')
expectFinalZdotdirRestoreContext(zshrc)
expectFinalZdotdirRestoreContext(zlogin)
})
it('writes wrappers that restore agent config homes after user startup files', async () => {
@ -644,6 +663,56 @@ export ZDOTDIR="$HOME/.config/zsh"
expect(result.stdout).toContain('from-zshenv-function')
})
it('sources user startup files with their own ZDOTDIR in scope', async () => {
// Why: plugin managers such as Antidote resolve files from $ZDOTDIR
// while .zprofile/.zshrc/.zlogin are sourced.
const xdgZshDir = join(testHome, '.config', 'zsh')
const zdotdirLog = join(testHome, 'zdotdir.log')
mkdirSync(xdgZshDir, { recursive: true })
writeFileSync(join(testHome, '.zshenv'), 'export ZDOTDIR="$HOME/.config/zsh"\n')
writeFileSync(
join(xdgZshDir, '.zprofile'),
'printf "zprofile=%s\\n" "$ZDOTDIR" >> "$HOME/zdotdir.log"\n'
)
writeFileSync(
join(xdgZshDir, '.zshrc'),
'printf "zshrc=%s\\n" "$ZDOTDIR" >> "$HOME/zdotdir.log"\n'
)
writeFileSync(
join(xdgZshDir, '.zlogin'),
'printf "zlogin=%s\\n" "$ZDOTDIR" >> "$HOME/zdotdir.log"\n'
)
const { getShellReadyLaunchConfig } = await importFreshLocalPtyShellReady()
const config = getShellReadyLaunchConfig('/bin/zsh')
const cleanEnv: Record<string, string | undefined> = { ...process.env, HOME: testHome }
delete cleanEnv.ZDOTDIR
delete cleanEnv.ORCA_ORIG_ZDOTDIR
cleanEnv.ZDOTDIR = config.env.ZDOTDIR
const result = spawnSync(
'zsh',
['-l', '-i', '-c', 'printf "command=%s\\n" "$ZDOTDIR" >> "$HOME/zdotdir.log"'],
{
env: cleanEnv as NodeJS.ProcessEnv,
encoding: 'utf8',
timeout: 5000
}
)
expect(result.status).toBe(0)
expect(readFileSync(zdotdirLog, 'utf8')).toBe(
[
`zprofile=${xdgZshDir}`,
`zshrc=${xdgZshDir}`,
`zlogin=${xdgZshDir}`,
`command=${xdgZshDir}`,
''
].join('\n')
)
})
it('survives early return in user .zshenv without crashing', async () => {
// Why: common pattern to skip non-interactive sourcing. A direct source
// at zsh top level must keep the wrapper running, matching normal zsh.

View File

@ -21,7 +21,11 @@ import {
isPowerShellExecutableName
} from '../powershell-osc133-bootstrap'
import { getPosixOmpShellWrapper } from '../pty/omp-shell-wrapper'
import { getZshEnvTemplate } from '../shell-templates'
import {
getZshEnvTemplate,
getZshFinalZdotdirRestoreBlock,
getZshStartupFileSourceBlock
} from '../shell-templates'
let didEnsureShellReadyWrappers = false
@ -259,10 +263,11 @@ trap '__orca_osc133_preexec' DEBUG
export function getZshShellReadyRcfileContent(): string {
return `# Orca zsh shell-ready wrapper
_orca_home="\${ORCA_ORIG_ZDOTDIR:-$HOME}"
if [[ "$_orca_home" != "$ZDOTDIR" && -o interactive && -f "$_orca_home/.zshrc" ]]; then
source "$_orca_home/.zshrc"
fi
${getZshStartupFileSourceBlock({
fileName: '.zshrc',
interactiveOnly: true,
skipWhenHomeIsCurrentZdotdir: true
})}
__orca_restore_attribution_path() {
[[ -n "\${ORCA_ATTRIBUTION_SHIM_DIR:-}" ]] || return 0
case "$PATH" in
@ -299,6 +304,9 @@ __orca_osc133_preexec() {
# Why: prepend so Orca captures $? before user prompt hooks can overwrite it.
precmd_functions=(__orca_osc133_precmd \${precmd_functions[@]})
preexec_functions=(__orca_osc133_preexec \${preexec_functions[@]})
if [[ ! -o login ]]; then
${getZshFinalZdotdirRestoreBlock()}
fi
`
}
@ -317,21 +325,11 @@ function ensureShellReadyWrappers(): void {
const zshEnv = getZshEnvTemplate(zshDir)
const zshProfile = `# Orca zsh shell-ready wrapper
_orca_home="\${ORCA_ORIG_ZDOTDIR:-$HOME}"
case "\${_orca_home%/}" in
*/shell-ready/zsh) _orca_home="$HOME" ;;
esac
[[ -f "$_orca_home/.zprofile" ]] && source "$_orca_home/.zprofile"
${getZshStartupFileSourceBlock({ fileName: '.zprofile' })}
`
const zshRc = getZshShellReadyRcfileContent()
const zshLogin = `# Orca zsh shell-ready wrapper
_orca_home="\${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
${getZshStartupFileSourceBlock({ fileName: '.zlogin', interactiveOnly: true })}
__orca_restore_attribution_path() {
[[ -n "\${ORCA_ATTRIBUTION_SHIM_DIR:-}" ]] || return 0
case "$PATH" in
@ -358,6 +356,7 @@ if [[ "\${ORCA_SHELL_READY_MARKER:-0}" == "1" ]]; then
zle -N __orca_prompt_mark
add-zle-hook-widget line-init __orca_prompt_mark
fi
${getZshFinalZdotdirRestoreBlock()}
`
const bashRc = getBashShellReadyRcfileContent()

View File

@ -71,3 +71,44 @@ export ZDOTDIR=${quotePosixSingle(zshDir)}
unset _orca_spawn_orig_zdotdir _orca_user_zdotdir _orca_zshenv_source_dir _orca_zshenv_path _orca_discovered_zdotdir
`
}
export function getZshStartupFileSourceBlock(options: {
fileName: '.zprofile' | '.zshrc' | '.zlogin'
homeExpression?: string
interactiveOnly?: boolean
skipWhenHomeIsCurrentZdotdir?: boolean
}): string {
const homeExpression = options.homeExpression ?? '"${ORCA_ORIG_ZDOTDIR:-$HOME}"'
const checks = [
options.skipWhenHomeIsCurrentZdotdir ? '"$_orca_home" != "$ZDOTDIR"' : null,
options.interactiveOnly ? '-o interactive' : null,
`-f "$_orca_home/${options.fileName}"`
].filter(Boolean)
return `_orca_home=${homeExpression}
case "\${_orca_home%/}" in
*/shell-ready/zsh) _orca_home="$HOME" ;;
esac
if [[ ${checks.join(' && ')} ]]; then
_orca_wrapper_zdotdir="$ZDOTDIR"
# Why: user startup files resolve plugin/config paths from their own ZDOTDIR;
# Orca restores its wrapper dir afterward so zsh still loads wrapper files.
export ZDOTDIR="$_orca_home"
source "$_orca_home/${options.fileName}"
export ZDOTDIR="$_orca_wrapper_zdotdir"
unset _orca_wrapper_zdotdir
fi
`
}
export function getZshFinalZdotdirRestoreBlock(homeExpression = '"${ORCA_ORIG_ZDOTDIR:-$HOME}"') {
return `_orca_home=${homeExpression}
case "\${_orca_home%/}" in
*/shell-ready/zsh) _orca_home="$HOME" ;;
esac
# Why: after Orca's last wrapper file has loaded, the interactive shell should
# expose the same ZDOTDIR a normal zsh startup would expose.
export ZDOTDIR="$_orca_home"
unset _orca_home
`
}

View File

@ -44,6 +44,17 @@ function expectBashOsc133Lifecycle(output: string): void {
expect(output.split(oscD)).toHaveLength(3)
}
function expectZdotdirSourceContext(content: string, fileName: '.zprofile' | '.zshrc' | '.zlogin') {
expect(content).toContain('export ZDOTDIR="$_orca_home"')
expect(content).toContain(`source "$_orca_home/${fileName}"`)
expect(content).toContain('export ZDOTDIR="$_orca_wrapper_zdotdir"')
}
function expectFinalZdotdirRestoreContext(content: string) {
expect(content).toContain("after Orca's last wrapper file has loaded")
expect(content).toContain('export ZDOTDIR="$_orca_home"')
}
describe('getRelayShellLaunchConfig', () => {
let homeDir: string
@ -72,12 +83,16 @@ describe('getRelayShellLaunchConfig', () => {
expect(readFileSync(join(zshRoot, '.zprofile'), 'utf8')).toContain(
'_orca_home="${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"'
)
expect(readFileSync(join(zshRoot, '.zshrc'), 'utf8')).toContain(
'_orca_home="${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"'
)
expect(readFileSync(join(zshRoot, '.zlogin'), 'utf8')).toContain(
'_orca_home="${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"'
)
const zprofile = readFileSync(join(zshRoot, '.zprofile'), 'utf8')
const zshrc = readFileSync(join(zshRoot, '.zshrc'), 'utf8')
const zlogin = readFileSync(join(zshRoot, '.zlogin'), 'utf8')
expect(zshrc).toContain('_orca_home="${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"')
expect(zlogin).toContain('_orca_home="${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"')
expectZdotdirSourceContext(zprofile, '.zprofile')
expectZdotdirSourceContext(zshrc, '.zshrc')
expectZdotdirSourceContext(zlogin, '.zlogin')
expectFinalZdotdirRestoreContext(zshrc)
expectFinalZdotdirRestoreContext(zlogin)
}
)

View File

@ -2,6 +2,10 @@ 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'
import {
getZshFinalZdotdirRestoreBlock,
getZshStartupFileSourceBlock
} from '../main/shell-templates'
const RELAY_SHELL_READY_DIR = '.orca-relay/shell-ready'
const POSIX_LOGIN_ARGS = ['-l']
@ -66,20 +70,17 @@ 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"
${getZshStartupFileSourceBlock({
fileName: '.zprofile',
homeExpression: '"${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"'
})}
`
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
${getZshStartupFileSourceBlock({
fileName: '.zshrc',
homeExpression: '"${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"',
interactiveOnly: true
})}
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}"
@ -90,15 +91,16 @@ if [[ ! -o login ]]; then
[[ -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
if [[ ! -o login ]]; then
${getZshFinalZdotdirRestoreBlock('"${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"')}
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
${getZshStartupFileSourceBlock({
fileName: '.zlogin',
homeExpression: '"${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"',
interactiveOnly: true
})}
# 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}"
@ -107,6 +109,7 @@ if [[ -z "\${ORCA_PI_CODING_AGENT_DIR:-}" && -n "\${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()}
${getZshFinalZdotdirRestoreBlock('"${ORCA_USER_ZDOTDIR:-${ORCA_ORIG_ZDOTDIR:-$HOME}}"')}
`
const bashRc = `# Orca relay bash overlay wrapper
[[ -f /etc/profile ]] && source /etc/profile