Why-147369/dsh-line-pool

dsh-line-pool

DSH 出口线路池(测试阶段): 集中管理 GitHub/npm/PyPI/HF 出站线路,自动探测健康度、按速选线、失败切换;子进程走正规通道且零污染全局配置

미분류

dsh-line-pool · DSH 出口线路池

⚠️ 测试阶段(v0.6.3) — 核心能力已实现并通过 562 项测试(离线 472 项,不含核对脚本),但在真实使用中的长期表现尚未积累数据。接口与线路表可能调整。欢迎试用与反馈,不建议在生产环境依赖。

给 DeepSeek Harness 一个集中管理的出站线路池:把 GitHub / npm / PyPI / HuggingFace 等各类出口的候选线路收在一处,自动探测健康度、按速度选线、失败自动切换,让模型不再把时间浪费在慢线路和超时上。

  • 许可:MIT
  • 平台:Windows(DSH Desktop 实测);核心逻辑跨平台
  • 无需管理员权限:不改 hosts、不装 CA、不设系统代理
  • 状态:测试阶段(见上方提示)

快速开始

cd <你的路径>\line-pool
node install-line-pool.mjs install    # 装进 profile 并跑 pnpm install

然后完全退出并重启 DSH(不是刷新页面)。重启后自动生效,无需任何配置:

  • 模型写 pip install requests → 实际执行 pip install -i https://mirrors.aliyun.com/pypi/simple/ requests
  • 模型写 git clone https://github.com/o/r → 自动走最快的 GitHub 镜像
  • 面板出现在右侧栏(装了 dsh-better-sidebar 则在「设置 → 侧边卡片」)

装完没反应?看 %USERPROFILE%\.dsh\line-pool\line-pool.log,或查故障排查。


这个插件解决什么

原始痛点不是"不通",而是**"用错了那条"**:

npm 装包     官方源超时 10.3s  →  腾讯镜像 224ms        差 46 倍
GitHub raw   直连 653ms 可用   →  而 DSH 里硬编码的第一条兜底 gh-proxy.com 是死的
pip 装包     官方源 817ms      →  阿里云镜像 983ms      ← 反直觉:官方更快

最后一条尤其关键:"国内镜像一定快"是错的。速度随时间和网络环境波动,必须实测,不能按刻板印象写死。本插件就是干这个的。


它能干什么

能力说明
线路池管理内置 15 条实测过的线路(GitHub raw/codeload/main、npm、PyPI、HF、通用直连),外加 10 条已知失效线路备查
两阶段探测先并发 TCP/TLS 握手(便宜)→ 按延迟+丢包排序 → 只对前 N 条做真实 HTTP 拉取(贵)。15 条线路约 7 秒探完
健康状态机HEALTHY → COOLING → PROBING,失败只认 DNS/TLS/超时/断连四类;冷却阶梯 2→5→15 分钟,成功即恢复
按类选线专线优先、通用兜底。github-raw 请求只走 GitHub 类线路,全挂才用直连垫底
URL 改写4 种规则:前缀拼接(gh-proxy 类)、registry 替换、index 替换、域名替换(HF);另有 rewrite: null 表示直连不改写
子进程传递git / pip / npm / pnpm / gh / curl 各走各自的正规通道,零污染用户全局配置
上游 IP 表自验拉取公开的 GitHub 域名→IP 表(多源自动切换),逐个 IP 做 TLS 握手 + 证书链校验(自签名/过期/域名不符一律拒),只保留真正可用的 IP;导出 hosts 片段供用户自行应用
线路表自动更新内置 20 条候选镜像,全部经真实握手 + HTTP 验证后才纳入(实测:纳入 16 / 拒绝 4)。每次启动后若无记录则自动发现一次,之后每 24 小时一遍;另支持人工补充(lines-custom.json)。优先级 custom > auto > seed
侧边栏面板可视化查看每条线路的健康/延迟/失败原因、按类别的选线顺序、上游自验结果;支持单条手测、启停线路、手动重测、复制 hosts 片段。挂在 dsh-better-sidebar 的侧边卡片上(可启停开关),没装时自动回退到 DSH 原生右侧栏
接线:给模型的工具注册 line_pool_status(查现在哪条快、各类目标会选哪条)与 line_pool_run(跑一条下载命令并自动注入最快线路的镜像参数)
接线:拦截子进程包住 DSH 的 shell seam,自动改写模型的下载命令:git clone → URL 加前缀、pip install → 插 -i、npm/pnpm → --registry=、HF → HF_ENDPOINT、curl/wget → URL 加前缀。改写失败或无可用线路 → 原样放行;用户已指定镜像 → 不覆盖;命令结构复杂 → 整体放弃
网络环境检测面板常驻显示 TUN 开关状态与本地代理端口:网卡名 + TUN 常用网段(198.18/15)实时判定,纯本地零成本;代理端口扫描 13 个常见端口(回环,7ms)。两枚芯片均可点击立即重测
状态可观测探测结果落 status.json + 日志;每次探测后刷新

覆盖范围(诚实声明)

范围是否覆盖原因
模型用 bash 工具跑的下载命令✅ 覆盖插件包住 ctx.shell.execute,命令到达 shell 前被改写
模型主动调 line_pool_run✅ 覆盖插件注册的工具,显式走线路池
其他插件显式调 ctx.linePool.fetchLine()✅ 可覆盖但需要调用方自己这么做,插件不会替它接管
DSH 宿主自身的 fetch❌ 不覆盖技术可行(P0 实测 setGlobalDispatcher 能接管原生 fetch),但本插件从未接线。当前接管方式只有上表前两条
Electron 渲染进程的 fetch❌ 不覆盖走 Chromium 网络栈,与 Node/undici 是两套独立栈(实测确认,见下文)

⚠️ 上表第 4 行是最容易误解的一条:"装了本插件"不等于"DSH 里所有网络请求都会走最优线路"。 它只接管模型跑的命令与显式调用。宿主内部、页面里、其他插件自己的 fetch 都不受影响。 B 方案(接管宿主 fetch)在 P0 验证过可行性,但未实现 —— 见 P0/verdict.md。

它不做什么(同样重要)

不会做的事原因
不设系统代理、不改注册表一旦进程崩溃不恢复,会导致整机断网(dev-sidecar 记录过这类副作用)
不装 CA 证书、不做 MITM装 CA 需要管理员权限,且部分应用仍不信任;出问题排查成本极高
不自动写 hosts写 hosts 要管理员权限,写错会让整机某些域名全挂。只产出已验证片段,应用与否由你决定
不修改你的全局配置不写 ~/.npmrc、不写 git config --global、不写 pip.conf。所有改写都用命令行参数或单次 env 传递
不联网上报任何数据只有出站请求(拉上游 IP 表、探测线路),没有遥测、没有回调

设计原则:能力靠 URL 改写实现,出问题最多影响一次请求,不会让整机断网。


安装

前置

  • DSH Desktop(本机实测 0.2.0-rc.2,Node 24.18.1)
  • 无第三方依赖(v0.6.1 起移除了 undici —— 它当时只用于一个没人读的版本字符串)。 插件的宿主包解析走 createRequire().resolve(),从 profile / DSH 安装目录动态定位,不写死路径

安装步骤

# 1. 进入本目录
cd <你的路径>\line-pool

# 2. 安装(自动改 profile 的 dependencies + bundles,并跑 pnpm install)
node install-line-pool.mjs install

# 3. 完全退出并重启 DSH
#    重启后插件会自动做首次探测

# 4. 查看结果
#    日志  : %USERPROFILE%\.dsh\line-pool\line-pool.log
#    快照  : %USERPROFILE%\.dsh\line-pool\status.json

可选参数:

node install-line-pool.mjs install --profile=<profile名>       # 默认 desktop
node install-line-pool.mjs install --profile-dir=<绝对路径>     # 直接指定目录
node install-line-pool.mjs status                               # 只看状态,不改动

卸载

node install-line-pool.mjs uninstall
# 然后重启 DSH。数据目录会保留(供诊断),需要的话手动删 %USERPROFILE%\.dsh\line-pool

怎么用

1. 看线路状态(安装后无需配置)

重启后自动探测,结果在 %USERPROFILE%\.dsh\line-pool\status.json:

{
  "lastProbeAt": "2026-10-16T10:02:45.194Z",
  "envInfo": { "viaProxy": false, "note": "未检测到代理环境变量…" },
  "lines": [
    { "id": "pypi-aliyun", "healthy": true, "latencyMs": 75, "state": "HEALTHY" }
  ],
  "availableByKind": {
    "github-raw": ["gh-raw-ghfast", "gh-raw-direct", "http-direct"]
  }
}

availableByKind 就是每类请求会按什么顺序尝试——排在最前的是最快的。

2. 把请求走选中线路(宿主内)

// 在你的插件里:从 ctx 拿到本插件实例
const pool = ctx.linePool;

// 按类别发请求,自动选最快线路 + 失败自动换下一条
const res = await pool.fetchLine('github-raw', 'https://raw.githubusercontent.com/a/b/c');

3. 让子进程走选中线路

import { spawnWithLine } from 'dsh-line-pool/lib/spawn.js';

// 各工具自动选正经通道,不改任何全局配置
await spawnWithLine('git',  ['clone', 'https://github.com/a/b'], line);
await spawnWithLine('pip',  ['install', 'requests'],            line);
await spawnWithLine('npm',  ['install', 'lodash'],              line);

先用 planSpawn 预览会执行什么(不实际运行):

import { planSpawn, planToCommandLine } from 'dsh-line-pool/lib/spawn.js';
const plan = planSpawn('git', ['clone', 'https://github.com/a/b'], line);
console.log(planToCommandLine(plan));
// git -c http.proxy=http://127.0.0.1:7890 clone https://github.com/a/b
console.log(plan.warnings);  // 例如 --trusted-host 的安全提示

4. 上游 IP 表自验(可选)

插件每 30 分钟自动拉取一次公开的 GitHub 域名→IP 表,逐个 IP 做 TLS + 证书链校验,结果落 %USERPROFILE%\.dsh\line-pool\upstream-ips.json,并生成可直接使用的 hosts 片段 %USERPROFILE%\.dsh\line-pool\hosts-fragment.txt。

import { fetchAndVerify, saveUpstreamSnapshot } from 'dsh-line-pool/lib/upstream.js';

const up = await fetchAndVerify({ onLog: console.log });
console.log(up.verified);
// { 'github.com': [{ ip: '20.205.243.166', ms: 8 }], ... }
console.log(up.domainsWithoutUsableIp);   // 上游给了 IP 但实际全部连不上的域名

为什么不自动写 hosts:需要管理员权限(本机实测 Access denied),而且写错会导致整机某些域名全挂。插件只产出经过验证的片段,是否应用由用户决定(可粘贴到 hosts,或用 SwitchHosts 等工具)。

为什么必须自验:实测证据 —— 上游表给的 IP 里有相当比例从本机根本连不通。 2026-10-16 一次实测:7 个 IP 中 2 个不可用(gist.github.com、github.global.ssl.fastly.net 全部失败)。 上游 IP 表不是全可用,且可用性会波动,所以自验必须每次都做,不能缓存一次结果永久使用。

为什么判据是 TLS 而不是 TCP:TCP 握手可以被「本地应答」欺骗 —— 本机有 TUN/代理时, 连 RFC 5737 保留地址都能秒回成功(实测 3ms 连上 Azure 是不可能的)。 所以判据是 TLS 握手 + 证书链过系统 CA + CN/SAN 覆盖目标域名,三者缺一不可。 伪造一张 CN=github.com 的自签名证书会被拒(报 CHAIN_INVALID)。

5. 侧边栏面板

装好后会出现在两个地方(取决于你是否装了 dsh-better-sidebar):

  • 装了 dsh-better-sidebar → 设置 → 侧边卡片 里出现「线路池」卡片(带启停开关),同时作为原生 chip 出现在右侧栏
  • 没装 → 直接从右侧栏 + 菜单打开(自动回退到 DSH 原生右侧栏)

面板内容:

┌─ 线路池 ───────────────[重测]─┐
│ 3 分钟前探测                    │
├──────────────────────────────┤
│ 按类别(选线顺序,可折叠)        │
│  ▾ github-raw       3 条        │
│    ● GitHub raw 直连     97ms   │  ← 实心=当前首选
│    ○ ghfast.top 前缀   218ms   │
│    ○ 直连(不使用代理)  —       │
├──────────────────────────────┤
│ 全部线路(15/15 启用)           │
│  ● 阿里云 PyPI  pypi-index  75ms │
│  ◐ hf-mirror    hf          —   1:42  ← 冷却倒计时
│  ○ npmjs 官方源 npm-registry —  TIMEOUT
│              [测] [停]           │  ← 单条手测 / 启停
├──────────────────────────────┤
│ 上游 IP 自验                    │
│  源 github520-json · 可用5/不可用2 │
│  ⚠ 无可用 IP: gist.github.com…   │
│  [复制 hosts 片段]               │
└──────────────────────────────┘

两个功能开关(在卡片的「功能设置」里):

开关默认作用
自动探测开关闭后host 侧每 10 分钟的周期探测停止,面板也不自动刷新(只能手动「重测」)。选择持久化,重启后延续
显示上游 IP 自验开关闭后隐藏面板底部的上游区块

注意:better-sidebar 的卡片开关在关闭状态下不绘制开关图形(它用"开关消失 + 卡片失去高亮"表达已关闭)。点击卡片主体仍可重新启用。这是上游设计,对所有卡片一致。

6. 线路表自动更新

为什么需要:镜像死亡率极高(gh-proxy 类实测 9 个里 7 个死),而且会复活——实测发现我 5 小时前手工判定"死了"的线路里,gh-proxy.com、ghproxy.net、清华 PyPI、npmjs 官方源都已经恢复可用。写死的清单永远不会知道这些变化。

机制:

候选池(内置 20 条) → 逐条真实握手+HTTP 验证 → 通过的纳入 / 不通过的拒绝并记原因
                                                    ↓
                            <数据目录>/lines-auto.json(含纳入与拒绝的完整记录)
                                                    ↓
                     与种子表、自定义表合并(custom > auto > seed,同 id 高优先级覆盖)
  • 绝不盲信清单:候选池里的条目只是"值得一试",必须实测通过才纳入(依据:上游 hosts 表的 7 个 IP 里 2 个根本连不上)
  • 不覆盖种子表:自动发现的结果单独存;某条自动线路死了不影响出厂基线
  • 带来源标记:纳入的条目都带 source: 'auto-discovered' 与 discoveredAt,便于排查与撤销
  • 覆盖时保留手动开关:同 id 覆盖时保留用户设过的 enabled 状态,不会因为重新发现而把你的禁用改回启用
  • 调度:启动时若无记录则跑一次;之后每 24 小时一遍

实测效果(2026-10-16):

纳入 16 / 拒绝 4(共 20)
  ✓ gh-proxy.org   717ms   ← 新发现,最快的 GitHub 镜像
  ✓ gh-proxy.com  2029ms   ← 此前被我判为"死"(TIMEOUT),已复活
  ✓ npmjs 官方源    747ms   ← 此前超时 10.3s,现已恢复
  ✗ github.moeyy.xyz  DNS 失败      ✗ npm.taobao.org  TLS 错误(旧域名证书失效)
  ✗ huggingface.co    超时          ✗ gh.ddlc.top      16 秒超时

这组数字的价值不在具体毫秒数(会变),而在结论:同一条线路几小时内可能 死而复生,写死的清单永远跟不上,所以必须每次实测。

人工补充:写 <数据目录>/lines-custom.json(可用面板的 customLinesTemplate RPC 取模板):

{
  "lines": [
    {
      "id": "my-mirror",
      "kind": "github-raw",
      "name": "我的 GitHub 镜像",
      "enabled": true,
      "rewrite": { "type": "prefix", "prefix": "https://example.com/" },
      "probeUrl": "https://example.com/https://raw.githubusercontent.com/torvalds/linux/master/README",
      "probeKind": "http"
    }
  ]
}

必填 id / kind / name / probeUrl;rewrite 见 lib/rewrite.js 的 4 种类型(prefix / registry / index / host-replace;null 表示直连不改写)。缺字段的条目会被忽略并记日志。custom 优先级最高,可以覆盖种子表或自动发现的同 id 条目。

7. 两个入口(都在安装后自动生效)

入口 A —— 注册给模型的工具(模型主动调用)

工具用途
line_pool_status查当前哪条线路健康、各类目标会选哪条
line_pool_run跑一条下载命令,自动注入最快健康线路的镜像参数

入口 B —— 拦截子进程(模型不用做任何事)

接入点是 DSH 的 shell seam:ctx.shell.execute(spec)。插件包住这个方法, 只在命令里确实有可改写的下载命令时才动。四道安全阀:

情况行为
改写失败 / 没有可用线路原样透传(绝不会因为插件出错而让命令跑不了)
用户已显式指定镜像(-i / --registry / --endpoint)尊重用户,不覆盖
命令结构复杂($(...)、heredoc、引号未闭合)整体放弃改写,不半改
URL 含 user:token@ 形式跳过改写,避免凭据被送到第三方镜像

改写明细见日志:[shell-hook] 改写: 「pip install x」→ …(线路 pypi-aliyun)

注意:入口 B 只覆盖模型跑的 bash 命令。被 pwsh 工具包装过的命令 (第一 token 不是 pip/git/npm)不会被识别 —— 这是设计如此,详见「覆盖范围」。


子进程线路传递:各工具的正规通道

工具用什么为什么不这么用别的
gitgit -c http.proxy=<url>-c 单次生效、无持久状态、天然可逆。不写 git config --global(污染用户配置)
pip-i <index> --trusted-host <host>这是换源不是代理。--extra-index-url 绝不可当 failover——pip 官方文档原文:"There is no priority in the locations that are searched",所有源都会被查,一个慢/挂的会拖死安装
npm / pnpm--registry <url>不改 ~/.npmrc
ghHTTPS_PROXY / https_proxy envgh 没有镜像概念,只能直连或走代理
curl-x <url> 或 URL 前缀改写—
其他env 代理(若线路提供 proxyUrl)能做的有限,如实标注

大小写都设(HTTPS_PROXY + https_proxy):不同工具认的大小写不同,两个都设最稳。


验证与测试

node test/run-all.mjs                 # 一键跑全部 21 套件(含联网),570 项
node test/run-all.mjs --offline       # 只跑不联网的(16 套件,480 项)
node test/run-all.mjs --json          # 额外输出机器可读摘要

# 或单独跑
node test/unit.test.mjs            # 68 项
node test/apply.test.mjs           # 20 项(插件生命周期 + 工具执行 + P0 回归)
node test/client.test.mjs          # 7 项(客户端模块:加载 + apply)
node test/client-render.test.mjs   # 12 项(客户端渲染:预置 state 真渲染)
node test/apply-online.test.mjs    # 9 项(联网:有可用线路时的成功路径 —— picked 映射 / 真实改写 / 4 个 RPC 端点 / 开关落盘)
node test/fetchline.test.mjs       # 12 项:fetchLine 换线逻辑(403/429/5xx)+ diagnoseFetch 入参校验
node test/client-interact.test.mjs # 10 项:面板交互(4 个按钮 / 复制 hosts / 错误态)
node test/tunnel.test.mjs          # 12 项:TUN 网卡识别(跨平台 + 误报检验)/ tunnelProbe 三态
node test/upstream-failures.test.mjs # 25 项:tcpCheck 防崩溃 / 证书匹配边界 / verifyIps 不跳过 / 解析边界
node test/degradation.test.mjs     # 8 项:A/C/RPC 服务缺失时的降级(失败必须可见,不静默)
node test/spawn.test.mjs           # 40 项:子进程参数规划(含"不污染全局配置"强断言)
node test/check-plugin.test.mjs    # 124 项(含 P1 回归):文件结构 / import / patch / 注册形态契约 / A+C 接线
node test/upstream.test.mjs        # 22 项:上游表解析 / 分组 / hosts 片段(离线)
node test/rpc.test.mjs             # 28 项:RPC endpoint 分派 / 错误处理 / 开关同步
node test/lines-auto.test.mjs      # 25 项:线路合并优先级 / 自定义线路解析(离线)
node test/cmd-rewrite.test.mjs     # 59 项(含多行/凭据断言):命令改写 + shell 拦截(离线)
node test/readme-facts.mjs          # 8 项:README 声明与仓库实况核对(离线)
node test/integration.test.mjs     # 12 项:真实线路探测 + 选线(联网,约 10 秒)
node test/spawn-verify.mjs         # 10 项:真机跑子进程 + 全局配置前后快照对比(联网)
node test/upstream.test.mjs --online # 30 项:上游真实拉取 + IP 自验(联网)
node test/lines-auto.test.mjs --online # 29 项:候选池真实验证与纳入(联网)

合计 570 项(离线 480 项)。其中 562/472 是插件自身的被测项数,另外 8 项是 README 事实核对脚本(它检查文档与仓库是否一致,不计入插件质量指标)。集成、真机与在线上游测试需要联网。

已实测的关键结论

结论证据
setGlobalDispatcher 能接管宿主原生 fetch录制型 dispatcher 录到 origin/path/method(11/11 PASS)
渲染进程 fetch 不被接管渲染进程实发 3 条请求(UA 含 Chrome/152),但录制器对该路径录到 0 次;对照组 host 自查路径录到 1 次
死线路快速失败ECONNREFUSED 2ms 内抛出
子进程零污染git config --global 前后 13 行完全一致;npm registry / pip config 均未变
国内镜像不一定快pip 阿里云 983ms vs 官方 817ms(同一包、同一时刻)
上游 IP 表不可盲信上游 7 个 IP 实测 2 个不可用(gist.github.com、github.global.ssl.fastly.net 全部握手失败)。判据是 TLS + 证书链而非裸 TCP —— 裸 TCP 在 TUN 下会被本地应答欺骗(连 RFC 5737 保留地址都秒回成功),而 TLS 判据在 TUN 下依然有效(实测 github.com 848ms、证书链通过)
ctx.get 看不到兄弟插件的服务better-sidebar 装着(0.26.0)但 ctx.get('betterSidebar') 返回 undefined;同次探测 ctx.get('slots') 正常 → 是作用域可见性问题(cordis 的 get 只查自己这层 store,不沿 fiber 父链上溯)
可选依赖的正确写法是 ctx.injectctx.inject(['betterSidebar'], cb) 真机验证:回调会执行、scoped.betterSidebar 属性直取可用、有 registerTab;依赖缺失时静默不执行且不报错
只有 better-sidebar 路径才有设置页卡片其卡片列表来自自己的 getTabs(),不收录外部注册到原生 sidebarRightTabs 的类型
卡片开关只认 === true上流源码 checked: read(key) === true;未写入时为 undefined → 显示为关,需要注册时补默认值

技术决策与依据

为什么只做「URL 改写」,不做 hosts / 代理 / CA

三条路实测对比(详见 P0/verdict.md):

路线结论证据
改 hosts❌无管理员权限写不了 C:\Windows\System32\drivers\etc\hosts(实测 Access denied);写错=整机某些域名全挂
本地代理 / MITM❌必须装 CA + 改系统代理。同类项目 dev-sidecar 的 README 记录了实证副作用:装了 CA 浏览器(Firefox)仍可能不信任;应用崩溃不恢复系统代理会导致整机断网;与其它代理软件冲突
镜像 URL 改写✅零权限、零副作用、失败只影响一次请求

旁证:VS Code 官方文档也是这个哲学——自己不改 hosts、不装 CA,而是"声明需要放行的域名清单 + 完全交给系统代理栈",甚至承认 "Extensions don't benefit yet by the same proxy support that VS Code supports"。

健康检查算法为什么不引熔断库

调研了同类项目(nrm / chsrc / GitHub520 / fetch-github-hosts / Watt Toolkit / dev-sidecar / CloudflareSpeedTest / dsh-geek2api 等),没有一个实现指数退避或半开熔断。主流是两档:固定短冷却 + 定期全量重算。线路数是个位数时全量探测只要几秒,不需要"半开"来省成本。所以本插件自写状态机(state.js 约 160 行),少一个依赖。

两阶段探测(抄 CloudflareSpeedTest 的行业标准做法)

  1. 并发 TCP/TLS 握手(几毫秒级,可全量)
  2. 按「延迟升序 + 丢包分档」排序
  3. 只对前 N 条做真实 HTTP 拉取(贵)

两条继承来的硬警告:

  • 探测前必须检测代理/TUN——否则测到的是代理链路的延迟,"最优线路"是假的。本插件用三级信号判定:① 读 HTTPS_PROXY / https_proxy / NODE_USE_ENV_PROXY;② 查本机网卡(utun/tun/wintun/FlClash 等名称 + 198.18.0.0/15 网段)——这是 TUN 的关键信号,环境变量里看不到;③ 连通性探测(探 google:443)交叉验证。三者任一判定有隧道就改用 HTTP 层判定健康度(TUN 下 TCP 握手对所有目标都成功,判据失效)
  • 开机首次测速延迟偏高,结果仅供参考

探测目标不可比的问题

http-direct(通用直连)的探针目标是别的站点,延迟天生更低。如果让它和 GitHub 专线同台比延迟,它总能"胜出"——但那是拿不同目标的延迟在比,不公平。所以选线规则是:专线优先,通用线路只在专线全挂时兜底。


已知限制

  1. 渲染进程 fetch 不覆盖——这是 Electron 架构决定的(Chromium 网络栈 vs Node/undici 是两套)。若需要覆盖页面发出的请求,必须让它们经宿主中转。
  2. 宿主自身的 fetch 也不覆盖——B 方案(setGlobalDispatcher)在 P0 验证过可行但未实现。当前只接管模型跑的 bash 命令与 line_pool_run。详见「覆盖范围」表。
  3. 线路表需要维护——gh-proxy 类镜像死亡率很高(2026-10-16 实测 9 个里 7 个死)。插件会按探测结果自动降级死线路,但种子表仍需人工偶尔补充新线路。
  4. gh 无镜像通道——只能直连或走代理。
  5. 上游 IP 表质量不稳定——实测含不可用 IP,且可用性随时间波动;插件会自验并标注"无可用 IP 的域名",但不会替用户修 hosts。
  6. TUN 检测有盲区——靠网卡名(utun/tun/wintun/FlClash 等)+ 198.18.0.0/15 网段判定。若某代理软件的 TUN 适配器既不含关键词、也不用该网段(如 Windows 上叫"以太网 2"且用 10.0.0.1),会漏检;此时靠连通性探测兜底,面板显示 TUN 开? 如实标注不确定。

目录结构

line-pool/
├── dsh-line-pool/              ← 插件本体(这就是被安装的包)
│   ├── package.json
│   ├── cordis.patch.yml        ← bundle 注册
│   ├── icon.svg                ← DSH 插件列表的图标
│   ├── locale/{zh,en}.json
│   ├── client.js               ← 面板(Client half):双路径注册 + UI
│   └── lib/
│       ├── index.js            ← 主入口:探测调度 / fetchLine / RPC 注册 / 状态输出
│       ├── lines-default.js    ← 种子线路表
│       ├── probe.js            ← 两阶段探测 / 失败分类 / 代理环境检测
│       ├── rewrite.js          ← 4 种 URL 改写规则(prefix / registry / index / host-replace)
│       ├── state.js            ← 健康状态机 + 带 TTL 持久化
│       ├── spawn.js            ← 子进程线路传递(planSpawn 纯函数 + spawnWithLine)
│       ├── upstream.js         ← 上游 IP 表拉取 + 自验 + hosts 片段
│       ├── rpc.js              ← 面板数据通道(endpoint 分派)
│       ├── lines-auto.js       ← 线路表自动更新(候选池验证 / 三来源合并)
│       ├── cmd-rewrite.js      ← 下载命令改写(7 类工具 × 4 种规则,纯函数)
│       ├── shell-hook.js       ← shell seam 拦截(包住 ctx.shell.execute,最小侵入可卸载)
│       └── tools.js            ← 注册给模型的工具(line_pool_status / line_pool_run)
├── test/                       ← 570 项测试(21 套件;562 项为插件自身)
├── P0/verdict.md               ← 架构验证结论(host 可接管 / 渲染进程不可)
├── P3/probe-findings.md        ← 可选依赖探测结论(ctx.get vs ctx.inject)
├── P3/panel-dual-scheme.md     ← 面板双方案设计
├── .github/README-CI.md        ← CI 激活说明(推 workflow 需 gh token 的 workflow scope)
├── install-line-pool.mjs       ← 安装器
└── README.md

说明:P0/probe-plugin/ 与 P3/probe2/ 是一次性验证用的探针目录,已从仓库移除 (它们的 package.json 也声明 dsh.bundle,会干扰上游收录 CI 的 manifest 扫描—— 详见 CHANGELOG v0.4.0)。探针源码保留在开发者的本地归档中,不在发布仓库里。


故障排查

现象原因处理
装完没反应,%USERPROFILE%\.dsh\line-pool\ 不存在没重启 DSH完全退出并重启(不是刷新页面)
安装时报 'pnpm' is not recognized本机 PATH 里没有 pnpm,且没能自动定位到 DSH 自带的那个安装器会打印它试过的位置。显式指定:node install-line-pool.mjs install --pnpm="<DSH安装目录>/resources/runtime/primary-runtime/dependencies/pnpm/bin/pnpm.cjs",或设 DSH_PNPM 环境变量。注意 DSH 的 pnpm 路径随版本变化过(runtime/pnpm/bin/ → runtime/primary-runtime/dependencies/pnpm/bin/)
装了但 pnpm 报 Already up to date 且 node_modules/ 下没有链接用了 file: 绝对路径依赖改用 link:(本安装器已用)
卸载后 node_modules/dsh-line-pool 还在pnpm 不移除 link: 软链本安装器会手动清;手动清用 cmd /c rmdir
安装成功但重启后插件没加载安装目录被移走/删除(link: 指向的路径失效)node install-line-pool.mjs status 看 linkTarget 是否可达;不可达就重装
探测结果显示"经过代理"环境变量里配了代理正常提示;若想测直连速度,先清代理环境变量
面板显示 TUN 开 但实际没开残留的 TUN 网卡(软件异常退出)点面板上的 TUN 芯片强制重测;仍有问题就查 ipconfig /all 里是否有 TUN 类适配器
某条线路一直 COOLING该线路确实挂了查 status.json 的 failure 字段(DNS/TLS/TIMEOUT/CONNECTION)
日志里 webServer 不可用(非致命)宿主 webServer 有鉴权(实测根路径 401),且 Electron 页面走 IPC 桥正常降级,改用 status.json 观测

维护与更新

  • 已收录:awesome-dsh-plugin(PR #7600,2026-10-17 合并)
  • 更新市场条目(改描述/分类、仓库迁移、废弃移除)见 UPDATING-MARKET-ENTRY.md
  • DSH 升级后自查:node test/run-all.mjs(21 套件 570 项),再看日志里 A/C 方案是否都生效

借鉴来源与许可

本插件为原创实现,但在设计与算法上借鉴了以下公开项目(详见 NOTICE.md 与 P0/verdict.md):

项目借鉴内容
XIU2/CloudflareSpeedTest两阶段测速剪枝算法、"可用数 vs 过筛数"双指标、两条探测警告
CoderKuo/dsh-geek2apiDSH 插件内多入口线路池的产品形态(10 分钟测速、失败冷却、状态展示)
521xueweihan/GitHub520上游 IP/域名数据源与多源切换的必要性
docmirror/dev-sidecar"代理路线不可行"的实证材料(CA/系统代理的副作用记录)
microsoft/vscode"声明域名清单 + 依赖系统栈,绝不自己造代理"的架构哲学
nodejs/undicisetGlobalDispatcher / ProxyAgent / EnvHttpProxyAgent 的官方语义

MIT License。第三方项目版权归原作者所有,本项目不包含其代码。