























@@ -355,6 +355,25 @@ function normalizeShellLineEndings(value: string): string {
355355return value.replace(/\r\n/g, "\n");
356356}
357357358+function testCrabboxConfigDir(home: string): string {
359+if (process.platform === "darwin") {
360+return path.join(home, "Library", "Application Support", "crabbox");
361+}
362+if (process.platform === "win32") {
363+return path.join(home, "AppData", "Roaming", "crabbox");
364+}
365+return path.join(home, ".config", "crabbox");
366+}
367+368+function testHomeEnv(home: string): Record<string, string> {
369+return {
370+APPDATA: path.join(home, "AppData", "Roaming"),
371+HOME: home,
372+USERPROFILE: home,
373+XDG_CONFIG_HOME: path.join(home, ".config"),
374+};
375+}
376+358377function expectGroupedShellCommand(remoteCommand: string, command: string): void {
359378expect(remoteCommand).toContain(`&& { ${command}`);
360379if (process.platform !== "win32") {
@@ -490,6 +509,70 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
490509expect(parseFakeCrabboxOutput(result).args).toContain("blacksmith-testbox");
491510});
492511512+it("rejects reused Blacksmith Testboxes that were not created by Crabbox", () => {
513+const home = mkdtempSync(path.join(tmpdir(), "openclaw-crabbox-home-"));
514+tempDirs.push(home);
515+516+const result = runWrapper(
517+"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
518+["run", "--provider", "blacksmith-testbox", "--id", "tbx_direct", "--", "echo ok"],
519+{ env: testHomeEnv(home) },
520+);
521+522+expect(result.status).toBe(2);
523+expect(result.stdout).toBe("");
524+expect(result.stderr).toContain("provider=blacksmith-testbox --id tbx_direct");
525+expect(result.stderr).toContain("has no Crabbox SSH key");
526+expect(result.stderr).toContain("direct `blacksmith testbox warmup` leases");
527+});
528+529+it("allows reused Blacksmith Testboxes when the Crabbox SSH key exists", () => {
530+const home = mkdtempSync(path.join(tmpdir(), "openclaw-crabbox-home-"));
531+tempDirs.push(home);
532+const keyPath = path.join(testCrabboxConfigDir(home), "testboxes", "tbx_owned", "id_ed25519");
533+mkdirSync(path.dirname(keyPath), { recursive: true });
534+writeFileSync(keyPath, "fake test key\n", "utf8");
535+536+const result = runWrapper(
537+"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
538+["run", "--provider", "blacksmith-testbox", "--id", "tbx_owned", "--", "echo ok"],
539+{ env: testHomeEnv(home) },
540+);
541+542+expect(result.status).toBe(0);
543+expect(parseFakeCrabboxOutput(result).args).toEqual([
544+"run",
545+"--provider",
546+"blacksmith-testbox",
547+"--id",
548+"tbx_owned",
549+"--",
550+"echo ok",
551+]);
552+});
553+554+it("lets Crabbox resolve reusable Testbox slugs", () => {
555+const home = mkdtempSync(path.join(tmpdir(), "openclaw-crabbox-home-"));
556+tempDirs.push(home);
557+558+const result = runWrapper(
559+"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
560+["run", "--provider", "blacksmith-testbox", "--id", "blue-hermit", "--", "echo ok"],
561+{ env: testHomeEnv(home) },
562+);
563+564+expect(result.status).toBe(0);
565+expect(parseFakeCrabboxOutput(result).args).toEqual([
566+"run",
567+"--provider",
568+"blacksmith-testbox",
569+"--id",
570+"blue-hermit",
571+"--",
572+"echo ok",
573+]);
574+});
575+493576it("only forces the short local-container Docker work root on Linux", () => {
494577const result = runWrapper(
495578"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。