

























11import { chmodSync, copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2+import { createServer } from "node:net";
23import { tmpdir } from "node:os";
34import { delimiter, join, win32 } from "node:path";
45import { pathToFileURL } from "node:url";
@@ -14,6 +15,7 @@ import {
1415shellQuote,
1516} from "../../scripts/e2e/parallels/common.ts";
1617import { resolveHostCommandInvocation } from "../../scripts/e2e/parallels/host-command.ts";
18+import { testing as hostServerTesting } from "../../scripts/e2e/parallels/host-server.ts";
1719import { spawnNodeEvalSync } from "../../src/test-utils/node-process.js";
18201921const WRAPPERS = {
@@ -95,6 +97,19 @@ function withEnv<T>(env: Record<string, string>, callback: () => T): T {
9597}
9698}
9799100+async function unusedLoopbackPort(): Promise<number> {
101+const server = createServer();
102+await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
103+const address = server.address();
104+await new Promise<void>((resolve, reject) =>
105+server.close((error) => (error ? reject(error) : resolve())),
106+);
107+if (!address || typeof address === "string") {
108+throw new Error("Expected TCP server address.");
109+}
110+return address.port;
111+}
112+98113describe("Parallels smoke model selection", () => {
99114it("keeps the public shell entrypoints as thin TypeScript launchers", () => {
100115for (const [platform, wrapperPath] of Object.entries(WRAPPERS)) {
@@ -174,6 +189,57 @@ describe("Parallels smoke model selection", () => {
174189}
175190});
176191192+it("bounds host artifact server startup stderr", () => {
193+const retained = hostServerTesting.appendBoundedOutput(
194+"a".repeat(10),
195+Buffer.from("b".repeat(10)),
196+12,
197+);
198+expect(retained).toBe(`${"a".repeat(2)}${"b".repeat(10)}`);
199+});
200+201+it.runIf(process.platform !== "win32")(
202+"reports only the bounded host artifact server stderr tail",
203+async () => {
204+const tempDir = mkdtempSync(join(tmpdir(), "openclaw-parallels-host-server-"));
205+const fakePython = join(tempDir, "python3");
206+writeFileSync(
207+fakePython,
208+`#!/usr/bin/env bash
209+set -euo pipefail
210+printf 'BEGIN_MARKER\\n' >&2
211+head -c 200000 </dev/zero | tr '\\0' x >&2
212+printf '\\nTAIL_MARKER\\n' >&2
213+exit 42
214+`,
215+);
216+chmodSync(fakePython, 0o755);
217+218+try {
219+const port = await unusedLoopbackPort();
220+const result = spawnNodeEvalSync(
221+`import { startHostServer } from "./${TS_PATHS.hostServer}"; await startHostServer({ dir: ".", hostIp: "127.0.0.1", port: ${port}, artifactPath: "artifact.tgz", label: "artifact" });`,
222+{
223+env: {
224+ ...process.env,
225+PATH: `${tempDir}${delimiter}${process.env.PATH ?? ""}`,
226+},
227+imports: ["tsx"],
228+maxBuffer: 1024 * 1024,
229+},
230+);
231+232+expect(result.status).toBe(1);
233+expect(result.stderr).toContain("host artifact server exited early");
234+expect(result.stderr).toContain("TAIL_MARKER");
235+expect(result.stderr).not.toContain("BEGIN_MARKER");
236+expect(Buffer.byteLength(result.stderr, "utf8")).toBeLessThan(90 * 1024);
237+} finally {
238+rmSync(tempDir, { force: true, recursive: true });
239+}
240+},
241+);
242+177243it("quotes shell args and resolves fuzzy snapshot hints through the shared TypeScript helper", () => {
178244const tempDir = mkdtempSync(join(tmpdir(), "openclaw-parallels-helper-"));
179245writeFakePrlctl(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。