dsh-web-fetch-fakeip
A fake-ip-aware ctx.web fetch provider that takes over the stock http provider id: a hostname is accepted when every DNS answer is a placeholder inside the proxy's fake-ip block (198.18.0.0/15 by default), so web_fetch works under mihomo/Clash TUN mode without disabling fake-ip or setting proxy envi
Install
dsh plugin --profile web add dsh-web-fetch-fakeip
Sandbox test
This plugin has not been tested yet. Submit a test →
README
description: "面向 DeepSeek Harness web 接缝(ctx.web)的 fake-ip 感知 WebFetchProvider:在 mihomo/Clash TUN 模式 + fake-ip DNS 下让 web_fetch 工具继续可用,无需关闭 fake-ip,也无需设置任何代理环境变量。"
kind: "package-reference"
dsh-web-fetch-fakeip
[English](README.md) | 中文
@deepseek-ai/dsh-web-fetch-http 的直接替代品:当本机 DNS 处于 fake-ip
模式、且网络走透明代理(TUN)时,让 web_fetch 工具继续可用——fake-ip 保持
开启,且不需要设置 http_proxy / https_proxy / all_proxy。
摘要
dsh-web-fetch-http 会自行解析每个主机名,并要求所有应答都是公网 unicast
地址,否则拒绝请求。而在 fake-ip 模式下,mihomo 与 Clash 会用
dns.fake-ip-range(默认 198.18.0.1/16)中的占位地址应答每一个主机名。
ipaddr.js 把 198.18.0.0/15 归类为 reserved(RFC 2544 基准测试段),因此
原版 provider 会拒绝所有主机名:
Error: URL hostname "example.com" resolves to a non-public IP address
curl 等工具不受影响,因为它们根本不做这项校验:它们把包发往占位地址,TUN
网卡截走并交给代理,由代理解析真实源站。原版 provider 的代理逃生通道同样无效,
因为 proxyRouteFor() 只读 http_proxy / https_proxy / all_proxy,而 TUN
方案一个都不需要设。
本插件完整复用原版 provider 的传输层,只替换目的地判定策略。
目录
- [快速开始](#快速开始)
- [示例配置](#示例配置)
- [配置项](#配置项)
- [改变与不改变的行为](#改变与不改变的行为)
- [诊断你的环境](#诊断你的环境)
- [工作原理](#工作原理)
- [开发](#开发)
- [安全说明](#安全说明)
- [已知限制](#已知限制)
- [许可证](#许可证)
<a id="快速开始"></a>
快速开始
安装到 profile
本包已发布到 npm。裸安装解析到 latest:
dsh plugin --profile web add dsh-web-fetch-fakeip
从 DSH 0.1.2-rc.1 到 0.2.x 的所有线,这条命令都是对的。上一版 latest
(0.1.1)会被 DSH 0.2.0-rc.2 的兼容性门禁跳过;若你在该 DSH 版本上且早于
本次发布就已安装,执行一次更新即可:
dsh plugin --profile web update dsh-web-fetch-fakeip
dsh plugin 会把参数原样转发给 profile 内的 pnpm,因此任何 npm 描述符都可以
——精确版本(dsh-web-fetch-fakeip@0.2.0)、dist-tag(...@next)、git URL
或本地路径。
dist-tag 语义:latest 跟踪当前 DSH 线。版本号带预发布后缀(-rc.1、
-alpha.2)的发布走 next,因此 latest 只会移向刻意切出的版本。插件的
minor 版本即它支持的 DSH minor 线——0.2.x 支持 DSH 0.2.*——详见
[版本策略](CHANGELOG.md#versioning-policy)。
也可以从本地 checkout 安装:
# 在包含本仓库的目录下执行。相对路径以你当前所在目录为基准,而非 profile。 dsh plugin --profile web add /path/to/dsh-web-fetch-fakeip
由于本包在 package.json 中声明了 dsh.bundle,dsh plugin 会自动把它加入
dsh.profile.bundles,并把它的 [cordis.patch.yml](cordis.patch.yml) 作为一层
应用:禁用原版 web-fetch-http 行,插入本 provider。
重启 profile(或等 patchReload: live 监听器生效)后,web_fetch 即可恢复。
手动安装
若不想安装为包,可把目录复制进 profile,再用 patch 行指向文件:
cp -r dsh-web-fetch-fakeip "$DSH_HOME/profiles/web/plugins/"
# $DSH_HOME/profiles/web/cordis.patch.yml
- id: web-fetch-http
disabled: true
- insert:
- id: web-fetch-fakeip
name: './plugins/dsh-web-fetch-fakeip/src/index.js'
patch 会整体替换目标行的 config;匹配不到任何行的 patch 只会告警并被跳过,
所以即使原版行不存在,上面的 disabled: true 也是安全的。
验证
# 应打印出组合结果:web-fetch-http 已禁用,web-fetch-fakeip 已插入。 dsh --profile web --dump-config | grep -A3 fakeip
随后让 agent 抓取一个页面,或直接跑真实网络测试:
npm run test:live
安装或卸载 bundle 后必须重启。 profile 的 patchReload: live 监听器只会
重新读取 cordis.patch.yml;而 dsh.profile.bundles 列表只在启动时读取一次。
安装 bundle 会立刻把它写入该列表(因此 --dump-config 能看到),但正在运行的
host 会一直沿用启动时的层栈,直到重启。
卸载
dsh plugin --profile web remove dsh-web-fetch-fakeip
dsh plugin 会在 pnpm 结束后核对 dsh.profile.bundles,因此该 bundle 层会随
依赖一起离开层栈。之后重启 profile。原版 web-fetch-http 行会自动恢复——那条
禁用来自本 bundle 的 patch,所以移除 bundle 也就移除了禁用。
更新
dsh plugin --profile web update dsh-web-fetch-fakeip
不带 ref 的 github: 说明符跟踪仓库的默认分支,pnpm 在安装/更新时才解析它——
因此更新是显式的,绝不会静默发生。
DSH 升级后插件不再加载时
自 DSH 0.2.0-rc.2 起,若某个 bundle 的 peerDependencies 不包含当前运行的 DSH
版本,harness 会拒绝加载该 bundle,并在启动时提示一次:
dsh: skipping profile bundle "dsh-web-fetch-fakeip": Error: Plugin dsh-web-fetch-fakeip@0.1.1 is incompatible with dsh 0.2.0-rc.2: ...
此时 web_fetch 工具的表现与本插件存在之前完全一样——fake-ip DNS 下每个主机名
都以 resolves to a non-public IP address 失败。这不是崩溃:bundle 被跳过,原版
provider 接管了。
正确的修法是更新:
dsh plugin --profile web update dsh-web-fetch-fakeip
peer 范围采用的是有界区间(>=0.1.2-rc.1 <0.3.0-0),而不是逐一罗列已发布的
预发布版本,因此新的 0.2.x 或 0.2.0 正式版不会再触发该门禁。0.3.0 线则
有意仍会触发:那是一个尚未经过任何验证的兼容性声明,harness 要求明确决策是
对的。
也可以用 dsh plugin allow-version 授予精确版本豁免。那是"你确知接缝未变"时的
逃生通道,而不是修复方案。对本插件而言,诚实的答案就是更新——出问题的是声明,
不是代码。
<a id="示例配置"></a>
示例配置
常见情形:mihomo/Clash 默认网段
无需任何配置——每个字段的默认值都与原版 provider 一致。随包提供的
[cordis.patch.yml](cordis.patch.yml) 就是这样做的:
- id: web-fetch-http
disabled: true
- insert:
- id: web-fetch-fakeip
name: 'dsh-web-fetch-fakeip'
显式写出的版本见
[examples/mihomo-default.patch.yml](examples/mihomo-default.patch.yml)。
非默认的 dns.fake-ip-range
把 fakeIpRanges 改成代理实际写入的网段,否则 fake-ip 主机名仍会失败:
- id: web-fetch-http
disabled: true
- insert:
- id: web-fetch-fakeip
name: 'dsh-web-fetch-fakeip'
config:
fakeIpRanges:
- 10.18.0.0/16
- 198.18.0.0/15
允许配置多个网段;只要一组应答中的每个地址都落在某个已配置网段内,该组应答
就被接受。这个例子加上收紧的传输限制见
[examples/custom-range.patch.yml](examples/custom-range.patch.yml)。
查询代理的实际网段
代理 · 配置键
mihomo / Clash / Clash Verge · dns.fake-ip-range
sing-box · dns.fakeip.range
对 GUI 客户端而言,实际生效值在生成的运行时配置里,而不是订阅配置里——到
客户端自己的配置目录中查找上表中的键,并确认 enhanced-mode 为 fake-ip:
dns: enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16
scripts/diagnose.mjs 会直接读回 DNS 的实际应答,因此你不必找到那个文件也能知道
该配置什么。
<a id="配置项"></a>
配置项
每个字段的默认值都与原版 provider 一致,因此不带 config: 挂载本插件时,改变的
只有一件事:目的地如何判定。
字段 · 默认值 · 含义
fakeIpRanges · ['198.18.0.0/15'] · 可作为 fake-ip 应答接受的占位地址段
maxResponseBytes · 5000000 · 响应体最大字节数
maxBodyChars · 100000 · 解码后正文最大字符数
timeoutMs · 30000 · 抓取超时——资源兜底,不是工具预算
maxRedirects · 5 · 同源重定向最大跳数(0 表示不跟随)
userAgent · deepseek-harness/0.0.1 (+https://github.com/deepseek-ai) · 每个请求发送的 User-Agent
非法值会在插件构造时直接报错,而不是构造出一个限制值荒谬的 provider——与原版
provider 一致。URL 长度上限固定为 2048 字符。
为什么默认是 198.18.0.0/15 而不是 198.18.0.1/16
mihomo 的文档默认值是 198.18.0.1/16,它是 198.18.0.0/15 的子集。较宽的
网段正是 RFC 2544 基准测试段整体,因此默认值同时覆盖两种写法以及部分配置使用的
相邻网段。若你希望更严格,收窄到 /16 是安全的。
<a id="改变与不改变的行为"></a>
改变与不改变的行为
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::6,最后更新 2026-10-03