dsh-file-attachment
拖拽/粘贴文件(支持多文件)进输入框:图片沿用原有草稿图片流程;文档读取内容存入项目根 .dsh-file-attachment 临时目录并插入 @绝对路径引用
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 28, 2026
- Updated
- Aug 28, 2026
Introduction
dsh-file-attachment
DeepSeek Harness (dsh) web GUI 插件:在会话输入框中拖入或 Ctrl+V 粘贴文档(支持多文件,一次可拖入/粘贴多个文件)时,浏览器端批量读取全文,保存到当前会话工作区根目录下的 .dsh-file-attachment/,并在输入框光标处插入 @<保存副本绝对路径> 引用。agent 读取 @路径 时直接读到落盘副本,不再依赖原始文件位置。
图片文件完全沿用 dsh web 既有草稿图片流程,不拦截、不改动。
行为一览
| 操作 | 行为 |
|---|---|
| 拖入/粘贴 文档(docx/xlsx/pdf/md/txt/rpk 等非图片文件) | 浏览器读全文 → base64 → 宿主落盘 <会话工作区>/.dsh-file-attachment/<时间戳>-<文件名> → 光标插入 @绝对路径,底部 toast 提示保存目录 |
| 一次拖入/粘贴 多个文件(支持多文件) | 批量处理:逐个读取全文 → base64 → 落盘 → 在光标处一次性插入全部 @ 引用,toast 汇总保存数量与目录 |
| 拖入 目录 | 拒绝,toast 提示「不支持拖入目录」,不插入引用 |
| 拖入/粘贴 图片 | 不拦截,原样走 dsh web 既有草稿图片流程(零变化) |
| 粘贴 纯文本(含从地址栏复制的目录路径字符串) | 不改写,完全原生行为 |
| 重复拖入同名文件(同一秒内) | 追加 -1、-2 序号,绝不覆盖 |
| 单文件 > 50MB | 跳过并提示 |
文件命名
<时间戳>-<清洗后文件名>,例如 2026-02-11T15-04-05-说明.docx;时间戳为 2026-02-11T15-04-05 格式(去掉冒号/毫秒/Z,各平台文件名安全且可排序)。同名冲突在扩展名前追加 -N。
项目根解析
保存目录的根 = 当前会话工作区(session.header.cwd),回退顺序:会话 cwd → sandboxPolicy.workspaceRoot → process.cwd()。每个项目(工作区)都各自维护自己的 .dsh-file-attachment/ 目录。
跨平台
- 保存路径用 Node
path.join(平台分隔符自适应) - 时间戳/文件名清洗同时对 Windows 与 POSIX 命名规则安全
- Host 半直接
node:fs/promises写字节,无 shell 依赖
安装(官方 dsh plugin 方式)
本仓库是一个标准 dsh 插件包(声明了 dsh.bundle.patch),并已发布为 npm 包 @wszhoho/dsh-file-attachment。用官方 CLI 安装,pnpm 会自动处理依赖、并把本包加入 profile 的 dsh.profile.bundles 层。dsh plugin add 的参数会转发给 profile 目录的 pnpm 执行,三种来源任选其一:
从 npm 安装(普通用户推荐)
dsh plugin --profile web add @wszhoho/dsh-file-attachment
# 重启 web GUI:dsh web(或从托盘重启)
本地源码安装(开发用,边改边验)
# 在仓库父目录执行(<parent> 换成放置仓库的实际目录,如 E:\wszhoho)
cd <parent>
dsh plugin --profile web add ./dsh-file-attachment
# ./dsh-file-attachment 相对路径等价 pnpm link;改完 lib/*.js 后重启 dsh web 即生效
从 GitHub 安装
dsh plugin --profile web add github:<你的账号>/dsh-file-attachment
- 无论哪种来源,安装后都自动进入
dsh.profile.bundles,无需手改 profilepackage.json; - 升级:
dsh plugin --profile web update dsh-file-attachment。
架构
packages/dsh-file-attachment/
├── package.json # dsh.client 声明 + bundle patch 指向
├── cordis.patch.yml # bundle 补丁:插入本插件行
└── lib/
├── index.js # Host 半:webServer 路由(POST /dsh-file-attachment/save),node:fs/promises 写盘
└── client.js # Client 半:document 级 capture drop/paste/dragenter 监听 + 槽组件
- Host 半:
webServer.register前缀路由/dsh-file-attachment,POST/dsh-file-attachment/save接收{name, data(base64), sessionId},base64 解码后用node:fs/promises写盘到会话工作区.dsh-file-attachment/,返回{ok, value:{path,dir,name,size}}。 - Client 半:
conversation.composer.dock(list 槽,同步桥状态,不渲染文本)+shell.overlay(list 槽,toast 通知展示),document 级 capture 监听先于应用 bubble 监听执行。 - 通知:
shell.overlayframe-wide 浮动 toast(不参与文档流,不破坏布局),4 秒自动消失。 - 浮层:拖入任何文件时 capture 阶段拦截应用自带「拖入图片…」DropOverlay(文案面向图片,对文档是误导)。
开发说明
lib/*.js使用 UTF-8 with BOM(项目约定);package.json、cordis.patch.yml使用 UTF-8 无 BOM。- Host 半用
webServer.register({ kind: 'prefix', path: '/dsh-file-attachment', handler })收文件(参考dsh-upload-file同架构的/dsh-upload路由),纯 ESM 无构建,不依赖装饰器/远程反射。 - Client 半保存用
fetch('/dsh-file-attachment/save', { method: 'POST', body: JSON.stringify({ name, data, sessionId }) }),信封为{ ok, value | error }。 - 项目根 = 会话工作区(
session.header.cwd→sandboxPolicy.workspaceRoot→process.cwd()兜底)。 - 50MB 上限两侧一致(client 跳过 + host 校验)。
License
MIT