diff --git a/src/components/providers/AddProviderDialog.tsx b/src/components/providers/AddProviderDialog.tsx index c611d0acf..3ee783e31 100644 --- a/src/components/providers/AddProviderDialog.tsx +++ b/src/components/providers/AddProviderDialog.tsx @@ -17,6 +17,7 @@ import { UniversalProviderPanel } from "@/components/universal"; import { providerPresets } from "@/config/claudeProviderPresets"; import { codexProviderPresets } from "@/config/codexProviderPresets"; import { geminiProviderPresets } from "@/config/geminiProviderPresets"; +import { claudeDesktopProviderPresets } from "@/config/claudeDesktopProviderPresets"; import { extractCodexBaseUrl } from "@/utils/providerConfigUtils"; import type { OpenClawSuggestedDefaults } from "@/config/openclawProviderPresets"; import type { UniversalProviderPreset } from "@/config/universalProviderPresets"; @@ -42,7 +43,10 @@ export function AddProviderDialog({ const { t } = useTranslation(); // OpenCode and OpenClaw don't support universal providers const showUniversalTab = - appId !== "opencode" && appId !== "openclaw" && appId !== "hermes"; + appId !== "opencode" && + appId !== "openclaw" && + appId !== "hermes" && + appId !== "claude-desktop"; const [activeTab, setActiveTab] = useState<"app-specific" | "universal">( "app-specific", ); @@ -171,6 +175,22 @@ export function AddProviderDialog({ preset.endpointCandidates.forEach(addUrl); } } + } else if (appId === "claude-desktop") { + const presets = claudeDesktopProviderPresets; + const presetIndex = parseInt( + values.presetId.replace("claude-desktop-", ""), + ); + if ( + !isNaN(presetIndex) && + presetIndex >= 0 && + presetIndex < presets.length + ) { + const preset = presets[presetIndex]; + if (Array.isArray(preset.endpointCandidates)) { + preset.endpointCandidates.forEach(addUrl); + } + addUrl(preset.baseUrl); + } } } @@ -179,6 +199,11 @@ export function AddProviderDialog({ if (env?.ANTHROPIC_BASE_URL) { addUrl(env.ANTHROPIC_BASE_URL); } + } else if (appId === "claude-desktop") { + const env = parsedConfig.env as Record | undefined; + if (env?.ANTHROPIC_BASE_URL) { + addUrl(env.ANTHROPIC_BASE_URL); + } } else if (appId === "codex") { const config = parsedConfig.config as string | undefined; if (config) { diff --git a/src/components/providers/forms/ClaudeDesktopProviderForm.tsx b/src/components/providers/forms/ClaudeDesktopProviderForm.tsx index dcae69ff3..45eef3793 100644 --- a/src/components/providers/forms/ClaudeDesktopProviderForm.tsx +++ b/src/components/providers/forms/ClaudeDesktopProviderForm.tsx @@ -39,6 +39,7 @@ import { Switch } from "@/components/ui/switch"; import { BasicFormFields } from "./BasicFormFields"; import { EndpointField } from "./shared/EndpointField"; import { ModelDropdown } from "./shared/ModelDropdown"; +import { ProviderPresetSelector } from "./ProviderPresetSelector"; import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider"; import type { ClaudeApiFormat, @@ -47,6 +48,10 @@ import type { ProviderMeta, } from "@/types"; import type { OpenClawSuggestedDefaults } from "@/config/openclawProviderPresets"; +import { + claudeDesktopProviderPresets, + type ClaudeDesktopProviderPreset, +} from "@/config/claudeDesktopProviderPresets"; import { fetchModelsForConfig, showFetchModelsError, @@ -61,11 +66,19 @@ export type ClaudeDesktopProviderFormValues = ProviderFormData & { presetId?: string; presetCategory?: ProviderCategory; isPartner?: boolean; + partnerPromotionKey?: string; meta?: ProviderMeta; providerKey?: string; suggestedDefaults?: OpenClawSuggestedDefaults; }; +type ApiKeyField = "ANTHROPIC_AUTH_TOKEN" | "ANTHROPIC_API_KEY"; + +type PresetEntry = { + id: string; + preset: ClaudeDesktopProviderPreset; +}; + export interface ClaudeDesktopProviderFormProps { submitLabel: string; onSubmit: (values: ClaudeDesktopProviderFormValues) => Promise | void; @@ -174,6 +187,20 @@ export function ClaudeDesktopProviderForm({ envString(initialData?.settingsConfig, "ANTHROPIC_AUTH_TOKEN") || envString(initialData?.settingsConfig, "ANTHROPIC_API_KEY"), ); + const [apiKeyField, setApiKeyField] = useState(() => + envString(initialData?.settingsConfig, "ANTHROPIC_API_KEY") + ? "ANTHROPIC_API_KEY" + : "ANTHROPIC_AUTH_TOKEN", + ); + const [selectedPresetId, setSelectedPresetId] = useState( + "custom", + ); + const [activePreset, setActivePreset] = useState<{ + id: string; + category?: ProviderCategory; + isPartner?: boolean; + partnerPromotionKey?: string; + } | null>(null); const [routes, setRoutes] = useState(() => initialRouteRows(initialData?.meta?.claudeDesktopModelRoutes), ); @@ -225,6 +252,106 @@ export function ClaudeDesktopProviderForm({ onSubmittingChange?.(form.formState.isSubmitting || isFetchingModels); }, [form.formState.isSubmitting, isFetchingModels, onSubmittingChange]); + const presetEntries = useMemo( + () => + claudeDesktopProviderPresets.map((preset, index) => ({ + id: `claude-desktop-${index}`, + preset, + })), + [], + ); + + const groupedPresets = useMemo( + () => + presetEntries.reduce>((acc, entry) => { + const cat = entry.preset.category ?? "others"; + if (!acc[cat]) acc[cat] = []; + acc[cat].push(entry); + return acc; + }, {}), + [presetEntries], + ); + + const categoryKeys = useMemo( + () => + Object.keys(groupedPresets).filter( + (key) => key !== "custom" && groupedPresets[key]?.length, + ), + [groupedPresets], + ); + + const presetCategoryLabels: Record = useMemo( + () => ({ + official: t("providerForm.categoryOfficial", { defaultValue: "官方" }), + cn_official: t("providerForm.categoryCnOfficial", { + defaultValue: "国内官方", + }), + aggregator: t("providerForm.categoryAggregation", { + defaultValue: "聚合服务", + }), + third_party: t("providerForm.categoryThirdParty", { + defaultValue: "第三方", + }), + }), + [t], + ); + + const applyDesktopPreset = (preset: ClaudeDesktopProviderPreset) => { + form.setValue("name", preset.nameKey ? t(preset.nameKey) : preset.name); + form.setValue("websiteUrl", preset.websiteUrl); + form.setValue("notes", ""); + form.setValue("icon", preset.icon ?? ""); + form.setValue("iconColor", preset.iconColor ?? ""); + + setBaseUrl(preset.baseUrl); + setApiKey(""); + setApiKeyField(preset.apiKeyField ?? "ANTHROPIC_AUTH_TOKEN"); + setApiFormat(preset.apiFormat ?? "anthropic"); + + didSeedDefaultRoutes.current = true; + setMode(preset.mode); + if (preset.mode === "proxy" && preset.modelRoutes) { + setRoutes( + preset.modelRoutes.map((r) => ({ + route: r.routeId, + model: r.upstreamModel, + displayName: r.displayName, + supports1m: r.supports1m, + })), + ); + } else { + setRoutes([]); + } + }; + + const handlePresetChange = (value: string) => { + setSelectedPresetId(value); + + if (value === "custom") { + setActivePreset(null); + form.reset(defaultValues); + setBaseUrl(""); + setApiKey(""); + setApiKeyField("ANTHROPIC_AUTH_TOKEN"); + setApiFormat("anthropic"); + didSeedDefaultRoutes.current = false; + setMode("direct"); + setRoutes([]); + return; + } + + const entry = presetEntries.find((item) => item.id === value); + if (!entry) return; + + setActivePreset({ + id: value, + category: entry.preset.category, + isPartner: entry.preset.isPartner, + partnerPromotionKey: entry.preset.partnerPromotionKey, + }); + applyDesktopPreset(entry.preset); + }; + const updateRoute = (index: number, patch: Partial) => { setRoutes((current) => current.map((row, i) => (i === index ? { ...row, ...patch } : row)), @@ -361,10 +488,15 @@ export function ClaudeDesktopProviderForm({ const settingsConfig = clonePlainRecord(initialData?.settingsConfig); const env = clonePlainRecord(settingsConfig.env); + const otherKey: ApiKeyField = + apiKeyField === "ANTHROPIC_AUTH_TOKEN" + ? "ANTHROPIC_API_KEY" + : "ANTHROPIC_AUTH_TOKEN"; + delete env[otherKey]; settingsConfig.env = { ...env, ANTHROPIC_BASE_URL: baseUrl.trim().replace(/\/+$/, ""), - ANTHROPIC_AUTH_TOKEN: apiKey.trim(), + [apiKeyField]: apiKey.trim(), }; const routeMap = routeEntries.reduce< @@ -397,6 +529,10 @@ export function ClaudeDesktopProviderForm({ notes: values.notes?.trim() ?? "", settingsConfig: JSON.stringify(settingsConfig, null, 2), meta, + presetId: activePreset?.id, + presetCategory: activePreset?.category, + isPartner: activePreset?.isPartner, + partnerPromotionKey: activePreset?.partnerPromotionKey, }); }; @@ -437,6 +573,17 @@ export function ClaudeDesktopProviderForm({ onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6" > + {!initialData && ( + + )} +
diff --git a/src/components/providers/forms/ProviderPresetSelector.tsx b/src/components/providers/forms/ProviderPresetSelector.tsx index d1e3f7bad..47b88b82b 100644 --- a/src/components/providers/forms/ProviderPresetSelector.tsx +++ b/src/components/providers/forms/ProviderPresetSelector.tsx @@ -5,6 +5,7 @@ import { Zap, Star, Layers, Settings2 } from "lucide-react"; import type { ProviderPreset } from "@/config/claudeProviderPresets"; import type { CodexProviderPreset } from "@/config/codexProviderPresets"; import type { GeminiProviderPreset } from "@/config/geminiProviderPresets"; +import type { ClaudeDesktopProviderPreset } from "@/config/claudeDesktopProviderPresets"; import type { ProviderCategory } from "@/types"; import { universalProviderPresets, @@ -12,9 +13,15 @@ import { } from "@/config/universalProviderPresets"; import { ProviderIcon } from "@/components/ProviderIcon"; +type AnyPreset = + | ProviderPreset + | CodexProviderPreset + | GeminiProviderPreset + | ClaudeDesktopProviderPreset; + type PresetEntry = { id: string; - preset: ProviderPreset | CodexProviderPreset | GeminiProviderPreset; + preset: AnyPreset; }; interface ProviderPresetSelectorProps { @@ -74,9 +81,7 @@ export function ProviderPresetSelector({ } }; - const renderPresetIcon = ( - preset: ProviderPreset | CodexProviderPreset | GeminiProviderPreset, - ) => { + const renderPresetIcon = (preset: AnyPreset) => { const iconType = preset.theme?.icon; if (!iconType) return null; @@ -94,10 +99,7 @@ export function ProviderPresetSelector({ } }; - const getPresetButtonClass = ( - isSelected: boolean, - preset: ProviderPreset | CodexProviderPreset | GeminiProviderPreset, - ) => { + const getPresetButtonClass = (isSelected: boolean, preset: AnyPreset) => { const baseClass = "inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors"; @@ -111,10 +113,7 @@ export function ProviderPresetSelector({ return `${baseClass} bg-accent text-muted-foreground hover:bg-accent/80`; }; - const getPresetButtonStyle = ( - isSelected: boolean, - preset: ProviderPreset | CodexProviderPreset | GeminiProviderPreset, - ) => { + const getPresetButtonStyle = (isSelected: boolean, preset: AnyPreset) => { if (!isSelected || !preset.theme?.backgroundColor) { return undefined; } diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts new file mode 100644 index 000000000..c63462807 --- /dev/null +++ b/src/config/claudeDesktopProviderPresets.ts @@ -0,0 +1,808 @@ +/** + * Claude Desktop 预设供应商配置模板 + * + * 形态与 Claude Code 预设不同: + * - baseUrl 是顶级字段,而不是 settingsConfig.env.ANTHROPIC_BASE_URL + * - 模型信息以"请求模型 → 上游模型 → 显示模型"三段式表达, + * 对应后端 ClaudeDesktopModelRoute 的 routeId / model / displayName + * + * 翻译来源:src/config/claudeProviderPresets.ts(排除 OAuth 与不兼容预设) + */ +import { ProviderCategory } from "../types"; +import type { PresetTheme } from "./claudeProviderPresets"; + +export type ClaudeDesktopApiFormat = + | "anthropic" + | "openai_chat" + | "openai_responses" + | "gemini_native"; + +export interface ClaudeDesktopRoutePreset { + routeId: string; + upstreamModel: string; + displayName: string; + supports1m: boolean; +} + +export interface ClaudeDesktopProviderPreset { + name: string; + nameKey?: string; + websiteUrl: string; + apiKeyUrl?: string; + category?: ProviderCategory; + isPartner?: boolean; + partnerPromotionKey?: string; + + baseUrl: string; + apiKeyField?: "ANTHROPIC_AUTH_TOKEN" | "ANTHROPIC_API_KEY"; + + mode: "direct" | "proxy"; + apiFormat?: ClaudeDesktopApiFormat; + modelRoutes?: ClaudeDesktopRoutePreset[]; + + endpointCandidates?: string[]; + theme?: PresetTheme; + icon?: string; + iconColor?: string; +} + +const passthroughRoutes = (supports1m = false): ClaudeDesktopRoutePreset[] => [ + { + routeId: "claude-sonnet-4-6", + upstreamModel: "claude-sonnet-4-6", + displayName: "Sonnet", + supports1m, + }, + { + routeId: "claude-opus-4-7", + upstreamModel: "claude-opus-4-7", + displayName: "Opus", + supports1m, + }, + { + routeId: "claude-haiku-4-5", + upstreamModel: "claude-haiku-4-5", + displayName: "Haiku", + supports1m, + }, +]; + +const mappedRoutes = ( + sonnet: string, + opus: string, + haiku: string, + supports1m = false, +): ClaudeDesktopRoutePreset[] => [ + { + routeId: "claude-sonnet-4-6", + upstreamModel: sonnet, + displayName: "Sonnet", + supports1m, + }, + { + routeId: "claude-opus-4-7", + upstreamModel: opus, + displayName: "Opus", + supports1m, + }, + { + routeId: "claude-haiku-4-5", + upstreamModel: haiku, + displayName: "Haiku", + supports1m, + }, +]; + +/** + * 非 Claude 上游模型用此工厂:displayName 直接等于 upstreamModel, + * 让用户在 Claude Desktop 看到的就是实际请求的模型 ID(如 "deepseek-v4-pro")。 + */ +const brandedRoutes = ( + sonnet: string, + opus: string, + haiku: string, + supports1m = false, +): ClaudeDesktopRoutePreset[] => [ + { + routeId: "claude-sonnet-4-6", + upstreamModel: sonnet, + displayName: sonnet, + supports1m, + }, + { + routeId: "claude-opus-4-7", + upstreamModel: opus, + displayName: opus, + supports1m, + }, + { + routeId: "claude-haiku-4-5", + upstreamModel: haiku, + displayName: haiku, + supports1m, + }, +]; + +export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ + { + name: "Shengsuanyun", + nameKey: "providerForm.presets.shengsuanyun", + websiteUrl: "https://www.shengsuanyun.com", + apiKeyUrl: "https://www.shengsuanyun.com/?from=CH_4HHXMRYF", + category: "aggregator", + baseUrl: "https://router.shengsuanyun.com/api", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "shengsuanyun", + icon: "shengsuanyun", + }, + { + name: "Gemini Native", + websiteUrl: "https://ai.google.dev/gemini-api", + apiKeyUrl: "https://aistudio.google.com/app/apikey", + category: "third_party", + baseUrl: "https://generativelanguage.googleapis.com", + apiKeyField: "ANTHROPIC_API_KEY", + mode: "proxy", + apiFormat: "gemini_native", + modelRoutes: brandedRoutes( + "gemini-3.1-pro", + "gemini-3.1-pro", + "gemini-3-flash", + ), + endpointCandidates: ["https://generativelanguage.googleapis.com"], + icon: "gemini", + iconColor: "#4285F4", + }, + { + name: "DeepSeek", + websiteUrl: "https://platform.deepseek.com", + category: "cn_official", + baseUrl: "https://api.deepseek.com/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "deepseek-v4-pro", + "deepseek-v4-pro", + "deepseek-v4-flash", + ), + icon: "deepseek", + iconColor: "#1E88E5", + }, + { + name: "Zhipu GLM", + websiteUrl: "https://open.bigmodel.cn", + apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII", + category: "cn_official", + baseUrl: "https://open.bigmodel.cn/api/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes("glm-5", "glm-5", "glm-5"), + icon: "zhipu", + iconColor: "#0F62FE", + }, + { + name: "Zhipu GLM en", + websiteUrl: "https://z.ai", + apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB", + category: "cn_official", + baseUrl: "https://api.z.ai/api/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes("glm-5", "glm-5", "glm-5"), + icon: "zhipu", + iconColor: "#0F62FE", + }, + { + name: "Baidu Qianfan Coding Plan", + websiteUrl: "https://cloud.baidu.com/product/qianfan_modelbuilder", + apiKeyUrl: + "https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application", + category: "cn_official", + baseUrl: "https://qianfan.baidubce.com/anthropic/coding", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "qianfan-code-latest", + "qianfan-code-latest", + "qianfan-code-latest", + ), + endpointCandidates: ["https://qianfan.baidubce.com/anthropic/coding"], + icon: "baidu", + iconColor: "#2932E1", + }, + { + name: "Bailian", + websiteUrl: "https://bailian.console.aliyun.com", + category: "cn_official", + baseUrl: "https://dashscope.aliyuncs.com/apps/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + icon: "bailian", + iconColor: "#624AFF", + }, + { + name: "Bailian For Coding", + websiteUrl: "https://bailian.console.aliyun.com", + category: "cn_official", + baseUrl: "https://coding.dashscope.aliyuncs.com/apps/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + icon: "bailian", + iconColor: "#624AFF", + }, + { + name: "Kimi", + websiteUrl: "https://platform.moonshot.cn/console", + category: "cn_official", + baseUrl: "https://api.moonshot.cn/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes("kimi-k2.6", "kimi-k2.6", "kimi-k2.6"), + icon: "kimi", + iconColor: "#6366F1", + }, + { + name: "Kimi For Coding", + websiteUrl: "https://www.kimi.com/code/docs/", + category: "cn_official", + baseUrl: "https://api.kimi.com/coding/", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + icon: "kimi", + iconColor: "#6366F1", + }, + { + name: "StepFun", + websiteUrl: "https://platform.stepfun.com/step-plan", + apiKeyUrl: "https://platform.stepfun.com/interface-key", + category: "cn_official", + baseUrl: "https://api.stepfun.com/step_plan", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "step-3.5-flash-2603", + "step-3.5-flash-2603", + "step-3.5-flash-2603", + ), + endpointCandidates: ["https://api.stepfun.com/step_plan"], + icon: "stepfun", + iconColor: "#16D6D2", + }, + { + name: "StepFun en", + websiteUrl: "https://platform.stepfun.ai/step-plan", + apiKeyUrl: "https://platform.stepfun.ai/interface-key", + category: "cn_official", + baseUrl: "https://api.stepfun.ai/step_plan", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "step-3.5-flash-2603", + "step-3.5-flash-2603", + "step-3.5-flash-2603", + ), + endpointCandidates: ["https://api.stepfun.ai/step_plan"], + icon: "stepfun", + iconColor: "#16D6D2", + }, + { + name: "ModelScope", + websiteUrl: "https://modelscope.cn", + category: "aggregator", + baseUrl: "https://api-inference.modelscope.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "ZhipuAI/GLM-5", + "ZhipuAI/GLM-5", + "ZhipuAI/GLM-5", + ), + icon: "modelscope", + iconColor: "#624AFF", + }, + { + name: "Longcat", + websiteUrl: "https://longcat.chat/platform", + apiKeyUrl: "https://longcat.chat/platform/api_keys", + category: "cn_official", + baseUrl: "https://api.longcat.chat/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "LongCat-Flash-Chat", + "LongCat-Flash-Chat", + "LongCat-Flash-Chat", + ), + icon: "longcat", + iconColor: "#29E154", + }, + { + name: "MiniMax", + websiteUrl: "https://platform.minimaxi.com", + apiKeyUrl: "https://platform.minimaxi.com/subscribe/coding-plan", + category: "cn_official", + baseUrl: "https://api.minimaxi.com/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "MiniMax-M2.7", + "MiniMax-M2.7", + "MiniMax-M2.7", + ), + isPartner: true, + partnerPromotionKey: "minimax_cn", + theme: { + backgroundColor: "#f64551", + textColor: "#FFFFFF", + }, + icon: "minimax", + iconColor: "#FF6B6B", + }, + { + name: "MiniMax en", + websiteUrl: "https://platform.minimax.io", + apiKeyUrl: "https://platform.minimax.io/subscribe/coding-plan", + category: "cn_official", + baseUrl: "https://api.minimax.io/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "MiniMax-M2.7", + "MiniMax-M2.7", + "MiniMax-M2.7", + ), + isPartner: true, + partnerPromotionKey: "minimax_en", + theme: { + backgroundColor: "#f64551", + textColor: "#FFFFFF", + }, + icon: "minimax", + iconColor: "#FF6B6B", + }, + { + name: "DouBaoSeed", + websiteUrl: "https://www.volcengine.com/product/doubao", + apiKeyUrl: "https://www.volcengine.com/product/doubao", + category: "cn_official", + baseUrl: "https://ark.cn-beijing.volces.com/api/coding", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "doubao-seed-2-0-code-preview-latest", + "doubao-seed-2-0-code-preview-latest", + "doubao-seed-2-0-code-preview-latest", + ), + icon: "doubao", + iconColor: "#3370FF", + }, + { + name: "BaiLing", + websiteUrl: "https://alipaytbox.yuque.com/sxs0ba/ling/get_started", + category: "cn_official", + baseUrl: "https://api.tbox.cn/api/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "Ling-2.5-1T", + "Ling-2.5-1T", + "Ling-2.5-1T", + ), + }, + { + name: "AiHubMix", + websiteUrl: "https://aihubmix.com", + apiKeyUrl: "https://aihubmix.com", + category: "aggregator", + baseUrl: "https://aihubmix.com", + apiKeyField: "ANTHROPIC_API_KEY", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://aihubmix.com", "https://api.aihubmix.com"], + icon: "aihubmix", + iconColor: "#006FFB", + }, + { + name: "SiliconFlow", + websiteUrl: "https://siliconflow.cn", + apiKeyUrl: "https://cloud.siliconflow.cn/i/drGuwc9k", + category: "aggregator", + baseUrl: "https://api.siliconflow.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "Pro/MiniMaxAI/MiniMax-M2.7", + "Pro/MiniMaxAI/MiniMax-M2.7", + "Pro/MiniMaxAI/MiniMax-M2.7", + ), + isPartner: true, + partnerPromotionKey: "siliconflow", + icon: "siliconflow", + iconColor: "#6E29F6", + }, + { + name: "SiliconFlow en", + websiteUrl: "https://siliconflow.com", + apiKeyUrl: "https://cloud.siliconflow.cn/i/drGuwc9k", + category: "aggregator", + baseUrl: "https://api.siliconflow.com", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "MiniMaxAI/MiniMax-M2.7", + "MiniMaxAI/MiniMax-M2.7", + "MiniMaxAI/MiniMax-M2.7", + ), + isPartner: true, + partnerPromotionKey: "siliconflow", + icon: "siliconflow", + iconColor: "#000000", + }, + { + name: "DMXAPI", + websiteUrl: "https://www.dmxapi.cn", + apiKeyUrl: "https://www.dmxapi.cn", + category: "aggregator", + baseUrl: "https://www.dmxapi.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://www.dmxapi.cn", "https://api.dmxapi.cn"], + isPartner: true, + partnerPromotionKey: "dmxapi", + }, + { + name: "PackyCode", + websiteUrl: "https://www.packyapi.com", + apiKeyUrl: "https://www.packyapi.com/register?aff=cc-switch", + category: "third_party", + baseUrl: "https://www.packyapi.com", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: [ + "https://www.packyapi.com", + "https://api-slb.packyapi.com", + ], + isPartner: true, + partnerPromotionKey: "packycode", + icon: "packycode", + }, + { + name: "Cubence", + websiteUrl: "https://cubence.com", + apiKeyUrl: "https://cubence.com/signup?code=CCSWITCH&source=ccs", + category: "third_party", + baseUrl: "https://api.cubence.com", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: [ + "https://api.cubence.com", + "https://api-cf.cubence.com", + "https://api-dmit.cubence.com", + "https://api-bwg.cubence.com", + ], + isPartner: true, + partnerPromotionKey: "cubence", + icon: "cubence", + iconColor: "#000000", + }, + { + name: "AIGoCode", + websiteUrl: "https://aigocode.com", + apiKeyUrl: "https://aigocode.com/invite/CC-SWITCH", + category: "third_party", + baseUrl: "https://api.aigocode.com", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://api.aigocode.com"], + isPartner: true, + partnerPromotionKey: "aigocode", + icon: "aigocode", + iconColor: "#5B7FFF", + }, + { + name: "RightCode", + websiteUrl: "https://www.right.codes", + apiKeyUrl: "https://www.right.codes/register?aff=CCSWITCH", + category: "third_party", + baseUrl: "https://www.right.codes/claude", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "rightcode", + icon: "rc", + iconColor: "#E96B2C", + }, + { + name: "AICodeMirror", + websiteUrl: "https://www.aicodemirror.com", + apiKeyUrl: "https://www.aicodemirror.com/register?invitecode=9915W3", + category: "third_party", + baseUrl: "https://api.aicodemirror.com/api/claudecode", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: [ + "https://api.aicodemirror.com/api/claudecode", + "https://api.claudecode.net.cn/api/claudecode", + ], + isPartner: true, + partnerPromotionKey: "aicodemirror", + icon: "aicodemirror", + iconColor: "#000000", + }, + { + name: "AICoding", + websiteUrl: "https://aicoding.sh", + apiKeyUrl: "https://aicoding.sh/i/CCSWITCH", + category: "third_party", + baseUrl: "https://api.aicoding.sh", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://api.aicoding.sh"], + isPartner: true, + partnerPromotionKey: "aicoding", + icon: "aicoding", + iconColor: "#000000", + }, + { + name: "CrazyRouter", + websiteUrl: "https://www.crazyrouter.com", + apiKeyUrl: "https://www.crazyrouter.com/register?aff=OZcm&ref=cc-switch", + category: "third_party", + baseUrl: "https://crazyrouter.com", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://crazyrouter.com"], + isPartner: true, + partnerPromotionKey: "crazyrouter", + icon: "crazyrouter", + iconColor: "#000000", + }, + { + name: "SSSAiCode", + websiteUrl: "https://www.sssaicode.com", + apiKeyUrl: "https://www.sssaicode.com/register?ref=DCP0SM", + category: "third_party", + baseUrl: "https://node-hk.sssaicode.com/api", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: [ + "https://node-hk.sssaicode.com/api", + "https://claude2.sssaicode.com/api", + "https://anti.sssaicode.com/api", + ], + isPartner: true, + partnerPromotionKey: "sssaicode", + icon: "sssaicode", + iconColor: "#000000", + }, + { + name: "Compshare", + nameKey: "providerForm.presets.ucloud", + websiteUrl: "https://www.compshare.cn", + apiKeyUrl: + "https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch", + category: "aggregator", + baseUrl: "https://api.modelverse.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://api.modelverse.cn"], + isPartner: true, + partnerPromotionKey: "ucloud", + icon: "ucloud", + iconColor: "#000000", + }, + { + name: "Compshare Coding Plan", + nameKey: "providerForm.presets.ucloudCoding", + websiteUrl: "https://www.compshare.cn", + apiKeyUrl: + "https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch", + category: "aggregator", + baseUrl: "https://cp.compshare.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://cp.compshare.cn"], + isPartner: true, + partnerPromotionKey: "ucloud", + icon: "ucloud", + iconColor: "#000000", + }, + { + name: "Micu", + websiteUrl: "https://www.openclaudecode.cn", + apiKeyUrl: "https://www.openclaudecode.cn/register?aff=aOYQ", + category: "third_party", + baseUrl: "https://www.openclaudecode.cn", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://www.openclaudecode.cn"], + isPartner: true, + partnerPromotionKey: "micu", + icon: "micu", + iconColor: "#000000", + }, + { + name: "CTok.ai", + websiteUrl: "https://ctok.ai", + apiKeyUrl: "https://ctok.ai", + category: "third_party", + baseUrl: "https://api.ctok.ai", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "ctok", + icon: "ctok", + iconColor: "#000000", + }, + { + name: "DDSHub", + websiteUrl: "https://www.ddshub.cc", + apiKeyUrl: "https://ddshub.short.gy/ccswitch", + category: "third_party", + baseUrl: "https://www.ddshub.cc", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "ddshub", + icon: "dds", + iconColor: "#000000", + }, + { + name: "E-FlowCode", + websiteUrl: "https://e-flowcode.cc", + apiKeyUrl: "https://e-flowcode.cc", + category: "third_party", + baseUrl: "https://e-flowcode.cc", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + endpointCandidates: ["https://e-flowcode.cc"], + icon: "eflowcode", + iconColor: "#000000", + }, + { + name: "LionCCAPI", + websiteUrl: "https://vibecodingapi.ai", + category: "third_party", + baseUrl: "https://vibecodingapi.ai", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "lionccapi", + icon: "lioncc", + }, + { + name: "OpenRouter", + websiteUrl: "https://openrouter.ai", + apiKeyUrl: "https://openrouter.ai/keys", + category: "aggregator", + baseUrl: "https://openrouter.ai/api", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: mappedRoutes( + "anthropic/claude-sonnet-4.6", + "anthropic/claude-opus-4.7", + "anthropic/claude-haiku-4.5", + true, + ), + icon: "openrouter", + iconColor: "#6566F1", + }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + category: "aggregator", + baseUrl: "https://api.therouter.ai", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: mappedRoutes( + "anthropic/claude-sonnet-4.6", + "anthropic/claude-opus-4.7", + "anthropic/claude-haiku-4.5", + true, + ), + endpointCandidates: ["https://api.therouter.ai"], + }, + { + name: "Novita AI", + websiteUrl: "https://novita.ai", + apiKeyUrl: "https://novita.ai", + category: "aggregator", + baseUrl: "https://api.novita.ai/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "zai-org/glm-5", + "zai-org/glm-5", + "zai-org/glm-5", + ), + endpointCandidates: ["https://api.novita.ai/anthropic"], + icon: "novita", + iconColor: "#000000", + }, + { + name: "LemonData", + websiteUrl: "https://lemondata.cc", + apiKeyUrl: "https://lemondata.cc/r/FFX1ZDUP", + category: "third_party", + baseUrl: "https://api.lemondata.cc", + apiKeyField: "ANTHROPIC_API_KEY", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(), + isPartner: true, + partnerPromotionKey: "lemondata", + icon: "lemondata", + }, + { + name: "Nvidia", + websiteUrl: "https://build.nvidia.com", + apiKeyUrl: "https://build.nvidia.com/settings/api-keys", + category: "aggregator", + baseUrl: "https://integrate.api.nvidia.com", + mode: "proxy", + apiFormat: "openai_chat", + modelRoutes: brandedRoutes( + "moonshotai/kimi-k2.5", + "moonshotai/kimi-k2.5", + "moonshotai/kimi-k2.5", + ), + icon: "nvidia", + iconColor: "#000000", + }, + { + name: "PIPELLM", + websiteUrl: "https://code.pipellm.ai", + apiKeyUrl: "https://code.pipellm.ai/login?ref=uvw650za", + category: "aggregator", + baseUrl: "https://cc-api.pipellm.ai", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: mappedRoutes( + "claude-sonnet-4-6", + "claude-opus-4-7", + "claude-haiku-4-5-20251001", + true, + ), + icon: "pipellm", + }, + { + name: "Xiaomi MiMo", + websiteUrl: "https://platform.xiaomimimo.com", + apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys", + category: "cn_official", + baseUrl: "https://api.xiaomimimo.com/anthropic", + mode: "proxy", + apiFormat: "anthropic", + modelRoutes: brandedRoutes( + "mimo-v2-pro", + "mimo-v2-pro", + "mimo-v2-pro", + ), + icon: "xiaomimimo", + iconColor: "#000000", + }, +]; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b9d07fd37..8b06c11be 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -698,6 +698,7 @@ "apps": { "claude": "Claude", "claudeDesktop": "Claude Desktop", + "claude-desktop": "Claude Desktop", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 31165c6f2..af2bbda5b 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -698,6 +698,7 @@ "apps": { "claude": "Claude", "claudeDesktop": "Claude Desktop", + "claude-desktop": "Claude Desktop", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index af4745078..1ac9b656f 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -698,6 +698,7 @@ "apps": { "claude": "Claude", "claudeDesktop": "Claude Desktop", + "claude-desktop": "Claude Desktop", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode",