DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Undici Timeout — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
U

dsh-undici-timeout

Undici Timeout

用于 DSH LLM 出口的可配置直接通道:采用保持连接的 undici Agent,支持调整连接、标头、正文和保持连接超时,强制使用 IPv4,默认关闭 HTTP/2,在代理后自动停用,并提供只读状态路由。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:mqnu00/dsh-undici-timeout#23044ac81cf6e6b7c7c0e385a592169fd9d2d7cc
README兼容性版本

兼容性与来源证明

Undici Timeout 以 dsh-undici-timeout 发布,当前版本为 0.1.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/10/15

版本

0.1.1stable
2026/10/15

相关插件

正在加载相关插件…

最新版
0.1.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/15
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 integrations-communication 分类下经过校验的插件。

Im@xmanrui/dsh-im将微信、飞书、企微、钉钉、QQ、Telegram、WhatsApp、Slack、Discord 和 Matrix 接入 DSH。Free Searchdsh-free-searchDeepSeek Harness 的免费网页搜索:24 个引擎(Bing/DuckDuckGo/AnySearch/SearXNG/Exa/Tavily/Keenable/Firecrawl/Parallel 无需密钥;通过 MCP 使用 Zhihu 全局/站内搜索,以及 Perplexity/SerpBase/DeepSeek/You.com/Baidu/Kimi/Aliyun/Doubao,需密钥;OpenAI/Gemini/ClaudeDafeiyudsh-dafeiyu由 DeepSeek Harness 会话事件驱动的原生桌面端 BigFish companion。Wechat Plugindsh-wechat-pluginDeepSeek Harness 的微信渠道:锁定电脑后离开,通过手机驱动代理。无需 OpenClaw,也无需公网 IP。

README

dsh-undici-timeout

DSH 插件:给 LLM 出口(globalThis.fetch → Node 内置 undici)换一条可配置超时 + 可保活复用的直连通道。

English: README.en.md · 发布流程: RELEASING.md · 收录条目: contrib/awesome-dsh-plugin/

为什么需要它

  • Node 内置 fetch 背后的 undici,connectTimeout 固定 10s(DNS+TCP+TLS 全算在内), 没有环境变量/参数可调;
  • 本机(WSL)直连 opencode.ai 的新建握手实测出现过 11.6s,双栈探测也常整轮 AggregateError(IPv6 无出口 + IPv4 抖动);
  • pi-ai 每次重试都 new OpenAI(...) → Shims.getDefaultFetch() 当场读全局 fetch, 所以每次尝试都是新 TCP+TLS,被 10s 砍掉 → TIMEOUT/TRANSPORT → 重试又重连。 今天(2026-10-13)的日志:87 次 TIMEOUT + 8 次 TRANSPORT,autoswitch 开代理 10 次。

对比:OpenCode(Go 客户端)全程复用一条已建立的 HTTP/2 连接,握手只发生一次, 所以同样的网络下它不超时。本插件就是把这套「长超时 + 保活复用」搬给 DSH。

这东西的寿命:它是一个补丁,不是一块功能

它存在的理由只有两条,而且两条都在上游,不是 DSH 的既定设计:

  1. Node 内置 fetch 的 connectTimeout 固定 10s 且没有开关(Node/undici 上游行为);
  2. undici 8 把 allowH2 的默认值翻成 true,而 Cloudflare 边缘对 h2 上的 SSE 长流会 回 NGHTTP2_ENHANCE_YOUR_CALM(上游默认值 × 边缘策略)。

这两条一旦在 DSH 侧被解决 —— 比如 LLM 层允许指定 dispatcher / 连接超时,或者换掉内置 fetch —— 本插件就没有存在价值了,直接卸掉:

dsh plugin --profile web remove dsh-undici-timeout   # 或者删掉 profile 里那条 insert

判断「可以卸了」的依据(任一条成立即可):

  • 状态路由 stats 长期没有 TIMEOUT,且直连握手不再接近 10s;
  • DSH 的 LLM 层能自己配连接超时 / 强制复用连接(看 release notes,或 dsh --profile web --dump-config 里出现了对应配置项);
  • 上游把内置 fetch 换成了自带 Agent 的实现。

在那之前它是最小补丁:单文件、零依赖、只接管白名单域名、随时可摘。 所以只从 GitHub 分发,不发 npm、不进插件市场。

原理

agent 回合
 └─ dsh-llm-retry(重试)→ dsh-llm-pi-ai → pi-ai → new OpenAI({ fetch: undefined })
      → openai/client.mjs: this.fetch = Shims.getDefaultFetch()   ← 每次尝试当场读全局
      → 【本插件包装过的 globalThis.fetch】
           ├─ 命中 hosts 白名单 且 无代理 env → undici.fetch(url, { dispatcher: 自建 Agent })
           └─ 其它(代理模式 / 非白名单 / Request 输入)→ 原样透传给原来的 fetch
  • 用解析到的那份 undici(argv[1] → profile 扫描 → bare)自建 Agent: connect.timeout、headersTimeout、bodyTimeout、keepAliveTimeout/MaxTimeout、 pipelining、可选 family。
  • 不碰 setGlobalDispatcher:那是 @deepseek-ai/dsh-http-proxy 的地盘(代理联动靠它), 抢了会把 net-proxy 弄坏。本插件只接管「本来要走直连」的那部分流量。
  • 代理 env(http_proxy/https_proxy/ALL_PROXY 任一大写或小写)在场时自动让路, 由 harness 的代理层处理——所以能和 dsh-net-proxy / dsh-net-autoswitch 共存。

配置项(cordis.patch.yml 里本插件的 config:)

键默认说明
enabledtrue总开关。false 时不改 fetch,状态路由仍在。
hosts["opencode.ai"]命中这些域名(含子域)才接管;"*" = 全部直连流量。
connectTimeoutMs30000核心项。DNS+TCP+TLS 的总预算(内置固定 10s)。范围 1000–600000。
headersTimeoutMs300000等响应头。
bodyTimeoutMs0相邻 body 块间隔;0=关闭,卡流交给 pi-ai 的 streamIdleTimeoutMs 看门狗。
keepAliveTimeoutMs60000空闲连接保留时长(复用 = 不重握手)。
keepAliveMaxTimeoutMs600000保活上限。
pipelining1每连接并发请求数。
family00=跟随系统;4/6=强制 IP 族(本机 IPv6 无出口时 4 很划算)。
allowH2false⚠️ 见下面「h2 的坑」。undici 8 把 allowH2 的默认值翻成了 true,不显式关掉,本插件会顺手把 LLM 出口从 h1 换成 h2。
autoSelectFamilyAttemptTimeoutMs0Node happy-eyeballs 的地址轮换间隔(默认 250ms)。注意:尝试是并行保留的,这个值不是单地址的放弃窗口——黑洞地址实测默认与调大都会等到 connect 超时才死,改大只会拖慢轮换,一般别动。保留它只为极端调优。
delegateOnProxyEnvtrue有代理 env 时让路。
undiciPath""留空自动解析;可显式指到某份 undici/index.js。
probeOnStarttrue启动时打一次探针(默认打 LLM 网关的 models 端点,不花 token)。
probeUrl / probeTimeoutMs…/zen/go/v1/models / 15000探针目标与超时。

安装

只从 GitHub 分发(本插件是上游行为的补丁,不发 npm、不上市场):

dsh plugin --profile web add github:mqnu00/dsh-undici-timeout

装完重启 DSH 生效。也可以钉住某个版本:

dsh plugin --profile web add github:mqnu00/dsh-undici-timeout#v0.1.1

零 npm 依赖、无构建步骤,所以 pnpm 不会弹 allowBuilds 授权(本插件没有任何 prepare / install 脚本)。⚠️ 别和下面路线 B 的 file:// 直插同时用: 包内 cordis.patch.yml 的 insert id 也是 undici-timeout,同 id 冲突。

改完代码想知道「别人 clone 下来到底装不装得上」,本地跑一遍试装验证 —— 把包装进一个 临时 DSH_HOME 里的干净 profile、真启动一次、再请求插件自己的状态路由:

./scripts/trial-install.sh          # 走 tarball(等价于 GitHub Release 资产)
./scripts/trial-install.sh --git    # 走本地裸仓库 git+file://(等价于 github: 源)
./scripts/trial-install.sh --spec=github:mqnu00/dsh-undici-timeout   # 直接验真实远端
# 🎉 试装验证通过 = 组合成功 + 启动就绪 + 状态路由应答

打 tag / 建 Release 的步骤见 RELEASING.md。

路线 B:file:// 直插 profile 用户层(改代码时用这条)

~/.dsh/profiles/web/cordis.patch.yml 末尾追加:

- insert:
    - id: undici-timeout
      name: file:///home/mqnu/program/dsh/dsh-undici-timeout/lib/index.js?v=5
      config:
        enabled: true
        hosts: [opencode.ai]
        connectTimeoutMs: 30000
        # 本机 IPv6 无出口:别让地址族探测把预算烧在 10 个死地址上
        family: 4
        # undici 8 的 allowH2 默认是 true,不关掉就会把出口换成 h2(见「h2 的坑」)
        allowH2: false
        bodyTimeoutMs: 0
        keepAliveTimeoutMs: 60000

dsh-hmr 会监听该文件,原地写 2 秒内自动重挂(tmp+rename 会换 inode,收不到事件)。 ⚠️ insert 行按 id 幂等:只改 name(换 ?v=)或改 config 不会立刻重挂——要么摘掉再插回, 要么重启 DSH(HMR 的实测行为,和 dsh-net-autoswitch 一致)。 2026-10-15 复验:只把 ?v=4 改成 ?v=5 并加配置,HMR 确实重挂了(实例 id、installedAt 都换新),但跑的还是旧模块代码 —— 日志里没有新加的 h2=off 字段,状态路由里也读不到 allowH2。原因就是同 id 的 entry 按旧 URL 重新 import。摘掉(等 HMR 跑一轮)→ 再插回, 这次 ?v=5 才真正被 import,h2=off 出现在日志里。改代码务必按这个顺序。

h2 的坑(2026-10-15 定位,allowH2 就是为它加的)

症状:插件明明在跑(matched 一直涨),却仍然「很频繁地失败」,重试日志里 code=TRANSPORT 暴涨。

根因是一条静默换协议:

用的 undiciALPN 实测结果
内置 fetch(Node 22 自带)6.24.1http/1.1h1
本插件(argv 解析到 harness 那份)8.11.2h2h2

undici 8 把 allowH2 的默认值从 false 翻成了 true (lib/core/connect.js: allowH2 = allowH2 != null ? allowH2 : true, lib/dispatcher/client.js: “enabled by default”)。我们用 undici 8 建 Agent, 于是 LLM 出口从 h1 变成了 h2 —— 这不是本插件想要的,却实实在在换了传输层。

代价:opencode.ai 前面是 Cloudflare,h2 上的 SSE 长流会被边缘掐:

请求失败 CONNECTION(2275ms)…/chat/completions :: ERR_HTTP2_STREAM_ERROR / Stream closed with error code NGHTTP2_ENHANCE_YOUR_CALM
  • 2026-10-15 当天:插件记了 85 次失败,其中 79 次是 nghttp2 流错误(93%), 全部落在 /chat/completions(长流),/models(短请求)一次都没有;
  • 首次出现 ERR_HTTP2 是 2026-10-14T07:00,即代理被关掉、插件开始全量接管直连之后; 重试日志里 TRANSPORT 从 ~10/天 → 201/天(10-14),TIMEOUT 则从 238/天 → 4/天 (插件治超时的效果是真的,只是换了个失败形态)。

修法:allowH2: false(已进 DEFAULTS)。关掉之后复用一点没少:

4 连发(读完 body)新建连接数耗时
allowH2: false1924ms → 383 → 363 → 374ms
allowH2: true1947ms → 340 → 350 → 349ms

即 h1 + keepAliveTimeout 的复用与 h2 等价,「不重握手」的目标照旧达成, 而 Cloudflare 那套 h2 流控彻底不参与。

2026-10-15 03:31 热更后(?v=5,family=4 + allowH2:false)现场复验: 75 秒内 22 个请求(全是 /chat/completions)20 成功 0 失败,ERR_HTTP2 0 次 —— 同一台机器、同一分钟里,改之前是 85 次失败/天、其中 93% 是这个 h2 流错误。

直连失败的另一半:20 个地址里的 10 个是死的(2026-10-15 实测)

opencode.ai 的 DNS 一次回 10 个 IPv4 + 10 个 IPv6,本机 IPv6 无出口 (curl -6 秒失败)。地址族探测(happy eyeballs)会把预算烧在这 10 个死地址上:

同一分钟内的对照结果
裸 TCP 连 10 个 IPv4全部成功,245–1332ms
curl(双栈)/ curl -48/8、5/5 成功,connect 0.27–0.46s
内置 fetch(undici 6)2/6 成功,4 次 UND_ERR_CONNECT_TIMEOUT,~2511ms
插件 Agent,family=06/6 成功,中位 352ms
插件 Agent,family=46/6 成功,中位 379ms

所以「插件生效了吗」的答案是:生效,而且比内置通道明显强; 但 connectTimeoutMs: 30000 救不了这种形态 —— 整轮在 ~1.0s / ~2.5s 就死掉, 是地址族探测把整轮判死,不是 10s 的 connect 预算不够。family: 4 能把这半边也堵上。

WSL 里直连脆弱、本机代理(127.0.0.1:10808)实测 3/3 HTTP 200(~0.9s)—— 代理开着时插件按设计让路,也就不会被 h2 那套流控碰到。

状态/排障

# 读状态(在机器上直接 curl;Host 白名单只放行回环/本机网卡)
curl -s http://127.0.0.1:3080/_dsh/undici-timeout | python3 -m json.tool

# 手动补一枪探针
curl -s -X POST http://127.0.0.1:3080/_dsh/undici-timeout \
  -H 'x-dsh-undici-timeout: 1' -H 'content-type: application/json' \
  -d '{"action":"probeNow"}'
  • stats.matched/ok/failed/byError:接管了多少请求、失败怎么归类;
  • stats.delegatedProxy:因代理 env 让路了多少(联动在工作就正常);
  • stats.lastRequests:最近 10 条(host/path/ms/status/error);
  • lastProbe:最近一次探针(启动探针也会写日志)。
  • 日志:~/.dsh/logs/dsh-undici-timeout/host.log。

冒烟(不经过 DSH,验证解析与网络):

node test/live-smoke.mjs        # 本机实测:HTTP 200,约 1.6s
node --test test/*.test.mjs     # 单测(18/18)

对照探针(2026-10-15 定位上述两个问题用的,都能单独跑):

node test/h2-probe.mjs 8        # allowH2 默认 vs false:看 ALPN / 失败形态
node test/family-probe.mjs 6    # 内置 fetch / family=0 / family=4 / family=4+h1 四路对照
node test/burst-probe.mjs 12 3  # 同一条连接并发 12:h2 与 h1 是否被掐
node test/sse-probe.mjs 6       # 真 SSE(含中途 abort):h2 与 h1 对照,需 ~/.dsh/.credentials.yaml

实测 keep-alive 复用(3 连发):2358ms → 416ms → 420ms,后两次不再握手。

与 net-proxy 的共存

dsh-net-proxy 只在「代理开」时才包装 globalThis.fetch,且其包装器闭包惰性读配置; 本插件只在没有代理 env 时接管直连,两边互不拆台。开关顺序无论谁先上链都成立:

  • 代理关:我们的包装器在链上;直连请求走长超时 Agent;
  • 代理开:net-proxy 的包装器叠在上面,流量走它的中继;我们让路;
  • 代理再关:net-proxy 还原到它上任时的 originalFetch(就是我们)。

改代码之后

本插件刻意单文件(lib/index.js,零相对导入、零 npm 依赖),因为 loader 按 URL 缓存 ESM:改代码只需把 patch 里的 ?v=N 加 1(多文件时相对导入不跟随 query, net-autoswitch 的 runtime/vN 复制目录就是为绕这条)。只改 config: 不用动代码, 原地保存即热更。

实测记录(2026-10-13,本机)

项目结果
直连握手(裸 curl,含 IPv6 首试)11.6s(内置 fetch 的 10s 砍在这里)
curl -4 / curl -61.2s / 直接失败(本机无 IPv6 出口)
自建 Agent,connect=3s,黑洞地址3.5s ConnectTimeoutError(超时生效)
自建 Agent,family: 4200 ✓
keep-alive 复用2358ms → 416ms → 420ms
坏相位里的 ~1s 整轮失败是 OS/WSL 层的快速失败(ETIMEDOUT 毫秒级返回,node:tls 裸探针同样 ~1s 全灭),不是 undici/Node 的放弃计时器
黑洞地址 4 连 × 默认/5s/15s attemptTimeout三者都等到 connect 超时(12.5s)才死——尝试是并行保留的,attemptTimeout 只管轮换节奏
单测18/18 通过(0.1.1)

实测记录(2026-10-15,本机)—— 上面「h2 的坑」「20 个地址里的 10 个是死的」两节的原始数据

项目结果
opencode.ai 的 DNS一次回 20 个地址:10 个 IPv4 + 10 个 IPv6
裸 TCP 连这 10 个 IPv4全部成功,245–1332ms
curl -6秒失败(本机无 IPv6 出口)
curl(双栈,默认 happy eyeballs)8/8 HTTP 200,connect 0.27–0.46s
内置 fetch(undici 6.24.1,ALPN=h1)2/6 成功,4 次 UND_ERR_CONNECT_TIMEOUT,中位 2511ms
插件 Agent,family: 06/6 成功,中位 352ms
插件 Agent,family: 46/6 成功,中位 379ms
插件 Agent,ALPN(allowH2 默认)h2
插件 Agent,allowH2: falsehttp/1.1,4 连发新建连接数 = 1(复用不受影响)
同一连接并发 12 × 3 轮(/models)h2 36/36、h1 36/36 —— 简单并发压不出 ENHANCE_YOUR_CALM,它只在 SSE 长流上出现
本机代理 127.0.0.1:108083/3 HTTP 200,~0.9s(代理开着时插件让路,也就碰不到 h2 那套流控)
logSlowMs0>0 时把达到该耗时的请求写日志。
logFailurestrue失败请求记日志(含 cause)。
routePath/_dsh/undici-timeout状态路由。
logFile$DSH_HOME/logs/dsh-undici-timeout/host.log日志落盘。