DSH HUB
HomePlugin StoreRankingsPublish Guide
Plugin source
Back to catalog

khiqwq /

dsh-system-proxy

Verified

DSH host plugin - smart outbound HTTP(S) routing: named proxies (http/https/socks4/4a/5/5h), per-host/provider/plugin rules, direct-first fallback with health memory (global fetch + node http/https)

★ 4 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@35c49152

dsh-system-proxy

DeepSeek Harness(DSH)的出站网络路由插件。它在 DSH 主进程中统一接管 fetch 与可选的 node:http / node:https 请求,根据目标主机及显式调用上下文,在直连、HTTP(S) 代理和 SOCKS4/4a/5/5h 代理之间选择线路;支持直连优先的故障回退、逐跳重定向重新决策、每主机健康记忆,以及内网/云元数据端点的默认保护。

当前版本已实现下述全部能力并通过 npm test(81 项断言 + 真实 Cordis loader 挂载/热重载验证)。

目标能力

  • HTTP、HTTPS、SOCKS4、SOCKS4a、SOCKS5、SOCKS5h 代理;
  • 多个具名代理(named proxies);
  • 按 host、provider、plugin 路由;
  • direct、proxy、fallback、block 四种动作;
  • 直连失败或延迟过高时使用代理保底;
  • 每目标 EWMA 延迟、失败冷却(cooldown)与健康记忆;
  • 显式 provider/plugin 上下文,不伪造调用方身份;
  • 代理凭据脱敏、安全日志和热重载恢复。

工作原理

插件加载后:

  1. 包装 globalThis.fetch,按规则选择直连或代理 dispatcher;
  2. patchNodeHttp: true 时包装 node:http / node:https 的 request 与 get;
  3. 用 AsyncLocalStorage 传递显式 provider / plugin 信息;
  4. 为 fallback 目标保存直连 EWMA 延迟及失败冷却;
  5. 插件销毁或热重载时还原自己安装的包装器,并关闭代理 Agent。

这是进程级全局补丁。同一进程内不应同时加载多个相互竞争的传输包装插件;本插件在还原时只恢复仍指向自身包装器的全局对象,不覆盖后来安装的包装器。

安装

源码仓库:https://github.com/khiqwq/dsh-system-proxy

从源码安装:

git clone https://github.com/khiqwq/dsh-system-proxy.git
cd dsh-system-proxy
npm install
npm test
npm run build
npm run client-test

cd "$env:DSH_HOME\profiles\<name>"
npm install <dsh-system-proxy 源码目录的绝对路径>
# 将 "dsh-system-proxy" 加入 package.json 的 dsh.profile.bundles

发布后可使用:

dsh plugin --profile <name> add dsh-system-proxy

本包声明了 dsh.bundle.patch,安装为 DSH profile bundle 后会提供 system-proxy loader entry。

配置

交互式配置

安装到带有 Settings 服务的 DSH profile 后,插件会以 system-proxy 命名空间注册交互式配置,并通过包内 ./client Web bundle 在 DSH Settings → 插件页面显示可折叠的 系统代理 / System Proxy 配置卡。卡片沿用官方 PluginCard 的紧凑层级、原生 DSH CSS variables、field grid、状态 badge 与 primary/secondary actions;中文 locale 自动显示中文,其他 locale 使用英文 fallback。双语词典由 @deepseek-ai/dsh-client-locale 注册并实时订阅切换。卡片可编辑基础开关、代理 URL、passwordRef 和只写密码;所有输入先保存在浏览器本地草稿中,折叠不会丢失草稿,只有回车或点击 保存并加密 / Save and encrypt 才写入。URL 是普通可见 endpoint 字段并明确拒绝 user:pass@ userinfo;真正的密码输入使用 type=password。命名空间不可用时卡片不会出现;只读状态和保存失败会在卡片内明确显示。保存后插件会串行停止旧传输包装并按最新配置热重装,无需手工编辑 patch 或重启主进程。loader entry 中的 config 作为 base 层:没有用户设置时保持原行为,清除用户覆盖后也会回落到该 base。

代理密码必须优先使用 passwordRef:隐式 default 代理默认使用 DSH_PROXY_PASSWORD(也可改成其他环境变量格式标识符),保存时先写 credential,再写 passwordRef 与其他 settings。保存后密码草稿立即清空,页面重新打开时只通过 credentials.describe() 获取“已配置/未配置”,不会调用 resolve()、回填原文、显示首尾或显示 DPAPI 密文;密码仅通过单向 credentials.set() 提交,绝不会保存到 settings。配套的 dsh-credentials-system 使用 Windows 当前用户绑定的 DPAPI 密文存储,并替换默认的明文 dsh-credentials-local。role("secret") 只负责页面/API 脱敏,本身不等于磁盘加密。

仍可直接使用下面的 cordis.patch.yml 配置;交互式用户层优先于 entry base。

配置位于 profile 的 cordis.patch.yml。以下示例与当前 package schema 一致:proxies 是对象,rules 是数组,默认策略位于 default 对象中。

- id: system-proxy
  config:
    enabled: true
    mode: auto                 # auto | env | system | manual
    url: ''                    # 旧配置兼容:隐式 default 代理(不要包含 userinfo)
    # passwordRef: DSH_PROXY_PASSWORD  # 隐式 default 代理的只写 credential 引用
    patchNodeHttp: true
    protectPrivate: true         # 默认直连私网、链路本地和 metadata
    healthMaxEntries: 10000      # 每主机健康表上限(LRU 淘汰)

    noProxy:
      - localhost
      - '127.0.0.1'
      - '::1'
      - api.deepseek.com
    only: []                   # 旧配置兼容:非空时只代理这里的主机

    proxies:
      clash-http: http://127.0.0.1:7890
      v2ray-socks: socks5h://127.0.0.1:10808
      legacy-socks: socks4a://127.0.0.1:1080
      os-proxy: { source: system }
      # 推荐用 passwordRef 从 DSH credentials 服务解析密码;不能与 password 同时设置
      authenticated: { url: http://proxy.example:8080, username: user, passwordRef: DSH_PROXY_OFFICE_PASSWORD }

    rules:
      - host: [api.deepseek.com]
        action: direct
      - host: [api.openai.com]
        provider: [openai]
        action: proxy
        proxy: v2ray-socks
      - plugin: [some-unreachable-plugin]
        action: fallback
        proxy: clash-http
        directTimeoutMs: 2000
      - host: [api.blocked.example]
        action: block

    default:
      strategy: fallback       # direct | proxy | fallback
      proxy: clash-http
      directTimeoutMs: 3000
      latencyThresholdMs: 1500
      cooldownMs: 60000
      methods: [GET, HEAD, OPTIONS, TRACE]

    trustRouteHeaders: false
    routeHeaderPrefix: x-dsh-route

Loader 配置是整体替换

Cordis loader 只在 EntryOptions 顶层合并。config 一旦出现在后续 patch 中,就会整体替换原 config;rules 或 proxies 使用数组/对象不会产生跨层深合并。

因此,在 profile patch 中增加一条规则时,必须重述要保留的完整 config,包括全部 proxies、rules、default 和兼容键。只写增量字段会丢失其他配置。

代理来源和环境变量

mode 来源
auto 代理环境变量 → OS 系统代理 → 显式 url
env HTTPS_PROXY / HTTP_PROXY / ALL_PROXY(含小写)
system Windows WinINET / macOS scutil
manual 仅显式 url

专用环境变量:

  • DSH_PROXY_URL:运维级代理 URL 覆盖;
  • DSH_PROXY_MODE:覆盖 mode;
  • DSH_PROXY_DISABLE:任意非空值禁用插件;
  • NO_PROXY / no_proxy:追加直连规则。

显式 URL 支持 http://、https://、socks4://、socks4a://、socks5://、socks5h://(SOCKS 默认端口 1080;socks4a 与 socks5h 由代理端解析域名,socks4 与 socks5 本地解析)。

SOCKS 实现说明(fetch 路径)

fetch 路径对所有 socks 协议统一使用自定义无状态 connector + undici Agent(connector 模式借鉴 @undicijs/proxy 的 buildSocksProxyConnector,MIT,已在 lib/socks.js 顶部署名):

  • 每个连接都是独立的 SOCKS 握手(无状态,无跨请求隧道复用);undici Agent 按 origin 池隔离——闭合了 GHSA-hm92-r4w5-c3mj 那类 socks 隧道跨 origin 复用串线问题(不使用 undici 内置 Socks5ProxyAgent)。
  • HTTPS 目标:SOCKS 建连后把原始 socket 以 httpSocket 交给 undici 自己的 buildConnector 完成 TLS(ALPN/会话复用与 undici 一致);HTTP 目标直接返回原始 socket。
  • SOCKS4/4a + IPv6 目标(协议限制)在握手前显式抛错 SOCKS4_IPV6_UNSUPPORTED,绝不静默直连。
  • 认证凭据通过 SOCKS 握手(userId/password)传递,可来自 URL user:pass@ 或 username/password 字段;密码错误会明确失败。
  • node http(s) 路径由 SocksHttpAgent/SocksHttpsAgent(同一隧道原语)覆盖。

规则语义

规则按数组顺序匹配,首条命中的规则生效:

  • 所有指定字段必须同时命中(字段之间是 AND);每个字段自己的值列表内部是 OR("host 是 A 或 B,且 provider 是 p")。指定字段在请求上下文中未知时,该规则不匹配。想表达"任一路由条件都行",就拆成多条按先后顺序的规则,或用 host:port 等更精确的模式缩小范围。

例如 provider 为 openai 且 host 为两个候选之一,才命中这条规则:

- host: [api.openai.com, gateway.example.com]
  provider: [openai]
  action: proxy
  proxy: v2ray-socks
动作 含义
direct 只直连,不使用代理
proxy 强制使用指定具名代理
fallback 先直连;满足安全条件时在连接前失败或健康记忆触发后改用代理
block 本地拒绝请求(NETWORK_BLOCKED)

引用不存在或当前不可用的代理名会 fail loud(UNKNOWN_PROXY),不会静默降级直连;source: system|env 当前没有可用代理时,只有未选择该代理的请求可以继续。

Provider / plugin 上下文

全局 fetch 只能可靠看到目标 URL,无法自动判断任意请求来自哪个 Cordis 插件。调用方必须显式使用插件服务:

await ctx.networkRoute.run(
  { provider: 'openai', plugin: 'my-plugin' },
  () => fetch('https://api.openai.com/v1/models'),
)

流式响应(SSE)的归属:als.run(meta, () => iterable) 不会让后续迭代保留上下文——next() 在调用方自己的异步上下文里执行,currentRoute() 会丢。对惰性消费的流,用 ctx.networkRoute.iterable(route, stream) 包装:next / return / throw 每次都会在 als.run(route, ...) 内调用,abort(return)与异常(throw)同样保留归属:

const attributed = ctx.networkRoute.iterable(
  { provider: 'openai' },
  response.body, // 或任意 async iterable(含 llm/stream 适配器的异步迭代器)
)
for await (const chunk of attributed) { /* currentRoute() 可见 */ }

DSH llm/stream 集成应包装异步迭代器的 next / return / throw 生命周期,使惰性 SSE 消费期间仍保留 provider/model 上下文。

trustRouteHeaders 默认关闭。若开启,可读取 x-dsh-route-provider 与 x-dsh-route-plugin。无论是否信任,这些内部路由控制头都会在发出请求前被自动剥离,不会泄漏给 API 上游(fetch 与 node http(s) 两条路径都已处理)。

Fallback 安全模型

自动回退不是普通重试。为避免重复创建 LLM 请求或重复计费:

  • 默认只允许 GET、HEAD、OPTIONS、TRACE 自动换路;
  • POST 默认不重放;
  • 流式上传、FormData 和超出缓冲上限的 body 不重放;
  • 用户主动 abort 不重试;
  • 收到响应头或 SSE 字节后不重试;
  • 只有能够证明请求字节尚未写出的连接前失败才安全换路;
  • onConnect 已发生后的 socket 错误属于不确定状态,默认不重试。

延迟阈值使用到响应头的观测值记录 EWMA;连接失败进入有上限的 cooldown。主动健康探测默认关闭,避免插件启动时自行访问外网。

NO_PROXY 与内网安全

安全默认(protectLocal / protectPrivate,均可关闭)让 localhost、loopback、169.254.0.0/16、云 metadata 端点、RFC1918 内网和 IPv6 ULA(fc00::/7、fe80::/10)一律直连,所以"全部走代理"的配置也不会把内网/metadata 流量交给外部代理。

noProxy 与 NO_PROXY / no_proxy 支持:

  • *:全部直连;
  • example.com / .example.com / *.example.com:精确或后缀;
  • 10.0.0.0/8、fc00::/7:IPv4/IPv6 CIDR;
  • 127.0.0.1:8080、example.com:443:host:port(端口必须一致才命中);
  • 裸 IP。

不要把内网、metadata 或带明文凭据的 HTTP API 交给不受信任的代理。

凭据与日志安全

代理 URL 可以包含凭据,但日志只能显示 http://***@proxy.example:8080。插件不得记录 API Authorization、请求 body 或代理密码。具名代理可设置 passwordRef(环境变量风格标识符,如 DSH_PROXY_OFFICE_PASSWORD),插件通过 DSH credentials.resolve() 在运行时取值,并在该引用更新时重载代理;缺少凭据服务或引用未配置会明确失败。passwordRef 与明文 password 不能同时设置。

实现上,URL 里的凭据(或 username/password 字段)会在解析时剥离:交给 undici ProxyAgent / http(s)-proxy-agent 的 URL 不再含用户信息,认证改由 Proxy-Authorization(undici token、node agent headers 选项)或 SOCKS 握手(userId/password)注入。SOCKS 路径使用插件自研 agent,不做任何 URL 调试输出。npm test 含 DEBUG=* 泄漏测试:stderr 不得出现代理凭据、URL 形式或上游 Bearer token。

残余(依赖固有):明文 HTTP 请求经 http-proxy-agent 且进程开启 DEBUG=* 时,依赖会把该请求的完整请求头(含上游 Authorization)dump 到 stderr;https 请求的内层头在 TLS 内不受影响。仍建议不要在包含真实 key 的生产进程里开启通配 DEBUG=*。

重定向安全

路由策略对重定向链的每一跳重新计算(redirect: "manual" + 手动跟随):代理请求被 30x 引向内网或 metadata 地址时,下一跳会按规则命中 direct/block,不会继续走外部代理。调用方显式请求 redirect: "manual" 时原样返回 3xx;redirect: "error" 时抛出。默认最多跟随 20 跳。

可观测性(无裸 /status)

插件不暴露任何裸 HTTP 状态端点。状态通过两个受控表面提供,返回的都是纯 JSON、只含脱敏信息(代理 URL 用 *** 掩码、健康表按上限采样、无凭据/请求数据/句柄):

  • Cordis Service systemProxyStatus(总是可用):其他宿主插件可 ctx.get("systemProxyStatus").status()。
  • Typert Remote(部署挂载 typert 时自动生效):严格手写 manifest(无装饰器),namespace systemProxyStatus / method status,由 DSH 的 typert 网关作为 Remote 服务。manifest 的 disposer 绑定在插件自身的 fiber 上——热重载时先撤销旧注册再注册,避免重复端点。
{
  "plugin": "dsh-system-proxy",
  "version": "0.2.0",
  "enabled": true,
  "patchNodeHttp": true,
  "proxies": [{ "name": "default", "scheme": "http", "url": "http://***@127.0.0.1:7890" }],
  "strategy": "fallback",
  "fallback": { "directTimeoutMs": 3000, "latencyThresholdMs": 1500, "cooldownMs": 60000, "methods": ["GET","HEAD","OPTIONS","TRACE"] },
  "health": { "api.openai.com": { "ewmaMs": 820.4, "proxyEwmaMs": null, "failures": 0, "coolingDown": false } }
}

当前限制

  • SOCKS4/4a 无法寻址 IPv6 目标(协议限制),此类组合会明确报错,绝不静默直连。
  • fallback 的"延迟阈值"基于到响应头的观测 EWMA 决定后续请求选路;不会在 SSE 已开始后中断线路。
  • 主动健康探测默认关闭(避免启动时自行访问外网);每主机健康表默认上限 10000 条(healthMaxEntries 可调,LRU 淘汰)。
  • 同一进程内不应同时加载多个相互竞争的传输包装插件。
  • 明文 HTTP 请求 + DEBUG=* 时,http-proxy-agent 依赖会 dump 上游请求头(见"凭据与日志安全")。

测试

npm install
npm test

覆盖:规则/配置/健康单元测试(含 CIDR、*、host:port、IPv6、健康表上限)、旧版兼容、多代理路由、provider 作用域、路由头剥离、block、fallback 安全重放(GET 重放 / POST 不重放)、socks4/4a/5/5h 在 fetch 与 node http 双路径(含 DNS 语义与 SSE/backpressure/abort)、逐跳重定向、内网与 metadata 默认保护、node (url,cb)/(options,cb)/get 全调用形式、热重载无连接泄漏、dispose 不打断在途请求、ALS 异步迭代器上下文、真实 Cordis loader 挂载与热重载。测试使用本地直连服务器、HTTP CONNECT 代理和 SOCKS 测试服务,不需要访问外部 API。

社区参考

以下项目已核验真实存在,但解决的是观测、诊断、端点修复或模型层 fallback,不等同于本插件的传输层网络路由:

  • liaohch3/claude-tap
  • TYEclipse/dsh-netdoctor
  • kanghelyu/dsh-search-endpoint-guard
  • btspoony/dsh-llm-fallbacks

不引用无法公开验证的 dsh-external/* 地址。

免责声明 / Disclaimer

本项目是社区维护的非官方开源插件,与 DeepSeek、DeepSeek Harness 及其关联方不存在隶属、授权、背书或商业合作关系。项目名称及第三方商标仅用于说明兼容对象,其权利归各自权利人所有。

本软件按“原样”(AS IS)提供,不承诺适销性、特定用途适用性、持续可用性、安全性或无错误。代理配置会影响 DSH 主进程的网络流量;错误的路由、凭据或第三方代理可能导致请求失败、信息泄露、额外费用、账号限制或其他损失。使用者应自行审查源码与配置,只使用可信代理,遵守所在地法律、服务条款及网络管理政策,并自行承担部署和使用风险。在法律允许的最大范围内,作者及贡献者不对因使用或无法使用本软件产生的任何索赔、损害或责任负责。若本节与 MIT License 冲突,以 MIT License 英文原文为准。

This is an unofficial, community-maintained open-source plugin. It is not affiliated with, authorized, endorsed, or sponsored by DeepSeek, DeepSeek Harness, or their affiliates. Project names and third-party trademarks are used only to identify compatibility and remain the property of their respective owners.

The software is provided “AS IS,” without warranties of merchantability, fitness for a particular purpose, availability, security, or error-free operation. Proxy configuration affects traffic from the DSH Host process; incorrect routes, credentials, or untrusted proxies may cause failed requests, data exposure, additional charges, account restrictions, or other loss. You are responsible for reviewing the code and configuration, using trusted proxies, complying with applicable law, service terms, and network policies, and accepting all deployment and usage risks. To the maximum extent permitted by law, the authors and contributors are not liable for claims, damages, or other liability arising from use of or inability to use this software. If this section conflicts with the MIT License, the English MIT License text controls.

开源协议 / License

本项目采用 MIT License。在保留版权声明和许可声明的前提下,你可以使用、复制、修改、合并、发布、分发、再许可及销售本软件副本。MIT License 不提供任何明示或默示担保,完整且具有法律效力的条款以仓库中的 LICENSE 英文原文为准。

This project is licensed under the MIT License. You may use, copy, modify, merge, publish, distribute, sublicense, and sell copies, provided that the copyright and permission notices are retained. The license provides no express or implied warranty. See LICENSE for the complete controlling text.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

APIPublish GuideAbout
—/ 5

No ratings yet

Verified DSH bundle

Commit 35c4915241fc

Community comments

No comments yet. Be the first to write one.