dsh-file-download:用 /api/dl 按真实文件名交付下载

前言

在 DSH(DeepSeek Harness)里用内置的 GET /api/file?path=<绝对路径> 取本地文件很方便:预览、图片渲染、GUI 的 present 卡片都走这条路。但这条响应不带 Content-Disposition,浏览器只能把 URL 最后一段当成文件名,于是下载往往统统变成 file.pdf、file.md、file.zip,除非在页面里手动「另存为」,真实文件名(含中文、空格)就丢了。

社区插件 dsh-file-download(维护者 liruizhe,分类:客户端)在同一条已鉴权的共享 API 通道上另注册 exact 路由 GET/HEAD /api/dl,补齐 attachment; filename*=UTF-8''<真实名>,专门用于「交付下载」,而不去覆盖内置 /api/file(DSH 的 registerFetchRoute 对同一 exact 路径重复注册会直接抛错,核心也没有开关能为 /api/file 加上 disposition)。

下面介绍插件定位、接口、安装启用与可选配置;事实以 GitHub 仓库 README 为准。

这是什么

dsh-file-download 是 DSH Host 插件:零运行时依赖、无需构建,装完即用。它在 /api/ 下增加下载路由,按真实文件名交付文件,与内置 /api/file 共享鉴权、trustedHost、CSRF 策略,不额外扩大暴露面。

/api/file(内置) /api/dl(本插件)
用途 内联预览、图片渲染、GUI 的 present 卡片 交付下载
Content-Disposition 无 → 存成 file.xxx attachment; filename*=UTF-8''<真实名>
鉴权 共享 API 通道 同一条通道

当前版本 0.1.0,许可证 MIT。包不发 npm,GitHub 为唯一分发渠道。

核心功能与接口

路由形式如下:

GET/HEAD  https://<你的 DSH 域名>/api/dl?path=<URL 编码的绝对路径>

查询参数:

参数 作用
path 必填,绝对路径(相对路径或含 NUL → 400)
name 覆盖下载名;服务端收敛成单段安全名
inline=1 内联预览,但仍给出真实文件名
download=1 强制下载(配置 mode: inline 时用得上)

常见 HTTP 状态:不存在 404、目录 403、超过 maxBytes 413、未登录 401。响应头含 Cache-Control: private, no-store、X-Content-Type-Options: nosniff,以及与内置 /api/file 一致的 Content-Security-Policy: sandbox。

示例 URL:

https://example.org/api/dl?path=%2Fhome%2Fuser%2Fdeliverables%2Freport.pdf
https://example.org/api/dl?path=%2Ftmp%2Fa.md&name=%E7%BB%BC%E8%BF%B0-2026-11-10.md

安全方面:只读,无写删列目录接口;可读范围由宿主 fs 服务与 /api/file 同权;必须登录,非 owner principal 为 403;文件名经收敛,Content-Disposition 不作为注入点。

安装与启用

前提:DSH ≥ 0.2.0-rc.1(connection.fetch 与 fs 服务);Node ≥ 20;从 git 安装需本机有 git 且能访问 github.com。本包无构建脚本,从 git 安装不会被 pnpm 的 allowBuilds 拦住。

1、从 GitHub 安装

dsh plugin --profile web add github:liruizhe/dsh-file-download#v0.1.0   # 或 #main 跟随主干

私有仓库可用完整 git URL:

dsh plugin --profile web add "git+ssh://git@your.git.host:<owner>/dsh-file-download.git"

无法访问 github.com 时,可在有网络的机器上 pnpm pack,将 tgz 拷到目标机后:

dsh plugin --profile web add /path/to/dsh-file-download-0.1.0.tgz

2、本地目录(开发 / 自用)

dsh plugin --profile web add file:/path/to/dsh-file-download          # 或 link: 保持就地编辑

3、激活 bundle 并重启

dsh plugin … 只转发给 pnpm,不会自动写入 GUI 插件管理器里的 dsh.profile.bundles。需手动把 bundle 名加入 profile 的 package.json 后重启 DSH:

cd ~/.dsh/profiles/web
node -e 'const fs=require("fs"),f="package.json",m=JSON.parse(fs.readFileSync(f));const p=(m.dsh??={}).profile??={};p.bundles=[...new Set([...(p.bundles??[]),"dsh-file-download"])];fs.writeFileSync(f,JSON.stringify(m,null,2)+"\n")'
systemctl restart dsh-web        # 或你启动 dsh 的方式

卸载:dsh plugin --profile web remove dsh-file-download,并从 dsh.profile.bundles 删除对应项后重启。

可选配置

默认即 /api/dl、单文件上限 20 MiB、attachment 模式,可不配。若要调整,在 profile 的 cordis.patch.yml 里按 id 写裸 row(勿再写 insert:,避免挂载两次):

- id: dsh-file-download
  config:
    route: /api/dl        # 必须在 /api/ 下
    maxBytes: 52428800    # 50 MiB
    mode: inline          # 默认内联预览,?download=1 才强制下载

改完重启生效。GUI 无配置表单(避免依赖 @deepseek-ai/schemastery,保持零依赖)。

典型用法与注意

  1. 智能体或脚本生成报告、交付物:在已登录环境下构造 /api/dl?path=… 链接,用户点击即可用正确文件名保存。
  2. 与 /api/file 分工:预览、内嵌展示继续用 /api/file;需要浏览器「下载」语义时用 /api/dl。
  3. 升级核心后 404:核心升级可能重写 profile 依赖树,需重跑安装命令并确认 dsh.profile.bundles 含 dsh-file-download 后重启。
  4. 日志 skipping bundle:查 journalctl 或控制台,常见原因为 peerDependencies 与核心版本不匹配,或 bundle 名拼写错误。

插件以当前 dsh 进程权限读文件,安装前建议浏览源码与 MIT 许可证;DSH 生态强调「一切皆插件」,SkillHub 目录页 为社区索引,与 DeepSeek / 幻方无官方从属关系。

结尾

dsh-file-download 用一条不碰核心、不绕鉴权的新路由,补上内置 /api/file 下载文件名丢失的问题。需要按真实名交付文件时,安装、写入 bundles、重启后即可使用 GET/HEAD /api/dl。

  • 目录页:https://www.skillhub.cn/plugins/liruizhe/dsh-file-download
  • 源码与文档:https://github.com/liruizhe/dsh-file-download
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜