CC Switch 里「应用」这个概念有六套枚举

2026-08-31

在 CC Switch 的前端代码里问一句「哪些应用支持这个功能」,你会拿到不止一个答案。麻烦的是,这些答案并不是散落在各处的硬编码,它们几乎全挤在同一个文件里,彼此相邻、命名规整、看上去像同一批东西——但成员就是不一样。

这个文件是 src/config/appConfig.tsx。它是 src/config/ 目录下唯一的 .tsx 文件,也是这个目录里被 import 次数最多的模块:应用切换器、可见性设置、MCP 面板、Skills 面板、代理面板与接管开关、故障转移队列、用量看板的页眉页脚,都从它这里取「应用」。换句话说,只要功能里出现「按应用分」的地方,判据基本都来自这一个文件。

本文写在 v3.20.1 这个快照上(仓库内版本号 3.20.1,快照 commit 3217f725,核对日 2026-08-31)。下面所有行号、常量名与成员划分,都是静态读源码读出来的:我们没有编译、没有运行、也没有安装过这个桌面应用,因此不涉及界面长什么样、点起来什么感觉。凡是实读源码的地方都给到文件与行号,你可以自己去仓库里核。

六套集合各在哪一行

先把位置摆出来。前五处都在 src/config/appConfig.tsx,最后一处在另一个文件里:

常量位置它回答的问题形态
APP_IDSappConfig.tsx:19这个软件一共托管哪些应用手写数组
DEFAULT_VISIBLE_APPSappConfig.tsx:31装上之后默认显示哪些页签以应用 id 为键的记录,值全为 true
SKILLS_APP_IDSappConfig.tsx:44哪些应用出现在 Skills 面板里手写数组
PROXY_APP_IDSappConfig.tsx:60哪些应用有本地网关、能被代理接管手写数组
ADDITIVE_APP_IDSappConfig.tsx:76哪些应用走「累加模式」手写数组
McpAppId / MCP_APP_IDSappConfig.tsx:88 / :89哪些应用参与 MCP 同步Exclude<AppId, ...> 类型 + 配套数组
AppType / KNOWN_APP_TYPESsrc/types/usage.ts:192 / :202用量看板认哪些应用联合类型 + 配套数组

表里是七行,但 DEFAULT_VISIBLE_APPS 的键和 APP_IDS 的成员是同一批,所以按「成员集合」算是六套。这些成员划分是 v3.20.1 快照的状态,新增或调整一个受管应用时六处都要各自表态,成员随版本变动,请以仓库当前的 appConfig.tsx 为准。

最直观的分歧点是 Pi。v3.20.0 的发布说明把 Pi 作为新加入的受管应用来讲,源码里它确实排在 APP_IDS 数组的末位。但顺着上面这张表往下看,Pi 出现在全集、Skills、累加模式和用量口径这四套里,缺席代理与 MCP 这两套。同一个应用,在同一个文件的相邻几行里,一半在册一半不在册。

每一套服务的是不同一层的判定

把六套集合的判据摊开,会发现它们根本不在一个维度上。

PROXY_APP_IDS 判的是运行时能力:这个应用有没有一条完整的本地网关与故障转移数据面。有网关才谈得上「接管」,没有网关,接管开关就是个空动作。v3.20.0 发布说明里有一条连带修复正好落在这上面(docs/release-notes/v3.20.0-zh.md:70):代理与故障转移命令现在对所有无本地网关的应用显式拒绝,而不是写下一堆死配置。也就是说,这个集合之外的应用,走到代理路径上应该得到一个明确的拒绝,而不是一个悄悄写坏的配置文件。这条判据没法从别的集合推出来。

ADDITIVE_APP_IDS 判的是配置模型,和能力无关。发布说明对累加模式的描述是(v3.20.0-zh.md:68):供应商的启用与否等于其键是否存在于配置文件里,多个供应商可以共存。而非累加的应用是「切换」语义——一次只有一个生效的供应商。这是两种在数据模型上就不兼容的东西:一个是集合的成员关系,一个是单选。同一个界面要同时装下这两种模型,就必须有一个集合把它们分开,否则「切换到某个供应商」这个动作在两类应用上的含义都不一样。

McpAppId 判的是宿主有没有那个东西。它在类型层面是从全集里减出来的:Exclude<AppId, "claude-desktop" | "openclaw" | "pi">appConfig.tsx:88),紧跟其后的 MCP_APP_IDS:89-96)再按这个类型物化成一个运行时数组——类型负责挡住写错,数组负责给消费方遍历。紧挨着类型定义上一行有一句源码注释(appConfig.tsx:87),把设计意图写得比任何文档都清楚:

/** Pi has no native MCP registry; do not manufacture a disabled mirror. */

「不要造一个禁用状态的镜像」——这句话是理解整篇文章的钥匙,下一节会回到它。

AppTypeKNOWN_APP_TYPES 在另一个文件里(src/types/usage.ts:192:202),判的是有没有用量数据可算。用量看板是一条独立的数据链路,能不能进这个口径取决于用量有没有来源,和能不能被代理接管是两回事。关于用量数字的两条来源,我们另有一篇专门讲:CC Switch 的用量数字从哪来:代理日志还是本地会话文件

SKILLS_APP_IDS 判的是面板归属。DEFAULT_VISIBLE_APPS 更特别,它压根不是能力集合——它是一份初值,值全为 true,表达的是「默认全部可见」,而可见性是用户可以在设置里改的。把一个用户可改的初值和五个由代码能力决定的集合放在一起看,就能明白为什么它必须是一份记录而不是数组:数组表达「在册」,记录表达「每个应用的当前开关值」。

为什么不能合成一套

看到六套集合的第一反应通常是「合成一张表不就行了」——一个应用一行,后面挂几列布尔值。这个想法在这份源码里有明确的反例。

第一层障碍是那句注释:do not manufacture a disabled mirror。假如把 MCP 这一维也做成「每个应用一个布尔位」,那么 Pi 这一行就得填一个 false,意思是「Pi 的 MCP 同步处于关闭状态」。但事实不是关闭,是宿主根本没有原生 MCP 注册表这个东西——这两件事对使用者的含义天差地别:前者暗示「打开就能用」,后者是「不存在」。用类型层面的 Exclude 把它减掉,「把 Pi 当成一个 MCP 应用传进去」这件事在编译期就通不过,运行时那份 MCP_APP_IDS 里也没有它;用布尔位表达,则要靠每个消费方自己记得判断。发布说明里也把这个边界写成了明确的「不接」清单(v3.20.0-zh.md:70):MCP 同步、代理接管、故障转移、托盘存在,这几项都不为 Pi 提供。

第二层障碍是判据不同源。前面已经拆过:能力、配置模型、宿主特性、数据来源、面板归属、用户可改的初值,六套集合的判据分别属于六个不同的层。合成一张表,表面上省了五个常量,实际上是把六个来源不同、变更节奏也不同的判据塞进同一处。改动其中一个维度时,你面对的是一整张表,而不是一个语义明确的数组。

第三层障碍藏在形态里。这几套的写法本来就不一样:多数是手写数组,默认可见是一份以应用 id 为键的记录,MCP 那一维先由类型层的减法框定、再物化成一个成员相同的数组,用量口径则是联合类型配一个配套数组。它们能被写成不同形态,恰恰是因为它们表达的东西不同。真要合成一套,最后得到的是一个所有维度共用的最宽松形态,六套里那些原本由类型系统承担的约束会全部退化成运行时判断。

需要说清楚的是:这里只是把六套集合并列摆出来,说明各自的判据来自哪一层。哪种组织方式更好、作者当初怎么权衡的,源码里没有写,本文也不做推断。

接一个新应用,六套都要各自表态

这套结构的代价在版本更新时会直接显形。v3.20.0 引入 Pi 的主提交(84e75ad2,对应 PR #6064)在一次改动里穿过了前端组件层、前端数据层、Rust 服务层、Tauri 命令层、会话管理器、代理层、深链、数据库与国际化——接一个新应用不是加一个配置文件的事。而在 appConfig.tsx 这一层,它体现为六次独立的判断:进全集、进默认可见、进 Skills、不进代理、进累加、不进 MCP,再加上用量侧那个文件里的一次判断。

顺带说一句版本口径:v3.19.2 的时候 APP_IDS 里还没有 pi 这一项,v3.20.1 快照上它已经在数组末位。所以任何一句「这个软件支持哪些应用」的断言,离开版本号都不成立。

还有一处更容易被漏掉的分组,它压根不落在常量里。v3.20.0 发布说明讲备份恢复时写了这么一条(v3.20.0-zh.md:193):恢复数据库之后,会把恢复后的库向外投影到除 Pi 外每个受管应用的 live 配置。理由原文是 Pi 的 models.json 仍是事实源、下次启动反向导入——因为累加模式下这个文件是双向的,这个软件写它,也从它导入,所以恢复时不能单向覆盖。

这条「除 Pi 外」的成员划分,是第七种事实上的分组,但它没有对应的常量,只写在恢复逻辑和发布说明里。所以在这个仓库里查「哪些应用参与某件事」,只 grep appConfig.tsx 里的常量是不够的——有些分组是靠一句 if 或一段文档承载的。

同一个取向在预设层也出现了

值得一提的是,「宁可各写各的,不要强行联动」这个取向不只出现在应用集合上。src/config/ 下每个受管应用各占一个预设文件,而且好几个文件的头注释都在强调彼此不联动:Grok Build 的预设注释写明它初始条目取自当时的 Codex 预设快照,此后两边各自演进;Claude Desktop 的预设文件头注释直说自己是从 Claude Code 预设「翻译」出来的;Pi 的预设文件注释(src/config/piProviderPresets.ts:78-85)则明确写着它初始对齐另一个应用的目录,但运行时不 import、也不派生自另一个应用的预设。三处独立的注释指向同一个决定。

同一个目录里也并存着相反的做法:src/config/universalProviderPresets.ts 的文件头写着「统一供应商是跨应用共享的配置,修改后会自动同步到 Claude、Codex、Gemini 三个应用」——那是唯一一个「一条配置管三个应用」的预设类型。关于它的边界,可以看CC Switch 的 universal 统一供应商:边界在哪

你可以自己做的核对

如果要在这份代码里判断某个功能覆盖哪些应用,最省事的路径是:把 src/config/appConfig.tsx 从头到尾读一遍(它不长,六套集合前后相邻),再看一眼 src/types/usage.ts:192 起那一段。这两处合起来基本覆盖了「按应用分」的全部判据。剩下的例外就是上面说的那类没有常量承载的分组,得顺着具体功能的实现去看。

至于哪一套该用在哪,源码给的信号其实很清楚:常量名里的动词就是判据本身。PROXY_ 问的是有没有网关,ADDITIVE_ 问的是配置模型,McpAppId 问的是宿主有没有那个注册表。想知道后端这几层是怎么接住这些判据的,可以看CC Switch 后端四层架构:Commands、Services、DAO 与数据库;想看 MCP 那一套在面板里怎么落地,可以看CC Switch 用一个面板管多个应用的 MCP:路径与格式差异


本文依据 CC Switch 官方仓库(github.com/farion1231/cc-switch)的 README、docs/ 下的用户手册、 路由指南与发布说明,以及 src/src-tauri/tests/ 的源码整理, 核对日 2026-08-31,对应仓库快照 3217f725(仓库内版本号 3.20.1)。 本文内容为仓库源码与文档口径,我们没有安装或运行过这个桌面应用, 因此不涉及界面外观、操作手感与切换速度的任何描述。 文中出现的阈值与默认值均为源码中的默认配置,不构成对实际运行结果的保证。 该项目仍在快速迭代,版本与默认值随时可能变动,请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。

留言讨论

评论发布后会被人工复核,违规内容将被删除。

    还没有人评论,来说说你的看法

    如果发表没有反应,可以前往联系我们告诉我们。

    在 CC Switch 里加一个国内直连的供应商

    力达云网关,注册送 ¥5 额度,一期提供 DeepSeek。

    去添加

    这个页面有问题?

    提交时会附带当前页面地址和浏览器信息,帮助我们定位问题。不填联系方式即为匿名。