fix: respond to ConPTY DSR cursor query on spawn
ConPTY sends ESC[6n (Device Status Report — report cursor position) immediately on session start and blocks the child process until it receives a reply. Since portable-pty never responds, the child hangs forever and the PTY times out. Fix: write ESC[1;1R (cursor at row 1, col 1) to the PTY input pipe right after spawning, before reading any output. This unblocks ConPTY and allows the child process to start normally.
This commit is contained in:
@@ -61,3 +61,4 @@ python/omp-rpc/src/omp_rpc.egg-info/
|
||||
# parallel-agent worktrees
|
||||
.wt/
|
||||
CPU*.md
|
||||
packages/coding-agent/binaries/
|
||||
|
||||
@@ -252,8 +252,18 @@ fn run_pty_sync(
|
||||
.map_err(|err| Error::from_reason(format!("Failed to open PTY: {err}")))?
|
||||
};
|
||||
|
||||
let mut cmd = CommandBuilder::new(config.shell.as_deref().unwrap_or("sh"));
|
||||
cmd.arg("-lc");
|
||||
let shell = config.shell.as_deref().unwrap_or("sh");
|
||||
let mut cmd = CommandBuilder::new(shell);
|
||||
// Use shell-appropriate command execution flags
|
||||
let lower = shell.to_lowercase();
|
||||
if lower.ends_with("cmd.exe") || lower.ends_with("cmd") {
|
||||
cmd.arg("/c");
|
||||
} else if lower.contains("powershell") || lower.contains("pwsh") {
|
||||
cmd.arg("-Command");
|
||||
} else {
|
||||
// sh/bash/zsh/fish etc.
|
||||
cmd.arg("-lc");
|
||||
}
|
||||
cmd.arg(&config.command);
|
||||
if let Some(cwd) = config.cwd.as_ref() {
|
||||
cmd.cwd(cwd);
|
||||
@@ -276,6 +286,10 @@ fn run_pty_sync(
|
||||
let mut writer = master
|
||||
.take_writer()
|
||||
.map_err(|err| Error::from_reason(format!("Failed to create PTY writer: {err}")))?;
|
||||
// ConPTY sends ESC[6n (cursor position query) and blocks until we reply.
|
||||
// Reply with cursor at 1,1 so it unblocks the child spawn.
|
||||
let _ = writer.write_all(b"\x1b[1;1R");
|
||||
let _ = writer.flush();
|
||||
let mut reader = master
|
||||
.try_clone_reader()
|
||||
.map_err(|err| Error::from_reason(format!("Failed to create PTY reader: {err}")))?;
|
||||
|
||||
Vendored
+5
@@ -1137,6 +1137,11 @@ export interface PtyStartOptions {
|
||||
cols?: number
|
||||
/** PTY row count. */
|
||||
rows?: number
|
||||
/**
|
||||
* Shell binary to use (e.g. "sh", "bash", or an absolute path).
|
||||
* Defaults to "sh" if not provided.
|
||||
*/
|
||||
shell?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use portable_pty::{native_pty_system, PtySize, CommandBuilder};
|
||||
|
||||
fn main() {
|
||||
println!("Starting PTY reproduction...");
|
||||
let pty_system = native_pty_system();
|
||||
println!("Got pty system");
|
||||
|
||||
let pair = pty_system.openpty(PtySize {
|
||||
rows: 40,
|
||||
cols: 120,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
});
|
||||
match pair {
|
||||
Ok(p) => println!("openpty succeeded!"),
|
||||
Err(e) => println!("openpty failed: {}", e),
|
||||
}
|
||||
|
||||
println!("Done.");
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { PtySession } from "@oh-my-pi/pi-natives";
|
||||
|
||||
async function testPty(shell: string, command: string, label: string) {
|
||||
const session = new PtySession();
|
||||
const t0 = Date.now();
|
||||
let output = "";
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
session.start(
|
||||
{
|
||||
command,
|
||||
shell,
|
||||
timeoutMs: 5000,
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
},
|
||||
(_err, chunk) => {
|
||||
if (chunk) output += chunk;
|
||||
},
|
||||
).then((result) => {
|
||||
const ms = Date.now() - t0;
|
||||
// Strip VT sequences for readable output
|
||||
const clean = output.replace(/\x1b\[[^a-zA-Z]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").trim();
|
||||
console.log(`[${label}] ${ms}ms timedOut=${result.timedOut} exitCode=${result.exitCode} output="${clean}"`);
|
||||
resolve();
|
||||
}).catch((err) => {
|
||||
console.log(`[${label}] ${Date.now() - t0}ms ERROR: ${err.message}`);
|
||||
resolve();
|
||||
});
|
||||
setTimeout(() => { console.log(`[${label}] HUNG`); resolve(); }, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
await testPty("cmd.exe", "echo hello from cmd", "cmd");
|
||||
await testPty("powershell.exe", "Write-Host 'hello from pwsh'", "pwsh");
|
||||
await testPty("sh", "echo hello from bash", "sh-git-bash");
|
||||
process.exit(0);
|
||||
Reference in New Issue
Block a user