mirror of
https://github.com/stablyai/orca.git
synced 2026-10-13 00:02:54 +00:00
* fix(terminal): submit startup commands with Enter on every platform LF is Ctrl+J to a line editor, so a shell that remaps it left the command at the prompt. Fixes #23250 Co-authored-by: Aashish Mahato <145881415+aashish254@users.noreply.github.com> * test(terminal): expect CR in pty ipc startup tests * fix(terminal): submit every line of a raw multiline startup command with CR The CR switch only replaced the trailing terminator. On the raw path — a shell with no bracketed paste, so each line break submits its own line — interior LFs stayed LF, which is the same remappable Ctrl+J the fix set out to remove. A multiline startup command in sh/dash/nushell/PowerShell therefore still stalled partway through for a user who rebinds Ctrl+J. Normalise interior breaks to CR on that path only; the bracketed-paste payload keeps its literal newlines. * fix(terminal): quote multi-line PowerShell arguments on one line A multi-line agent prompt was quoted as a single-quoted PowerShell string with raw line breaks. Typed into Windows PowerShell 5.1 without PSReadLine, each break submits a line and the shell waits at `>>` forever. Values with CR/LF now become one double-quoted line with backtick escapes, keeping every byte; single-line values keep the single-quoted form unchanged. * style(terminal): keep typographic quotes as \u escapes in PowerShell quoting Restores the escape form the single-quoted regex used on main and applies it to the new double-quoted regex and the native argv test, so the diff against main stays minimal and the characters are visible in review. --------- Co-authored-by: Aashish Mahato <145881415+aashish254@users.noreply.github.com> Co-authored-by: Neil <neil@stably.ai> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { expect, it } from 'vitest'
|
|
import { runProcess } from './child-process/run-process'
|
|
import { quotePowerShellLiteral } from './powershell-native-argument'
|
|
|
|
async function captureNativeArgv(root: string, quotedArg: string): Promise<string[]> {
|
|
const capturePath = join(root, 'argv.json')
|
|
const scriptPath = join(root, 'capture.js')
|
|
await writeFile(
|
|
scriptPath,
|
|
`require('node:fs').writeFileSync(${JSON.stringify(capturePath)}, JSON.stringify(process.argv.slice(2)))`
|
|
)
|
|
const result = await runProcess({
|
|
program: 'powershell.exe',
|
|
args: [
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-Command',
|
|
`& ${quotePowerShellLiteral(process.execPath)} ${quotePowerShellLiteral(scriptPath)} ${quotedArg}`
|
|
],
|
|
cwd: root
|
|
})
|
|
expect(result.code, JSON.stringify(result)).toBe(0)
|
|
const argv: string[] = JSON.parse(await readFile(capturePath, 'utf8'))
|
|
return argv
|
|
}
|
|
|
|
// Why native: argv marshalling, not the generated text, is what a launched agent actually receives.
|
|
it.skipIf(process.platform !== 'win32')(
|
|
'passes a multi-line value to a native program exactly as the single-quoted form did',
|
|
async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'orca-ps-multiline-'))
|
|
try {
|
|
const plain = 'line one\r\nline two $HOME `tick\nO\u2019Brien \u201Cdouble\u201D end'
|
|
expect(await captureNativeArgv(root, quotePowerShellLiteral(plain))).toEqual([plain])
|
|
|
|
// Why compare with the old raw single-quoted form: 5.1 drops embedded `"` from native argv
|
|
// either way, and the one-line form must not change what reaches the program.
|
|
const withQuotes = 'say "hi"\nbye'
|
|
const legacy = `'${withQuotes.replace(/'/g, "''")}'`
|
|
expect(await captureNativeArgv(root, quotePowerShellLiteral(withQuotes))).toEqual(
|
|
await captureNativeArgv(root, legacy)
|
|
)
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
}
|
|
)
|