Plugins: generate bundled auth env metadata

This commit is contained in:
Vincent Koc
2026-03-18 10:53:48 -07:00
parent 8d73bc77fa
commit 7d08070dd7
7 changed files with 269 additions and 343 deletions

View File

@@ -0,0 +1,38 @@
// Auto-generated by scripts/generate-bundled-provider-auth-env-vars.mjs. Do not edit directly.
export const BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES = {
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
byteplus: ["BYTEPLUS_API_KEY"],
chutes: ["CHUTES_API_KEY", "CHUTES_OAUTH_TOKEN"],
"cloudflare-ai-gateway": ["CLOUDFLARE_AI_GATEWAY_API_KEY"],
fal: ["FAL_KEY"],
"github-copilot": ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"],
google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
huggingface: ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"],
kilocode: ["KILOCODE_API_KEY"],
kimi: ["KIMI_API_KEY", "KIMICODE_API_KEY"],
"kimi-coding": ["KIMI_API_KEY", "KIMICODE_API_KEY"],
minimax: ["MINIMAX_API_KEY"],
"minimax-portal": ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"],
mistral: ["MISTRAL_API_KEY"],
modelstudio: ["MODELSTUDIO_API_KEY"],
moonshot: ["MOONSHOT_API_KEY"],
nvidia: ["NVIDIA_API_KEY"],
ollama: ["OLLAMA_API_KEY"],
openai: ["OPENAI_API_KEY"],
opencode: ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"],
"opencode-go": ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"],
openrouter: ["OPENROUTER_API_KEY"],
qianfan: ["QIANFAN_API_KEY"],
"qwen-portal": ["QWEN_OAUTH_TOKEN", "QWEN_PORTAL_API_KEY"],
sglang: ["SGLANG_API_KEY"],
synthetic: ["SYNTHETIC_API_KEY"],
together: ["TOGETHER_API_KEY"],
venice: ["VENICE_API_KEY"],
"vercel-ai-gateway": ["AI_GATEWAY_API_KEY"],
vllm: ["VLLM_API_KEY"],
volcengine: ["VOLCANO_ENGINE_API_KEY"],
xai: ["XAI_API_KEY"],
xiaomi: ["XIAOMI_API_KEY"],
zai: ["ZAI_API_KEY", "Z_AI_API_KEY"],
} as const satisfies Record<string, readonly string[]>;

View File

@@ -1,7 +1,35 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach } from "vitest";
import {
collectBundledProviderAuthEnvVars,
writeBundledProviderAuthEnvVarModule,
} from "../../scripts/generate-bundled-provider-auth-env-vars.mjs";
import { BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES } from "./bundled-provider-auth-env-vars.js";
const repoRoot = path.resolve(import.meta.dirname, "../..");
const tempDirs: string[] = [];
function writeJson(filePath: string, value: unknown): void {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
afterEach(() => {
for (const dir of tempDirs.splice(0, tempDirs.length)) {
fs.rmSync(dir, { recursive: true, force: true });
}
});
describe("bundled provider auth env vars", () => {
it("matches the generated manifest snapshot", () => {
expect(BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES).toEqual(
collectBundledProviderAuthEnvVars({ repoRoot }),
);
});
it("reads bundled provider auth env vars from plugin manifests", () => {
expect(BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES["github-copilot"]).toEqual([
"COPILOT_GITHUB_TOKEN",
@@ -17,6 +45,47 @@ describe("bundled provider auth env vars", () => {
"MINIMAX_API_KEY",
]);
expect(BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES.openai).toEqual(["OPENAI_API_KEY"]);
expect(BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES["openai-codex"]).toBeUndefined();
expect(BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES.fal).toEqual(["FAL_KEY"]);
expect("openai-codex" in BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES).toBe(false);
});
it("supports check mode for stale generated artifacts", () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-provider-auth-env-vars-"));
tempDirs.push(tempRoot);
writeJson(path.join(tempRoot, "extensions", "alpha", "openclaw.plugin.json"), {
id: "alpha",
providerAuthEnvVars: {
alpha: ["ALPHA_TOKEN"],
},
});
const initial = writeBundledProviderAuthEnvVarModule({
repoRoot: tempRoot,
outputPath: "src/plugins/bundled-provider-auth-env-vars.generated.ts",
});
expect(initial.wrote).toBe(true);
const current = writeBundledProviderAuthEnvVarModule({
repoRoot: tempRoot,
outputPath: "src/plugins/bundled-provider-auth-env-vars.generated.ts",
check: true,
});
expect(current.changed).toBe(false);
expect(current.wrote).toBe(false);
fs.writeFileSync(
path.join(tempRoot, "src/plugins/bundled-provider-auth-env-vars.generated.ts"),
"// stale\n",
"utf8",
);
const stale = writeBundledProviderAuthEnvVarModule({
repoRoot: tempRoot,
outputPath: "src/plugins/bundled-provider-auth-env-vars.generated.ts",
check: true,
});
expect(stale.changed).toBe(true);
expect(stale.wrote).toBe(false);
});
});

View File

@@ -1,93 +1,3 @@
import ANTHROPIC_MANIFEST from "../../extensions/anthropic/openclaw.plugin.json" with { type: "json" };
import BYTEPLUS_MANIFEST from "../../extensions/byteplus/openclaw.plugin.json" with { type: "json" };
import CLOUDFLARE_AI_GATEWAY_MANIFEST from "../../extensions/cloudflare-ai-gateway/openclaw.plugin.json" with { type: "json" };
import COPILOT_PROXY_MANIFEST from "../../extensions/copilot-proxy/openclaw.plugin.json" with { type: "json" };
import GITHUB_COPILOT_MANIFEST from "../../extensions/github-copilot/openclaw.plugin.json" with { type: "json" };
import GOOGLE_MANIFEST from "../../extensions/google/openclaw.plugin.json" with { type: "json" };
import HUGGINGFACE_MANIFEST from "../../extensions/huggingface/openclaw.plugin.json" with { type: "json" };
import KILOCODE_MANIFEST from "../../extensions/kilocode/openclaw.plugin.json" with { type: "json" };
import KIMI_CODING_MANIFEST from "../../extensions/kimi-coding/openclaw.plugin.json" with { type: "json" };
import MINIMAX_MANIFEST from "../../extensions/minimax/openclaw.plugin.json" with { type: "json" };
import MISTRAL_MANIFEST from "../../extensions/mistral/openclaw.plugin.json" with { type: "json" };
import MODELSTUDIO_MANIFEST from "../../extensions/modelstudio/openclaw.plugin.json" with { type: "json" };
import MOONSHOT_MANIFEST from "../../extensions/moonshot/openclaw.plugin.json" with { type: "json" };
import NVIDIA_MANIFEST from "../../extensions/nvidia/openclaw.plugin.json" with { type: "json" };
import OLLAMA_MANIFEST from "../../extensions/ollama/openclaw.plugin.json" with { type: "json" };
import OPENAI_MANIFEST from "../../extensions/openai/openclaw.plugin.json" with { type: "json" };
import OPENCODE_GO_MANIFEST from "../../extensions/opencode-go/openclaw.plugin.json" with { type: "json" };
import OPENCODE_MANIFEST from "../../extensions/opencode/openclaw.plugin.json" with { type: "json" };
import OPENROUTER_MANIFEST from "../../extensions/openrouter/openclaw.plugin.json" with { type: "json" };
import QIANFAN_MANIFEST from "../../extensions/qianfan/openclaw.plugin.json" with { type: "json" };
import QWEN_PORTAL_AUTH_MANIFEST from "../../extensions/qwen-portal-auth/openclaw.plugin.json" with { type: "json" };
import SGLANG_MANIFEST from "../../extensions/sglang/openclaw.plugin.json" with { type: "json" };
import SYNTHETIC_MANIFEST from "../../extensions/synthetic/openclaw.plugin.json" with { type: "json" };
import TOGETHER_MANIFEST from "../../extensions/together/openclaw.plugin.json" with { type: "json" };
import VENICE_MANIFEST from "../../extensions/venice/openclaw.plugin.json" with { type: "json" };
import VERCEL_AI_GATEWAY_MANIFEST from "../../extensions/vercel-ai-gateway/openclaw.plugin.json" with { type: "json" };
import VLLM_MANIFEST from "../../extensions/vllm/openclaw.plugin.json" with { type: "json" };
import VOLCENGINE_MANIFEST from "../../extensions/volcengine/openclaw.plugin.json" with { type: "json" };
import XAI_MANIFEST from "../../extensions/xai/openclaw.plugin.json" with { type: "json" };
import XIAOMI_MANIFEST from "../../extensions/xiaomi/openclaw.plugin.json" with { type: "json" };
import ZAI_MANIFEST from "../../extensions/zai/openclaw.plugin.json" with { type: "json" };
type ProviderAuthEnvVarManifest = {
id?: string;
providerAuthEnvVars?: Record<string, string[]>;
};
function collectBundledProviderAuthEnvVars(
manifests: readonly ProviderAuthEnvVarManifest[],
): Record<string, readonly string[]> {
const entries: Record<string, readonly string[]> = {};
for (const manifest of manifests) {
const providerAuthEnvVars = manifest.providerAuthEnvVars;
if (!providerAuthEnvVars) {
continue;
}
for (const [providerId, envVars] of Object.entries(providerAuthEnvVars)) {
const normalizedProviderId = providerId.trim();
const normalizedEnvVars = envVars.map((value) => value.trim()).filter(Boolean);
if (!normalizedProviderId || normalizedEnvVars.length === 0) {
continue;
}
entries[normalizedProviderId] = normalizedEnvVars;
}
}
return entries;
}
// Read bundled provider auth env metadata from manifests so env-based auth
// lookup stays cheap and does not need to boot plugin runtime code.
export const BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES = collectBundledProviderAuthEnvVars([
ANTHROPIC_MANIFEST,
BYTEPLUS_MANIFEST,
CLOUDFLARE_AI_GATEWAY_MANIFEST,
COPILOT_PROXY_MANIFEST,
GITHUB_COPILOT_MANIFEST,
GOOGLE_MANIFEST,
HUGGINGFACE_MANIFEST,
KILOCODE_MANIFEST,
KIMI_CODING_MANIFEST,
MINIMAX_MANIFEST,
MISTRAL_MANIFEST,
MODELSTUDIO_MANIFEST,
MOONSHOT_MANIFEST,
NVIDIA_MANIFEST,
OLLAMA_MANIFEST,
OPENAI_MANIFEST,
OPENCODE_GO_MANIFEST,
OPENCODE_MANIFEST,
OPENROUTER_MANIFEST,
QIANFAN_MANIFEST,
QWEN_PORTAL_AUTH_MANIFEST,
SGLANG_MANIFEST,
SYNTHETIC_MANIFEST,
TOGETHER_MANIFEST,
VENICE_MANIFEST,
VERCEL_AI_GATEWAY_MANIFEST,
VLLM_MANIFEST,
VOLCENGINE_MANIFEST,
XAI_MANIFEST,
XIAOMI_MANIFEST,
ZAI_MANIFEST,
]);
// Generated from extension manifests so core secrets/auth code does not need
// static imports into extension source trees.
export { BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES } from "./bundled-provider-auth-env-vars.generated.js";