Add 5 pi extensions: pi-subagents, pi-crew, rpiv-pi, pi-interactive-shell, pi-intercom
This commit is contained in:
86
extensions/pi-crew/src/runtime/post-exit-stdio-guard.ts
Normal file
86
extensions/pi-crew/src/runtime/post-exit-stdio-guard.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { ChildProcess } from "node:child_process";
|
||||
|
||||
interface PostExitStdioGuardOptions {
|
||||
idleMs: number;
|
||||
hardMs: number;
|
||||
}
|
||||
|
||||
export interface ChildWithPipedStdio {
|
||||
stdout: ChildProcess["stdout"];
|
||||
stderr: ChildProcess["stderr"];
|
||||
on: ChildProcess["on"];
|
||||
}
|
||||
|
||||
export interface ChildWithKill {
|
||||
kill(signal?: NodeJS.Signals | number): boolean;
|
||||
}
|
||||
|
||||
export function trySignalChild(child: ChildWithKill, signal: NodeJS.Signals): boolean {
|
||||
try {
|
||||
return child.kill(signal);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function attachPostExitStdioGuard(child: ChildWithPipedStdio, options: PostExitStdioGuardOptions): () => void {
|
||||
const { idleMs, hardMs } = options;
|
||||
let exited = false;
|
||||
let stdoutEnded = false;
|
||||
let stderrEnded = false;
|
||||
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let hardTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
const destroyUnendedStdio = (): void => {
|
||||
if (!stdoutEnded) {
|
||||
try {
|
||||
child.stdout?.destroy();
|
||||
} catch {}
|
||||
}
|
||||
if (!stderrEnded) {
|
||||
try {
|
||||
child.stderr?.destroy();
|
||||
} catch {}
|
||||
}
|
||||
};
|
||||
|
||||
const clearTimers = (): void => {
|
||||
if (idleTimer) {
|
||||
clearTimeout(idleTimer);
|
||||
idleTimer = undefined;
|
||||
}
|
||||
if (hardTimer) {
|
||||
clearTimeout(hardTimer);
|
||||
hardTimer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const armIdleTimer = () => {
|
||||
if (!exited) return;
|
||||
if (idleTimer) clearTimeout(idleTimer);
|
||||
idleTimer = setTimeout(destroyUnendedStdio, idleMs);
|
||||
idleTimer.unref();
|
||||
};
|
||||
|
||||
child.stdout?.on("data", armIdleTimer);
|
||||
child.stderr?.on("data", armIdleTimer);
|
||||
child.stdout?.on("end", () => {
|
||||
stdoutEnded = true;
|
||||
if (stdoutEnded && stderrEnded) clearTimers();
|
||||
});
|
||||
child.stderr?.on("end", () => {
|
||||
stderrEnded = true;
|
||||
if (stdoutEnded && stderrEnded) clearTimers();
|
||||
});
|
||||
child.on("exit", () => {
|
||||
exited = true;
|
||||
armIdleTimer();
|
||||
if (hardTimer) return;
|
||||
hardTimer = setTimeout(destroyUnendedStdio, hardMs);
|
||||
hardTimer.unref();
|
||||
});
|
||||
child.on("close", clearTimers);
|
||||
child.on("error", clearTimers);
|
||||
|
||||
return clearTimers;
|
||||
}
|
||||
Reference in New Issue
Block a user