From 3e02bc2f28cf52f3ccdbbbdc5c378fea655f6d93 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 2 May 2026 05:39:09 +0100 Subject: [PATCH] chore: sanity-check crabbox wrapper binary --- CHANGELOG.md | 1 + package.json | 9 ++--- scripts/crabbox-wrapper.mjs | 71 +++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100755 scripts/crabbox-wrapper.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0086c644e4f..c05e582f0e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai - Providers/OpenAI: add `extraBody`/`extra_body` passthrough for OpenAI-compatible TTS endpoints, so custom speech servers can receive fields such as `lang` in `/audio/speech` requests. Fixes #39900. Thanks @R3NK0R. - Dependencies: refresh workspace dependency pins, including TypeBox 1.1.37, AWS SDK 3.1041.0, Microsoft Teams 2.0.9, and Marked 18.0.3. Thanks @mariozechner, @aws, and @microsoft. - Discord/channels: add reusable message-channel access groups plus Discord channel-audience DM authorization, so allowlists can reference `accessGroup:` across channel auth paths. (#75813) +- Crabbox/scripts: print the selected Crabbox binary, version, and supported providers before `pnpm crabbox:*` commands, and reject stale binaries that lack `blacksmith-testbox` provider support. ### Fixes diff --git a/package.json b/package.json index a4cc7427e53..c3d2a303364 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "docs/", "!docs/.generated/**", "!docs/channels/qa-channel.md", + "scripts/crabbox-wrapper.mjs", "patches/", "skills/", "scripts/npm-runner.mjs", @@ -1302,10 +1303,10 @@ "config:docs:gen": "node --import tsx scripts/generate-config-doc-baseline.ts --write", "config:schema:check": "node --import tsx scripts/generate-base-config-schema.ts --check", "config:schema:gen": "node --import tsx scripts/generate-base-config-schema.ts --write", - "crabbox:hydrate": "sh -c 'if [ \"${1-}\" = \"--\" ]; then shift; fi; bin=crabbox; if [ -x ../crabbox/bin/crabbox ]; then bin=../crabbox/bin/crabbox; fi; exec \"$bin\" actions hydrate \"$@\"' --", - "crabbox:run": "sh -c 'if [ \"${1-}\" = \"--\" ]; then shift; fi; bin=crabbox; if [ -x ../crabbox/bin/crabbox ]; then bin=../crabbox/bin/crabbox; fi; exec \"$bin\" run \"$@\"' --", - "crabbox:stop": "sh -c 'if [ \"${1-}\" = \"--\" ]; then shift; fi; bin=crabbox; if [ -x ../crabbox/bin/crabbox ]; then bin=../crabbox/bin/crabbox; fi; exec \"$bin\" stop \"$@\"' --", - "crabbox:warmup": "sh -c 'if [ \"${1-}\" = \"--\" ]; then shift; fi; bin=crabbox; if [ -x ../crabbox/bin/crabbox ]; then bin=../crabbox/bin/crabbox; fi; exec \"$bin\" warmup \"$@\"' --", + "crabbox:hydrate": "node scripts/crabbox-wrapper.mjs actions hydrate", + "crabbox:run": "node scripts/crabbox-wrapper.mjs run", + "crabbox:stop": "node scripts/crabbox-wrapper.mjs stop", + "crabbox:warmup": "node scripts/crabbox-wrapper.mjs warmup", "deadcode:ci": "pnpm deadcode:report:ci:knip", "deadcode:dependencies": "pnpm --config.minimum-release-age=0 dlx knip@6.8.0 --config knip.config.ts --production --no-progress --reporter compact --dependencies --no-config-hints", "deadcode:knip": "pnpm dlx knip --config knip.config.ts --production --no-progress --reporter compact --files --dependencies", diff --git a/scripts/crabbox-wrapper.mjs b/scripts/crabbox-wrapper.mjs new file mode 100755 index 00000000000..cc47b07335b --- /dev/null +++ b/scripts/crabbox-wrapper.mjs @@ -0,0 +1,71 @@ +#!/usr/bin/env node +import { spawn, spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import { dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const repoLocal = resolve(repoRoot, "../crabbox/bin/crabbox"); +const binary = existsSync(repoLocal) ? repoLocal : "crabbox"; +const args = process.argv.slice(2); + +if (args[0] === "--") { + args.shift(); +} +const userArgStart = args[0] === "actions" && args[1] === "hydrate" ? 2 : 1; +if (args[userArgStart] === "--") { + args.splice(userArgStart, 1); +} + +function checkedOutput(command, commandArgs) { + const result = spawnSync(command, commandArgs, { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + return { + status: result.status ?? 1, + text: `${result.stdout ?? ""}${result.stderr ?? ""}`.trim(), + }; +} + +const version = checkedOutput(binary, ["--version"]); +const help = checkedOutput(binary, ["run", "--help"]); +const providers = ["hetzner", "aws", "blacksmith-testbox"].filter((provider) => + help.text.includes(provider), +); +const displayBinary = binary === "crabbox" ? "crabbox" : relative(repoRoot, binary); + +console.error( + `[crabbox] bin=${displayBinary} version=${version.text || "unknown"} providers=${providers.join(",") || "unknown"}`, +); + +if (version.status !== 0 || help.status !== 0) { + console.error("[crabbox] selected binary failed basic --version/--help sanity checks"); + process.exit(2); +} + +if (!providers.includes("blacksmith-testbox")) { + console.error( + "[crabbox] selected binary does not advertise provider blacksmith-testbox; refusing stale Crabbox binary", + ); + process.exit(2); +} + +const child = spawn(binary, args, { + cwd: repoRoot, + stdio: "inherit", +}); + +child.on("exit", (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); +}); + +child.on("error", (error) => { + console.error(`[crabbox] failed to execute ${displayBinary}: ${error.message}`); + process.exit(2); +});