修复(P0): 版本解析统一 + ws early-data + 下载完整性校验 + naive 漂移提示#10
Closed
Sway-Chan wants to merge 22 commits into
Closed
Conversation
阻止 QUIC 的 reject(UDP 443) 规则原本只放在路由末尾,但 smart 模式下 "google 关键词→代理"和"geosite-!cn→代理"两条规则在它之前且是终止规则, 国外 QUIC 在被路由到代理时即终止,走不到末尾的 reject —— 导致 smart 模式 下 Block QUIC 只拦到不在任何 geosite 列表里的未知域名 QUIC。(global 模式 无 geosite 规则,末尾 reject 可兜全部,不受影响。) 改为给每条"→代理"规则各配对一条放在其之前的 UDP-443 reject,使代理向 QUIC 在被路由前就回退 TCP;CN 直连规则(geosite-cn/geoip-cn)不配对,故 CN/直连 QUIC 保持直连不拦。末尾兜底 reject 保留(global 模式 + smart 模式 未被 google/!cn 命中的代理向 QUIC)。三条 reject 共用新增的 blockProxyQuic 协议 guard(跳过 hysteria2/tuic,其传输走 QUIC dial-server)。 注: 上游合并 dododook#45 时未包含此 smart 模式修复,故对 v3.5.1 重新提交。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
两个问题合并修复:
1) 订阅解析丢节点(数据缺失 bug)
SubscriptionService 的 sing-box outbound 白名单 SUPPORTED 漏掉 naive,
导致订阅里的 naive 节点被静默跳过——真实订阅(29 个 naive 节点)拉取后
全部消失。补入 naive 分支:解析 username/password/tls,并按 quic 字段
推导 useHttp3。
2) naive HTTP/3(QUIC)支持
sing-box 的 naive outbound 通过 quic:true 走 h3(QUIC/UDP) 传输,对应
服务端 `--listen=quic://`;此前 FlowZ 从不下发该字段,naive-h3 节点被
当成 H2/TCP 拨号。新增 NaiveSettings.useHttp3:
- 生成侧:useHttp3 时给 naive outbound 注入 quic:true;
- blockQuic guard:naive-h3 属 UDP-transport(QUIC dial-server),
reject UDP443 会经 strict_route 回流误杀其传输,故并入跳过集合
(原仅 hysteria2/tuic)。抽出 isUdpTransportNode 统一判定;
- UI:naive 表单新增 HTTP/3 开关 + 中英文案;
- 类型:ServerConfig.naiveSettings、SingBoxOutbound.quic、订阅侧
SingboxOutbound.username/quic。
验证:sing-box 1.13.3 check 差分确认 naive 的 quic 为合法 bool 字段
(unknown-field / 类型错误对照),quic:true 配置解析通过;tsc/eslint/
prettier/vite build 全绿,无新增类型错误。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
根因:blockQuic guard 原以"拨号传输"(isUdpTransportNode: hy2/tuic/naive-h3)判定跳过, 混淆了两个正交属性——决定"代理向 QUIC 会不会黑洞"的是"节点能否中继客户端 UDP",而非 节点拨号用什么传输。naive 拨号虽可为 h3(QUIC),但它只过 TCP(HTTP CONNECT)、不能中继 客户端 UDP,被错误归入"UDP-transport 跳过"集 → 开 blockQuic 时其代理向 QUIC 仍走代理 → naive 收不了 → 黑洞(影响用户 29 个 naive-h3 节点)。 实测定论(netns 隔离 TUN,auto_route+strict_route,抓包差分): - 带 `reject udp443` 时 hy2 拨号包仍正常逸出(14 vs baseline 10); - 客户端 udp443 被 reject(egress 0 vs 控制 udp444 1)。 证伪 PR-1"reject 经 strict_route 回流误杀拨号"的假设——拨号是 sing-box 进程自有 fwmark socket,受 auto_detect_interface 保护、绕过 route 规则,reject 只作用于 tun-inbound 客户端流。 改为按 isUdpRelayCapable 分流: - 不能中继(ssh/http/anytls/naive;naive 仅在 udp_over_tcp 下可中继,FlowZ 不下发该字段): 恒 reject 全部代理向 UDP(逼 QUIC 回退 TCP、其余 UDP 快速失败而非穿透 final 跌 direct 假死 30s); - 能中继(hy2/tuic 原生;vless/vmess/trojan/ss 经 UoT):仅 blockQuic 开时 reject 代理向 QUIC(udp443)。 连带修两个 bug: 1. naive 漏在原 tcpOnly 集 ['ssh','http','anytls'] → 代理向 UDP 黑洞;补入。 2. tcpOnly 全-UDP reject 原仅在路由末尾,smart 模式下 geosite-!cn→代理 为终止规则先命中、末尾 reject 够不着 → 给全-UDP reject 加 smart 配对(每条 →代理 之前),与 PR-1 对 blockQuic 的修法一致。 本提交取代 PR-1/PR-3 引入的 isUdpTransportNode 临时 guard;naive 生成侧注释同步更正。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 把"节点域名/IP → direct"豁免从"仅选中节点"扩展到"全部节点",使切换节点 / detour 前置 代理无需重生成配置即被豁免(为无缝切换铺路)。 - CDN 安全:域名节点仅用 domain + domain_suffix(靠 sniff 出的 SNI 精确匹配节点域名), 去掉过宽的 domain_keyword(会误匹配任意"含该域名串"的无关域名);不把域名预解析为共享 CDN IP(共享 IP 加直连会误伤同 IP 的被墙站点、且抗不住 IP 轮换)。仅 IP-literal 节点用 ip_cidr 排除(专用 IP、安全)。 已知遗留(待真机验证,已在代码注释标注):Windows TUN 的 resolvedIps 预解析仍把域名节点 解析为 IP 加 route_exclude_address——即 Windows 上的 CDN 风险点。是否可删需 Wintun 真机验证 "域名规则 + sniff 是否足以断回流环";若 Windows 确需 IP 级兜底,应仅对 IP-literal 节点保留。 无 Windows 环境暂不擅改。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
落实设计纠正——「禁 QUIC」的实现应只有一条 udp443 reject,且与选中节点无关。 删除的非预期实现: - rejectProxyUdpAll(reject 全部代理向 UDP):超出「禁 QUIC(443)」范围,会误砍能中继节点的 游戏/WebRTC 等非 QUIC UDP; - isUdpRelayCapable / TCP_ONLY_PROTOCOLS 节点分类; - 连带:blockQuic 不再跳过 hy2/tuic——concern-A 实测已证伪「reject 杀拨号」,无需跳过。 实测依据(netns 隔离 TUN,零宿主改动):UDP 路由到不能中继的出站时,sing-box 在出站层直接 拒绝(日志 "UDP is not supported by outbound"),不漏 direct、不黑洞。故非 QUIC 的代理向 UDP 无需路由层按节点固化 reject——FlowZ 旧注释「穿透 final 跌到 direct 假死 30s」系未验证的错误 前提(与 concern-A 同源)。 收敛后: - blockProxyQuic = config.blockQuic === true && !!selectedServer(节点无关); - smart 模式每条 →代理 前配对、以及末尾兜底,均只 reject udp443; - hy2/tuic 自身 QUIC 拨号受 fwmark 保护、不受影响(已实测)。 红利:路由配置与选中节点解耦 → 为 PR-5 selector 跨协议无缝热切换消除规则失配隐患。 本提交取代 PR-2 的「中继能力分类」模型(PR-2 未推送,最终提交时建议与 PR-1/PR-2 合并为一条 「节点无关 Block QUIC」PR)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
切节点从「重启 sing-box(断流)」改为 clash_api 热切换 selector,默认不断流(优雅切换)。 出站生成(generateOutbounds): - 改为生成【全部】节点出站(逐节点 try/catch 防单点拖垮)+ 一个 selector(tag=proxy-selector, 列出全部节点、default=当前选中、interrupt_exist_connections=用户开关)。 - 路由 final 与「→代理」规则、DNS detour 统一指向 proxy-selector,与选中节点解耦——PR-2 简化已 使 UDP 规则节点无关、PR-4 已豁免全节点域名,故跨协议热切换零规则失配。 切换链路: - CONFIG_CHANGED 监听 → switchMode 智能分派:当且仅当唯一变化是「切到运行中 selector 内的节点」 → clash_api `PUT /proxies/proxy-selector` 热切换、持久化 selectedServerId、不重启;失败退回重启; 其余变化(模式/端口/TUN/规则/节点集合/interrupt 开关)仍重启。canHotSwitch 严格校验。 开关: - UserConfig.interruptConnectionsOnSwitch(默认 false=优雅,连接保留至自然关闭)→ selector.interrupt_exist_connections;高级设置新增开关 + 中英文案。 连带修复 / 一致性: - 修复旧代码多跳 detour 链断裂(旧逻辑仅设选中节点 detour、链中节点 detour 丢失)→ 逐节点设 detour + 内联环检测(成环跳过、避免 sing-box 循环引用启动失败); - 删除因此变为死代码的 getDetourChain; - Shadow-TLS 节点反查改为按 idToTagMap 名称匹配(适配全节点名 tag); - 校正 blockQuic UI 与节点无关后端一致:移除对 hy2/tuic 的禁用(isQuicNode useMemo)+ 文案"不适用 Hysteria2/TUIC"(concern-A 实测已证伪跳过依据)。 验证:sing-box check 通过 selector(interrupt false/true 两态);tsc/eslint/prettier/vite build 全绿;渲染 tsc 错误数维持基线 10、PR-5 改动文件零新增类型错误。 注:「热切换真不断流」属运行时功效,需真机验收(本机无 GUI/host-TUN)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 个抗封特性的「类型 + 订阅解析 + 生成 + sing-box check」(运行时抗封效果需真机验收): - ECH(tls.ech.enabled):隐藏 SNI; - Multiplex(multiplex,h2mux/smux/yamux + padding):reality+vision(xtls-rprx-vision) 与 mux 不兼容 → 生成侧 guard 自动跳过; - TLS Fragment(tls.fragment):切分 ClientHello 抗 SNI-DPI。提供全局开关(高级设置,对所有 TLS 节点生效)+ 每节点字段; - httpupgrade 传输(transport.type=httpupgrade,复用 ws 的 path/Host); - Hy2 端口跳跃(server_ports/hop_interval,支持多段逗号分隔范围)。 实现: - 生成统一后处理 applyAntiCensorshipOptions(ECH/每节点 Fragment/Multiplex/Hy2 跳跃),避免散落到 各协议的 tls 构建点;httpupgrade 在 generateTransportConfig;全局 Fragment 在 generateOutbounds 后处理所有 TLS 节点出站。 - 订阅 SubscriptionService 解析以上字段,使订阅节点自动携带(主入口)。 字段确切性:5 项逐一 sing-box check 验证合法字段(确认是 tls.fragment 而非 outbound 级 tls_fragment); 五合一 check(含在 PR-5 selector 内)通过。 UI 覆盖说明:全局 TLS Fragment 开关有完整 UI(纯客户端、非订阅来源);ECH/Multiplex/httpupgrade/ Hy2-跳跃 经订阅自动解析+生成已完整可用,手动节点表单 UI 作为后续补充(用户节点来自订阅)。 验证:tsc/eslint/prettier/JSON 全绿;渲染 tsc 维持基线 10;PR-6 文件零新增类型错误。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HIGH
- [H1] app/custom 分流的「→代理」终止规则补配对 udp443 reject(原 blockQuic 对这些域名失效);
- [H2] 托盘切节点改走 switchMode(原直接 stop+start 硬重启断流);
- [H3] cache_file 会持久化 selector 的 clash_api 选择、重启后覆盖 config default(已实测证实)→
启动后用 clash_api 把 selector 校正回 config.selectedServerId,让 FlowZ 配置成单一真值。
MED
- 节点域名绕过去掉裸 apex:domain_suffix 仅 `.${d}`,防止把共享 apex 下别的真实站点沉降到直连;
- blockProxyQuic 门改判代理可用性(proxyMode!=direct && servers.length>0),不再依赖 selectedServer
解析成功(避免选中失效但 selector 仍出流量时 QUIC 漏过);
- tls.fragment 排除 hy2/tuic/naive:实测 naive 出站拒绝 fragment 字段("fragment is not supported
on naive outbound" → 启动 FATAL,h2/h3 皆然);hy2/tuic 的 TLS 在 QUIC 内、无 TCP ClientHello;
- multiplex 的 vision guard 只看 flow 含 'vision'(去掉 reality 条件——普通 TLS+vision 同样与 mux 不兼容);
- 仅含 server_ports 的 Hy2 端口跳跃节点不再被订阅解析丢弃(从首段范围低位推导 port);
- canHotSwitch 对 servers 按 id 归一化比较(订阅更新仅重排节点不再触发多余重启)。
LOW
- clash_api PUT 加 2s 超时(AbortSignal.timeout);stop() 清 currentIdToTagMap 防对已停 selector 误切。
验证:tsc/eslint/prettier 全绿;渲染 tsc 维持基线 10;naive-fragment、selector、五特性合一 sing-box
check 均通过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HIGH
- canHotSwitch 重写为「对齐 selectedServerId + servers 按 id 归一化后整体深比较」:
· 修「切节点 + 改某设置」同时发生时那个设置被静默丢弃——原 hasModeChanged 白名单没覆盖
blockQuic/tlsFragment/dnsConfig/allowLan/bypassLAN/mixedPort/各 TUN 子字段 等;深比较后任何
非 selectedServerId 的差异都退回重启。
· Windows TUN 一律退回重启:route_exclude_address 在 start 时按「选中+appRules」节点构建、
热切换不重生成配置无法补排除集,否则 Wintun 把节点自身出向包回捕进 TUN 成环。
· 移除被取代的死方法 hasModeChanged。
MED
- reassertSelectorSelection 每轮读最新 currentConfig.selectedServerId:避免启动窗口内用户已热切
到别的节点时、reassert 把它 revert 回启动时的旧节点;
- getUniqueTag 预占 proxy-selector/direct/block/direct-loopback 保留 tag:防用户把节点命名为这些
导致 tag 撞车启动 FATAL。
LOW
- 全节点生成失败时提前抛清晰错误(避免空 selector 触发含糊 FATAL);
- naive useHttp3Desc 文案更正:节点自身 QUIC 拨号受保护、浏览器到该节点的 QUIC 仍按 Block QUIC
处理(原文案"不受 Block QUIC 影响"误导)。
验证:tsc/eslint/prettier/JSON 全绿;渲染 tsc 维持基线 10;naive h2/h3 拒绝 fragment 已实测确认
(全 naive 排除正确)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
根因:FlowZ 的 sing-box 编进了 cronet-go 绑定,但运行时靠 purego 从「核心同目录/系统库路径」
加载 libcronet,而 resources/ 未带该库 → 选中 naive 节点必报 "cronet: library not found"、连不上。
与 PR-5 的「全节点 selector」叠加更严重:sing-box 启动会预初始化全部出站,任一 naive 节点缺库即
整体启动 FATAL(连非 naive 节点也用不了)。
打包(与 sing-box 同模式:resources 随 extraResources 的 `**/*` filter 打进安装包):
- scripts/fetch-cronet.mjs:按平台下载 libcronet(linux/win/mac-arm64/mac-x64) → resources/{平台}/,
版本 CRONET_VERSION 应与打包 sing-box 的 cronet-go 对齐(cronet 走 Chromium C API,跨小版本一般兼容);
- package.json 加 fetch:cronet,并接入 release:prepare(打包前先拉库);
- libcronet 体积大、.gitignore 不入库,由 CI/维护者 fetch 后随包打入(区别于较小的 sing-box 直接入库)。
运行时定位:
- ResourceManager 加 getCronetLibFilename / hasCronetLib / ensureCronetBeside;
- ensureWritableCore(Linux 写可写核心) 与 CoreUpdateService(核心单独更新写盘后) 都把 libcronet 拷到
核心同目录——官方 sing-box 包不含 libcronet,naive 始终依赖随 app 打包的那个;mac/win 非便携从
resources 同目录天然可见。
防 brick:缺库时 generateOutbounds 跳过 naive 节点并告警,保证用户仍能用其它协议节点。
验证:tsc/eslint/prettier 全绿;fetch 脚本 node --check 通过。运行时(libcronet 加载、naive 真连通)
需在带库的真机/打包环境验收——本机下载该原生库被安全门拦截、无法端到端实测。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HIGH - [H1] fetch-cronet 去掉 mac 目标:cronet-go 不发 darwin 预编译库(实测最新 release 0 个 dylib), 原脚本两个 mac 目标 404 → process.exit(1) 让 release:prepare 的 && 链中断、所有平台都打不出包。 mac naive 需用 cronet-go build-naive 从源码构建 libcronet.dylib,否则 hasCronetLib()=false 优雅跳过。 - [H2] 被跳过的 naive 节点仍被 app/custom 分流规则按 tag 引用 → sing-box "outbound not found" 启动 FATAL,没真正防住 brick。改为 generateSingBoxConfig 末尾统一把指向「无效出站」的 route.outbound 修正为 proxy-selector(覆盖所有死引用来源)。 MED - [M1] 选中节点是被跳过的 naive → 不再静默切到 nodeTags[0],改为明确抛错(提示选其它协议节点); - [M3] hasCronetLib 改查「sing-box 实际加载目录(dirname getSingBoxPath)」而非内置 resources 目录—— 否则 ensureCronetBeside 拷贝失败时仍误判"可用"、naive 不被跳过 → 找不到库 FATAL; - [M4] 手动替换核心 replaceManualCore 补 ensureCronetBeside,与自动核心更新路径一致; - [M5] fetch 原子写(.tmp + rename)+ 失败删残,避免截断文件被下次当"已存在"误用; - 抽出 isNodeUsable 统一判定(generateOutbounds 跳过 / M1 / H2 共用)。 验证:tsc/eslint/prettier 全绿;fetch node --check 通过;渲染 tsc 维持基线 10。运行时(libcronet 加载、naive 真连通、mac 限制)仍需带库真机验收。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
实测 sing-box 二进制:mac-arm64 含静态 cronet 符号(1219 处)、无 dlopen libcronet.dylib —— cronet 由 CGO 静态编入,naive 开箱即用、无需外部库; mac-x64 二进制未含 cronet(0 处)→ naive 不可用。linux/win 仍走 dlopen 动态库。原 hasCronetLib 在 mac 查 .dylib 必为 false → 误跳 arm64 上本可 用的 naive 节点(甚至选中时抛错)。 - hasCronetLib:darwin 改按 arch 判定(arm64=静态可用,x64=不可用), 不再查 .dylib;linux/win 维持查同目录动态库。 - fetch-cronet.mjs:更正 mac 注释(静态编入、非源码构建),mac 不在下载 范围。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
三核统一到官方 1.13.13(with_naive_outbound),消除版本漂移 (原 linux 1.13.3 / win 1.13.6 / mac-arm64 1.13.11)。mac-x64 按当前 编译保持 1.12.13(FlowZ 仅出 arm64 包;Intel naive 暂不纳入)。 libcronet:fetch-cronet 钉的 v148.0.7778.96-1 实测与 1.13.13 内置 cronet(cronet-go ade33496)字节一致(sha256 dc7293a9…),ABI 匹配, 不需改 fetch-cronet。 实测验证: - sing-box check 通过 FlowZ 1.13.x 全特性配置(selector / naive / hy2 跳端口 / tls.ech / tls.fragment / multiplex / httpupgrade / block 出站 / route action:sniff)。 - naive 出站运行时 dlopen cronet 正常(成功初始化引擎并拨号)。 - 1.13.13 按预期拒绝 inbound sniff(removed in 1.13.0),FlowZ coreVersion<1.13 分支 guard 正确,升级后仍生成 1.13.x 风格配置。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
紧接前一提交的三核统一,把 mac-x64 也升到官方 1.13.13 darwin-amd64 (with_naive_outbound + lib/darwin_amd64 静态 cronet),不再留 1.12.13。 - resources/mac-x64/sing-box → 1.13.13(静态 cronet,Intel mac naive 可用) - electron-builder.json:mac target arch 加 x64 → arm64/x64 一并打包发布 - ResourceManager.hasCronetLib:darwin 改为恒 true(两 arch 核心均静态含 cronet,naive 皆可用),移除原 arch-gate - ProxyManager:更新 1.12.x sniff 分叉注释(打包核心现全 ≥1.13.13,该分支 仅为向后兼容旧 userData 核心保留) 至此四平台核心统一 1.13.13;naive 在 mac 两 arch + linux + win 全可用。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
解决"核心更新后配置解析不支持导致 brick"的健壮性问题: - 预检(替换前):新核心二进制可执行 + 用「针对新版本生成的」FlowZ 配置跑 sing-box check;不通过则不动现役核心、代理继续运行,并标记问题版本。 - 自动回滚:更新后新核心首次启动失败(proxy 'error')→ 回滚到备份核心并以 旧核心重启;标记问题版本防再次自动安装。 - 问题版本跳过:预检/启动失败的版本记入 known-bad,checkUpdate 自动跳过 (手动更新可强制安装)。 - 备份生命周期:更新后保留旧核心备份作回滚网,新核心「首次成功运行」 (recordSuccessfulVersion)即删除备份——稳定即不再回滚、省磁盘。 - 兼容版本带闸门:默认仅自动更新到配置生成器已验证的 minor 带(≤1.13);跨 minor(如 1.14)不自动更、提示随 App 升级。设置可关(手动更新不受限)。 实现:CoreUpdateService(预检/known-bad/备份/自动回滚/带闸门)+ ProxyManager .buildPreflightConfigJson(按目标版本生成预检配置,复用 coreVersion 分叉)+ index 'error' 钩子 + UserConfig.restrictCoreUpdateToCompatibleMinor 开关 (设置 UI + 中英 i18n)。 实测:sing-box check 正确接受 1.13.x 全特性配置、拒绝跨版本不兼容字段 (inbound sniff);build:main / vite build / tsc / eslint / prettier 全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- #1 版本带闸门 parseFloat 误判(HIGH):改整数编码 major*1000+minor 比较, 修 "1.100"→1.1 漏放、"1.9"→1.9 误判等多位 minor 塌缩问题。 - #2 自动回滚被自动重启风暴延迟(HIGH):待验证窗口内 ProxyManager 抑制自动 重启(setAutoRestartSuppressed),新核心首次异常退出立即上报 error → 立即 回滚,而非在已知坏核心上空转 MAX_RESTART_COUNT 次后才回滚。 - #3 1s 假成功删备份致 brick(HIGH):'started' 仅代表存活 1s,不再立即删 备份;改启动 30s 稳定观察期,期内无 error 才删;期内崩溃→回滚(备份仍在)。 - #4 陈旧 pending 误回滚(MED):pending 加更新时间戳,超 5 分钟未成功运行的 error 不再触发回滚(视为与本次更新无关,如用户期间改了配置)。 - #7 问题版本永不解除(LOW):版本成功运行后从 known-bad 移除,误标记可恢复。 - #9 exec 注入面(LOW):预检改 execFile(无 shell),消除引号转义类问题。 - 附带(review 外自查):预检在备份之前,原 catch 的 restoreBackup 可能误恢复 陈旧 .bak 致静默降级 → 加 backupMade 守卫,仅本次确已备份才恢复。 tsc/eslint/prettier 全绿;encodeMajorMinor 各边界(1.13/1.14/1.20/1.100/1.9/ 2.0)实测判定正确。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ProxyManager: 修正 sniff 版本分叉处的陈旧注释(核心统一后仅 mac-x64 留 1.12.13,非 Windows);代码本身按实测 coreVersion 分叉,行为不变。 - naive-form: Switch checked 加 `?? false` 防御 useHttp3 为 undefined(受控 组件健壮性;运行时 getDefaultValues 已兜底,纯防御)。 完整 review(4 域并行)结论:0 HIGH / 0 MED;其余 LOW 为可接受的注释/简写。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 功能特性新增:无缝切换节点、Block QUIC、抗封增强(ECH/Multiplex/ Fragment/httpupgrade/Hy2 端口跳跃)条目 - 新增「🛡 抗封 / 切换 / NaiveProxy 说明」章节 - NaiveProxy 说明:mac(arm64 与 x64)cronet 静态编入、开箱即用;linux/win 走动态库随包打入。同步 Intel mac 现已提供 x64 预构建包(核心统一 1.13.13) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
特性: TUN 路由修复 + naive HTTP/3 + 域名绕过 + 无缝切换 + 抗封增强
特性: NaiveProxy 核心库打包 + sing-box 核心统一 1.13.13(含 mac x64)
文档: README 补充新特性说明
主线(根因):配置生成层用 parseFloat("1."+minor) 判断版本带,把 minor 当小数
——"1.9"→1.9 被误判 >1.13、"1.20"→1.2 被误判 <1.13。而核心更新层早已用整数编码避坑。
本次把两边统一到单一权威 src/main/utils/version.ts。
1. 版本解析统一(主线)
- 新增 utils/version.ts:encodeMajorMinor(major*1000+minor,容忍 v 前缀/后缀)+
coreVersionAtLeast(未知→现代,匹配打包核 ≥1.13.13 与 getCoreVersion 的 1.13.0 兜底)。
- ProxyManager 6 处版本门(含一处复用 versionNum 的下游站点)改用 coreVersionAtLeast。
- CoreUpdateService 删私有 encodeMajorMinor,改用共享版(消除双实现)。
- 真实版本映射完全一致,额外修正 1.9/1.20 误判;6 站点统一未知→现代默认(经
caller-trace 确认 coreVersion 在生成时恒为真实版本,旧 legacy 兜底为死路径)。
2. ws early-data 落配置:generateTransportConfig 补 emit max_early_data /
early_data_header_name(订阅/分享链已解析存入 wsSettings,此前未落运行时配置致静默失效)。
3. 下载完整性校验:downloadFile 累计字节与 Content-Length 比对,截断下载经 handleError
自动换镜像重试一次,拦截不完整包流入 extractCore。
4. naive 漂移提示:核心更新后若存在 naive 节点,提示 naive 依赖随 app 打包的 libcronet、
跨版本更新可能 ABI 漂移(该漂移运行时 dlopen 才暴露,sing-box check 预检无法发现)。
build:main + eslint 通过;独立 review 全绿(语义保形经 caller-trace 确认)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sway-Chan
added a commit
that referenced
this pull request
Jun 24, 2026
…修 + 3 取舍 + probing 死链清理) dododook#174 引入: - 缓存污染(#3/#10/dododook#15):state 文件兜底的乐观 loggedIn 用 skipCache 不进登录缓存(缓存只存 STATUS 真值),免 revoked/过期 key 的 state 残留固化误显已连接 - 瞬态登录核 authUrl 清理(#5):空 authUrl 下沉 finalize + loginCompleted 守卫,覆盖取消/崩溃/超时 - 设置弹窗文案(#8):去掉错误的「不自动重启」承诺(TS 设置改保存确会 switchMode 重连) - EVENT_TAILSCALE_STATE_CLEARED(#4):reconcile 删 root state 后 main 发事件、渲染端清缓存+登录态,免撕裂 已合入 dododook#163–dododook#173 回归: - win-tun 用 powershellPath(#1,PATH 劫持 fail-open→dododook#159 释放门 no-op) - ProxyManager startInternal 补 win32 换核重解析(#2) - StatsService OOM eviction 改 LRU 近似(#7,免删健康长连接) - helper chownTree 加 uid==0 guard(#12);helper upgradeable 跨窗口重发(#14) probing 死链清理(批3 删自动探针后的全链死代码):probe 字段/setter/检测中 badge/handler 分支/PROBE channel+handler/probeTailscaleLoginStates+killTailscaleStatusProbe+tailscaleStatusProbe+emitTailscaleStatus probe 参数/buildTailscaleStatusProbeConfig+单测。 取舍(核实后不改,有据):#6 删 state 是 v5 非提权恢复手段、#9 tailscaleApiPort 每次新分配临时端口免撞、#13 dododook#168 已知 TS-H2 预存限制。 gate:tsc(main+renderer)+eslint+jest 1838 + go vet 全绿。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
独立 review 发现的 P0 修复,主线为版本解析统一(根因)。
根因主线
配置生成层(ProxyManager)用
parseFloat("1."+minor)判断<1.13 / ≥1.13版本带,把 minor 当小数:1.9→1.9 被误判 **>**1.13、1.20→1.2 被误判 **<**1.13。核心更新层早已用整数编码避坑——本次统一到单一权威src/main/utils/version.ts。改动
utils/version.ts(encodeMajorMinor+coreVersionAtLeast);ProxyManager 6 处版本门改用之;CoreUpdateService 删私有重复实现。真实版本映射完全一致,额外修正 1.9/1.20 误判;未知→现代默认经 caller-trace 确认安全(生成时 coreVersion 恒为真实版本)。generateTransportConfig补 emitmax_early_data/early_data_header_name(订阅已解析存入 wsSettings,此前未落运行时配置致静默失效)。downloadFile字节数与 Content-Length 比对,截断包经 handleError 自动换镜像重试,拦截不完整包流入 extractCore。验证
npm run build:main+ eslint 通过;独立 agent review 全绿(语义保形经 caller-trace 确认、Content-Length 重试有界、early-data 字段名正确)。🤖 Generated with Claude Code