From f2a0b3d2e2488c596ae3aadb17a10adeb5d1749b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 17 May 2026 02:24:28 +0100 Subject: [PATCH] chore(crabbox): warn about raw aws runtime commands --- CHANGELOG.md | 1 + scripts/crabbox-wrapper.mjs | 222 ++++++++++++++++++++++++++++++++++-- 2 files changed, 212 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7536fd1ad..c7b3af51d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai - Security/audit: add `security.audit.suppressions` for intentionally accepted audit findings, keeping suppressed matches out of the active summary while preserving them in JSON output with an active suppression notice. (#76949) Thanks @100menotu001. - Agents/subagents: label delegated task and subagent completion handoffs as ready for parent review, and tell requester agents to review/verify results before calling them done. (#78985) Thanks @100menotu001. - Providers/media: add fal and OpenRouter music-generation providers for the shared `music_generate` tool, including fal MiniMax/ACE/Stable Audio endpoints and OpenRouter Lyria audio output. +- Maintainer tooling: warn before running JS package commands on raw Crabbox AWS boxes, pointing maintainers to Actions hydration or Blacksmith Testbox for CI-like proof. - Control UI: show provider quota usage in the Overview card and Chat header, and recover stale Chat in-progress state after missed terminal events. (#82647) - Mac app remote setup can now be preconfigured from `openclaw-mac configure-remote`, skips onboarding when config is already complete, supports direct LAN/Tailnet gateway URLs, allows private same-origin Control UI loads, and owns the SSH tunnel process when SSH is selected. - Providers/xAI: add xAI Grok OAuth login for SuperGrok subscribers, letting `xai/*` models and xAI media/tool providers authenticate without `XAI_API_KEY`. diff --git a/scripts/crabbox-wrapper.mjs b/scripts/crabbox-wrapper.mjs index 2e012c906ff..3a934f76d8e 100755 --- a/scripts/crabbox-wrapper.mjs +++ b/scripts/crabbox-wrapper.mjs @@ -30,6 +30,10 @@ function checkedOutput(command, commandArgs) { } function configuredProvider() { + const envProvider = process.env.CRABBOX_PROVIDER?.trim(); + if (envProvider) { + return envProvider; + } try { const config = readFileSync(resolve(repoRoot, ".crabbox.yaml"), "utf8"); const match = config.match(/^provider:\s*([^\s#]+)/m); @@ -39,17 +43,192 @@ function configuredProvider() { } } -function selectedProvider(commandArgs) { - for (let index = 0; index < commandArgs.length; index += 1) { +const runValueOptions = new Set([ + "allow-env", + "azure-location", + "azure-os-disk", + "azure-resource-group", + "azure-subnet", + "azure-vnet", + "blacksmith-job", + "blacksmith-org", + "blacksmith-ref", + "blacksmith-workflow", + "capture-stderr", + "capture-stdout", + "class", + "cloudflare-url", + "cloudflare-workdir", + "daytona-api-url", + "daytona-snapshot", + "daytona-ssh-access-minutes", + "daytona-ssh-gateway-host", + "daytona-target", + "daytona-user", + "daytona-work-root", + "download", + "env-from-profile", + "env-helper", + "e2b-api-url", + "e2b-domain", + "e2b-template", + "e2b-user", + "e2b-workdir", + "fresh-pr", + "id", + "idle-timeout", + "islo-base-url", + "islo-disk-gb", + "islo-gateway-profile", + "islo-image", + "islo-memory-mb", + "islo-snapshot-name", + "islo-vcpus", + "islo-workdir", + "junit", + "market", + "modal-app", + "modal-image", + "modal-python", + "modal-workdir", + "namespace-auto-stop-idle-timeout", + "namespace-image", + "namespace-repository", + "namespace-site", + "namespace-size", + "namespace-volume-size-gb", + "namespace-work-root", + "network", + "preflight-tools", + "profile", + "provider", + "proxmox-api-url", + "proxmox-bridge", + "proxmox-node", + "proxmox-pool", + "proxmox-storage", + "proxmox-template-id", + "proxmox-user", + "proxmox-work-root", + "script", + "semaphore-host", + "semaphore-idle-timeout", + "semaphore-machine", + "semaphore-os-image", + "semaphore-project", + "sprites-api-url", + "sprites-work-root", + "static-host", + "static-port", + "static-user", + "static-work-root", + "tailscale-auth-key-env", + "tailscale-exit-node", + "tailscale-hostname-template", + "tailscale-tags", + "target", + "tensorlake-api-url", + "tensorlake-cli", + "tensorlake-cpus", + "tensorlake-disk-mb", + "tensorlake-image", + "tensorlake-memory-mb", + "tensorlake-namespace", + "tensorlake-organization-id", + "tensorlake-project-id", + "tensorlake-snapshot", + "tensorlake-timeout-secs", + "tensorlake-workdir", + "ttl", + "type", + "windows-mode", +]); + +function runOptionName(arg) { + return arg.replace(/^-+/u, "").split("=", 1)[0]; +} + +function runCommandBounds(commandArgs) { + if (commandArgs[0] !== "run") { + return { start: -1, optionEnd: commandArgs.length }; + } + for (let index = 1; index < commandArgs.length; index += 1) { const arg = commandArgs[index]; - if (arg === "--provider") { - return commandArgs[index + 1] ?? ""; + if (arg === "--") { + return { start: index + 1, optionEnd: index }; } - if (arg.startsWith("--provider=")) { - return arg.slice("--provider=".length); + if (!arg.startsWith("-")) { + return { start: index, optionEnd: index }; + } + if (!arg.includes("=") && runValueOptions.has(runOptionName(arg))) { + index += 1; } } - return configuredProvider(); + return { start: -1, optionEnd: commandArgs.length }; +} + +function crabboxOptionArgs(commandArgs) { + const bounds = runCommandBounds(commandArgs); + if (commandArgs[0] === "run") { + return commandArgs.slice(0, bounds.optionEnd); + } + const delimiter = commandArgs.indexOf("--"); + return delimiter >= 0 ? commandArgs.slice(0, delimiter) : commandArgs; +} + +function commandProvider(commandArgs) { + commandArgs = crabboxOptionArgs(commandArgs); + for (let index = 0; index < commandArgs.length; index += 1) { + const arg = commandArgs[index]; + if (arg === "--provider" || arg === "-provider") { + return commandArgs[index + 1] ?? ""; + } + if (arg.startsWith("--provider=") || arg.startsWith("-provider=")) { + return arg.slice(arg.indexOf("=") + 1); + } + } + return ""; +} + +function selectedProvider(commandArgs) { + return commandProvider(commandArgs) || configuredProvider(); +} + +function optionValue(commandArgs, name) { + commandArgs = crabboxOptionArgs(commandArgs); + for (let index = 0; index < commandArgs.length; index += 1) { + const arg = commandArgs[index]; + if (arg === name || arg === name.replace(/^--/u, "-")) { + return commandArgs[index + 1] ?? ""; + } + if (arg.startsWith(`${name}=`) || arg.startsWith(`${name.replace(/^--/u, "-")}=`)) { + return arg.slice(arg.indexOf("=") + 1); + } + } + return ""; +} + +function runCommandArgs(commandArgs) { + const { start } = runCommandBounds(commandArgs); + return start >= 0 ? commandArgs.slice(start) : []; +} + +function commandRuntimeEntrypoint(commandArgs) { + const words = commandArgs.length === 1 ? commandArgs[0].split(/\s+/u) : commandArgs; + while (words[0] === "env") { + words.shift(); + while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) { + words.shift(); + } + } + while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) { + words.shift(); + } + const first = (words[0] ?? "") + .replace(/^['"]|['";|&()]+$/g, "") + .split("/") + .pop(); + return ["pnpm", "npm", "npx", "corepack", "node", "yarn", "bun"].includes(first) ? first : ""; } const version = checkedOutput(binary, ["--version"]); @@ -72,12 +251,11 @@ const knownProviders = [ "cloudflare", ]; const providers = knownProviders.filter((provider) => - new RegExp(`\\b${provider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test( - help.text, - ), + new RegExp(`\\b${provider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(help.text), ); const displayBinary = binary === "crabbox" ? "crabbox" : relative(repoRoot, binary); const provider = selectedProvider(args); +const commandProviderValue = commandProvider(args); console.error( `[crabbox] bin=${displayBinary} version=${version.text || "unknown"} provider=${provider || "unknown"} providers=${providers.join(",") || "unknown"}`, @@ -96,8 +274,30 @@ if (provider && knownProviders.includes(provider) && !providers.includes(provide } if (provider === "blacksmith-testbox") { + const envProvider = process.env.CRABBOX_PROVIDER?.trim(); + const source = commandProviderValue + ? "explicit" + : envProvider + ? "from CRABBOX_PROVIDER" + : "from config"; + const fallback = commandProviderValue + ? "rerun without --provider to use .crabbox.yaml" + : envProvider + ? "unset CRABBOX_PROVIDER to use .crabbox.yaml" + : "pass another --provider to override it"; console.error( - "[crabbox] provider=blacksmith-testbox explicit; if Testbox is queued or down, rerun without --provider to use .crabbox.yaml", + `[crabbox] provider=blacksmith-testbox ${source}; if Testbox is queued or down, ${fallback}`, + ); +} + +const runtimeEntrypoint = commandRuntimeEntrypoint(runCommandArgs(args)); +if (args[0] === "run" && provider === "aws" && runtimeEntrypoint) { + const id = optionValue(args, "--id"); + const hydrate = id + ? `pnpm crabbox:hydrate -- --id ${id}` + : "pnpm crabbox:warmup, then pnpm crabbox:hydrate -- --id "; + console.error( + `[crabbox] warning: provider=aws raw boxes may lack Node/Corepack/pnpm for ${runtimeEntrypoint}; hydrate first (${hydrate}) or pass --provider blacksmith-testbox for OpenClaw CI-like proof; not switching providers automatically`, ); }