cyberhunter775/dsh-plugin-file-download

资料足够(README 完整),目录页为 JS 渲染未取到正文,不影响成稿。星标、分类未标注则不写。

call write_result_md + write_meta_json
call write_result_mdmarkdown# dsh-plugin-file-download:给 DSH 对话里的文件加一个下载按钮

前言

在 DSH Web 对话里跑智能体时,模型经常要用 present 工具把生成的文件(报告、导出结果、脚本)呈现给用户。文件卡片会显示文件名和内容摘要,但用户想拿到文件本体时,还是要登录服务器、翻工作区目录、找那个带哈希的路径。这个问题在让非技术同事用对话时尤其明显:他们不会开终端,也没有路径概念。

下面介绍的插件 dsh-plugin-file-download(维护者 CyberHunter775)就是专解决这个问题的:在文件卡片上直接放一个「下载」按钮,浏览器点一下就能保存到本地。

这是什么

一句话定位:为 DSH Web 对话中呈现的文件添加下载按钮的插件,把「在服务器上找文件」变成「点一下存本地」。

  • 维护者:CyberHunter775
  • 版本:1.0.0(来自仓库 package.json)
  • 许可证:MIT
  • 实现:纯 JavaScript,零依赖,无需构建
  • 技术背景:DSH 的理念是「一切皆插件」。插件分为 host 侧(服务端路由)和 client 侧(Web 客户端 UI)两部分,本插件同时改了这两侧。

它解决的是交付环节的摩擦,不改变文件的产生方式,也不做文件管理,只做一件事:让对话里出现过的文件能被直接下载。

核心功能

按 README 列出的能力逐条说明,以下能力均已核实,没有加工。

1、下载按钮出现在对话的文件卡片上。覆盖两类卡片:deliverables 卡片、deliverables.review 文件卡片。按钮位置通过注入 deliverables.file.actions 和 deliverables.review.file.actions 两个插槽实现,不覆盖插件原有的卡片渲染。

2、流式传输,按 1 MiB 分块。这样单个大文件不会一次性读进内存,下载过程对服务端压力可控。

3、文件名完整保留,支持中文。通过 Content-Disposition 的 RFC 5987 编码实现,中文文件名、带空格和特殊字符的文件名都能正确落到本地。

4、样式跟随 DSH 原生主题。按钮是幽灵按钮(ghost button),自动适配亮色/暗色主题。

5、中英文界面。文案跟随 DSH 的语言设置切换,对应 下载 / Download 两种写法,词条放在 locale/zh.json 和 locale/en.json。

6、鉴权复用当前会话 Cookie。下载路由只允许访问本会话日志中出现过的文件,不能凭猜测的地址拖走工作区里的其他文件。这里的安全边界是「当前登录者 + 已呈现过的文件」。

安装与启用

README 给出的安装方式是写进 DSH profile 的依赖,支持本地路径或 GitHub 仓库:

# 从本地路径安装
dsh plugin install file:/path/to/dsh-plugin-file-download

仓库说明里还提到可以用 pnpm link 或 git 依赖的方式添加到 profile,没有给出统一的 dsh plugin add github:... 命令。如果走 GitHub 方式,按 profile 的包管理器配置走 git 依赖即可,不要把本地路径示例当成通用命令。

安装完成后,在对话里用 present 工具让模型呈现文件,文件卡片上就会出现下载按钮。插件在 package.json 里声明了 dsh.client.immediately: true 和注入目标 @deepseek-ai/dsh-client-ui-conversation,所以客户端 UI 会在加载时自动注入,不需要额外开关。

典型用法

README 没有给出端到端的示例脚本,下面是一个可复现的最小流程,命令均来自插件自身的标识符,没有额外拼接。

1、准备一个 DSH profile,把插件目录加入依赖:

dsh plugin install file:/path/to/dsh-plugin-file-download

这一步是把插件注册进当前 profile,host 侧会加载 index.js,client 侧会加载 client.js。

2、启动 DSH Web 服务(按你现有 profile 的启动方式即可,插件本身没有额外的启动参数)。

3、在 Web 对话里让模型生成并呈现文件,例如让它把某段分析结果写成文件并呈现。文件卡片渲染出来后,右侧会出现「下载」按钮。

4、点击按钮,浏览器按文件名保存文件。中文文件名、超过 1 MiB 的文件都按上面功能节的方式处理。

对应的文件结构也很简单,需要改动源码时按文件找即可:

index.js        # host 插件:服务端路由
client.js       # web 客户端插件:UI 按钮
cordis.patch.yml  # Cordis 插件注册
locale/zh.json, locale/en.json  # 界面文案

工作原理

分两侧理解会更清楚,README 也是按这个结构描述的。

Host 侧(index.js)注册 GET /plugins/file-download 路由。请求进来后,通过会话日志接口 sessionQuery.readEvent 把 sessionId + seq + index 解析成工作区里的文件路径。这里的 seq 和 index 指的是该文件在对话事件流中的出现位置,因此下载的文件不是「工作区里随便某个文件」,而是「这条消息里呈现的那个文件」。解析完成后先鉴权,再以附件形式、按 1 MiB 分块流式返回内容。

Client 侧(client.js)负责 UI。它向对话渲染层注入上面提到的两个插槽,文件卡片渲染时把下载按钮挂到插槽位置。按钮样式、文案、交互都在客户端完成,点击后向自己的 DSH 域名发起同源请求,天然带上会话 Cookie。

适用场景与注意

适合谁:

  • 用 DSH Web 对话做交付、要给非技术同事文件的用户。这是最有价值的场景,省掉「开终端找路径」这一步。
  • 在 Web 端批量产出报告、表格、脚本,希望每次生成完就能下载的人。
  • 想给 DSH 交付物卡片加自定义动作的插件开发者,可以参照它的插槽注入方式。

需要注意:

1、插件以当前 dsh 进程的权限运行。下载路由能够读取会话工作区里呈现过的文件,也意味着进程能访问的东西它都能访问。安装前请检查源码和许可证(MIT,允许在遵守声明的前提下复制和修改)。
2、安全边界依赖会话鉴权与 sessionId + seq + index 的路径解析,不要把它当成独立的文件保险箱;对外暴露 DSH 服务时,仍要保证部署层的访问控制到位。
3、功能覆盖范围限于「对话里呈现过的文件」。当前没有看到对工作区里未呈现文件提供列表或搜索的接口,如果需要列出全部产物,这个插件不解决。
4、插件版本为 1.0.0,README 里没有列出后续路线,升级前留意仓库提交。

结尾

这个插件的价值很集中:它不改氛围、不造概念,只是把 DSH 交付环节里最后一步人工操作(找路径、开终端、取文件)收进一次点击。对于已经在用 DSH Web 对话做交付的团队,它是个成本很低的补充;写插件的人也可以把它当作 host 路由 + client 插槽注入的一个紧凑范例。

  • 社区目录页:https://www.skillhub.cn/plugins/CyberHunter775/dsh-plugin-file-download
  • 代码仓库:https://github.com/CyberHunter775/dsh-plugin-file-download
羽毛球分组比赛记分
小程序二维码

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

小夜