mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
chore: post-merge cleanup for agent integration fixes (#33644)
Post-merge cleanup after the agent-integration fixes: remove the unreachable Windows hook-script stdin drain epilogue; Windows statusline exits instead of draining outside an Orca pane; Codex CLI fork uses the \\?\-aware rollout path; WSL browser-network relay falls back to orcad's pinned Node (shared launcher revision stamp); stranded sleep records only drop for their own worktree; renames (hidden probe cwd, local Windows ConPTY helper).
This commit is contained in:
@@ -9094,7 +9094,7 @@
|
||||
"src/renderer/src/components/terminal-pane/terminal-context-menu-dismiss.test.ts": 33,
|
||||
"src/renderer/src/components/terminal-pane/terminal-copy-rejection-handling.test.ts": 44,
|
||||
"src/renderer/src/components/terminal-pane/terminal-copy-shortcut.test.ts": 1457,
|
||||
"src/renderer/src/components/terminal-pane/terminal-ctrl-arrow-conpty.test.ts": 1372,
|
||||
"src/renderer/src/components/terminal-pane/terminal-pane-local-windows-conpty.test.ts": 1372,
|
||||
"src/renderer/src/components/terminal-pane/terminal-ctrl-enter.test.ts": 92,
|
||||
"src/renderer/src/components/terminal-pane/terminal-cursor-appearance-precedence.test.ts": 296,
|
||||
"src/renderer/src/components/terminal-pane/terminal-cursor-inactive-style.test.ts": 337,
|
||||
|
||||
@@ -132,7 +132,6 @@ export function buildPosixHookSpoolLines(source: string, eventNameVar?: string):
|
||||
]
|
||||
}
|
||||
|
||||
export const WINDOWS_HOOK_STDIN_DRAIN_LABEL = 'orca_agent_hook_drain_stdin'
|
||||
// Windows searches the worktree before PATH; never give hook payloads to a repo-local executable.
|
||||
export const WINDOWS_HOOK_STDIN_READER = '"%SystemRoot%\\System32\\more.com"'
|
||||
export const WINDOWS_HOOK_STDIN_DRAIN_COMMAND = `${WINDOWS_HOOK_STDIN_READER} >nul 2>nul`
|
||||
@@ -157,7 +156,3 @@ export const WINDOWS_GIT_BASH_HOOK_ENVIRONMENT_GUARD = `if ${WINDOWS_HOOK_ENVIRO
|
||||
export const WINDOWS_POWERSHELL_HOOK_ENVIRONMENT_GUARD = `if (${WINDOWS_HOOK_ENVIRONMENT_VARS.map(
|
||||
(name) => `-not $env:${name}`
|
||||
).join(' -or ')}) { exit 0 }`
|
||||
|
||||
export function buildWindowsHookStdinDrainEpilogue(): string[] {
|
||||
return [`:${WINDOWS_HOOK_STDIN_DRAIN_LABEL}`, WINDOWS_HOOK_STDIN_DRAIN_COMMAND, 'exit /b 0']
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ describe('Windows managed hook stdin structure', () => {
|
||||
: [scriptPath]
|
||||
const result = await runHookProcess(executable, args, hookEnvironment())
|
||||
expect(result.exitCode, `${fileName} exit code`).toBe(0)
|
||||
// Only missing-context Windows paths permit broken writers; in-pane hooks must drain.
|
||||
// Missing-context guards exit before reading stdin, so writers may see a broken pipe.
|
||||
for (const error of result.stdinErrors) {
|
||||
expect(WRITER_BROKEN_BY_EARLY_EXIT, `${fileName} stdin error`).toContain(error.code)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { relayBundleCandidates } from '../ssh/relay-bundle-paths'
|
||||
import { withWslGuestLauncherRevision } from '../wsl/wsl-guest-launcher-revision'
|
||||
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
|
||||
|
||||
import type { MultiplexerTransport } from '../ssh/ssh-channel-multiplexer'
|
||||
@@ -36,7 +36,7 @@ export function resolveWslHookRelayBundle(): WslHookRelayBundle | null {
|
||||
const version = readFileSync(versionPath, 'utf8').trim()
|
||||
// Versions become shell literals and path segments; accept only a safe alphabet.
|
||||
if (/^[A-Za-z0-9+.-]+$/.test(version)) {
|
||||
return { jsPath, version: `${version}.${guestLauncherRevision()}` }
|
||||
return { jsPath, version: withWslGuestLauncherRevision(version, buildGuestLaunchScript) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,20 +48,10 @@ function guestRelayDirExpr(version: string): string {
|
||||
return `$HOME/${WSL_HOOK_RELAY_DIR}/${version}`
|
||||
}
|
||||
|
||||
let launcherRevision: string | undefined
|
||||
// The bundle hash alone misses launcher edits; a new guest dir forces one reinstall of launch.sh.
|
||||
function guestLauncherRevision(): string {
|
||||
launcherRevision ??= createHash('sha256')
|
||||
.update(buildGuestLaunchScript('0'))
|
||||
.digest('hex')
|
||||
.slice(0, 8)
|
||||
return launcherRevision
|
||||
}
|
||||
|
||||
/** Probe candidate Node versions: noninteractive sh may miss a newer nvm installation. */
|
||||
export function buildGuestLaunchScript(version: string): string {
|
||||
const dir = guestRelayDirExpr(version)
|
||||
// A WSL host distro may have no system node but always has orcad's pinned runtime.
|
||||
// A WSL host distro may have no system node but usually has orcad's pinned runtime.
|
||||
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
|
||||
return [
|
||||
'#!/bin/sh',
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { AgentJournalSnapshot } from '../../shared/agent-session-journal-ty
|
||||
import { agentJournalSubmissionKey } from '../../shared/agent-session-journal-item-key'
|
||||
import { setStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry'
|
||||
import { prepareStructuredChatCliFork } from './structured-chat-cli-fork'
|
||||
import { structuredAgentRuntimeRegistration } from '../runtime/structured-agent-runtime-registrations'
|
||||
import { prepareLegacySharedCodexSessionResume } from '../codex/codex-legacy-session-resume'
|
||||
import { AiVaultPrepareSessionResumeParams } from '../../shared/rpc-contract/ai-vault-params'
|
||||
|
||||
@@ -98,6 +99,43 @@ it('locates the current Codex rollout in its recorded home and materializes the
|
||||
})
|
||||
})
|
||||
|
||||
it.runIf(process.platform === 'win32')(
|
||||
'maps an extended-length Windows rollout path into the prepared home (#27200)',
|
||||
async () => {
|
||||
root = await mkdtemp(join(tmpdir(), 'chat-cli-fork-'))
|
||||
vi.stubEnv('ORCA_USER_DATA_PATH', root)
|
||||
const home = join(root, 'codex-runtime-home', 'home')
|
||||
const system = join(root, 'system')
|
||||
const record = agentSessionRecordFixture()
|
||||
record.provider = 'codex'
|
||||
record.accountHome = { variable: 'CODEX_HOME', path: home }
|
||||
record.providerHandleChain = [
|
||||
{
|
||||
...record.providerHandleChain[0]!,
|
||||
handle: { transport: 'codex-app-server', agent: 'codex', nativeId: 'current-conversation' }
|
||||
}
|
||||
]
|
||||
const relative = join('2026', '10', '15', 'rollout-2026-10-15-current-conversation.jsonl')
|
||||
await mkdir(join(home, 'sessions', '2026', '10', '15'), { recursive: true })
|
||||
await writeFile(join(home, 'sessions', relative), '{"saved":true}\n')
|
||||
vi.spyOn(
|
||||
structuredAgentRuntimeRegistration('codex')!.transcriptImport!,
|
||||
'findTranscript'
|
||||
).mockResolvedValueOnce(`\\\\?\\${join(home, 'sessions', relative)}`)
|
||||
install(record)
|
||||
const result = await prepareStructuredChatCliFork(request, async (args) => ({
|
||||
...(await prepareLegacySharedCodexSessionResume(args, {
|
||||
isHostSystemDefaultRealHomeSelected: () => true,
|
||||
legacyCodexHomePath: home,
|
||||
systemCodexHomePath: system
|
||||
})),
|
||||
useRealCodexHome: false,
|
||||
substituteCodexHome: system
|
||||
}))
|
||||
expect(result.cliForkSource?.filePath).toBe(join(system, 'sessions', relative))
|
||||
}
|
||||
)
|
||||
|
||||
it('copies OpenCode directly from the selected record and preserves its typed account and copy id', async () => {
|
||||
root = await mkdtemp(join(tmpdir(), 'chat-cli-fork-'))
|
||||
const record = agentSessionRecordFixture()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { stat } from 'node:fs/promises'
|
||||
import { isAbsolute, join, relative } from 'node:path'
|
||||
import { isAbsolute, join } from 'node:path'
|
||||
import { getSystemCodexHomePath } from '../codex/codex-home-paths'
|
||||
import { codexDatedRolloutRelativePath } from '../codex/codex-session-resume-home'
|
||||
import { agentForksConversations, agentForksThroughChat } from '../../shared/agent-resume-argv'
|
||||
import { isLegacyAgentSessionAccountHome } from '../../shared/agent-session-account-home'
|
||||
import { agentSessionProviderHandleChainHead } from '../../shared/agent-session-provider-handle'
|
||||
@@ -173,11 +174,12 @@ export async function prepareStructuredChatCliFork(
|
||||
})
|
||||
const preparedHome = result.useRealCodexHome ? null : (result.substituteCodexHome ?? codexHome)
|
||||
if (codexHome && (result.useRealCodexHome || result.substituteCodexHome)) {
|
||||
filePath = join(
|
||||
preparedHome ?? getSystemCodexHomePath(),
|
||||
'sessions',
|
||||
relative(join(codexHome, 'sessions'), filePath)
|
||||
)
|
||||
// Codex canonicalizes CODEX_HOME, so Windows transcripts can carry a \\?\ prefix (#27200).
|
||||
const relativePath = codexDatedRolloutRelativePath(join(codexHome, 'sessions'), filePath)
|
||||
if (!relativePath) {
|
||||
throw new Error('The prepared conversation is unavailable in the selected account.')
|
||||
}
|
||||
filePath = join(preparedHome ?? getSystemCodexHomePath(), 'sessions', relativePath)
|
||||
const preparedFile = await stat(filePath)
|
||||
if (!preparedFile.isFile() || preparedFile.size === 0) {
|
||||
throw new Error('The prepared conversation is unavailable in the selected account.')
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
|
||||
import {
|
||||
buildWslBrowserNetworkGuestInstallScript,
|
||||
buildWslBrowserNetworkGuestLaunchScript
|
||||
buildWslBrowserNetworkGuestLaunchScript,
|
||||
resolveWslBrowserNetworkRelayBundle
|
||||
} from './wsl-browser-network-relay-launch'
|
||||
|
||||
const roots: string[] = []
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs()
|
||||
for (const root of roots.splice(0)) {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
@@ -29,6 +32,22 @@ describe('WSL browser network relay launch', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('falls back to orcad pinned runtimes after system node, under a launcher-stamped version', () => {
|
||||
const forLine = buildWslBrowserNetworkGuestLaunchScript('0.1.0+abc')
|
||||
.split('\n')
|
||||
.find((line) => line.startsWith('for c in '))
|
||||
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
|
||||
expect(forLine).toContain(`"$HOME/.local/bin/node" ${orcadRuntime} ${orcadMachineRuntimes}; do`)
|
||||
|
||||
const relayRoot = mkdtempSync(join(tmpdir(), 'orca-wsl-browser-network-bundle-'))
|
||||
roots.push(relayRoot)
|
||||
vi.stubEnv('ORCA_RELAY_PATH', relayRoot)
|
||||
mkdirSync(join(relayRoot, 'wsl'))
|
||||
writeFileSync(join(relayRoot, 'wsl', 'wsl-browser-network-relay.js'), '')
|
||||
writeFileSync(join(relayRoot, 'wsl', '.browser-network-version'), '0.1.0+abc\n')
|
||||
expect(resolveWslBrowserNetworkRelayBundle()?.version).toMatch(/^0\.1\.0\+abc\.[0-9a-f]{8}$/)
|
||||
})
|
||||
|
||||
it('installs exact bundle bytes and writes the version marker last', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'orca-wsl-browser-network-'))
|
||||
roots.push(root)
|
||||
|
||||
@@ -2,6 +2,8 @@ import { spawnProcess } from '@orca/process-host'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { relayBundleCandidates } from '../ssh/relay-bundle-paths'
|
||||
import { withWslGuestLauncherRevision } from '../wsl/wsl-guest-launcher-revision'
|
||||
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
|
||||
import {
|
||||
WSL_BROWSER_NETWORK_RELAY_BUNDLE_NAME,
|
||||
WSL_BROWSER_NETWORK_RELAY_DIR,
|
||||
@@ -33,7 +35,10 @@ export function resolveWslBrowserNetworkRelayBundle(): WslBrowserNetworkRelayBun
|
||||
}
|
||||
const version = readFileSync(versionPath, 'utf8').trim()
|
||||
if (/^[A-Za-z0-9+.-]+$/.test(version)) {
|
||||
return { jsPath, version }
|
||||
return {
|
||||
jsPath,
|
||||
version: withWslGuestLauncherRevision(version, buildWslBrowserNetworkGuestLaunchScript)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -48,13 +53,15 @@ function guestRelayDir(version: string): string {
|
||||
|
||||
export function buildWslBrowserNetworkGuestLaunchScript(version: string): string {
|
||||
const dir = guestRelayDir(version)
|
||||
// A WSL host distro may have no system node but usually has orcad's pinned runtime.
|
||||
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
|
||||
return [
|
||||
'#!/bin/sh',
|
||||
`d="${dir}"`,
|
||||
`v="$(cat "$d/${WSL_BROWSER_NETWORK_RELAY_VERSION_FILE}" 2>/dev/null || true)"`,
|
||||
`[ "$v" = '${version}' ] || exit ${WSL_BROWSER_NETWORK_RELAY_STALE_EXIT_CODE}`,
|
||||
'n=""',
|
||||
'for c in "$(command -v node 2>/dev/null || true)" "$HOME/.nvm/versions/node"/*/bin/node /usr/local/bin/node /usr/bin/node "$HOME/.local/bin/node"; do',
|
||||
`for c in "$(command -v node 2>/dev/null || true)" "$HOME/.nvm/versions/node"/*/bin/node /usr/local/bin/node /usr/bin/node "$HOME/.local/bin/node" ${orcadRuntime} ${orcadMachineRuntimes}; do`,
|
||||
' [ -n "$c" ] && [ -x "$c" ] || continue',
|
||||
` if "$c" -e 'process.exit(Number(process.versions.node.split(".")[0])>=18?0:1)' 2>/dev/null; then`,
|
||||
' n="$c"',
|
||||
|
||||
@@ -17,8 +17,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
|
||||
// Daemon workers inherit stale pane keys; exit before draining their potentially abandoned stdin.
|
||||
@@ -58,7 +57,6 @@ export function getManagedScript(
|
||||
// Avoid a PowerShell startup per hook.
|
||||
buildWindowsAgentHookCurlPostCommand(source),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ vi.mock('../git-bash', async (importOriginal) => ({
|
||||
|
||||
import type { SFTPWrapper } from 'ssh2'
|
||||
import { createManagedCommandMatcher, WINDOWS_CMD_SAFE_PATH } from '../agent-hooks/installer-utils'
|
||||
import { WINDOWS_HOOK_STDIN_DRAIN_LABEL } from '../agent-hooks/hook-stdin-contract'
|
||||
import { ClaudeHookService } from './hook-service'
|
||||
import { CLAUDE_EVENTS } from './claude-managed-hook-events'
|
||||
import { getWindowsManagedLifecycleHook, OPENCLAUDE_HOOK_SETTINGS } from './hook-settings'
|
||||
@@ -656,7 +655,7 @@ describe('backgrounded-session pane guard (#9236)', () => {
|
||||
expect(guard).toBeDefined()
|
||||
expect(script.indexOf('CLAUDE_JOB_DIR')).toBeLessThan(script.indexOf('curl'))
|
||||
if (target === 'win32') {
|
||||
expect(guard).not.toContain(WINDOWS_HOOK_STDIN_DRAIN_LABEL)
|
||||
expect(guard).toBe('if not "%CLAUDE_JOB_DIR%"=="" exit /b 0')
|
||||
}
|
||||
} finally {
|
||||
Object.defineProperty(process, 'platform', platform)
|
||||
@@ -680,7 +679,6 @@ describe('backgrounded-session pane guard (#9236)', () => {
|
||||
)
|
||||
const guard = script.split('\r\n').find((line) => line.includes('CLAUDE_JOB_DIR'))
|
||||
expect(guard).toBe('if not "%CLAUDE_JOB_DIR%"=="" exit /b 0')
|
||||
expect(guard).not.toContain(WINDOWS_HOOK_STDIN_DRAIN_LABEL)
|
||||
} finally {
|
||||
Object.defineProperty(process, 'platform', platform)
|
||||
vi.unstubAllEnvs()
|
||||
|
||||
@@ -82,16 +82,13 @@ describe('getManagedStatusLineScript (win32 local)', () => {
|
||||
expect(script).not.toContain('"configDir=%CLAUDE_CONFIG_DIR%"')
|
||||
})
|
||||
|
||||
it('drains stdin before exiting when the pane key is missing', () => {
|
||||
it('exits without reading stdin when the pane key is missing (#24907)', () => {
|
||||
stubPlatform('win32')
|
||||
const script = getManagedStatusLineScript('local')
|
||||
const paneGuardIndex = script.indexOf(
|
||||
'if "%ORCA_PANE_KEY%"=="" goto :orca_agent_hook_drain_stdin'
|
||||
)
|
||||
const captureIndex = script.indexOf('more.com')
|
||||
const paneGuardIndex = script.indexOf('if "%ORCA_PANE_KEY%"=="" exit /b 0')
|
||||
expect(paneGuardIndex).toBeGreaterThan(-1)
|
||||
expect(paneGuardIndex).toBeLessThan(captureIndex)
|
||||
expect(script).toContain(':orca_agent_hook_drain_stdin')
|
||||
expect(paneGuardIndex).toBeLessThan(script.indexOf('more.com'))
|
||||
expect(script.match(/more\.com/g)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('throttles with an all-builtin seconds-of-day stamp that fails open to posting', () => {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
buildWindowsHookStdinDrainEpilogue,
|
||||
WINDOWS_HOOK_STDIN_DRAIN_LABEL,
|
||||
WINDOWS_HOOK_STDIN_READER
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { WINDOWS_HOOK_STDIN_READER } from '../agent-hooks/hook-stdin-contract'
|
||||
import {
|
||||
CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS,
|
||||
CLAUDE_STATUSLINE_PATHNAME
|
||||
@@ -31,7 +27,7 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
|
||||
// Daemon workers inherit stale pane keys; exit before reading their potentially abandoned stdin.
|
||||
'if not "%CLAUDE_JOB_DIR%"=="" exit /b 0',
|
||||
// Endpoint refresh never sets the pane key, so this guard can precede stdin capture.
|
||||
`if "%ORCA_PANE_KEY%"=="" goto :${WINDOWS_HOOK_STDIN_DRAIN_LABEL}`,
|
||||
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
|
||||
// UUID suffixes and sanitized legacy delimiters keep filenames safe.
|
||||
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_PANE_KEY:~-36%"',
|
||||
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_STATUSLINE_PANE_ID::=_%"',
|
||||
@@ -80,7 +76,6 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
|
||||
`:${STATUSLINE_CLEANUP_LABEL}`,
|
||||
'del "%ORCA_STATUSLINE_PAYLOAD_FILE%" >nul 2>nul',
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '../agent-hooks/managed-hook-script-refresh'
|
||||
import {
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
WINDOWS_HOOK_STDIN_DRAIN_COMMAND
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import {
|
||||
resolveBundledWindowsNativeHookExecutable,
|
||||
@@ -85,7 +85,8 @@ export function getWindowsClaudeHookEntry(): string {
|
||||
'echo {}',
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
WINDOWS_CLAUDE_BACKGROUND_JOB_GUARD,
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
WINDOWS_HOOK_STDIN_DRAIN_COMMAND,
|
||||
'exit /b 0',
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { runCodexAppServerSession } from './codex-app-server-session'
|
||||
import { isCodexSkillsListUnsupported, requestCodexSkillReport } from './codex-skill-list'
|
||||
import { supportsCodexStructuredLocation } from './codex-structured-location-support'
|
||||
import { resolveHiddenRateLimitPtyCwd } from '../rate-limits/hidden-rate-limit-pty-cwd'
|
||||
import { resolveHiddenProbeCwd } from '../rate-limits/hidden-probe-cwd'
|
||||
import {
|
||||
resolveCodexStructuredInvocation,
|
||||
type CodexStructuredInvocationDeps
|
||||
@@ -108,7 +108,7 @@ async function probeCodexSkills(
|
||||
command,
|
||||
args: [...args, 'app-server'],
|
||||
// skills/list takes the folder per request; a settling probe in it blocks Windows worktree removal (#33174).
|
||||
cwd: resolveHiddenRateLimitPtyCwd(),
|
||||
cwd: resolveHiddenProbeCwd(),
|
||||
cliPath: command,
|
||||
env: { ...definedEnv(environment), CODEX_HOME: accountHomePath },
|
||||
envToDelete: [NATIVE_CHAT_SCRATCH_DIR_ENV, LEGACY_NATIVE_CHAT_VISUALS_DIR_ENV],
|
||||
|
||||
@@ -2,8 +2,7 @@ import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildWindowsAgentHookCurlPostCommand } from '../agent-hooks/installer-utils'
|
||||
|
||||
@@ -17,7 +16,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('codex'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ import { buildWindowsAgentHookPostCommand } from '../agent-hooks/installer-utils
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
|
||||
export type CommandCodeManagedScriptTarget = 'local' | 'posix'
|
||||
@@ -36,7 +35,6 @@ export function buildCommandCodeManagedScript(
|
||||
'if not "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
|
||||
'for /f "tokens=2 delims==" %%P in (\'findstr /b /c:"set ORCA_AGENT_HOOK_PORT=" "%~1" 2^>nul\') do if "%%P"=="%ORCA_AGENT_HOOK_PORT%" call "%~1" 2>nul',
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
|
||||
export function getManagedScriptFileName(): string {
|
||||
@@ -34,7 +33,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
' --data-urlencode "hookEventName=%ORCA_COPILOT_HOOK_EVENT%" ^'
|
||||
]),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import {
|
||||
buildPosixGrokReplayGuardLines,
|
||||
@@ -59,7 +58,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsGrokReplayGuardLines(),
|
||||
buildWindowsAgentHookPostCommand('cursor'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -49,7 +48,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookPostCommand('devin'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
|
||||
@@ -81,7 +80,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookPostCommand('droid'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -57,7 +56,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('dsh'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
|
||||
@@ -64,7 +63,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookPostCommand('gemini'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { buildWindowsAgentHookPostCommand } from '../agent-hooks/installer-utils'
|
||||
import {
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildWindowsHookEnvironmentGuardLines } from '../agent-hooks/hook-stdin-contract'
|
||||
|
||||
/** Matches the envelope length cap used by the POSIX grok-hook branch. */
|
||||
export const GROK_HOME_ENVELOPE_MAX_LENGTH = 4096
|
||||
@@ -31,7 +28,6 @@ export function buildWindowsGrokHookScript(): string {
|
||||
':orca_grok_home_ready',
|
||||
WINDOWS_GROK_HOOK_POST_COMMAND,
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
} from '../agent-hooks/installer-utils-remote'
|
||||
import {
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue,
|
||||
POSIX_HOOK_STDIN_DRAIN_COMMAND,
|
||||
WINDOWS_HOOK_STDIN_DRAIN_COMMAND
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
@@ -60,7 +59,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
])}`,
|
||||
`del "${payloadFile}" 2>nul`,
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -82,7 +81,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('kimi'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ import { refreshManagedScriptIfPresent } from '../agent-hooks/managed-hook-scrip
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -50,7 +49,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('kiro'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -51,7 +50,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('muse'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -31,10 +31,7 @@ import {
|
||||
supplementCodexRateLimitResetCredits
|
||||
} from './codex-reset-credit-client'
|
||||
import { readCodexRateLimitsViaRpc } from './codex-rpc-rate-limit-probe'
|
||||
import {
|
||||
getHiddenRateLimitWslCwdSetupCommands,
|
||||
resolveHiddenRateLimitPtyCwd
|
||||
} from './hidden-rate-limit-pty-cwd'
|
||||
import { getHiddenProbeWslCwdSetupCommands, resolveHiddenProbeCwd } from './hidden-probe-cwd'
|
||||
import { quotePosixShell } from '../../shared/shell-argument-quoting'
|
||||
import { resolveWslInteropSpawnCwd } from '../wsl-interop-spawn-directory'
|
||||
|
||||
@@ -54,7 +51,7 @@ function buildWslCodexCommand(
|
||||
return null
|
||||
}
|
||||
const setupCommands = [
|
||||
...getHiddenRateLimitWslCwdSetupCommands(),
|
||||
...getHiddenProbeWslCwdSetupCommands(),
|
||||
`export CODEX_HOME=${quotePosixShell(wslInfo.linuxPath)}`
|
||||
].join(' && ')
|
||||
const execSuffix = `${args.map(quotePosixShell).join(' ')} <&3 >&4 3<&- 4>&-`
|
||||
@@ -99,7 +96,7 @@ async function fetchViaRpc(options?: CodexRateLimitFetchOptions): Promise<Provid
|
||||
program: wslCodex ? wslCodex.command : codexCommand,
|
||||
args: wslCodex ? wslCodex.args : codexArgs,
|
||||
// wsl.exe holds cwd busy for ~60s; keep probes outside profile folders (#29845).
|
||||
cwd: wslCodex ? resolveWslInteropSpawnCwd() : resolveHiddenRateLimitPtyCwd(),
|
||||
cwd: wslCodex ? resolveWslInteropSpawnCwd() : resolveHiddenProbeCwd(),
|
||||
env: withCliRuntimeOnPath(codexCommand, {
|
||||
...(wslCodex ? processEnvWithoutCodexHome() : process.env),
|
||||
// WSL probes keep the guest's own proxy env; a Windows proxy address may not resolve there.
|
||||
|
||||
+8
-7
@@ -4,8 +4,9 @@ import { join } from 'node:path'
|
||||
|
||||
import { isRootLikePath } from '../providers/pty-path-safety'
|
||||
|
||||
const HIDDEN_RATE_LIMIT_PTY_CWD_DIR = 'rate-limit-pty-cwd'
|
||||
const WSL_RATE_LIMIT_PTY_CWD_DIR = 'orca-rate-limit-pty-cwd'
|
||||
// Folder names predate the generic probe naming; keep them so existing dirs are reused.
|
||||
const HIDDEN_PROBE_CWD_DIR = 'rate-limit-pty-cwd'
|
||||
const WSL_HIDDEN_PROBE_CWD_DIR = 'orca-rate-limit-pty-cwd'
|
||||
|
||||
// Never let hidden Claude discovery start at a filesystem root; bound cwd to userData or tmpdir (runaway-cpu-hidden-usage-pty-design.md).
|
||||
function resolveUserDataRoot(userDataPath?: string | null): string {
|
||||
@@ -16,19 +17,19 @@ function resolveUserDataRoot(userDataPath?: string | null): string {
|
||||
return join(tmpdir(), 'orca-rate-limit-pty')
|
||||
}
|
||||
|
||||
export function resolveHiddenRateLimitPtyCwd(options?: { userDataPath?: string | null }): string {
|
||||
const cwd = join(resolveUserDataRoot(options?.userDataPath), HIDDEN_RATE_LIMIT_PTY_CWD_DIR)
|
||||
export function resolveHiddenProbeCwd(options?: { userDataPath?: string | null }): string {
|
||||
const cwd = join(resolveUserDataRoot(options?.userDataPath), HIDDEN_PROBE_CWD_DIR)
|
||||
mkdirSync(cwd, { recursive: true })
|
||||
const realCwd = realpathSync(cwd)
|
||||
if (isRootLikePath(realCwd) || !statSync(realCwd).isDirectory()) {
|
||||
throw new Error(`Hidden rate-limit PTY cwd is not a safe directory: ${realCwd}`)
|
||||
throw new Error(`Hidden probe cwd is not a safe directory: ${realCwd}`)
|
||||
}
|
||||
return realCwd
|
||||
}
|
||||
|
||||
export function getHiddenRateLimitWslCwdSetupCommands(): string[] {
|
||||
export function getHiddenProbeWslCwdSetupCommands(): string[] {
|
||||
return [
|
||||
`orca_rate_limit_cwd="\${TMPDIR:-/tmp}/${WSL_RATE_LIMIT_PTY_CWD_DIR}"`,
|
||||
`orca_rate_limit_cwd="\${TMPDIR:-/tmp}/${WSL_HIDDEN_PROBE_CWD_DIR}"`,
|
||||
'mkdir -p "$orca_rate_limit_cwd"',
|
||||
'cd "$orca_rate_limit_cwd"'
|
||||
]
|
||||
@@ -44,14 +44,15 @@ function session(
|
||||
describe('dropStrandedSleepRecord', () => {
|
||||
it("drops a workspace sleep's record once no tab holds its pane", () => {
|
||||
expect(
|
||||
dropStrandedSleepRecord(session([]), slept.paneKey)?.sleepingAgentSessionsByPaneKey
|
||||
dropStrandedSleepRecord(session([]), 'wt-1', slept.paneKey)?.sleepingAgentSessionsByPaneKey
|
||||
).toEqual({})
|
||||
})
|
||||
|
||||
// A window must not discard a record main still resumes when its tab opens.
|
||||
it('keeps a record whose tab is still listed, and window-owned records', () => {
|
||||
expect(dropStrandedSleepRecord(session(['tab-1']), slept.paneKey)).toBeNull()
|
||||
it("keeps a listed tab's record, window-owned records, and another worktree's record", () => {
|
||||
expect(dropStrandedSleepRecord(session(['tab-1']), 'wt-1', slept.paneKey)).toBeNull()
|
||||
const quit = { ...slept, origin: 'quit' as const }
|
||||
expect(dropStrandedSleepRecord(session([], quit), quit.paneKey)).toBeNull()
|
||||
expect(dropStrandedSleepRecord(session([], quit), 'wt-1', quit.paneKey)).toBeNull()
|
||||
expect(dropStrandedSleepRecord(session([]), 'wt-2', slept.paneKey)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -163,6 +163,7 @@ export function clearClosedWorktreeResumeRecords(
|
||||
/** The session without a workspace-sleep record whose tab it no longer lists, or null when the record is still placed. */
|
||||
export function dropStrandedSleepRecord(
|
||||
session: WorkspaceSessionState,
|
||||
worktreeId: string,
|
||||
paneKey: string
|
||||
): WorkspaceSessionState | null {
|
||||
const record = session.sleepingAgentSessionsByPaneKey?.[paneKey]
|
||||
@@ -170,7 +171,7 @@ export function dropStrandedSleepRecord(
|
||||
const tabListed = Object.values(session.tabsByWorktree).some((tabs) =>
|
||||
tabs.some((tab) => tab.id === tabId)
|
||||
)
|
||||
if (!record || !isWorkspaceSleepRecord(record) || tabListed) {
|
||||
if (!record || record.worktreeId !== worktreeId || !isWorkspaceSleepRecord(record) || tabListed) {
|
||||
return null
|
||||
}
|
||||
const { [paneKey]: _dropped, ...rest } = session.sleepingAgentSessionsByPaneKey ?? {}
|
||||
|
||||
@@ -270,7 +270,7 @@ export abstract class OrcaRuntimeWithListManagedWorktrees extends OrcaRuntimeWit
|
||||
async retireStrandedSleepRecord(worktreeId: string, paneKey: string): Promise<void> {
|
||||
await this.commitWorkspaceSessionEdit(
|
||||
this.getWorkspaceSessionHostIdForWorktree(worktreeId),
|
||||
(session) => dropStrandedSleepRecord(session, paneKey)
|
||||
(session) => dropStrandedSleepRecord(session, worktreeId, paneKey)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
|
||||
const revisions = new Map<(version: string) => string, string>()
|
||||
|
||||
/** The bundle hash alone misses launcher edits; a new guest dir forces one reinstall of launch.sh. */
|
||||
export function withWslGuestLauncherRevision(
|
||||
bundleVersion: string,
|
||||
buildLaunchScript: (version: string) => string
|
||||
): string {
|
||||
let revision = revisions.get(buildLaunchScript)
|
||||
if (!revision) {
|
||||
revision = createHash('sha256').update(buildLaunchScript('0')).digest('hex').slice(0, 8)
|
||||
revisions.set(buildLaunchScript, revision)
|
||||
}
|
||||
return `${bundleVersion}.${revision}`
|
||||
}
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
import {
|
||||
buildPosixHookPayloadCapture,
|
||||
buildPosixHookSpoolLines,
|
||||
buildWindowsHookEnvironmentGuardLines,
|
||||
buildWindowsHookStdinDrainEpilogue
|
||||
buildWindowsHookEnvironmentGuardLines
|
||||
} from '../agent-hooks/hook-stdin-contract'
|
||||
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
|
||||
import {
|
||||
@@ -49,7 +48,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
|
||||
...buildWindowsHookEnvironmentGuardLines(),
|
||||
buildWindowsAgentHookCurlPostCommand('zcode'),
|
||||
'exit /b 0',
|
||||
...buildWindowsHookStdinDrainEpilogue(),
|
||||
''
|
||||
].join('\r\n')
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import { hasCtrlEnterCsiUAuthorityForPane } from './terminal-ctrl-enter'
|
||||
import { titleHasExplicitAgentIdentity } from './title-agent-identity'
|
||||
import { isTuiAgent } from '../../../../shared/tui-agent-config'
|
||||
import { resolveTerminalInputHostPlatform } from './terminal-input-host-platform'
|
||||
import { isLocalWindowsConptyPaneForCtrlArrow } from './terminal-ctrl-arrow-conpty'
|
||||
import { isTerminalPaneLocalWindowsConpty } from './terminal-pane-local-windows-conpty'
|
||||
import { keyboardEventBelongsToScope } from './terminal-keyboard-scope'
|
||||
import {
|
||||
isEditableTarget,
|
||||
@@ -135,7 +135,7 @@ export function createTerminalKeyboardRuntime(options: RuntimeOptions) {
|
||||
if (!pane) {
|
||||
return false
|
||||
}
|
||||
return isLocalWindowsConptyPaneForCtrlArrow({
|
||||
return isTerminalPaneLocalWindowsConpty({
|
||||
isWindows,
|
||||
userAgent: navigator.userAgent,
|
||||
state: useAppStore.getState(),
|
||||
|
||||
+9
-11
@@ -1,16 +1,14 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
import {
|
||||
isLocalWindowsConptyPaneForCtrlArrow,
|
||||
type TerminalCtrlArrowConptyState
|
||||
} from './terminal-ctrl-arrow-conpty'
|
||||
isTerminalPaneLocalWindowsConpty,
|
||||
type TerminalPaneConptyState
|
||||
} from './terminal-pane-local-windows-conpty'
|
||||
import type { PtyTransport } from './pty-transport-types'
|
||||
|
||||
const WINDOWS_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
|
||||
|
||||
function state(
|
||||
overrides: Partial<TerminalCtrlArrowConptyState> = {}
|
||||
): TerminalCtrlArrowConptyState {
|
||||
function state(overrides: Partial<TerminalPaneConptyState> = {}): TerminalPaneConptyState {
|
||||
return {
|
||||
repos: [],
|
||||
worktreesByRepo: {},
|
||||
@@ -43,10 +41,10 @@ function remoteRuntimeTransport(): Pick<
|
||||
}
|
||||
}
|
||||
|
||||
describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
|
||||
describe('isTerminalPaneLocalWindowsConpty', () => {
|
||||
it('uses live local session metadata for synthetic floating terminals with no repo record', () => {
|
||||
expect(
|
||||
isLocalWindowsConptyPaneForCtrlArrow({
|
||||
isTerminalPaneLocalWindowsConpty({
|
||||
isWindows: true,
|
||||
userAgent: WINDOWS_UA,
|
||||
state: state({
|
||||
@@ -67,7 +65,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
|
||||
it('keeps an already-launched local PowerShell pane local after store ownership changes', () => {
|
||||
const worktreeId = 'repo-1::C:\\repo'
|
||||
expect(
|
||||
isLocalWindowsConptyPaneForCtrlArrow({
|
||||
isTerminalPaneLocalWindowsConpty({
|
||||
isWindows: true,
|
||||
userAgent: WINDOWS_UA,
|
||||
state: state({
|
||||
@@ -88,7 +86,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
|
||||
|
||||
it('does not treat a live remote-runtime terminal as local ConPTY', () => {
|
||||
expect(
|
||||
isLocalWindowsConptyPaneForCtrlArrow({
|
||||
isTerminalPaneLocalWindowsConpty({
|
||||
isWindows: true,
|
||||
userAgent: WINDOWS_UA,
|
||||
state: state(),
|
||||
@@ -104,7 +102,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
|
||||
|
||||
it('stays conservative for unresolved repo-backed panes before a live session exists', () => {
|
||||
expect(
|
||||
isLocalWindowsConptyPaneForCtrlArrow({
|
||||
isTerminalPaneLocalWindowsConpty({
|
||||
isWindows: true,
|
||||
userAgent: WINDOWS_UA,
|
||||
state: state(),
|
||||
+7
-7
@@ -15,28 +15,28 @@ type TerminalTabShellState = {
|
||||
>
|
||||
}
|
||||
|
||||
export type TerminalCtrlArrowConptyState = Parameters<typeof getConnectionIdFromState>[0] &
|
||||
export type TerminalPaneConptyState = Parameters<typeof getConnectionIdFromState>[0] &
|
||||
WorktreeRuntimeOwnerState &
|
||||
TerminalTabShellState
|
||||
|
||||
type TerminalCtrlArrowConptyTransport = Pick<
|
||||
type TerminalPaneConptyTransport = Pick<
|
||||
PtyTransport,
|
||||
'getPtyId' | 'getConnectionId' | 'getLocalSessionMetadata'
|
||||
>
|
||||
|
||||
type TerminalCtrlArrowConptyArgs = {
|
||||
type TerminalPaneConptyArgs = {
|
||||
isWindows: boolean
|
||||
userAgent: string
|
||||
state: TerminalCtrlArrowConptyState
|
||||
state: TerminalPaneConptyState
|
||||
worktreeId: string
|
||||
tabId: string
|
||||
paneId: number
|
||||
paneCwd: PaneCwdMap
|
||||
fallbackCwd: string
|
||||
transport: TerminalCtrlArrowConptyTransport | null
|
||||
transport: TerminalPaneConptyTransport | null
|
||||
}
|
||||
|
||||
export function isLocalWindowsConptyPaneForCtrlArrow({
|
||||
export function isTerminalPaneLocalWindowsConpty({
|
||||
isWindows,
|
||||
userAgent,
|
||||
state,
|
||||
@@ -46,7 +46,7 @@ export function isLocalWindowsConptyPaneForCtrlArrow({
|
||||
paneCwd,
|
||||
fallbackCwd,
|
||||
transport
|
||||
}: TerminalCtrlArrowConptyArgs): boolean {
|
||||
}: TerminalPaneConptyArgs): boolean {
|
||||
if (!isWindows) {
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user