dsh-plugin-multi-root-workspace
Verified@dsh-electron/dsh-plugin-multi-root-workspace · v0.1.9 · MIT · Web UI
Multi-root Workspace for DeepSeek Harness: adds N additional roots to the workspace sandbox scope as an out-of-tree bundle
Install
dsh plugin add @dsh-electron/dsh-plugin-multi-root-workspace Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Readme
项目简介
DeepSeek Harness 默认把新建工作区所在的目录作为 Agent 的主目录,在默认的工作区读写模式(workspace-write)下,沙箱的读写范围也仅覆盖这一个目录。本插件将 DSH 的工作区范围拓展到“一个主目录 + 多个附加目录”。
并非所有项目都是 monorepo。一个开发项目往往由多个独立 Git 仓库组成,跨仓库干活原本得反复换会话。同类工具早已支持在一个工作区添加多个文件夹,本插件把同样的能力带给 DSH:把其他仓库登记为附加根,并按需授予只读、读写或不受限的访问级别,Agent 即可在授权范围内读写。
- 不新增工具:Agent 照旧使用原生
read/write/edit/bash,只是授权的目录变多了。 - 沙箱约束:附加根可继承会话(主目录)的权限设置,也可单独固定为只读或读写;在只读与读写下,约束不靠系统提示词,而由进程内文件围栏 + 内核级沙箱(macOS Seatbelt / Linux bwrap / Landlock / Windows ACL)执行,文件与 bash 共享同一份授权范围。
- 可选 Auto review(默认关闭):fork 上游实验性「自动授权审查」,向 Reviewer 补充附加目录的审查上下文——当前工作区拓扑、各根访问级别与实时指令文件;出厂关闭,需在插件管理器的行列表中显式打开
@dsh-electron/dsh-plugin-multi-root-workspace/auto-review行后生效,启用时接管官方同名 bundle。 - 不配等于没装:未登记附加根时行为与未装插件逐项一致;配置错误一律明确报错,从不静默降级。
当前发布版本 v0.1.9。各版本的用户可见变更见 CHANGELOG;进度与发布状态见路线图 §进度总账。
快速开始
插件已发布到 npm。在 DSH 侧栏的插件页点添加插件、填入包名 @dsh-electron/dsh-plugin-multi-root-workspace 即可安装;也可以用命令行两步装完:
dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
dsh --profile web # --profile 按你的启动方式选择,可选值见 DSH 文档
tarball、GitHub、本地源码等其他安装方式见安装。
启动后侧栏底部出现 Folders(🗂)动作,或直接在会话里:
/workspace-folders add ~/code/another-repo [--inherit|--read-only|--read-write]
之后 Agent 即可在该目录读写、跑 bash——默认 inherit,访问级别跟随会话模式。DSH 的动态 working directory 只影响新操作的相对路径和执行目录,切换 cwd 不会新增授权,也不会改变注册表归属。
环境要求
使用本插件:一个下表中的 DSH 版本(精确匹配,不是范围)。dsh plugin 负责安装,无需本地 Node 工具链。
| DSH 系列 | 受支持的版本 |
|---|---|
0.1.5 |
0.1.5-rc.2 |
0.1.6 |
0.1.6-alpha.1、0.1.6-alpha.2 |
0.1.7 |
0.1.7-alpha.1、0.1.7-alpha.2、0.1.7-rc.1、0.1.7-rc.2 |
0.2.0 |
0.2.0-rc.1、0.2.0-rc.2 |
0.2.1 |
0.2.1-alpha.1、0.2.1-alpha.2、0.2.1-alpha.3、0.2.1-alpha.4、0.2.1-alpha.5、0.2.1-rc.1、0.2.1-rc.2 |
DSH 版本不在表内、或 @deepseek-ai/dsh-* 各包版本不一致时,插件不会启动,行为等同没装。
从源码构建 / 贡献:Node.js ^22.19.0 || >=24,pnpm 11.25.0(建议经 corepack 启用);开发 pin 的 DSH 为 0.2.1-rc.2。
运行平台:macOS(Seatbelt)、Linux(bwrap / Landlock)、Windows(root-set ACL runner)。
安装
不使用命令行时,DSH 侧栏的插件页提供同一安装入口:点添加插件,填入 npm 包名(或 Git 地址、tarball、本地绝对路径)。
dsh plugin 支持四种安装来源。以下均以 web profile 为例;桌面端(Electron)把 --profile web 换成 --profile desktop,安装方式同源。
| 来源 | 命令 | 说明 |
|---|---|---|
| npm 注册表(推荐) | dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace |
预构建产物,即装即用 |
| GitHub | dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace |
安装时自动从源码构建;allowBuilds 需含本包,建议锁定版本 |
| tarball | dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-<version>.tgz |
预构建离线包,适合内网/离线 |
| 从源码安装 | dsh plugin --profile web add /path/to/package/dsh-plugin-multi-root-workspace |
pnpm link: 链接本地 checkout,适合开发调试;无需 allowBuilds |
首次安装时 dsh plugin add 会以 [ERR_PNPM_IGNORED_BUILDS] 失败:在该 profile 的 pnpm-workspace.yaml 里补上下面这行,再执行一次 add 即可:
allowBuilds:
koffi: true
如果重跑 退出 0 但插件没有进入 dsh.profile.bundles,做一次 remove + add。更多排查见故障排查。
从 npm 安装(推荐)
dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
安装的是预构建产物,首次安装需要添加 allowBuilds。
从 tarball 安装
pnpm pack @dsh-electron/dsh-plugin-multi-root-workspace
# 或从 GitHub Release 资产下载,例如:
# https://github.com/cherrchen/dsh-plugin-multi-root-workspace/releases/download/v0.1.9/dsh-electron-dsh-plugin-multi-root-workspace-0.1.9.tgz
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.9.tgz
同样是预构建产物,适合内网或离线环境交付;首次安装同样需要添加 allowBuilds。
从 GitHub 安装
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace
GitHub 来源装的是源码,add 时会自动构建,不需要手动编译;但 allowBuilds 要写两行——本包自身和 koffi:
allowBuilds:
'@dsh-electron/dsh-plugin-multi-root-workspace': true
koffi: true
建议锁定版本(如 #v0.1.9),让后续推送无法悄悄改变实际运行的内容:
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.9
从本地源码安装
git clone https://github.com/cherrchen/dsh-plugin-multi-root-workspace.git
cd dsh-plugin-multi-root-workspace
export CI=true
pnpm install
pnpm build
dsh plugin --profile web add /path/to/repo
[!TIP]
dsh不在 PATH 里时(例如用源码仓库跑 DSH),把命令里的dsh换成pnpm dsh。
运行方法
开发自检:
pnpm lint && pnpm typecheck
pnpm test
pnpm kernel:probe
pnpm smoke
pnpm docs:check
安装进 DSH 运行时并验证组合:
dsh plugin --profile web add /path/to/repo
dsh --profile web --dump-config | grep -A3 'multi-root'
预期输出:
# == @deepseek-ai/dsh-base, patched by @dsh-electron/dsh-plugin-multi-root-workspace
- id: sandbox
name: '@deepseek-ai/dsh-sandbox-local'
disabled: true
--
# == @deepseek-ai/dsh-base, patched by @dsh-electron/dsh-plugin-multi-root-workspace
- id: fs-sandbox
name: '@deepseek-ai/dsh-fs-sandbox'
disabled: true
--
# == @dsh-electron/dsh-plugin-multi-root-workspace
- id: multi-root-compat
name: '@dsh-electron/dsh-plugin-multi-root-workspace/compat'
- id: multi-root-fs
name: '@dsh-electron/dsh-plugin-multi-root-workspace/fs'
- id: multi-root-sandbox
name: '@dsh-electron/dsh-plugin-multi-root-workspace/sandbox'
- id: multi-root-providers
name: '@dsh-electron/dsh-plugin-multi-root-workspace/providers'
- id: multi-root-scope
name: '@dsh-electron/dsh-plugin-multi-root-workspace/scope'
config:
toolAccess: {}
- id: multi-root-registry
name: '@dsh-electron/dsh-plugin-multi-root-workspace/registry'
config:
leasePath: !!js dshHomePath('storages/multi_root_workspace.lock')
maxAdditionalRoots: 16
- id: multi-root-instructions
name: '@dsh-electron/dsh-plugin-multi-root-workspace/instructions'
config:
maxBytes: 65536
- id: multi-root-lsp
name: '@dsh-electron/dsh-plugin-multi-root-workspace/lsp'
- id: multi-root-workspace-files
name: '@dsh-electron/dsh-plugin-multi-root-workspace/workspace-files'
- id: multi-root-command
name: '@dsh-electron/dsh-plugin-multi-root-workspace/command'
- id: multi-root-client
name: '@dsh-electron/dsh-plugin-multi-root-workspace'
即上游 fs-sandbox 与 sandbox 两行被禁用、由插件同名服务接管,插件自身的 multi-root-* 行全部挂载。启动后侧栏底部会出现 Folders 动作。
使用方法
命令
/workspace-folders [list] # 列出主根与附加根(含每根访问级别与状态)
/workspace-folders add <absolute path> [--inherit|--read-only|--read-write] # 不带路径时打开系统目录选择器;新登记默认 inherit
/workspace-folders access <order|path> inherit|read-only|read-write # 切换访问级别(扩权要求该根此刻可用)
/workspace-folders alias <order|path> [alias] # 不带别名则清除已有别名
/workspace-folders remove <order|path> # 序号即 list 输出的编号
/workspace-folders reveal <order|path> # 在文件管理器中显示
/workspace-folders isolate-profiles [--apply] # 检查 $DSH_HOME profile 依赖导入方式与多链接文件;--apply 写入 copy 配置
/workspace-folders help # 查看命令帮助
路径可带引号;与已有根重复、嵌套在已有根内部、或就是主根本身的路径会被拒绝。
访问级别是三态的(ADR-0017,语义细节见使用指南):inherit 跟随会话模式——workspace-write 可写、read-only 拒写、danger-full-access 不受限;read-only 在任何模式下都拒写(包括 Full-access);read-write 固定在只读会话里仍可写。旧版插件登记的可写记录升级后按 inherit 读。每个主根最多登记 16 个附加根(maxAdditionalRoots 配置可调至 64)。宿主还可以在 multi-root-scope 行配置 toolAccess,例如 { bash: 'read-only' } 让 bash 永远不能写任何附加根(仍可写主根)。
add 后执行 list 的预期输出(workspace-write 会话,website 登记为只读):
Workspace root (primary; access follows the current sandbox mode): /home/me/monorepo
1 /home/me/payments-service [支付] (inherit)
2 /home/me/website (read-only)
2 of 16 additional roots.
Writable additional roots under the current mode: 1 of 2.
面板
侧栏底部的 Folders 对话框列出主根与附加根,支持添加、移除、别名、切换只读/可写、复制路径、在文件管理器中显示与上下移排序。
模型侧
附加根的拓扑信息会注入系统提示词,让模型知道当前有哪些目录可用,授权变化时信息随之更新,不需要向模型提供新的工具。
可选 Auto Review 适配
需要 Auto Review 多根上下文时,在插件管理器的主包行列表中启用默认关闭的 Auto Review 入口。该集成仅面向 DSH 0.2.1-rc.2;普通安装保持原支持范围且不会启用或选择 Auto。
项目结构
.
├── src/
│ ├── roots.ts 纯规则层:canonical 化、冲突校验、登记状态分类(available/missing/redirected/invalid)
│ ├── registry.ts 根注册表:dsh-storage-domain 持久化,同一主根的变更在一条队列里串行
│ ├── registry-lease.ts 跨进程 Registry Authority:POSIX flock(进程死亡由内核释放)
│ ├── registry-lease-win32.ts 同上,Windows named semaphore 实现
│ ├── scope.ts ctx.multiRootScope:唯一的授权源,兼发模型可见的拓扑快照
│ ├── fs.ts 多根文件系统 provider(进程内 fence,子类自上游 LocalFileSystem)
│ ├── sandbox.ts 多根内核沙箱 provider(子类自上游 LocalSandboxProvider)
│ ├── dialects.ts Seatbelt / bwrap / Landlock profile 的识别与附加 grant 拼装(不认识即响亮报错)
│ ├── windows-*.ts Windows root-set ACL runner 与 restricted-token 执行
│ ├── containment.ts 路径包含判定(词法快速路径 + dev/ino 别名回退)
│ ├── instructions.ts 附加根 AGENTS.md / CLAUDE.md 的发现、增量投递、预算与撤回(顶层 + 本会话触碰过的子目录,经 agent/pre-step 注入)
│ ├── lsp.ts 附加根按所在根初始化独立 LSP workspace
│ ├── workspace-files.ts Files 页签附加根文件浏览的 host 路由与 confinement
│ ├── compat.ts multi-root-compat 启动门禁(版本 allowlist 与混装判定)
│ ├── compat/ 版本差异适配层:dsh-version / sandbox-confine / agent-instructions / llm-message 等(可选 peer 按需加载)
│ ├── command.ts /workspace-folders 命令与面板 RPC 的 host 半部
│ ├── contract.ts 面板线协议(zod 双端校验,可内联进浏览器 bundle)
│ └── client/ 浏览器半部:侧栏动作、Folders 对话框、Files 页签根切换器、双语词典
├── tests/ 差分 parity、方言真实执行矩阵、契约往返、组件与 locale 门禁、跨进程 lease e2e、指令注入
├── scripts/ 冒烟(compose / behavior / journey)、文档与内核 runner 检查、兼容性契约检查、升级流程
└── docs/ 需求、架构、决策记录(ADR)、计划、开发工作流
贡献指南
欢迎 Issue 与 PR:
- 先读
AGENTS.md(仓库级规则)与开发工作流(环境、命令、运行时支持矩阵、升级流程)。 - 从
main拉分支;提交信息遵循 conventional commits(feat/fix/perf/refactor+ scope),参见现有历史。 - PR 前本地必须全部通过:
pnpm lint、pnpm typecheck、pnpm build、pnpm test、pnpm docs:check、pnpm smoke(CI 按同序执行,先 build 后 test)。 - 影响行为的变更需同批更新
docs/下的对应文档;README 保持中英双语同步;有取舍的工程决策请新增 ADR。 - 硬约束:不修改上游仓库(deepseek-harness)任何包;插件只做 out-of-tree 扩展。
已知限制
- 附加根的指令文件按需到达模型:根目录顶层的那一份在会话第一步之前注入,子目录里的那一份在本会话成功触碰过该目录之后注入;nested 文件只会在其目录被考察到时重新检查(根层每一步,子目录依赖已投递或有新触碰),投递状态是进程内的(resume 后可能再告知一次),也只识别
read/write/edit三个工具名。主根与 user-global 的指令链仍由上游负责,本插件不重复注入(ADR-0010)。 - 跨进程单写者:同一
$DSH_HOME上同时只允许一个 DSH 进程持有根登记表;另一个进程显示登记表不可用(registry-contended),持锁者退出或崩溃后刷新即接管——这是刻意的 fail-closed,不是待修的竞态(ADR-0007)。 - 支持矩阵是精确版本 allowlist:宿主版本不在清单上、或核心包混装了不同版本时,
fs/sandbox/registry/instructions/lsp/workspace-files六行不启动,组合退化为"没装这个插件"(ADR-0009)。 - Windows 多根 runner 继承上游 partial enforcement、caller-owned 目录及 private temp 分离条件;已启动进程保留启动时根集合,后续进程使用更新后的集合(ADR-0011)。
- Windows 上的网络与设备路径不能成为附加根:UNC(
\\server\share)、\\?\、\\.\与映射网络盘在登记时即以unsupported-path拒绝——restricted token 的 capability SID 无法绑定 SMB 访问(ADR-0011 修订)。 - Windows 受限 shell 是 pwsh,不含 Git Bash:MSYS 在 restricted Low-integrity token 下无法启动(上游 runner 与插件 runner 表现一致,属上游限制);上游 runner 的 confined 子进程因继承 ignore-Ctrl+C 标志而收不到 Ctrl+C,插件 runner 下子进程能收到并由 runner 镜像其退出码。
- 附加根访问级别是三态的:新登记默认
inherit(跟随会话模式),--read-only在任何模式下都拒写(包括 Full-access),--read-write固定在只读会话里仍可写(写授权问题,不是读 confinement;ADR-0017)。已启动的进程与持久终端保留启动时的 deny 集——一个在某根变为只读之前就启动的终端保持不受限,直到重启;cap/access 变化只约束之后的调用与新进程(ADR-0013);提供 workingDirectory 的宿主可以把附加根作为后续工具的执行目录,Session 原始项目与登记身份不变(ADR-0012)。 - 上游选择 Landlock 的 Linux 主机上,受限 Full-access 需要 bwrap:Landlock 的 allowlist 表达不了「全部可写、除这些根」,该组合改选 bubblewrap 拼出拒写边界;主机没有可用的 bwrap(未安装或禁用非特权 userns)时,存在任何拒写边界(只读根或受保护的
$DSH_HOME)的 Full-access 命令在启动前失败关闭。启用 bwrap,或在该主机上避免在 Full-access 下使用显式只读根(ADR-0017)。 - 附加根文件树由 Files 页签的根选择器承载:头部入口列出主根与全部附加根,旁边仍显示选中根的完整路径;选中附加根时逐字节复刻的上游 Files 树体接管树区(懒加载目录、原生行式与图标、watch 自动刷新、文件点击进宿主 viewer、header 原位提供手动刷新),header 右侧「在应用中打开」按钮同时由逐字节复刻的上游控件顶替、对选中根而非主根发起打开;切回主根即恢复原生视图;对话框回归纯管理。该接管依赖上游
data-*DOM 钩子,钩子失效时静默退化为原生观感(ADR-0015、ADR-0016)。LSP 按附加文件所在根初始化独立 workspace;需要宿主配置对应语言服务器(ADR-0011)。 - 命令的输出文案为英文(host 侧没有活动语言信息),面板文案中英双语跟随界面语言。
- Workspace Folders 面板是「侧栏底部动作 + 对话框」,不是独立全屏面板:
sidebar.footer.action是所有受支持版本都提供的插槽,而sidebar.panellist/main不是。
文档
项目长期文档位于 docs/:
Coding Agent 的仓库级规则定义于 AGENTS.md。