Files
aiturk-hermes-ide/apps/desktop/electron/codex-link-client.ts
T

133 lines
4.0 KiB
TypeScript

import { CODEX_LINK_RESPONSE_BYTES, parseControlResponse, validateControlRequest } from '../codex-link-control'
import type { CodexLinkControlResponse } from '../codex-link-control.types'
import { type AiturkCliRunnerDeps, resolveAiturkCliPath } from './aiturk-login'
type Dependencies = AiturkCliRunnerDeps
// Only the fixed entrypoint and fixed arguments go through cmd.exe. All values travel on stdin.
// eslint-disable-next-line no-control-regex -- Reject control bytes in executable paths before cmd parsing.
const UNSAFE_PATH = /["%^!\u0000-\u001f]/
const ABSOLUTE_PATH = /^[A-Za-z]:\\/
export function createCodexLinkClient(deps: Dependencies) {
const setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms))
const clearTimer = deps.clearTimer ?? (timer => clearTimeout(timer as ReturnType<typeof setTimeout>))
return async function control(raw: unknown): Promise<CodexLinkControlResponse> {
validateControlRequest(raw)
const cli = resolveAiturkCliPath(deps)
if (!cli) {
throw new Error('codex-link:cli_unavailable')
}
const systemRoot = String(deps.env.SystemRoot || deps.env.SYSTEMROOT || 'C:\\Windows').replace(/[\\/]+$/, '')
if (UNSAFE_PATH.test(cli) || UNSAFE_PATH.test(systemRoot) || !ABSOLUTE_PATH.test(systemRoot)) {
throw new Error('codex-link:invalid_entrypoint')
}
return new Promise((resolve, reject) => {
let settled = false
let timer: unknown
let bytes = 0
const chunks: Buffer[] = []
let child: any
const finish = (error?: Error, response?: CodexLinkControlResponse) => {
if (settled) {
return
}
settled = true
if (timer !== undefined) {
clearTimer(timer)
}
error ? reject(error) : resolve(response!)
}
try {
child = deps.spawn(
`${systemRoot}\\System32\\cmd.exe`,
['/d', '/s', '/c', `""${cli}" codex-link control --stdin"`],
{
shell: false,
windowsHide: true,
windowsVerbatimArguments: true,
stdio: ['pipe', 'pipe', 'pipe']
}
)
} catch {
finish(new Error('codex-link:cli_unavailable'))
return
}
timer = setTimer(() => {
// The durable manager may have accepted a write. Never repeat it under a new ID.
finish(new Error('codex-link:outcome_unknown'))
try {
child.kill()
} catch {
/* proxy already exited; manager is independent */
}
}, deps.timeoutMs ?? 30_000)
child.stdout.on('data', (chunk: Buffer) => {
if (settled) {
return
}
bytes += chunk.length
if (bytes > CODEX_LINK_RESPONSE_BYTES) {
finish(new Error('codex-link:outcome_unknown'))
try {
child.kill()
} catch {
/* no process tree termination */
}
} else {
chunks.push(Buffer.from(chunk))
}
})
// Drain concurrently, with no history, paths, credentials or uncontrolled diagnostics logged.
child.stderr.on('data', () => {})
child.stdin.on('error', () => {})
child.once('error', () => finish(new Error('codex-link:outcome_unknown')))
child.once('close', (code: number | null) => {
if (settled) {
return
}
if (code !== 0 && code !== 1) {
finish(new Error('codex-link:outcome_unknown'))
return
}
try {
const response = parseControlResponse(Buffer.concat(chunks), raw)
if (code === 1 && response.kind !== 'control_error') {
finish(new Error('codex-link:outcome_unknown'))
} else {
finish(undefined, response)
}
} catch {
finish(new Error('codex-link:outcome_unknown'))
}
})
try {
child.stdin.end(JSON.stringify(raw) + '\n')
} catch {
finish(new Error('codex-link:outcome_unknown'))
}
})
}
}