@tr1v3r/dsh-proxy
Hot-switches in-process DSH fetch traffic among direct, manual HTTP(S)/SOCKS5 proxy, and ambient system-proxy modes, with no-proxy rules and optional proxy-environment export for newly spawned child processes.
Install
dsh plugin --profile web add @tr1v3r/dsh-proxy
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-proxy — DSH 运行时可切换出站代理
English readme: [README.md](README.md)。



@tr1v3r/dsh-proxy 是 DeepSeek Harness 插件,把进程内所有出站请求——
LLM 提供方、web_search / web_fetch、streamable-http MCP——经由
HTTP(S) CONNECT 或 SOCKS5 代理转发,并且支持运行时随时开关、随时换代理:
可以在 Web「设置 → 通用 → 网络代理」中操作,也可以编辑 profile 的
cordis.patch.yml 中的 dsh-proxy 条目(热加载),全程零重启。
上面的动图展示路由引擎,安装后可运行 node scripts/demo.mjs 复现。
工作原理
DSH 与 pi-ai 的请求都走 globalThis.fetch,而它读取的是 undici 的全局
dispatcher 槽位(Symbol.for('undici.globalDispatcher.1'))。本插件接管该槽位:
http(s)://代理 →EnvHttpProxyAgent(https 走 CONNECT 隧道)socks5://代理 → undici 内置Socks5ProxyAgent(支持 URL 内鉴权;
socks5h:// / socks:// 自动归一;域名在代理端远程解析)
noProxy规则 → 两条路径统一走RoutingDispatcher分流,HTTP 与 SOCKS
语义完全一致(undici 风格:裸条目匹配主机及点边界子域;host:port 锁定
端口; 全部直连;前导点 / . 前缀视同裸条目等价写法)。manual 模式
下 dispatcher 刻意忽略环境变量里的 NO_PROXY/HTTP_PROXY——导出的 env
只引导子进程,进程内路由完全由 profile 条目配置决定;system 模式则相反,
跟随环境代理——HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY,环境变量
缺失时再回退到 macOS 系统设置里的网络代理(scutil --proxy)——每次分节
应用时重新探测一次,而非持续轮询)
exportEnv: true(默认)时,切换还会同步导出
HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY 到 dsh 进程环境——切换后
新拉起的子进程(bash 工具里的 curl/git、stdio MCP server)跟着走同一
代理。启动时由你自己设置的环境变量绝不会被覆盖;禁用/卸载时全部还原。回环直连开启
(默认)时,导出的 NO_PROXY 还会并入回环默认集(localhost,127.0.0.1,::1,与你的
规则去重合并);bypassLoopback: false 时按你自己的列表原样导出。
被替换下来的旧 dispatcher 先优雅关闭、30 秒后强制销毁,确保切换真正切断
旧的 keep-alive 连接。在途请求同样只有这 30 秒宽限(RETIRE_DESTROY_MS):
切换后仍持续超过约 30 秒的流式响应,会在旧 dispatcher 的 socket 被强制销毁时
被中断。
凭据安全
代理 URL 中内嵌的 user:pass@ 凭据以明文存放在磁盘上——profile 的
cordis.patch.yml 与 settings 持久化中——仅靠文件权限保护。默认
exportEnv: true 时,凭据还会随 HTTP(S)_PROXY 环境变量写入 dsh 进程,
切换后拉起的子进程都会携带(同一用户可通过 ps -E 或
/proc/<PID>/environ 看到;其他用户通常需要 root 等特权才能读取,
具体取决于平台权限设置,并非所有同机用户都可见)。为此不引入新的配置项;凭据敏感的场景,
建议让 dsh-proxy 指向本机免鉴权的代理入口(例如 http://127.0.0.1:7890,
由它再对接需要鉴权的上游),而不是把 user:pass@ 写进 URL。
安装
在目标 profile 目录(~/.config/dsh/profiles/<name>/):
1. package.json 加依赖与 bundle(合并进现有 dsh.profile.bundles 列表):
{
"dependencies": {
"@tr1v3r/dsh-proxy": "^0.2.5"
},
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@tr1v3r/dsh-proxy"]
}
}
}
2. 安装:
dsh plugin --profile <name> install --no-frozen-lockfile
3. 重启一次 dsh 挂载插件;此后再无需重启——切换全在 settings 里。
使用
Web 主界面侧栏底部(设置上方)只有「代理状态」图标;悬浮、聚焦或打开菜单可查看
当前选择的模式,并在直连、跟随系统和手动代理之间切换。它与设置页共用同一个配置条目,外部编辑
profile 的 cordis.patch.yml 后也会同步更新;悬浮提示中的手动代理地址会隐藏用户名和密码。
「跟随系统」表示已选择的模式,不保证系统探测到了可用代理;实际出口以 DSH
日志为准。没有有效代理地址时,快捷菜单不会启用手动模式。要编辑地址、直连规则
或子进程开关,请打开「设置 → 通用 → 网络代理」。
Web profile 也可打开「设置 → 通用 → 网络代理」:从下拉列表选择直连、跟随系统或手动代理,
选择后立即生效,无需「应用」按钮。手动模式可填写 HTTP(S)/SOCKS5 URL、直连地址
(每行一个)和子进程环境变量开关;URL 与直连地址失焦后保存,开关切换后立即保存,
无效 URL 不写入文件。图形界面写入同一个 dsh-proxy 配置条目,文件配置仍然完整保留:
直接编辑 profile 的 cordis.patch.yml 会热加载并同步到图形界面;并发修改由修订号保护,
避免覆盖新值。
下拉菜单提供三种出站模式:
也可以在 ~/.config/dsh/profiles/<name>/cordis.patch.yml 添加如下条目
(热加载,立即生效);如果文件已有条目,就追加到现有 YAML 列表中;已有
dsh-proxy 条目时直接修改该条目。mode 可在
direct(直连)、system(跟随系统)、manual(手动)间切换:
- id: dsh-proxy
config:
mode: manual # direct | system | manual
proxy: socks5://127.0.0.1:1080 # 仅 manual——http://…、https://…、
# socks5://user:pass@host:1080、socks5h://…
noProxy: # 仅 manual——可选分流规则
- localhost
- .internal.example
- registry.corp:443
bypassLoopback: true # manual 与 system 均生效——本地回环默认直连
# (false 可改为走代理)
exportEnv: true # 仅 manual——同步设置子进程的 HTTP(S)_PROXY
mode · 行为
direct · 直连,不走任何代理(等价于旧的 enabled: false)。
system · 跟随主机代理,每次分节应用时探测一次:读取 HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY 环境变量;环境变量缺失时,在 macOS 上再读取系统设置里的网络代理(scutil --proxy)。是保存时探测、非持续轮询;Windows 注册表、Linux 桌面与 PAC 暂未覆盖。忽略 proxy/noProxy/exportEnv。
manual · 走 proxy URL,可用 noProxy 分流(等价于旧的 enabled: true)。
enabled: true/false 仍作为旧写法兼容——未设置 mode 时分别映射到
manual/direct:
- id: dsh-proxy
config:
enabled: true # ≡ mode: manual
proxy: http://127.0.0.1:7890
每次保存立即重路由。插件会记录每次切换:
dsh-proxy: routing global fetch via socks5://***@127.0.0.1:1080, noProxy 3 rule(s) dsh-proxy: following system proxy (http://127.0.0.1:7890, noProxy 3 rule(s)) dsh-proxy: direct (mode: direct)
(日志中代理 URL 的用户名密码会打码。system 模式只读环境/系统代理,
不会回写这些环境变量。)
已应用路由快照
通用设置中的代理卡片通过可选、已认证的 Connection Fetch 路由,读取 Host
最近应用的默认路由:所选模式、来源、分别脱敏的 HTTP/HTTPS 端点、绕过策略、
代次及稳定的应用/回退代码。保存完成不等于 Host 已应用该版本,必要时手动刷新。
不支持此能力或断连时显示“状态不可用”,不影响原有设置及快捷切换。
快照不是连接健康检查或逐请求追踪。direct 恢复原始 dispatcher(可能被其他插件
配置),不保证物理直连;system 展示上次实际安装结果,不重新探测环境/系统。
NO_PROXY 和回环策略仍可能让个别请求绕过代理。此功能不发送任意 URL 探针或
提供方请求,也不传递凭据或完整代理 URL。提供方连接诊断仍延期,等待适配器给出
明确的有效目标与安全契约。
开启回环绕过时,导出的 NO_PROXY 只对去除首尾空白后完全相同的条目去重,保留
用户拼写和顺序,并补齐缺失的标准默认值:localhost、127.0.0.1、::1。
大小写、尾点和 IPv6 方括号变体各自保留,例如 127.0.0.1. 不能挤掉
127.0.0.1,[::1] 不能挤掉 ::1。外部客户端不一定遵循进程内的 hostname
匹配规则(curl 对上述 IP 变体的处理就不同),也不假定各客户端对 localhost
变体的解释等价。后缀/通配符/端口规则及其他地址不做规范化。
bypassLoopback: false 仍按原用户列表导出,不注入默认值也不去重,具体含义由
子进程客户端自行解释。进程内更广泛的回环识别逻辑保持不变。
覆盖范围
流量 · 是否代理
pi-ai 各提供方(zai-coding-cn、自定义 openai 兼容路由……) · ✅
dsh-llm-deepseek(deepseek-official) · ✅
web_search / web_fetch · ✅
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::9,最后更新 2026-10-04