
























@@ -445,6 +445,110 @@ describe("bundled plugin install/uninstall probe", () => {
445445}
446446});
447447448+it.runIf(process.platform !== "win32")(
449+"rejects package-manager grandchildren under runtime gateways",
450+async () => {
451+const runtimeSmoke = await importRuntimeSmokeWithEnv({
452+OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS: "50",
453+OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS: "1000",
454+});
455+const root = makePackageRoot();
456+const entrypoint = path.join(root, "dist", "gateway-with-package-manager-grandchild.js");
457+const logPath = path.join(root, "gateway-package-manager.log");
458+const packageManagerPidPath = path.join(root, "package-manager.pid");
459+const packageManagerScript = "setInterval(() => {}, 1000);";
460+const helperScript = [
461+"import childProcess from 'node:child_process';",
462+"import fs from 'node:fs';",
463+`const child = childProcess.spawn(process.execPath, ["-e", ${JSON.stringify(
464+ packageManagerScript,
465+ )}], { argv0: "pnpm", stdio: "ignore" });`,
466+`fs.writeFileSync(${JSON.stringify(packageManagerPidPath)}, String(child.pid));`,
467+"process.on('SIGTERM', () => { child.kill('SIGTERM'); process.exit(0); });",
468+"setInterval(() => {}, 1000);",
469+].join("\n");
470+fs.writeFileSync(
471+entrypoint,
472+[
473+"import childProcess from 'node:child_process';",
474+"if (process.argv[2] === 'gateway') {",
475+` childProcess.spawn(process.execPath, ["--input-type=module", "--eval", ${JSON.stringify(
476+ helperScript,
477+ )}], { stdio: "ignore" });`,
478+" process.on('SIGTERM', () => process.exit(0));",
479+" setInterval(() => {}, 1000);",
480+"}",
481+"",
482+].join("\n"),
483+"utf8",
484+);
485+486+const child = runtimeSmoke.startGateway({
487+ entrypoint,
488+env: {},
489+ logPath,
490+port: 19007,
491+skipChannels: true,
492+});
493+let packageManagerPid: number | undefined;
494+try {
495+await waitForFile(packageManagerPidPath, 1000);
496+packageManagerPid = Number(fs.readFileSync(packageManagerPidPath, "utf8"));
497+expect(pidIsAlive(packageManagerPid)).toBe(true);
498+499+await expect(runtimeSmoke.assertNoPackageManagerChildren(child.pid)).rejects.toThrow(
500+/package manager descendant process still running/u,
501+);
502+} finally {
503+await runtimeSmoke.stopGateway(child);
504+if (packageManagerPid !== undefined && pidIsAlive(packageManagerPid)) {
505+process.kill(packageManagerPid, "SIGKILL");
506+}
507+}
508+},
509+);
510+511+it("finds package-manager descendants recursively in process snapshots", async () => {
512+const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href);
513+const runtimeSmokeSource = fs.readFileSync(runtimeSmokePath, "utf8");
514+const longWrapperPath = `/tmp/${"nested/".repeat(40)}pnpm.cjs`;
515+516+const descendants = runtimeSmoke.findPackageManagerDescendants(
517+[
518+" 100 1 node gateway",
519+" 101 100 sh -c helper",
520+" 102 101 /usr/local/bin/pnpm install",
521+" 103 100 /usr/bin/npm-helper",
522+" 104 1 yarn install",
523+" 105 101 node /opt/pnpm.cjs install",
524+` 106 101 node ${longWrapperPath} install`,
525+].join("\n"),
526+100,
527+);
528+529+expect(runtimeSmokeSource).toContain('["-ww", "-eo", "pid=,ppid=,args="]');
530+expect(
531+descendants.toSorted((left: { pid: number }, right: { pid: number }) => left.pid - right.pid),
532+).toEqual([
533+{ args: "/usr/local/bin/pnpm install", pid: 102, ppid: 101 },
534+{ args: "/usr/bin/npm-helper", pid: 103, ppid: 100 },
535+{ args: "node /opt/pnpm.cjs install", pid: 105, ppid: 101 },
536+{ args: `node ${longWrapperPath} install`, pid: 106, ppid: 101 },
537+]);
538+expect(
539+runtimeSmoke.findPackageManagerDescendants(
540+[
541+" 100 1 node gateway",
542+" 101 100 sh -c helper",
543+" 102 101 /usr/local/bin/pnpm install",
544+" 103 100 /usr/bin/npm-helper",
545+" 104 1 yarn install",
546+].join("\n"),
547+100,
548+),
549+).not.toContainEqual({ args: "yarn install", pid: 104, ppid: 1 });
550+});
551+448552it.runIf(process.platform !== "win32")(
449553"cleans detached runtime gateway groups when the parent is signaled",
450554async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。