83dd5369ab
M4 URLTest 自动选线:
- ClashApiClient.getGroupDelay() — GET /group/<name>/delay 触发按需测速
- ClashApiClient.extractUrltestResults() — 从 /proxies 响应解析 URLTest
组成员最新延迟,填充 stats 帧的 urltestResults 字段
- DesktopKernelProcess._startStatsPoll() — 每秒并行拉取连接统计 + URLTest
延迟(_clashApi.getProxies()),URLTest 失败不影响主统计
- DesktopVpnBridge.selectOutbound(tag) — Clash API PUT /proxies/proxy,
tag 可传节点 tag 或 urltest 组 tag(如 "auto-select")恢复自动
- DesktopVpnBridge.getActiveOutbound() — GET /proxies 读 proxy.now
- app/kernel/poc/reality_client.config.json.tmpl — 增加 urltest("auto-select")
+ selector("proxy") 出口组,route.final 改为 "proxy"
M5 Kill-switch + 弹性重连:
- DesktopVpnBridge.setKillSwitch({required bool on}) — 将 _killSwitchEnabled
写入 TUN inbound strict_route 字段(applyKillSwitchToConfig);内核运行中
触发静默重载(kill → start(lastConfig))
- DesktopVpnBridge.applyKillSwitchToConfig() — static,killSwitch=true ↔
TUN strict_route=true,killSwitch=false ↔ strict_route=false
- 自动退避重连:kernel 崩溃推 error → _scheduleReconnect() 退避序列
1s/2s/4s/8s/16s/30s(上限),stop() 取消,重连成功后重置计数器
- DesktopKernelProcess 意外退出清理 _clashApi(避免下次 spawn 残留)
- app/pangolin/test/killswitch_checklist.md — 三端故障注入验收清单
(Desktop ✅,iOS/Android 欠账说明)
新增测试(client/test/bridge/desktop_vpn_bridge_m4m5_test.dart):
- M4: extractUrltestResults 解析/空/无 history 边界
- M4: getGroupDelay HTTP 请求格式验证
- M4: selectOutbound / getActiveOutbound Clash API 调用断言
- M5: applyKillSwitchToConfig 字段覆盖 + 无 TUN 边界
- M5: setKillSwitch 状态注入 + 同值幂等
- M5: 崩溃→error 不崩 / 首次重连在退避延迟内触发 / stop 后不再重连
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
128 lines
3.1 KiB
Cheetah
128 lines
3.1 KiB
Cheetah
{
|
||
"_comment": "sing-box 客户端配置模板 — VLESS+REALITY+TUN(PoC tsk_SLCsjNgtmng3 / M4/M5 tsk_xAQhC1xuCd8x)",
|
||
"_usage": "渲染脚本: app/kernel/poc/gen-poc-config.sh;占位符一律 __UPPER_SNAKE__",
|
||
"_m4_note": "URLTest 自动选线:auto-select(urltest) → 成员节点延迟最低者。手动切换经 Clash API PUT /proxies/proxy",
|
||
|
||
"log": {
|
||
"level": "info",
|
||
"timestamp": true
|
||
},
|
||
|
||
"dns": {
|
||
"servers": [
|
||
{
|
||
"tag": "dns-remote",
|
||
"address": "tls://1.1.1.1",
|
||
"address_resolver": "dns-local",
|
||
"detour": "proxy"
|
||
},
|
||
{
|
||
"tag": "dns-local",
|
||
"address": "local",
|
||
"detour": "direct"
|
||
},
|
||
{
|
||
"tag": "dns-block",
|
||
"address": "rcode://success"
|
||
}
|
||
],
|
||
"rules": [
|
||
{ "outbound": "any", "server": "dns-local" },
|
||
{ "geosite": "cn", "server": "dns-local" },
|
||
{ "geoip": "private", "server": "dns-local" }
|
||
],
|
||
"final": "dns-remote",
|
||
"independent_cache": true
|
||
},
|
||
|
||
"inbounds": [
|
||
{
|
||
"type": "tun",
|
||
"tag": "tun-in",
|
||
"inet4_address": "172.19.0.1/30",
|
||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||
"mtu": 1492,
|
||
"auto_route": true,
|
||
"strict_route": true,
|
||
"stack": "system",
|
||
"sniff": true,
|
||
"sniff_override_destination": false
|
||
}
|
||
],
|
||
|
||
"outbounds": [
|
||
{
|
||
"_comment": "M4: URLTest 自动选优出口组(成员:所有实际代理节点 tag)",
|
||
"type": "urltest",
|
||
"tag": "auto-select",
|
||
"outbounds": ["reality-out"],
|
||
"url": "http://www.gstatic.com/generate_204",
|
||
"interval": "3m",
|
||
"tolerance": 50
|
||
},
|
||
{
|
||
"_comment": "M4: Selector 出口组(Clash API 操作此组来手动覆盖 / 切回自动)",
|
||
"type": "selector",
|
||
"tag": "proxy",
|
||
"outbounds": ["auto-select", "reality-out"],
|
||
"default": "auto-select"
|
||
},
|
||
{
|
||
"type": "vless",
|
||
"tag": "reality-out",
|
||
"server": "__SERVER_HOST__",
|
||
"server_port": __SERVER_PORT__,
|
||
"uuid": "__UUID__",
|
||
"flow": "xtls-rprx-vision",
|
||
"tls": {
|
||
"enabled": true,
|
||
"server_name": "__REALITY_SNI__",
|
||
"utls": {
|
||
"enabled": true,
|
||
"fingerprint": "chrome"
|
||
},
|
||
"reality": {
|
||
"enabled": true,
|
||
"public_key": "__REALITY_PUBLIC_KEY__",
|
||
"short_id": "__REALITY_SHORT_ID__"
|
||
}
|
||
},
|
||
"packet_encoding": "xudp"
|
||
},
|
||
{
|
||
"type": "direct",
|
||
"tag": "direct"
|
||
},
|
||
{
|
||
"type": "block",
|
||
"tag": "block"
|
||
},
|
||
{
|
||
"type": "dns",
|
||
"tag": "dns-out"
|
||
}
|
||
],
|
||
|
||
"route": {
|
||
"rules": [
|
||
{ "protocol": "dns", "outbound": "dns-out" },
|
||
{ "geosite": "cn", "outbound": "direct" },
|
||
{ "geoip": "cn", "outbound": "direct" },
|
||
{ "geoip": "private", "outbound": "direct" }
|
||
],
|
||
"final": "proxy",
|
||
"auto_detect_interface": true
|
||
},
|
||
|
||
"experimental": {
|
||
"clash_api": {
|
||
"external_controller": "127.0.0.1:__CLASH_API_PORT__",
|
||
"secret": "__CLASH_API_SECRET__"
|
||
},
|
||
"cache_file": {
|
||
"enabled": true,
|
||
"path": "/tmp/pangolin-poc.db"
|
||
}
|
||
}
|
||
}
|