


























11#!/usr/bin/env node
22/**
3- * Release preflight helper that verifies required provider API keys can reach
4- * their model-list endpoints without printing secret values.
3+ * Release preflight helper that verifies required provider API keys without
4+ * printing secret values. Anthropic must complete a prompt because model-list
5+ * access does not prove billing or inference entitlement.
56 */
67import process from "node:process";
7889const args = new Map();
910for (let index = 2; index < process.argv.length; index += 1) {
1011const arg = process.argv[index];
11-if (!arg.startsWith("--")) continue;
12+if (!arg.startsWith("--")) {
13+continue;
14+}
1215const [key, inlineValue] = arg.slice(2).split("=", 2);
1316const value = inlineValue ?? process.argv[index + 1];
14-if (inlineValue === undefined) index += 1;
17+if (inlineValue === undefined) {
18+index += 1;
19+}
1520args.set(key, value);
1621}
1722@@ -28,7 +33,9 @@ const timeoutMs = Number(args.get("timeout-ms") ?? 10_000);
2833function envFirst(names) {
2934for (const name of names) {
3035const value = process.env[name]?.trim();
31-if (value) return { name, value };
36+if (value) {
37+return { name, value };
38+}
3239}
3340return undefined;
3441}
@@ -42,15 +49,21 @@ async function checkProvider(id, config) {
4249const controller = new AbortController();
4350const timer = setTimeout(() => controller.abort(), timeoutMs);
4451try {
45-const headers = config.headers(secret);
52+const headers = config.headers(secret.value);
4653const response = await fetch(config.url, {
54+body: config.body,
4755 headers,
56+method: config.method,
4857signal: controller.signal,
4958});
59+const responseBody = config.validateResponse
60+ ? await response.json().catch(() => undefined)
61+ : undefined;
62+const ok = response.ok && (!config.validateResponse || config.validateResponse(responseBody));
5063return {
5164 id,
52-ok: response.ok,
53-status: response.ok ? "ok" : `http_${response.status}`,
65+ ok,
66+status: response.ok ? (ok ? "ok" : "invalid_response") : `http_${response.status}`,
5467env: secret.name,
5568};
5669} catch (error) {
@@ -69,32 +82,35 @@ const providers = {
6982openai: {
7083env: ["OPENAI_API_KEY"],
7184url: "https://api.openai.com/v1/models",
72-headers: ({ value }) => ({ authorization: `Bearer ${value}` }),
85+headers: (token) => ({ authorization: `Bearer ${token}` }),
7386},
7487anthropic: {
75-env: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_API_TOKEN"],
76-url: "https://api.anthropic.com/v1/models",
77-headers: ({ name, value }) =>
78-name === "ANTHROPIC_OAUTH_TOKEN"
79- ? {
80-"anthropic-beta": "oauth-2025-04-20",
81-"anthropic-version": "2023-06-01",
82-authorization: `Bearer ${value}`,
83-}
84- : {
85-"anthropic-version": "2023-06-01",
86-"x-api-key": value,
87-},
88+env: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_TOKEN"],
89+url: "https://api.anthropic.com/v1/messages",
90+method: "POST",
91+body: JSON.stringify({
92+max_tokens: 8,
93+messages: [{ role: "user", content: "Reply with OK." }],
94+model: "claude-haiku-4-5",
95+}),
96+headers: (token) => ({
97+"anthropic-version": "2023-06-01",
98+"content-type": "application/json",
99+"x-api-key": token,
100+}),
101+validateResponse: (body) =>
102+Array.isArray(body?.content) &&
103+body.content.some((part) => typeof part?.text === "string" && part.text.trim()),
88104},
89105fireworks: {
90106env: ["FIREWORKS_API_KEY"],
91107url: "https://api.fireworks.ai/inference/v1/models",
92-headers: ({ value }) => ({ authorization: `Bearer ${value}` }),
108+headers: (token) => ({ authorization: `Bearer ${token}` }),
93109},
94110openrouter: {
95111env: ["OPENROUTER_API_KEY"],
96112url: "https://openrouter.ai/api/v1/models",
97-headers: ({ value }) => ({ authorization: `Bearer ${value}` }),
113+headers: (token) => ({ authorization: `Bearer ${token}` }),
98114},
99115};
100116@@ -115,7 +131,9 @@ let failed = false;
115131for (const result of results) {
116132const requiredLabel = required.has(result.id) ? "required" : "optional";
117133console.log(`${result.id}: ${result.status} env=${result.env} ${requiredLabel}`);
118-if (required.has(result.id) && !result.ok) failed = true;
134+if (required.has(result.id) && !result.ok) {
135+failed = true;
136+}
119137}
120138121139if (failed) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。