@dsh-electron/dsh-plugin-multi-root-workspace 0.1.7 → 0.1.8
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 +8 -1
- package/CHANGELOG.md +8 -1
- package/README.en.md +5 -5
- package/README.md +5 -5
- package/cordis.patch.yml +12 -4
- package/lib/{compat-DF_yL1bB.js → compat-DR2LvmYZ.js} +176 -29
- package/lib/compat.js +1 -1
- package/lib/{fs-9JIzGaf3.js → fs-Nlp2AiRX.js} +1 -5
- package/lib/fs.js +1 -1
- package/lib/index.js +4 -4
- package/lib/{sandbox-Kup4_ZIb.js → sandbox-CVAcT_gy.js} +2 -6
- package/lib/sandbox.js +1 -1
- package/lib/types/compat/dsh-version.d.ts +56 -8
- package/lib/types/compat/pty-marker.d.ts +1 -1
- package/lib/types/compat.d.ts +15 -5
- package/lib/types/index.d.ts +1 -1
- package/lib/windows-runner.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.en.md
CHANGED
|
@@ -8,6 +8,12 @@ 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.8] - 2026-10-18
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **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)).
|
|
16
|
+
|
|
11
17
|
## [0.1.7] - 2026-10-17
|
|
12
18
|
|
|
13
19
|
### Added
|
|
@@ -156,7 +162,8 @@ The first release: the three MVP milestones (M1–M3).
|
|
|
156
162
|
|
|
157
163
|
- 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
164
|
|
|
159
|
-
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.
|
|
165
|
+
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.8...HEAD
|
|
166
|
+
[0.1.8]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.7...v0.1.8
|
|
160
167
|
[0.1.7]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.6...v0.1.7
|
|
161
168
|
[0.1.6]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.5...v0.1.6
|
|
162
169
|
[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,12 @@ 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.8] - 2026-10-18
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **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))。
|
|
16
|
+
|
|
11
17
|
## [0.1.7] - 2026-10-17
|
|
12
18
|
|
|
13
19
|
### Added
|
|
@@ -156,7 +162,8 @@ English: [CHANGELOG.en.md](./CHANGELOG.en.md)
|
|
|
156
162
|
|
|
157
163
|
- 发布前外部评审提出的 8 项发现(其中 3 项发布阻断)全部修复,每项各带一个回归测试:登记目录被替换为符号链接后授权转移(引入 `recordedPath`,**重新解析路径不等于重新授权**)、并发修改注册表丢写或复活已撤销的授权(每个主根的变更串行)、刷新不重新检查目录、手输路径被目录选择器覆盖、`reveal` 的应答契约与失败码、重复 id 未校验、主根未纳入嵌套校验、CI 先测后构建。
|
|
158
164
|
|
|
159
|
-
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.
|
|
165
|
+
[Unreleased]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.8...HEAD
|
|
166
|
+
[0.1.8]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.7...v0.1.8
|
|
160
167
|
[0.1.7]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.6...v0.1.7
|
|
161
168
|
[0.1.6]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.5...v0.1.6
|
|
162
169
|
[0.1.5]: https://github.com/cherrchen/dsh-plugin-multi-root-workspace/compare/v0.1.4...v0.1.5
|
package/README.en.md
CHANGED
|
@@ -16,7 +16,7 @@ A real development project is usually several independent Git repositories, and
|
|
|
16
16
|
- **Security does not degrade**: additional roots are authorized through the same mechanism the harness already uses — 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. It never degenerates into full access or prompt-level constraints.
|
|
17
17
|
- **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
18
|
|
|
19
|
-
Current released version: `v0.1.
|
|
19
|
+
Current released version: `v0.1.8`. 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
20
|
|
|
21
21
|
## Quick Start
|
|
22
22
|
|
|
@@ -87,8 +87,8 @@ This installs pre-built artifacts; the first install needs the `allowBuilds` add
|
|
|
87
87
|
```sh
|
|
88
88
|
pnpm pack @dsh-electron/dsh-plugin-multi-root-workspace
|
|
89
89
|
# 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.
|
|
90
|
+
# https://github.com/cherrchen/dsh-plugin-multi-root-workspace/releases/download/v0.1.8/dsh-electron-dsh-plugin-multi-root-workspace-0.1.8.tgz
|
|
91
|
+
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.8.tgz
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
Also pre-built, handy for air-gapped or offline delivery; the first install needs the same `allowBuilds` addition.
|
|
@@ -107,10 +107,10 @@ allowBuilds:
|
|
|
107
107
|
koffi: true
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Pinning a version (e.g. `#v0.1.
|
|
110
|
+
Pinning a version (e.g. `#v0.1.8`) is recommended so a later push cannot silently change what actually runs:
|
|
111
111
|
|
|
112
112
|
```sh
|
|
113
|
-
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.
|
|
113
|
+
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.8
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
### Install from a local clone
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ DSH(DeepSeek Harness)原生只把会话启动的目录当作 Agent 唯一的
|
|
|
16
16
|
- **安全不降级**:附加根走上游同款授权机制——进程内文件围栏 + 内核级沙箱(macOS Seatbelt / Linux bwrap / Landlock / Windows ACL),文件与 bash 共享同一份授权范围,绝不退化成全开放或只靠提示词约束。
|
|
17
17
|
- **不配等于没装**:未登记附加根时行为与未装插件逐项一致;配置错误一律明确报错,从不静默降级。
|
|
18
18
|
|
|
19
|
-
当前发布版本 `v0.1.
|
|
19
|
+
当前发布版本 `v0.1.8`。各版本的用户可见变更见 [CHANGELOG](./CHANGELOG.md);进度与发布状态见[路线图 §进度总账](./docs/plans/active/2026-09-12-multi-root-workspace.md#进度总账)。
|
|
20
20
|
|
|
21
21
|
## 快速开始
|
|
22
22
|
|
|
@@ -89,8 +89,8 @@ dsh plugin --profile web add @dsh-electron/dsh-plugin-multi-root-workspace
|
|
|
89
89
|
```sh
|
|
90
90
|
pnpm pack @dsh-electron/dsh-plugin-multi-root-workspace
|
|
91
91
|
# 或从 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.
|
|
92
|
+
# https://github.com/cherrchen/dsh-plugin-multi-root-workspace/releases/download/v0.1.8/dsh-electron-dsh-plugin-multi-root-workspace-0.1.8.tgz
|
|
93
|
+
dsh plugin --profile web add ./dsh-electron-dsh-plugin-multi-root-workspace-0.1.8.tgz
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
同样是预构建产物,适合内网或离线环境交付;首次安装同样需要添加 `allowBuilds`。
|
|
@@ -109,10 +109,10 @@ allowBuilds:
|
|
|
109
109
|
koffi: true
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
建议锁定版本(如 `#v0.1.
|
|
112
|
+
建议锁定版本(如 `#v0.1.8`),让后续推送无法悄悄改变实际运行的内容:
|
|
113
113
|
|
|
114
114
|
```sh
|
|
115
|
-
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.
|
|
115
|
+
dsh plugin --profile web add github:cherrchen/dsh-plugin-multi-root-workspace#v0.1.8
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
### 从本地源码安装
|
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
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { dirname, join, sep } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
//#region src/compat/dsh-version.ts
|
|
6
7
|
/**
|
|
7
8
|
* The DSH compatibility contract: which upstream releases this plugin is
|
|
@@ -86,39 +87,175 @@ const OPTIONAL_CORE_PACKAGES = [
|
|
|
86
87
|
"@deepseek-ai/dsh-subprocess",
|
|
87
88
|
"@deepseek-ai/dsh-system-prompt"
|
|
88
89
|
];
|
|
90
|
+
/**
|
|
91
|
+
* The optional packages this plugin loads itself at run time, through dynamic
|
|
92
|
+
* `import()`s inside this package's own modules. The copy this module resolves
|
|
93
|
+
* IS the copy that would execute, so every resolvable hit testifies — a
|
|
94
|
+
* private copy inside this package's own tree genuinely loads, and a version
|
|
95
|
+
* that disagrees with the host is a real mixed installation, not a phantom.
|
|
96
|
+
*/
|
|
97
|
+
const RUNTIME_OPTIONAL_PACKAGES = [
|
|
98
|
+
"@deepseek-ai/dsh-agent-instructions",
|
|
99
|
+
"@deepseek-ai/dsh-llm",
|
|
100
|
+
"@deepseek-ai/dsh-session",
|
|
101
|
+
"@deepseek-ai/dsh-typert-protocol"
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* The optional packages this plugin consumes only as mounted host services or
|
|
105
|
+
* ambient types — it never imports them, so a copy inside this package's own
|
|
106
|
+
* tree can never execute. The only version that testifies is the one the
|
|
107
|
+
* COMPOSITION resolves (the installation or the profile): a resolution that
|
|
108
|
+
* lands inside this package's own root is a private development or bundled
|
|
109
|
+
* copy and must be reported absent — otherwise a `link:`-installed checkout
|
|
110
|
+
* whose devDependencies carry a newer release phantom-mixes every supported
|
|
111
|
+
* host it is developed against (the report that motivated this split:
|
|
112
|
+
* `dsh-lsp`/`dsh-lsp-stdio`/`dsh-working-directory` resolving to the plugin's
|
|
113
|
+
* devDependencies on a `0.2.0-rc.2` host produced a `mixed` verdict on a
|
|
114
|
+
* uniformly `0.2.0-rc.2` installation).
|
|
115
|
+
*/
|
|
116
|
+
const HOST_SERVICE_PACKAGES = [
|
|
117
|
+
"@deepseek-ai/dsh-agent",
|
|
118
|
+
"@deepseek-ai/dsh-api-workspace-files",
|
|
119
|
+
"@deepseek-ai/dsh-client-connection",
|
|
120
|
+
"@deepseek-ai/dsh-commands",
|
|
121
|
+
"@deepseek-ai/dsh-lsp",
|
|
122
|
+
"@deepseek-ai/dsh-lsp-stdio",
|
|
123
|
+
"@deepseek-ai/dsh-subprocess",
|
|
124
|
+
"@deepseek-ai/dsh-system-prompt",
|
|
125
|
+
"@deepseek-ai/dsh-tools",
|
|
126
|
+
"@deepseek-ai/dsh-working-directory"
|
|
127
|
+
];
|
|
128
|
+
/**
|
|
129
|
+
* Optional packages this plugin executes that are resolved through an anchor
|
|
130
|
+
* OTHER than this module. The standalone Windows ACL runner
|
|
131
|
+
* (`src/windows-runner.ts`) imports `@deepseek-ai/dsh-sandbox-windows-acl` by
|
|
132
|
+
* self-reference from the upstream runner's own install location — a file the
|
|
133
|
+
* installed `dsh-sandbox-local` points at in its confined argv — so the copy
|
|
134
|
+
* that testifies is the one sibling to that required package, not whatever
|
|
135
|
+
* this module can reach. Neither the self-resolution hit nor the
|
|
136
|
+
* `@deepseek-ai/dsh` host anchor reflects execution here: if sandbox-local's
|
|
137
|
+
* own anchor cannot see the package, the runner cannot load it either, and
|
|
138
|
+
* absent is the honest answer.
|
|
139
|
+
*/
|
|
140
|
+
const RUNNER_ANCHORED_PACKAGES = ["@deepseek-ai/dsh-sandbox-windows-acl"];
|
|
141
|
+
/** The required package whose tree produces the upstream runner argv. */
|
|
142
|
+
const RUNNER_ANCHOR_PACKAGE = "@deepseek-ai/dsh-sandbox-local";
|
|
89
143
|
/** Every package the contract inspects, required first. */
|
|
90
144
|
const CORE_PACKAGES = [...REQUIRED_CORE_PACKAGES, ...OPTIONAL_CORE_PACKAGES];
|
|
91
|
-
/** Services looked up structurally; older runtimes never install these modules. */
|
|
92
|
-
const HOST_RESOLVED_PACKAGES = ["@deepseek-ai/dsh-working-directory"];
|
|
93
145
|
/** The environment variable that relaxes the gate. */
|
|
94
146
|
const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
95
147
|
/**
|
|
148
|
+
* Resolve one package's `package.json` path through a require, or `undefined`
|
|
149
|
+
* when it cannot be resolved. The path — not just the version — matters: the
|
|
150
|
+
* caller needs to know WHERE the copy lives to judge whether it belongs to the
|
|
151
|
+
* inspected installation.
|
|
152
|
+
* @param require - the require to resolve through.
|
|
153
|
+
* @param packageName - the package to locate.
|
|
154
|
+
* @returns the manifest path, or `undefined`.
|
|
155
|
+
*/
|
|
156
|
+
function resolvePackageJson(require, packageName) {
|
|
157
|
+
try {
|
|
158
|
+
return require.resolve(`${packageName}/package.json`);
|
|
159
|
+
} catch {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Read `version` off a resolved manifest path; a broken manifest is absent. */
|
|
164
|
+
function versionOfManifest(manifestPath) {
|
|
165
|
+
try {
|
|
166
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
167
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
168
|
+
} catch {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The second resolution anchor: a require rooted at the installed
|
|
174
|
+
* `@deepseek-ai/dsh` meta package. Host-internal packages that never appear as
|
|
175
|
+
* plugin peers are only reachable this way (the pattern the retired
|
|
176
|
+
* `HOST_RESOLVED_PACKAGES` list carved out for `dsh-working-directory`,
|
|
177
|
+
* generalized here to every host-service package).
|
|
178
|
+
* @param require - this module's require, used to locate the meta package.
|
|
179
|
+
* @returns the host-rooted require, or `undefined` when `dsh` itself cannot be
|
|
180
|
+
* resolved.
|
|
181
|
+
*/
|
|
182
|
+
function hostAnchorRequire(require) {
|
|
183
|
+
const anchor = resolvePackageJson(require, "@deepseek-ai/dsh");
|
|
184
|
+
return anchor === void 0 ? void 0 : createRequire(anchor);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* This package's own install root — the boundary between a copy that belongs
|
|
188
|
+
* to the inspected installation and a private copy only this package can see.
|
|
189
|
+
* @param require - this module's require; self-reference through the package's
|
|
190
|
+
* `exports` is tried first, then a walk up from this module's file.
|
|
191
|
+
* @returns the package root, or `undefined` when neither anchor works.
|
|
192
|
+
*/
|
|
193
|
+
function pluginPackageRoot(require) {
|
|
194
|
+
try {
|
|
195
|
+
return dirname(require.resolve("@dsh-electron/dsh-plugin-multi-root-workspace/package.json"));
|
|
196
|
+
} catch {
|
|
197
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
198
|
+
while (true) {
|
|
199
|
+
if (existsSync(join(dir, "package.json"))) return dir;
|
|
200
|
+
const parent = dirname(dir);
|
|
201
|
+
if (parent === dir) return void 0;
|
|
202
|
+
dir = parent;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Whether a resolved manifest path lives inside this package's own root — the
|
|
208
|
+
* mark of a private copy no composition can mount. An unknown root cannot
|
|
209
|
+
* exclude anything, so it answers `false` rather than guessing.
|
|
210
|
+
*/
|
|
211
|
+
function insidePackageRoot(manifestPath, root) {
|
|
212
|
+
return root !== void 0 && (manifestPath === root || manifestPath.startsWith(`${root}${sep}`));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
96
215
|
* Read one package's declared version through Node resolution.
|
|
97
216
|
*
|
|
98
|
-
* Resolution runs from THIS module
|
|
99
|
-
* the
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
217
|
+
* Resolution runs from THIS module — for required and runtime-loaded packages
|
|
218
|
+
* the copy found here is the copy that would execute, which is what makes the
|
|
219
|
+
* answer meaningful: the upstream packages are peer dependencies, so they
|
|
220
|
+
* resolve to the single copy the host provides — the same copy the providers
|
|
221
|
+
* will subclass at runtime.
|
|
222
|
+
*
|
|
223
|
+
* {@link HOST_SERVICE_PACKAGES} are different: the plugin never imports them,
|
|
224
|
+
* so a copy inside this package's own tree can never execute, and counting it
|
|
225
|
+
* would report a mixed installation out of a linked checkout's devDependencies
|
|
226
|
+
* alone. Only a hit OUTSIDE this package's root — a copy the composition could
|
|
227
|
+
* mount — testifies; when this module cannot reach the package at all, the
|
|
228
|
+
* `@deepseek-ai/dsh` host manifest supplies a second anchor for host-internal
|
|
229
|
+
* packages that are never plugin peers.
|
|
230
|
+
*
|
|
231
|
+
* {@link RUNNER_ANCHORED_PACKAGES} are a third policy: executed, but resolved
|
|
232
|
+
* from the upstream-runner anchor rather than this module, so only a hit
|
|
233
|
+
* sibling to the installed `dsh-sandbox-local` testifies.
|
|
234
|
+
*
|
|
235
|
+
* A package that cannot be resolved in either place is reported absent rather
|
|
236
|
+
* than throwing, because "not installed" is a legitimate answer for every
|
|
237
|
+
* optional package.
|
|
104
238
|
* @param packageName - the package to inspect.
|
|
105
239
|
* @returns the declared version, or `undefined` when the package or its
|
|
106
240
|
* manifest cannot be resolved.
|
|
107
241
|
*/
|
|
108
242
|
function readInstalledVersion(packageName) {
|
|
109
243
|
const require = createRequire(import.meta.url);
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} catch {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
244
|
+
if (RUNNER_ANCHORED_PACKAGES.includes(packageName)) {
|
|
245
|
+
const anchor = resolvePackageJson(require, RUNNER_ANCHOR_PACKAGE);
|
|
246
|
+
const hit = anchor === void 0 ? void 0 : resolvePackageJson(createRequire(anchor), packageName);
|
|
247
|
+
return hit === void 0 ? void 0 : versionOfManifest(hit);
|
|
248
|
+
}
|
|
249
|
+
if (!HOST_SERVICE_PACKAGES.includes(packageName)) {
|
|
250
|
+
const hit = resolvePackageJson(require, packageName);
|
|
251
|
+
return hit === void 0 ? void 0 : versionOfManifest(hit);
|
|
121
252
|
}
|
|
253
|
+
const root = pluginPackageRoot(require);
|
|
254
|
+
const selfHit = resolvePackageJson(require, packageName);
|
|
255
|
+
if (selfHit !== void 0 && !insidePackageRoot(selfHit, root)) return versionOfManifest(selfHit);
|
|
256
|
+
const host = hostAnchorRequire(require);
|
|
257
|
+
const hostHit = host === void 0 ? void 0 : resolvePackageJson(host, packageName);
|
|
258
|
+
return hostHit === void 0 || insidePackageRoot(hostHit, root) ? void 0 : versionOfManifest(hostHit);
|
|
122
259
|
}
|
|
123
260
|
/**
|
|
124
261
|
* Judge one installation against the contract.
|
|
@@ -223,7 +360,7 @@ function report(verdict, release, packages, lines) {
|
|
|
223
360
|
* Resolution follows the optional-peer precedent (`agent-instructions.ts`):
|
|
224
361
|
* the backend is not a peer of this plugin, so the plugin anchor is tried
|
|
225
362
|
* first (flat layouts) and the `@deepseek-ai/dsh` host anchor second — the
|
|
226
|
-
* same
|
|
363
|
+
* same host-anchor fallback pattern as `dsh-version.ts` — because the
|
|
227
364
|
* backend rides inside the harness meta package's own dependency tree.
|
|
228
365
|
*
|
|
229
366
|
* @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/pty-marker
|
|
@@ -256,7 +393,7 @@ var PtyMarkerContractError = class extends Error {
|
|
|
256
393
|
* reach: this module first (flat layouts where the backend is a direct
|
|
257
394
|
* install), then the `@deepseek-ai/dsh` host manifest (pnpm-style layouts
|
|
258
395
|
* where it is the meta package's own dependency — the
|
|
259
|
-
* `
|
|
396
|
+
* host-anchor fallback pattern in `dsh-version.ts`). A resolution failure that is not a
|
|
260
397
|
* genuine NOT_FOUND propagates: a resolvable-but-broken specifier is not
|
|
261
398
|
* evidence of absence.
|
|
262
399
|
*/
|
|
@@ -354,16 +491,26 @@ function assertAgentPtyMarker(io = defaultIo) {
|
|
|
354
491
|
* `multi-root-compat`: the startup gate that turns the DSH compatibility
|
|
355
492
|
* contract from documentation into a precondition.
|
|
356
493
|
*
|
|
357
|
-
*
|
|
358
|
-
* `multi-root-
|
|
359
|
-
*
|
|
360
|
-
*
|
|
494
|
+
* Every widened-authority row of this bundle — `multi-root-providers`,
|
|
495
|
+
* `multi-root-registry`, `multi-root-instructions`, `multi-root-lsp`,
|
|
496
|
+
* `multi-root-workspace-files`, `multi-root-process-fence` — injects
|
|
497
|
+
* `multiRootCompat`. Cordis will not start a plugin whose injected service is
|
|
498
|
+
* absent, so this row is what decides whether they run at all:
|
|
361
499
|
*
|
|
362
500
|
* ```text
|
|
363
|
-
* compat check passes → ctx.multiRootCompat exists → the
|
|
501
|
+
* compat check passes → ctx.multiRootCompat exists → the gated rows start
|
|
364
502
|
* compat check fails → the row throws → they never start
|
|
365
503
|
* ```
|
|
366
504
|
*
|
|
505
|
+
* The two PROVIDER rows (`multi-root-fs`, `multi-root-sandbox`) deliberately
|
|
506
|
+
* do NOT inject the gate: their widening is scope-driven, and the gated
|
|
507
|
+
* registry can never populate the scope on a refused installation — so they
|
|
508
|
+
* degenerate to the upstream providers. That is what keeps a refused gate a
|
|
509
|
+
* loud degradation (the plugin's features absent, the profile intact) instead
|
|
510
|
+
* of a dead profile: upstream `fs-sandbox`/`sandbox` are already disabled by
|
|
511
|
+
* the patch, so holding `ctx.fs`/`ctx.sandbox` back would take `ctx.shell`,
|
|
512
|
+
* `permissionPresets` and every confining tool down with them.
|
|
513
|
+
*
|
|
367
514
|
* That is why the gate is a plain `apply` and not a `Service` subclass mounted
|
|
368
515
|
* directly: the check has to be able to fail BEFORE the service exists. A
|
|
369
516
|
* `Service` constructor has already run `super(ctx, name)` — and therefore
|
|
@@ -450,4 +597,4 @@ function apply(ctx) {
|
|
|
450
597
|
ctx.plugin(MultiRootCompatService, report);
|
|
451
598
|
}
|
|
452
599
|
//#endregion
|
|
453
|
-
export { inject as a, agentPtyMarkerVerdict as c,
|
|
600
|
+
export { classifyInstallation as _, inject as a, agentPtyMarkerVerdict as c, HOST_SERVICE_PACKAGES as d, OPTIONAL_CORE_PACKAGES as f, SUPPORTED_DSH_RELEASES as g, RUNTIME_OPTIONAL_PACKAGES as h, assertSupportedInstallation as i, assertAgentPtyMarker as l, RUNNER_ANCHORED_PACKAGES as m, MultiRootCompatService as n, AGENT_PTY_MARKER as o, REQUIRED_CORE_PACKAGES as p, apply as r, PtyMarkerContractError as s, DshCompatUnsupportedError as t, CORE_PACKAGES as u, compatEnforcement as v, readInstalledVersion as y };
|
package/lib/compat.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as inject, i as assertSupportedInstallation, n as MultiRootCompatService, r as apply, t as DshCompatUnsupportedError } from "./compat-
|
|
1
|
+
import { a as inject, i as assertSupportedInstallation, n as MultiRootCompatService, r as apply, t as DshCompatUnsupportedError } from "./compat-DR2LvmYZ.js";
|
|
2
2
|
export { DshCompatUnsupportedError, MultiRootCompatService, apply, assertSupportedInstallation, inject };
|
|
@@ -65,11 +65,7 @@ async function isPathUnder(path, root, caseSensitive = process.platform !== "win
|
|
|
65
65
|
* roots.
|
|
66
66
|
*/
|
|
67
67
|
var MultiRootFileSystem = class extends LocalFileSystem {
|
|
68
|
-
static inject = [
|
|
69
|
-
"multiRootCompat",
|
|
70
|
-
"sandboxPolicy",
|
|
71
|
-
"multiRootScope"
|
|
72
|
-
];
|
|
68
|
+
static inject = ["sandboxPolicy", "multiRootScope"];
|
|
73
69
|
defaultMode;
|
|
74
70
|
constructor(ctx, config) {
|
|
75
71
|
super(ctx, config);
|
package/lib/fs.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as agentPtyMarkerVerdict, d as
|
|
1
|
+
import { _ as classifyInstallation, c as agentPtyMarkerVerdict, d as HOST_SERVICE_PACKAGES, f as OPTIONAL_CORE_PACKAGES, g as SUPPORTED_DSH_RELEASES, h as RUNTIME_OPTIONAL_PACKAGES, i as assertSupportedInstallation, l as assertAgentPtyMarker, m as RUNNER_ANCHORED_PACKAGES, n as MultiRootCompatService, o as AGENT_PTY_MARKER, p as REQUIRED_CORE_PACKAGES, s as PtyMarkerContractError, t as DshCompatUnsupportedError, u as CORE_PACKAGES, v as compatEnforcement, y as readInstalledVersion } from "./compat-DR2LvmYZ.js";
|
|
2
2
|
import { a as composeInstructionMessage, c as createInstructionMessage, l as instructionsApi, r as PLUGIN_SOURCE, t as DEFAULT_MAX_BYTES, u as renderInstructions } from "./instructions-D19jRxN0.js";
|
|
3
|
-
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-
|
|
3
|
+
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-CVAcT_gy.js";
|
|
4
4
|
import { a as classifyStoredRoots, c as indexedStatuses, i as canonicalRoot, l as isCanonicallyUnder, m as validateRootCandidate, n as additionalRootId, p as resolveRootRef, r as availableRoots, s as expandRootInput, t as RootValidationError, u as removeStatusAt } from "./roots-B-0l8mNG.js";
|
|
5
5
|
import { MULTI_ROOT_PROVIDER_BRAND, MultiRootProviders, brandMultiRootProvider, isMultiRootProvider } from "./providers.js";
|
|
6
|
-
import { t as MultiRootFileSystem } from "./fs-
|
|
6
|
+
import { t as MultiRootFileSystem } from "./fs-Nlp2AiRX.js";
|
|
7
7
|
import { a as parseRevealedView, i as parsePanelCall, n as PANEL_ENDPOINTS, o as parseRootsView, t as PANEL_CHANNEL } from "./contract-DZ4zczDT.js";
|
|
8
8
|
import { MultiRootScopeService, renderWorkspaceRootsContext, sanitizeAdditionalRoots } from "./scope.js";
|
|
9
9
|
import { a as MultiRootRegistry, c as multiRootDomainSpec, i as MAX_ALIAS_LENGTH, l as RegistryAuthorityLease, n as DOMAIN_NAME, o as REGISTRY_CONTENDED_MESSAGE, r as MAX_ADDITIONAL_ROOTS_CEILING, t as DEFAULT_MAX_ADDITIONAL_ROOTS, u as RegistryLeaseContendedError } from "./registry-DCxC5Lay.js";
|
|
@@ -18,4 +18,4 @@ import { a as renderRootsReport, c as revealArgv, i as parseFoldersCommand, o as
|
|
|
18
18
|
const inject = [];
|
|
19
19
|
function apply() {}
|
|
20
20
|
//#endregion
|
|
21
|
-
export { AGENT_PTY_MARKER, COMMAND_NAME, CORE_PACKAGES, DEFAULT_MAX_ADDITIONAL_ROOTS, DEFAULT_MAX_BYTES, DOMAIN_NAME, DshCompatUnsupportedError, MAX_ADDITIONAL_ROOTS_CEILING, MAX_ALIAS_LENGTH, MULTI_ROOT_PROVIDER_BRAND, MultiRootCompatService, MultiRootFileSystem, MultiRootProviders, MultiRootRegistry, MultiRootSandboxProvider, MultiRootScopeService, OPTIONAL_CORE_PACKAGES, PANEL_CHANNEL, PANEL_ENDPOINTS, PLUGIN_SOURCE, PtyMarkerContractError, REGISTRY_CONTENDED_MESSAGE, REQUIRED_CORE_PACKAGES, RegistryAuthorityLease, RegistryLeaseContendedError, RootValidationError, SUPPORTED_DSH_RELEASES, additionalRootId, agentPtyMarkerVerdict, apply, assertAgentPtyMarker, assertSupportedInstallation, availableRoots, brandMultiRootProvider, canonicalRoot, classifyInstallation, classifyStoredRoots, compatEnforcement, composeInstructionMessage, createInstructionMessage, expandRootInput, indexedStatuses, inject, instructionsApi, isCanonicallyUnder, isMultiRootProvider, multiRootDomainSpec, parseFoldersCommand, parsePanelCall, parseRevealedView, parseRootsView, readInstalledVersion, removeStatusAt, renderInstructions, renderRootsReport, renderWorkspaceRootsContext, resolvePanelPrimaryRoot, resolveRootRef, revealArgv, sanitizeAdditionalRoots, validateRootCandidate, widenConfined };
|
|
21
|
+
export { AGENT_PTY_MARKER, COMMAND_NAME, CORE_PACKAGES, DEFAULT_MAX_ADDITIONAL_ROOTS, DEFAULT_MAX_BYTES, DOMAIN_NAME, DshCompatUnsupportedError, HOST_SERVICE_PACKAGES, MAX_ADDITIONAL_ROOTS_CEILING, MAX_ALIAS_LENGTH, MULTI_ROOT_PROVIDER_BRAND, MultiRootCompatService, MultiRootFileSystem, MultiRootProviders, MultiRootRegistry, MultiRootSandboxProvider, MultiRootScopeService, OPTIONAL_CORE_PACKAGES, PANEL_CHANNEL, PANEL_ENDPOINTS, PLUGIN_SOURCE, PtyMarkerContractError, REGISTRY_CONTENDED_MESSAGE, REQUIRED_CORE_PACKAGES, RUNNER_ANCHORED_PACKAGES, RUNTIME_OPTIONAL_PACKAGES, RegistryAuthorityLease, RegistryLeaseContendedError, RootValidationError, SUPPORTED_DSH_RELEASES, additionalRootId, agentPtyMarkerVerdict, apply, assertAgentPtyMarker, assertSupportedInstallation, availableRoots, brandMultiRootProvider, canonicalRoot, classifyInstallation, classifyStoredRoots, compatEnforcement, composeInstructionMessage, createInstructionMessage, expandRootInput, indexedStatuses, inject, instructionsApi, isCanonicallyUnder, isMultiRootProvider, multiRootDomainSpec, parseFoldersCommand, parsePanelCall, parseRevealedView, parseRootsView, readInstalledVersion, removeStatusAt, renderInstructions, renderRootsReport, renderWorkspaceRootsContext, resolvePanelPrimaryRoot, resolveRootRef, revealArgv, sanitizeAdditionalRoots, validateRootCandidate, widenConfined };
|
|
@@ -2,9 +2,9 @@ import { a as parseWindowsProfile, i as isWindowsNetworkOrDevicePath, n as asser
|
|
|
2
2
|
import { l as isCanonicallyUnder } from "./roots-B-0l8mNG.js";
|
|
3
3
|
import { brandMultiRootProvider } from "./providers.js";
|
|
4
4
|
import { posix } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { SandboxUnavailableError, canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
6
7
|
import { LocalSandboxProvider } from "@deepseek-ai/dsh-sandbox-local";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
8
|
//#region src/compat/sandbox-confine.ts
|
|
9
9
|
/** Whether a value is a thenable, i.e. whether the installed `confine` is async. */
|
|
10
10
|
function isThenable(value) {
|
|
@@ -557,11 +557,7 @@ function windowsContained(candidate, root) {
|
|
|
557
557
|
* grant mechanics stay the upstream implementation's.
|
|
558
558
|
*/
|
|
559
559
|
var MultiRootSandboxProvider = class extends LocalSandboxProvider {
|
|
560
|
-
static inject = [
|
|
561
|
-
"multiRootCompat",
|
|
562
|
-
"sandboxPolicy",
|
|
563
|
-
"multiRootScope"
|
|
564
|
-
];
|
|
560
|
+
static inject = ["sandboxPolicy", "multiRootScope"];
|
|
565
561
|
constructor(ctx, config) {
|
|
566
562
|
super(ctx, config);
|
|
567
563
|
brandMultiRootProvider(this, "sandbox");
|
package/lib/sandbox.js
CHANGED
|
@@ -43,10 +43,43 @@ export declare const REQUIRED_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@
|
|
|
43
43
|
* that is the mixed-installation failure this contract exists to catch.
|
|
44
44
|
*/
|
|
45
45
|
export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
46
|
+
/**
|
|
47
|
+
* The optional packages this plugin loads itself at run time, through dynamic
|
|
48
|
+
* `import()`s inside this package's own modules. The copy this module resolves
|
|
49
|
+
* IS the copy that would execute, so every resolvable hit testifies — a
|
|
50
|
+
* private copy inside this package's own tree genuinely loads, and a version
|
|
51
|
+
* that disagrees with the host is a real mixed installation, not a phantom.
|
|
52
|
+
*/
|
|
53
|
+
export declare const RUNTIME_OPTIONAL_PACKAGES: readonly ["@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-typert-protocol"];
|
|
54
|
+
/**
|
|
55
|
+
* The optional packages this plugin consumes only as mounted host services or
|
|
56
|
+
* ambient types — it never imports them, so a copy inside this package's own
|
|
57
|
+
* tree can never execute. The only version that testifies is the one the
|
|
58
|
+
* COMPOSITION resolves (the installation or the profile): a resolution that
|
|
59
|
+
* lands inside this package's own root is a private development or bundled
|
|
60
|
+
* copy and must be reported absent — otherwise a `link:`-installed checkout
|
|
61
|
+
* whose devDependencies carry a newer release phantom-mixes every supported
|
|
62
|
+
* host it is developed against (the report that motivated this split:
|
|
63
|
+
* `dsh-lsp`/`dsh-lsp-stdio`/`dsh-working-directory` resolving to the plugin's
|
|
64
|
+
* devDependencies on a `0.2.0-rc.2` host produced a `mixed` verdict on a
|
|
65
|
+
* uniformly `0.2.0-rc.2` installation).
|
|
66
|
+
*/
|
|
67
|
+
export declare const HOST_SERVICE_PACKAGES: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory"];
|
|
68
|
+
/**
|
|
69
|
+
* Optional packages this plugin executes that are resolved through an anchor
|
|
70
|
+
* OTHER than this module. The standalone Windows ACL runner
|
|
71
|
+
* (`src/windows-runner.ts`) imports `@deepseek-ai/dsh-sandbox-windows-acl` by
|
|
72
|
+
* self-reference from the upstream runner's own install location — a file the
|
|
73
|
+
* installed `dsh-sandbox-local` points at in its confined argv — so the copy
|
|
74
|
+
* that testifies is the one sibling to that required package, not whatever
|
|
75
|
+
* this module can reach. Neither the self-resolution hit nor the
|
|
76
|
+
* `@deepseek-ai/dsh` host anchor reflects execution here: if sandbox-local's
|
|
77
|
+
* own anchor cannot see the package, the runner cannot load it either, and
|
|
78
|
+
* absent is the honest answer.
|
|
79
|
+
*/
|
|
80
|
+
export declare const RUNNER_ANCHORED_PACKAGES: readonly ["@deepseek-ai/dsh-sandbox-windows-acl"];
|
|
46
81
|
/** Every package the contract inspects, required first. */
|
|
47
82
|
export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
48
|
-
/** Services looked up structurally; older runtimes never install these modules. */
|
|
49
|
-
export declare const HOST_RESOLVED_PACKAGES: readonly ["@deepseek-ai/dsh-working-directory"];
|
|
50
83
|
/** How this plugin reacts to an installation it does not support. */
|
|
51
84
|
export type CompatEnforcement = 'enforce' | 'warn';
|
|
52
85
|
/** The environment variable that relaxes the gate. */
|
|
@@ -85,12 +118,27 @@ export type VersionReader = (packageName: string) => string | undefined;
|
|
|
85
118
|
/**
|
|
86
119
|
* Read one package's declared version through Node resolution.
|
|
87
120
|
*
|
|
88
|
-
* Resolution runs from THIS module
|
|
89
|
-
* the
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
121
|
+
* Resolution runs from THIS module — for required and runtime-loaded packages
|
|
122
|
+
* the copy found here is the copy that would execute, which is what makes the
|
|
123
|
+
* answer meaningful: the upstream packages are peer dependencies, so they
|
|
124
|
+
* resolve to the single copy the host provides — the same copy the providers
|
|
125
|
+
* will subclass at runtime.
|
|
126
|
+
*
|
|
127
|
+
* {@link HOST_SERVICE_PACKAGES} are different: the plugin never imports them,
|
|
128
|
+
* so a copy inside this package's own tree can never execute, and counting it
|
|
129
|
+
* would report a mixed installation out of a linked checkout's devDependencies
|
|
130
|
+
* alone. Only a hit OUTSIDE this package's root — a copy the composition could
|
|
131
|
+
* mount — testifies; when this module cannot reach the package at all, the
|
|
132
|
+
* `@deepseek-ai/dsh` host manifest supplies a second anchor for host-internal
|
|
133
|
+
* packages that are never plugin peers.
|
|
134
|
+
*
|
|
135
|
+
* {@link RUNNER_ANCHORED_PACKAGES} are a third policy: executed, but resolved
|
|
136
|
+
* from the upstream-runner anchor rather than this module, so only a hit
|
|
137
|
+
* sibling to the installed `dsh-sandbox-local` testifies.
|
|
138
|
+
*
|
|
139
|
+
* A package that cannot be resolved in either place is reported absent rather
|
|
140
|
+
* than throwing, because "not installed" is a legitimate answer for every
|
|
141
|
+
* optional package.
|
|
94
142
|
* @param packageName - the package to inspect.
|
|
95
143
|
* @returns the declared version, or `undefined` when the package or its
|
|
96
144
|
* manifest cannot be resolved.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Resolution follows the optional-peer precedent (`agent-instructions.ts`):
|
|
22
22
|
* the backend is not a peer of this plugin, so the plugin anchor is tried
|
|
23
23
|
* first (flat layouts) and the `@deepseek-ai/dsh` host anchor second — the
|
|
24
|
-
* same
|
|
24
|
+
* same host-anchor fallback pattern as `dsh-version.ts` — because the
|
|
25
25
|
* backend rides inside the harness meta package's own dependency tree.
|
|
26
26
|
*
|
|
27
27
|
* @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/pty-marker
|
package/lib/types/compat.d.ts
CHANGED
|
@@ -2,16 +2,26 @@
|
|
|
2
2
|
* `multi-root-compat`: the startup gate that turns the DSH compatibility
|
|
3
3
|
* contract from documentation into a precondition.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* `multi-root-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Every widened-authority row of this bundle — `multi-root-providers`,
|
|
6
|
+
* `multi-root-registry`, `multi-root-instructions`, `multi-root-lsp`,
|
|
7
|
+
* `multi-root-workspace-files`, `multi-root-process-fence` — injects
|
|
8
|
+
* `multiRootCompat`. Cordis will not start a plugin whose injected service is
|
|
9
|
+
* absent, so this row is what decides whether they run at all:
|
|
9
10
|
*
|
|
10
11
|
* ```text
|
|
11
|
-
* compat check passes → ctx.multiRootCompat exists → the
|
|
12
|
+
* compat check passes → ctx.multiRootCompat exists → the gated rows start
|
|
12
13
|
* compat check fails → the row throws → they never start
|
|
13
14
|
* ```
|
|
14
15
|
*
|
|
16
|
+
* The two PROVIDER rows (`multi-root-fs`, `multi-root-sandbox`) deliberately
|
|
17
|
+
* do NOT inject the gate: their widening is scope-driven, and the gated
|
|
18
|
+
* registry can never populate the scope on a refused installation — so they
|
|
19
|
+
* degenerate to the upstream providers. That is what keeps a refused gate a
|
|
20
|
+
* loud degradation (the plugin's features absent, the profile intact) instead
|
|
21
|
+
* of a dead profile: upstream `fs-sandbox`/`sandbox` are already disabled by
|
|
22
|
+
* the patch, so holding `ctx.fs`/`ctx.sandbox` back would take `ctx.shell`,
|
|
23
|
+
* `permissionPresets` and every confining tool down with them.
|
|
24
|
+
*
|
|
15
25
|
* That is why the gate is a plain `apply` and not a `Service` subclass mounted
|
|
16
26
|
* directly: the check has to be able to fail BEFORE the service exists. A
|
|
17
27
|
* `Service` constructor has already run `super(ctx, name)` — and therefore
|
package/lib/types/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* @module @dsh-electron/dsh-plugin-multi-root-workspace
|
|
22
22
|
*/
|
|
23
23
|
export { DshCompatUnsupportedError, MultiRootCompatService, assertSupportedInstallation } from './compat.ts';
|
|
24
|
-
export { classifyInstallation, compatEnforcement, CORE_PACKAGES, OPTIONAL_CORE_PACKAGES, readInstalledVersion, REQUIRED_CORE_PACKAGES, SUPPORTED_DSH_RELEASES, } from './compat/dsh-version.ts';
|
|
24
|
+
export { classifyInstallation, compatEnforcement, CORE_PACKAGES, HOST_SERVICE_PACKAGES, OPTIONAL_CORE_PACKAGES, readInstalledVersion, REQUIRED_CORE_PACKAGES, RUNNER_ANCHORED_PACKAGES, RUNTIME_OPTIONAL_PACKAGES, SUPPORTED_DSH_RELEASES, } from './compat/dsh-version.ts';
|
|
25
25
|
export type { CompatEnforcement, CompatReport, CompatVerdict, InspectedPackage, SupportedDshRelease, VersionReader, } from './compat/dsh-version.ts';
|
|
26
26
|
export { instructionsApi, renderInstructions } from './compat/agent-instructions.ts';
|
|
27
27
|
export type { InstructionsApi, RenderedInstructions } from './compat/agent-instructions.ts';
|
package/lib/windows-runner.js
CHANGED
|
@@ -2,8 +2,8 @@ import { a as parseWindowsProfile, i as isWindowsNetworkOrDevicePath, n as asser
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { closeSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, win32 } from "node:path";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
5
|
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
7
|
import { createConnection } from "node:net";
|
|
8
8
|
//#region src/compat/windows-native.ts
|
|
9
9
|
/** Public Win32 process primitives, resolved from the upstream ACL installation.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-electron/dsh-plugin-multi-root-workspace",
|
|
3
3
|
"description": "Multi-root Workspace for DeepSeek Harness: adds N additional roots to the workspace sandbox scope as an out-of-tree bundle",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.8",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|