DeepSeek Harness 插件

Zenjibad/dsh-drop-any-file

分类 UI 增强 收录于 2026-08-25

让 DSH 网页聊天支持拖拽任意类型文件:非图片文件保存到当前会话工作区,供 Agent 读取使用。

安装

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:Zenjibad/dsh-drop-any-file

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWEDERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yamlallowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

扩展 DeepSeek Harness (DSH) Web UI 聊天的拖拽能力,让任意文件类型都能被接受——内置输入框只允许图片(PNG/JPG/WebP/GIF)内联附加,其余一律拒绝。拖入非图片文件将保存到当前会话工作区,供 Agent 读取使用。

English: README.md · LLM 索引: llms.txt · Agent 指南: AGENTS.md

关键词 / Keywords: dsh-plugin · deepseek-harness-plugin · drag-drop · file-upload · workspace · attachment · any-file · 拖拽 · 任意文件 · 工作区

仓库 / Repo

GitHub: Zenjibad/dsh-drop-any-file · 需要 DSH ≥ 0.1 及 web profile。


📑 目录


✨ 特性

特性 说明
📥 任意文件类型 拖入 .pdf.zip.txt.csv.bin…… 不再出现「仅支持 PNG/JPG」的拒绝提示
💾 保存到工作区 每个拖入的非图片文件都会写入当前会话的工作区目录,Agent 可用常规文件工具读取/搜索/使用
🖼️ 图片行为不变 只包含图片的拖拽仍走内置内联附加流程——已有的好用功能不受影响
🚫 绕过输入框拒绝 在 window 上以捕获阶段监听拖拽事件,先于输入框的仅图片处理器执行,拒绝 toast 不会触发
🫳 拖拽遮罩 + 提示 拖拽时显示全窗口主题遮罩(「松手保存到工作区」);保存后用 toast 提示结果(成功或失败原因)
🛡️ 文件名清洗 + 大小上限 文件名取 basename 并剔除非法路径字符;请求体 80 MB / 文件 60 MB 上限
🌗 主题自适应 遮罩与 toast 全部使用 --dsw-alias-* 设计令牌,亮/暗色自动跟随
♨️ 重启常驻 真实 profile 打包插件:dsh plugin add 安装一次,每次 DSH 启动自动加载 —— 无需 cordis_define、无需每次重装

🏗️ 工作原理

用户把非图片文件拖入聊天窗口
                                  │
Client 半区(浏览器)              ▼
  └─ window 上捕获阶段监听:dragenter / dragover / dragleave / drop
       (从 conversation.input.dock 席位注册,该席位携带 sessionId)
  └─ drop 含非图片文件?→ preventDefault + stopPropagation
       (输入框的仅图片处理器根本收不到该事件)
  └─ FileReader → base64 → fetch POST /drop-any-file/api
       body: { sessionId, fileName, base64 }
                                  │
Host 半区(DSH 进程内)            ▼
  └─ webServer 路由 POST /drop-any-file/api
  └─ 解析目标目录:sessions.get(sessionId).header.cwd(即工作区)
  └─ 清洗 basename → 解码 base64 → node:fs writeFile
  └─ 返回 { ok, saved: { name, path, bytes } }
                                  │
Client 半区(浏览器)              ▼
  └─ toast:「已保存到工作区:<文件名>」(或错误信息)
  • 浏览器主动拉取/提交:无推送、无事件;只有用户真正放下文件时才 POST,Host 一律返回 JSON(失败时 {ok:false,error},绝无非 JSON 500)。
  • 纯图片拖拽放行:处理器提前 return、不 stopPropagation,内置的内联附加流程照常运行。
  • 持久化:随包声明 dsh.bundlecordis.patch.yml)+ dsh.clientexports["./client"] 打包产物),作为真实 profile 插件安装,DSH client-modules 每次启动都会扫描加载。

🚀 快速开始

标准安装:dsh plugin add(重启常驻)

从本仓库安装:

# 本地目录(在本仓库父目录执行):
dsh plugin --profile web add ./dsh-drop-any-file

# 或直接从 GitHub(任意 DSH 机器):
dsh plugin --profile web add github:Zenjibad/dsh-drop-any-file
# 或:
dsh plugin --profile web add git+https://github.com/Zenjibad/dsh-drop-any-file.git

dsh plugin add = 向 profile 做 pnpm add + dsh.profile.bundles 协调:识别到本包的 dsh.bundle 声明后,把 dsh-drop-any-file 追加进 bundle 栈。重启 DSH,然后硬刷新浏览器标签页Ctrl+F5)。启动时 client-modules 扫描器解析 exports["./client"],拖拽处理器随即生效。无需 cordis_define,重启后依旧。

⚠️ 注意:安装(或更新)客户端插件后必须硬刷新页面Ctrl+F5)——DSH 客户端 HMR 只会热替换已加载的 bundle,不会把新增的 bundle 注入已打开的标签页。

手动挂载(备选)

  1. git clone https://github.com/Zenjibad/dsh-drop-any-file.git(任意位置)。
  2. ~/.dsh/profiles/web/package.jsondependencies"dsh-drop-any-file": "link:<仓库路径>",然后在 profile 目录 pnpm install
  3. 重启 DSH。

使用前提

  • 任意处于活动状态的 DSH web 会话(保存目标是当前会话的 cwd)。
  • 没有打开会话 → 没有可保存的位置 → 插件不生效(输入框保持默认行为)。

⚙️ 配置

无配置文件、无持久化设置。行为由源码中的常量固定:

可调项 位置 默认值
POST 请求体上限(base64) src/index.ts 中的 MAX_BODY_BYTES 80 MB(约 60 MB 文件)
单个文件上限 src/index.ts 中的 MAX_FILE_BYTES 60 MB
保存目标 每次 drop 时解析 sessions.get(sessionId).header.cwd(会话工作区)
拖拽拦截 src/client/index.tsx window 上捕获阶段监听 dragenter/dragover/dragleave/drop
Toast 时长 src/client/index.tsx 6 秒

❓ 常见问题

Q: 我拖了文件还是提示「仅支持 PNG、JPG、WebP、GIF 格式的图片」? A: 插件没有加载到当前页面。先重启 DSH(如果 Host 半区尚未挂载),再硬刷新浏览器标签页Ctrl+F5)。新增的客户端 bundle 只有整页刷新才会加载——HMR 不会把新 bundle 加进已打开的标签页。

Q: 我拖了文件但没反应 / 没有 toast? A: 拖拽必须发生在聊天窗口上方、且已打开会话,并且 drop 中至少要有一个非图片文件。如果全是图片,则走内置附加流程(设计如此)。可查看浏览器控制台的 drop-any-file save error 日志。

Q: 文件保存到哪里? A: 当前会话的工作区目录sessions.get(sessionId).header.cwd)——也就是 Agent 的工作目录,Agent 可以用 read/glob/grep 等工具直接读取。

Q: 支持一次拖多个文件吗? A: 支持——drop 中每个非图片文件都会按顺序保存,toast 会逐一列出保存的文件名并统计失败数。

Q: 能拖超过上限的大文件吗? A: 不能——Host 对超过 60 MB 的文件返回 413,客户端显示失败 toast。如需更大文件,改 src/index.ts 里的 MAX_FILE_BYTES(以及 MAX_BODY_BYTES)后重新构建。

Q: 装了本插件后旧的动态插件还在? A: 本插件是打包式而非动态——没有动态孪生。若你之前安装过动态拖拽处理器,请用 cordis_stop / cordis_undefine 停掉,避免双重拦截。

Q: 如何彻底移除? A: dsh plugin --profile web rm dsh-drop-any-file(或删除 profile 依赖与 bundle 条目)后重启 DSH。

⚠️ 安全须知

  • 防路径穿越:文件名先取 path.basename(),再剔除所有非法路径字符(<>:"/\\|?* 与控制字符),恶意文件名也无法逃出工作区。
  • 写入范围受限:文件只写入解析出的会话 cwd,绝不写入任意路径。
  • 大小上限:请求体上限 80 MB、解码后文件上限 60 MB,Host 不会缓冲无界载荷。
  • 无额外 I/O:插件不发起网络请求、不执行 shell、不扫描磁盘——只做请求的那一次文件写入。
  • 同源 base64 传输:文件字节以 base64 JSON 提交到同源路由(DSH webServer),不经过任何第三方端点。

📦 项目结构

dsh-drop-any-file/
├── src/
│   ├── index.ts            # host 半区:body 读取、会话 cwd 解析、文件名清洗、writeFile、/drop-any-file/api 路由
│   └── client/index.tsx    # client 包:捕获阶段拖拽监听、遮罩、toast、POST 到 host 路由
├── cordis.patch.yml        # dsh.bundle patch(启动时插入插件行)
├── tsdown.config.ts        # 打包 host(node ESM)+ client(CJS ModuleLoader)
├── package.json            # name、exports["./client"]、dsh.client + dsh.bundle
├── lib/                    # 构建产物(index.js、client.js)
├── AGENTS.md               # AI agent 仓库指南
├── llms.txt / llms-full.txt
├── README.md / README.zh.md
└── LICENSE

🙏 致谢

  • DeepSeek Harness — DSH 插件/动态运行时、Slots、主题、webServer、client-modules。
  • headroom-stats-plugin — 打包式 client 插件构建模式参考(tsdown host/client 拆分、cordis.patch.ymldsh.client)。
  • DSH-better-sidebar — 打包式 client 插件构建模式的另一参考。

📄 License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。