refactor(presets): align CCSub to end of partner block across apps

Move the CCSub preset to sit right after DouBaoSeed, at the end of the
partner block and before the first non-partner provider, so its position
is consistent across all six apps:

- Codex / OpenCode: moved up from the 2nd slot (between Shengsuanyun and
  the next partner) to the block tail
- OpenClaw / Hermes: moved up from the aggregator section to the block tail
- Claude / Claude Desktop: already at the block tail

Also add the missing CHANGELOG entry for the CCSub preset, and drop the
provider preset order test that enforced a now-unneeded ordering invariant.
This commit is contained in:
Jason
2026-06-08 23:07:50 +08:00
parent fa17194d84
commit 5beb63e67d
8 changed files with 197 additions and 281 deletions
+1
View File
@@ -19,6 +19,7 @@ Development since v3.16.1 focuses on broadening data portability and usage obser
- **Unsupported Image Fallback Rectifier**: Added a proxy rectifier that replaces Anthropic image blocks with an `[Unsupported Image]` marker when the routed model is text-only (declared, or detected via a built-in model-name heuristic) or when the upstream rejects image input, so conversations are not interrupted. A new Settings toggle controls the fallback, with a separate toggle for the heuristic detection. - **Unsupported Image Fallback Rectifier**: Added a proxy rectifier that replaces Anthropic image blocks with an `[Unsupported Image]` marker when the routed model is text-only (declared, or detected via a built-in model-name heuristic) or when the upstream rejects image input, so conversations are not interrupted. A new Settings toggle controls the fallback, with a separate toggle for the heuristic detection.
- **ZenMux Token Plan Provider**: Added ZenMux as a Token Plan coding-plan provider that accepts a manually entered API key and base URL in the usage-script modal and renders its quota with USD-denominated used / limit values (#2709). - **ZenMux Token Plan Provider**: Added ZenMux as a Token Plan coding-plan provider that accepts a manually entered API key and base URL in the usage-script modal and renders its quota with USD-denominated used / limit values (#2709).
- **CherryIN Preset**: Added the CherryIN aggregator gateway as a quick-config preset across all seven supported apps — Anthropic-format endpoint for Claude Code / Claude Desktop / OpenClaw / Hermes, `@ai-sdk/anthropic` for OpenCode, the OpenAI-compatible endpoint for Codex, and the Gemini-compatible endpoint for Gemini CLI — with the official brand icon, placed next to AiHubMix (#3643). - **CherryIN Preset**: Added the CherryIN aggregator gateway as a quick-config preset across all seven supported apps — Anthropic-format endpoint for Claude Code / Claude Desktop / OpenClaw / Hermes, `@ai-sdk/anthropic` for OpenCode, the OpenAI-compatible endpoint for Codex, and the Gemini-compatible endpoint for Gemini CLI — with the official brand icon, placed next to AiHubMix (#3643).
- **CCSub Preset**: Added CCSub, a multi-model aggregator partner, as a quick-config preset across six apps — Claude Code, Claude Desktop, Codex, OpenCode, OpenClaw, and Hermes — with the official brand icon and the partner referral link prefilled as the API-key signup URL (`gpt-5.5` for the OpenAI-compatible Codex and OpenCode endpoints).
- **Codex CLI Models Endpoint**: The local proxy now answers `GET /v1/models`, which Codex CLI probes at startup, returning the cc-switch-managed Codex model catalog. A stale-catalog guard parses the live `config.toml` and only serves the catalog when `model_catalog_json` still references the cc-switch-owned file, so a leftover catalog from a previous provider is not advertised (#3818). - **Codex CLI Models Endpoint**: The local proxy now answers `GET /v1/models`, which Codex CLI probes at startup, returning the cc-switch-managed Codex model catalog. A stale-catalog guard parses the live `config.toml` and only serves the catalog when `model_catalog_json` still references the cc-switch-owned file, so a leftover catalog from a previous provider is not advertised (#3818).
- **Codex Chat File and Audio Attachments**: The Codex Responses-to-Chat converter now maps `input_file` parts (carrying `file_id` or inline `file_data`) and `input_audio` parts into their Chat Completions equivalents, and emits top-level `input_*` items that were previously dropped, so file and audio attachments reach Chat-only Codex upstreams. - **Codex Chat File and Audio Attachments**: The Codex Responses-to-Chat converter now maps `input_file` parts (carrying `file_id` or inline `file_data`) and `input_audio` parts into their Chat Completions equivalents, and emits top-level `input_*` items that were previously dropped, so file and audio attachments reach Chat-only Codex upstreams.
+13 -13
View File
@@ -167,19 +167,6 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [
partnerPromotionKey: "shengsuanyun", partnerPromotionKey: "shengsuanyun",
icon: "shengsuanyun", icon: "shengsuanyun",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
category: "aggregator",
baseUrl: "https://www.ccsub.net",
mode: "direct",
apiFormat: "anthropic",
modelRoutes: passthroughRoutes(true),
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "PatewayAI", name: "PatewayAI",
websiteUrl: "https://pateway.ai", websiteUrl: "https://pateway.ai",
@@ -253,6 +240,19 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [
icon: "doubao", icon: "doubao",
iconColor: "#3370FF", iconColor: "#3370FF",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
category: "aggregator",
baseUrl: "https://www.ccsub.net",
mode: "direct",
apiFormat: "anthropic",
modelRoutes: passthroughRoutes(true),
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "Gemini Native", name: "Gemini Native",
websiteUrl: "https://ai.google.dev/gemini-api", websiteUrl: "https://ai.google.dev/gemini-api",
+15 -15
View File
@@ -109,21 +109,6 @@ export const providerPresets: ProviderPreset[] = [
partnerPromotionKey: "shengsuanyun", partnerPromotionKey: "shengsuanyun",
icon: "shengsuanyun", icon: "shengsuanyun",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
env: {
ANTHROPIC_BASE_URL: "https://www.ccsub.net",
ANTHROPIC_AUTH_TOKEN: "",
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "PatewayAI", name: "PatewayAI",
websiteUrl: "https://pateway.ai", websiteUrl: "https://pateway.ai",
@@ -208,6 +193,21 @@ export const providerPresets: ProviderPreset[] = [
icon: "doubao", icon: "doubao",
iconColor: "#3370FF", iconColor: "#3370FF",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
env: {
ANTHROPIC_BASE_URL: "https://www.ccsub.net",
ANTHROPIC_AUTH_TOKEN: "",
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "Gemini Native", name: "Gemini Native",
websiteUrl: "https://ai.google.dev/gemini-api", websiteUrl: "https://ai.google.dev/gemini-api",
+16 -16
View File
@@ -116,22 +116,6 @@ export const codexProviderPresets: CodexProviderPreset[] = [
partnerPromotionKey: "shengsuanyun", partnerPromotionKey: "shengsuanyun",
icon: "shengsuanyun", icon: "shengsuanyun",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
category: "aggregator",
auth: generateThirdPartyAuth(""),
config: generateThirdPartyConfig(
"ccsub",
"https://www.ccsub.net/v1",
"gpt-5.5",
),
endpointCandidates: ["https://www.ccsub.net/v1"],
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "PatewayAI", name: "PatewayAI",
websiteUrl: "https://pateway.ai", websiteUrl: "https://pateway.ai",
@@ -231,6 +215,22 @@ export const codexProviderPresets: CodexProviderPreset[] = [
icon: "doubao", icon: "doubao",
iconColor: "#3370FF", iconColor: "#3370FF",
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
category: "aggregator",
auth: generateThirdPartyAuth(""),
config: generateThirdPartyConfig(
"ccsub",
"https://www.ccsub.net/v1",
"gpt-5.5",
),
endpointCandidates: ["https://www.ccsub.net/v1"],
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
},
{ {
name: "Azure OpenAI", name: "Azure OpenAI",
websiteUrl: websiteUrl:
+50 -50
View File
@@ -238,6 +238,56 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
name: "ccsub",
base_url: "https://www.ccsub.net/v1",
api_key: "",
api_mode: "chat_completions",
models: [
{
id: "claude-opus-4-8",
name: "Claude Opus 4.8",
context_length: 1000000,
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6",
context_length: 1000000,
},
{
id: "gpt-5.5",
name: "GPT-5.5",
context_length: 400000,
},
{
id: "o3",
name: "o3",
context_length: 200000,
},
{
id: "gemini-3.1-pro",
name: "Gemini 3.1 Pro",
context_length: 1000000,
},
{
id: "deepseek-v4-flash",
name: "DeepSeek V4 Flash",
context_length: 1000000,
},
],
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
suggestedDefaults: {
model: { default: "claude-opus-4-8", provider: "ccsub" },
},
},
{ {
name: "OpenRouter", name: "OpenRouter",
nameKey: "providerForm.presets.openrouter", nameKey: "providerForm.presets.openrouter",
@@ -351,56 +401,6 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
name: "ccsub",
base_url: "https://www.ccsub.net/v1",
api_key: "",
api_mode: "chat_completions",
models: [
{
id: "claude-opus-4-8",
name: "Claude Opus 4.8",
context_length: 1000000,
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6",
context_length: 1000000,
},
{
id: "gpt-5.5",
name: "GPT-5.5",
context_length: 400000,
},
{
id: "o3",
name: "o3",
context_length: 200000,
},
{
id: "gemini-3.1-pro",
name: "Gemini 3.1 Pro",
context_length: 1000000,
},
{
id: "deepseek-v4-flash",
name: "DeepSeek V4 Flash",
context_length: 1000000,
},
],
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
suggestedDefaults: {
model: { default: "claude-opus-4-8", provider: "ccsub" },
},
},
{ {
name: "Nous Research", name: "Nous Research",
websiteUrl: "https://nousresearch.com", websiteUrl: "https://nousresearch.com",
+69 -69
View File
@@ -256,6 +256,75 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
baseUrl: "https://www.ccsub.net/v1",
apiKey: "",
api: "openai-completions",
models: [
{
id: "claude-opus-4-8",
name: "Claude Opus 4.8",
contextWindow: 1000000,
cost: { input: 5, output: 25 },
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6",
contextWindow: 1000000,
cost: { input: 3, output: 15 },
},
{
id: "gpt-5.5",
name: "GPT-5.5",
contextWindow: 400000,
cost: { input: 5, output: 15 },
},
{
id: "o3",
name: "o3",
contextWindow: 200000,
cost: { input: 10, output: 40 },
},
{
id: "gemini-3.1-pro",
name: "Gemini 3.1 Pro",
contextWindow: 1000000,
cost: { input: 1.25, output: 10 },
},
{
id: "deepseek-v4-flash",
name: "DeepSeek V4 Flash",
contextWindow: 1000000,
cost: { input: 0.14, output: 0.28 },
},
],
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "",
editorValue: "",
},
},
suggestedDefaults: {
model: {
primary: "ccsub/claude-opus-4-8",
fallbacks: ["ccsub/claude-sonnet-4-6"],
},
modelCatalog: {
"ccsub/claude-opus-4-8": { alias: "Opus" },
"ccsub/claude-sonnet-4-6": { alias: "Sonnet" },
},
},
},
// ========== Chinese Officials ========== // ========== Chinese Officials ==========
{ {
name: "DeepSeek", name: "DeepSeek",
@@ -900,75 +969,6 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
baseUrl: "https://www.ccsub.net/v1",
apiKey: "",
api: "openai-completions",
models: [
{
id: "claude-opus-4-8",
name: "Claude Opus 4.8",
contextWindow: 1000000,
cost: { input: 5, output: 25 },
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6",
contextWindow: 1000000,
cost: { input: 3, output: 15 },
},
{
id: "gpt-5.5",
name: "GPT-5.5",
contextWindow: 400000,
cost: { input: 5, output: 15 },
},
{
id: "o3",
name: "o3",
contextWindow: 200000,
cost: { input: 10, output: 40 },
},
{
id: "gemini-3.1-pro",
name: "Gemini 3.1 Pro",
contextWindow: 1000000,
cost: { input: 1.25, output: 10 },
},
{
id: "deepseek-v4-flash",
name: "DeepSeek V4 Flash",
contextWindow: 1000000,
cost: { input: 0.14, output: 0.28 },
},
],
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "",
editorValue: "",
},
},
suggestedDefaults: {
model: {
primary: "ccsub/claude-opus-4-8",
fallbacks: ["ccsub/claude-sonnet-4-6"],
},
modelCatalog: {
"ccsub/claude-opus-4-8": { alias: "Opus" },
"ccsub/claude-sonnet-4-6": { alias: "Sonnet" },
},
},
},
{ {
name: "CherryIN", name: "CherryIN",
websiteUrl: "https://open.cherryin.ai", websiteUrl: "https://open.cherryin.ai",
+33 -33
View File
@@ -308,39 +308,6 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
npm: "@ai-sdk/openai-compatible",
name: "CCSub",
options: {
baseURL: "https://www.ccsub.net/v1",
apiKey: "",
setCacheKey: true,
},
models: {
"claude-opus-4-8": { name: "Claude Opus 4.8" },
"claude-sonnet-4-6": { name: "Claude Sonnet 4.6" },
"gpt-5.5": { name: "GPT-5.5" },
o3: { name: "o3" },
"gemini-3.1-pro": { name: "Gemini 3.1 Pro" },
"deepseek-v4-flash": { name: "DeepSeek V4 Flash" },
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "",
editorValue: "",
},
},
},
{ {
name: "火山Agentplan", name: "火山Agentplan",
websiteUrl: websiteUrl:
@@ -440,6 +407,39 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
}, },
}, },
}, },
{
name: "CCSub",
websiteUrl: "https://www.ccsub.net",
apiKeyUrl: "https://www.ccsub.net/register?ref=Y6Z8DXEA",
settingsConfig: {
npm: "@ai-sdk/openai-compatible",
name: "CCSub",
options: {
baseURL: "https://www.ccsub.net/v1",
apiKey: "",
setCacheKey: true,
},
models: {
"claude-opus-4-8": { name: "Claude Opus 4.8" },
"claude-sonnet-4-6": { name: "Claude Sonnet 4.6" },
"gpt-5.5": { name: "GPT-5.5" },
o3: { name: "o3" },
"gemini-3.1-pro": { name: "Gemini 3.1 Pro" },
"deepseek-v4-flash": { name: "DeepSeek V4 Flash" },
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "ccsub",
icon: "ccsub",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "",
editorValue: "",
},
},
},
{ {
name: "DeepSeek", name: "DeepSeek",
websiteUrl: "https://platform.deepseek.com", websiteUrl: "https://platform.deepseek.com",
-85
View File
@@ -1,85 +0,0 @@
import { describe, expect, it } from "vitest";
import { providerPresets } from "@/config/claudeProviderPresets";
import { claudeDesktopProviderPresets } from "@/config/claudeDesktopProviderPresets";
import { codexProviderPresets } from "@/config/codexProviderPresets";
import { opencodeProviderPresets } from "@/config/opencodeProviderPresets";
import { openclawProviderPresets } from "@/config/openclawProviderPresets";
import { hermesProviderPresets } from "@/config/hermesProviderPresets";
const namesOf = (presets: Array<{ name: string }>) =>
presets.map((preset) => preset.name);
const expectInOrder = (names: string[], expected: string[]) => {
const indexes = expected.map((name) => names.indexOf(name));
expect(indexes).not.toContain(-1);
expect(indexes).toEqual(expected.map((_, index) => indexes[0] + index));
};
describe("provider preset order", () => {
it("Claude 预设按合作伙伴优先顺序排列", () => {
expectInOrder(namesOf(providerPresets), [
"Shengsuanyun",
"PatewayAI",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
it("Claude Desktop 预设按合作伙伴优先顺序排列", () => {
expectInOrder(namesOf(claudeDesktopProviderPresets), [
"Shengsuanyun",
"PatewayAI",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
it("Claude Desktop 预设包含官方登录入口", () => {
expect(claudeDesktopProviderPresets[0]).toMatchObject({
name: "Claude Desktop Official",
category: "official",
baseUrl: "",
mode: "direct",
});
});
it("Codex 预设按合作伙伴优先顺序排列", () => {
expectInOrder(namesOf(codexProviderPresets), [
"Shengsuanyun",
"PatewayAI",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
it("OpenCode 预设把火山、BytePlus、DouBaoSeed 放在胜算云后面", () => {
expectInOrder(namesOf(opencodeProviderPresets), [
"Shengsuanyun",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
it("OpenClaw 预设把火山、BytePlus、DouBaoSeed 放在胜算云后面", () => {
expectInOrder(namesOf(openclawProviderPresets), [
"Shengsuanyun",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
it("Hermes 预设把火山、BytePlus、DouBaoSeed 放在胜算云后面", () => {
expectInOrder(namesOf(hermesProviderPresets), [
"Shengsuanyun",
"火山Agentplan",
"BytePlus",
"DouBaoSeed",
]);
});
});