@dsh-electron/dsh-plugin-multi-root-workspace 0.1.5 → 0.1.7
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 +56 -1
- package/CHANGELOG.md +56 -1
- package/README.en.md +157 -92
- package/README.md +158 -93
- package/cordis.patch.yml +61 -0
- package/lib/client.js +2819 -115
- package/lib/client.js.map +1 -1
- package/lib/command-CESuwlVt.js +1306 -0
- package/lib/command.js +2 -2
- package/lib/{compat-82Y27Iom.js → compat-DF_yL1bB.js} +180 -4
- package/lib/compat.js +1 -1
- package/lib/contract-DZ4zczDT.js +238 -0
- package/lib/{fs-D_Sw3o5z.js → fs-9JIzGaf3.js} +55 -19
- package/lib/fs.js +2 -1
- package/lib/index.js +10 -8
- package/lib/{instructions-Cn1Jj5Xi.js → instructions-D19jRxN0.js} +109 -28
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +43 -0
- package/lib/method-wrapper-CFY3p3j2.js +26 -0
- package/lib/process-fence.js +280 -0
- package/lib/providers.js +87 -0
- package/lib/{registry-DySMbjgW.js → registry-DCxC5Lay.js} +256 -35
- package/lib/registry.js +2 -2
- package/lib/{roots-DasWEBJk.js → roots-B-0l8mNG.js} +117 -20
- package/lib/sandbox-Kup4_ZIb.js +747 -0
- package/lib/sandbox.js +2 -1
- package/lib/scope.js +240 -25
- package/lib/session-directory-BLOstut1.js +9 -0
- package/lib/tool-context-CpgIFcCB.js +6 -0
- package/lib/types/client/RootSwitcher.d.ts +63 -0
- package/lib/types/client/WorkspaceFoldersAction.d.ts +41 -0
- package/lib/types/client/files-tree/FilesBody.module.css.d.ts +24 -0
- package/lib/types/client/files-tree/FilesTree.d.ts +47 -0
- package/lib/types/client/files-tree/adapter.d.ts +94 -0
- package/lib/types/client/files-tree/directory-node.d.ts +62 -0
- package/lib/types/client/files-tree/face.d.ts +107 -0
- package/lib/types/client/files-tree/locales.d.ts +10 -0
- package/lib/types/client/files-tree/primitives.d.ts +33 -0
- package/lib/types/client/files-tree/store.d.ts +81 -0
- package/lib/types/client/index.d.ts +13 -2
- package/lib/types/client/locales.d.ts +60 -0
- package/lib/types/client/open-in-app/OpenInAppAction.d.ts +27 -0
- package/lib/types/client/open-in-app/OpenTargetButton.d.ts +52 -0
- package/lib/types/client/open-in-app/OpenTargetButton.module.css.d.ts +29 -0
- package/lib/types/client/open-in-app/adapter.d.ts +46 -0
- package/lib/types/client/open-in-app/applications.d.ts +4 -0
- package/lib/types/client/open-in-app/controller.d.ts +53 -0
- package/lib/types/client/open-in-app/locales.d.ts +10 -0
- package/lib/types/client/open-in-app/open-failure-toast.d.ts +11 -0
- package/lib/types/client/open-in-app/open-path.d.ts +13 -0
- package/lib/types/client/panel-client.d.ts +2 -1
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/command.d.ts +16 -3
- package/lib/types/compat/client-icons.d.ts +18 -0
- package/lib/types/compat/client-session.d.ts +2 -0
- package/lib/types/compat/dsh-version.d.ts +5 -3
- package/lib/types/compat/instruction-overlap.d.ts +7 -0
- package/lib/types/compat/open-in-app-routes.d.ts +38 -0
- package/lib/types/compat/process-entrypoints.d.ts +31 -0
- package/lib/types/compat/process-result.d.ts +14 -0
- package/lib/types/compat/pty-marker.d.ts +83 -0
- package/lib/types/compat/sandbox-confine.d.ts +2 -0
- package/lib/types/compat/session-directory.d.ts +4 -0
- package/lib/types/compat/storage-zod.d.ts +65 -0
- package/lib/types/compat/tool-context.d.ts +9 -0
- package/lib/types/compat/windows-deny-preflight.d.ts +21 -0
- package/lib/types/compat/windows-native.d.ts +68 -0
- package/lib/types/compat/windows-path-guard.d.ts +56 -0
- package/lib/types/compat/workspace-files.d.ts +7 -0
- package/lib/types/contract.d.ts +35 -4
- package/lib/types/dialects.d.ts +99 -3
- package/lib/types/fs.d.ts +32 -10
- package/lib/types/index.d.ts +5 -0
- package/lib/types/lsp.d.ts +4 -0
- package/lib/types/method-wrapper.d.ts +2 -0
- package/lib/types/process-fence.d.ts +54 -0
- package/lib/types/profile-isolation.d.ts +178 -0
- package/lib/types/providers.d.ts +70 -0
- package/lib/types/registry.d.ts +132 -31
- package/lib/types/roots.d.ts +100 -13
- package/lib/types/sandbox.d.ts +59 -12
- package/lib/types/scope.d.ts +203 -14
- package/lib/types/windows-broker.d.ts +17 -0
- package/lib/types/windows-deny.d.ts +1 -0
- package/lib/types/windows-profile.d.ts +10 -0
- package/lib/types/windows-runner-contract.d.ts +97 -0
- package/lib/types/windows-runner.d.ts +1 -0
- package/lib/types/workspace-files.d.ts +4 -0
- package/lib/types/workspace-routing.d.ts +11 -0
- package/lib/windows-runner-contract-CEfmZzu0.js +141 -0
- package/lib/windows-runner.js +1311 -0
- package/lib/workspace-files.js +203 -0
- package/lib/workspace-routing-YPxaQ4V3.js +26 -0
- package/package.json +116 -51
- package/scripts/windows-broker/install.ps1 +89 -0
- package/scripts/windows-broker/rights.cs +58 -0
- package/scripts/windows-broker/service.cs +323 -0
- package/scripts/windows-broker/verify-install.ps1 +80 -0
- package/lib/command-DyOWujPo.js +0 -690
- package/lib/sandbox-D8Fw95FM.js +0 -290
- package/lib/{registry-lease-win32-dy8TQMD9.js → registry-lease-win32-DTO6QpT9.js} +2 -2
package/lib/command.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as renderRootsReport, i as parseFoldersCommand, n as apply, o as resolvePanelPrimaryRoot, r as inject, s as
|
|
2
|
-
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealArgv };
|
|
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-CESuwlVt.js";
|
|
2
|
+
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealAccepted, revealArgv };
|
|
@@ -1,5 +1,7 @@
|
|
|
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
5
|
//#region src/compat/dsh-version.ts
|
|
4
6
|
/**
|
|
5
7
|
* The DSH compatibility contract: which upstream releases this plugin is
|
|
@@ -40,7 +42,14 @@ const SUPPORTED_DSH_RELEASES = [
|
|
|
40
42
|
"0.1.7-rc.1",
|
|
41
43
|
"0.1.7-rc.2",
|
|
42
44
|
"0.2.0-rc.1",
|
|
43
|
-
"0.2.0-rc.2"
|
|
45
|
+
"0.2.0-rc.2",
|
|
46
|
+
"0.2.1-alpha.1",
|
|
47
|
+
"0.2.1-alpha.2",
|
|
48
|
+
"0.2.1-alpha.3",
|
|
49
|
+
"0.2.1-alpha.4",
|
|
50
|
+
"0.2.1-alpha.5",
|
|
51
|
+
"0.2.1-rc.1",
|
|
52
|
+
"0.2.1-rc.2"
|
|
44
53
|
];
|
|
45
54
|
/**
|
|
46
55
|
* Packages whose absence means this plugin cannot function at all: every one of
|
|
@@ -61,6 +70,13 @@ const REQUIRED_CORE_PACKAGES = [
|
|
|
61
70
|
* that is the mixed-installation failure this contract exists to catch.
|
|
62
71
|
*/
|
|
63
72
|
const OPTIONAL_CORE_PACKAGES = [
|
|
73
|
+
"@deepseek-ai/dsh-tools",
|
|
74
|
+
"@deepseek-ai/dsh-working-directory",
|
|
75
|
+
"@deepseek-ai/dsh-lsp",
|
|
76
|
+
"@deepseek-ai/dsh-lsp-stdio",
|
|
77
|
+
"@deepseek-ai/dsh-api-workspace-files",
|
|
78
|
+
"@deepseek-ai/dsh-typert-protocol",
|
|
79
|
+
"@deepseek-ai/dsh-sandbox-windows-acl",
|
|
64
80
|
"@deepseek-ai/dsh-agent",
|
|
65
81
|
"@deepseek-ai/dsh-agent-instructions",
|
|
66
82
|
"@deepseek-ai/dsh-client-connection",
|
|
@@ -72,6 +88,8 @@ const OPTIONAL_CORE_PACKAGES = [
|
|
|
72
88
|
];
|
|
73
89
|
/** Every package the contract inspects, required first. */
|
|
74
90
|
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"];
|
|
75
93
|
/** The environment variable that relaxes the gate. */
|
|
76
94
|
const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
77
95
|
/**
|
|
@@ -88,11 +106,18 @@ const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
|
88
106
|
* manifest cannot be resolved.
|
|
89
107
|
*/
|
|
90
108
|
function readInstalledVersion(packageName) {
|
|
109
|
+
const require = createRequire(import.meta.url);
|
|
91
110
|
try {
|
|
92
|
-
const manifest =
|
|
111
|
+
const manifest = require(`${packageName}/package.json`);
|
|
93
112
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
94
113
|
} catch {
|
|
95
|
-
return;
|
|
114
|
+
if (!HOST_RESOLVED_PACKAGES.includes(packageName)) return void 0;
|
|
115
|
+
try {
|
|
116
|
+
const manifest = createRequire(require.resolve("@deepseek-ai/dsh/package.json"))(`${packageName}/package.json`);
|
|
117
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
118
|
+
} catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
/**
|
|
@@ -174,6 +199,156 @@ function report(verdict, release, packages, lines) {
|
|
|
174
199
|
};
|
|
175
200
|
}
|
|
176
201
|
//#endregion
|
|
202
|
+
//#region src/compat/pty-marker.ts
|
|
203
|
+
/**
|
|
204
|
+
* Boot-time probe for the agent-PTY marker contract.
|
|
205
|
+
*
|
|
206
|
+
* `multi-root-process-fence` tells an agent terminal from the user's own by
|
|
207
|
+
* one env fact: every supported `dsh-terminal-bash` backend stamps
|
|
208
|
+
* `DSH_PTY_SESSION_ID` (alongside `DSH_SESSION_ID`) into the PTY spawn spec,
|
|
209
|
+
* while the panel's terminal controller stamps only `DSH_SESSION_ID`. That
|
|
210
|
+
* marker is the ONLY discriminator — a backend that ever stops stamping it
|
|
211
|
+
* would reclassify agent PTYs as user terminals and pass them through
|
|
212
|
+
* unconfined under a restricted `danger-full-access`, which is a silent
|
|
213
|
+
* fail-open on a security boundary.
|
|
214
|
+
*
|
|
215
|
+
* The marker is an env key inside upstream's emitted code, not an exported
|
|
216
|
+
* symbol, so the honest structural probe is content: the installed backend
|
|
217
|
+
* package must still write the marker. This module is called from the
|
|
218
|
+
* `multi-root-compat` gate (`src/compat.ts`) BEFORE the service registers —
|
|
219
|
+
* like the version verdict it is a precondition, not a diagnostic, and a
|
|
220
|
+
* breach refuses the composition the same way `mixed`/`incomplete` do: it is
|
|
221
|
+
* not relaxable by `DSH_MULTI_ROOT_COMPAT=warn`.
|
|
222
|
+
*
|
|
223
|
+
* Resolution follows the optional-peer precedent (`agent-instructions.ts`):
|
|
224
|
+
* the backend is not a peer of this plugin, so the plugin anchor is tried
|
|
225
|
+
* first (flat layouts) and the `@deepseek-ai/dsh` host anchor second — the
|
|
226
|
+
* same `HOST_RESOLVED_PACKAGES` pattern as `dsh-version.ts` — because the
|
|
227
|
+
* backend rides inside the harness meta package's own dependency tree.
|
|
228
|
+
*
|
|
229
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/pty-marker
|
|
230
|
+
*/
|
|
231
|
+
/** The env key that identifies an agent-launched PTY spawn spec. */
|
|
232
|
+
const AGENT_PTY_MARKER = "DSH_PTY_SESSION_ID";
|
|
233
|
+
/** The upstream package whose backend owns the marker stamp. */
|
|
234
|
+
const TERMINAL_BACKEND = "@deepseek-ai/dsh-terminal-bash";
|
|
235
|
+
/** The harness meta package the backend rides inside (its dependency). */
|
|
236
|
+
const HOST_ANCHOR = "@deepseek-ai/dsh/package.json";
|
|
237
|
+
/** Scan budget: enough for any plausible backend layout, bounded forever. */
|
|
238
|
+
const MAX_SOURCE_FILES = 256;
|
|
239
|
+
const MAX_TOTAL_BYTES = 8388608;
|
|
240
|
+
/**
|
|
241
|
+
* Raised when the installed terminal backend does not uphold the marker
|
|
242
|
+
* contract. Thrown from `apply` like `DshCompatUnsupportedError`, so it
|
|
243
|
+
* surfaces as a plugin load failure with the diagnosis attached.
|
|
244
|
+
*/
|
|
245
|
+
var PtyMarkerContractError = class extends Error {
|
|
246
|
+
/** The resolved backend path when one was found. */
|
|
247
|
+
backend;
|
|
248
|
+
constructor(message, backend) {
|
|
249
|
+
super(message);
|
|
250
|
+
this.name = "PtyMarkerContractError";
|
|
251
|
+
this.backend = backend;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Resolve the backend package root through the two anchors the contract can
|
|
256
|
+
* reach: this module first (flat layouts where the backend is a direct
|
|
257
|
+
* install), then the `@deepseek-ai/dsh` host manifest (pnpm-style layouts
|
|
258
|
+
* where it is the meta package's own dependency — the
|
|
259
|
+
* `HOST_RESOLVED_PACKAGES` precedent). A resolution failure that is not a
|
|
260
|
+
* genuine NOT_FOUND propagates: a resolvable-but-broken specifier is not
|
|
261
|
+
* evidence of absence.
|
|
262
|
+
*/
|
|
263
|
+
function resolveBackendRoot() {
|
|
264
|
+
const self = createRequire(import.meta.url);
|
|
265
|
+
const anchors = [self];
|
|
266
|
+
try {
|
|
267
|
+
anchors.push(createRequire(self.resolve(HOST_ANCHOR)));
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (!isNotFound(error)) throw error;
|
|
270
|
+
}
|
|
271
|
+
for (const anchor of anchors) try {
|
|
272
|
+
return dirname(anchor.resolve(`${TERMINAL_BACKEND}/package.json`));
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (!isNotFound(error)) throw error;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function isNotFound(error) {
|
|
278
|
+
const code = error?.code;
|
|
279
|
+
return code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
|
|
280
|
+
}
|
|
281
|
+
/** Recursively collect `.js` sources under the package root, within budget. */
|
|
282
|
+
function listSources(root) {
|
|
283
|
+
const files = [];
|
|
284
|
+
const visit = (dir) => {
|
|
285
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
286
|
+
if (files.length >= MAX_SOURCE_FILES) return;
|
|
287
|
+
const path = join(dir, entry.name);
|
|
288
|
+
if (entry.isDirectory() && entry.name !== "node_modules") visit(path);
|
|
289
|
+
else if (entry.isFile() && entry.name.endsWith(".js")) files.push(path);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
visit(root);
|
|
293
|
+
return files;
|
|
294
|
+
}
|
|
295
|
+
const defaultIo = {
|
|
296
|
+
resolveBackendRoot,
|
|
297
|
+
listSources,
|
|
298
|
+
read: (path) => readFileSync(path, "utf8")
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Judge the installed terminal backend against the marker contract.
|
|
302
|
+
*
|
|
303
|
+
* Never throws: every outcome — absent package, unreadable source, missing
|
|
304
|
+
* marker — is folded into the verdict so the caller's reaction is a policy
|
|
305
|
+
* decision in one place.
|
|
306
|
+
* @param io - filesystem seams; tests inject them, production uses real fs.
|
|
307
|
+
* @returns the verdict.
|
|
308
|
+
*/
|
|
309
|
+
function agentPtyMarkerVerdict(io = defaultIo) {
|
|
310
|
+
let root;
|
|
311
|
+
try {
|
|
312
|
+
root = io.resolveBackendRoot();
|
|
313
|
+
} catch {
|
|
314
|
+
return "unverifiable";
|
|
315
|
+
}
|
|
316
|
+
if (root === void 0) return "uninstalled";
|
|
317
|
+
let files;
|
|
318
|
+
try {
|
|
319
|
+
files = io.listSources(root);
|
|
320
|
+
} catch {
|
|
321
|
+
return "unverifiable";
|
|
322
|
+
}
|
|
323
|
+
let remaining = MAX_TOTAL_BYTES;
|
|
324
|
+
for (const file of files) {
|
|
325
|
+
let content;
|
|
326
|
+
try {
|
|
327
|
+
content = io.read(file);
|
|
328
|
+
} catch {
|
|
329
|
+
return "unverifiable";
|
|
330
|
+
}
|
|
331
|
+
if (content.length > remaining) return "unverifiable";
|
|
332
|
+
if (content.includes("DSH_PTY_SESSION_ID")) return "stamped";
|
|
333
|
+
remaining -= content.length;
|
|
334
|
+
}
|
|
335
|
+
return "unverifiable";
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Enforce the contract: refuse when the marker cannot be confirmed.
|
|
339
|
+
*
|
|
340
|
+
* @param io - filesystem seams; tests inject them.
|
|
341
|
+
* @throws {PtyMarkerContractError} on `unverifiable`.
|
|
342
|
+
*/
|
|
343
|
+
function assertAgentPtyMarker(io = defaultIo) {
|
|
344
|
+
if (agentPtyMarkerVerdict(io) !== "unverifiable") return;
|
|
345
|
+
let backend;
|
|
346
|
+
try {
|
|
347
|
+
backend = io.resolveBackendRoot();
|
|
348
|
+
} catch {}
|
|
349
|
+
throw new PtyMarkerContractError(`multi-root workspace: the installed ${TERMINAL_BACKEND} does not stamp ${AGENT_PTY_MARKER} on agent PTY spawns, or its sources cannot be verified. That marker is the only fact that separates an agent terminal from the user's own — without it the process fence cannot confine a restricted danger-full-access agent PTY without confining the user's terminal too. The plugin refuses to run rather than let agent terminals pass unconfined. See docs/decisions/ADR-0017-three-state-root-access-and-sandbox-modes.md.`, backend);
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
177
352
|
//#region src/compat.ts
|
|
178
353
|
/**
|
|
179
354
|
* `multi-root-compat`: the startup gate that turns the DSH compatibility
|
|
@@ -271,7 +446,8 @@ function apply(ctx) {
|
|
|
271
446
|
const report = classifyInstallation();
|
|
272
447
|
const warning = assertSupportedInstallation(report, compatEnforcement());
|
|
273
448
|
if (warning !== void 0) ctx.logger.warn(warning);
|
|
449
|
+
assertAgentPtyMarker();
|
|
274
450
|
ctx.plugin(MultiRootCompatService, report);
|
|
275
451
|
}
|
|
276
452
|
//#endregion
|
|
277
|
-
export { inject as a,
|
|
453
|
+
export { inject as a, agentPtyMarkerVerdict as c, OPTIONAL_CORE_PACKAGES as d, REQUIRED_CORE_PACKAGES as f, readInstalledVersion as g, compatEnforcement as h, assertSupportedInstallation as i, assertAgentPtyMarker as l, classifyInstallation as m, MultiRootCompatService as n, AGENT_PTY_MARKER as o, SUPPORTED_DSH_RELEASES as p, apply as r, PtyMarkerContractError as s, DshCompatUnsupportedError as t, CORE_PACKAGES as u };
|
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-DF_yL1bB.js";
|
|
2
2
|
export { DshCompatUnsupportedError, MultiRootCompatService, apply, assertSupportedInstallation, inject };
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/contract.ts
|
|
3
|
+
/**
|
|
4
|
+
* The wire contract between the browser panel and this plugin's host half.
|
|
5
|
+
*
|
|
6
|
+
* It is deliberately transport-agnostic plain data: the host registers one
|
|
7
|
+
* Connection RPC channel (see `command.ts`) and the client bundle inlines this
|
|
8
|
+
* module, so both sides agree on endpoint names and payload shapes without
|
|
9
|
+
* sharing any runtime identity. Error codes are the stable half of the
|
|
10
|
+
* contract; `message` is only a fallback the panel does not have to show.
|
|
11
|
+
*
|
|
12
|
+
* Two properties this module owns:
|
|
13
|
+
*
|
|
14
|
+
* - **One shape per endpoint.** {@link PanelResponseMap} is the single mapping
|
|
15
|
+
* from endpoint to response type, so the panel cannot silently decode one
|
|
16
|
+
* endpoint's answer as another's. `reveal` answers with
|
|
17
|
+
* {@link RevealedView}; root-management endpoints answer with
|
|
18
|
+
* {@link RootsView}.
|
|
19
|
+
* The host builds those answers in `command.ts` and the
|
|
20
|
+
* panel parses them here — one definition, two consumers.
|
|
21
|
+
* - **Runtime validation at the boundary.** The payload arrives from a browser
|
|
22
|
+
* and the answer arrives from the host; neither is trusted to match the
|
|
23
|
+
* TypeScript types. {@link parsePanelCall} and the `parse*View` functions
|
|
24
|
+
* check the shapes and report structural failures as a message, so a
|
|
25
|
+
* mismatch becomes a visible, localized error instead of a crash or a
|
|
26
|
+
* silently wrong render.
|
|
27
|
+
*
|
|
28
|
+
* Nothing here may import `node:*` or `@deepseek-ai/*`: this module is inlined
|
|
29
|
+
* into the browser bundle.
|
|
30
|
+
*
|
|
31
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/contract
|
|
32
|
+
*/
|
|
33
|
+
/** The absolute logical channel both halves speak over. */
|
|
34
|
+
const PANEL_CHANNEL = "/multi-root-workspace";
|
|
35
|
+
/** Every endpoint name, for exhaustive iteration in tests and callers. */
|
|
36
|
+
const PANEL_ENDPOINTS = [
|
|
37
|
+
"list",
|
|
38
|
+
"add",
|
|
39
|
+
"remove",
|
|
40
|
+
"alias",
|
|
41
|
+
"move",
|
|
42
|
+
"setAccess",
|
|
43
|
+
"reveal"
|
|
44
|
+
];
|
|
45
|
+
/** The shared nine-cell rule; tool dispatch may subsequently narrow writes. */
|
|
46
|
+
function effectiveRootAccess(access, mode) {
|
|
47
|
+
if (access !== "inherit") return access;
|
|
48
|
+
return mode === "danger-full-access" ? "full-access" : mode === "workspace-write" ? "read-write" : "read-only";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The request body on the wire. The endpoint travels as the RPC method, not as
|
|
52
|
+
* a field, so this schema covers the fields only; the host pairs it with the
|
|
53
|
+
* method the channel dispatched (and rejects the pair if the body contradicts
|
|
54
|
+
* it). Unknown keys are rejected rather than ignored: a client sending a field
|
|
55
|
+
* this host does not understand is out of contract, and silently dropping it
|
|
56
|
+
* would hide the drift.
|
|
57
|
+
*/
|
|
58
|
+
const panelRequestSchema = z.object({
|
|
59
|
+
sessionId: z.string().min(1),
|
|
60
|
+
entry: z.object({
|
|
61
|
+
ordinal: z.number().int().positive(),
|
|
62
|
+
id: z.string(),
|
|
63
|
+
path: z.string(),
|
|
64
|
+
addedAt: z.string()
|
|
65
|
+
}).strict().optional(),
|
|
66
|
+
id: z.string().optional(),
|
|
67
|
+
path: z.string().optional(),
|
|
68
|
+
alias: z.string().optional(),
|
|
69
|
+
access: z.enum([
|
|
70
|
+
"inherit",
|
|
71
|
+
"read-write",
|
|
72
|
+
"read-only"
|
|
73
|
+
]).optional(),
|
|
74
|
+
beforeEntry: z.object({
|
|
75
|
+
ordinal: z.number().int().positive(),
|
|
76
|
+
id: z.string(),
|
|
77
|
+
path: z.string(),
|
|
78
|
+
addedAt: z.string()
|
|
79
|
+
}).strict().optional(),
|
|
80
|
+
beforeId: z.string().optional()
|
|
81
|
+
}).strict();
|
|
82
|
+
/**
|
|
83
|
+
* One root as it travels over the channel. Deliberately NOT strict, in both
|
|
84
|
+
* directions: a newer host that adds a field must not brick an older panel, and
|
|
85
|
+
* the field that matters is the state it reports.
|
|
86
|
+
*/
|
|
87
|
+
const rootViewSchema = z.object({
|
|
88
|
+
ordinal: z.number().int().positive(),
|
|
89
|
+
id: z.string(),
|
|
90
|
+
path: z.string(),
|
|
91
|
+
alias: z.string().optional(),
|
|
92
|
+
addedAt: z.string(),
|
|
93
|
+
state: z.enum([
|
|
94
|
+
"available",
|
|
95
|
+
"missing",
|
|
96
|
+
"redirected",
|
|
97
|
+
"invalid"
|
|
98
|
+
]),
|
|
99
|
+
access: z.enum([
|
|
100
|
+
"inherit",
|
|
101
|
+
"read-write",
|
|
102
|
+
"read-only"
|
|
103
|
+
]).optional(),
|
|
104
|
+
detail: z.string().optional(),
|
|
105
|
+
effectiveAccess: z.enum([
|
|
106
|
+
"read-only",
|
|
107
|
+
"read-write",
|
|
108
|
+
"full-access",
|
|
109
|
+
"unavailable"
|
|
110
|
+
]).optional()
|
|
111
|
+
});
|
|
112
|
+
const rootsViewSchema = z.object({
|
|
113
|
+
primaryRoot: z.string(),
|
|
114
|
+
primaryName: z.string().optional(),
|
|
115
|
+
effectiveCwd: z.string().optional(),
|
|
116
|
+
mode: z.enum([
|
|
117
|
+
"read-only",
|
|
118
|
+
"workspace-write",
|
|
119
|
+
"danger-full-access"
|
|
120
|
+
]).optional(),
|
|
121
|
+
cappedTools: z.array(z.string()).optional(),
|
|
122
|
+
toolAccessInvalid: z.boolean().optional(),
|
|
123
|
+
roots: z.array(rootViewSchema),
|
|
124
|
+
limit: z.number().int().nonnegative().optional(),
|
|
125
|
+
count: z.number().int().nonnegative().optional(),
|
|
126
|
+
unavailable: z.string().optional()
|
|
127
|
+
});
|
|
128
|
+
const revealedViewSchema = z.object({ revealed: z.string() });
|
|
129
|
+
z.object({
|
|
130
|
+
code: z.string(),
|
|
131
|
+
message: z.string(),
|
|
132
|
+
details: z.unknown().optional()
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* Validate the payload the host received for one endpoint.
|
|
136
|
+
* @param endpoint - the endpoint the channel dispatched (the RPC method).
|
|
137
|
+
* @param payload - the raw channel payload (the request body).
|
|
138
|
+
* @returns the call the host may act on, or the reason it is not one.
|
|
139
|
+
*/
|
|
140
|
+
function parsePanelCall(endpoint, payload) {
|
|
141
|
+
if (!PANEL_ENDPOINTS.includes(endpoint)) return {
|
|
142
|
+
ok: false,
|
|
143
|
+
message: `unknown endpoint "${endpoint}"`
|
|
144
|
+
};
|
|
145
|
+
const parsed = panelRequestSchema.safeParse(payload ?? {});
|
|
146
|
+
if (!parsed.success) return {
|
|
147
|
+
ok: false,
|
|
148
|
+
message: `the request body is invalid: ${firstIssue(parsed.error)}`
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
value: narrowCall(endpoint, parsed.data)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validate a `RootsView` when it arrives.
|
|
157
|
+
* @param value - the raw endpoint value.
|
|
158
|
+
* @returns the view, or the reason it is not one.
|
|
159
|
+
*/
|
|
160
|
+
function parseRootsView(value) {
|
|
161
|
+
const parsed = rootsViewSchema.safeParse(value);
|
|
162
|
+
if (!parsed.success) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
message: `the answer is not a roots view: ${firstIssue(parsed.error)}`
|
|
165
|
+
};
|
|
166
|
+
const view = parsed.data;
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
value: {
|
|
170
|
+
primaryRoot: view.primaryRoot,
|
|
171
|
+
...view.primaryName === void 0 ? {} : { primaryName: view.primaryName },
|
|
172
|
+
...view.effectiveCwd === void 0 ? {} : { effectiveCwd: view.effectiveCwd },
|
|
173
|
+
...view.mode === void 0 ? {} : { mode: view.mode },
|
|
174
|
+
...view.cappedTools === void 0 ? {} : { cappedTools: view.cappedTools },
|
|
175
|
+
...view.toolAccessInvalid === void 0 ? {} : { toolAccessInvalid: view.toolAccessInvalid },
|
|
176
|
+
roots: view.roots.map(narrowRootView),
|
|
177
|
+
limit: view.limit ?? Number.MAX_SAFE_INTEGER,
|
|
178
|
+
count: view.count ?? view.roots.length,
|
|
179
|
+
...view.unavailable === void 0 ? {} : { unavailable: view.unavailable }
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Validate a `RevealedView` when it arrives.
|
|
185
|
+
* @param value - the raw endpoint value.
|
|
186
|
+
* @returns the view, or the reason it is not one.
|
|
187
|
+
*/
|
|
188
|
+
function parseRevealedView(value) {
|
|
189
|
+
const parsed = revealedViewSchema.safeParse(value);
|
|
190
|
+
if (!parsed.success) return {
|
|
191
|
+
ok: false,
|
|
192
|
+
message: `the answer is not a reveal result: ${firstIssue(parsed.error)}`
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
ok: true,
|
|
196
|
+
value: parsed.data
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Rebuild a parsed call as the declared type, keeping absent optional fields
|
|
201
|
+
* ABSENT (`exactOptionalPropertyTypes` distinguishes "absent" from "undefined",
|
|
202
|
+
* and the host's own code reads the two the same way the panel sent them).
|
|
203
|
+
*/
|
|
204
|
+
function narrowCall(endpoint, value) {
|
|
205
|
+
return {
|
|
206
|
+
endpoint,
|
|
207
|
+
sessionId: value.sessionId,
|
|
208
|
+
...value.entry === void 0 ? {} : { entry: value.entry },
|
|
209
|
+
...value.id === void 0 ? {} : { id: value.id },
|
|
210
|
+
...value.path === void 0 ? {} : { path: value.path },
|
|
211
|
+
...value.alias === void 0 ? {} : { alias: value.alias },
|
|
212
|
+
...value.access === void 0 ? {} : { access: value.access },
|
|
213
|
+
...value.beforeEntry === void 0 ? {} : { beforeEntry: value.beforeEntry },
|
|
214
|
+
...value.beforeId === void 0 ? {} : { beforeId: value.beforeId }
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Rebuild one parsed root as the declared `RootView`. */
|
|
218
|
+
function narrowRootView(value) {
|
|
219
|
+
return {
|
|
220
|
+
ordinal: value.ordinal,
|
|
221
|
+
id: value.id,
|
|
222
|
+
path: value.path,
|
|
223
|
+
addedAt: value.addedAt,
|
|
224
|
+
state: value.state,
|
|
225
|
+
access: value.access ?? "inherit",
|
|
226
|
+
...value.effectiveAccess === void 0 ? {} : { effectiveAccess: value.effectiveAccess },
|
|
227
|
+
...value.alias === void 0 ? {} : { alias: value.alias },
|
|
228
|
+
...value.detail === void 0 ? {} : { detail: value.detail }
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/** Render the first zod issue as one short sentence. */
|
|
232
|
+
function firstIssue(error) {
|
|
233
|
+
const issue = error.issues[0];
|
|
234
|
+
if (issue === void 0) return "invalid";
|
|
235
|
+
return `${issue.path.length === 0 ? "" : `${issue.path.join(".")}: `}${issue.message}`;
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
export { parseRevealedView as a, parsePanelCall as i, PANEL_ENDPOINTS as n, parseRootsView as o, effectiveRootAccess as r, PANEL_CHANNEL as t };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandMultiRootProvider } from "./providers.js";
|
|
1
2
|
import { dirname, sep } from "node:path";
|
|
2
3
|
import { writableRoots } from "@deepseek-ai/dsh-sandbox";
|
|
3
4
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
@@ -72,6 +73,7 @@ var MultiRootFileSystem = class extends LocalFileSystem {
|
|
|
72
73
|
defaultMode;
|
|
73
74
|
constructor(ctx, config) {
|
|
74
75
|
super(ctx, config);
|
|
76
|
+
brandMultiRootProvider(this, "fs");
|
|
75
77
|
this.defaultMode = ctx.sandboxPolicy.defaultMode;
|
|
76
78
|
}
|
|
77
79
|
/** The deployment default mode — the capability fact the tool layer reads to advertise escalation. */
|
|
@@ -107,47 +109,81 @@ var MultiRootFileSystem = class extends LocalFileSystem {
|
|
|
107
109
|
return super.editText(await this.checkedTarget(target, sandboxPolicy), edit, expected, signal);
|
|
108
110
|
}
|
|
109
111
|
/**
|
|
110
|
-
* The writable roots of one
|
|
111
|
-
*
|
|
112
|
-
* widened by the scope's
|
|
112
|
+
* The writable roots of one confined call: the upstream derivation (primary
|
|
113
|
+
* root plus the platform temp areas, canonical and deduplicated — empty
|
|
114
|
+
* under `read-only`) widened by the scope's EFFECTIVE writable members.
|
|
115
|
+
* Under `read-only` that is exactly the fixed `read-write` roots (the tool
|
|
116
|
+
* cap may already have emptied the scope's list), so an explicit grant
|
|
117
|
+
* survives a globally read-only session. An unavailable registry authority
|
|
118
|
+
* never contributes members: a process that cannot read the store grants
|
|
119
|
+
* only the primary root (and no shared temp grant).
|
|
113
120
|
* @param policy - the per-call policy.
|
|
114
121
|
* @param scope - the scope resolved once for this call; both the fence and
|
|
115
122
|
* the denial message below share it instead of re-resolving.
|
|
116
|
-
* @returns the canonical writable roots; empty
|
|
123
|
+
* @returns the canonical writable roots; possibly empty.
|
|
117
124
|
*/
|
|
118
125
|
rootsFor(policy, scope) {
|
|
126
|
+
if (scope.authority === "unavailable") return policy.mode === "read-only" ? [] : [scope.primaryRoot];
|
|
119
127
|
const roots = writableRoots(policy);
|
|
120
|
-
|
|
121
|
-
for (const root of scope.additionalRoots) if (!roots.includes(root)) roots.push(root);
|
|
128
|
+
for (const root of scope.writableRoots) if (!roots.includes(root)) roots.push(root);
|
|
122
129
|
return roots;
|
|
123
130
|
}
|
|
124
131
|
/**
|
|
125
|
-
* Enforce the per-call policy against `target` and return the EXACT target
|
|
126
|
-
* mutation must use, so the checked identity is the mutated one (no
|
|
127
|
-
* check-here-write-there TOCTOU).
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
132
|
+
* Enforce the per-call policy against `target` and return the EXACT target
|
|
133
|
+
* the mutation must use, so the checked identity is the mutated one (no
|
|
134
|
+
* check-here-write-there TOCTOU). Deny precedes containment: a root in
|
|
135
|
+
* `scope.deniedRoots` — an explicit `read-only` root, or any member when the
|
|
136
|
+
* call is tool-capped — refuses writes under EVERY mode, including
|
|
137
|
+
* `danger-full-access` and where a broader grant (the platform temp area)
|
|
138
|
+
* would otherwise cover the path. `read-only` then grants only the fixed
|
|
139
|
+
* `read-write` members; `workspace-write` requires containment under the
|
|
140
|
+
* widened writable set.
|
|
141
|
+
*
|
|
142
|
+
* `danger-full-access` is the only mode with a fast path, and only while
|
|
143
|
+
* the deny set is empty: it then returns the caller's target untouched,
|
|
144
|
+
* upstream-exact. Once ANY deny boundary exists the caller's `FsTarget` may
|
|
145
|
+
* be stale — the directory it resolved through can since have been replaced
|
|
146
|
+
* by a symlink into a denied root — so the fence re-resolves
|
|
147
|
+
* `target.displayPath`, checks the FRESH target key, and mutates that fresh
|
|
148
|
+
* target rather than the stale one the caller resolved earlier.
|
|
149
|
+
*
|
|
150
|
+
* One more Full-access rule: while the registry authority is `unavailable`
|
|
151
|
+
* the empty deny set is not trustworthy — the store this process cannot
|
|
152
|
+
* read may hold explicit `read-only` registrations — so the mode degrades
|
|
153
|
+
* to primary-root-only writes (no shared temp or additional-root grants)
|
|
154
|
+
* until the authority returns.
|
|
131
155
|
* @param target - the resolved target about to be mutated.
|
|
132
156
|
* @param sandboxPolicy - the per-call policy; omit for the deployment fallback.
|
|
133
157
|
* @returns the fresh canonical target the mutation must use.
|
|
134
158
|
*/
|
|
135
159
|
async checkedTarget(target, sandboxPolicy) {
|
|
136
160
|
const policy = sandboxPolicy ?? this.ctx.sandboxPolicy.resolve();
|
|
137
|
-
const { mode } = policy;
|
|
138
|
-
if (mode === "danger-full-access") return target;
|
|
139
|
-
if (mode === "read-only") throw new FsError(`cannot write "${target.displayPath}": file access denied under read-only mode`, "FS_SANDBOX_DENIED");
|
|
140
|
-
const fresh = await this.resolve(target.displayPath);
|
|
141
161
|
const scope = this.ctx.multiRootScope.resolve(policy);
|
|
142
|
-
const
|
|
162
|
+
const effectiveMode = scope.authority === "unavailable" && policy.mode === "danger-full-access" ? "workspace-write" : policy.mode;
|
|
163
|
+
if (effectiveMode === "danger-full-access" && scope.deniedRoots.length === 0) return target;
|
|
164
|
+
const fresh = await this.resolve(target.displayPath);
|
|
165
|
+
if (scope.deniedRoots.length > 0) {
|
|
166
|
+
for (const path of scope.deniedRoots) if (await isPathUnder(fresh.targetKey, path)) {
|
|
167
|
+
const root = scope.roots.find((root) => root.path === path);
|
|
168
|
+
const reason = path === scope.protectedHome ? `${path} is the DSH home directory, which is protected under danger-full-access while workspace restrictions apply` : root?.access === "read-only" || root === void 0 ? `${path} is a read-only additional root` : `${path} is an additional root this call may not write`;
|
|
169
|
+
throw new FsError(`cannot write "${target.displayPath}": ${reason}`, "FS_SANDBOX_DENIED");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (effectiveMode === "danger-full-access") return fresh;
|
|
173
|
+
const effectivePolicy = {
|
|
174
|
+
...policy,
|
|
175
|
+
mode: effectiveMode
|
|
176
|
+
};
|
|
177
|
+
const roots = this.rootsFor(effectivePolicy, scope);
|
|
143
178
|
let contained = false;
|
|
144
179
|
for (const root of roots) if (await isPathUnder(fresh.targetKey, root)) {
|
|
145
180
|
contained = true;
|
|
146
181
|
break;
|
|
147
182
|
}
|
|
148
183
|
if (!contained) {
|
|
149
|
-
|
|
150
|
-
|
|
184
|
+
let denial = `cannot write "${target.displayPath}": file access denied under ${effectiveMode} mode`;
|
|
185
|
+
if (scope.additionalRoots.length > 0) denial += `; allowed roots: ${roots.join(", ")}`;
|
|
186
|
+
throw new FsError(denial, "FS_SANDBOX_DENIED");
|
|
151
187
|
}
|
|
152
188
|
return fresh;
|
|
153
189
|
}
|
package/lib/fs.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { c as
|
|
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-
|
|
3
|
-
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-
|
|
4
|
-
import { a as classifyStoredRoots, c as indexedStatuses,
|
|
5
|
-
import {
|
|
1
|
+
import { c as agentPtyMarkerVerdict, d as OPTIONAL_CORE_PACKAGES, f as REQUIRED_CORE_PACKAGES, g as readInstalledVersion, h as compatEnforcement, i as assertSupportedInstallation, l as assertAgentPtyMarker, m as classifyInstallation, n as MultiRootCompatService, o as AGENT_PTY_MARKER, p as SUPPORTED_DSH_RELEASES, s as PtyMarkerContractError, t as DshCompatUnsupportedError, u as CORE_PACKAGES } from "./compat-DF_yL1bB.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-D19jRxN0.js";
|
|
3
|
+
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-Kup4_ZIb.js";
|
|
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
|
+
import { MULTI_ROOT_PROVIDER_BRAND, MultiRootProviders, brandMultiRootProvider, isMultiRootProvider } from "./providers.js";
|
|
6
|
+
import { t as MultiRootFileSystem } from "./fs-9JIzGaf3.js";
|
|
7
|
+
import { a as parseRevealedView, i as parsePanelCall, n as PANEL_ENDPOINTS, o as parseRootsView, t as PANEL_CHANNEL } from "./contract-DZ4zczDT.js";
|
|
6
8
|
import { MultiRootScopeService, renderWorkspaceRootsContext, sanitizeAdditionalRoots } from "./scope.js";
|
|
7
|
-
import { c as
|
|
8
|
-
import { a as renderRootsReport, c as
|
|
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";
|
|
10
|
+
import { a as renderRootsReport, c as revealArgv, i as parseFoldersCommand, o as resolvePanelPrimaryRoot, t as COMMAND_NAME } from "./command-CESuwlVt.js";
|
|
9
11
|
//#region src/index.ts
|
|
10
12
|
/**
|
|
11
13
|
* Carrier plugin face for the bare-package-name loader row (see the module
|
|
@@ -16,4 +18,4 @@ import { a as renderRootsReport, c as PANEL_CHANNEL, d as parseRevealedView, f a
|
|
|
16
18
|
const inject = [];
|
|
17
19
|
function apply() {}
|
|
18
20
|
//#endregion
|
|
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 };
|
|
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 };
|