repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/nushell/runtime.ts
Rawimport { spawn } from "node:child_process";
import { constants } from "node:fs";
import { access } from "node:fs/promises";
import { join } from "node:path";
import type { BashOperations } from "@earendil-works/pi-coding-agent";
import { span } from "./src/debug.ts";
import { findExecutable } from "./src/pi-ext-executable.ts";
const FRESH_ARGS = [
"--no-config-file",
"--no-history",
"--error-style",
"fancy",
"--table-mode",
"markdown",
"-c",
];
export function findNu(): string | undefined {
return findExecutable(process.platform === "win32" ? ["nu.exe"] : ["nu"]);
}
function stopProcess(pid: number | undefined): void {
if (!pid) return;
if (process.platform === "win32") {
const root = process.env["SystemRoot"] ?? "C:\\Windows";
spawn(
join(root, "System32", "taskkill.exe"),
["/F", "/T", "/PID", String(pid)],
{
stdio: "ignore",
windowsHide: true,
},
).unref();
} else {
try {
process.kill(-pid, "SIGKILL");
} catch {
try {
process.kill(pid, "SIGKILL");
} catch {
/* already exited */
}
}
}
}
export function freshOperations(binary: string): BashOperations {
return {
async exec(command, cwd, { onData, signal, timeout, env }) {
const finish = span?.(
"fresh",
timeout === undefined ? undefined : { timeoutMs: timeout * 1_000 },
);
let exitCode: number | null = null;
let timedOut = false;
let outputBytes = 0;
try {
if (signal?.aborted) throw new Error("aborted");
if (
timeout !== undefined &&
(!Number.isFinite(timeout) ||
timeout <= 0 ||
timeout * 1000 > 2_147_483_647)
)
throw new Error(
"Invalid timeout: must be a positive number of seconds within the timer range",
);
await access(cwd, constants.F_OK);
const child = spawn(binary, [...FRESH_ARGS, command], {
cwd,
env,
detached: process.platform !== "win32",
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true,
});
const onAbort = () => stopProcess(child.pid);
const timer =
timeout === undefined
? undefined
: setTimeout(() => {
timedOut = true;
onAbort();
}, timeout * 1000);
signal?.addEventListener("abort", onAbort, { once: true });
if (signal?.aborted) onAbort();
if (finish) {
const forward = (chunk: Buffer) => {
outputBytes += chunk.length;
onData(chunk);
};
child.stdout.on("data", forward);
child.stderr.on("data", forward);
} else {
child.stdout.on("data", onData);
child.stderr.on("data", onData);
}
try {
exitCode = await new Promise<number | null>((resolve, reject) => {
child.once("error", reject);
child.once("close", resolve);
});
if (signal?.aborted) throw new Error("aborted");
if (timedOut) throw new Error(`timeout:${timeout}`);
return { exitCode: exitCode ?? (child.signalCode ? 1 : null) };
} finally {
if (timer) clearTimeout(timer);
signal?.removeEventListener("abort", onAbort);
}
} finally {
const status = signal?.aborted
? "aborted"
: timedOut
? "timeout"
: exitCode === 0
? "success"
: exitCode === null
? "failed"
: "nonzero";
finish?.(
status === "success" || status === "nonzero" ? "finish" : "error",
{
status,
count: outputBytes,
},
);
}
},
};
}