mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
Fix copying from Codex started manually in remote shells (#34715)
* fix(terminal): forward copies from Codex started in remote shells * test(terminal): capture PowerShell copy probe exit status * fix(terminal): preserve configured shell arguments for remote profiles * test(terminal): isolate profile result from system startup output * test(terminal): run native daemon shell probes with Node * fix(terminal): keep an inherited remote-viewer marker out of local panes An Orca started from a remote viewer's pane inherits ORCA_REMOTE_VIEWER_CLIPBOARD, which would make every local Codex fake an SSH session. Treat the marker as per-spawn identity so only explicit launch env carries it.
This commit is contained in:
@@ -94,6 +94,10 @@ const NODE_RUNTIME_INCLUDE = [
|
||||
'src/**/*.node-pty.test.ts',
|
||||
'src/**/node-pty-*.test.ts',
|
||||
'src/**/*.live-shell.test.ts',
|
||||
// These drive live zsh through node-pty and rely on Node's native PTY stream delivery.
|
||||
'src/main/shell-startup-feature-channel.test.ts',
|
||||
'src/main/daemon/shell-ready.test.ts',
|
||||
'src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts',
|
||||
'src/**/*real-pty*.test.ts',
|
||||
'src/**/*-native.test.ts',
|
||||
'src/**/*retention*.test.ts',
|
||||
|
||||
@@ -119,6 +119,9 @@ if [[ -n "${__orca_codex_binary:-}" && -x "${__orca_codex_binary}" ]]; then
|
||||
function codex {
|
||||
# Why local: zsh's warn_create_global warns for each global a function creates.
|
||||
local __orca_codex_arg __orca_codex_isolate="${ORCA_CODEX_ISOLATE:-1}" __orca_codex_identity="${ORCA_CODEX_ISOLATE:-1}"
|
||||
if [[ "${ORCA_REMOTE_VIEWER_CLIPBOARD:-}" == 1 && -z "${SSH_CONNECTION+x}" && -z "${SSH_TTY+x}" ]]; then
|
||||
local -x SSH_CONNECTION='127.0.0.1 0 127.0.0.1 0'
|
||||
fi
|
||||
if [[ -n "${ORCA_CODEX_LAUNCH_PREFLIGHT:-}" && -x "${ORCA_CODEX_LAUNCH_PREFLIGHT}" ]]; then
|
||||
"${ORCA_CODEX_LAUNCH_PREFLIGHT}" agent hooks prepare-codex >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
@@ -21,7 +21,12 @@ if test "$__orca_codex_type" = file
|
||||
if test "$ORCA_CODEX_ISOLATE" != 0; and not string match -qr -- '^(agents|queue|--remote|--no-daemon|--remote=.*)$' $argv; and command codex --help 2>/dev/null </dev/null | string match -q -- '*--no-daemon*'
|
||||
set argv --no-daemon $argv
|
||||
end
|
||||
command codex $argv
|
||||
if test "$ORCA_REMOTE_VIEWER_CLIPBOARD" = 1; and not set -q SSH_CONNECTION; and not set -q SSH_TTY
|
||||
set -lx SSH_CONNECTION '127.0.0.1 0 127.0.0.1 0'
|
||||
command codex $argv
|
||||
else
|
||||
command codex $argv
|
||||
end
|
||||
end
|
||||
end
|
||||
set -e __orca_codex_type
|
||||
|
||||
@@ -202,6 +202,9 @@ __orca_deferred_init() {
|
||||
function codex {
|
||||
# Why local: zsh's warn_create_global warns for each global a function creates.
|
||||
local __orca_codex_arg __orca_codex_isolate="${ORCA_CODEX_ISOLATE:-1}" __orca_codex_identity="${ORCA_CODEX_ISOLATE:-1}"
|
||||
if [[ "${ORCA_REMOTE_VIEWER_CLIPBOARD:-}" == 1 && -z "${SSH_CONNECTION+x}" && -z "${SSH_TTY+x}" ]]; then
|
||||
local -x SSH_CONNECTION='127.0.0.1 0 127.0.0.1 0'
|
||||
fi
|
||||
if [[ -n "${ORCA_CODEX_LAUNCH_PREFLIGHT:-}" && -x "${ORCA_CODEX_LAUNCH_PREFLIGHT}" ]]; then
|
||||
"${ORCA_CODEX_LAUNCH_PREFLIGHT}" agent hooks prepare-codex >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
@@ -127,6 +127,9 @@ if [[ -n "${__orca_codex_binary:-}" && -x "${__orca_codex_binary}" ]]; then
|
||||
function codex {
|
||||
# Why local: zsh's warn_create_global warns for each global a function creates.
|
||||
local __orca_codex_arg __orca_codex_isolate="${ORCA_CODEX_ISOLATE:-1}" __orca_codex_identity="${ORCA_CODEX_ISOLATE:-1}"
|
||||
if [[ "${ORCA_REMOTE_VIEWER_CLIPBOARD:-}" == 1 && -z "${SSH_CONNECTION+x}" && -z "${SSH_TTY+x}" ]]; then
|
||||
local -x SSH_CONNECTION='127.0.0.1 0 127.0.0.1 0'
|
||||
fi
|
||||
if [[ -n "${ORCA_CODEX_LAUNCH_PREFLIGHT:-}" && -x "${ORCA_CODEX_LAUNCH_PREFLIGHT}" ]]; then
|
||||
"${ORCA_CODEX_LAUNCH_PREFLIGHT}" agent hooks prepare-codex >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
@@ -21,7 +21,12 @@ if test "$__orca_codex_type" = file
|
||||
if test "$ORCA_CODEX_ISOLATE" != 0; and not string match -qr -- '^(agents|queue|--remote|--no-daemon|--remote=.*)$' $argv; and command codex --help 2>/dev/null </dev/null | string match -q -- '*--no-daemon*'
|
||||
set argv --no-daemon $argv
|
||||
end
|
||||
command codex $argv
|
||||
if test "$ORCA_REMOTE_VIEWER_CLIPBOARD" = 1; and not set -q SSH_CONNECTION; and not set -q SSH_TTY
|
||||
set -lx SSH_CONNECTION '127.0.0.1 0 127.0.0.1 0'
|
||||
command codex $argv
|
||||
else
|
||||
command codex $argv
|
||||
end
|
||||
end
|
||||
end
|
||||
set -e __orca_codex_type
|
||||
|
||||
@@ -209,6 +209,9 @@ __orca_deferred_init() {
|
||||
function codex {
|
||||
# Why local: zsh's warn_create_global warns for each global a function creates.
|
||||
local __orca_codex_arg __orca_codex_isolate="${ORCA_CODEX_ISOLATE:-1}" __orca_codex_identity="${ORCA_CODEX_ISOLATE:-1}"
|
||||
if [[ "${ORCA_REMOTE_VIEWER_CLIPBOARD:-}" == 1 && -z "${SSH_CONNECTION+x}" && -z "${SSH_TTY+x}" ]]; then
|
||||
local -x SSH_CONNECTION='127.0.0.1 0 127.0.0.1 0'
|
||||
fi
|
||||
if [[ -n "${ORCA_CODEX_LAUNCH_PREFLIGHT:-}" && -x "${ORCA_CODEX_LAUNCH_PREFLIGHT}" ]]; then
|
||||
"${ORCA_CODEX_LAUNCH_PREFLIGHT}" agent hooks prepare-codex >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
@@ -128,9 +128,11 @@ describe('createPtySubprocess', () => {
|
||||
ORCA_TAB_ID: process.env.ORCA_TAB_ID,
|
||||
ORCA_WORKTREE_ID: process.env.ORCA_WORKTREE_ID,
|
||||
ORCA_WSL_CLI_DIR: process.env.ORCA_WSL_CLI_DIR,
|
||||
ORCA_ORCAD_MANAGED_ACTIVATION_ROOT: process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT
|
||||
ORCA_ORCAD_MANAGED_ACTIVATION_ROOT: process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT,
|
||||
ORCA_REMOTE_VIEWER_CLIPBOARD: process.env.ORCA_REMOTE_VIEWER_CLIPBOARD
|
||||
}
|
||||
process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT = '/home/u/.orca-remote/.activation'
|
||||
process.env.ORCA_REMOTE_VIEWER_CLIPBOARD = '1'
|
||||
process.env.ORCA_PANE_KEY = 'parent-tab:parent-leaf'
|
||||
process.env.ORCA_TAB_ID = 'parent-tab'
|
||||
process.env.ORCA_WORKTREE_ID = 'parent-worktree'
|
||||
@@ -154,6 +156,7 @@ describe('createPtySubprocess', () => {
|
||||
expect(env.ORCA_WORKTREE_ID).toBeUndefined()
|
||||
expect(env.ORCA_WSL_CLI_DIR).toBeUndefined()
|
||||
expect(env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT).toBeUndefined()
|
||||
expect(env.ORCA_REMOTE_VIEWER_CLIPBOARD).toBeUndefined()
|
||||
})
|
||||
|
||||
it('preserves explicit child Orca pane identity over parent env', async () => {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { applyScrubSafeAgentEnvAliases } from '../../../shared/agent-hook-scrub-
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
import type { PtySubprocessOptions } from '../pty-subprocess'
|
||||
import { ORCAD_MANAGED_ACTIVATION_ROOT_ENV } from '../../../shared/orcad-managed-launch-env'
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../../shared/remote-viewer-clipboard-env'
|
||||
|
||||
const PANE_IDENTITY_ENV_KEYS = [
|
||||
'ORCA_PANE_KEY',
|
||||
@@ -38,6 +39,8 @@ const PANE_IDENTITY_ENV_KEYS = [
|
||||
// Not identity but equally per-spawn: an inherited copy names another launch's CLI.
|
||||
'ORCA_WSL_CLI_DIR',
|
||||
'JCODE_RUNTIME_DIR',
|
||||
// Per-viewer: an Orca started from a remote viewer's pane must not mark its own local panes.
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV,
|
||||
// A server's own launch marker; a user's `orcad` in this shell is not client-managed.
|
||||
ORCAD_MANAGED_ACTIVATION_ROOT_ENV
|
||||
] as const
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
|
||||
/** Codex v0.162.1 emits OSC 52 under SSH/TMUX; a valid SSH_CONNECTION avoids tmux subprocess/passthrough and SSH_TTY device semantics. */
|
||||
export const CODEX_REMOTE_VIEWER_SSH_CONNECTION = '127.0.0.1 0 127.0.0.1 0'
|
||||
import {
|
||||
CODEX_REMOTE_VIEWER_SSH_CONNECTION,
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV
|
||||
} from '../../../../shared/remote-viewer-clipboard-env'
|
||||
export { CODEX_REMOTE_VIEWER_SSH_CONNECTION }
|
||||
|
||||
/** Pi's own opt-in to emit OSC 52 alongside its host clipboard write; inert for every other program. */
|
||||
export const PI_CLIPBOARD_OSC52_ENV = 'PI_CLIPBOARD_OSC52'
|
||||
@@ -18,6 +20,7 @@ export function applyRemoteViewerClipboardEnv(
|
||||
if (!options.viewerIsRemote) {
|
||||
return
|
||||
}
|
||||
env[REMOTE_VIEWER_CLIPBOARD_ENV] = '1'
|
||||
const inherited = options.inheritedEnv ?? process.env
|
||||
// Daemons merge their inherited environment after assembly; respect existing values there too.
|
||||
const isSet = (key: string): boolean => env[key] !== undefined || inherited[key] !== undefined
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from '../../../shared/jcode-runtime-dir'
|
||||
import { makePaneKey } from '../../../shared/stable-pane-id'
|
||||
import { selectShellStartupFeatures } from '../../shell-startup-features'
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../../shared/remote-viewer-clipboard-env'
|
||||
import {
|
||||
CODEX_REMOTE_VIEWER_SSH_CONNECTION,
|
||||
PI_CLIPBOARD_OSC52_ENV
|
||||
@@ -299,8 +300,7 @@ describe('registerPtyHandlers', () => {
|
||||
}
|
||||
)
|
||||
|
||||
// #33937: pi started by hand in a headless host's shell copied only to the host clipboard.
|
||||
it('asks a hand-started pi for OSC 52 copies in a plain shell', () => {
|
||||
it('installs clipboard forwarding for agents started by hand in a remote shell', () => {
|
||||
vi.stubEnv(PI_CLIPBOARD_OSC52_ENV, undefined)
|
||||
try {
|
||||
const env = buildPtyHostEnv(
|
||||
@@ -315,7 +315,17 @@ describe('registerPtyHandlers', () => {
|
||||
}
|
||||
)
|
||||
expect(env[PI_CLIPBOARD_OSC52_ENV]).toBe('1')
|
||||
expect(env[REMOTE_VIEWER_CLIPBOARD_ENV]).toBe('1')
|
||||
expect(env.SSH_CONNECTION).toBeUndefined()
|
||||
expect(
|
||||
selectShellStartupFeatures({
|
||||
shellPath: '/bin/bash',
|
||||
env,
|
||||
hasStartupCommand: false,
|
||||
waitsForShellReady: false,
|
||||
emitsStartupIdentity: false
|
||||
})
|
||||
).toContain('overlay')
|
||||
} finally {
|
||||
vi.unstubAllEnvs()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../shared/remote-viewer-clipboard-env'
|
||||
import { finalizeLocalPtySpawnEnvironment } from './local-pty-finalize-environment'
|
||||
import { createLocalPtyLaunchPlan } from './local-pty-launch-plan'
|
||||
|
||||
vi.mock('./local-pty-utils', () => ({
|
||||
@@ -65,17 +71,72 @@ describe.skipIf(process.platform === 'win32')('default terminal shell', () => {
|
||||
it.each([{ command: 'codex' }, { launchAgent: 'codex' as const }])(
|
||||
'keeps controlled login args for non-profile launches (%o)',
|
||||
(overrides) => {
|
||||
const plan = createLocalPtyLaunchPlan(
|
||||
{
|
||||
cwd: '/tmp',
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
terminalShellArgs: ['--rcfile', '/tmp/orca'],
|
||||
...overrides
|
||||
},
|
||||
() => ({ getDefaultShell: () => '/bin/zsh' })
|
||||
)
|
||||
expect('shellArgs' in plan && plan.shellArgs).toEqual(['-l'])
|
||||
const spawn = {
|
||||
cwd: '/tmp',
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
terminalShellArgs: ['--rcfile', '/tmp/orca'],
|
||||
...overrides
|
||||
}
|
||||
const getOptions = () => ({ getDefaultShell: () => '/bin/zsh' })
|
||||
const plan = createLocalPtyLaunchPlan(spawn, getOptions)
|
||||
if (!('shellArgs' in plan)) {
|
||||
throw new Error('Expected a POSIX shell launch plan')
|
||||
}
|
||||
finalizeLocalPtySpawnEnvironment({
|
||||
spawn,
|
||||
getOptions,
|
||||
plan,
|
||||
env: { [REMOTE_VIEWER_CLIPBOARD_ENV]: '1' }
|
||||
})
|
||||
expect(plan.shellArgs).toEqual(['-l'])
|
||||
}
|
||||
)
|
||||
|
||||
it.for([
|
||||
{ shell: '/bin/bash', shellArgs: ['--rcfile', 'custom rc'], expected: 'custom:off' },
|
||||
{ shell: '/bin/zsh', shellArgs: ['-f'], expected: 'unset:off' },
|
||||
{ shell: '/bin/zsh', shellArgs: [], expected: 'zshrc:off' }
|
||||
])(
|
||||
'honors remote profile startup behavior for $shell $shellArgs',
|
||||
({ shell, shellArgs, expected }, { skip }) => {
|
||||
if (spawnSync(shell, ['--version']).status !== 0) {
|
||||
skip('Shell is not installed')
|
||||
}
|
||||
const home = mkdtempSync(join(tmpdir(), 'orca-profile-argv-'))
|
||||
vi.stubEnv('ORCA_USER_DATA_PATH', home)
|
||||
vi.stubEnv('ZDOTDIR', home)
|
||||
try {
|
||||
writeFileSync(join(home, '.bash_profile'), 'export ORCA_TEST_STARTUP=profile\n')
|
||||
writeFileSync(join(home, 'custom rc'), 'export ORCA_TEST_STARTUP=custom\n')
|
||||
writeFileSync(join(home, '.zshrc'), 'export ORCA_TEST_STARTUP=zshrc\n')
|
||||
const terminalShellArgs = shellArgs.map((arg) =>
|
||||
arg === 'custom rc' ? join(home, arg) : arg
|
||||
)
|
||||
const spawn = { cwd: home, cols: 80, rows: 24, terminalShellArgs }
|
||||
const getOptions = () => ({ getDefaultShell: () => shell })
|
||||
const plan = createLocalPtyLaunchPlan(spawn, getOptions)
|
||||
if (!('shellArgs' in plan)) {
|
||||
throw new Error('Expected a POSIX shell launch plan')
|
||||
}
|
||||
const env = { HOME: home, PATH: '/usr/bin:/bin', [REMOTE_VIEWER_CLIPBOARD_ENV]: '1' }
|
||||
finalizeLocalPtySpawnEnvironment({ spawn, getOptions, plan, env })
|
||||
|
||||
const result = spawnSync(
|
||||
plan.shellPath,
|
||||
[
|
||||
...plan.shellArgs,
|
||||
'-i',
|
||||
'-c',
|
||||
'login=off; if [ -n "${ZSH_VERSION:-}" ]; then [[ -o login ]] && login=on; else shopt -q login_shell && login=on; fi; printf "\\nORCA_STARTUP_RESULT=%s:%s\\n" "${ORCA_TEST_STARTUP:-unset}" "$login"'
|
||||
],
|
||||
{ env, encoding: 'utf8', timeout: 10_000 }
|
||||
)
|
||||
expect(result.status, result.stderr).toBe(0)
|
||||
expect(result.stdout.match(/^ORCA_STARTUP_RESULT=(.*)$/m)?.[1]).toBe(expected)
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -98,6 +98,7 @@ export function finalizeLocalPtySpawnEnvironment(args: {
|
||||
// Only this shell launch config may choose Orca-owned features.
|
||||
delete env.ORCA_SHELL_FEATURES
|
||||
delete env[POSIX_SHELL_STARTUP_COMMAND_ENV]
|
||||
const userShellArgs = !spawn.command && !spawn.launchAgent ? spawn.terminalShellArgs : undefined
|
||||
// Capture before launch config prefixes XDG_DATA_DIRS.
|
||||
const fishLaunch = {
|
||||
inheritedXdgDataDirs: env.XDG_DATA_DIRS,
|
||||
@@ -130,7 +131,7 @@ export function finalizeLocalPtySpawnEnvironment(args: {
|
||||
Object.keys(shellLaunch.env).map((key) => [key, env[key]])
|
||||
)
|
||||
Object.assign(env, shellLaunch.env)
|
||||
plan.shellArgs = shellLaunch.args ?? plan.shellArgs
|
||||
plan.shellArgs = userShellArgs ?? shellLaunch.args ?? plan.shellArgs
|
||||
plan.shellReadyLaunch = spawn.command ? shellLaunch : null
|
||||
}
|
||||
return historyResult
|
||||
|
||||
@@ -5,6 +5,7 @@ import { resolvePathEnvKey } from '../pty/windows-environment-path'
|
||||
import { expandWindowsEnvironmentVariables } from '../../shared/windows-environment-expansion'
|
||||
import { resolveSafePtyDefaultCwd } from './pty-default-cwd'
|
||||
import { ORCAD_MANAGED_ACTIVATION_ROOT_ENV } from '../../shared/orcad-managed-launch-env'
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../shared/remote-viewer-clipboard-env'
|
||||
|
||||
const PANE_IDENTITY_ENV_KEYS = [
|
||||
'ORCA_PANE_KEY',
|
||||
@@ -12,6 +13,8 @@ const PANE_IDENTITY_ENV_KEYS = [
|
||||
'ORCA_WORKTREE_ID',
|
||||
'ORCA_AGENT_LAUNCH_TOKEN',
|
||||
'JCODE_RUNTIME_DIR',
|
||||
// Per-viewer: an Orca started from a remote viewer's pane must not mark its own local panes.
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV,
|
||||
// A server's own launch marker; a user's `orcad` in this shell is not client-managed.
|
||||
ORCAD_MANAGED_ACTIVATION_ROOT_ENV
|
||||
] as const
|
||||
|
||||
@@ -566,9 +566,11 @@ describe('LocalPtyProvider', () => {
|
||||
ORCA_PANE_KEY: process.env.ORCA_PANE_KEY,
|
||||
ORCA_TAB_ID: process.env.ORCA_TAB_ID,
|
||||
ORCA_WORKTREE_ID: process.env.ORCA_WORKTREE_ID,
|
||||
ORCA_ORCAD_MANAGED_ACTIVATION_ROOT: process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT
|
||||
ORCA_ORCAD_MANAGED_ACTIVATION_ROOT: process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT,
|
||||
ORCA_REMOTE_VIEWER_CLIPBOARD: process.env.ORCA_REMOTE_VIEWER_CLIPBOARD
|
||||
}
|
||||
process.env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT = '/home/u/.orca-remote/.activation'
|
||||
process.env.ORCA_REMOTE_VIEWER_CLIPBOARD = '1'
|
||||
process.env.ORCA_PANE_KEY = 'parent-tab:parent-leaf'
|
||||
process.env.ORCA_TAB_ID = 'parent-tab'
|
||||
process.env.ORCA_WORKTREE_ID = 'parent-worktree'
|
||||
@@ -590,6 +592,7 @@ describe('LocalPtyProvider', () => {
|
||||
expect(spawnCall[2].env.ORCA_TAB_ID).toBeUndefined()
|
||||
expect(spawnCall[2].env.ORCA_WORKTREE_ID).toBeUndefined()
|
||||
expect(spawnCall[2].env.ORCA_ORCAD_MANAGED_ACTIVATION_ROOT).toBeUndefined()
|
||||
expect(spawnCall[2].env.ORCA_REMOTE_VIEWER_CLIPBOARD).toBeUndefined()
|
||||
})
|
||||
|
||||
it('preserves explicit child Orca pane identity over parent env', async () => {
|
||||
|
||||
@@ -18,6 +18,10 @@ import {
|
||||
} from '../../shared/codex-shell-function'
|
||||
import { resolveFishBinary } from '../../shared/fish-binary-requirement'
|
||||
import { getPowerShellAgentWrappers } from '../../shared/powershell-agent-wrappers'
|
||||
import {
|
||||
CODEX_REMOTE_VIEWER_SSH_CONNECTION,
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV
|
||||
} from '../../shared/remote-viewer-clipboard-env'
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
const fishLookup = resolveFishBinary()
|
||||
@@ -109,6 +113,7 @@ if (a.length === 1 && a[0] === '--help') {
|
||||
process.exit(0)
|
||||
}
|
||||
let out = ['ARGV', ...a].join('|')
|
||||
if (process.env.FAKE_CODEX_READ_CLIPBOARD_ENV === '1') out += '|ssh=' + (process.env.SSH_CONNECTION ?? '<unset>')
|
||||
if (process.env.FAKE_CODEX_READ_STDIN === '1') out += '|stdin=' + fs.readFileSync(0, 'utf8').trim()
|
||||
console.log(out)
|
||||
process.exit(Number(process.env.FAKE_CODEX_EXIT || 0))
|
||||
@@ -125,6 +130,7 @@ process.exit(Number(process.env.FAKE_CODEX_EXIT || 0))
|
||||
if [ "$#" -eq 1 ] && [ "$1" = --help ]; then echo help >> ${JSON.stringify(helpLog)}; cat ${JSON.stringify(helpFile)}; exit 0; fi
|
||||
out=ARGV
|
||||
for a in "$@"; do out="$out|$a"; done
|
||||
[ "\${FAKE_CODEX_READ_CLIPBOARD_ENV:-}" = 1 ] && out="$out|ssh=\${SSH_CONNECTION-<unset>}"
|
||||
[ "\${FAKE_CODEX_READ_STDIN:-}" = 1 ] && out="$out|stdin=$(cat)"
|
||||
printf '%s\\n' "$out"
|
||||
exit "\${FAKE_CODEX_EXIT:-0}"
|
||||
@@ -190,6 +196,9 @@ function run(
|
||||
CODEX_HOME: join(sandbox.bin, '..', 'codex-home'),
|
||||
// Nested Orca tests must not forward the host pane's identity.
|
||||
...Object.fromEntries(CODEX_COMMAND_IDENTITY_ENV_KEYS.map((key) => [key, ''])),
|
||||
SSH_CONNECTION: undefined,
|
||||
SSH_TTY: undefined,
|
||||
[REMOTE_VIEWER_CLIPBOARD_ENV]: undefined,
|
||||
...env
|
||||
}
|
||||
})
|
||||
@@ -220,6 +229,37 @@ describe('codex wrapper --no-daemon rule', () => {
|
||||
|
||||
for (const [shell, available] of shells) {
|
||||
describe.skipIf(!available)(shell, () => {
|
||||
it.each(
|
||||
[
|
||||
['1', undefined, undefined, CODEX_REMOTE_VIEWER_SSH_CONNECTION, '0'],
|
||||
['1', undefined, undefined, CODEX_REMOTE_VIEWER_SSH_CONNECTION, '3'],
|
||||
['0', undefined, undefined, '<unset>', '0'],
|
||||
['1', '', undefined, '', '0'],
|
||||
['1', '10.0.0.1 5000 10.0.0.2 22', undefined, '10.0.0.1 5000 10.0.0.2 22', '0'],
|
||||
['1', undefined, '/dev/ttys004', '<unset>', '0']
|
||||
].filter(([, inherited]) => !isPowerShell(shell) || inherited !== '')
|
||||
)(
|
||||
'routes manual copies for remote=%s without marking the parent shell',
|
||||
(remote, inherited, inheritedTty, expected, exitCode) => {
|
||||
const sandbox = makeSandbox(HELP_WITH_FLAG)
|
||||
const bareCall = isPowerShell(shell)
|
||||
? '$orcaClipboardExit = $LASTEXITCODE\n& (Get-Command codex -CommandType Application | Select-Object -First 1).Source shell\nexit $orcaClipboardExit'
|
||||
: 'command codex shell'
|
||||
const result = run(shell, `codex clipboard\n${bareCall}`, sandbox, {
|
||||
FAKE_CODEX_READ_CLIPBOARD_ENV: '1',
|
||||
FAKE_CODEX_EXIT: exitCode ?? '0',
|
||||
[REMOTE_VIEWER_CLIPBOARD_ENV]: remote ?? '0',
|
||||
...(inherited !== undefined ? { SSH_CONNECTION: inherited } : {}),
|
||||
...(inheritedTty ? { SSH_TTY: inheritedTty } : {})
|
||||
})
|
||||
expect(result.status, result.stderr).toBe(Number(exitCode))
|
||||
expect(lines(result.stdout)).toEqual([
|
||||
`ARGV|--no-daemon|clipboard|ssh=${expected}`,
|
||||
`ARGV|shell|ssh=${inherited ?? '<unset>'}`
|
||||
])
|
||||
}
|
||||
)
|
||||
|
||||
it('adds --no-daemon first unless an argument is denylisted', () => {
|
||||
const sandbox = makeSandbox(HELP_WITH_FLAG)
|
||||
const result = run(shell, ALL.map((argv) => codexCall(shell, argv)).join('\n'), sandbox)
|
||||
|
||||
@@ -245,9 +245,13 @@ describe('addOrcaWslInteropEnv', () => {
|
||||
|
||||
// #10861: wsl.exe drops SSH_CONNECTION, so WSL Codex copied only to the Windows clipboard.
|
||||
it('crosses the Codex remote-viewer clipboard marker into the guest untranslated (/u)', () => {
|
||||
const env: Record<string, string> = { SSH_CONNECTION: CODEX_REMOTE_VIEWER_SSH_CONNECTION }
|
||||
const env: Record<string, string> = {
|
||||
SSH_CONNECTION: CODEX_REMOTE_VIEWER_SSH_CONNECTION,
|
||||
ORCA_REMOTE_VIEWER_CLIPBOARD: '1'
|
||||
}
|
||||
addOrcaWslInteropEnv(env)
|
||||
expect(env.WSLENV?.split(':')).toContain('SSH_CONNECTION/u')
|
||||
expect(env.WSLENV?.split(':')).toContain('ORCA_REMOTE_VIEWER_CLIPBOARD/u')
|
||||
})
|
||||
|
||||
it("leaves the Windows host's own SSH session out of the guest", () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '../../shared/setup-agent-sequencing'
|
||||
import { getShellReadyWrapperRoot } from '../providers/local-pty-shell-ready-wrapper-root'
|
||||
import { ORCA_IMAGE_PROTOCOL_ENV } from '../../shared/terminal-image-protocol'
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../shared/remote-viewer-clipboard-env'
|
||||
import {
|
||||
RUNTIME_SOURCE_ID_ENV,
|
||||
RUNTIME_SOURCE_INCARNATION_ENV,
|
||||
@@ -127,6 +128,7 @@ export function addOrcaWslInteropEnv(env: Record<string, string>): void {
|
||||
// Forward only Orca's clipboard marker; a host SSH session must not leak into bare guest shells.
|
||||
...(env.SSH_CONNECTION === CODEX_REMOTE_VIEWER_SSH_CONNECTION ? ['SSH_CONNECTION/u'] : []),
|
||||
`${PI_CLIPBOARD_OSC52_ENV}/u`,
|
||||
`${REMOTE_VIEWER_CLIPBOARD_ENV}/u`,
|
||||
'ORCA_OMP_FRESH_CONFIG/p',
|
||||
...worktreeSetupWslenvEntries(env)
|
||||
]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../shared/remote-viewer-clipboard-env'
|
||||
|
||||
// Wrappers consume the feature env before user config so children cannot inherit the selection.
|
||||
|
||||
export const SHELL_STARTUP_FEATURE_ENV = 'ORCA_SHELL_FEATURES'
|
||||
@@ -14,6 +16,7 @@ export const SHELL_STARTUP_FEATURES = [
|
||||
export type ShellStartupFeature = (typeof SHELL_STARTUP_FEATURES)[number]
|
||||
|
||||
const OVERLAY_ENV_KEYS = [
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV,
|
||||
'ORCA_DATA_ACCOUNT_DATA_HOME',
|
||||
'ORCA_OPENCODE_CONFIG_DIR',
|
||||
'ORCA_MIMOCODE_CONFIG_DIR',
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
CODEX_REMOTE_VIEWER_SSH_CONNECTION,
|
||||
REMOTE_VIEWER_CLIPBOARD_ENV
|
||||
} from './remote-viewer-clipboard-env'
|
||||
|
||||
// Embed launch-prep/--no-daemon wrappers on all hosts. Codex 0.156+ otherwise shares CODEX_HOME servers with the first tab’s Orca hooks/env and lifetime (#22873). agents/queue/--remote require sharing; any -c opts out.
|
||||
const CODEX_SHARED_SERVER_ARGS = ['agents', 'queue', '--remote'] as const
|
||||
export const CODEX_NO_DAEMON_SKIP_ARGS = [...CODEX_SHARED_SERVER_ARGS, '--no-daemon'] as const
|
||||
@@ -31,6 +36,9 @@ if [[ -n "\${__orca_codex_binary:-}" && -x "\${__orca_codex_binary}" ]]; then
|
||||
function codex {
|
||||
# Why local: zsh's warn_create_global warns for each global a function creates.
|
||||
local __orca_codex_arg __orca_codex_isolate="\${ORCA_CODEX_ISOLATE:-1}" __orca_codex_identity="\${ORCA_CODEX_ISOLATE:-1}"
|
||||
if [[ "\${${REMOTE_VIEWER_CLIPBOARD_ENV}:-}" == 1 && -z "\${SSH_CONNECTION+x}" && -z "\${SSH_TTY+x}" ]]; then
|
||||
local -x SSH_CONNECTION='${CODEX_REMOTE_VIEWER_SSH_CONNECTION}'
|
||||
fi
|
||||
if [[ -n "\${ORCA_CODEX_LAUNCH_PREFLIGHT:-}" && -x "\${ORCA_CODEX_LAUNCH_PREFLIGHT}" ]]; then
|
||||
"\${ORCA_CODEX_LAUNCH_PREFLIGHT}" agent hooks prepare-codex >/dev/null 2>&1 || :
|
||||
fi
|
||||
@@ -70,7 +78,12 @@ ${CODEX_COMMAND_IDENTITY_ENV_KEYS.map((key) => ` test -n "$${key}"; and not
|
||||
if test "$ORCA_CODEX_ISOLATE" != 0; and not string match -qr -- '${CODEX_NO_DAEMON_SKIP_ARG_PATTERN}' $argv; and command codex --help 2>/dev/null </dev/null | string match -q -- '*--no-daemon*'
|
||||
set argv --no-daemon $argv
|
||||
end
|
||||
command codex $argv
|
||||
if test "$${REMOTE_VIEWER_CLIPBOARD_ENV}" = 1; and not set -q SSH_CONNECTION; and not set -q SSH_TTY
|
||||
set -lx SSH_CONNECTION '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}'
|
||||
command codex $argv
|
||||
else
|
||||
command codex $argv
|
||||
end
|
||||
end
|
||||
end
|
||||
set -e __orca_codex_type`
|
||||
@@ -109,11 +122,17 @@ if ($orcaCodexCommand -and
|
||||
foreach ($k in ${CODEX_COMMAND_IDENTITY_ENV_KEYS.map((key) => `'${key}'`).join(',')}) { $v = [Environment]::GetEnvironmentVariable($k); if ($v -and -not $v.Contains("'")) { $orcaCodexIdentity += '-c', "${identitySetArg('$k', '$v')}" } }
|
||||
}
|
||||
$orcaCodexArgs = @(__OrcaEscapeNativeArgs $orcaCodexExecutable.Source (@($orcaCodexIdentity) + @($args)))
|
||||
$orcaCodexRemoteClipboard = $env:${REMOTE_VIEWER_CLIPBOARD_ENV} -eq '1' -and -not (Test-Path Env:SSH_CONNECTION) -and -not (Test-Path Env:SSH_TTY)
|
||||
if ($orcaCodexRemoteClipboard) { $env:SSH_CONNECTION = '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}' }
|
||||
# Why: a native command inside a function never sees the function's pipeline input on its own.
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & $orcaCodexExecutable.Source @orcaCodexFlags @orcaCodexArgs
|
||||
} else {
|
||||
& $orcaCodexExecutable.Source @orcaCodexFlags @orcaCodexArgs
|
||||
try {
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & $orcaCodexExecutable.Source @orcaCodexFlags @orcaCodexArgs
|
||||
} else {
|
||||
& $orcaCodexExecutable.Source @orcaCodexFlags @orcaCodexArgs
|
||||
}
|
||||
} finally {
|
||||
if ($orcaCodexRemoteClipboard) { Remove-Item Env:SSH_CONNECTION }
|
||||
}
|
||||
$global:LASTEXITCODE = $LASTEXITCODE
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const REMOTE_VIEWER_CLIPBOARD_ENV = 'ORCA_REMOTE_VIEWER_CLIPBOARD'
|
||||
|
||||
/** Codex forwards copies through OSC 52 under SSH; keep this marker out of the parent shell. */
|
||||
export const CODEX_REMOTE_VIEWER_SSH_CONNECTION = '127.0.0.1 0 127.0.0.1 0'
|
||||
Reference in New Issue
Block a user