@dsh-electron/dsh-plugin-multi-root-workspace 0.1.0 → 0.1.1
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 +77 -0
- package/CHANGELOG.md +77 -0
- package/README.en.md +94 -20
- package/README.md +95 -19
- package/cordis.patch.yml +22 -1
- package/lib/client.js +102 -58
- package/lib/client.js.map +1 -1
- package/lib/{command-BSkrYOAf.js → command-DyOWujPo.js} +24 -61
- package/lib/command.js +2 -2
- package/lib/compat-nFYqN5wt.js +267 -0
- package/lib/compat.js +2 -0
- package/lib/{fs-D7UoCb7T.js → fs-D_Sw3o5z.js} +7 -3
- package/lib/fs.js +1 -1
- package/lib/index.js +8 -6
- package/lib/instructions-BRGoC82G.js +635 -0
- package/lib/instructions.js +2 -0
- package/lib/registry-DySMbjgW.js +793 -0
- package/lib/registry-lease-win32-dy8TQMD9.js +93 -0
- package/lib/registry.js +2 -466
- package/lib/{sandbox-BhuMTDgH.js → sandbox-D8Fw95FM.js} +51 -4
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +1 -1
- package/lib/types/client/locales.d.ts +6 -0
- package/lib/types/command.d.ts +17 -0
- package/lib/types/compat/agent-instructions.d.ts +138 -0
- package/lib/types/compat/dsh-version.d.ts +119 -0
- package/lib/types/compat/llm-message.d.ts +34 -0
- package/lib/types/compat/sandbox-confine.d.ts +58 -0
- package/lib/types/compat.d.ts +85 -0
- package/lib/types/contract.d.ts +6 -8
- package/lib/types/index.d.ts +19 -5
- package/lib/types/instructions.d.ts +124 -0
- package/lib/types/registry-lease-win32.d.ts +29 -0
- package/lib/types/registry-lease.d.ts +62 -0
- package/lib/types/registry.d.ts +130 -19
- package/lib/types/roots.d.ts +1 -1
- package/lib/types/sandbox.d.ts +22 -2
- package/package.json +60 -13
- package/lib/{roots-DyT2jgEP.js → roots-DasWEBJk.js} +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { f as resolveRootRef, i as canonicalRoot, o as entryRootRef, r as availableRoots,
|
|
2
|
-
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
3
|
-
import { statSync } from "node:fs";
|
|
1
|
+
import { f as resolveRootRef, i as canonicalRoot, o as entryRootRef, r as availableRoots, t as RootValidationError } from "./roots-DasWEBJk.js";
|
|
4
2
|
import "@deepseek-ai/cordis";
|
|
3
|
+
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
//#region src/contract.ts
|
|
7
6
|
/**
|
|
@@ -53,8 +52,7 @@ const PANEL_ENDPOINTS = [
|
|
|
53
52
|
* would hide the drift.
|
|
54
53
|
*/
|
|
55
54
|
const panelRequestSchema = z.object({
|
|
56
|
-
|
|
57
|
-
sessionId: z.string().optional(),
|
|
55
|
+
sessionId: z.string().min(1),
|
|
58
56
|
entry: z.object({
|
|
59
57
|
ordinal: z.number().int().positive(),
|
|
60
58
|
id: z.string(),
|
|
@@ -170,8 +168,7 @@ function parseRevealedView(value) {
|
|
|
170
168
|
function narrowCall(endpoint, value) {
|
|
171
169
|
return {
|
|
172
170
|
endpoint,
|
|
173
|
-
|
|
174
|
-
...value.sessionId === void 0 ? {} : { sessionId: value.sessionId },
|
|
171
|
+
sessionId: value.sessionId,
|
|
175
172
|
...value.entry === void 0 ? {} : { entry: value.entry },
|
|
176
173
|
...value.id === void 0 ? {} : { id: value.id },
|
|
177
174
|
...value.path === void 0 ? {} : { path: value.path },
|
|
@@ -200,35 +197,6 @@ function firstIssue(error) {
|
|
|
200
197
|
}
|
|
201
198
|
//#endregion
|
|
202
199
|
//#region src/command.ts
|
|
203
|
-
/**
|
|
204
|
-
* The user-facing surface: the `/workspace-folders` command and the panel's
|
|
205
|
-
* RPC channel.
|
|
206
|
-
*
|
|
207
|
-
* They share one module because they are one thing — the operator-facing edge
|
|
208
|
-
* over `ctx.multiRootRegistry`. The command serves the text surfaces (the
|
|
209
|
-
* composer, headless diagnostics) and the channel serves the browser panel;
|
|
210
|
-
* both resolve the same canonical workspace root, call the same registry, and
|
|
211
|
-
* report the same `RootValidationCode` values.
|
|
212
|
-
*
|
|
213
|
-
* Both are also the two places a READ happens, so both go through the same
|
|
214
|
-
* revalidation entry point: `registry.refresh()` re-stats every registered
|
|
215
|
-
* directory, re-resolves it against the directory it was granted for, and
|
|
216
|
-
* republishes the scope BEFORE the list is rendered. Listing is therefore
|
|
217
|
-
* enough to notice that a directory disappeared (and enough to bring a
|
|
218
|
-
* restored one back) — which is what the panel's "Retry" button and the
|
|
219
|
-
* command's `list` both promise.
|
|
220
|
-
*
|
|
221
|
-
* Both halves are SOFT: the command registers only where a command registry is
|
|
222
|
-
* composed, and the channel only where a host Connection and web server exist
|
|
223
|
-
* (web), so a headless profile mounts this row with zero effect on the
|
|
224
|
-
* providers.
|
|
225
|
-
*
|
|
226
|
-
* The command's own text is English: a host-side command handler has no active
|
|
227
|
-
* locale to consult (the browser owns locale state). The panel is bilingual.
|
|
228
|
-
* That split is recorded in the M3 plan and the requirements as a known limit.
|
|
229
|
-
*
|
|
230
|
-
* @module @dsh-electron/dsh-plugin-multi-root-workspace/command
|
|
231
|
-
*/
|
|
232
200
|
/** Services this row needs before it may activate. */
|
|
233
201
|
const inject = [
|
|
234
202
|
"commands",
|
|
@@ -487,31 +455,26 @@ async function targetOf(registry, primaryRoot, text) {
|
|
|
487
455
|
};
|
|
488
456
|
}
|
|
489
457
|
/**
|
|
490
|
-
*
|
|
458
|
+
* Derive the primary root a panel request may act on.
|
|
459
|
+
*
|
|
460
|
+
* The browser names a live session; the host looks that session up and uses
|
|
461
|
+
* its immutable cwd. There is no client-supplied path and no deployment-default
|
|
462
|
+
* fallback: a panel call without a real host session has no workspace to
|
|
463
|
+
* manage. Property access would demand an injected `sessions` dependency this
|
|
464
|
+
* row does not name, so the lookup stays an explicit sibling `get` (the same
|
|
465
|
+
* pattern as `directoryPicker` / `subprocess`).
|
|
491
466
|
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
467
|
+
* @param ctx - the host context that may carry `sessions`.
|
|
468
|
+
* @param sessionId - the id the panel sent; already required by the wire schema.
|
|
469
|
+
* @returns the canonical cwd of that session.
|
|
470
|
+
* @throws {RootValidationError} `session-not-found` when the service is absent,
|
|
471
|
+
* the id does not name a live session, or that session has no cwd.
|
|
496
472
|
*/
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
isDirectory = statSync(canonical).isDirectory();
|
|
503
|
-
} catch {
|
|
504
|
-
isDirectory = false;
|
|
505
|
-
}
|
|
506
|
-
if (!isDirectory) throw new RootValidationError("missing", `"${request.primaryRoot}" is not an existing directory`, { reference: request.primaryRoot });
|
|
507
|
-
return canonical;
|
|
508
|
-
}
|
|
509
|
-
if (request.sessionId !== void 0 && request.sessionId !== "") {
|
|
510
|
-
const cwd = ctx.get("sessions")?.get(request.sessionId)?.header.cwd;
|
|
511
|
-
if (cwd !== void 0 && cwd !== "") return canonicalPath(cwd);
|
|
512
|
-
}
|
|
513
|
-
const policy = ctx.sandboxPolicy.resolve();
|
|
514
|
-
return canonicalRoot(policy.workspaceRoot);
|
|
473
|
+
function resolvePanelPrimaryRoot(ctx, sessionId) {
|
|
474
|
+
const session = ctx.get("sessions")?.get(sessionId);
|
|
475
|
+
const cwd = session?.header.cwd;
|
|
476
|
+
if (session === void 0 || cwd === void 0 || cwd === "") throw new RootValidationError("session-not-found", `session "${sessionId}" is not an active host session`, { reference: sessionId });
|
|
477
|
+
return canonicalPath(cwd);
|
|
515
478
|
}
|
|
516
479
|
/** Project one status into the panel's view. */
|
|
517
480
|
function toRootView(status, index) {
|
|
@@ -589,7 +552,7 @@ async function dispatchPanelRequest(ctx, endpoint, payload, _signal) {
|
|
|
589
552
|
};
|
|
590
553
|
const request = parsed.value;
|
|
591
554
|
const registry = ctx.multiRootRegistry;
|
|
592
|
-
const primaryRoot =
|
|
555
|
+
const primaryRoot = resolvePanelPrimaryRoot(ctx, request.sessionId);
|
|
593
556
|
switch (request.endpoint) {
|
|
594
557
|
case "list": return {
|
|
595
558
|
ok: true,
|
|
@@ -724,4 +687,4 @@ async function revealRoot(ctx, path) {
|
|
|
724
687
|
if (outcome.exitCode !== 0) throw new RootValidationError("reveal-unavailable", `the file manager exited with ${String(outcome.exitCode)}`);
|
|
725
688
|
}
|
|
726
689
|
//#endregion
|
|
727
|
-
export { renderRootsReport as a,
|
|
690
|
+
export { renderRootsReport as a, PANEL_CHANNEL as c, parseRevealedView as d, parseRootsView as f, parseFoldersCommand as i, PANEL_ENDPOINTS as l, apply as n, resolvePanelPrimaryRoot as o, inject as r, revealArgv as s, COMMAND_NAME as t, parsePanelCall as u };
|
package/lib/command.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as renderRootsReport, i as parseFoldersCommand, n as apply, o as
|
|
2
|
-
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, revealArgv };
|
|
1
|
+
import { a as renderRootsReport, i as parseFoldersCommand, n as apply, o as resolvePanelPrimaryRoot, r as inject, s as revealArgv, t as COMMAND_NAME } from "./command-DyOWujPo.js";
|
|
2
|
+
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealArgv };
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
+
//#region src/compat/dsh-version.ts
|
|
4
|
+
/**
|
|
5
|
+
* The DSH compatibility contract: which upstream releases this plugin is
|
|
6
|
+
* actually verified against, and how an installation is judged against that
|
|
7
|
+
* list.
|
|
8
|
+
*
|
|
9
|
+
* This module is the ONE place the support matrix is spelled out. It is
|
|
10
|
+
* deliberately pure — no cordis, no logging, no process exit — so that the
|
|
11
|
+
* runtime gate (`src/compat.ts`), the static gate (`scripts/check-dsh-compat.mjs`)
|
|
12
|
+
* and the tests all judge an installation by the same code instead of three
|
|
13
|
+
* drifting copies of the same list.
|
|
14
|
+
*
|
|
15
|
+
* Why a contract exists at all: this plugin replaces `ctx.fs` and `ctx.sandbox`
|
|
16
|
+
* with subclasses of the upstream providers, and `src/dialects.ts` recognizes
|
|
17
|
+
* the kernel-sandbox profile by OBSERVING the argv shape `super.confine`
|
|
18
|
+
* produced (ADR-0003). Both couplings are to pre-stable upstream internals that
|
|
19
|
+
* carry no semver promise. An npm range like `>=0.1.2-alpha.4 <0.2.0` therefore
|
|
20
|
+
* promises far more than the code was ever verified against; the allowlist
|
|
21
|
+
* below promises exactly what CI has run.
|
|
22
|
+
*
|
|
23
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/dsh-version
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The releases this plugin is verified against, as EXACT version strings.
|
|
27
|
+
*
|
|
28
|
+
* Not a semver range, on purpose: a range is a prediction about releases that
|
|
29
|
+
* do not exist yet, and this plugin's coupling to upstream argv shapes makes
|
|
30
|
+
* that prediction unsafe. Adding an entry is always a human act performed after
|
|
31
|
+
* the upgrade smoke passed on that exact version — a green CI run on an
|
|
32
|
+
* unlisted release is evidence, not authorization (ADR-0009).
|
|
33
|
+
*/
|
|
34
|
+
const SUPPORTED_DSH_RELEASES = ["0.1.5-rc.2", "0.1.6-alpha.1"];
|
|
35
|
+
/**
|
|
36
|
+
* Packages whose absence means this plugin cannot function at all: every one of
|
|
37
|
+
* them is either subclassed, imported for a value, or the owner of a service
|
|
38
|
+
* key the providers inject.
|
|
39
|
+
*/
|
|
40
|
+
const REQUIRED_CORE_PACKAGES = [
|
|
41
|
+
"@deepseek-ai/dsh-fs",
|
|
42
|
+
"@deepseek-ai/dsh-fs-local",
|
|
43
|
+
"@deepseek-ai/dsh-sandbox",
|
|
44
|
+
"@deepseek-ai/dsh-sandbox-local",
|
|
45
|
+
"@deepseek-ai/dsh-sandbox-policy",
|
|
46
|
+
"@deepseek-ai/dsh-storage-domain"
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Packages a full composition provides and a minimal one legitimately omits.
|
|
50
|
+
* Absence is fine; a version that DISAGREES with the required packages is not —
|
|
51
|
+
* that is the mixed-installation failure this contract exists to catch.
|
|
52
|
+
*/
|
|
53
|
+
const OPTIONAL_CORE_PACKAGES = [
|
|
54
|
+
"@deepseek-ai/dsh-agent",
|
|
55
|
+
"@deepseek-ai/dsh-agent-instructions",
|
|
56
|
+
"@deepseek-ai/dsh-client-connection",
|
|
57
|
+
"@deepseek-ai/dsh-commands",
|
|
58
|
+
"@deepseek-ai/dsh-llm",
|
|
59
|
+
"@deepseek-ai/dsh-session",
|
|
60
|
+
"@deepseek-ai/dsh-subprocess",
|
|
61
|
+
"@deepseek-ai/dsh-system-prompt"
|
|
62
|
+
];
|
|
63
|
+
/** Every package the contract inspects, required first. */
|
|
64
|
+
const CORE_PACKAGES = [...REQUIRED_CORE_PACKAGES, ...OPTIONAL_CORE_PACKAGES];
|
|
65
|
+
/** The environment variable that relaxes the gate. */
|
|
66
|
+
const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
67
|
+
/**
|
|
68
|
+
* Read one package's declared version through Node resolution.
|
|
69
|
+
*
|
|
70
|
+
* Resolution runs from THIS module, which is what makes the answer meaningful:
|
|
71
|
+
* the upstream packages are peer dependencies, so they resolve to the single
|
|
72
|
+
* copy the host provides — the same copy the providers will subclass at
|
|
73
|
+
* runtime. A package that cannot be resolved is reported absent rather than
|
|
74
|
+
* throwing, because "not installed" is a legitimate answer for every optional
|
|
75
|
+
* package.
|
|
76
|
+
* @param packageName - the package to inspect.
|
|
77
|
+
* @returns the declared version, or `undefined` when the package or its
|
|
78
|
+
* manifest cannot be resolved.
|
|
79
|
+
*/
|
|
80
|
+
function readInstalledVersion(packageName) {
|
|
81
|
+
try {
|
|
82
|
+
const manifest = createRequire(import.meta.url)(`${packageName}/package.json`);
|
|
83
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Judge one installation against the contract.
|
|
90
|
+
*
|
|
91
|
+
* The three checks run in this order, and the first failure wins, because each
|
|
92
|
+
* later check would report nonsense about an installation that failed an
|
|
93
|
+
* earlier one: a missing required package has no version to compare, and a
|
|
94
|
+
* mixed installation has no single version to look up in the allowlist.
|
|
95
|
+
* @param readVersion - version reader; defaults to Node resolution. Tests and
|
|
96
|
+
* the static checker pass their own.
|
|
97
|
+
* @returns the report; never throws.
|
|
98
|
+
*/
|
|
99
|
+
function classifyInstallation(readVersion = readInstalledVersion) {
|
|
100
|
+
const packages = CORE_PACKAGES.map((name) => ({
|
|
101
|
+
name,
|
|
102
|
+
version: readVersion(name),
|
|
103
|
+
required: REQUIRED_CORE_PACKAGES.includes(name)
|
|
104
|
+
}));
|
|
105
|
+
const missing = packages.filter((entry) => entry.required && entry.version === void 0);
|
|
106
|
+
if (missing.length > 0) return report("incomplete", void 0, packages, [
|
|
107
|
+
"multi-root workspace: required DSH packages are not installed.",
|
|
108
|
+
"",
|
|
109
|
+
...missing.map((entry) => ` missing: ${entry.name}`)
|
|
110
|
+
]);
|
|
111
|
+
const releases = [...new Set(packages.flatMap((entry) => entry.version === void 0 ? [] : [entry.version]))].sort();
|
|
112
|
+
const [release, ...extra] = releases;
|
|
113
|
+
if (release === void 0) return report("incomplete", void 0, packages, ["multi-root workspace: no DSH package could be inspected."]);
|
|
114
|
+
if (extra.length > 0) return report("mixed", void 0, packages, [
|
|
115
|
+
"multi-root workspace: this installation mixes several DSH releases.",
|
|
116
|
+
"",
|
|
117
|
+
"The filesystem fence and the kernel-sandbox dialects must agree on one upstream",
|
|
118
|
+
"release; a mixed installation means one half of the sandbox was verified against a",
|
|
119
|
+
"shape the other half no longer produces. Align every @deepseek-ai/dsh* package on",
|
|
120
|
+
"one release.",
|
|
121
|
+
"",
|
|
122
|
+
` releases found: ${releases.join(", ")}`
|
|
123
|
+
]);
|
|
124
|
+
if (!SUPPORTED_DSH_RELEASES.includes(release)) return report("unsupported", release, packages, [
|
|
125
|
+
`multi-root workspace: DSH ${release} is not a supported release.`,
|
|
126
|
+
"",
|
|
127
|
+
"This plugin replaces the workspace filesystem fence and the kernel-sandbox provider,",
|
|
128
|
+
"and it recognizes the sandbox profile by the argv shape the upstream provider produces.",
|
|
129
|
+
"Upstream makes no semver promise, so the plugin refuses to grant additional roots on a",
|
|
130
|
+
"release it has never been verified against rather than guess at that shape.",
|
|
131
|
+
"",
|
|
132
|
+
` supported: ${SUPPORTED_DSH_RELEASES.join(", ")}`,
|
|
133
|
+
` installed: ${release}`
|
|
134
|
+
]);
|
|
135
|
+
return report("supported", release, packages, [`multi-root workspace: DSH ${release} is a supported release.`]);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Read the enforcement mode.
|
|
139
|
+
*
|
|
140
|
+
* `warn` exists for exactly one caller: the upgrade smoke, which must run the
|
|
141
|
+
* whole behavior matrix against a release that is deliberately NOT on the
|
|
142
|
+
* allowlist yet — that run is how a release earns its place on the list. It is
|
|
143
|
+
* not a production escape hatch, and the warning says so.
|
|
144
|
+
* @param env - the environment to read; defaults to this process's.
|
|
145
|
+
* @returns `warn` only when explicitly requested, `enforce` otherwise.
|
|
146
|
+
*/
|
|
147
|
+
function compatEnforcement(env = process.env) {
|
|
148
|
+
return env["DSH_MULTI_ROOT_COMPAT"] === "warn" ? "warn" : "enforce";
|
|
149
|
+
}
|
|
150
|
+
/** Compose a report, appending the package inventory and the recovery pointer. */
|
|
151
|
+
function report(verdict, release, packages, lines) {
|
|
152
|
+
return {
|
|
153
|
+
verdict,
|
|
154
|
+
release,
|
|
155
|
+
packages,
|
|
156
|
+
message: (verdict === "supported" ? [...lines] : [
|
|
157
|
+
...lines,
|
|
158
|
+
"",
|
|
159
|
+
"Inspected packages:",
|
|
160
|
+
...packages.map((entry) => ` ${entry.name} ${entry.version ?? "(not installed)"}`),
|
|
161
|
+
"",
|
|
162
|
+
"See docs/troubleshooting/unsupported-dsh-release.md"
|
|
163
|
+
]).join("\n")
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/compat.ts
|
|
168
|
+
/**
|
|
169
|
+
* `multi-root-compat`: the startup gate that turns the DSH compatibility
|
|
170
|
+
* contract from documentation into a precondition.
|
|
171
|
+
*
|
|
172
|
+
* The four security-relevant rows of this bundle — `multi-root-fs`,
|
|
173
|
+
* `multi-root-sandbox`, `multi-root-registry`, `multi-root-instructions` —
|
|
174
|
+
* inject `multiRootCompat`. Cordis will not start a plugin whose injected
|
|
175
|
+
* service is absent, so this row is what decides whether they run at all:
|
|
176
|
+
*
|
|
177
|
+
* ```text
|
|
178
|
+
* compat check passes → ctx.multiRootCompat exists → the providers start
|
|
179
|
+
* compat check fails → the row throws → they never start
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* That is why the gate is a plain `apply` and not a `Service` subclass mounted
|
|
183
|
+
* directly: the check has to be able to fail BEFORE the service exists. A
|
|
184
|
+
* `Service` constructor has already run `super(ctx, name)` — and therefore
|
|
185
|
+
* already claimed the service key — by the time it could inspect anything.
|
|
186
|
+
*
|
|
187
|
+
* The verdict itself, and the allowlist it is judged against, live in
|
|
188
|
+
* `./compat/dsh-version.ts`. This module owns only the reaction to it.
|
|
189
|
+
*
|
|
190
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/compat
|
|
191
|
+
*/
|
|
192
|
+
/**
|
|
193
|
+
* Raised when the installed DSH release is not one this plugin is verified
|
|
194
|
+
* against. It is thrown from `apply`, so it surfaces as a plugin load failure
|
|
195
|
+
* with the full diagnosis attached — never as a silently degraded sandbox.
|
|
196
|
+
*/
|
|
197
|
+
var DshCompatUnsupportedError = class extends Error {
|
|
198
|
+
/** The report that produced the refusal. */
|
|
199
|
+
report;
|
|
200
|
+
constructor(report) {
|
|
201
|
+
super(report.message);
|
|
202
|
+
this.name = "DshCompatUnsupportedError";
|
|
203
|
+
this.report = report;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* The `ctx.multiRootCompat` service: the resolved compatibility verdict, held
|
|
208
|
+
* where the gated providers can read it.
|
|
209
|
+
*
|
|
210
|
+
* It intentionally exposes no per-version capability flags. The adapters under
|
|
211
|
+
* `./compat/` detect the shape they need STRUCTURALLY — whether a return value
|
|
212
|
+
* is a thenable, which export name a module carries — because a structural
|
|
213
|
+
* probe also survives upstream reshaping something within one release, while a
|
|
214
|
+
* version comparison does not. The release string here is for the gate and for
|
|
215
|
+
* diagnostics.
|
|
216
|
+
*/
|
|
217
|
+
var MultiRootCompatService = class extends Service {
|
|
218
|
+
/** The verdict this composition started under. */
|
|
219
|
+
report;
|
|
220
|
+
constructor(ctx, report) {
|
|
221
|
+
super(ctx, "multiRootCompat");
|
|
222
|
+
this.report = report;
|
|
223
|
+
}
|
|
224
|
+
/** The single DSH release every inspected core package agreed on. */
|
|
225
|
+
get release() {
|
|
226
|
+
return this.report.release;
|
|
227
|
+
}
|
|
228
|
+
/** Whether the installation is on the allowlist. */
|
|
229
|
+
get supported() {
|
|
230
|
+
return this.report.verdict === "supported";
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
/** This row needs nothing: it is the thing everything else waits for. */
|
|
234
|
+
const inject = [];
|
|
235
|
+
/**
|
|
236
|
+
* Apply the gate policy to one report.
|
|
237
|
+
*
|
|
238
|
+
* Under `DSH_MULTI_ROOT_COMPAT=warn` exactly one verdict is relaxed: an
|
|
239
|
+
* `unsupported` release that every inspected package agrees on. That is the
|
|
240
|
+
* only shape the upgrade smoke probes — a coherent tree on a release the
|
|
241
|
+
* allowlist does not name yet, which is how a release earns its place there.
|
|
242
|
+
* `mixed` and `incomplete` describe an installation the contract cannot judge
|
|
243
|
+
* at all, so they are refused in either mode; production deployments leave the
|
|
244
|
+
* variable unset and get the refusal for everything.
|
|
245
|
+
* @param report - the verdict to act on.
|
|
246
|
+
* @param enforcement - how to react to a non-`supported` verdict.
|
|
247
|
+
* @returns the warning to log, or `undefined` when there is nothing to say.
|
|
248
|
+
* @throws {DshCompatUnsupportedError} when refusing to run.
|
|
249
|
+
*/
|
|
250
|
+
function assertSupportedInstallation(report, enforcement) {
|
|
251
|
+
if (report.verdict === "supported") return void 0;
|
|
252
|
+
if (enforcement === "enforce" || report.verdict !== "unsupported") throw new DshCompatUnsupportedError(report);
|
|
253
|
+
return `${report.message}\n\nProceeding anyway because ${COMPAT_ENFORCEMENT_ENV}=warn. This is an upgrade-smoke mode: additional workspace roots are about to be granted through a sandbox profile shape that has not been verified on this release. Supported releases: ${SUPPORTED_DSH_RELEASES.join(", ")}.`;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Judge the installation, then either refuse to provide the service or provide
|
|
257
|
+
* it.
|
|
258
|
+
* @param ctx - the row's context.
|
|
259
|
+
*/
|
|
260
|
+
function apply(ctx) {
|
|
261
|
+
const report = classifyInstallation();
|
|
262
|
+
const warning = assertSupportedInstallation(report, compatEnforcement());
|
|
263
|
+
if (warning !== void 0) ctx.logger.warn(warning);
|
|
264
|
+
ctx.plugin(MultiRootCompatService, report);
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
export { inject as a, REQUIRED_CORE_PACKAGES as c, compatEnforcement as d, readInstalledVersion as f, assertSupportedInstallation as i, SUPPORTED_DSH_RELEASES as l, MultiRootCompatService as n, CORE_PACKAGES as o, apply as r, OPTIONAL_CORE_PACKAGES as s, DshCompatUnsupportedError as t, classifyInstallation as u };
|
package/lib/compat.js
ADDED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { dirname, sep } from "node:path";
|
|
2
|
+
import { writableRoots } from "@deepseek-ai/dsh-sandbox";
|
|
1
3
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
2
4
|
import { LocalFileSystem } from "@deepseek-ai/dsh-fs-local";
|
|
3
|
-
import { writableRoots } from "@deepseek-ai/dsh-sandbox";
|
|
4
5
|
import { stat } from "node:fs/promises";
|
|
5
|
-
import { dirname, sep } from "node:path";
|
|
6
6
|
//#region src/containment.ts
|
|
7
7
|
const MISSING_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR"]);
|
|
8
8
|
function isMissing(error) {
|
|
@@ -64,7 +64,11 @@ async function isPathUnder(path, root, caseSensitive = process.platform !== "win
|
|
|
64
64
|
* roots.
|
|
65
65
|
*/
|
|
66
66
|
var MultiRootFileSystem = class extends LocalFileSystem {
|
|
67
|
-
static inject = [
|
|
67
|
+
static inject = [
|
|
68
|
+
"multiRootCompat",
|
|
69
|
+
"sandboxPolicy",
|
|
70
|
+
"multiRootScope"
|
|
71
|
+
];
|
|
68
72
|
defaultMode;
|
|
69
73
|
constructor(ctx, config) {
|
|
70
74
|
super(ctx, config);
|
package/lib/fs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as MultiRootFileSystem } from "./fs-
|
|
1
|
+
import { t as MultiRootFileSystem } from "./fs-D_Sw3o5z.js";
|
|
2
2
|
export { MultiRootFileSystem, MultiRootFileSystem as default };
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import { t as
|
|
1
|
+
import { c as REQUIRED_CORE_PACKAGES, d as compatEnforcement, f as readInstalledVersion, i as assertSupportedInstallation, l as SUPPORTED_DSH_RELEASES, n as MultiRootCompatService, o as CORE_PACKAGES, s as OPTIONAL_CORE_PACKAGES, t as DshCompatUnsupportedError, u as classifyInstallation } from "./compat-nFYqN5wt.js";
|
|
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-BRGoC82G.js";
|
|
3
|
+
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-D8Fw95FM.js";
|
|
4
|
+
import { a as classifyStoredRoots, c as indexedStatuses, f as resolveRootRef, i as canonicalRoot, l as isCanonicallyUnder, n as additionalRootId, p as validateRootCandidate, r as availableRoots, s as expandRootInput, t as RootValidationError, u as removeStatusAt } from "./roots-DasWEBJk.js";
|
|
5
|
+
import { t as MultiRootFileSystem } from "./fs-D_Sw3o5z.js";
|
|
3
6
|
import { MultiRootScopeService, renderWorkspaceRootsContext, sanitizeAdditionalRoots } from "./scope.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { a as renderRootsReport, c as PANEL_ENDPOINTS, d as parseRootsView, i as parseFoldersCommand, l as parsePanelCall, o as revealArgv, s as PANEL_CHANNEL, t as COMMAND_NAME, u as parseRevealedView } from "./command-BSkrYOAf.js";
|
|
7
|
+
import { c as RegistryLeaseContendedError, i as REGISTRY_CONTENDED_MESSAGE, n as MAX_ALIAS_LENGTH, o as multiRootDomainSpec, r as MultiRootRegistry, s as RegistryAuthorityLease, t as DOMAIN_NAME } from "./registry-DySMbjgW.js";
|
|
8
|
+
import { a as renderRootsReport, c as PANEL_CHANNEL, d as parseRevealedView, f as parseRootsView, i as parseFoldersCommand, l as PANEL_ENDPOINTS, o as resolvePanelPrimaryRoot, s as revealArgv, t as COMMAND_NAME, u as parsePanelCall } from "./command-DyOWujPo.js";
|
|
7
9
|
//#region src/index.ts
|
|
8
10
|
/**
|
|
9
11
|
* Carrier plugin face for the bare-package-name loader row (see the module
|
|
@@ -14,4 +16,4 @@ import { a as renderRootsReport, c as PANEL_ENDPOINTS, d as parseRootsView, i as
|
|
|
14
16
|
const inject = [];
|
|
15
17
|
function apply() {}
|
|
16
18
|
//#endregion
|
|
17
|
-
export { COMMAND_NAME, DOMAIN_NAME, MAX_ALIAS_LENGTH, MultiRootFileSystem, MultiRootRegistry, MultiRootSandboxProvider, MultiRootScopeService, PANEL_CHANNEL, PANEL_ENDPOINTS, RootValidationError, additionalRootId, apply, availableRoots, canonicalRoot, classifyStoredRoots, expandRootInput, indexedStatuses, inject, isCanonicallyUnder, multiRootDomainSpec, parseFoldersCommand, parsePanelCall, parseRevealedView, parseRootsView, removeStatusAt, renderRootsReport, renderWorkspaceRootsContext, resolveRootRef, revealArgv, sanitizeAdditionalRoots, validateRootCandidate };
|
|
19
|
+
export { COMMAND_NAME, CORE_PACKAGES, DEFAULT_MAX_BYTES, DOMAIN_NAME, DshCompatUnsupportedError, MAX_ALIAS_LENGTH, MultiRootCompatService, MultiRootFileSystem, MultiRootRegistry, MultiRootSandboxProvider, MultiRootScopeService, OPTIONAL_CORE_PACKAGES, PANEL_CHANNEL, PANEL_ENDPOINTS, PLUGIN_SOURCE, REGISTRY_CONTENDED_MESSAGE, REQUIRED_CORE_PACKAGES, RegistryAuthorityLease, RegistryLeaseContendedError, RootValidationError, SUPPORTED_DSH_RELEASES, additionalRootId, apply, assertSupportedInstallation, availableRoots, canonicalRoot, classifyInstallation, classifyStoredRoots, compatEnforcement, composeInstructionMessage, createInstructionMessage, expandRootInput, indexedStatuses, inject, instructionsApi, isCanonicallyUnder, multiRootDomainSpec, parseFoldersCommand, parsePanelCall, parseRevealedView, parseRootsView, readInstalledVersion, removeStatusAt, renderInstructions, renderRootsReport, renderWorkspaceRootsContext, resolvePanelPrimaryRoot, resolveRootRef, revealArgv, sanitizeAdditionalRoots, validateRootCandidate, widenConfined };
|