fix(claude): route stream-json launch through process wrapper

This commit is contained in:
Merge Sim
2026-08-31 20:43:56 -07:00
parent a397cb8f01
commit 5c96e1bcbf
2 changed files with 14 additions and 15 deletions
@@ -1,7 +1,7 @@
import { EventEmitter } from 'node:events'
import { PassThrough } from 'node:stream'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { spawn } from 'node:child_process'
import type { spawnProcess } from '../../shared/child-process/run-process'
import {
openClaudeStreamJsonConnection,
type ClaudeControlRequest
@@ -22,10 +22,8 @@ function fakeSpawn() {
child.stdout = new PassThrough()
child.stderr = new PassThrough()
child.kill = vi.fn(() => true)
const spawnMock = vi.fn(
(_command: string, _args: readonly string[], _options: { env?: NodeJS.ProcessEnv }) => child
)
const spawnImpl = spawnMock as unknown as typeof spawn
const spawnMock = vi.fn((_spec: Parameters<typeof spawnProcess>[0]) => child)
const spawnImpl = spawnMock as unknown as typeof spawnProcess
return { child, spawnImpl, spawnMock }
}
@@ -75,12 +73,12 @@ describe('Claude stream-json connection', () => {
await expect(listing).resolves.toEqual({ models: [{ value: 'sonnet' }] })
expect(process.spawnImpl).toHaveBeenCalledWith(
'claude',
['-p'],
expect.objectContaining({
program: 'claude',
args: ['-p'],
cwd: '/work/repo',
env: expect.objectContaining({ CLAUDE_CONFIG_DIR: '/accounts/one' }),
windowsHide: true
stdio: ['pipe', 'pipe', 'pipe']
})
)
})
@@ -173,7 +171,7 @@ describe('Claude stream-json connection', () => {
process.spawnImpl
)
const env = process.spawnMock.mock.calls[0]?.[2]?.env
const env = process.spawnMock.mock.calls[0]?.[0]?.env
expect(env).toMatchObject({
ANTHROPIC_AUTH_TOKEN: 'configured-token',
ANTHROPIC_BASE_URL: 'https://gateway.example.test',
@@ -1,4 +1,4 @@
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
import { spawnProcess } from '../../shared/child-process/run-process'
import { waitForProcessExitUntil } from '../codex/codex-process-exit-deadline'
import { killCodexAppServerProcessTree } from '../codex/codex-app-server-session'
import { buildClaudeChildProcessEnv } from './claude-child-process-environment'
@@ -82,14 +82,15 @@ function exitError(stderrTail: string, cause?: Error): Error {
export async function openClaudeStreamJsonConnection(
launch: ClaudeStreamJsonLaunch,
handlers: ClaudeStreamJsonConnectionHandlers = {},
spawnImpl: typeof spawn = spawn
spawnImpl: typeof spawnProcess = spawnProcess
): Promise<ClaudeStreamJsonConnection> {
const child = spawnImpl(launch.command, launch.args, {
const child = spawnImpl({
program: launch.command,
args: launch.args,
cwd: launch.cwd,
env: buildClaudeChildProcessEnv(launch.env),
stdio: ['pipe', 'pipe', 'pipe'],
windowsHide: true
}) as ChildProcessWithoutNullStreams
stdio: ['pipe', 'pipe', 'pipe']
})
const pending = new Map<string, PendingRequest>()
let nextRequestId = 1
let stderrTail = ''