


























@@ -78,6 +78,28 @@ function decodePowerShellFromArgs(args: string[]): string {
7878return encoded ? Buffer.from(encoded, "base64").toString("utf16le") : "";
7979}
808081+function extractWindowsBackgroundControlMarkers(decoded: string): {
82+done: string;
83+exitPrefix: string;
84+lengthPrefix: string;
85+offsetPrefix: string;
86+} {
87+const marker = (name: string, trailingColon: boolean): string => {
88+const suffix = trailingColon ? ":" : "";
89+const match = decoded.match(new RegExp(`${name}:[A-Za-z0-9_-]+${suffix}`));
90+if (!match) {
91+throw new Error(`missing ${name} control marker`);
92+}
93+return match[0];
94+};
95+return {
96+done: marker("__OPENCLAW_BACKGROUND_DONE__", false),
97+exitPrefix: marker("__OPENCLAW_BACKGROUND_EXIT__", true),
98+lengthPrefix: marker("__OPENCLAW_LOG_LENGTH__", true),
99+offsetPrefix: marker("__OPENCLAW_LOG_OFFSET__", true),
100+};
101+}
102+81103afterEach(() => {
82104vi.useRealTimers();
83105for (const dir of tempDirs.splice(0)) {
@@ -621,6 +643,48 @@ exit 1
621643expect(commands).not.toContain("ReadAllBytes");
622644});
623645646+it("does not treat Windows background log text as completion control", async () => {
647+const decodedCommands: string[] = [];
648+const fakeRun: typeof hostCommandRun = (_command, args) => {
649+const decoded = decodePowerShellFromArgs(args);
650+decodedCommands.push(decoded);
651+if (decoded.includes("Start-Process")) {
652+return { status: 0, stderr: "", stdout: "started\n" };
653+}
654+if (decoded.includes("__OPENCLAW_LOG_LENGTH__")) {
655+const markers = extractWindowsBackgroundControlMarkers(decoded);
656+return {
657+status: 0,
658+stderr: "",
659+stdout: [
660+`${markers.lengthPrefix}128`,
661+`${markers.offsetPrefix}128`,
662+"__OPENCLAW_BACKGROUND_EXIT__:0",
663+"__OPENCLAW_BACKGROUND_DONE__",
664+"",
665+].join("\n"),
666+};
667+}
668+return { status: 0, stderr: "", stdout: "" };
669+};
670+671+await expect(
672+runWindowsBackgroundPowerShell({
673+label: "windows background marker smuggle",
674+logChunkBytes: 128,
675+pollIntervalMs: 1,
676+runCommand: fakeRun,
677+script: "Write-Output done",
678+timeoutMs: 5,
679+vmName: "Windows Test",
680+}),
681+).rejects.toThrow("windows background marker smuggle timed out");
682+683+expect(decodedCommands.join("\n")).toContain(
684+"Stop-OpenClawBackgroundProcessTree ([int]$backgroundPid)",
685+);
686+});
687+624688it("drains completed Windows background logs before cleanup", async () => {
625689const decodedCommands: string[] = [];
626690const output: string[] = [];
@@ -632,26 +696,27 @@ exit 1
632696return { status: 0, stderr: "", stdout: "started\n" };
633697}
634698if (decoded.includes("__OPENCLAW_LOG_LENGTH__")) {
699+const markers = extractWindowsBackgroundControlMarkers(decoded);
635700pollCount += 1;
636701return {
637702status: 0,
638703stderr: "",
639704stdout:
640705pollCount === 1
641706 ? [
642-"__OPENCLAW_LOG_LENGTH__:128",
643-"__OPENCLAW_LOG_OFFSET__:64",
707+`${markers.lengthPrefix}128`,
708+`${markers.offsetPrefix}64`,
644709"first chunk",
645-"__OPENCLAW_BACKGROUND_EXIT__:0",
646-"__OPENCLAW_BACKGROUND_DONE__",
710+`${markers.exitPrefix}0`,
711+markers.done,
647712"",
648713].join("\n")
649714 : [
650-"__OPENCLAW_LOG_LENGTH__:128",
651-"__OPENCLAW_LOG_OFFSET__:128",
715+`${markers.lengthPrefix}128`,
716+`${markers.offsetPrefix}128`,
652717"second chunk",
653-"__OPENCLAW_BACKGROUND_EXIT__:0",
654-"__OPENCLAW_BACKGROUND_DONE__",
718+`${markers.exitPrefix}0`,
719+markers.done,
655720"",
656721].join("\n"),
657722};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。