This commit is contained in:
Hiroki 2026-01-30 11:55:31 +00:00 committed by GitHub
commit fd897e3fe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 20 additions and 6 deletions

View File

@ -79,11 +79,13 @@ const APPROVAL_SLUG_LENGTH = 8;
type PtyExitEvent = { exitCode: number; signal?: number };
type PtyListener<T> = (event: T) => void;
type PtyDisposable = { dispose: () => void };
type PtyHandle = {
pid: number;
write: (data: string | Buffer) => void;
onData: (listener: PtyListener<string>) => void;
onExit: (listener: PtyListener<PtyExitEvent>) => void;
onData: (listener: PtyListener<string>) => PtyDisposable;
onExit: (listener: PtyListener<PtyExitEvent>) => PtyDisposable;
kill: (signal?: string) => void;
};
type PtySpawn = (
file: string,
@ -361,6 +363,8 @@ async function runExecProcess(opts: {
let child: ChildProcessWithoutNullStreams | null = null;
let pty: PtyHandle | null = null;
let stdin: SessionStdin | undefined;
let ptyDataDisposable: PtyDisposable | null = null;
let ptyExitDisposable: PtyDisposable | null = null;
if (opts.sandbox) {
const { child: spawned } = await spawnWithFallback({
@ -601,7 +605,7 @@ async function runExecProcess(opts: {
if (pty) {
const cursorResponse = buildCursorPositionResponse();
pty.onData((data) => {
ptyDataDisposable = pty.onData((data) => {
const raw = data.toString();
const { cleaned, requests } = stripDsrRequests(raw);
if (requests > 0) {
@ -664,10 +668,18 @@ async function runExecProcess(opts: {
};
if (pty) {
pty.onExit((event) => {
ptyExitDisposable = pty.onExit((event) => {
const rawSignal = event.signal ?? null;
const normalizedSignal = rawSignal === 0 ? null : rawSignal;
handleExit(event.exitCode ?? null, normalizedSignal);
// Clean up PTY resources to prevent FD leaks
try {
ptyDataDisposable?.dispose();
ptyExitDisposable?.dispose();
pty.kill();
} catch {
// Ignore cleanup errors
}
});
} else if (child) {
child.once("close", (code, exitSignal) => {

View File

@ -1,11 +1,13 @@
declare module "@lydell/node-pty" {
export type PtyExitEvent = { exitCode: number; signal?: number };
export type PtyListener<T> = (event: T) => void;
export type PtyDisposable = { dispose: () => void };
export type PtyHandle = {
pid: number;
write: (data: string | Buffer) => void;
onData: (listener: PtyListener<string>) => void;
onExit: (listener: PtyListener<PtyExitEvent>) => void;
onData: (listener: PtyListener<string>) => PtyDisposable;
onExit: (listener: PtyListener<PtyExitEvent>) => PtyDisposable;
kill: (signal?: string) => void;
};
export type PtySpawn = (