sharkymew
dsh-utility-tools
DSH(DeepSeek Harness)对话工具插件:拖拽任意文件进入对话 + 选中文本引用。
- Stars
- 2
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-utility-tools
DSH(DeepSeek Harness)对话工具插件:拖拽任意文件进入对话 + 选中文本引用。
支持两种加载方式:静态挂载(dsh plugin add 一行命令安装,推荐)与动态加载(cordis_define,零依赖)。
功能
1. 拖拽文件附件(不限于图片)
- 拖入任意文件到对话区域即进入附件列表:输入框上方显示「📎 附件(N)」胶囊;
- 点击胶囊展开管理菜单:查看文件名/大小、逐个移除;
- 发送(Enter 或发送按钮)时随消息一并提交:
- Electron 环境:直接引用
File.path原路径,不复制; - 普通浏览器:字节无损暂存到系统临时目录(macOS/Linux 用
/tmp,Windows 用每用户%TEMP%)下的dsh-drops/<会话id>/,消息引用绝对路径,模型可直接读取;
- Electron 环境:直接引用
- 文件内容不会出现在输入框或消息气泡里(只引用路径与大小),不污染工作目录;
- 图片拖拽仍走系统原生流程;图片与文件混拖时图片自动转交原输入栏。
2. 选中文本引用
- 在对话中鼠标选中文字 → 选区上方弹出「引用」菜单;
- 点击后选中文字显示在输入框上方(引用条),带「✕」取消;
- 发送时引用以
> 引用:块置于消息开头一并提交。
目录结构
dsh-utility-tools/
├── README.md # 本文件(含导入指南)
├── package.json # 包声明:dsh.bundle(自动插入 entry)+ dsh.client(静态 Client 半区)
├── cordis.patch.yml # ★ bundle patch:`- insert:` 插入插件 entry(一行命令生效的关键)
├── LICENSE # MIT
├── .gitignore
├── lib/ # ★ 静态版(一行命令安装用)
│ ├── index.js # Host 入口(ESM,export default 插件,零依赖)
│ └── client.js # Client 半区(__ModuleLoader__.load 格式)
└── dynamic/ # 动态版(cordis_define 粘贴用,已验证)
├── host.body.js # code.host 函数体
└── client.body.js # code.client 函数体
如何导入(给使用者)
方式 B:静态挂载(一行命令,推荐 ✅)
# 安装到 web profile(包自带 dsh.bundle,entry 自动插入插件树)
dsh plugin --profile web add dsh-utility-tools
# 重启 dsh web 生效
- 包通过
dsh.bundle声明(cordis.patch.yml内的- insert:行)自动向插件树插入 entry,无需手动编辑任何 patch 文件; - 安装命令等价于在 profile 目录执行
pnpm add; - 行内
name是模块说明符:loader 经import(name)加载包入口main(Host ESM),client 模块表扫描 entry 的dsh.client声明并 serve./client导出(__ModuleLoader__格式); - Host 半区注册 HTTP 路由
/dsh-utility-tools/save,Client 半区经同源fetch上传附件。
方式 A:动态加载(零依赖,已验证)
git clone <本仓库地址>
把 dynamic/host.body.js 与 dynamic/client.body.js 的全文交给你的 AI:
用 cordis_define 创建插件:
code.host=dynamic/host.body.js全文,code.client=dynamic/client.body.js全文,然后 cordis_run 运行。
在 UI 中批准运行请求即可。动态插件是 DSH 一等公民特性,进程内加载、卸载即清理。
架构说明
| 层面 | 位置 | 职责 |
|---|---|---|
| Client UI | conversation.input.dock(id file-drop-quote) | 引用条、附件胶囊/管理菜单;document 捕获阶段监听 drop/keydown/click/mouseup 等 |
| Client UI | conversation.input.overlay(id ref-menu) | 选区上方悬浮「引用」菜单 |
| Client→Host 通道 | 动态:host.call('save-dropped-file');静态:fetch('/dsh-utility-tools/save') | 发送时上传附件字节(base64) |
| Host | 动态:harness.handle;静态:webServer.register 路由 | 纯 JS base64 解码 → ctx.fs 写入系统临时目录 dsh-drops/<会话id>/(平台自适应 + 重名自动加序号)→ 返回绝对路径 |
关键技术点
- 字节无损:沙箱内置
atob是 UTF-8 感知的(Buffer.from(b64).toString('utf-8')),会损坏非 ASCII 字节;Host 端使用纯 JS 手写 base64 解码器,逐字节精确还原; - 沙箱兼容:
/tmp位于 workspace-write 策略的可写白名单(writableRoots= 工作区根 +/tmp+os.tmpdir()),模型侧工具可直接读写暂存文件; - 写盘兜底:
ctx.fs.writeText传入Uint8Array时底层会在原子写盘后抛错(normalizeLineEndings仅支持字符串),以fs.stat校验大小确认落盘成功; - 发送拦截:document 捕获阶段拦截 Enter(textarea 焦点、非 IME 组合、非 Shift)与发送按钮点击(aria-label 匹配「发送消息 / Send message」),先合并引用/附件摘要到草稿再调用
inputActions.submit(),同时跳过/命令流与机器忙态; - 深色模式:按钮使用
--dsw-alias-button-info-fill(明/暗均为蓝色底白字),避免--dsw-alias-brand-primary在深色下反转成近白色导致白字不可见。
已知限制
- 普通浏览器无法获取拖入文件的真实磁盘路径(平台安全设计),只能按名称引用或暂存到系统临时目录;Electron 环境(
File.path)可直引原路径; - 单文件上限 8MB(传输载荷约束),超出仅提示不暂存;
- 暂存目录平台自适应:macOS/Linux 用
/tmp,Windows 优先每用户%TEMP%(C:/Users/<用户>/AppData/Local/Temp),回退C:/Windows/Temp等;系统重启后临时目录可能被清空; - 纯文本引用上限 2000 字符;
- 附件在发送时统一暂存,移除附件不会产生任何磁盘残留;
- 静态版 HTTP 路由仅监听回环地址且只写系统临时目录(受沙箱策略约束),已做最小暴露。
License
MIT