Skip to content

Releases: having5548/dsh-downloader

v0.6.0 — harnessProxy:让 harness 自身出网也走内核

Choose a tag to compare

@having5548 having5548 released this 17 Oct 09:01

dsh-downloader v0.6.0 — harnessProxy:让 harness 自身出网也走内核

原先只有 dsh_download 与 dsh_run_proxied 走代理。这一版把内核开放给 harness 自身。

用法(三步)

  1. 设置 → 下载代理 → 打开 「开放给 harness」(harnessProxy),端口默认 17890
  2. 调 dsh_harness_proxy({ action: "apply" }) —— 写入 $DSH_HOME/.env(写前备份)
  3. 重启 DSH

覆盖 / 不覆盖

会跟着走 不会跟着走
web_fetch Electron 渲染进程与内嵌 webview
shell 子进程里的 curl / git / npm 系统里其它程序(浏览器、别的 App)
MCP 的 HTTP 传输
jobs / workflow / subagent 派生的进程

为什么 webview 不行:Chromium 读的是系统代理,不读环境变量。插件不会去改系统代理(那会波及全机,且与「只服务 DSH」的边界冲突)。想覆盖真浏览器只能设系统代理,而且需要一个真的 HTTP/SOCKS 代理。

设计要点

  • 令牌必须持久化:.env 里要写死它,随机令牌每次重启都会 407。存在 <数据目录>/harness-token.txt(mode 0600)。
  • 端口必须固定:.env 指不到随机端口。被占用时自动退回随机端口并 warn —— 此时开放实际不生效,status 会报「与配置不一致」。
  • NO_PROXY 自动补上回环 + 国内 AI 平台域名:前者避免「代理指向自己」的循环,后者保证代理抖动时不打断模型长连接。
  • 不会有自指环:内核与订阅自己拉的流量走 httpFlow(Node http/https 模块),不受全局 undici dispatcher 影响 —— 插件里没有用 fetch() 的核心代码,这一点是刻意确认过的。
  • 内核从「单令牌」改为令牌集合(本进程令牌 + 持久化令牌),未知令牌照旧 407;SOCKS5 在门禁开启时照旧直接拒绝。

兼容

harnessProxy 默认关闭。不开时内核仍是随机端口 + 随机令牌,只服务本插件自己的下载,行为与 0.5.3 完全一致。

质量

node test/smoke.mjs → 406 passed, 0 failed(上一版 368)。新增:令牌持久化与只读模式、URL 入参校验、6 个 *_PROXY 键增删的幂等性、备份与还原、NO_PROXY 合并、不一致检测,以及内核同时接受两个令牌 / 未知令牌仍被拒的运行时断言。

安装

dsh plugin --profile desktop add @having5548/dsh-downloader

装完重启 DSH。

MIT。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.5.3 — 修一个会把 DSH 宿主崩掉的严重问题

Choose a tag to compare

@having5548 having5548 released this 11 Oct 03:53

dsh-downloader v0.5.3 — 修一个会把 DSH 宿主崩掉的严重问题

现象

宿主在 2026-10-11 连续两次异常退出(crash-2026-10-11T03-46-46 / 03-48-03),两份日志的栈指向同一处:

dsh: fatal uncaught exception: Error: write EPIPE
    at Writable.write (node:internal/streams/writable:508:10)
    at Object.write (.../@having5548/dsh-downloader/lib/core/transports.js:598:28)
    at Socket.<anonymous> (.../@having5548/dsh-downloader/lib/core/proxy-server.js:154:9)

根因

nativeConnect() 用 stdio: ["pipe","pipe","pipe"] 起 Go 连接器,但三根管道一个 error 监听都没挂。

连接器进程一死(连接失败 / 超时 / 被 kill),管道随即断裂,之后任何一次写入都会让 child.stdin 抛出 EPIPE —— 而在 Node 里没有监听者的 error 事件就是未捕获异常,直接把宿主带走。

原有的 ended 标志挡不住这个竞态:写入可能排在一个已经断掉的管道上,而 exit 事件还没派发。

修法(三层)

  1. 新增 stdioFace(child):给 stdin / stdout / stderr 与子进程本身都挂 error 监听,写入再包一层 try/catch(防同步抛出),并保留连接器最后的 stderr 供排查;
  2. ProxyServer.#relay() 里的 face.write(chunk) 包 try/catch,失败即关闭上游并销毁客户端连接;
  3. 新增独立子进程回归测试:让子进程关掉 stdin 但继续活着(于是 ended 不会置位、管道却已断),再灌 1.25 MB 数据。

这条测试是有牙齿的 —— 用修复前的写法跑同一个探针必定崩溃(实测 exit=1、code: 'EOF'),修复后存活。

质量

node test/smoke.mjs → 368 passed, 0 failed(上一版 366)。

安装

dsh plugin --profile desktop add @having5548/dsh-downloader

或从 Release 装。装完重启 DSH(这个修复必须重启才生效 —— 崩溃路径在已加载的旧模块里)。

MIT。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.5.2 — 让 dsh_run_proxied 对 git 真正可用

Choose a tag to compare

@having5548 having5548 released this 10 Oct 14:22

dsh-downloader v0.5.2 — 让 dsh_run_proxied 对 git 真正可用

两处都是把 dsh_run_proxied 真正用起来时才暴露的:git push 打插件内核必定失败。

修复

① 内核的 407 挑战写成了 Bearer,客户端无法应答

  • 现象:git push 报 CONNECT tunnel failed, response 407;GIT_CURL_VERBOSE=1 显示 git 发的 CONNECT 里根本没有 Proxy-Authorization,而应答是 Proxy-Authenticate: Bearer realm="dsh-downloader"
  • 根因:内核两种凭据都接受(Bearer <令牌> 与 Basic dsh:<令牌>),但挑战只宣告了 Bearer —— curl 拿到自己满足不了的方案会直接放弃重试;而 git 连预发 Proxy-Authorization 都不做(只有 curl 会预发),于是必然吃 407
  • 修法:挑战改成 Proxy-Authenticate: Basic realm="dsh-downloader"。两种凭据照旧都接受,只是宣告了客户端普遍能应答的那一种

② dsh_run_proxied 没有为 git 单独配好代理

  • 现象:407 解决后,仍要手写 git -c http.proxy=… -c http.proxyAuthMethod=basic 才通
  • 根因:git 不吃「URL 里带凭据的环境变量」这一套,不会把凭据预发给 libcurl
  • 修法:buildShellPrefix() 额外通过 GIT_CONFIG_COUNT / GIT_CONFIG_KEY_n / GIT_CONFIG_VALUE_n 注入 http.proxy(带凭据的同一个 URL)与 http.proxyAuthMethod=basic。对非 git 命令完全无副作用,所以不做命令嗅探;bash 与 pwsh 两种前缀都注入

⚠️ 顺带说明两件事

  • 这两条修好后「走代理」这条路才是通的,但节点本身会整段时间性失效(实测连 upload.wikimedia.org 都连不上)。那种情况下改用直连,或者换个订阅。
  • 本机 github.com 的解析里混过污染 IP(次级 DNS 8.8.8.8 的应答里带 205.251.x / 198.51.44.8 / 127.0.0.1)。直连可用时不要绕代理 —— 先比 DNS、再试直连,最后才上代理。

质量

node test/smoke.mjs → 366 passed, 0 failed(上一版 360)。新增「未带凭据的 CONNECT 收 407」「407 挑战宣告的是 Basic 而非 Bearer」以及 pwsh / bash 两种前缀里的 http.proxy、http.proxyAuthMethod、GIT_CONFIG_COUNT 断言。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.5.2.tgz

装完重启 DSH。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),连接器源码在 native/。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.5.1 — dsh_download 支持带鉴权的下载

Choose a tag to compare

@having5548 having5548 released this 10 Oct 08:48

dsh-downloader v0.5.1 — dsh_download 支持带鉴权的下载

新增

auth 参数(推荐) —— 只接受引用,token 由插件自己去取,不进会话记录:

取值 含义
gh 插件跑 gh auth token --hostname github.com,从 GitHub CLI 凭据库读
env:变量名 读环境变量,如 auth: "env:GH_TOKEN"
bearer:token 字面量(会进会话记录,返回值里带提醒)

headers 参数 —— 附加任意请求头。Host / Content-Length / Connection / Transfer-Encoding / Proxy-Authorization / Proxy-Connection / Upgrade 会被静默忽略,避免调用方把请求弄坏。

用法:

dsh_download({ url: "<archive_download_url>", save_path: "...", auth: "gh" })

这样 GitHub Actions 产物也能拿到 dsh_download 的全套能力:sha256、大小上限、多线程分片、停滞检测、失败回退。

变更

  • 跨主机重定向会剥掉 Authorization / Cookie。GitHub 的 archive_download_url 会 302 到 blob 域,把凭据带过去既没用、又等于把 token 交给第三方主机,某些情况下还会被对端 400 拒绝。同主机跳转保持原样。
  • 凭据贯穿每一条请求:初次探测、downloadSegmented 的每个 Range 分片、以及回退后的 attemptSingleStream 重试。

修复

  • 分片请求漏传 headers(写这一版时自己踩的):trySegmented() 组装 downloadSegmented() 参数时没带 headers,于是带鉴权的分片下载每一条 Range 都被服务端回 401 → 触发回退单连接(结果正确,但白白退化成单线程)。已补上,并加断言钉死。

质量

node test/smoke.mjs → 360 passed, 0 failed(上一版 338)。新增:sanitizeHeaders 取舍、敏感头识别与剥离、resolveAuth 四条成功路径与四条失败路径、端到端鉴权下载(带头 200 / 不带头 401)、鉴权 + 分片、以及同域保留 / 跨域剥离凭据的重定向断言。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.5.1.tgz

装完重启 DSH。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),连接器源码在 native/。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.5.0 — 多线程下载 + 侧边栏实时流量

Choose a tag to compare

@having5548 having5548 released this 05 Oct 06:35

dsh-downloader v0.5.0 — 多线程下载 + 侧边栏实时流量

⚡ 多线程(分片)下载

服务端支持 Range 时,大文件自动分成 threads(默认 4)个连接并发下载。

关键不是"切成 N 份",而是怎么切。 固定等分只在各家网速相当时才快 —— 一条慢连接会把整体拖到「最慢分片 × 份数 ÷ 并发」。所以这里照搬 gopeed(internal/protocol/http/fetcher.go)的做法:

  • 起始按 threads 均分;
  • 谁先下完,谁就去偷「剩余字节最多的分片」的前半段(下限 512 KiB,太碎不值得单开请求);
  • 慢分片被不断切走,快连接一直满载,直到全部抢完。
情况 行为
没有 accept-ranges: bytes 单连接
文件 < 1 MiB 单连接(协调开销大于收益)
threads = 1 单连接
服务端嘴上支持 Range 却返回 200 检测到后回退单连接重下,绝不拼出坏文件
Content-Range 起点不符 / 合计字节数不符 同上

分片用定位写落进同一个文件的对应偏移,完成后按顺序流式算 sha256(分片乱序落盘,边下边算会算错)。返回值里的 via_threads / segments 说明实际走了哪条路。

📊 侧边栏实时流量

侧边栏页脚(设置图标旁)新增流量徽标:

↑ 1.2 MB/s   340 KB
↓ 3.4 MB/s   1.8 GB
  • 只统计经过本插件内核的流量,不是全机网卡流量;
  • 侧边栏收起成 56px 窄条时自动切极简排版,只显示下行速率;
  • 后台标签页降到 5 秒一次;内核没跑时不占地方;
  • 内核侧加了 1.2 秒采样缓存 —— 否则面板(5 秒)与徽标(1.5 秒)会互相偷走采样窗口,读数乱跳。

修复

  • fs.promises.FileHandle.write 不接受回调。分片写入最初照 fs.write 的写法传了回调,该参数被静默忽略、Promise 永不 settle,整个下载挂死(实测:测试套件卡在分片那一步)。改为 await Promise 并校验 bytesWritten。
  • 流量采样缓存(见上)。

质量

node test/smoke.mjs → 338 passed, 0 failed(上一版 305)。新增:supportsRanges / parseContentRange / planChunks / stealSlice 的边界,以及端到端分片下载(3 MiB、sha256 逐字节比对、4 与 8 分片、threads=1 走单连接、假 Range 服务器必须回退且结果仍正确、经插件规则内核的分片下载)。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.5.0.tgz

装完重启 DSH。

MIT。多线程设计参考 gopeed(GPL-3.0)的实现思路,代码为独立编写;内核部分派生自 dsh-clash-proxy 0.2.0(MIT)。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.4.0 — 订阅真正能用:三种格式 + hysteria2 混淆

Choose a tag to compare

@having5548 having5548 released this 05 Oct 06:23

dsh-downloader v0.4.0 — 订阅真正能用:三种格式 + hysteria2 混淆

你报的「proxyUrl 填了才有用、subscriptionUrl 填了没用而且不能选节点」,查出来是三件事叠在一起。你的订阅是 base64 分享链接列表,9 个节点全是 hysteria2 + salamander 混淆 —— 三个坑全踩上了。

① proxyUrl 会把订阅整个挤掉

  • 现象:两者都填时状态显示 上游模式:explicit,节点列表只有一个 configured-proxy,订阅从未被读取
  • 根因:#rebuild() 的 explicit 分支先匹配并直接 return,subscription 分支根本走不到
  • 修法:订阅优先 —— 订阅(节点配置 → subscriptionUrl)→ proxyUrl → 环境代理 → none。订阅给的是一份可选节点列表,proxyUrl 只是一条固定上游;现在 proxyUrl 退居两用:抓订阅时当代理、订阅拿不到节点时兜底,并在原因里说明为何回退

② 订阅解析只认 YAML,不认分享链接列表

  • 现象:解析你的订阅直接抛「订阅内容既不是 YAML,也不是 base64 包裹的 YAML」
  • 根因:parseSubscription() 只试「原文 YAML」与「base64 → YAML」两条路
  • 修法:新增 lib/core/share-links.js,支持 hysteria2://(含 salamander 参数)、ss://(三种写法)、trojan://、vmess://、vless://(含 reality)、socks5://、http(s)://;明文与 base64 列表都能吃。认不出的行计数并报告,不再静默丢弃

③ 原生连接器没把混淆参数传下去

  • 现象:接上连接器后 hysteria2 依然连不通(5 秒超时后关闭),且看不出原因
  • 根因:nativeConnect() 漏传 obfs / obfs-password;服务端拿不到混淆参数会直接丢掉我们的 QUIC 包,表现为"拨号超时"而不是认证失败
  • 修法:补上字段;并给混淆包装补 SetReadBuffer / SetWriteBuffer 消除 quic-go 警告(否则回退小缓冲区,掉吞吐)

新增:自带 Go 原生连接器

lib/native/connector.exe 现在随包发布(原先未打包,这两类节点被静默跳过)。源码一并放在 native/,可复现编译:

  • native/salamander.go(新增):线格式 = 8 字节随机 salt ‖ (明文 ⊕ BLAKE2b-256(password‖salt) keystream),与 hysteria2 参考实现及 sagernet/sing-quic/hysteria2 一致
  • native/hysteria2.go:改用 quic.Transport{Conn: 包装后的 PacketConn} + Transport.Dial() —— quic.DialAddr() 会自建 socket,混淆没有插进去的位置

实测(你的真实订阅)

项目 结果
订阅抓取 200,2540 字节;subscription-userinfo 报告已用 ≈49.7 GB / 300 GB
解析 9 个节点全部拿到,名字 / sni / obfs 齐全
连通性(经插件 transport) 9 个里 8 个通(HTTP/1.1 204);唯一失败的「香港-移动网络专属」名字即说明限移动网络
对照:不带混淆 dial failed: hysteria2 quic dial: timeout —— 证明混淆是必需的
端到端 · 国内 直连,102356 字节,sha256 与历史一致
端到端 · 境外 route=proxy,经节点下载 registry.npmjs.org 成功 102356 字节

质量

node test/smoke.mjs → 305 passed, 0 failed(上一版 255)。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.4.0.tgz

装完重启 DSH。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),连接器源码在 native/ 并保留出处。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.3.1 — 实测发现的两处 bug

Choose a tag to compare

@having5548 having5548 released this 05 Oct 06:00

dsh-downloader v0.3.1 — 实测发现的两处 bug

两处都是装上真机实测才暴露的 —— 离线单测覆盖不到「与宿主其它工具/服务交互」的那一层。

修复

① dsh_run_proxied 委派给 shell 工具时漏传必填的 description

  • 现象:execute=true 必然失败,报 invalid arguments: missing required property "description"
  • 根因:dsh-tool-pwsh / dsh-tool-bash 的 description 是必填参数,而组装委派参数时只带了 command
  • 修法:抽出纯函数 buildShellToolArguments(),总是带 description(调用方优先,否则按命令自动生成);dsh_run_proxied 也新增了可选的 description 参数

② 挂载 / 重启后第一次调 dsh_run_proxied 报「代理内核还没就绪」

  • 现象:刚装完插件、或刚重启 DSH 时立刻调用必定失败;先跑一次 dsh_proxy_status 再调就好了
  • 根因:内核懒启动(第一次 resolve() 才监听端口、生成令牌),而工具直接读 shellProxyEnv()
  • 修法:工具先 await upstream.resolve();并在 apply() 末尾异步预热一次(不 await,慢订阅不拖挂载),让同步的工具守卫也能尽早生效

实测覆盖到的(都通过)

场景 结果
裸的境外 curl(无代理) 被守卫拦下,提示改用 dsh_run_proxied
裸的国内 curl(百度) 放行,http=200 / 0.117s
命令里已带 HTTP_PROXY 放行(不误拦修正后的重试)
dsh_run_proxied 的 execute=false 正确拼出 8 个代理变量 + 40 个 AI 域名的 NO_PROXY
国内目标经内核 规则引擎判定直连,http=200 / 0.106s
境外目标经内核 令牌鉴权通过(否则会是 407)、判定走代理、上游失败回 502
dsh_download 国内 直连成功,sha256 与历史一致
dsh_download 境外 走代理,错误信息带上游模式与建议

质量

node test/smoke.mjs → 255 passed, 0 failed(上一版 250)。新增 5 条钉住 buildShellToolArguments()。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.3.1.tgz

装完重启 DSH —— 实测确认:remove + install 只更新磁盘,运行中的宿主仍执行已加载的旧模块。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),各文件头部保留出处。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.3.0 — git / curl 下载境外内容也走这个代理

Choose a tag to compare

@having5548 having5548 released this 05 Oct 05:41

dsh-downloader v0.3.0 — git / curl 下载境外内容也走这个代理

先说清楚技术事实

git / curl 跑在 shell 子进程里,工具型插件无法在进程外截获它们。所以这件事只能做成两条腿,缺一条都不成立。

① 给它一条带上代理的命令:dsh_run_proxied

dsh_run_proxied({ command: "git clone https://github.com/x/y.git" })

它为这一次 shell 调用注入代理环境(HTTP_PROXY / HTTPS_PROXY / ALL_PROXY 及小写形式,指向本插件内核),然后委派给真正的 shell 工具执行 —— 沙箱与权限预设照常生效,不是绕过策略的后门。

  • 国内目标仍由内核判定直连;国内 AI 平台域名写进 NO_PROXY,连内核都不经过。
  • execute=false 时只返回拼好的命令,自己用 pwsh 跑也行。

② 检测到就拦:guardShellDownloads(默认开)

当 shell 命令看起来要从境外下载、又没带代理时,命令会被拦下来并提示改用 dsh_run_proxied(下文件则用 dsh_download)。

不会误拦的情况:命令里已带代理(HTTP_PROXY / curl -x / --proxy / git -c http.proxy);目标是回环或内网;SSH 远端(git@github.com:… 不走 HTTP,代理环境变量对它无效);内核没有可用出口(拦下来却没有替代方案比不拦更糟)。

嫌它碍事可以在设置里关掉。

判定是离线的

用插件内置的规则引擎判目标境内外,不发任何网络请求。所以它不会被网络状况影响,判定结果和 dsh_download 走的是同一套规则。

装完要重启

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.3.0.tgz

宿主对已加载模块有 ESM 缓存,remove+install 只更新磁盘。

质量

node test/smoke.mjs → 250 passed, 0 failed(上一版 225)。新增:URL 提取与去重、七类判定(境外 / 境内 / 回环 / 内网 / SSH / 已带代理 / REJECT)、pwsh 与 bash 前缀生成、代理 URL 组成、dsh_run_proxied 的两种渲染。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),各文件头部保留出处。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.2.1 — 配置列表与状态渲染的两处文案

Choose a tag to compare

@having5548 having5548 released this 05 Oct 05:37

dsh-downloader v0.2.1 — 两处渲染文案

位置 之前 现在
dsh_profile 列表 未选中的配置显示 ? 个节点 只在当前配置上显示节点数(其余本来就没解析过)
dsh_proxy_status 有配置时仍打印「订阅地址 | proxyUrl | 启动环境代理」 只留一行「旧版单条订阅地址:已填(当前不生效,被上面的配置覆盖)/ 未填」;没有配置时才输出完整三字段

原因:这两处文案是 0.1.x 单条 subscriptionUrl 模型下写的,0.2.0 引入多份配置后没跟着改。

本版仅修文案,无功能与行为改动。 测试 224 → 225 passed, 0 failed。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.2.1.tgz

装完重启 DSH(宿主对已加载模块有 ESM 缓存,remove+install 只更新磁盘)。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),各文件头部保留出处。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。

v0.2.0 — 节点导入对齐 FlClash + 代理只服务 DSH 自身下载

Choose a tag to compare

@having5548 having5548 released this 05 Oct 05:35

dsh-downloader v0.2.0 — 节点导入对齐 FlClash + 代理作用域收紧

一、节点导入对齐 FlClash

参考了 example/FlClash-main 的 Profile 模型与 ProfilesAction:FlClash 把配置存成一份列表,每份要么是订阅 URL、要么是导入的本地文件,各自带名字、上次更新时间与自动更新间隔。这一版按同一模型实现。

三种导入入口(面板「节点配置」卡片,或让模型调 dsh_profile):

入口 说明
从订阅地址 粘贴 https://…,或直接粘 FlClash / Clash Verge 那种 clash://install-config?url=… 深链(自动取出里面的地址)
从文件导入 选一个本地 .yaml / .yml 配置
交给模型 「把这个订阅导入:…」→ dsh_profile 工具

按 FlClash 的规则取名:先取响应头 Content-Disposition 里的文件名,再退回域名。流量与到期取自 subscription-userinfo(upload/download/total/expire),面板与工具返回里都会显示。

每份配置独立:自己的自动更新开关与间隔(默认 24 小时);本地文件配置永不自动更新(对应 FlClash 的 realAutoUpdate);到点判断用 lastUpdateDate + autoUpdateDuration,由 5 分钟一次的扫描驱动,而不是全局统一节拍。

支持 选择 / 更新 / 删除 / 改名 / 排序。

存放位置:$DSH_HOME/dsh-downloader/profiles.json + profiles/<id>.yaml(原子写入)。

旧的单条 subscriptionUrl 配置保留为回退:没有任何配置时才走它,既有安装不受影响。

二、代理作用域:只服务 DSH 自己发起的下载

这不是靠约定,而是三重硬约束:

约束 效果
只绑 127.0.0.1 局域网内其它机器根本连不上
每进程随机令牌(24 字节,只活在进程内,不落盘不写日志) 没有令牌的 HTTP 连接一律 407;SOCKS5 握手装不下令牌,直接拒绝
不碰系统代理、不写环境变量 浏览器、其它 App、DSH 里非本插件的出网请求全部保持原样

dsh_proxy_status 的 scope 字段如实报告这三条,面板「上游」卡片也写出来。就算端口被扫到也用不了;除 dsh_download 外的任何流量从未经过它。

安装

dsh plugin --profile desktop add https://github.com/having5548/dsh-downloader/releases/latest/download/having5548-dsh-downloader-0.2.0.tgz

装完重启 DSH(宿主对已加载模块有 ESM 缓存)。

质量

node test/smoke.mjs → 224 passed, 0 failed(上一版 171)。新增:配置存储往返、clash:// 深链与 subscription-userinfo 解析、文件名起名、自动更新到期判定,以及令牌鉴权的三条路径(带令牌成功 / 无令牌 407 / SOCKS5 被拒)。

MIT。内核部分派生自 dsh-clash-proxy 0.2.0(MIT),各文件头部保留出处。仅提供技术能力,请确保你的代理服务与使用方式符合当地法律法规。