


































@@ -30,6 +30,10 @@ function checkedOutput(command, commandArgs) {
3030}
31313232function configuredProvider() {
33+const envProvider = process.env.CRABBOX_PROVIDER?.trim();
34+if (envProvider) {
35+return envProvider;
36+}
3337try {
3438const config = readFileSync(resolve(repoRoot, ".crabbox.yaml"), "utf8");
3539const match = config.match(/^provider:\s*([^\s#]+)/m);
@@ -39,17 +43,192 @@ function configuredProvider() {
3943}
4044}
414546+const runValueOptions = new Set([
47+"allow-env",
48+"azure-location",
49+"azure-os-disk",
50+"azure-resource-group",
51+"azure-subnet",
52+"azure-vnet",
53+"blacksmith-job",
54+"blacksmith-org",
55+"blacksmith-ref",
56+"blacksmith-workflow",
57+"capture-stderr",
58+"capture-stdout",
59+"class",
60+"cloudflare-url",
61+"cloudflare-workdir",
62+"daytona-api-url",
63+"daytona-snapshot",
64+"daytona-ssh-access-minutes",
65+"daytona-ssh-gateway-host",
66+"daytona-target",
67+"daytona-user",
68+"daytona-work-root",
69+"download",
70+"env-from-profile",
71+"env-helper",
72+"e2b-api-url",
73+"e2b-domain",
74+"e2b-template",
75+"e2b-user",
76+"e2b-workdir",
77+"fresh-pr",
78+"id",
79+"idle-timeout",
80+"islo-base-url",
81+"islo-disk-gb",
82+"islo-gateway-profile",
83+"islo-image",
84+"islo-memory-mb",
85+"islo-snapshot-name",
86+"islo-vcpus",
87+"islo-workdir",
88+"junit",
89+"market",
90+"modal-app",
91+"modal-image",
92+"modal-python",
93+"modal-workdir",
94+"namespace-auto-stop-idle-timeout",
95+"namespace-image",
96+"namespace-repository",
97+"namespace-site",
98+"namespace-size",
99+"namespace-volume-size-gb",
100+"namespace-work-root",
101+"network",
102+"preflight-tools",
103+"profile",
104+"provider",
105+"proxmox-api-url",
106+"proxmox-bridge",
107+"proxmox-node",
108+"proxmox-pool",
109+"proxmox-storage",
110+"proxmox-template-id",
111+"proxmox-user",
112+"proxmox-work-root",
113+"script",
114+"semaphore-host",
115+"semaphore-idle-timeout",
116+"semaphore-machine",
117+"semaphore-os-image",
118+"semaphore-project",
119+"sprites-api-url",
120+"sprites-work-root",
121+"static-host",
122+"static-port",
123+"static-user",
124+"static-work-root",
125+"tailscale-auth-key-env",
126+"tailscale-exit-node",
127+"tailscale-hostname-template",
128+"tailscale-tags",
129+"target",
130+"tensorlake-api-url",
131+"tensorlake-cli",
132+"tensorlake-cpus",
133+"tensorlake-disk-mb",
134+"tensorlake-image",
135+"tensorlake-memory-mb",
136+"tensorlake-namespace",
137+"tensorlake-organization-id",
138+"tensorlake-project-id",
139+"tensorlake-snapshot",
140+"tensorlake-timeout-secs",
141+"tensorlake-workdir",
142+"ttl",
143+"type",
144+"windows-mode",
145+]);
146+147+function runOptionName(arg) {
148+return arg.replace(/^-+/u, "").split("=", 1)[0];
149+}
150+151+function runCommandBounds(commandArgs) {
152+if (commandArgs[0] !== "run") {
153+return { start: -1, optionEnd: commandArgs.length };
154+}
155+for (let index = 1; index < commandArgs.length; index += 1) {
156+const arg = commandArgs[index];
157+if (arg === "--") {
158+return { start: index + 1, optionEnd: index };
159+}
160+if (!arg.startsWith("-")) {
161+return { start: index, optionEnd: index };
162+}
163+if (!arg.includes("=") && runValueOptions.has(runOptionName(arg))) {
164+index += 1;
165+}
166+}
167+return { start: -1, optionEnd: commandArgs.length };
168+}
169+170+function crabboxOptionArgs(commandArgs) {
171+const bounds = runCommandBounds(commandArgs);
172+if (commandArgs[0] === "run") {
173+return commandArgs.slice(0, bounds.optionEnd);
174+}
175+const delimiter = commandArgs.indexOf("--");
176+return delimiter >= 0 ? commandArgs.slice(0, delimiter) : commandArgs;
177+}
178+179+function commandProvider(commandArgs) {
180+commandArgs = crabboxOptionArgs(commandArgs);
181+for (let index = 0; index < commandArgs.length; index += 1) {
182+const arg = commandArgs[index];
183+if (arg === "--provider" || arg === "-provider") {
184+return commandArgs[index + 1] ?? "";
185+}
186+if (arg.startsWith("--provider=") || arg.startsWith("-provider=")) {
187+return arg.slice(arg.indexOf("=") + 1);
188+}
189+}
190+return "";
191+}
192+42193function selectedProvider(commandArgs) {
194+return commandProvider(commandArgs) || configuredProvider();
195+}
196+197+function optionValue(commandArgs, name) {
198+commandArgs = crabboxOptionArgs(commandArgs);
43199for (let index = 0; index < commandArgs.length; index += 1) {
44200const arg = commandArgs[index];
45-if (arg === "--provider") {
201+if (arg === name || arg === name.replace(/^--/u, "-")) {
46202return commandArgs[index + 1] ?? "";
47203}
48-if (arg.startsWith("--provider=")) {
49-return arg.slice("--provider=".length);
204+if (arg.startsWith(`${name}=`) || arg.startsWith(`${name.replace(/^--/u, "-")}=`)) {
205+return arg.slice(arg.indexOf("=") + 1);
206+}
207+}
208+return "";
209+}
210+211+function runCommandArgs(commandArgs) {
212+const { start } = runCommandBounds(commandArgs);
213+return start >= 0 ? commandArgs.slice(start) : [];
214+}
215+216+function commandRuntimeEntrypoint(commandArgs) {
217+const words = commandArgs.length === 1 ? commandArgs[0].split(/\s+/u) : commandArgs;
218+while (words[0] === "env") {
219+words.shift();
220+while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) {
221+words.shift();
50222}
51223}
52-return configuredProvider();
224+while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) {
225+words.shift();
226+}
227+const first = (words[0] ?? "")
228+.replace(/^['"]|['";|&()]+$/g, "")
229+.split("/")
230+.pop();
231+return ["pnpm", "npm", "npx", "corepack", "node", "yarn", "bun"].includes(first) ? first : "";
53232}
5423355234const version = checkedOutput(binary, ["--version"]);
@@ -72,12 +251,11 @@ const knownProviders = [
72251"cloudflare",
73252];
74253const providers = knownProviders.filter((provider) =>
75-new RegExp(`\\b${provider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(
76-help.text,
77-),
254+new RegExp(`\\b${provider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(help.text),
78255);
79256const displayBinary = binary === "crabbox" ? "crabbox" : relative(repoRoot, binary);
80257const provider = selectedProvider(args);
258+const commandProviderValue = commandProvider(args);
8125982260console.error(
83261`[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
96274}
9727598276if (provider === "blacksmith-testbox") {
277+const envProvider = process.env.CRABBOX_PROVIDER?.trim();
278+const source = commandProviderValue
279+ ? "explicit"
280+ : envProvider
281+ ? "from CRABBOX_PROVIDER"
282+ : "from config";
283+const fallback = commandProviderValue
284+ ? "rerun without --provider to use .crabbox.yaml"
285+ : envProvider
286+ ? "unset CRABBOX_PROVIDER to use .crabbox.yaml"
287+ : "pass another --provider to override it";
288+console.error(
289+`[crabbox] provider=blacksmith-testbox ${source}; if Testbox is queued or down, ${fallback}`,
290+);
291+}
292+293+const runtimeEntrypoint = commandRuntimeEntrypoint(runCommandArgs(args));
294+if (args[0] === "run" && provider === "aws" && runtimeEntrypoint) {
295+const id = optionValue(args, "--id");
296+const hydrate = id
297+ ? `pnpm crabbox:hydrate -- --id ${id}`
298+ : "pnpm crabbox:warmup, then pnpm crabbox:hydrate -- --id <id>";
99299console.error(
100-"[crabbox] provider=blacksmith-testbox explicit; if Testbox is queued or down, rerun without --provider to use .crabbox.yaml",
300+`[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`,
101301);
102302}
103303此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。