



























@@ -6,35 +6,24 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest";
6677const tempDirs: string[] = [];
88const repoRoot = process.cwd();
9+const fakeCrabboxBinDirs = new Map<string, string>();
9101011function makeFakeCrabbox(helpText: string): string {
12+const cached = fakeCrabboxBinDirs.get(helpText);
13+if (cached) {
14+return cached;
15+}
1116const binDir = mkdtempSync(path.join(tmpdir(), "openclaw-fake-crabbox-"));
1217tempDirs.push(binDir);
1318writeFakeCrabbox(binDir, helpText);
19+fakeCrabboxBinDirs.set(helpText, binDir);
1420return binDir;
1521}
16221723function writeFakeCrabbox(binDir: string, helpText: string): string {
1824mkdirSync(binDir, { recursive: true });
1925const crabboxPath = path.join(binDir, "crabbox");
2026const helperPath = path.join(binDir, "fake-crabbox-json.cjs");
21-const helperScript = [
22-"const args = process.argv.slice(2);",
23-'if (args[0] === "config" && args[1] === "show" && args.includes("--json")) {',
24-" const status = Number.parseInt(process.env.OPENCLAW_FAKE_CRABBOX_CONFIG_STATUS || '0', 10);",
25-" if (status !== 0) {",
26-" process.stderr.write('config unavailable\\n');",
27-" process.exit(status);",
28-" }",
29-' process.stdout.write(process.env.OPENCLAW_FAKE_CRABBOX_CONFIG_JSON || \'{"coordinator":"configured-broker","brokerAuth":"configured"}\');',
30-" process.exit(0);",
31-"}",
32-"const scriptIndex = args.findIndex((arg) => arg === '--script' || arg === '-script');",
33-"const scriptPath = scriptIndex >= 0 ? args[scriptIndex + 1] : '';",
34-"const scriptContent = scriptPath ? require('node:fs').readFileSync(scriptPath, 'utf8') : '';",
35-"console.log(JSON.stringify({ args, cwd: process.cwd(), scriptContent }));",
36-].join("\n");
37-writeFileSync(helperPath, `${helperScript}\n`, "utf8");
38273928if (process.platform !== "win32") {
4029const script = [
@@ -64,13 +53,48 @@ function writeFakeCrabbox(binDir: string, helpText: string): string {
6453" fi",
6554" done",
6655"fi",
67-`exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(helperPath)} "$@"`,
56+'script_path=""',
57+'previous_arg=""',
58+'for arg in "$@"; do',
59+' if [ "$previous_arg" = "--script" ] || [ "$previous_arg" = "-script" ]; then',
60+' script_path="$arg"',
61+" break",
62+" fi",
63+' previous_arg="$arg"',
64+"done",
65+'printf "%s\\0" "__OPENCLAW_FAKE_CRABBOX_V1__"',
66+'printf "%s\\0" "$PWD"',
67+'printf "%s\\0" "$#"',
68+'for arg in "$@"; do',
69+' printf "%s\\0" "$arg"',
70+"done",
71+'if [ -n "$script_path" ] && [ -f "$script_path" ]; then',
72+' cat "$script_path"',
73+"fi",
6874].join("\n");
6975writeFileSync(crabboxPath, `${script}\n`, "utf8");
7076chmodSync(crabboxPath, 0o755);
7177return crabboxPath;
7278}
737980+const helperScript = [
81+"const args = process.argv.slice(2);",
82+'if (args[0] === "config" && args[1] === "show" && args.includes("--json")) {',
83+" const status = Number.parseInt(process.env.OPENCLAW_FAKE_CRABBOX_CONFIG_STATUS || '0', 10);",
84+" if (status !== 0) {",
85+" process.stderr.write('config unavailable\\n');",
86+" process.exit(status);",
87+" }",
88+' process.stdout.write(process.env.OPENCLAW_FAKE_CRABBOX_CONFIG_JSON || \'{"coordinator":"configured-broker","brokerAuth":"configured"}\');',
89+" process.exit(0);",
90+"}",
91+"const scriptIndex = args.findIndex((arg) => arg === '--script' || arg === '-script');",
92+"const scriptPath = scriptIndex >= 0 ? args[scriptIndex + 1] : '';",
93+"const scriptContent = scriptPath ? require('node:fs').readFileSync(scriptPath, 'utf8') : '';",
94+"console.log(JSON.stringify({ args, cwd: process.cwd(), scriptContent }));",
95+].join("\n");
96+writeFileSync(helperPath, `${helperScript}\n`, "utf8");
97+7498const script = [
7599"#!/usr/bin/env node",
76100"const args = process.argv.slice(2);",
@@ -104,6 +128,39 @@ function makeFakeGit(
104128const binDir = mkdtempSync(path.join(tmpdir(), "openclaw-fake-git-"));
105129tempDirs.push(binDir);
106130const gitPath = path.join(binDir, "git");
131+if (process.platform !== "win32") {
132+const script = [
133+"#!/bin/sh",
134+'if [ "$1" = "worktree" ] && [ "$2" = "add" ]; then',
135+' mkdir -p "$4"',
136+" exit 0",
137+"fi",
138+'if [ "$1" = "-C" ] && [ "$3" = "sparse-checkout" ] && [ "$4" = "disable" ]; then',
139+" exit 0",
140+"fi",
141+'if [ "$1" = "-C" ] && [ "$3" = "reset" ] && [ "$4" = "--mixed" ]; then',
142+" exit 0",
143+"fi",
144+'if [ "$1" = "worktree" ] && [ "$2" = "remove" ]; then',
145+" exit 0",
146+"fi",
147+ ...Object.entries(responses).flatMap(([key, response]) => {
148+const args = key.split("\u0000");
149+return [
150+`if ${shellArgListCondition(args)}; then`,
151+response.stdout ? ` printf "%s" ${shellSingleQuote(response.stdout)}` : "",
152+response.stderr ? ` printf "%s" ${shellSingleQuote(response.stderr)} >&2` : "",
153+` exit ${response.status ?? 0}`,
154+"fi",
155+].filter(Boolean);
156+}),
157+"exit 1",
158+].join("\n");
159+writeFileSync(gitPath, `${script}\n`, "utf8");
160+chmodSync(gitPath, 0o755);
161+return binDir;
162+}
163+107164const script = [
108165"#!/usr/bin/env node",
109166"const fs = require('node:fs');",
@@ -126,6 +183,14 @@ function makeFakeGit(
126183return binDir;
127184}
128185186+function shellArgListCondition(args: string[]): string {
187+const checks = [`[ "$#" -eq ${args.length} ]`];
188+for (const [index, arg] of args.entries()) {
189+checks.push(`[ "$${index + 1}" = ${shellSingleQuote(arg)} ]`);
190+}
191+return checks.join(" && ");
192+}
193+129194function runWrapper(
130195helpText: string,
131196args: string[],
@@ -170,6 +235,24 @@ function parseFakeCrabboxOutput(result: ReturnType<typeof runWrapper>): {
170235cwd: string;
171236scriptContent?: string;
172237} {
238+const marker = "__OPENCLAW_FAKE_CRABBOX_V1__\0";
239+if (result.stdout.startsWith(marker)) {
240+let offset = marker.length;
241+const readField = () => {
242+const end = result.stdout.indexOf("\0", offset);
243+if (end < 0) {
244+throw new Error("missing fake Crabbox output field terminator");
245+}
246+const value = result.stdout.slice(offset, end);
247+offset = end + 1;
248+return value;
249+};
250+const cwd = readField();
251+const argCount = Number.parseInt(readField(), 10);
252+const args = Array.from({ length: argCount }, () => readField());
253+const scriptContent = result.stdout.slice(offset);
254+return { args, cwd, scriptContent };
255+}
173256return JSON.parse(result.stdout.trim()) as {
174257args: string[];
175258cwd: string;
@@ -231,7 +314,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
231314);
232315233316expect(result.status).toBe(0);
234-expect(result.stdout).toContain('"local-container"');
317+expect(parseFakeCrabboxOutput(result).args).toContain("local-container");
235318});
236319237320it("defaults AWS macOS runs to on-demand capacity", () => {
@@ -1238,7 +1321,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
12381321);
1239132212401323expect(result.status).toBe(0);
1241-expect(result.stdout).toContain('"docker"');
1324+expect(parseFakeCrabboxOutput(result).args).toContain("docker");
12421325expect(result.stderr).toContain(
12431326"providers=hetzner,aws,local-container,blacksmith-testbox,docker,cloudflare",
12441327);
@@ -1297,7 +1380,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
12971380]);
1298138112991382expect(result.status, alias).toBe(0);
1300-expect(result.stdout).toContain(`"${alias}"`);
1383+expect(parseFakeCrabboxOutput(result).args).toContain(alias);
13011384},
13021385);
13031386@@ -1312,7 +1395,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
13121395const result = runWrapper(helpText, ["run", "--provider", provider, "--", "echo ok"]);
1313139613141397expect(result.status, provider).toBe(0);
1315-expect(result.stdout).toContain(`"${provider}"`);
1398+expect(parseFakeCrabboxOutput(result).args).toContain(provider);
13161399}
13171400});
13181401@@ -1361,7 +1444,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
13611444);
1362144513631446expect(result.status).toBe(0);
1364-expect(result.stdout).not.toContain('"--no-sync"');
1447+expect(parseFakeCrabboxOutput(result).args).not.toContain("--no-sync");
13651448expect(result.stderr).toContain("syncing from temporary full checkout");
13661449expect(parseFakeCrabboxOutput(result).cwd).toContain("openclaw-crabbox-sync-");
13671450});
@@ -1971,7 +2054,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
19712054);
1972205519732056expect(result.status).toBe(0);
1974-expect(result.stdout).not.toContain('"--no-sync"');
2057+expect(parseFakeCrabboxOutput(result).args).not.toContain("--no-sync");
19752058expect(result.stderr).toContain("syncing from temporary full checkout");
19762059expect(parseFakeCrabboxOutput(result).cwd).toContain("openclaw-crabbox-sync-");
19772060});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。