dsh-copy-file
Identified★ 1Copy real files from DSH delivery cards to the system clipboard (CF_HDROP), with Windows-like card interaction. Pinned to DSH 0.2.0-rc.2.
dsh-copy-file
把 DSH 文件卡片里的"文件本体"放进系统剪贴板 —— 不是复制路径,不是复制文本,而是真正能在资源管理器 / 微信 / 飞书 / 邮件附件里 Ctrl+V 出来的文件。
English | 中文
它解决什么问题
DSH(DeepSeek Harness)用 present 展示交付文件时,卡片上只能"打开 / 在侧栏预览 / 显示文件位置"。想把这个文件发给别人,只能先打开它、再另存为。
浏览器做不到这件事:Clipboard API 只能写文本和位图,没有任何办法把文件列表写进系统剪贴板(Windows 需要 CF_HDROP)。所以这个扩展做了一件很直接的事:
浏览器里点菜单 → 请求 同一个域名下、已登录认证 的 DSH 路由 → 宿主按会话日志里的坐标解析出真实路径 → 由一个短命的 PowerShell 子进程调用
Clipboard.SetFileDropList把文件放进剪贴板。
文件路径从来不由浏览器提供。前端只能传"哪个会话、第几条交付事件、第几个文件",宿主自己去会话日志和 Session 文件系统里查。这就是它敢开源、敢直接用在你机器上的原因。
你会得到什么
菜单里多三项(原来那组"打开方式"和"显示文件位置"一个都没动):
| 菜单项 | 行为 |
|---|---|
| 复制文件 | 文件本体进剪贴板(CF_HDROP)。可以粘到资源管理器、微信、飞书、邮件附件区 |
| 复制全部 N 个文件 | 同一次 present 的全部文件一次放进剪贴板(仅当 N > 1 时出现) |
| 复制文件路径 | 把绝对路径作为文本放进剪贴板 |
| 失败时 | 自动降级为"复制路径",并用 toast 说明原因(文件已被删除 / 剪贴板被占用 / 不支持的系统…),不会假装成功;如果连路径都写不进剪贴板,才显示「复制失败,请重试」 |
卡片本身还获得 Windows 风格的交互(可选,默认开启):
- 单击 = 选中(底色直接用 DSH 自己的 hover 变深,没有发明新视觉);
- 双击 = 在右侧栏打开预览(Enter / Space 同样可以);
- 选中后
Ctrl+C= 复制这个文件; - 拖动卡片:跟随鼠标的半透明 ghost(DSH 现成文件图标 + 小字号文件名),
- 在页面内松手 → 作为当前对话的附件(所有浏览器可用);
- 拖到资源管理器松手 → 落地一份副本(仅 Chromium)。
截图
三张图都来自真实 dsh web 会话,并且只裁到控件本身,所以看不到工作区路径、会话标题或文件名:
| 菜单里的三项 | 复制成功 | 降级提示 |
|---|---|---|
![]() | ![]() | ![]() |
支持范围
| 能力 | Edge / Chrome / Brave / Opera 等 Chromium | Firefox | Safari |
|---|---|---|---|
| 复制文件 / 复制全部 / 复制路径 | ✅ | ✅ | ✅ |
| 单击选中、双击预览、Ctrl+C | ✅ | ✅ | ✅ |
| 拖进对话(附件) | ✅ | ✅ | ✅(14.1+) |
| 拖到资源管理器落地副本 | ✅ | ❌ 浏览器不支持 | ❌ |
"复制"不挑浏览器:写剪贴板发生在本地宿主进程里,跟浏览器无关。
"拖出去"只有 Chromium 行:靠 dataTransfer 的 DownloadURL,Firefox/Safari 没有这个能力,所以那两个浏览器上不会出现拖拽(不给你一个拖了没反应的假动作)。
平台:Windows 11 + dsh web,已实测。macOS / Linux 的代码路径已实现(osascript / wl-copy·xclip),未测试,不要当成品用。
安装
前提:Node 22.19+、dsh 0.2.0-rc.2、已经能跑 dsh web。
# 1) 克隆到任意位置
git clone <this-repo> dsh-copy-file
# 2) 一键安装(幂等,可重复执行)
cd dsh-copy-file
powershell -ExecutionPolicy Bypass -File scripts/install.ps1
装完重启一次 dsh web,再刷新页面。
为什么必须重启:DSH 的配置热更可以"移除"一个行,但"新增一个此前没解析过的包"需要启动期解析,实测热更不生效(
window.__DSH_BOOT__里不会出现新行)。
它到底改了什么
只改你自己的 profile,DSH 安装目录一个字节都不动:
%USERPROFILE%\.dsh\profiles\web\
├─ package.json ← dsh.profile.bundles 末尾追加 "dsh-copy-file"
├─ node_modules\dsh-copy-file\ ← 这个包
└─ .dsh-copy-file\install.json ← 安装记录(含安装前的 bundle 列表,用于精确回滚)
不需要 pnpm、不需要联网、不写注册表、不装服务、不设开机自启。
用法
聊天里让模型 present 一个文件 → 卡片右侧点箭头 → 选「复制文件」→ 到资源管理器 Ctrl+V。
卸载
powershell -ExecutionPolicy Bypass -File scripts/uninstall.ps1
- 只从
dsh.profile.bundles移除它自己那一项(你之后手动加的其他 bundle 会保留,不会被"还原"掉); - 删除
node_modules\dsh-copy-file和安装记录; - 清理可能存在的 helper 进程与自启快捷方式(本版本默认都没有)。
刷新页面即可:实测移除是即时生效的,无需重启(__DSH_BOOT__ 会回到只有 shipped 行,卡片恢复原生行为)。
校验
powershell -ExecutionPolicy Bypass -File scripts/verify.ps1 # 已安装时
powershell -ExecutionPolicy Bypass -File scripts/verify.ps1 -ExpectAbsent # 卸载后
检查项:包文件完整性、lib/client.js 是否与当前安装的 DSH 上游一致、profile 清单、以及 dsh --profile web --dump-config 里"我们的行存在 / 原生行被禁用"(卸载后反过来断言)。
自测(不需要 GUI):
node tests/clipboard.test.mjs # 真机剪贴板:中文名/空格/引号/220 字符路径/多文件/缺文件
node tests/presented.test.mjs # 坐标解析与拒绝分支
node tests/routes.test.mjs # 四个路由 + 降级返回
node tests/client-bundle.test.mjs # 浏览器半能加载、注册 6 个槽位与字典
跑
node --test时如果 Node 在沙箱里无法 spawn 子进程,逐个文件跑即可(node tests/xxx.test.mjs)。
安全与隐私
| 关注点 | 做法 |
|---|---|
| 监听端口 | 不监听任何端口。复制是一次性的 powershell.exe -STA 子进程,跑完即退 |
| 鉴权 | 复用 DSH 自己的登录 cookie + Host/Origin 栅栏(和 present.open 同一条通道),不引入第二个 token |
| 任意路径 | 路由没有 path 参数。前端只能给 sessionId / seq / index |
| 越权访问 | 宿主从只追加的会话日志取声明,再经 Session 文件系统解析;还要 processPathFromHostPath 往返一致(同名文件冒充不了);最终符号链接、目录、缺失文件都被拒 |
| 读写范围 | 不读文件内容(只 stat + 把路径交给系统 API);不写系统目录;临时 JSON 用后即删 |
| 联网 | 不联网、无遥测、不上传任何东西 |
| 进程残留 | 子进程 8 秒超时强杀;卸载会清理 helper 与自启项 |
已知攻击面:能调用这些路由的前提是已经登录这个本机 DSH,也就是能打开这个网页的人。
兼容与升级
只适配 DSH 0.2.0-rc.2(package.json 里写死、vendor-client.mjs 会校验上游版本,不匹配直接拒绝生成)。
原因:原生下拉菜单没有"菜单项插槽",要在它内部加行,唯一非侵入的做法是把上游那个浏览器包原样 vendored 过来、打 3 处增量 hunk,再用 bundle 行替换原行。这份 lib/client.js 是生成物:
src/copy-ui.js ← 全部新增代码(可读、可改)
lib/client.js ← 生成物,不要手改
scripts/vendor-client.mjs ← 从你安装的 DSH 重新生成,锚点缺失/不唯一就报错
升级 DSH 后:
node scripts/vendor-client.mjs --check会告诉你是否过期;node scripts/vendor-client.mjs重新生成(若上游改了锚点,它会失败并指名哪一处,不会产出半坏的包);- 重跑
scripts/install.ps1,重启一次dsh web。
两条已实测的行为差异:新增行需要重启,移除行即时生效。
上游建议
这个扩展之所以要 vendor 一份浏览器包,是因为原生下拉菜单没有"菜单项插槽"。 给上游的 issue 草稿在 docs/upstream-issue.md(能让以后不再需要 fork)。
已知限制
- 拖到资源管理器只有 Chromium 可行(
DownloadURL)。微信/飞书聊天窗口是否识别取决于它们自己;最稳的是先复制、再在目标里粘贴。 - 任务栏永远不接受文件拖放:拖到任务栏图标上一定是"禁止"光标,任何来源(包括资源管理器自己)都一样。要落地请拖进已打开文件夹的文件列表区或桌面。
DownloadURL只认文件系统落点:Explorer 侧栏的文件夹图标、任务栏、微信/飞书窗口都会拒绝;拖动过程中 Windows 不会自动把目标窗口提到前台,所以先把文件夹窗口露出来再拖。- 原生拖拽出卡片没有在真机窗口里完整复现(调试窗口不在前台时物理拖拽进不了页面);
DownloadURL机制本身在独立页面实测成功(Edge 155,中文名原样落地)。页内"拖进对话"已用真实 dragstart+drop 验证。 - 路径超过 260 字符:剪贴板本身能放,但个别粘贴目标可能截断。
- 会话工作区之外的文件:与"打开文件"能力的边界一致,会被拒绝。
- 改名/移动文件:会话日志不可变,卡片会指向旧路径 → 复制与预览都会失败;这时用「复制文件路径」仍可拿到路径(已实现并实测)。
- 原生应用列表很长:那是上游行为(列出所有已注册应用),我们没动。
- macOS / Linux 未测试;Electron 桌面版未测试。
开发
lib/index.js 宿主半:四个已认证路由(copy.file / copy.path / copy.probe / copy.raw)
lib/presented.mjs 坐标 → 路径(含"声明路径"降级解析)
lib/clipboard.mjs CF_HDROP 写入 + 回读校验(Windows 用 .NET Clipboard)
src/copy-ui.js 浏览器半的全部新增代码(菜单、选中、双击、Ctrl+C、拖拽)
cordis.patch.yml bundle 补丁:禁用 ui-open-in-app,插入 dsh-copy-file 行
scripts/install.ps1 幂等安装(记录安装前状态)
scripts/uninstall.ps1 精确回滚
scripts/verify.ps1 只读校验(含 -ExpectAbsent)
几个踩过的坑(留给下一个改它的人):
pwsh 7没有Set-Clipboard -Path(只有 5.1 有),所以用System.Windows.Forms.Clipboard.SetFileDropList,并且-STA;- 路径不要走命令行参数(中文/空格/引号/超长),走临时 UTF-8 JSON 文件;
- PowerShell 的
ConvertFrom-Json对"数组"只回一个对象,@(...)会套成嵌套数组——必须再过一个ForEach-Object展平; - 控制台代码页会把中文路径变成
?,所以回读一律写 UTF-8 文件,不读 stdout; - 要覆盖上游时,
id、语言命名空间、槽位占用 id 都换成自己的,避免热切换瞬间重复注册抛错。
License
MIT(见 LICENSE)。lib/client.js 是 @deepseek-ai/dsh-client-ui-open-in-app(MIT)的派生副本,归属声明见 NOTICE。
未经许可,仓库名、版权署名、截图请自行替换为你自己的。
Comments
Loading…
From the same category
DeepSeek Harness plugin for Reactive Resume: bridges your resumes and job applications into a Harness session over MCP.
★ 41.7k
↓ 94/wk
MIT
Aug 24, 2026
dsh plugin --profile web add dsh-plugin-reactive-resumeby Tencent
Let AI agents use your real, logged-in browser without interrupting your work. CLI + extension for browser automation across any shell-capable AI agent.
★ 9.6k
↓ 8.9k/wk
MIT
TypeScript
Oct 25, 2026
dsh plugin --profile terminal add @wxg-prc-cpg/browser-skill-dsh-pluginby dsh-market
The plugin market inside DeepSeek Harness — browse, search, one-click install · DSH 可视化插件市场
★ 8.9k
↓ 175.2k/wk
MIT
TypeScript
Oct 24, 2026
dsh plugin --profile web add dshmarketby yjh051108
dsh-routing-suite — injector + router-standard kit: install the runtime injector first, then the task-aware reasoning-mode router preset (measured P1-P23).
★ 7k
MIT
JavaScript
Sep 18, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby Q00
Agent OS: the agent gets smarter on its own. We just hold the line: Interview-gated, staged evaluation, budgeted evolution loop. MCP server, 14 runtimes: Claude Code, Codex CLI, Gemini CLI, OpenCode,
★ 6.3k
MIT
Python
Oct 25, 2026
by superdesigndev
OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn
★ 6.1k
NOASSERTION
Python
Oct 26, 2026
dsh plugin --profile web add treg-dsh

