cc-switch 本地路由重构:显式开关 × 持久化意图全解

发布时间:2026/9/20 16:49:30
cc-switch 本地路由重构:显式开关 × 持久化意图全解 cc-switch 本地路由重构显式开关 × 持久化意图全解【免费下载链接】cc-switchA cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build Hermes Agent. Only official website: ccswitch.io项目地址: https://gitcode.com/GitHub_Trending/cc/cc-switch本次改动把「本地路由随启动隐式拉起、关掉又被悄悄打开」换成了「按应用持久化的enabled意图是唯一启动依据」。读完你能带走三点启动时到底依据什么决定是否恢复路由、start/stop如何把意图先落库再收敛、以及哪些契约目前还没有测试兜底。旧行为的代价想象一个场景你在设置里关闭了本地路由端口随即释放。可重启 CC Switch 后127.0.0.1:15721又自动听了起来——你那次「关闭」从未真正生效。这正是变更记录里被点名的缺陷v3.9.0-1的修复项「Disable auto-start on app launch by resetting enabled flag on stop」CHANGELOG.md以及v3.9.0-2的「remove global auto-start flag」CHANGELOG.md。根因是运行时状态与持久化意图脱节停止操作只清掉了内存里的服务句柄却把持久化的proxy_enabled意图静默留在了true。于是形成三条因果链停止只take()服务句柄、不回写意图 → 数据库里意图仍是true下次启动读取该意图 → 判定「应该运行」→ 端口被重新打开用户反复关闭、反复复活 → 开关「关了又开」本地端口始终无法保持关闭。新契约行为边界表改动后持久化的enabled/proxy_enabled成为「期望状态」的唯一来源。启动、停止都先写意图、再收敛运行态二者不再各走各的。触发条件期望行为持久化影响启动时某应用proxy_config.enabled true恢复该应用接管并拉起路由不改写意图只读用户手动启动路由监听端口置proxy_enabled true意图落库为开启用户手动停止stop停止服务置proxy_enabled false意图落库为关闭跨重启保持用户关闭路由stop_with_restore恢复 Live 配置 清除各应用enabled意图清除下次不自动恢复仍有应用处于接管态时调用停止拒绝停止并返回提示无任何持久化影响某应用enabledtrue但恢复失败清除该应用状态避免下启重试该应用意图回滚不变量默认监听端口仍固定为15721src-tauri/src/proxy/types.rs接管依旧是按应用粒度的独立控制stop仅停服务与stop_with_restore停服务并恢复 Live 配置的语义分离保持不变异常退出后的残留恢复recover_from_crash路径也未改动。这些都不随本次显式开关的重构而变化。机制拆解从意图到运行态读取持久化意图以划定恢复范围启动时不猜测、不默认只从数据库读每个应用的enabled布尔值收集出「本次需要恢复接管」的白名单。没有任何应用为true就直接返回端口保持关闭。async fn enabled_proxy_apps_on_startup(db: Database) - Vecstatic str { let mut apps Vec::new(); for app_type in PROXY_STARTUP_APP_TYPES { if db.get_proxy_config_for_app(app_type).await .is_ok_and(|config| config.enabled) { apps.push(app_type); } } apps }该函数位于 src-tauri/src/lib.rs它是「启动依据什么」的唯一答案意图为false的应用永远不会被恢复直接否定了旧行为里「隐式拉起」的前提。按应用收敛接管状态拿到白名单后逐个恢复接管任何一项失败就回滚该应用避免下一次启动又重试同一个坏状态。for app_type in apps_to_restore { match state.proxy_service.set_takeover_for_app(app_type, true).await { Ok(()) { log::info!(✓ 已恢复 {app_type} 的代理接管状态); } Err(e) { log::error!(✗ 恢复 {app_type} 的代理接管状态失败: {e}); // 失败时清除该应用状态避免下次启动再次尝试 let _ state.proxy_service.set_takeover_for_app(app_type, false).await; } } }见 src-tauri/src/lib.rs。这段把「意图 → 运行态」的收敛做成了幂等循环且失败即回滚正是边界表中「恢复失败 → 意图回滚」一行的落点。启动即把意图先落库start()的第一步不是绑端口而是把proxy_enabled写成true并落库。意图先于运行态固化因此「启动了」这件事必然留下持久痕迹。let mut global_config self.db.get_global_proxy_config().await.map_err(...)?; if !global_config.proxy_enabled { global_config.proxy_enabled true; self.db.update_global_proxy_config(global_config.clone()).await.map_err(...)?; } // … 随后才读取配置、绑定端口见 src-tauri/src/services/proxy.rs。落库失败会直接向调用方报错而非继续绑端口杜绝了旧式「运行时起来了、意图却没写进去」的漂移。停止即回写关闭意图stop()在take()服务句柄、等任务结束后把proxy_enabled回写为false。关闭这一次是「真的关了」。if let Some(server) self.server.write().await.take() { server.stop().await.map_err(...)?; let mut global_config self.db.get_global_proxy_config().await.map_err(...)?; if global_config.proxy_enabled { global_config.proxy_enabled false; self.db.update_global_proxy_config(global_config).await...; } }见 src-tauri/src/services/proxy.rs。意图与运行态在同一函数里成对变更从结构上堵住了「停止只清运行时、意图残留true」这条复活路径。而stop_with_restore进一步清除各应用enabled保证手动关闭后下次启动不再自动恢复src-tauri/src/services/proxy.rs。异常路径与用户感知拒绝停止stop_proxy_server会先查接管状态只要还有任一应用处于接管态就返回中文提示「仍有应用处于代理接管状态请先…再停止」不产生任何持久化副作用src-tauri/src/commands/proxy.rs。端口绑定失败start()在TcpListener::bind失败时返回BindFailed错误如实呈现而非静默重试。停止超时服务停止带 5 秒超时保护超时返回StopTimeout并「强制继续」不会挂死整个退出流程src-tauri/src/proxy/server.rs。启动残留恢复检测到异常退出残留时先recover_from_crash再进入正常启动序列恢复与显式开关互不干扰src-tauri/src/lib.rs。用户需要做什么想关掉路由设置里关闭一次即可保持关闭想让某应用走路由对该应用单独开启接管停止被拒先逐应用关闭接管再停止升级后端口仍为15721无需改动测试如何守住契约src-tauri/tests/proxy_commands.rs当前只锁住「成本倍率」与「定价来源」两条命令的往返round-trip对proxy_enabled/enabled意图的落库与跨重启契约没有测试覆盖——这是本次重构最需要补的回归缺口。src-tauri/tests/proxy_commands.rs 中default_cost_multiplier_commands_round_trip仅断言倍率读写不涉及启停意图「停止后意图为 false」无断言。src-tauri/tests/provider_commands.rs涉及代理相关命令面但聚焦供应商命令未覆盖「启动按enabled恢复」这一收敛路径。一句话结论启停意图的持久化契约在仓库里当前无测试覆盖读者若要验证「关闭即持久」需自行以集成测试补断言。资料索引docs/user-manual/zh/4-proxy/4.1-service.md补充本地路由代理服务的用途与使用入口。src-tauri/src/services/proxy.rsstart/stop/stop_with_restore的完整实现比正文片段更全。src-tauri/src/lib.rs启动序列、recover_from_crash与恢复调用的完整上下文。src-tauri/src/commands/proxy.rs前后端命令边界看清「拒绝停止」等前端可见行为从哪来。CHANGELOG.md定位v3.9.0-1/v3.9.0-2两条自动启动相关变更的原始记录。【免费下载链接】cc-switchA cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build Hermes Agent. Only official website: ccswitch.io项目地址: https://gitcode.com/GitHub_Trending/cc/cc-switch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考