claude-code-router/scripts/update-models-cdn.mjs
2026-10-09 18:11:41 +08:00

59 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const catalogFile = path.join(projectRoot, "packages/core/models.json");
const args = process.argv.slice(2);
async function main() {
if (args.includes("--help")) {
console.log("Usage: npm run models:update -- [--upload-only] [--dry-run]\nGenerates models.json and uploads it to Cloudflare R2.\nRequired: CLOUDFLARE_R2_BUCKET; authenticate Wrangler or set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.\n--upload-only: upload the existing catalog without regenerating.\n--dry-run: validate configuration/catalog and print the upload command without generating or uploading.");
return;
}
for (const arg of args) {
if (!["--upload-only", "--dry-run"].includes(arg)) throw new Error(`Unknown option: ${arg}`);
}
try { process.loadEnvFile(path.join(projectRoot, ".env")); } catch (error) {
if (error.code !== "ENOENT") throw error;
}
const bucket = process.env.CLOUDFLARE_R2_BUCKET?.trim();
const key = process.env.CCR_MODEL_CATALOG_R2_KEY?.trim() || "models.json";
if (!bucket || !/^[a-z0-9][a-z0-9-]*$/.test(bucket)) throw new Error("Set CLOUDFLARE_R2_BUCKET to your R2 bucket name.");
if (key.startsWith("/") || key.includes("\\") || key.split("/").includes("..")) throw new Error("CCR_MODEL_CATALOG_R2_KEY must be a relative R2 object key.");
if (!args.includes("--upload-only") && !args.includes("--dry-run")) {
run(process.execPath, ["scripts/generate-models-json.mjs", "--strict"]);
}
const catalog = JSON.parse(await readFile(catalogFile, "utf8"));
if (catalog.schemaVersion !== 2 || !Array.isArray(catalog.models) || !catalog.models.length ||
!catalog.models.every((model) => model && typeof model.id === "string")) {
throw new Error("Invalid models.json; run npm run models:build first.");
}
const command = ["--yes", "wrangler@4", "r2", "object", "put", `${bucket}/${key}`, "--remote",
"--file", catalogFile, "--content-type", "application/json", "--cache-control", "public, max-age=300, must-revalidate"];
if (args.includes("--dry-run")) {
console.log(command.map((arg) => JSON.stringify(arg)).join(" "));
console.log(`Validated ${catalog.models.length} models. No upload performed.`);
return;
}
if (process.platform === "win32") {
if (!process.env.npm_execpath) throw new Error("Run this command through npm run models:update on Windows.");
run(process.execPath, [path.join(path.dirname(process.env.npm_execpath), "npx-cli.js"), ...command]);
} else {
run("npx", command);
}
console.log(`Updated R2 object ${bucket}/${key} (${catalog.models.length} models).`);
}
function run(command, commandArgs) {
const result = spawnSync(command, commandArgs, { cwd: projectRoot, env: process.env, stdio: "inherit" });
if (result.error) throw result.error;
if (result.status !== 0) throw new Error(`${path.basename(command)} failed (${result.status ?? result.signal}).`);
}
main().catch((error) => {
console.error(`[models:update] ${error.message}`);
process.exitCode = 1;
});