feat(presets): add OpenCode Zen for Claude Code and Codex

OpenCode Zen is the pay-as-you-go side of the same workspace key that
OpenCode Go uses, so both now sit in one "OpenCode" family row (Go as the
coding subscription, Zen as pay-as-you-go).

Both presets connect directly without local routing: Claude Code talks to
Zen's native /v1/messages with ANTHROPIC_API_KEY (the endpoint only reads
x-api-key), and Codex talks to /v1/responses. Codex defaults to gpt-6-sol
because Zen currently returns 403 "Model access is disabled" for
gpt-5.6-sol. Free Zen models are rejected outside OpenCode, so no preset
defaults to them. OpenCode and Pi already ship Zen as a built-in provider
and get no preset.

Closes #6016
This commit is contained in:
Jason committed 2026-10-06 21:03:50 +08:00
1 parent 3c3b94939a
commit 8569ddd75f
4 files changed
+80

No files matched your search

+27
View File
@@ -1074,6 +1074,8 @@ export const providerPresets: ProviderPreset[] = [
},
{
name: "OpenCode Go",
family: "opencode",
planKey: "coding",
websiteUrl: "https://opencode.ai/go",
apiKeyUrl: "https://opencode.ai/go?ref=2YTRG2NGTX",
partnerPromotionKey: "opencode_go",
@@ -1097,6 +1099,31 @@ export const providerPresets: ProviderPreset[] = [
icon: "opencode",
iconColor: "#211E1E",
},
{
// Zen 按量网关:Claude 模型原生走 /v1/messages,直连不需要路由。
// 和 Go 一样只认 x-api-key(Bearer 报 Missing API key)。
// 免费模型只能在 OpenCode 里用(外部调用 403 FreeTierError),默认不用。
name: "OpenCode Zen",
family: "opencode",
planKey: "payg",
websiteUrl: "https://opencode.ai/zen",
apiKeyUrl: "https://opencode.ai/auth",
apiKeyField: "ANTHROPIC_API_KEY",
settingsConfig: {
env: {
ANTHROPIC_BASE_URL: "https://opencode.ai/zen",
ANTHROPIC_API_KEY: "",
ANTHROPIC_MODEL: "claude-sonnet-5-5",
ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-haiku-4-5",
ANTHROPIC_DEFAULT_SONNET_MODEL: "claude-sonnet-5-5",
ANTHROPIC_DEFAULT_OPUS_MODEL: "claude-opus-5-5",
},
},
category: "aggregator",
endpointCandidates: ["https://opencode.ai/zen"],
icon: "opencode",
iconColor: "#211E1E",
},
{
// 腾讯云 Token Plan 个人版(1823/130060,2026-08-21 版):通用 + Hy 两
// 系列共用同一端点与 API Key;Auto 智能路由的调用 ID 是 tc-code-latest。
+23
View File
@@ -3260,6 +3260,8 @@ requires_openai_auth = true`,
},
{
name: "OpenCode Go",
family: "opencode",
planKey: "coding",
websiteUrl: "https://opencode.ai/go",
apiKeyUrl: "https://opencode.ai/go?ref=2YTRG2NGTX",
partnerPromotionKey: "opencode_go",
@@ -3331,6 +3333,27 @@ requires_openai_auth = true`,
icon: "opencode",
iconColor: "#211E1E",
},
{
// Zen 按量网关:GPT 模型原生走 /v1/responses,直连不需要路由;
// 免费模型只能在 OpenCode 里用(外部调用 403 FreeTierError),默认不用。
// 默认不用全仓通用的 gpt-5.6-sol:Zen 上游对它返回 403「Model access is
// disabled」(2026-10-06 真 Key 实测),gpt-6-sol 经 Codex 跑通工具调用。
name: "OpenCode Zen",
family: "opencode",
planKey: "payg",
websiteUrl: "https://opencode.ai/zen",
apiKeyUrl: "https://opencode.ai/auth",
auth: generateThirdPartyAuth(""),
config: generateThirdPartyConfig(
"opencode_zen",
"https://opencode.ai/zen/v1",
"gpt-6-sol",
),
endpointCandidates: ["https://opencode.ai/zen/v1"],
category: "aggregator",
icon: "opencode",
iconColor: "#211E1E",
},
{
name: "AiHubMix",
websiteUrl: "https://aihubmix.com",
+5
View File
@@ -26,6 +26,11 @@ export const PRESET_FAMILIES = {
planOrder: ["payg", "coding"],
},
minimax: { name: "MiniMax" },
// Go 是编程订阅、Zen 是按量付费,同一个工作区 Key
opencode: {
name: "OpenCode",
planOrder: ["coding", "payg"],
},
qianwen: { name: "千问AI平台" },
qwencloud: { name: "QwenCloud" },
siliconflow: { name: "SiliconFlow" },
@@ -87,6 +87,31 @@ describe("OpenCode Go Provider Preset", () => {
});
});
describe("OpenCode Zen Provider Preset", () => {
const openCodeZen = providerPresets.find((p) => p.name === "OpenCode Zen");
it("should connect Claude models to Zen /messages directly with x-api-key auth", () => {
expect(openCodeZen).toBeDefined();
expect(openCodeZen!.family).toBe("opencode");
const env = (openCodeZen!.settingsConfig as any).env;
expect(env.ANTHROPIC_BASE_URL).toBe("https://opencode.ai/zen");
// 和 Go 同一套网关:/messages 只认 x-api-key
expect(env).not.toHaveProperty("ANTHROPIC_AUTH_TOKEN");
expect(openCodeZen!.apiKeyField).toBe("ANTHROPIC_API_KEY");
expect(openCodeZen!.apiFormat).toBeUndefined();
// 免费模型外部调用会被 403,默认模型不能是 *-free / big-pickle
for (const key of [
"ANTHROPIC_MODEL",
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
"ANTHROPIC_DEFAULT_SONNET_MODEL",
"ANTHROPIC_DEFAULT_OPUS_MODEL",
]) {
expect(env[key]).toMatch(/^claude-/);
}
});
});
describe("AWS Bedrock Provider Presets", () => {
const bedrockAksk = providerPresets.find(
(p) => p.name === "AWS Bedrock (AKSK)",