dsh-menu
为侧边栏「工作区文件」树添加自定义右键菜单:新建文件夹、重命名、在文件管理器中显示、浏览器打开、VS Code 打开、复制路径、流式 ZIP 下载、删除。
安装
dsh plugin --profile web add dsh-menu
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-menu — DeepSeek Harness 插件(当前:工作区文件右键菜单)




为 DeepSeek Harness Web 客户端侧边栏的「工作区文件」树添加自定义右键菜单,替代浏览器默认菜单。
纯前端 + Host 路由实现,无第三方依赖、无构建步骤。项目名为 dsh-menu,后续可在此基础上扩展更多菜单。
已发布到 npm,一行命令装好(装完重启宿主进程 + 浏览器硬刷新):
dsh plugin --profile web add dsh-menu
深色模式 · 浅色模式
 · 
<sub>右键「工作区文件」中的任意行 → 自定义菜单(新建文件夹 / 重命名 / 在文件管理器中显示 / 在浏览器中打开 / 在 VS Code 中打开 / 复制文件路径 / 下载文件夹 / 删除)</sub>
✨ 功能特性
右键「工作区文件」任意文件 / 文件夹行,弹出自定义菜单(顺序即菜单顺序):
# · 菜单项 · 说明
1 · 新建文件夹 · 内联输入框;目录行在其内部创建,文件行在其父目录创建
2 · 重命名 · 内联输入框,可编辑当前文件名
3 · 在文件管理器中显示 · 调用官方 remote 服务,在资源管理器中定位该文件
4 · 在浏览器中打开 · 文件原样返回内容;目录渲染为可浏览的 HTML 列表
5 · 在 VS Code 中打开 · 自动解析本机 VS Code 并打开文件/目录
6 · 复制文件路径 · 复制工作区路径到剪贴板
7 · 下载文件夹 / 下载文件 · 目录打包为流式 ZIP 下载;文件直接下载
8 · 删除(红色) · 危险项红色标注,点击后二次确认
交互细节
- 🪟 毛玻璃卡片:半透明主题色底 +
backdrop-filter: blur(18px) saturate(140%) - ⌨️ 内联表单:重命名 / 新建文件夹复用同一输入体验,支持中文输入法组合输入(composition 感知,避免回车打断候选词)、
Enter提交、Esc取消 - ⚠️ 删除二次确认:点击「删除」切换到确认面板,取消或再次确认
- 🔔 结果 toast:成功 / 失败提示,数秒后自动消失
- 🖱️ 点击外部、
Esc、滚动、窗口缩放都会自动关闭菜单 - 🌐 中英双语:
zh/en两套文案,跟随界面语言
📦 目录结构
dsh-menu/ ├── index.js # Host 半区:全部文件系统操作路由(约 743 行) ├── client.js # Client 半区:右键菜单 UI(约 680 行) ├── package.json # 包元信息 + dsh.bundle / dsh.client 声明 ├── cordis.patch.yml # 把插件并入 Harness profile 的补丁 ├── screenshot-dark.png # 深色模式效果图 ├── screenshot-light.png # 浅色模式效果图 └── README.md # 本文档
🧩 工作原理
- Client 半区(
client.js)在文档捕获阶段拦截contextmenu事件,命中[data-files-entry]树行后preventDefault掉浏览器默认菜单,改弹自定义菜单(挂载在shell.overlay槽位,是一个独立 overlay 组件)。 - Host 半区(
index.js)通过connection.fetch.register注册一组/api/workspace-files-menu/*路由,所有文件系统操作(重命名 / 建目录 / 删除 / 打开 / 下载)都由 Host 执行,Client 只发 JSON。 - 客户端不依赖任何 Harness Client 包:状态用自实现的 observable store +
React.useSyncExternalStore,样式只用官方主题 CSS token(--dsw-alias-*)。 - 按钮式「在文件管理器中显示」走官方
remote.session.openWorkspacePath({ action: "reveal" }),其余操作走自建路由。
Host 路由一览
错误响应统一为 { ok: false, error: { code, message } }。
路由 · 方法 · 参数 · 行为
/api/workspace-files-menu/rename · POST · { sessionId, path, name } · 重命名文件或目录(仅改名,不移动)
/api/workspace-files-menu/mkdir · POST · { sessionId, path, name } · 在 path(必须是目录)内新建文件夹
/api/workspace-files-menu/delete · POST · { sessionId, path } · 删除文件或目录(递归)
/api/workspace-files-menu/open-app · POST · { sessionId, path, app: "vscode" } · 在外部应用中打开;当前支持 vscode
/api/workspace-files-menu/open · GET · ?sessionId=&path= · 浏览器打开:文件返回内容,目录返回 HTML 列表
/api/workspace-files-menu/download · GET · ?sessionId=&path= · 文件直接下载;目录流式打包 ZIP 下载
主要状态码:200 成功;400 非法参数 / 非法文件名 / 父级不是目录;403 目标在工作区之外(含删除根、越界穿越);404 目标不存在;409 重名冲突(rename 到已存在名 / 已存在同名新目录)。
📥 安装
前置:已部署并运行 DeepSeek Harness(含 Web GUI);宿主进程可直接运行 Node.js 与 pnpm。
方式 A:从 npm 安装(推荐)
dsh plugin --profile web add dsh-menu
方式 B:从 GitHub 安装
dsh plugin --profile web add github:tangu2023/dsh-menu
方式 C:从本地目录安装
1. 把整个仓库目录放到本地任意位置(例如 D:\plugins\dsh-menu),无需构建。
2. 在 DSH 的「插件管理」中安装该 bundle,目标选择仓库目录的绝对路径。插件管理器会自动应用 cordis.patch.yml(把 dsh-menu 插入当前 profile)。
三种方式装完后都一样
3. 重启 DSH 宿主进程让 Host 半区加载(插件管理器的 enable/disable 不会重载已缓存的模块)。
4. 浏览器硬刷新(Ctrl+Shift+R)加载 Client 半区。
说明:cordis.patch.yml是唯一要求的配置文件,请勿手工修改 profile 下的package.json/cordis.patch.yml,统一由插件管理器写入。
本插件零依赖、无构建步骤,所以 git 安装不需要任何 build 授权。
卸载:dsh plugin --profile web remove dsh-menu(或插件管理器中禁用后移除)。
若 pnpm 提示新包保护(minimumReleaseAge),dsh plugin add会自动把dsh-menu加入 profile 的minimumReleaseAgeExclude,无需手动处理。
🖱️ 使用
在侧边栏「工作区文件」树中右键任意文件或文件夹行即可弹出菜单;「新建文件夹 / 重命名」为内联输入,「删除」需二次确认,其余操作即时执行并 toast 反馈。
🔒 安全设计
- 工作区边界:所有 Host 路由先取会话工作区根(
sessionWorkspaceRoot),再对目标做双向往返路径校验(processPathFromHostPath→fs.resolve→processPath)与严格前缀边界检查(防D:/workspace2这类前缀穿透),任何越界请求返回403。 - 拒绝删根:对工作区根目录的删除操作一律
403。 - 名称校验:非法字符 / 保留名返回
400;重名返回409;路径按normalize后仅以名称参与拼接,杜绝路径穿越。 - 浏览器打开带隔离:通过
/open返回的任何text/html都会附加Content-Security-Policy: sandbox,防止工作区内 HTML 以 GUI 同源权限执行。 - ZIP 流式打包:目录下载不落临时文件、内存有界(
Readable.from+Readable.toWeb背压),手写 ZIP 写入器(UTF-8 文件名、CRC32、data descriptor),零依赖。
🛠️ 开发与脚本
- 无第三方依赖、无构建步骤;语法自检:
node --check index.js/node --check client.js。 - 改 Host(
index.js):需要重启 DSH 宿主进程(看门狗会自动拉起)。 - 改 Client(
client.js):浏览器硬刷新即可(Host 按请求从磁盘读取 client 模块并自带版本缓存破击,无需重启)。 - 冒烟测试:服务运行后直接对路由发起请求,例如:
curl -X POST http://127.0.0.1:12012/api/workspace-files-menu/rename \
-H "Content-Type: application/json" \
-d '{"sessionId":"<会话ID>","path":"D:/workspace/a.txt","name":"b.txt"}'
常见问题
现象 · 处理
右键仍是浏览器默认菜单 · 浏览器硬刷新;确认 shell.overlay 槽位存在 workspace-files-menu 占用
Host 路由 500 / 404 · 插件是 enable 状态但模块未重载 → 重启 DSH 宿主进程
「在浏览器中打开」403 · 目标不在当前会话工作区内
重命名报「已存在」 · 目标名已占用(409),换名即可
下载中文名文件报 500 · 已在 1.0.0 修复(RFC 6266 响应头编码);旧版本请升级
安装 bundle 报 'pnpm' 不是内部或外部命令 · 宿主进程找不到 pnpm → 执行 npm i -g pnpm,或在宿主工作目录放一个 pnpm.cmd shim 指向全局 pnpm
🤝 兼容性与冲突
依赖的 Harness 接口(随 DSH 版本演进可能需要小幅适配)
shell.overlay槽位:右键菜单的宿主容器- 侧边栏树行属性:
data-files-entry/data-files-path - 主题 token:
--dsw-alias-*(缺失时样式降级,功能不受影响) - Client 注入:
dsh-api-session-controller/dsh-api-remotes/dsh-client-ui-sidebar-right - 官方能力:`remote.sessio
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:4,最后更新 2026-10-08