dsh-net-proxy-plugin

August 30, 2026 · View on GitHub

English | 简体中文

dsh-net-proxy-plugin

DeepSeek Harness 的本地网络代理兜底插件bundle):自动扫描系统里已有的网络代理(环境变量、Windows 系统代理、macOS/Linux 系统代理、常见本地代理端口),探测海外网站(默认 Google 204 与 GitHub)的连通性;当直连不通而某个本地代理可用时,把该代理接入正在运行的 dsh —— 进程内所有 fetch 走该代理,并同步设置 HTTPS_PROXY/HTTP_PROXY/NO_PROXY 环境变量,让 agent 启动的 git、pnpm 等子进程同样可用代理。

这是兜底方案,不是默认接管:没有可用代理或直连正常时,dsh 的行为与未安装本插件完全一致;卸载时精确恢复环境变量与全局 dispatcher。

更新日志

此处仅展示最新版本,完整版本历史见 change-log.md

0.1.0(2026-08-31)

  • 首发 即插即用,无需构建安装(仓库已提交构建产物 lib/)。

功能

  • 自动发现候选代理(优先级去重):extraProxies 配置 → 环境变量(HTTPS_PROXY/HTTP_PROXY/ALL_PROXY 及小写)→ 系统代理(Windows WinINet 注册表、macOS scutil --proxy、Linux GNOME gsettings)→ 本地监听端口(默认 7890/7897/7891/10809/10808/8888/8080,http 与 socks5 两种都试)。
  • 连通性探测:直连 + 逐候选,对每个目标发一次 GET;返回 HTTP 状态码 < 500 视为可达。
  • 自动决策activation: auto 默认):有代理能全量到达目标且直连存在失败目标 → 激活该代理;activation: always 则只要有可用代理就激活。默认每 5 分钟复检一次(reprobeIntervalMs: 0 关闭),VPN 断开自动切回直连、重开后再次激活;/net-proxy refresh 可立即重检。
  • 激活范围:进程内所有 fetch(undici 全局 dispatcher 换成按主机名路由的 RoutingDispatcherbypassHosts 内的主机仍直连;覆盖 LLM、web_search 等)+ 子进程(标准代理环境变量 + NO_PROXY)。
  • 可选的 net-proxy 提供方web_fetch 内置 web-fetch-http 提供方为安全起见自建连接、不走全局 fetch 代理;本插件提供语义一致的兼容提供方,可一键切换(见下)。
  • /net-proxy 命令:Web UI 里查看插件当前状态,/net-proxy refresh 立即重检。

实测记录

开发期间在真实环境(Windows + VPN 系统代理 http://127.0.0.1:51266)验证:插件启动后检测到直连 GitHub 失败、系统代理全量可达 → 自动激活;随后模型首次 web_fetch 直连失败、重试经代理返回 204 —— 正是「超时后重试走本地代理」的路径。dsh 日志输出 dsh-net-proxy: probe done — direct blocked, working proxies: ..., active: http://127.0.0.1:51266

单机单次记录,结果受当天网络影响;不同机器/代理环境下以插件自身的探测输出为准。

安装

仓库地址:https://github.com/minatoAI/dsh-net-proxy-plugin

插件按 bundle 方式分发,用 dsh plugin 安装进 profile(从源码 checkout 运行时用 pnpm dsh 代替 dsh):

从 GitHub 安装。仓库已提交构建产物 lib/,无 build 脚本,无需 allowBuilds 授权

dsh plugin --profile web add github:minatoAI/dsh-net-proxy-plugin

更稳妥:固定到某个 commit,避免后续推送改变实际安装到的代码

dsh plugin --profile web add github:minatoAI/dsh-net-proxy-plugin#<commit-sha>

或本地文件夹安装(开发调试用,需先构建)

pnpm install
pnpm run build
dsh plugin --profile web add ./dsh-net-proxy-plugin

安装完成后重启 dsh(新 bundle 在下次启动时生效):

dsh --profile web

安装时仅从 npm 拉取唯一运行时依赖 undici@deepseek-ai/cordis@deepseek-ai/schemastery@deepseek-ai/dsh-web 是 peerDependencies,运行时从 dsh 安装目录解析,不需要额外安装。

使用

启动后代理检测自动进行。首次探测需要几秒钟(直连 + 各候选 × 目标,每个最多 probeTimeoutMs);此窗口内发起的一次直连请求可能失败,下一次重试就会命中已激活的代理。

  • 激活成功:dsh 日志输出 dsh-net-proxy: activated <url> (system, ...)probe done 摘要。
  • Web UI 输入 /net-proxy 查看当前状态(激活的代理、来源、激活时间、直连可达性),/net-proxy refresh 立即重检。

配置

在 profile 的 cordis.patch.yml 里覆盖 net-proxy 行:

- id: net-proxy
  config:
    targets: [https://www.google.com/generate_204, https://github.com]
    probeTimeoutMs: 5000
    reprobeIntervalMs: 300000
    bypassHosts: [localhost, 127.0.0.1, ::1, api.deepseek.com, "*.deepseek.com"]
    extraProxies: [http://127.0.0.1:7890]
    probePorts: [7890, 7897, 10809]
    activation: auto
字段默认说明
enabledtrue总开关
targetshttps://www.google.com/generate_204, https://github.com连通性探测目标
probeTimeoutMs5000单次探测超时
reprobeIntervalMs300000周期复检间隔;0 关闭
bypassHostslocalhost, 127.0.0.1, ::1, api.deepseek.com, *.deepseek.com激活后仍直连的主机(精确 / *.domain 通配 / *
extraProxies[]手动指定的代理,优先于自动发现
probePorts7890, 7897, 7891, 10809, 10808, 8888, 8080扫描的本地端口
activationautoauto:直连失败才启用;always:有可用代理即启用
fetchTimeoutMs30000net-proxy web 提供方超时
maxResponseBytes5000000net-proxy web 提供方响应上限
maxRedirects5net-proxy web 提供方同源重定向上限

让 web_fetch 工具也走代理

web_fetch(dsh 内置 web-fetch-http 提供方)为安全起见自建连接、固定公网 IP,不经过全局 fetch 代理。本插件提供语义一致的 net-proxy 提供方(WEB_FETCH_TIMEOUT/WEB_ABORTED/WEB_REDIRECT_BLOCKED/WEB_BLOCKED_URL/WEB_FETCH_TOO_LARGE 等错误码与内置一致;走代理时公网 IP 校验由你选用的代理承载)。在你的 profile 的 cordis.patch.yml 里改一行即可启用:

- id: web
  config:
    searchProvider: deepseek-official
    fetchProvider: net-proxy

不配置时 web_fetch 维持原有的直连安全路径,不受本插件影响。

网络与代理(中国大陆用户)

插件读取的是系统里已有的代理,不内置任何代理服务。VPN/代理开启时自动发现并复用;VPN 重启换了端口也会在下一次复检(默认 5 分钟)或 /net-proxy refresh 后自愈。请只使用你信任的本地代理 —— 代理会看到并转发你通过它的全部流量;插件不会修改 Windows 系统代理设置,仅影响 dsh 进程及其子进程的网络访问。

卸载

dsh plugin --profile web remove dsh-net-proxy-plugin

仓库结构

dsh-net-proxy-plugin/
├── package.json       # manifest: "dsh": { "bundle": {"patch": ...} };构建产物 lib/ 随仓库提交
├── cordis.patch.yml   # 组合层:单个行 net-proxy(name: dsh-net-proxy-plugin)
├── src/               # TypeScript 源码(NodeNext ESM,strict)
│   ├── index.ts       # 插件主体:配置 Schema、探测调度、激活决策、提供方/命令注册、生命周期
│   ├── candidates.ts  # 候选发现(配置→环境变量→系统代理→本地端口);系统代理读取(WinINet/scutil/gsettings)
│   ├── probe.ts       # 直连/逐代理连通性探测
│   ├── dispatcher.ts  # undici 代理 dispatcher 与按主机名路由的 RoutingDispatcher
│   ├── activate.ts    # ProxyActivation:全局 dispatcher 与代理环境变量的激活/停用/切换恢复
│   ├── bypass.ts      # 主机匹配(精确 / *.domain 通配)与 NO_PROXY 值生成
│   └── fetch-provider.ts # 可选的 net-proxy WebFetchProvider(同源重定向/大小上限/超时/错误码)
├── lib/               # 构建产物(已提交,GitHub 安装开箱即用)
├── tests/             # vitest:40 个用例(解析/路由/探测/激活/提供方/系统代理集成)
├── README.md          # 简体中文说明(本文件)
├── README.en.md       # English README
├── change-log.md      # 完整版本历史(简体中文)
└── change-log.en.md   # 完整版本历史(English)

开发说明

  • 组合层遵循 dsh 约定:单个行 net-proxyname 为精确包名 dsh-net-proxy-plugin(client-modules 扫描按行名定位根 manifest)。
  • 插件 inject 为空:探测调度与激活不依赖任何服务,启动即工作;web/commands 为可选服务,通过 ctx.inject 注册提供方与命令 —— 组合里没有对应服务时相关功能自动不注册,不会造成 loader 启动挂起。
  • 所有注册都是 effect(ctx.effect/yield 返回 disposer),生命周期 effect 负责清理复检定时器并 activation.dispose()(恢复全局 dispatcher 与代理环境变量)。
  • src/ 相对导入带 .js 后缀(tsc NodeNext 输出要求);tsconfig.json 开启 strictnoUncheckedIndexedAccessexactOptionalPropertyTypes

测试

pnpm install
pnpm test       # vitest:40 个用例
pnpm run build  # tsc 输出 lib/
dsh plugin check .   # 用 dsh 校验 patch 层与插件挂载(从 fork 源码运行时用 pnpm dsh)

测试使用本地 mock 代理(HTTP 绝对形式 + CONNECT 隧道)与真实目标服务器路由,覆盖候选解析、主机匹配/NO_PROXY、RoutingDispatcher、探测/决策、激活/停用/切换恢复、WebFetchProvider 契约等 40 个用例。

注:dsh plugin check 会提示 unknown inject dependencies: commands —— 这是检查工具自身已知服务清单未收录 commands 的提示(组合中实际存在),不影响安装与运行。