dsh-proxy-failover:TUN fake-ip 下的 web_fetch 与代理关闭后的同请求直连

前言

在 Mihomo / Clash 等工具开启 TUN 与 fake-ip 时,DeepSeek Harness(DSH)里的 web_fetch 常会先本地解析域名,再校验地址是否为公网 IP。fake-ip 返回的是合成地址(例如 198.18.0.22),校验失败,连接根本不会发出,报错类似「hostname resolves to a non-public IP address」。

社区里已有插件(如 dsh-web-fetch-proxy)会把本地 HTTP 代理装成 @deepseek-ai/dsh-http-proxy 的进程级路由,让 web_fetch 走「已代理」路径、跳过本地 DNS 校验,抓取可以恢复。但若之后关掉代理软件、又不重启 DSH,那条仍指向 127.0.0.1:7890 等端口的进程级路由不会消失,模型 API、dsh-llm-retry 的重试都会继续拨一个已无人监听的端口,直到整轮任务中断。

下面介绍维护者 PILIHU2022 的 dsh-proxy-failover(目录分类:联网工具):用本机回环 relay 作为进程级代理路由,保留 fake-ip 场景下的 web_fetch 修复,并在上游代理不可用时于同一请求内改走直连,代理恢复后自动切回。

这是什么

dsh-proxy-failover 是 DSH 扩展插件,npm 包名 dsh-proxy-failover,设置页显示名「代理容错出网」。当前版本 0.1.0,许可证 MIT,仓库:https://github.com/PILIHU2022/dsh-proxy-failover。

它不再把真实代理地址直接写进全局 dispatcher,而是在 127.0.0.1 上启动小型 HTTP relay,把 relay 地址注册为进程级出站路由。每次请求由 relay 决定:上游可达则隧道到 Mihomo / Clash 等本地代理(域名由代理侧解析,fake-ip 问题照旧被绕过);上游不可达则在同一次请求里直连,并标记上游故障,避免后续请求反复连接死端口。

症状与根因(简要)

开着 TUN + fake-ip 时,web_fetch 可能报:

URL hostname "raw.githubusercontent.com" resolves to a non-public IP address

根因有两层叠在一起:fake-ip 导致本地 DNS 校验失败;若用「写死真实代理」的进程级路由,路由生命周期与 DSH 进程绑定,代理关闭后路由仍指向旧端口,所有经全局 dispatcher 的出站(含模型 API)都会 ECONNREFUSED 并触发重试失败。

本插件用「永远在线」的回环 relay 替代「随代理进程生死」的上游地址作为路由目标,只在上游段做可用性判断与切换。

核心功能

  1. 进程级 relay 路由:与 @deepseek-ai/dsh-http-proxy 配合,proxyRouteFor() 指向 relay 时 web_fetch 仍视为已代理,跳过公网 IP 校验。
  2. 同请求故障回退:连接上游失败时,同一 HTTP 请求内改直连,无需等模型层多轮重试死端口。
  3. 后台探活与自动切回:默认每 1500 ms 探测上游;代理晚于 DSH 启动或关闭后再打开,可自动重新接管。
  4. 代理失联时关旧隧道:减少连接池复用已断开的代理隧道。
  5. 直连 DNS 双路径:OS 解析与 c-ares 并行,减轻 TUN 刚关闭时系统解析卡住导致的 resolveTimeoutMs(默认 5000 ms)拖满。
  6. 上游自动发现:proxy: auto 时按显式配置 → 环境变量 HTTPS_PROXY / HTTP_PROXY / ALL_PROXY → Clash / Mihomo 配置中的 mixed-port → 常见端口等顺序发现;端口探测结果会额外做 CONNECT 握手,降低误识别。
  7. 设置页与状态接口:设置 → 常规 中「代理容错出网」可查看 relay、上游、切换次数等;宿主提供 http://127.0.0.1:3080/proxy-failover/status 等接口(仅回环)。

与 dsh-web-fetch-proxy 的关系:两者都会安装进程级路由,不要同时启用。README 推荐保留本插件并禁用 dsh-web-fetch-proxy;本插件在 fake-ip 修复之上补全代理关闭后的直连与自动恢复,功能上为其超集。

安装与启用

环境要求(来自 package.json):Node ^22.19.0 || >=24.0.0,DSH >=0.2.0-rc.2 || >=0.2.1-rc.2;可选 peer @deepseek-ai/dsh-http-proxy。

从 GitHub 安装(npm 尚未发布时的官方方式):

dsh plugin --profile web add github:PILIHU2022/dsh-proxy-failover

本地开发目录安装:

dsh plugin --profile web add link:/path/to/dsh-proxy-failover

安装后 bundle patch 会插入类似配置(proxy: auto 为默认):

- insert:
    - id: proxy-failover
      name: dsh-proxy-failover
      config:
        proxy: auto

更新插件代码后需重新启用该行或重启 DSH 以加载新代码。首次出现设置页项需要重启一次 DSH(Web 客户端包扫描对非客户端包有负缓存;宿主状态接口不依赖该重启)。

配置要点

部署级字段在 cordis.patch.yml 的 config 中,常用项如下:

字段 默认 说明
enabled true false 时不介入,保持宿主原有路由
proxy auto 自动发现;off / none / direct 强制直连;也可写死 http://127.0.0.1:7890
noProxy "" 追加 no_proxy,逗号分隔;回环始终绕过
probeIntervalMs 1500 上游探活间隔
listenPort 0 0 为随机端口,仅监听 127.0.0.1

完整字段见仓库 README。若希望代理关闭时连流式中断后的那一次 dsh-llm-retry 也尽量避免,可将模型 API 主机名加入 noProxy(例如 api.deepseek.com),使该流量不经本地代理。

典型用法与排障

读取运行状态(仅本机):

curl -s http://127.0.0.1:3080/proxy-failover/status | jq

重新探测上游:

curl -s -X POST -H 'content-type: application/json' \
  --data '{"action":"redetect"}' http://127.0.0.1:3080/proxy-failover/api

判读示例:routed: true 表示全局路由已指向 relay;upstreamUp: false 且 fallbacks > 0 表示近期请求已走直连。日志中可见「上游代理已启用 / 不可用,后续请求自动直连」等迁移信息。

仓库提供的验证命令(需在克隆后的插件目录执行,此处仅作参考路径说明):

node --test test/*.test.mjs
node scripts/verify.mjs

适用场景与注意

适合:日常用 Mihomo / Clash TUN + fake-ip,又需要在 DSH 里稳定使用 web_fetch、MCP、搜索等出站能力,且会频繁开关本地代理、不希望因此拖死整轮对话的用户。

务必知晓:

  • 插件以当前 DSH 进程权限运行,relay 只监听 127.0.0.1;安装前请自行审阅源码与 MIT 许可证。
  • 仅支持 http(s):// 上游代理,不支持 SOCKS / PAC;上游 Basic 认证仅支持写在代理 URL 中。
  • 代理在响应传输中途被杀死时,已建立的隧道仍会断一次;正在流式返回的请求在代理关闭瞬间无法迁移到直连,dsh-llm-retry 可能仍会重跑该步(README 说明了与 noProxy、c-ares 兜底相关的优化)。
  • DSH 社区插件目录为独立站点(如 https://www.skillhub.cn/plugins/PILIHU2022/dsh-proxy-failover),与 DeepSeek / 幻方无官方从属关系;安装命令以仓库 README 为准。

结尾

dsh-proxy-failover 把进程级出站路由从「指向可能消失的代理端口」改为「指向本机常在线的 relay」,在保留 TUN fake-ip 下 web_fetch 修复的同时,解决代理关闭后模型与工具链对死端口反复重试的问题。需要详情、配置表与安全说明时,以 GitHub README 为准。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye