





























@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
22import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
33import { tmpdir } from "node:os";
44import path from "node:path";
5-import { afterEach, beforeAll, describe, expect, it } from "vitest";
5+import { afterAll, beforeAll, describe, expect, it } from "vitest";
6677const tempDirs: string[] = [];
88const repoRoot = process.cwd();
@@ -17,17 +17,9 @@ function makeFakeCrabbox(helpText: string): string {
1717function writeFakeCrabbox(binDir: string, helpText: string): string {
1818mkdirSync(binDir, { recursive: true });
1919const crabboxPath = path.join(binDir, "crabbox");
20-const script = [
21- "#!/usr/bin/env node",
20+const helperPath = path.join(binDir, "fake-crabbox-json.cjs");
21+const helperScript = [
2222"const args = process.argv.slice(2);",
23-'if (args[0] === "--version") {',
24-' console.log("crabbox 0.15.0");',
25-" process.exit(0);",
26-"}",
27-'if (args[0] === "run" && args[1] === "--help") {',
28-` process.stdout.write(${JSON.stringify(helpText)});`,
29-" process.exit(0);",
30-"}",
3123'if (args[0] === "config" && args[1] === "show" && args.includes("--json")) {',
3224" const status = Number.parseInt(process.env.OPENCLAW_FAKE_CRABBOX_CONFIG_STATUS || '0', 10);",
3325" if (status !== 0) {",
@@ -42,6 +34,39 @@ function writeFakeCrabbox(binDir: string, helpText: string): string {
4234"const scriptContent = scriptPath ? require('node:fs').readFileSync(scriptPath, 'utf8') : '';",
4335"console.log(JSON.stringify({ args, cwd: process.cwd(), scriptContent }));",
4436].join("\n");
37+writeFileSync(helperPath, `${helperScript}\n`, "utf8");
38+39+if (process.platform !== "win32") {
40+const script = [
41+"#!/bin/sh",
42+'if [ "$1" = "--version" ]; then',
43+' printf "%s\\n" "crabbox 0.15.0"',
44+" exit 0",
45+"fi",
46+'if [ "$1" = "run" ] && [ "$2" = "--help" ]; then',
47+` printf "%s" ${shellSingleQuote(helpText)}`,
48+" exit 0",
49+"fi",
50+`exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(helperPath)} "$@"`,
51+].join("\n");
52+writeFileSync(crabboxPath, `${script}\n`, "utf8");
53+chmodSync(crabboxPath, 0o755);
54+return crabboxPath;
55+}
56+57+const script = [
58+"#!/usr/bin/env node",
59+"const args = process.argv.slice(2);",
60+'if (args[0] === "--version") {',
61+' console.log("crabbox 0.15.0");',
62+" process.exit(0);",
63+"}",
64+'if (args[0] === "run" && args[1] === "--help") {',
65+` process.stdout.write(${JSON.stringify(helpText)});`,
66+" process.exit(0);",
67+"}",
68+`require(${JSON.stringify(helperPath)});`,
69+].join("\n");
4570writeFileSync(crabboxPath, `${script}\n`, "utf8");
4671writeFileSync(
4772`${crabboxPath}.cmd`,
@@ -52,6 +77,10 @@ function writeFakeCrabbox(binDir: string, helpText: string): string {
5277return crabboxPath;
5378}
547980+function shellSingleQuote(value: string): string {
81+return `'${value.replaceAll("'", "'\\''")}'`;
82+}
83+5584function makeFakeGit(
5685responses: Record<string, { status?: number; stdout?: string; stderr?: string }>,
5786): string {
@@ -142,13 +171,13 @@ function expectGroupedShellCommand(remoteCommand: string, command: string): void
142171}
143172}
144173145-afterEach(() => {
174+afterAll(() => {
146175for (const dir of tempDirs.splice(0)) {
147176rmSync(dir, { recursive: true, force: true });
148177}
149178});
150179151-describe("scripts/crabbox-wrapper", () => {
180+describe.concurrent("scripts/crabbox-wrapper", () => {
152181const advertisedProviderAliasHelp = [
153182"provider: hetzner, aws, gcp, local-container, blacksmith-testbox,",
154183" namespace-devbox, runpod, semaphore, cloudflare, railway, exe-dev, or ssh",
@@ -397,7 +426,7 @@ describe("scripts/crabbox-wrapper", () => {
397426expect(remoteCommand).toContain("openclaw_crabbox_env");
398427expect(remoteCommand).not.toContain("export -f env openclaw_crabbox_env");
399428expect(remoteCommand).not.toContain('env() { openclaw_crabbox_env "$@"; };');
400-expect(remoteCommand).toContain('PATH=$PATH:${1#PATH=}');
429+expect(remoteCommand).toContain("PATH=$PATH:${1#PATH=}");
401430expect(remoteCommand).toContain("pnpm --version >&2");
402431expectGroupedShellCommand(
403432remoteCommand,
@@ -429,8 +458,8 @@ describe("scripts/crabbox-wrapper", () => {
429458const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? "");
430459expect(result.status).toBe(0);
431460expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js");
432-expect(remoteCommand).toContain('-u|--unset|-C|--chdir)');
433-expect(remoteCommand).toContain('-i|--ignore-environment)');
461+expect(remoteCommand).toContain("-u|--unset|-C|--chdir)");
462+expect(remoteCommand).toContain("-i|--ignore-environment)");
434463expect(remoteCommand).toContain("pnpm --version >&2");
435464expectGroupedShellCommand(
436465remoteCommand,
@@ -518,7 +547,7 @@ describe("scripts/crabbox-wrapper", () => {
518547});
519548520549it("does not shadow unrelated env calls in AWS macOS shell commands", () => {
521-const shellScript = 'node --version; env -i PATH=/usr/bin:/bin printenv PATH';
550+const shellScript = "node --version; env -i PATH=/usr/bin:/bin printenv PATH";
522551const result = runWrapper(
523552"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
524553["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript],
@@ -536,18 +565,7 @@ describe("scripts/crabbox-wrapper", () => {
536565it("does not bootstrap env split-string commands after ignore-environment", () => {
537566const result = runWrapper(
538567"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
539-[
540-"run",
541-"--provider",
542-"aws",
543-"--target",
544-"macos",
545-"--",
546-"env",
547-"-i",
548-"-S",
549-"pnpm --version",
550-],
568+["run", "--provider", "aws", "--target", "macos", "--", "env", "-i", "-S", "pnpm --version"],
551569);
552570553571const output = parseFakeCrabboxOutput(result);
@@ -1690,17 +1708,7 @@ describe("scripts/crabbox-wrapper", () => {
16901708it("preserves sparse changed-gate Git bootstrap for direct timeout-wrapped shell commands", () => {
16911709const result = runWrapper(
16921710"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
1693-[
1694-"run",
1695-"--provider",
1696-"aws",
1697-"--",
1698-"timeout",
1699-"1200s",
1700-"bash",
1701-"-lc",
1702-"pnpm check:changed",
1703-],
1711+["run", "--provider", "aws", "--", "timeout", "1200s", "bash", "-lc", "pnpm check:changed"],
17041712{
17051713gitResponses: {
17061714["config\u0000--bool\u0000core.sparseCheckout"]: { stdout: "true\n" },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。