@dsh-electron/dsh-plugin-multi-root-workspace 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.en.md +16 -1
- package/CHANGELOG.md +16 -1
- package/README.en.md +21 -13
- package/README.md +29 -21
- package/cordis.patch.yml +18 -4
- package/lib/auto-review-packages-nRjnb4pU.js +15 -0
- package/lib/auto-review.js +166 -0
- package/lib/{command-CESuwlVt.js → command-Bwd6EGNV.js} +3 -3
- package/lib/command.js +1 -1
- package/lib/{compat-DF_yL1bB.js → compat-CagUNzRB.js} +176 -29
- package/lib/compat.js +1 -1
- package/lib/fork-CzbeWxHp.js +670 -0
- package/lib/{fs-9JIzGaf3.js → fs-BTXySwSG.js} +2 -6
- package/lib/fs.js +1 -1
- package/lib/index.js +8 -8
- package/lib/{instructions-D19jRxN0.js → instructions-CGNS3H0H.js} +211 -57
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +1 -1
- package/lib/{registry-DCxC5Lay.js → registry-BClm6wfh.js} +6 -6
- package/lib/registry.js +1 -1
- package/lib/review-context-D-ohSVmA.js +98 -0
- package/lib/review-context.js +2 -0
- package/lib/{roots-B-0l8mNG.js → roots-Ca_W1S9n.js} +1 -1
- package/lib/{sandbox-Kup4_ZIb.js → sandbox-CXk5h-ct.js} +4 -8
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +14 -5
- package/lib/types/auto-review/entry.d.ts +5 -0
- package/lib/types/compat/auto-review-contract.d.ts +3 -0
- package/lib/types/compat/auto-review-loader.d.ts +33 -0
- package/lib/types/compat/auto-review-packages.d.ts +1 -0
- package/lib/types/compat/dsh-version.d.ts +56 -8
- package/lib/types/compat/instruction-activity.d.ts +17 -0
- package/lib/types/compat/pty-marker.d.ts +1 -1
- package/lib/types/compat/review-session.d.ts +2 -0
- package/lib/types/compat.d.ts +15 -5
- package/lib/types/index.d.ts +1 -1
- package/lib/types/instructions.d.ts +18 -1
- package/lib/types/review-context.d.ts +49 -0
- package/lib/types/scope.d.ts +5 -0
- package/lib/windows-runner.js +2 -2
- package/lib/workspace-files.js +2 -2
- package/package.json +47 -7
- package/src/auto-review/FORK.md +26 -0
- package/src/auto-review/LICENSE +21 -0
- package/src/auto-review/README.en.md +22 -0
- package/src/auto-review/README.md +22 -0
- package/lib/{registry-lease-win32-DTO6QpT9.js → registry-lease-win32-DVTq9d4D.js} +1 -1
- package/lib/{windows-runner-contract-CEfmZzu0.js → windows-runner-contract-B5R2K3mM.js} +1 -1
package/CHANGELOG.en.md
CHANGED
|
@@ -8,6 +8,19 @@ This file records the user-visible changes of every **released** version.
|
|
|
8
8
|
- For installation sources, the supported upstream runtimes, and known limitations, see the [README](./README.en.md).
|
|
9
9
|
- The evidence, numbering, and release state of each version live in the [roadmap progress ledger](./docs/plans/active/2026-09-12-multi-root-workspace.md#进度总账). The body of each GitHub Release is generated from the commit history by `scripts/release-notes.mjs` and is the complete raw list behind this file.
|
|
10
10
|
|
|
11
|
+
## [0.1.9] - 2026-10-20
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Optional Auto Review (off by default)**: the main package gains an `./auto-review` subpath export and a `multi-root-auto-review` patch row — a fork of upstream's experimental "auto review" that feeds the reviewer the additional roots' context: the current workspace topology, each root's effective access level, and live instruction files. The row ships `disabled: true`; while off it mounts neither the reviewer nor the review context and does not register the Auto preset. The ordinary support matrix is unchanged — only DSH `0.2.1-rc.2` can enable it, and enabling it on any other release fails loudly rather than silently degrading. To turn it on, open the `@dsh-electron/dsh-plugin-multi-root-workspace/auto-review` row in the Plugin Manager row list (profiles without hot reload ask for a restart), or set `disabled: false` in the profile's `cordis.patch.yml`; the context and the reviewer share this single switch.
|
|
16
|
+
- **Takes over the official reviewer while on**: the same switch mounts the multi-root review context and the fork reviewer, and stops the official `@deepseek-ai/dsh-experimental-auto-review` row in the same group at run time — the official bundle may sit before or after this package, the takeover only touches runtime state and never writes back the official row's persisted configuration; when the official bundle was never selected, the row runs standalone (it registers its own Auto preset). Disabling restores the official row per its original configuration on the next reload/restart and never re-selects Auto on its own; unloading any live Auto owner keeps the official Auto → Full access semantics. A profile that ever installed the old standalone companion package from a local tarball/link must remove its bundle selection and dependency before enabling this row (a leftover companion alongside the built-in entry is refused explicitly). Usage notes live in [src/auto-review/README.en.md](./src/auto-review/README.en.md) ([ADR-0021](./docs/decisions/ADR-0021-auto-review-context-companion.md), [ADR-0022](./docs/decisions/ADR-0022-auto-review-loader-entry.md)).
|
|
17
|
+
|
|
18
|
+
## [0.1.8] - 2026-10-18
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Permission settings page unavailable on DSH 0.2.0**: when installed via `link:`, the plugin resolved optional packages it never loads — packages consumed only as mounted host services (`dsh-lsp` / `dsh-lsp-stdio` / `dsh-working-directory`, among others) — to the checkout's own devDependencies, so a uniformly-versioned host was misjudged as `mixed`; and because the two replacement provider rows injected the compatibility gate while upstream `fs-sandbox` / `sandbox` were already disabled unconditionally, a refused gate removed `ctx.fs` / `ctx.sandbox`, taking down `ctx.shell` and `permissionPresets` with it (`permissionPresets/catalog` reported `service-unavailable`). Optional packages are now classified by who loads the resolved copy and through which anchor — runtime-loaded, host-service, or runner-anchored — so host-service copies inside the plugin root no longer testify, and the Windows ACL package is inspected from the `dsh-sandbox-local` anchor; the two provider rows no longer inject the gate and degrade to upstream-equivalent behavior on a refused installation, while every widened-authority row stays gated (see [ADR-0009 amendment](./docs/decisions/ADR-0009-dsh-compat-contract.md#修订2026-10-18permissionpresetscatalog-事故误报检测--被门禁的-provider)).
|
|
23
|
+
|
|
11
24
|
## [0.1.7] - 2026-10-17
|
|
12
25
|
|
|
13
26
|
### Added
|
|
@@ -156,7 +169,9 @@ The first release: the three MVP milestones (M1–M3).
|
|
|
156
169
|
|
|
157
170
|
- All eight findings of the pre-release external review (three of them release-blocking) are fixed, each with a regression test: a registered directory replaced by a symlink transplanting its authority (which introduced `recordedPath` — **re-resolving a path is not re-authorizing it**), concurrent registry mutations losing a write or reviving a revoked grant (per-primary-root mutations are serialized), a refresh not re-checking directories, a typed path being overwritten by the directory picker, the `reveal` response contract and failure code, duplicate ids not validated, the primary root missing from nesting validation, and CI testing before building.
|
|
158
171
|
|
|
159
|
-
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.
|
|
172
|
+
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.9...HEAD
|
|
173
|
+
[0.1.9]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.8...v0.1.9
|
|
174
|
+
[0.1.8]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.7...v0.1.8
|
|
160
175
|
[0.1.7]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.6...v0.1.7
|
|
161
176
|
[0.1.6]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.5...v0.1.6
|
|
162
177
|
[0.1.5]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.4...v0.1.5
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,19 @@ English: [CHANGELOG.en.md](./CHANGELOG.en.md)
|
|
|
8
8
|
- 安装方式、受支持的上游运行时与已知限制见 [README](./README.md)。
|
|
9
9
|
- 每个版本的实施证据、编号口径与发布状态见[路线图 §进度总账](./docs/plans/active/2026-09-12-multi-root-workspace.md#进度总账);GitHub Release 的正文由 `scripts/release-notes.mjs` 从 commit 历史生成,是本文件之外的完整原始清单。
|
|
10
10
|
|
|
11
|
+
## [0.1.9] - 2026-10-20
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **可选 Auto Review(默认关闭)**:主包新增 `./auto-review` 子路径导出与 `multi-root-auto-review` patch 行——fork 上游实验性「自动授权审查」,向 Reviewer 补充附加根的审查上下文:当前工作区拓扑、各根有效访问级别与实时指令文件。该行出厂 `disabled: true`,关闭时不挂载 reviewer 或 review context、不注册 Auto preset;普通支持矩阵不变,仅 DSH `0.2.1-rc.2` 可启用——在任何其他版本上打开会响亮失败,不会静默降级。启用方式:在插件管理器的行列表中打开 `@dsh-electron/dsh-plugin-multi-root-workspace/auto-review` 行(无热重载的 profile 按提示重启),或在 profile 的 `cordis.patch.yml` 中置 `disabled: false`;context 与 reviewer 共用这一个开关。
|
|
16
|
+
- **启用后接管官方 reviewer**:同一个开关挂载多根 review context 与 fork reviewer,并在运行时停止同组官方 `@deepseek-ai/dsh-experimental-auto-review` 行——官方 bundle 位于主包前后均可,接管只改运行时状态、不写回官方的持久配置;官方 bundle 未被选择时该行独立运行(自带注册 Auto preset)。关闭后官方按原配置随 reload/restart 恢复,不自动重选 Auto;任何存活 Auto owner 的卸载沿用官方 Auto → Full access 语义。曾以本地 tarball/link 安装过旧独立 companion 包的 profile,须先移除其 bundle 与依赖再启用本行(残留 companion 与内置入口共存会被明确拒绝)。使用说明见 [src/auto-review/README.md](./src/auto-review/README.md)([ADR-0021](./docs/decisions/ADR-0021-auto-review-context-companion.md)、[ADR-0022](./docs/decisions/ADR-0022-auto-review-loader-entry.md))。
|
|
17
|
+
|
|
18
|
+
## [0.1.8] - 2026-10-18
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **DSH 0.2.0 上权限设置页不可用**:以 `link:` 方式安装时,插件把只作为宿主服务消费、自身从不加载的可选包(`dsh-lsp` / `dsh-lsp-stdio` / `dsh-working-directory` 等)解析到检出目录自己的 devDependencies,版本统一的宿主被误判为 `mixed`;同时两个 provider 替身行 inject 兼容门禁而上游 `fs-sandbox` / `sandbox` 已被无条件禁用,门禁一旦拒绝,`ctx.fs` / `ctx.sandbox` 消失并连带 `ctx.shell` 与 `permissionPresets` 瘫痪(`permissionPresets/catalog` 报 `service-unavailable`)。可选包现按"谁加载副本、经哪个锚点"分为运行时加载、宿主服务与 runner 锚定三类——插件根内的宿主服务副本不再作证,Windows ACL 包改从 `dsh-sandbox-local` 锚点取证;两个 provider 行不再 inject 门禁,在被拒安装上退化为上游等价行为,宽授权行的门禁不变([ADR-0009 修订](./docs/decisions/ADR-0009-dsh-compat-contract.md#修订2026-10-18permissionpresetscatalog-事故误报检测--被门禁的-provider))。
|
|
23
|
+
|
|
11
24
|
## [0.1.7] - 2026-10-17
|
|
12
25
|
|
|
13
26
|
### Added
|
|
@@ -156,7 +169,9 @@ English: [CHANGELOG.en.md](./CHANGELOG.en.md)
|
|
|
156
169
|
|
|
157
170
|
- 发布前外部评审提出的 8 项发现(其中 3 项发布阻断)全部修复,每项各带一个回归测试:登记目录被替换为符号链接后授权转移(引入 `recordedPath`,**重新解析路径不等于重新授权**)、并发修改注册表丢写或复活已撤销的授权(每个主根的变更串行)、刷新不重新检查目录、手输路径被目录选择器覆盖、`reveal` 的应答契约与失败码、重复 id 未校验、主根未纳入嵌套校验、CI 先测后构建。
|
|
158
171
|
|
|
159
|
-
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.
|
|
172
|
+
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.9...HEAD
|
|
173
|
+
[0.1.9]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.8...v0.1.9
|
|
174
|
+
[0.1.8]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.7...v0.1.8
|
|
160
175
|
[0.1.7]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.6...v0.1.7
|
|
161
176
|
[0.1.6]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.5...v0.1.6
|
|
162
177
|
[0.1.5]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.4...v0.1.5
|
package/README.en.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
+
<img src="public/multi-root-mark.svg" width="120" />
|
|
4
|
+
|
|
3
5
|
# dsh-plugin-multi-root-workspace
|
|
4
6
|
|
|
5
7
|
[中文](./README.md) | English
|
|
@@ -8,24 +10,24 @@
|
|
|
8
10
|
|
|
9
11
|
## What & Why
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
By default, [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) treats the directory a new workspace is created in as the agent's primary directory; under the default workspace read-write mode (`workspace-write`), the sandbox's read-write scope covers that directory alone. This plugin extends DSH's workspace scope to "one primary root + N additional roots".
|
|
12
14
|
|
|
13
|
-
A real development project is usually several independent Git repositories, and working across them meant juggling sessions. Peer harnesses already
|
|
15
|
+
Not every project is a monorepo. A real development project is usually several independent Git repositories, and working across them meant juggling sessions. Peer harnesses already support adding multiple folders to a single workspace. This plugin brings the same capability to DSH — register the other repositories as additional roots, grant each read-only, read-write, or unrestricted access, and the agent can work in them within that grant.
|
|
14
16
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
+
- **No new tools**: the agent keeps using the native `read` / `write` / `edit` / `bash`; only the granted directories grow.
|
|
18
|
+
- **Sandbox confinement**: an additional root may inherit the session's (primary root's) permission setting, or be pinned to read-only or read-write; under read-only and read-write, confinement does not rest on system prompts — it is enforced by an in-process filesystem fence plus kernel-level sandboxes (macOS Seatbelt / Linux bwrap / Landlock / Windows ACL), with files and bash sharing one and the same scope.
|
|
19
|
+
- **Optional Auto review (off by default)**: a fork of upstream's experimental auto-review that gives the reviewer the additional directories' context — the current workspace topology, each root's access level, and live instruction files; it ships disabled and only takes effect once the `@dsh-electron/dsh-plugin-multi-root-workspace/auto-review` row is explicitly enabled in the Plugin Manager row list, taking over the official bundle while on.
|
|
17
20
|
- **Absent when absent**: with no additional root configured, its behavior is item-for-item identical to an uninstalled harness; every misconfiguration fails loudly instead of silently degrading.
|
|
18
21
|
|
|
19
|
-
Current released version: `v0.1.
|
|
22
|
+
Current released version: `v0.1.9`. The user-visible changes of each version live in the [CHANGELOG](./CHANGELOG.en.md); progress and release state live in the [roadmap progress ledger](./docs/plans/active/2026-09-12-multi-root-workspace.md#进度总账).
|
|
20
23
|
|
|
21
24
|
## Quick Start
|
|
22
25
|
|
|
23
|
-
The plugin is published on [npm](https://www.npmjs.com/package/@dsh-electron/dsh-plugin-multi-root-workspace).
|
|
26
|
+
The plugin is published on [npm](https://www.npmjs.com/package/@dsh-electron/dsh-plugin-multi-root-workspace). Open the **Plugins** page in the sidebar, choose **Add plugin**, and paste the package name `@dsh-electron/dsh-plugin-multi-root-workspace` — or install it in two commands:
|
|
24
27
|
|
|
25
28
|
```sh
|
|
26
29
|
dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
|
|
27
|
-
dsh --profile web
|
|
28
|
-
# pick the --profile for how you launch DSH; see its docs for the values
|
|
30
|
+
dsh --profile web # pick the --profile for how you launch DSH; see its docs for the values
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
Once it is up, the **Folders** action (`🗂`) appears at the sidebar foot — or, straight in a session:
|
|
@@ -56,6 +58,8 @@ The plugin does not start — as if it were not installed — when the DSH relea
|
|
|
56
58
|
|
|
57
59
|
## Installation
|
|
58
60
|
|
|
61
|
+
The same install is available without the CLI: open **Plugins** in the sidebar, choose **Add plugin**, and enter the npm package name (or a Git address, tarball, or absolute local path).
|
|
62
|
+
|
|
59
63
|
`dsh plugin` supports four install sources. All commands below use the web profile as the example; for the Electron desktop swap `--profile web` for `--profile desktop` — the install method is the same.
|
|
60
64
|
|
|
61
65
|
| Source | Command | Notes |
|
|
@@ -72,7 +76,7 @@ allowBuilds:
|
|
|
72
76
|
koffi: true
|
|
73
77
|
```
|
|
74
78
|
|
|
75
|
-
If the retry exits 0 but the plugin never lands in `dsh.profile.bundles`, do a `remove` + `add`. Further diagnosis: [troubleshooting](./docs/troubleshooting/install-stops-at-build-approval.md).
|
|
79
|
+
If the retry `exits 0` but the plugin never lands in `dsh.profile.bundles`, do a `remove` + `add`. Further diagnosis: [troubleshooting](./docs/troubleshooting/install-stops-at-build-approval.md).
|
|
76
80
|
|
|
77
81
|
### Install from npm (recommended)
|
|
78
82
|
|
|
@@ -87,8 +91,8 @@ This installs pre-built artifacts; the first install needs the `allowBuilds` add
|
|
|
87
91
|
```sh
|
|
88
92
|
pnpm pack @dsh-electron/dsh-plugin-multi-root-workspace
|
|
89
93
|
# or download the tgz from the GitHub Release assets, e.g.:
|
|
90
|
-
# https://github.com/cherrchen/dsh-plugin-multi-root-workspace/releases/download/v0.1.
|
|
91
|
-
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.
|
|
94
|
+
# 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
|
|
95
|
+
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.9.tgz
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
Also pre-built, handy for air-gapped or offline delivery; the first install needs the same `allowBuilds` addition.
|
|
@@ -107,10 +111,10 @@ allowBuilds:
|
|
|
107
111
|
koffi: true
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
Pinning a version (e.g. `#v0.1.
|
|
114
|
+
Pinning a version (e.g. `#v0.1.9`) is recommended so a later push cannot silently change what actually runs:
|
|
111
115
|
|
|
112
116
|
```sh
|
|
113
|
-
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.
|
|
117
|
+
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.9
|
|
114
118
|
```
|
|
115
119
|
|
|
116
120
|
### Install from a local clone
|
|
@@ -230,6 +234,10 @@ The **Folders** dialog at the sidebar foot lists the primary root and the additi
|
|
|
230
234
|
|
|
231
235
|
The additional-root topology is injected into the system prompt so the model knows which directories are available, and the information updates when grants change. No new tool needs to be exposed to the model.
|
|
232
236
|
|
|
237
|
+
## Optional Auto Review integration
|
|
238
|
+
|
|
239
|
+
For multi-root reviewer context, enable the default-disabled [Auto Review row](./src/auto-review/README.en.md) in the main package’s Plugin Manager row list. The joint integration targets only DSH `0.2.1-rc.2`; ordinary installation keeps the existing support matrix and does not enable or select Auto.
|
|
240
|
+
|
|
233
241
|
## Project Structure
|
|
234
242
|
|
|
235
243
|
```text
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
+
<img src="public/multi-root-mark.svg" width="120" />
|
|
4
|
+
|
|
3
5
|
# dsh-plugin-multi-root-workspace
|
|
4
6
|
|
|
5
7
|
中文 | [English](./README.en.md)
|
|
@@ -8,24 +10,24 @@
|
|
|
8
10
|
|
|
9
11
|
## 项目简介
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 默认把新建工作区所在的目录作为 Agent 的主目录,在默认的工作区读写模式(`workspace-write`)下,沙箱的读写范围也仅覆盖这一个目录。本插件将 DSH 的工作区范围拓展到“一个主目录 + 多个附加目录”。
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
并非所有项目都是 monorepo。一个开发项目往往由多个独立 Git 仓库组成,跨仓库干活原本得反复换会话。同类工具早已支持在一个工作区添加多个文件夹,本插件把同样的能力带给 DSH:把其他仓库登记为附加根,并按需授予只读、读写或不受限的访问级别,Agent 即可在授权范围内读写。
|
|
14
16
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
+
- **不新增工具**:Agent 照旧使用原生 `read` / `write` / `edit` / `bash`,只是授权的目录变多了。
|
|
18
|
+
- **沙箱约束**:附加根可继承会话(主目录)的权限设置,也可单独固定为只读或读写;在只读与读写下,约束不靠系统提示词,而由进程内文件围栏 + 内核级沙箱(macOS Seatbelt / Linux bwrap / Landlock / Windows ACL)执行,文件与 bash 共享同一份授权范围。
|
|
19
|
+
- **可选 Auto review(默认关闭)**:fork 上游实验性「自动授权审查」,向 Reviewer 补充附加目录的审查上下文——当前工作区拓扑、各根访问级别与实时指令文件;出厂关闭,需在插件管理器的行列表中显式打开 `@dsh-electron/dsh-plugin-multi-root-workspace/auto-review` 行后生效,启用时接管官方同名 bundle。
|
|
17
20
|
- **不配等于没装**:未登记附加根时行为与未装插件逐项一致;配置错误一律明确报错,从不静默降级。
|
|
18
21
|
|
|
19
|
-
当前发布版本 `v0.1.
|
|
22
|
+
当前发布版本 `v0.1.9`。各版本的用户可见变更见 [CHANGELOG](./CHANGELOG.md);进度与发布状态见[路线图 §进度总账](./docs/plans/active/2026-09-12-multi-root-workspace.md#进度总账)。
|
|
20
23
|
|
|
21
24
|
## 快速开始
|
|
22
25
|
|
|
23
|
-
插件已发布到 [npm](https://www.npmjs.com/package/@dsh-electron/dsh-plugin-multi-root-workspace)
|
|
26
|
+
插件已发布到 [npm](https://www.npmjs.com/package/@dsh-electron/dsh-plugin-multi-root-workspace)。在 DSH 侧栏的**插件**页点**添加插件**、填入包名 `@dsh-electron/dsh-plugin-multi-root-workspace` 即可安装;也可以用命令行两步装完:
|
|
24
27
|
|
|
25
28
|
```sh
|
|
26
29
|
dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
|
|
27
|
-
dsh --profile web
|
|
28
|
-
# --profile 按你的启动方式选择,可选值见 DSH 文档
|
|
30
|
+
dsh --profile web # --profile 按你的启动方式选择,可选值见 DSH 文档
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
tarball、GitHub、本地源码等其他安装方式见[安装](#安装)。
|
|
@@ -58,6 +60,8 @@ DSH 版本不在表内、或 `@deepseek-ai/dsh-*` 各包版本不一致时,插
|
|
|
58
60
|
|
|
59
61
|
## 安装
|
|
60
62
|
|
|
63
|
+
不使用命令行时,DSH 侧栏的**插件**页提供同一安装入口:点**添加插件**,填入 npm 包名(或 Git 地址、tarball、本地绝对路径)。
|
|
64
|
+
|
|
61
65
|
`dsh plugin` 支持四种安装来源。以下均以 web profile 为例;桌面端(Electron)把 `--profile web` 换成 `--profile desktop`,安装方式同源。
|
|
62
66
|
|
|
63
67
|
| 来源 | 命令 | 说明 |
|
|
@@ -74,7 +78,7 @@ allowBuilds:
|
|
|
74
78
|
koffi: true
|
|
75
79
|
```
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
如果重跑 `退出 0` 但插件没有进入 `dsh.profile.bundles`,做一次 `remove` + `add`。更多排查见[故障排查](./docs/troubleshooting/install-stops-at-build-approval.md)。
|
|
78
82
|
|
|
79
83
|
### 从 npm 安装(推荐)
|
|
80
84
|
|
|
@@ -89,8 +93,8 @@ dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
|
|
|
89
93
|
```sh
|
|
90
94
|
pnpm pack @dsh-electron/dsh-plugin-multi-root-workspace
|
|
91
95
|
# 或从 GitHub Release 资产下载,例如:
|
|
92
|
-
# https://github.com/cherrchen/dsh-plugin-multi-root-workspace/releases/download/v0.1.
|
|
93
|
-
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.
|
|
96
|
+
# 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
|
|
97
|
+
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.9.tgz
|
|
94
98
|
```
|
|
95
99
|
|
|
96
100
|
同样是预构建产物,适合内网或离线环境交付;首次安装同样需要添加 `allowBuilds`。
|
|
@@ -109,10 +113,10 @@ allowBuilds:
|
|
|
109
113
|
koffi: true
|
|
110
114
|
```
|
|
111
115
|
|
|
112
|
-
建议锁定版本(如 `#v0.1.
|
|
116
|
+
建议锁定版本(如 `#v0.1.9`),让后续推送无法悄悄改变实际运行的内容:
|
|
113
117
|
|
|
114
118
|
```sh
|
|
115
|
-
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.
|
|
119
|
+
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.9
|
|
116
120
|
```
|
|
117
121
|
|
|
118
122
|
### 从本地源码安装
|
|
@@ -200,14 +204,14 @@ dsh --profile web --dump-config | grep -A3 'multi-root'
|
|
|
200
204
|
### 命令
|
|
201
205
|
|
|
202
206
|
```text
|
|
203
|
-
/workspace-folders [list]
|
|
204
|
-
/workspace-folders add
|
|
205
|
-
/workspace-folders access
|
|
206
|
-
/workspace-folders alias
|
|
207
|
-
/workspace-folders remove
|
|
208
|
-
/workspace-folders reveal
|
|
209
|
-
/workspace-folders isolate-profiles [--apply]
|
|
210
|
-
/workspace-folders help
|
|
207
|
+
/workspace-folders [list] # 列出主根与附加根(含每根访问级别与状态)
|
|
208
|
+
/workspace-folders add <absolute path> [--inherit|--read-only|--read-write] # 不带路径时打开系统目录选择器;新登记默认 inherit
|
|
209
|
+
/workspace-folders access <order|path> inherit|read-only|read-write # 切换访问级别(扩权要求该根此刻可用)
|
|
210
|
+
/workspace-folders alias <order|path> [alias] # 不带别名则清除已有别名
|
|
211
|
+
/workspace-folders remove <order|path> # 序号即 list 输出的编号
|
|
212
|
+
/workspace-folders reveal <order|path> # 在文件管理器中显示
|
|
213
|
+
/workspace-folders isolate-profiles [--apply] # 检查 $DSH_HOME profile 依赖导入方式与多链接文件;--apply 写入 copy 配置
|
|
214
|
+
/workspace-folders help # 查看命令帮助
|
|
211
215
|
```
|
|
212
216
|
|
|
213
217
|
路径可带引号;与已有根重复、嵌套在已有根内部、或就是主根本身的路径会被拒绝。
|
|
@@ -232,6 +236,10 @@ Writable additional roots under the current mode: 1 of 2.
|
|
|
232
236
|
|
|
233
237
|
附加根的拓扑信息会注入系统提示词,让模型知道当前有哪些目录可用,授权变化时信息随之更新,不需要向模型提供新的工具。
|
|
234
238
|
|
|
239
|
+
## 可选 Auto Review 适配
|
|
240
|
+
|
|
241
|
+
需要 Auto Review 多根上下文时,在插件管理器的主包行列表中启用默认关闭的 [Auto Review 入口](./src/auto-review/README.md)。该集成仅面向 DSH `0.2.1-rc.2`;普通安装保持原支持范围且不会启用或选择 Auto。
|
|
242
|
+
|
|
235
243
|
## 项目结构
|
|
236
244
|
|
|
237
245
|
```text
|
package/cordis.patch.yml
CHANGED
|
@@ -32,12 +32,20 @@
|
|
|
32
32
|
- id: sandbox
|
|
33
33
|
disabled: true
|
|
34
34
|
- insert:
|
|
35
|
-
# The compatibility gate comes first, and every
|
|
36
|
-
# injects `multiRootCompat`. If the installed DSH release is not on
|
|
37
|
-
# plugin's allowlist, this row throws and none of them start, rather
|
|
38
|
-
# widening a sandbox profile whose shape was never verified (ADR-0009).
|
|
35
|
+
# The compatibility gate comes first, and every widened-authority row
|
|
36
|
+
# below injects `multiRootCompat`. If the installed DSH release is not on
|
|
37
|
+
# this plugin's allowlist, this row throws and none of them start, rather
|
|
38
|
+
# than widening a sandbox profile whose shape was never verified (ADR-0009).
|
|
39
39
|
- id: multi-root-compat
|
|
40
40
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/compat'
|
|
41
|
+
# The two PROVIDER rows deliberately do NOT inject the gate: their widening
|
|
42
|
+
# is scope-driven, and the gated registry can never populate the scope on a
|
|
43
|
+
# refused installation, so they degenerate to the upstream providers. They
|
|
44
|
+
# MUST keep serving: upstream `fs-sandbox`/`sandbox` are already disabled
|
|
45
|
+
# above, so holding `ctx.fs`/`ctx.sandbox` back would take `ctx.shell`,
|
|
46
|
+
# `permissionPresets` and every confining tool down with them — a refused
|
|
47
|
+
# gate would brick the profile instead of degrading to "plugin not
|
|
48
|
+
# installed" (ADR-0009).
|
|
41
49
|
- id: multi-root-fs
|
|
42
50
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/fs'
|
|
43
51
|
- id: multi-root-sandbox
|
|
@@ -124,3 +132,9 @@
|
|
|
124
132
|
dshHome: !!js dshHomePath()
|
|
125
133
|
- id: multi-root-client
|
|
126
134
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace'
|
|
135
|
+
# One optional row owns its context service and review owner. It remains
|
|
136
|
+
# inert until explicitly enabled in the Plugin Manager row list.
|
|
137
|
+
- id: multi-root-auto-review
|
|
138
|
+
name: '@dsh-electron/dsh-plugin-multi-root-workspace/auto-review'
|
|
139
|
+
disabled: true
|
|
140
|
+
required: true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
//#region src/compat/auto-review-packages.ts
|
|
4
|
+
/** Exact fork execution copies; ordinary Multi-root keeps its broader contract. */
|
|
5
|
+
function assertForkPackages() {
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
for (const name of [
|
|
8
|
+
"@deepseek-ai/dsh-llm",
|
|
9
|
+
"@deepseek-ai/dsh-tools",
|
|
10
|
+
"@deepseek-ai/dsh-permission-presets",
|
|
11
|
+
"@deepseek-ai/dsh-util-values"
|
|
12
|
+
]) if (JSON.parse(readFileSync(require.resolve(`${name}/package.json`), "utf8")).version !== "0.2.1-rc.2") throw new Error(`multi-root Auto Review: incompatible execution copy of ${name}`);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { assertForkPackages as t };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { n as assertAutoReviewRelease, t as MultiRootReviewContext } from "./review-context-D-ohSVmA.js";
|
|
2
|
+
import { t as assertForkPackages } from "./auto-review-packages-nRjnb4pU.js";
|
|
3
|
+
import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
|
|
4
|
+
//#region src/compat/auto-review-loader.ts
|
|
5
|
+
const OFFICIAL_AUTO_REVIEW = "@deepseek-ai/dsh-experimental-auto-review";
|
|
6
|
+
const takeovers = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function cloneRows(value) {
|
|
8
|
+
try {
|
|
9
|
+
return structuredClone(value);
|
|
10
|
+
} catch {
|
|
11
|
+
return Array.isArray(value) ? value.map((row) => ({ ...row })) : { ...value };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* internal/plugin is emitted before Fiber checks dependencies or runs apply.
|
|
16
|
+
* Disposing there also stops an import already in flight when takeover began.
|
|
17
|
+
* The group update wrapper preserves desired configuration and drains our
|
|
18
|
+
* owner BEFORE the update that lets the official owner run again.
|
|
19
|
+
*/
|
|
20
|
+
var AutoReviewTakeover = class {
|
|
21
|
+
ctx;
|
|
22
|
+
entry;
|
|
23
|
+
group;
|
|
24
|
+
restoreUpdate;
|
|
25
|
+
predecessor;
|
|
26
|
+
closed;
|
|
27
|
+
releaseClosed;
|
|
28
|
+
stopHook;
|
|
29
|
+
desired;
|
|
30
|
+
stopOwner = async () => {};
|
|
31
|
+
exiting = false;
|
|
32
|
+
closing;
|
|
33
|
+
draining = /* @__PURE__ */ new Set();
|
|
34
|
+
constructor(ctx) {
|
|
35
|
+
this.ctx = ctx;
|
|
36
|
+
const entry = ctx.fiber.entry;
|
|
37
|
+
if (entry === void 0) throw new Error("multi-root Auto Review must be mounted as a Loader entry");
|
|
38
|
+
if (entry.disabled) throw new Error("multi-root Auto Review mounted on a disabled Loader entry");
|
|
39
|
+
this.entry = entry;
|
|
40
|
+
this.group = entry.parent;
|
|
41
|
+
const previous = takeovers.get(this.group);
|
|
42
|
+
if (previous !== void 0 && previous.entry !== entry && previous.closing === void 0) throw new Error("Only one multi-root Auto Review Loader entry may own a group");
|
|
43
|
+
this.desired = cloneRows(previous?.desired ?? this.group.data);
|
|
44
|
+
if (this.desired.some((row) => row.name === "@dsh-electron/dsh-plugin-multi-root-auto-review")) throw new Error("Remove the legacy @dsh-electron/dsh-plugin-multi-root-auto-review package/bundle before enabling the built-in Auto Review entry");
|
|
45
|
+
this.predecessor = previous?.closed ?? Promise.resolve();
|
|
46
|
+
this.closed = new Promise((resolve) => {
|
|
47
|
+
this.releaseClosed = resolve;
|
|
48
|
+
});
|
|
49
|
+
previous?.restoreUpdate();
|
|
50
|
+
takeovers.set(this.group, this);
|
|
51
|
+
const self = this;
|
|
52
|
+
this.restoreUpdate = wrapMethod(this.group, "update", (original) => function(config) {
|
|
53
|
+
self.desired = cloneRows(config);
|
|
54
|
+
if (config.some((row) => row.name === "@dsh-electron/dsh-plugin-multi-root-auto-review")) return Promise.reject(/* @__PURE__ */ new Error("Remove the legacy multi-root Auto Review companion before reloading this profile"));
|
|
55
|
+
const own = config.find((row) => row.id === self.entry.options.id);
|
|
56
|
+
if (own === void 0 || own.name !== self.entry.options.name || self.isDisabled(own)) {
|
|
57
|
+
self.exiting = true;
|
|
58
|
+
self.entry.options.disabled = true;
|
|
59
|
+
self.entry.fiber?.dispose();
|
|
60
|
+
}
|
|
61
|
+
return Reflect.apply(original, this, [config.filter((row) => row.name !== OFFICIAL_AUTO_REVIEW)]);
|
|
62
|
+
});
|
|
63
|
+
this.stopHook = ctx.on("internal/plugin", (fiber) => {
|
|
64
|
+
const candidate = fiber.entry;
|
|
65
|
+
if (fiber.uid === null || candidate?.parent !== this.group || candidate.options.name !== "@deepseek-ai/dsh-experimental-auto-review") return;
|
|
66
|
+
this.suppress(candidate, fiber);
|
|
67
|
+
}, { global: true });
|
|
68
|
+
}
|
|
69
|
+
isDisabled(options) {
|
|
70
|
+
const value = options.disabled;
|
|
71
|
+
return typeof value === "object" && value !== null && "__jsExpr" in value ? Boolean(this.entry.evaluate(String(value.__jsExpr))) : Boolean(value);
|
|
72
|
+
}
|
|
73
|
+
suppress(entry, fiber = entry.fiber) {
|
|
74
|
+
entry.options.disabled = true;
|
|
75
|
+
if (this.group.tree.store[entry.options.id] === entry) this.group.remove(entry.options.id);
|
|
76
|
+
if (fiber === void 0) return;
|
|
77
|
+
const task = Promise.resolve(fiber.dispose()).then(async () => {
|
|
78
|
+
await fiber.await();
|
|
79
|
+
});
|
|
80
|
+
this.draining.add(task);
|
|
81
|
+
task.finally(() => this.draining.delete(task)).catch((error) => this.ctx.logger.error(error));
|
|
82
|
+
}
|
|
83
|
+
async suspendOfficial() {
|
|
84
|
+
await this.predecessor;
|
|
85
|
+
for (const entry of Object.values(this.group.tree.store)) if (entry.parent === this.group && entry.options.name === "@deepseek-ai/dsh-experimental-auto-review") this.suppress(entry);
|
|
86
|
+
await Promise.allSettled([...this.draining]);
|
|
87
|
+
}
|
|
88
|
+
/** Called before any asynchronous startup, so early unload also drains it. */
|
|
89
|
+
setOwnerDisposer(stop) {
|
|
90
|
+
this.stopOwner = stop;
|
|
91
|
+
}
|
|
92
|
+
close(restore = true) {
|
|
93
|
+
return this.closing ??= this.finish(restore).finally(() => this.releaseClosed());
|
|
94
|
+
}
|
|
95
|
+
async finish(restore) {
|
|
96
|
+
await this.stopOwner();
|
|
97
|
+
await Promise.allSettled([...this.draining]);
|
|
98
|
+
this.stopHook();
|
|
99
|
+
this.restoreUpdate();
|
|
100
|
+
if (!restore || takeovers.get(this.group) !== this || !(this.exiting || this.entry.disabled) || this.group.ctx.fiber.state === 5 || this.group.ctx.fiber.uid === null) return;
|
|
101
|
+
for (const [index, options] of this.desired.entries()) {
|
|
102
|
+
if (options.name !== "@deepseek-ai/dsh-experimental-auto-review" || this.group.tree.store[options.id] !== void 0) continue;
|
|
103
|
+
const copy = cloneRows(options);
|
|
104
|
+
this.group.data.splice(Math.min(index, this.group.data.length), 0, copy);
|
|
105
|
+
const id = await this.group.create(copy);
|
|
106
|
+
await this.group.tree.store[id]?.fiber?.await();
|
|
107
|
+
}
|
|
108
|
+
takeovers.delete(this.group);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/auto-review/entry.ts
|
|
113
|
+
const name = "multi-root-auto-review";
|
|
114
|
+
const inject = [
|
|
115
|
+
"loader",
|
|
116
|
+
"multiRootCompat",
|
|
117
|
+
"multiRootProviders",
|
|
118
|
+
"multiRootScope",
|
|
119
|
+
"multiRootRegistry",
|
|
120
|
+
"multiRootInstructions",
|
|
121
|
+
"sandboxPolicy",
|
|
122
|
+
"workingDirectory",
|
|
123
|
+
"fs",
|
|
124
|
+
"approval",
|
|
125
|
+
"llm",
|
|
126
|
+
"permissionPresets",
|
|
127
|
+
"sessions",
|
|
128
|
+
"tools"
|
|
129
|
+
];
|
|
130
|
+
async function apply(ctx) {
|
|
131
|
+
assertAutoReviewRelease(ctx.multiRootCompat.release);
|
|
132
|
+
assertForkPackages();
|
|
133
|
+
await ctx.effect(async function* () {
|
|
134
|
+
const takeover = new AutoReviewTakeover(ctx);
|
|
135
|
+
yield takeover.stopHook;
|
|
136
|
+
yield () => takeover.close();
|
|
137
|
+
let context;
|
|
138
|
+
let reviewer;
|
|
139
|
+
const setup = (async () => {
|
|
140
|
+
await takeover.suspendOfficial();
|
|
141
|
+
ctx.fiber.assertActive();
|
|
142
|
+
const mountedContext = ctx.plugin(MultiRootReviewContext);
|
|
143
|
+
context = mountedContext.ctx.fiber;
|
|
144
|
+
await mountedContext;
|
|
145
|
+
if (context.state !== 2) throw new Error("multi-root Auto Review context did not activate");
|
|
146
|
+
const fork = await import("./fork-CzbeWxHp.js");
|
|
147
|
+
ctx.fiber.assertActive();
|
|
148
|
+
const mountedReviewer = ctx.plugin(fork);
|
|
149
|
+
reviewer = mountedReviewer.ctx.fiber;
|
|
150
|
+
await mountedReviewer;
|
|
151
|
+
if (reviewer.state !== 2) throw new Error("multi-root Auto Review owner did not activate");
|
|
152
|
+
})();
|
|
153
|
+
takeover.setOwnerDisposer(async () => {
|
|
154
|
+
await setup.catch(() => {});
|
|
155
|
+
await reviewer?.dispose();
|
|
156
|
+
await reviewer?.await();
|
|
157
|
+
await context?.dispose();
|
|
158
|
+
await context?.await();
|
|
159
|
+
});
|
|
160
|
+
await setup;
|
|
161
|
+
if (context !== void 0) yield context.dispose;
|
|
162
|
+
if (reviewer !== void 0) yield reviewer.dispose;
|
|
163
|
+
}, "optional Auto Review integration");
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
export { apply, inject, name };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { t as effectiveDirectory } from "./session-directory-BLOstut1.js";
|
|
2
|
-
import { r as canonicalPath$1 } from "./windows-runner-contract-
|
|
3
|
-
import { i as canonicalRoot, o as entryRootRef, p as resolveRootRef, r as availableRoots, t as RootValidationError } from "./roots-
|
|
2
|
+
import { r as canonicalPath$1 } from "./windows-runner-contract-B5R2K3mM.js";
|
|
3
|
+
import { i as canonicalRoot, o as entryRootRef, p as resolveRootRef, r as availableRoots, t as RootValidationError } from "./roots-Ca_W1S9n.js";
|
|
4
4
|
import { i as parsePanelCall, r as effectiveRootAccess, t as PANEL_CHANNEL } from "./contract-DZ4zczDT.js";
|
|
5
|
-
import "@deepseek-ai/cordis";
|
|
6
5
|
import { closeSync, lstatSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
7
6
|
import { isAbsolute, join } from "node:path";
|
|
7
|
+
import "@deepseek-ai/cordis";
|
|
8
8
|
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
9
9
|
//#region src/profile-isolation.ts
|
|
10
10
|
/**
|
package/lib/command.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as renderRootsReport, c as revealArgv, i as parseFoldersCommand, n as apply, o as resolvePanelPrimaryRoot, r as inject, s as revealAccepted, t as COMMAND_NAME } from "./command-
|
|
1
|
+
import { a as renderRootsReport, c as revealArgv, i as parseFoldersCommand, n as apply, o as resolvePanelPrimaryRoot, r as inject, s as revealAccepted, t as COMMAND_NAME } from "./command-Bwd6EGNV.js";
|
|
2
2
|
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealAccepted, revealArgv };
|