Files
openclaw/scripts/crabbox-wrapper-providers.mts
T
Peter Steinberger 468d621f9a fix(update): complete high-volume stable upgrades without stalls (#124651)
* fix(testing): restore published upgrade validation

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

* perf(sessions): batch transcript archive workers

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

* fix(cli): exit after legacy agent turns

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

* fix(testing): reset upgrade fixture ports

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

* chore(lint): shrink assertion safety baseline

Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-16 08:14:56 -07:00

95 lines
2.9 KiB
TypeScript

const providerAliases = new Map([
["blacksmith", "blacksmith-testbox"],
["cf", "cloudflare"],
["container", "local-container"],
["docker", "local-container"],
["exe", "exe-dev"],
["exedev", "exe-dev"],
["google", "gcp"],
["google-cloud", "gcp"],
["local-docker", "local-container"],
["namespace", "namespace-devbox"],
["namespace-devboxes", "namespace-devbox"],
["rail", "railway"],
["railwayapp", "railway"],
["run-pod", "runpod"],
["runpodio", "runpod"],
["sem", "semaphore"],
["static", "ssh"],
["static-ssh", "ssh"],
["tensorlake-sbx", "tensorlake"],
["tl", "tensorlake"],
]);
// Crabbox providerHelpAll can omit Tensorlake even when the binary accepts it.
const providerHelpOmissions = new Set(["tensorlake"]);
export function canonicalProviderName(provider: string) {
return providerAliases.get(provider) ?? provider;
}
function addProviderNames(names: Set<string>, text: string) {
for (const name of text
.replace(/\s+\(defaults?\b.*$/u, "")
.split(/\s*(?:,|\||\bor\b)\s*/u)
.map((s) => s.trim())
.filter(Boolean)) {
if (/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
names.add(name);
}
}
}
function providerListContinuation(line: string, previousText: string) {
const match = line.match(
/^\s*((?:or\s+)?[a-z0-9][a-z0-9-]*(?:\s*(?:,|\||\bor\b)\s*(?:or\s+)?[a-z0-9][a-z0-9-]*)*\s*(?:,|\|)?)(?:\s+\(defaults?\b.*)?\s*$/u,
);
if (!match) {
return "";
}
if (/[,|]\s*$/u.test(previousText) || /[,|]|\bor\b|\(defaults?\b/u.test(line)) {
return match[1] ?? "";
}
return "";
}
export function parseProvidersFromHelp(text: string) {
const names = new Set<string>();
const lines = text.split(/\r?\n/u);
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index] ?? "";
const providerMatch = line.match(/provider:\s*([a-z0-9][a-z0-9, -]*)(?:\s*\(defaults?\b|$)/u);
if (providerMatch) {
let providerText = providerMatch[1] ?? "";
while (!/\(defaults?\b/u.test(lines[index] ?? "") && index + 1 < lines.length) {
const continuation = providerListContinuation(lines[index + 1] ?? "", providerText);
if (!continuation) {
break;
}
index += 1;
providerText = `${providerText} ${continuation}`;
}
addProviderNames(names, providerText);
continue;
}
const flagMatch = line.match(
/^\s+-{1,2}provider(?:[=\s]+)([a-z0-9][a-z0-9|, -]*)(?:\s{2,}|\s+\(|$)/u,
);
const flagProviders = flagMatch?.[1] ?? "";
if (/[,|]|\bor\b/u.test(flagProviders)) {
addProviderNames(names, flagProviders);
}
}
return [...names];
}
export function isProviderAdvertised(provider: string, advertisedProviders: readonly string[]) {
const canonicalProvider = canonicalProviderName(provider);
return (
advertisedProviders.includes(provider) ||
advertisedProviders.includes(canonicalProvider) ||
providerHelpOmissions.has(canonicalProvider)
);
}