Bundle
dsh-file-fix
DeepSeek Harness upload UX plugin: unified file import — drag & drop / paste / picker for ANY file type into the session workspace, with a Hermes-style attachment rail
- Source
- re-ITRT
- stars
- 2 stars
- License
- MIT
- Updated
- Updated 11 days ago
Readme
# dsh-file-fix
DeepSeek Harness(DSH)上传体验优化插件:**统一文件导入体系**——任何后缀的文件都能
拖入 / 粘贴 / 点击选择,**字节上传到附件库**(不依赖工作区路径,服务器部署可用),
文件清单随消息注入模型上下文,历史消息下方显示文件气泡(可下载),agent 可读取内容
或导出到工作区。完全不使用 DSH 官方图片导入链路。
## 背景(DSH Web 原生痛点)
| 痛点 | 现状 |
| --- | --- |
| 非图片文件无法上传 | DSH Web 只收图片;非图片弹「仅支持 PNG、JPG、WebP、GIF」提示 |
| 拖入非图片后 overlay 卡住 | drop 后「拖入图片」界面不消失 |
| 无法点击选择文件 | 没有 file input |
| 粘贴只支持文本 | Ctrl+V 文件无反应 |
| 上传文件 agent 不可见 | 无文件清单注入,agent 只能猜路径(曾猜 75 步) |
## 方案
- 任何文件 drop / 粘贴 / 📎 选择 → 字节上传(`filefix/persistFile`)→ host 存入**内容寻址附件库**
`~/.dsh/attachments/filefix/`(sha256 去重,manifest.jsonl 索引)——与工作区完全解耦
- 发送时(`agent/pre-step`)注入文件清单消息(role=user + plugin 来源 + notice 表单:
UI 只显示「📎 附件 N 个文件」一行摘要,模型读到完整清单与 attachment_id)
- **模型侧工具**:
- `read_attachment`:按 attachment_id 读取内容;支持分段(offset/limit/more,默认段
48 KB 避开 dsh spill-policy 的 50 KB 内联阈值);大文件自动镜像完整副本到工作区
`.dsh-uploadux/reads/`(官方 `read` 工具在 spill-policy 中豁免,可读全量)
- `place_attachment`:把附件字节导出到会话工作区任意路径(边界校验,防 `../` 逃逸)
- 历史消息:`filefix/files` 会话事件(ignorable)记录「消息 ↔ 文件」关联,客户端
注册官方 Conversation Node(`conversationEvents` Definition + keyed renderer),
在文字气泡下方渲染文件列表气泡(文件名+大小+下载链接,
下载走 `/plugins/dsh-file-fix/download/<attachmentId>`)
- 交互照 Hermes:统一 rail 混排(缩略图降采样队列)、chip 三态(上传中/完成/失败点击重试)、
删除 chip 连带删附件、Esc 取消拖拽、深度计数防闪烁、drop 后焦点回输入框
- 限制(插件 config 可覆盖):单文件 50 MB、每批 20 个、批量总量 200 MB;超限整批拒绝 + 提示
## 两层横向拖放 UI + 视觉上下文
- **两层横向列表**(与 DSH 同一审美):
- **图像层**:图片拖到这里 → 走官方图片注入链路(直接进模型上下文)
- **文件层**:任意文件拖到这里 → 走插件文件链路(字节入附件库 + 文本/文件注入)
- 空白区 drop 按文件类型自动分流(图片 → 图像层,其他 → 文件层)
- **视觉上下文标记**:session 含任何直接图片注入(draft image 提交、read_image /
add_image_to_context 的调用结果)即标记为「需要视觉」;visual_assist(返回文本)不标记。
- **模型切换限制**:当 session 需要视觉时,模型选择器中**不支持图片输入的模型置灰不可选**;
从不需要视觉的 session 切换则无限制。
## 结构
- `src/` host 侧:`filefix` Typert Remote 服务(persistFile / limits / removeFile /
markPending / unmarkPending / listFiles / checkAvailable + 清理/视觉配置 RPC)+
附件库(内容寻址)+ 桥(session 事件监听 → 关联表 + pre-step 注入)+
`read_attachment` / `place_attachment` 工具 + 下载路由
- `client/` 浏览器侧:document 级 drop/paste 拦截(捕获阶段)+ rail + 📎 选择按钮 +
文件气泡(官方 Conversation Node:`filefix-files` Definition + keyed renderer,
挂在 `conversation.chat.node` slot 下)+ 设置页(视觉辅助 / 附件清理)
- `scripts/build-client.mjs` client bundle 构建(esbuild CJS + `__ModuleLoader__` 外壳,zod 内联)
- `scripts/repair-sessions.mjs` 会话日志修复工具(帧级 zstd 解压 → 清洗 → 重压;曾用于清除
早期版本误存进日志的 system 角色消息)
## 已知平台限制(win32)
- dsh spill-policy 阈值 50 KB:纯文本工具结果超过即替换为「头尾预览 + spill 路径」,
且 spill 定位是 Windows 路径(agent 的 bash 为 Linux 语义读不了)——插件已通过
48 KB 默认段 + 工作区镜像规避
- 工具集无 shell 执行能力时 agent 无法解压/运行文件(环境问题,非插件)
## 安装(推荐:npm 官方渠道)
```bash
dsh plugin --profile web add dsh-file-fix
```
装完重启 `dsh web` 即生效(输入框出现「上传文件」按钮)。
**已实测验证的完整生命周期**(干净 profile 实测):
| 操作 | 命令 | 结果 |
| --- | --- | --- |
| 全新安装 | `dsh plugin --profile web add dsh-file-fix` | 依赖 + 自动登记 bundles(插件树加载)✅ |
| 卸载 | `dsh plugin --profile web remove dsh-file-fix` | 依赖 + bundles 登记自动移除 ✅ |
| 重装 | `dsh plugin --profile web add dsh-file-fix` | 全部恢复 ✅ |
> **注意**:pnpm 10+ 首次 add 可能报 `[ERR_PNPM_IGNORED_BUILDS]`(dsh 官方依赖的原生模块构建被拦截,任何插件都如此)——此时**再跑一次 add** 即可(allowBuilds 已登记后 pnpm 干净退出,dsh 完成登记)。
## 从源码构建安装
```bash
# 1. 构建(需要 deepseek-harness 源码仓库的 node_modules 提供 tsc/esbuild)
npm install # 或 pnpm install(package-lock 已提交)
npm run build # host tsc 编译到 lib/ + esbuild 打包 dist/client.js
# 2. 挂载到 dsh profile(以 web profile 为例)
node scripts/mk-junction.cjs node_modules "<你的 dsh profile>/node_modules"
node scripts/mk-junction.cjs "<你的 dsh profile>/web/node_modules/dsh-file-fix" "$PWD"
# 3. 在 profile 的 cordis.patch.yml 里加载本插件(参照 cordis.dev.yml)
```
## 开发环境(官方教程路径:源码 checkout + 干净 profile)
一次性准备:
```bash
# 1. 源码 checkout(master),pnpm install + build
# 2. profile 保持干净(不 npm install 任何 @deepseek-ai 包):
# ~/.dsh/profiles/web/ 里只有 package.json(bundles 声明)+ cordis.patch.yml
# —— 运行时会由 dsh 自动 heal 出 ~/.dsh/profiles/node_modules 源码链接
# 3. 本项目依赖解析指向 heal 产物(Linux/macOS 用 ln -s):
ln -s "$DSH_HOME/profiles/node_modules" node_modules # 或 npm install + 手动链接 @deepseek-ai 包
# 4. 让 profile 能以包名解析本项目(client 插件发现机制需要):
ln -s "$PWD" "$DSH_HOME/profiles/<profile>/node_modules/dsh-file-fix"
```
开发循环(在 deepseek-harness 目录跑):
```bash
pnpm dsh web --patch ../dsh-file-fix/cordis.dev.yml --port 3081
# host 改动:npm run build 后重启 dsh(lib/ 是包入口)
# client 改动:npm run build(重建 dist/client.js)+ 刷新页面
```
```bash
npm run typecheck # host + client 类型检查(用仓库的 tsc:
# node <repo>/node_modules/typescript/bin/tsc -p tsconfig.json --noEmit)
npm run build # tsc 编译宿主侧到 lib/ + esbuild 打包 client bundle
```
## 日志约定
`[dsh-file-fix]` 前缀,全链路可还原:`intake(入口/分流统计) → persistFile(校验/拒绝 code/写入路径/耗时)
→ ref injected(引用注入) → chip removed(删除)`,失败带 code(TOO_LARGE / EMPTY / SESSION_NOT_FOUND /
NO_WORKSPACE / WRITE_FAILED / INVALID_PATH / REMOVE_FAILED)。
## 设计稿
见 `docs/design.md`(v0.2:完全不保留官方链路 + 照 Hermes 交互)。Install
dsh plugin --profile web add github:re-ITRT/dsh-file-fix#64ab40fe6a2bc91ea50ca57d67137ec6a7410d00
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-file-fix from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.