






















@@ -5,6 +5,7 @@ import { access, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promise
55import { tmpdir } from "node:os";
66import path from "node:path";
77import { pathToFileURL } from "node:url";
8+import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
89import { afterEach, describe, expect, it, vi } from "vitest";
910import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
1011import {
@@ -409,6 +410,15 @@ describe("resolve-openclaw-package-candidate", () => {
409410).rejects.toThrow(/produced more than \d+ captured stdout chars/u);
410411});
411412413+it("clamps oversized package runner command timers before scheduling", async () => {
414+await expect(
415+runCommandForTest(process.execPath, ["-e", "setTimeout(() => process.exit(0), 25);"], {
416+killAfterMs: MAX_TIMER_TIMEOUT_MS + 1,
417+timeoutMs: MAX_TIMER_TIMEOUT_MS + 1,
418+}),
419+).resolves.toBe("");
420+});
421+412422it("kills timed-out package runner process groups", async () => {
413423if (process.platform === "win32") {
414424return;
@@ -448,6 +458,45 @@ describe("resolve-openclaw-package-candidate", () => {
448458}
449459});
450460461+it("clamps oversized package runner kill grace before scheduling", async () => {
462+if (process.platform === "win32") {
463+return;
464+}
465+466+const dir = await mkdtemp(path.join(tmpdir(), "openclaw-package-runner-grace-"));
467+tempDirs.push(dir);
468+const childPidPath = path.join(dir, "child.pid");
469+const cleanupPath = path.join(dir, "child.cleanup");
470+let childPid: number | undefined;
471+472+try {
473+const childScript = [
474+"const fs = require('node:fs');",
475+`fs.writeFileSync(${JSON.stringify(childPidPath)}, String(process.pid));`,
476+"process.on('SIGTERM', () => {",
477+` setTimeout(() => { fs.writeFileSync(${JSON.stringify(cleanupPath)}, 'clean'); process.exit(0); }, 75);`,
478+"});",
479+"setInterval(() => {}, 1000);",
480+].join("\n");
481+482+const timeoutAssertion = expect(
483+runCommandForTest(process.execPath, ["-e", childScript], {
484+killAfterMs: MAX_TIMER_TIMEOUT_MS + 1,
485+timeoutMs: 500,
486+}),
487+).rejects.toThrow(/timed out after 500ms/u);
488+489+await waitForFile(childPidPath, 2_000);
490+childPid = Number.parseInt(readFileSync(childPidPath, "utf8"), 10);
491+await timeoutAssertion;
492+expect(readFileSync(cleanupPath, "utf8")).toBe("clean");
493+} finally {
494+if (childPid !== undefined && isProcessAlive(childPid)) {
495+process.kill(childPid, "SIGKILL");
496+}
497+}
498+});
499+451500it("rejects timed-out package runner commands when descendants exit cleanly", async () => {
452501if (process.platform === "win32") {
453502return;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。