






















@@ -7,38 +7,68 @@ export type LocalCommandProbe = {
77error?: string;
88};
9910+const LOCAL_COMMAND_PROBE_OUTPUT_MAX_CHARS = 16 * 1024;
11+const LOCAL_COMMAND_PROBE_KILL_GRACE_MS = 500;
12+13+function appendBounded(previous: string, chunk: string, limit: number): string {
14+const next = previous + chunk;
15+return next.length > limit ? next.slice(-limit) : next;
16+}
17+1018export async function probeLocalCommand(
1119command: string,
1220args: string[] = ["--version"],
13-opts: { timeoutMs?: number } = {},
21+opts: { outputLimit?: number; timeoutKillGraceMs?: number; timeoutMs?: number } = {},
1422): Promise<LocalCommandProbe> {
1523const timeoutMs = opts.timeoutMs ?? 1_500;
24+const outputLimit = opts.outputLimit ?? LOCAL_COMMAND_PROBE_OUTPUT_MAX_CHARS;
25+const timeoutKillGraceMs = opts.timeoutKillGraceMs ?? LOCAL_COMMAND_PROBE_KILL_GRACE_MS;
1626return await new Promise((resolve) => {
1727let stdout = "";
1828let stderr = "";
1929let settled = false;
30+let timedOut = false;
31+let killTimer: NodeJS.Timeout | undefined;
2032const child = spawn(command, args, {
2133stdio: ["ignore", "pipe", "pipe"],
2234});
35+const timeoutResult = (): LocalCommandProbe => ({
36+ command,
37+found: true,
38+error: `timed out after ${timeoutMs}ms`,
39+});
2340const finish = (result: LocalCommandProbe) => {
2441if (settled) {
2542return;
2643}
2744settled = true;
2845clearTimeout(timer);
46+if (killTimer) {
47+clearTimeout(killTimer);
48+}
2949resolve(result);
3050};
3151const timer = setTimeout(() => {
52+timedOut = true;
3253child.kill("SIGTERM");
33-finish({ command, found: true, error: `timed out after ${timeoutMs}ms` });
54+killTimer = setTimeout(
55+() => {
56+child.kill("SIGKILL");
57+child.stdout.destroy();
58+child.stderr.destroy();
59+finish(timeoutResult());
60+},
61+Math.max(0, timeoutKillGraceMs),
62+);
63+killTimer.unref?.();
3464}, timeoutMs);
3565child.stdout.setEncoding("utf8");
3666child.stderr.setEncoding("utf8");
3767child.stdout.on("data", (chunk) => {
38-stdout += String(chunk);
68+stdout = appendBounded(stdout, String(chunk), outputLimit);
3969});
4070child.stderr.on("data", (chunk) => {
41-stderr += String(chunk);
71+stderr = appendBounded(stderr, String(chunk), outputLimit);
4272});
4373child.on("error", (err: NodeJS.ErrnoException) => {
4474finish({
@@ -48,6 +78,10 @@ export async function probeLocalCommand(
4878});
4979});
5080child.on("close", (code) => {
81+if (timedOut) {
82+finish(timeoutResult());
83+return;
84+}
5185const text = `${stdout}\n${stderr}`.trim().split(/\r?\n/)[0]?.trim();
5286finish({
5387 command,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。