- Home
- Plugins
- Tools & Capabilities
- dsh-tizi-switch
dsh-tizi-switch
bzyyyy-china/dsh-tizi-switch
Switches this Harness instance's outbound proxy on and off at runtime, without restarting or changing the operating system proxy, and releases it automatically when the proxy client exits.
Install
dsh plugin --profile web add github:bzyyyy-china/dsh-tizi-switchREADME
梯子开关
在 DeepSeek Harness 运行中随时切换出网代理 —— 不重启、不改系统代理、不用配端口。
它解决什么问题
DSH 只在启动时解析一次代理策略(HTTP_PROXY / HTTPS_PROXY),之后整个进程生命周期都不再改变。于是只有两个都不好的选择:
- 启动时代理客户端没开 → 整个进程一直是直连,想翻墙只能重启 DSH
- 把代理写死在环境变量里 → 客户端一关,所有请求指向死端口,DSH 直接瘫痪
本插件复用 DSH 自带的 @deepseek-ai/dsh-http-proxy 包(它的 install 本就返回 disposer),在会话继续运行的同时替换进程级的 undici dispatcher。
它会自己找到你的代理端口
Clash Verge 监听 7897,v2rayN 的 HTTP 入站在 10809,sing-box 在 2080,裸 shadowsocks 客户端在 1080。把其中任何一个当成默认值,都会让用别的客户端的人装完即失效——所以插件不假设端口,而是按顺序问这台机器:
- 代理环境变量 —
HTTP_PROXY/HTTPS_PROXY/ALL_PROXY - Windows 系统代理 — 客户端自己发布到注册表的地址
- 常见客户端默认端口 — 十五个,覆盖 Clash Verge、v2rayN、mihomo、sing-box、v2rayA、Hiddify 等
探测只在插件加载时做一次,结果缓存到进程结束。之后每次开/关代理都用缓存,不会再扫端口——反复遍历本机端口正是安全软件判定端口扫描的行为。proxy_status 会显示「本次已探测 N/3 次」,正常恒为 1/3;整个进程最多 3 次,不会因为你反复点「开代理」就反复探测。
探测结果只存内存,不写你的配置文件。 说「开代理」时直接用这个结果装上,不用你手填端口。
两种情况会自动重探,都不做全端口扫描:
- 加载时什么都没找到(先开 DSH、后开客户端)→ 下次开代理时重探
- 缓存的端口已经不通(换了客户端,或客户端关过)→ 重探到新端口
第 2 条很关键:只信加载时的缓存的话,换了客户端之后插件会一直报「找不到」,哪怕新客户端跑得好好的。
但装之前一定确认端口是通的。 缓存只是「上次看到它在哪」,不是承诺。装死端口会让每个请求 ECONNREFUSED,那正是这插件要防的事。所以 proxy_on 安装前做一次回环连接确认(只连这一个已知端口,不枚举任何东西)。通了才装,不通就说明「客户端可能已退出」并保持直连。
可选:扫描全部监听端口
客户端改到了非常见端口、上面三步都找不到时,可以显式开启:
- id: tizi-switch
config:
scanListeningPorts: true
默认关闭,因为全端口扫描更容易触发安全软件告警;常见端口已覆盖绝大多数人。
开启后整个进程最多扫一次(加载那次),后续重探只读环境变量、系统代理和那 15 个常见端口。proxy_status 会显示「已用过一次全端口扫描」。
端口能连上,不等于它是代理
DSH 自己的 Web 界面、开发服务器、本地数据库都接受连接。把 dispatcher 指过去,每个出网请求都会失败——所以候选端口必须应答 HTTP CONNECT 握手,这是 HTTP 代理才实现的协议,普通 Web 服务不会正确回应。
探测的目标固定为 127.0.0.1:9(没有服务的丢弃端口),因此握手不会真的建立隧道,探测过程不会向你当前的代理节点发送任何流量。
SOCKS 端口会被识别,但不会被使用
DSH 只接受 http:// / https:// 代理 URL。如果只找到 SOCKS 端口(v2rayN 的 10808、v2rayA 的 20171),插件会明确告诉你「这是 SOCKS 端口,请在客户端里再开一个 HTTP / mixed 入站」,而不是假装切换成功。
四个工具
| 工具 | 作用 |
|---|---|
proxy_on | 自动找到本机代理端口;通了就装上并立即生效,不通则说明原因并保持原路由不变 |
proxy_off | 卸掉代理,恢复直连,立即生效 |
proxy_status | 只读:现在走不走代理、用的是哪个端口、端口通不通 |
proxy_detect | 只读:列出本机找到的所有代理端口,以及每个端口被接受或拒绝的原因 |
自动回退:客户端退出后不再断网
代理装上后,本进程的每个请求都指向那个端口。客户端一关,请求会以 ECONNREFUSED 在毫秒级失败——从使用者的角度看就是「网断了」。
所以插件每 3 秒探测一次端口,发现不通就自动卸掉代理、回到直连:
| 阶段 | 结果 |
|---|---|
| 装代理后请求 | HTTP 200 |
| 关掉代理端口 | ECONNREFUSED(约 2 ms) |
| 健康检查触发(≤3 s) | 自动回退到直连 |
| 回退后请求 | HTTP 200 |
为什么不自动重新开启:代理客户端的端口在启动阶段可能反复抖动,自动恢复会让路由来回切,状态不可预期。所以回退是单向的;客户端重新就绪后,说一句「开代理」即可。
用 healthCheckMs: 0 关闭自动检测。
安装
在 插件 页面安装,或者:
dsh plugin add dsh-tizi-switch
配置
无需配置。 端口由自动探测处理。
想固定端口、跳过探测:
- id: tizi-switch
config:
port: 7897
即使固定了端口,安装前仍会校验:死端口会被拒绝并保持直连,而不是把 DSH 弄瘫。
| 配置项 | 默认值 | 说明 |
|---|---|---|
host | 127.0.0.1 | 代理主机,探测和安装都用它 |
port | 不设=自动探测 | 指定后跳过自动探测 |
probeTimeoutMs | 400 | 单个端口的探测预算 |
healthCheckMs | 3000 | 对已启用端口的健康检查间隔;0 关闭 |
scanListeningPorts | false | 是否扫描全部监听端口(可能触发安全软件告警) |
reportLimit | 5 | proxy_status 最多显示几个端口 |
覆盖范围与边界
覆盖:模型调用、web_fetch、web_search、HTTP MCP —— 任何走 globalThis.fetch 的请求。
不覆盖(DSH 上游既定边界,不是本插件的取舍):
- 模型自己写的脚本子进程:它们刻意不继承可能带凭据的代理 URL
- 使用
node:http的遥测通道 - 指向字面私有地址的
web_fetch
只作用于本进程:改的是本进程的全局 dispatcher,不影响机器上任何其它程序。插件会读 Windows 系统代理来了解客户端监听在哪,但从不写入。
不支持:需要用户名口令的代理。会被识别并报告,但不会安装。
关键设计
切换前为什么先验证端口:死端口会快速 ECONNREFUSED。无脑装代理就等于把「忘了开客户端」变成「DSH 瘫掉」。所以 proxy_on 先完成 HTTP 握手,答不对就拒绝,并且不改动当前路由。
proxy_off 为什么不等 dispose:dispose() 尾部的 agent.close() 要等在途连接结束,可能几十秒;但头部还原 setGlobalDispatcher 是同步的——路由早就切回去了。等整个 dispose 只会让工具调用看起来卡死。
为什么用动态导入:插件以本地 link 或 profile 包形式存在时,profile 的 node_modules 解析不到 @deepseek-ai/*,所以经 DSH_INSTALL_DIR 用 file:// URL 导入。
探测为什么零依赖:所有探针都是裸 node:net socket,插件保持无第三方依赖。
兼容性
需要 DSH 0.2.2-rc.2 或更高。它依赖 @deepseek-ai/dsh-http-proxy 的 installProxyFromEnvironment 返回 disposer,这是热切换的前提。该 API 若变更,插件需要同步更新。
许可证
Related plugins
archify (deepseek-harness)
tt-a1i/archify
WeKnora (dsh-weknora)
tencent/weknora
next-ai-draw-io (dsh-plugin)
dayuanjiang/next-ai-draw-io
weknora
tencent/weknora