@dsh-electron/dsh-plugin-multi-root-workspace 0.1.4 → 0.1.6
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 +19 -2
- package/CHANGELOG.md +19 -2
- package/README.en.md +17 -13
- package/README.md +16 -14
- package/cordis.patch.yml +4 -0
- package/lib/client.js +320 -18
- package/lib/client.js.map +1 -1
- package/lib/{command-DyOWujPo.js → command-CTdNvwt1.js} +107 -6
- package/lib/command.js +1 -1
- package/lib/{compat-PNEKXGa6.js → compat-B6ZqbQBy.js} +23 -3
- package/lib/compat.js +1 -1
- package/lib/index.js +5 -5
- package/lib/{instructions-Cn1Jj5Xi.js → instructions-DfzN-wVf.js} +106 -26
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +38 -0
- package/lib/method-wrapper-Zc-JlDP3.js +51 -0
- package/lib/sandbox-BRXl_FpF.js +132 -0
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +12 -6
- package/lib/session-directory-BLOstut1.js +9 -0
- package/lib/tool-context-CpgIFcCB.js +6 -0
- package/lib/types/client/RootFileTree.d.ts +12 -0
- package/lib/types/client/WorkspaceFoldersAction.d.ts +3 -0
- package/lib/types/client/index.d.ts +13 -2
- package/lib/types/client/locales.d.ts +14 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/compat/client-icons.d.ts +1 -1
- 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/session-directory.d.ts +4 -0
- package/lib/types/compat/tool-context.d.ts +9 -0
- package/lib/types/compat/tool-result.d.ts +1 -1
- package/lib/types/compat/workspace-files.d.ts +5 -0
- package/lib/types/contract.d.ts +24 -5
- package/lib/types/index.d.ts +2 -2
- package/lib/types/lsp.d.ts +4 -0
- package/lib/types/method-wrapper.d.ts +2 -0
- package/lib/types/panel-files.d.ts +3 -0
- package/lib/types/sandbox.d.ts +2 -11
- package/lib/types/scope.d.ts +3 -0
- package/lib/types/windows-profile.d.ts +13 -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/{sandbox-D8Fw95FM.js → windows-profile-CiFsFYIV.js} +64 -138
- package/lib/windows-runner.js +68 -0
- package/lib/workspace-files.js +68 -0
- package/package.json +90 -47
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as effectiveDirectory } from "./session-directory-BLOstut1.js";
|
|
2
|
+
import { f as resolveRootRef, i as canonicalRoot, l as isCanonicallyUnder, o as entryRootRef, r as availableRoots, t as RootValidationError } from "./roots-DasWEBJk.js";
|
|
2
3
|
import "@deepseek-ai/cordis";
|
|
4
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
5
|
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
4
6
|
import { z } from "zod";
|
|
5
7
|
//#region src/contract.ts
|
|
@@ -17,8 +19,9 @@ import { z } from "zod";
|
|
|
17
19
|
* - **One shape per endpoint.** {@link PanelResponseMap} is the single mapping
|
|
18
20
|
* from endpoint to response type, so the panel cannot silently decode one
|
|
19
21
|
* endpoint's answer as another's. `reveal` answers with
|
|
20
|
-
* {@link RevealedView};
|
|
21
|
-
* {@link RootsView}
|
|
22
|
+
* {@link RevealedView}; root-management endpoints answer with
|
|
23
|
+
* {@link RootsView}, and browsing returns {@link FilesView} or {@link FilePreview}.
|
|
24
|
+
* The host builds those answers in `command.ts` and the
|
|
22
25
|
* panel parses them here — one definition, two consumers.
|
|
23
26
|
* - **Runtime validation at the boundary.** The payload arrives from a browser
|
|
24
27
|
* and the answer arrives from the host; neither is trusted to match the
|
|
@@ -41,7 +44,9 @@ const PANEL_ENDPOINTS = [
|
|
|
41
44
|
"remove",
|
|
42
45
|
"alias",
|
|
43
46
|
"move",
|
|
44
|
-
"reveal"
|
|
47
|
+
"reveal",
|
|
48
|
+
"files",
|
|
49
|
+
"readFile"
|
|
45
50
|
];
|
|
46
51
|
/**
|
|
47
52
|
* The request body on the wire. The endpoint travels as the RPC method, not as
|
|
@@ -92,10 +97,47 @@ const rootViewSchema = z.object({
|
|
|
92
97
|
const rootsViewSchema = z.object({
|
|
93
98
|
primaryRoot: z.string(),
|
|
94
99
|
primaryName: z.string().optional(),
|
|
100
|
+
effectiveCwd: z.string().optional(),
|
|
95
101
|
roots: z.array(rootViewSchema),
|
|
96
102
|
unavailable: z.string().optional()
|
|
97
103
|
});
|
|
98
104
|
const revealedViewSchema = z.object({ revealed: z.string() });
|
|
105
|
+
const filesViewSchema = z.object({
|
|
106
|
+
path: z.string(),
|
|
107
|
+
entries: z.array(z.object({
|
|
108
|
+
name: z.string().min(1).refine((name) => name !== "." && name !== ".." && !/[\\/]/.test(name) && !name.includes(String.fromCharCode(0))),
|
|
109
|
+
type: z.enum([
|
|
110
|
+
"file",
|
|
111
|
+
"directory",
|
|
112
|
+
"other"
|
|
113
|
+
])
|
|
114
|
+
})),
|
|
115
|
+
truncated: z.boolean()
|
|
116
|
+
});
|
|
117
|
+
const filePreviewSchema = z.object({
|
|
118
|
+
text: z.string(),
|
|
119
|
+
eof: z.boolean()
|
|
120
|
+
});
|
|
121
|
+
function parseFilesView(value) {
|
|
122
|
+
const parsed = filesViewSchema.safeParse(value);
|
|
123
|
+
return parsed.success ? {
|
|
124
|
+
ok: true,
|
|
125
|
+
value: parsed.data
|
|
126
|
+
} : {
|
|
127
|
+
ok: false,
|
|
128
|
+
message: firstIssue(parsed.error)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function parseFilePreview(value) {
|
|
132
|
+
const parsed = filePreviewSchema.safeParse(value);
|
|
133
|
+
return parsed.success ? {
|
|
134
|
+
ok: true,
|
|
135
|
+
value: parsed.data
|
|
136
|
+
} : {
|
|
137
|
+
ok: false,
|
|
138
|
+
message: firstIssue(parsed.error)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
99
141
|
z.object({
|
|
100
142
|
code: z.string(),
|
|
101
143
|
message: z.string(),
|
|
@@ -139,6 +181,7 @@ function parseRootsView(value) {
|
|
|
139
181
|
value: {
|
|
140
182
|
primaryRoot: view.primaryRoot,
|
|
141
183
|
...view.primaryName === void 0 ? {} : { primaryName: view.primaryName },
|
|
184
|
+
...view.effectiveCwd === void 0 ? {} : { effectiveCwd: view.effectiveCwd },
|
|
142
185
|
roots: view.roots.map(narrowRootView),
|
|
143
186
|
...view.unavailable === void 0 ? {} : { unavailable: view.unavailable }
|
|
144
187
|
}
|
|
@@ -196,6 +239,56 @@ function firstIssue(error) {
|
|
|
196
239
|
return `${issue.path.length === 0 ? "" : `${issue.path.join(".")}: `}${issue.message}`;
|
|
197
240
|
}
|
|
198
241
|
//#endregion
|
|
242
|
+
//#region src/panel-files.ts
|
|
243
|
+
/** Additional-root file browsing through the plugin's existing Connection channel. */
|
|
244
|
+
async function browseRoot(ctx, primaryRoot, request, signal) {
|
|
245
|
+
const roots = await ctx.multiRootRegistry.refresh(primaryRoot);
|
|
246
|
+
const ref = request.entry === void 0 ? request.id === void 0 ? void 0 : {
|
|
247
|
+
kind: "id",
|
|
248
|
+
id: request.id
|
|
249
|
+
} : {
|
|
250
|
+
kind: "entry",
|
|
251
|
+
...request.entry
|
|
252
|
+
};
|
|
253
|
+
if (ref === void 0) throw new RootValidationError("invalid-ref", "an exact root entry is required");
|
|
254
|
+
const root = resolveRootRef(roots, ref);
|
|
255
|
+
if (root.state !== "available") throw new RootValidationError("missing", "the additional root is not available");
|
|
256
|
+
const path = request.path ?? ".";
|
|
257
|
+
if (isAbsolute(path) || /^[a-z][a-z\d+.-]*:/iu.test(path) || path.includes("\0")) throw new RootValidationError("invalid-ref", "a root-relative path is required");
|
|
258
|
+
const absolute = canonicalPath(resolve(root.path, path));
|
|
259
|
+
if (!isCanonicallyUnder(absolute, root.path)) throw new RootValidationError("invalid-ref", "the path is outside the selected root");
|
|
260
|
+
const files = ctx.root.get("workspaceFiles");
|
|
261
|
+
if (files === void 0) throw new Error("workspace-files is not composed");
|
|
262
|
+
const scope = {
|
|
263
|
+
sessionId: request.sessionId,
|
|
264
|
+
workspaceRoot: root.path
|
|
265
|
+
};
|
|
266
|
+
let result;
|
|
267
|
+
if (request.endpoint === "files") {
|
|
268
|
+
const listing = await files.list(scope, absolute, signal);
|
|
269
|
+
result = {
|
|
270
|
+
path,
|
|
271
|
+
entries: listing.entries.map((entry) => ({
|
|
272
|
+
name: entry.name,
|
|
273
|
+
type: entry.type
|
|
274
|
+
})),
|
|
275
|
+
truncated: listing.truncated
|
|
276
|
+
};
|
|
277
|
+
} else {
|
|
278
|
+
const preview = await files.read(scope, absolute, {
|
|
279
|
+
offset: 1,
|
|
280
|
+
limit: 200
|
|
281
|
+
}, signal);
|
|
282
|
+
result = {
|
|
283
|
+
text: preview.text,
|
|
284
|
+
eof: preview.eof
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const latest = resolveRootRef(await ctx.multiRootRegistry.refresh(primaryRoot), ref);
|
|
288
|
+
if (latest.state !== "available" || latest.path !== root.path || canonicalPath(resolve(root.path, path)) !== absolute) throw new RootValidationError("missing", "the additional root changed while reading");
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
199
292
|
//#region src/command.ts
|
|
200
293
|
/** Services this row needs before it may activate. */
|
|
201
294
|
const inject = [
|
|
@@ -523,9 +616,12 @@ async function rootsViewOf(ctx, registry, primaryRoot, request) {
|
|
|
523
616
|
const statuses = await registry.refresh(primaryRoot);
|
|
524
617
|
const unavailable = registry.unavailable;
|
|
525
618
|
const primaryName = await primaryNameOf(ctx, primaryRoot, request);
|
|
619
|
+
const session = ctx.get("sessions")?.get(request.sessionId);
|
|
620
|
+
const effectiveCwd = session === void 0 ? void 0 : effectiveDirectory(ctx, session);
|
|
526
621
|
return {
|
|
527
622
|
primaryRoot: canonicalRoot(primaryRoot),
|
|
528
623
|
...primaryName === void 0 ? {} : { primaryName },
|
|
624
|
+
...effectiveCwd === void 0 ? {} : { effectiveCwd },
|
|
529
625
|
roots: statuses.map(toRootView),
|
|
530
626
|
...unavailable === void 0 ? {} : { unavailable }
|
|
531
627
|
};
|
|
@@ -539,7 +635,7 @@ async function rootsViewOf(ctx, registry, primaryRoot, request) {
|
|
|
539
635
|
* A malformed request is reported as `panel/bad-request` rather than throwing
|
|
540
636
|
* through the channel.
|
|
541
637
|
*/
|
|
542
|
-
async function dispatchPanelRequest(ctx, endpoint, payload,
|
|
638
|
+
async function dispatchPanelRequest(ctx, endpoint, payload, signal) {
|
|
543
639
|
try {
|
|
544
640
|
const parsed = parsePanelCall(endpoint, payload);
|
|
545
641
|
if (!parsed.ok) return {
|
|
@@ -554,6 +650,11 @@ async function dispatchPanelRequest(ctx, endpoint, payload, _signal) {
|
|
|
554
650
|
const registry = ctx.multiRootRegistry;
|
|
555
651
|
const primaryRoot = resolvePanelPrimaryRoot(ctx, request.sessionId);
|
|
556
652
|
switch (request.endpoint) {
|
|
653
|
+
case "files":
|
|
654
|
+
case "readFile": return {
|
|
655
|
+
ok: true,
|
|
656
|
+
value: await browseRoot(ctx, primaryRoot, request, signal)
|
|
657
|
+
};
|
|
557
658
|
case "list": return {
|
|
558
659
|
ok: true,
|
|
559
660
|
value: await rootsViewOf(ctx, registry, primaryRoot, request)
|
|
@@ -687,4 +788,4 @@ async function revealRoot(ctx, path) {
|
|
|
687
788
|
if (outcome.exitCode !== 0) throw new RootValidationError("reveal-unavailable", `the file manager exited with ${String(outcome.exitCode)}`);
|
|
688
789
|
}
|
|
689
790
|
//#endregion
|
|
690
|
-
export { renderRootsReport as a, PANEL_CHANNEL as c,
|
|
791
|
+
export { renderRootsReport as a, PANEL_CHANNEL as c, parseFilesView as d, parsePanelCall as f, parseFoldersCommand as i, PANEL_ENDPOINTS as l, parseRootsView as m, apply as n, resolvePanelPrimaryRoot as o, parseRevealedView as p, inject as r, revealArgv as s, COMMAND_NAME as t, parseFilePreview as u };
|
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 revealArgv, t as COMMAND_NAME } from "./command-
|
|
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-CTdNvwt1.js";
|
|
2
2
|
export { COMMAND_NAME, apply, inject, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealArgv };
|
|
@@ -38,7 +38,11 @@ const SUPPORTED_DSH_RELEASES = [
|
|
|
38
38
|
"0.1.7-alpha.1",
|
|
39
39
|
"0.1.7-alpha.2",
|
|
40
40
|
"0.1.7-rc.1",
|
|
41
|
-
"0.1.7-rc.2"
|
|
41
|
+
"0.1.7-rc.2",
|
|
42
|
+
"0.2.0-rc.1",
|
|
43
|
+
"0.2.0-rc.2",
|
|
44
|
+
"0.2.1-alpha.1",
|
|
45
|
+
"0.2.1-alpha.2"
|
|
42
46
|
];
|
|
43
47
|
/**
|
|
44
48
|
* Packages whose absence means this plugin cannot function at all: every one of
|
|
@@ -59,6 +63,13 @@ const REQUIRED_CORE_PACKAGES = [
|
|
|
59
63
|
* that is the mixed-installation failure this contract exists to catch.
|
|
60
64
|
*/
|
|
61
65
|
const OPTIONAL_CORE_PACKAGES = [
|
|
66
|
+
"@deepseek-ai/dsh-tools",
|
|
67
|
+
"@deepseek-ai/dsh-working-directory",
|
|
68
|
+
"@deepseek-ai/dsh-lsp",
|
|
69
|
+
"@deepseek-ai/dsh-lsp-stdio",
|
|
70
|
+
"@deepseek-ai/dsh-api-workspace-files",
|
|
71
|
+
"@deepseek-ai/dsh-typert-protocol",
|
|
72
|
+
"@deepseek-ai/dsh-sandbox-windows-acl",
|
|
62
73
|
"@deepseek-ai/dsh-agent",
|
|
63
74
|
"@deepseek-ai/dsh-agent-instructions",
|
|
64
75
|
"@deepseek-ai/dsh-client-connection",
|
|
@@ -70,6 +81,8 @@ const OPTIONAL_CORE_PACKAGES = [
|
|
|
70
81
|
];
|
|
71
82
|
/** Every package the contract inspects, required first. */
|
|
72
83
|
const CORE_PACKAGES = [...REQUIRED_CORE_PACKAGES, ...OPTIONAL_CORE_PACKAGES];
|
|
84
|
+
/** Services looked up structurally; older runtimes never install these modules. */
|
|
85
|
+
const HOST_RESOLVED_PACKAGES = ["@deepseek-ai/dsh-working-directory"];
|
|
73
86
|
/** The environment variable that relaxes the gate. */
|
|
74
87
|
const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
75
88
|
/**
|
|
@@ -86,11 +99,18 @@ const COMPAT_ENFORCEMENT_ENV = "DSH_MULTI_ROOT_COMPAT";
|
|
|
86
99
|
* manifest cannot be resolved.
|
|
87
100
|
*/
|
|
88
101
|
function readInstalledVersion(packageName) {
|
|
102
|
+
const require = createRequire(import.meta.url);
|
|
89
103
|
try {
|
|
90
|
-
const manifest =
|
|
104
|
+
const manifest = require(`${packageName}/package.json`);
|
|
91
105
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
92
106
|
} catch {
|
|
93
|
-
return;
|
|
107
|
+
if (!HOST_RESOLVED_PACKAGES.includes(packageName)) return void 0;
|
|
108
|
+
try {
|
|
109
|
+
const manifest = createRequire(require.resolve("@deepseek-ai/dsh/package.json"))(`${packageName}/package.json`);
|
|
110
|
+
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
111
|
+
} catch {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
94
114
|
}
|
|
95
115
|
}
|
|
96
116
|
/**
|
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-B6ZqbQBy.js";
|
|
2
2
|
export { DshCompatUnsupportedError, MultiRootCompatService, apply, assertSupportedInstallation, inject };
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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-
|
|
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-
|
|
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-B6ZqbQBy.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-DfzN-wVf.js";
|
|
3
|
+
import { n as widenConfined, t as MultiRootSandboxProvider } from "./sandbox-BRXl_FpF.js";
|
|
4
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
5
|
import { t as MultiRootFileSystem } from "./fs-D_Sw3o5z.js";
|
|
6
6
|
import { MultiRootScopeService, renderWorkspaceRootsContext, sanitizeAdditionalRoots } from "./scope.js";
|
|
7
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
|
|
8
|
+
import { a as renderRootsReport, c as PANEL_CHANNEL, d as parseFilesView, f as parsePanelCall, i as parseFoldersCommand, l as PANEL_ENDPOINTS, m as parseRootsView, o as resolvePanelPrimaryRoot, p as parseRevealedView, s as revealArgv, t as COMMAND_NAME, u as parseFilePreview } from "./command-CTdNvwt1.js";
|
|
9
9
|
//#region src/index.ts
|
|
10
10
|
/**
|
|
11
11
|
* Carrier plugin face for the bare-package-name loader row (see the module
|
|
@@ -16,4 +16,4 @@ import { a as renderRootsReport, c as PANEL_CHANNEL, d as parseRevealedView, f a
|
|
|
16
16
|
const inject = [];
|
|
17
17
|
function apply() {}
|
|
18
18
|
//#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 };
|
|
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, parseFilePreview, parseFilesView, parseFoldersCommand, parsePanelCall, parseRevealedView, parseRootsView, readInstalledVersion, removeStatusAt, renderInstructions, renderRootsReport, renderWorkspaceRootsContext, resolvePanelPrimaryRoot, resolveRootRef, revealArgv, sanitizeAdditionalRoots, validateRootCandidate, widenConfined };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { t as effectiveDirectory } from "./session-directory-BLOstut1.js";
|
|
2
|
+
import { t as aroundTool } from "./tool-context-CpgIFcCB.js";
|
|
1
3
|
import { l as isCanonicallyUnder } from "./roots-DasWEBJk.js";
|
|
2
4
|
import { createRequire } from "node:module";
|
|
3
5
|
import "@deepseek-ai/cordis";
|
|
4
6
|
import { createHash } from "node:crypto";
|
|
7
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
8
|
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
6
9
|
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
7
10
|
//#region src/compat/agent-instructions.ts
|
|
@@ -221,6 +224,51 @@ function toolResultFailed(message) {
|
|
|
221
224
|
return block.isError === true;
|
|
222
225
|
}
|
|
223
226
|
//#endregion
|
|
227
|
+
//#region src/compat/instruction-overlap.ts
|
|
228
|
+
/** Recognize files already represented by the native instruction baseline in this step. */
|
|
229
|
+
function nativeInstructionCopies(messages, cwd) {
|
|
230
|
+
const represented = /* @__PURE__ */ new Map();
|
|
231
|
+
const removed = /* @__PURE__ */ new Set();
|
|
232
|
+
let replacesBaseline = false;
|
|
233
|
+
for (const message of messages) {
|
|
234
|
+
const source = message.source;
|
|
235
|
+
if (source.kind !== "agent-instructions" || source.changes === void 0) continue;
|
|
236
|
+
if (source.baseline === true) {
|
|
237
|
+
replacesBaseline = true;
|
|
238
|
+
represented.clear();
|
|
239
|
+
}
|
|
240
|
+
for (const change of source.changes) {
|
|
241
|
+
if (change.action !== "remove") continue;
|
|
242
|
+
removed.add(change.path);
|
|
243
|
+
for (const [path, copy] of represented) if (copy.displayPath === change.path) represented.delete(path);
|
|
244
|
+
}
|
|
245
|
+
if (cwd === void 0 || source.baselineIdentity === void 0) continue;
|
|
246
|
+
let identity;
|
|
247
|
+
try {
|
|
248
|
+
identity = JSON.parse(source.baselineIdentity);
|
|
249
|
+
} catch {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (typeof identity.projectRoot !== "string") continue;
|
|
253
|
+
const project = resolve(cwd, identity.projectRoot);
|
|
254
|
+
for (const change of source.changes) {
|
|
255
|
+
if (change.action === "remove" || typeof change.digest !== "string") continue;
|
|
256
|
+
if (/^(?:~|\$|user-global[\\/]|agents-global[\\/])/.test(change.path)) continue;
|
|
257
|
+
represented.set(resolve(project, change.path), {
|
|
258
|
+
digest: change.digest,
|
|
259
|
+
displayPath: change.path
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
pathOf: (file) => {
|
|
265
|
+
const copy = represented.get(file.absolutePath);
|
|
266
|
+
return copy?.digest === createHash("sha1").update(file.content).digest("hex") ? copy.displayPath : void 0;
|
|
267
|
+
},
|
|
268
|
+
withdraws: (displayPath) => replacesBaseline || removed.has(displayPath)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
224
272
|
//#region src/instructions.ts
|
|
225
273
|
/**
|
|
226
274
|
* `multi-root-instructions`: the additional roots' own instruction files, made
|
|
@@ -342,16 +390,13 @@ function touchedPathOfToolCall(name, argumentsJson) {
|
|
|
342
390
|
* The directory a touched path's instructions would live in.
|
|
343
391
|
*
|
|
344
392
|
* Tool arguments are the model's raw input, so a relative `file_path` is
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* @param path - a touched path, absolute or relative to the session cwd.
|
|
349
|
-
* @param session - the session the touch belongs to.
|
|
393
|
+
* captured as an absolute path at the operation boundary. Mac's `realpath`
|
|
394
|
+
* spelling is applied here so it compares against the canonical roots.
|
|
395
|
+
* @param path - the absolute touched file path.
|
|
350
396
|
* @returns the canonical parent directory, or `undefined` when it cannot be named.
|
|
351
397
|
*/
|
|
352
|
-
function parentDirectoryOf(path
|
|
353
|
-
const
|
|
354
|
-
const absolute = isAbsolute(path) ? path : cwd === void 0 ? void 0 : resolve(cwd, path);
|
|
398
|
+
function parentDirectoryOf(path) {
|
|
399
|
+
const absolute = isAbsolute(path) ? path : void 0;
|
|
355
400
|
if (absolute === void 0) return void 0;
|
|
356
401
|
try {
|
|
357
402
|
return canonicalPath(dirname(absolute));
|
|
@@ -407,6 +452,8 @@ function apply(ctx, config = {}) {
|
|
|
407
452
|
if (maxBytes <= 0 || !Number.isFinite(maxBytes)) return;
|
|
408
453
|
const states = /* @__PURE__ */ new WeakMap();
|
|
409
454
|
const callsBySession = /* @__PURE__ */ new WeakMap();
|
|
455
|
+
const executions = new AsyncLocalStorage();
|
|
456
|
+
aroundTool(ctx, (exec, next) => executions.run(exec, next));
|
|
410
457
|
const stateOf = (session) => {
|
|
411
458
|
const existing = states.get(session);
|
|
412
459
|
if (existing !== void 0) return existing;
|
|
@@ -427,7 +474,8 @@ function apply(ctx, config = {}) {
|
|
|
427
474
|
}
|
|
428
475
|
pendingCalls.set(event.data.callId, {
|
|
429
476
|
name: event.data.name,
|
|
430
|
-
arguments: event.data.arguments
|
|
477
|
+
arguments: event.data.arguments,
|
|
478
|
+
cwd: effectiveDirectory(ctx, session)
|
|
431
479
|
});
|
|
432
480
|
return;
|
|
433
481
|
}
|
|
@@ -437,17 +485,24 @@ function apply(ctx, config = {}) {
|
|
|
437
485
|
pendingCalls.delete(callId);
|
|
438
486
|
if (call === void 0) return;
|
|
439
487
|
if (event.data.error !== void 0 || toolResultFailed(event.data.message)) return;
|
|
440
|
-
const path = touchedPathOfToolCall(call.name, call.arguments);
|
|
488
|
+
const path = call.observedPath ?? touchedPathOfToolCall(call.name, call.arguments);
|
|
441
489
|
if (path === void 0) return;
|
|
442
490
|
const state = stateOf(session);
|
|
443
491
|
if (state.touched.size >= MAX_TOUCHED_PATHS) return;
|
|
444
|
-
state.touched.add(path);
|
|
492
|
+
if (isAbsolute(path)) state.touched.add(path);
|
|
493
|
+
else if (call.cwd !== void 0) state.touched.add(resolve(call.cwd, path));
|
|
445
494
|
});
|
|
446
495
|
ctx.inject(["fs"], (scope) => {
|
|
496
|
+
scope.on("fs/observed", (target, _observation, actor) => {
|
|
497
|
+
const exec = executions.getStore();
|
|
498
|
+
if (exec?.agent === void 0 || FILE_TOUCH_TOOL_NAMES[exec.name] !== true || actor !== exec) return;
|
|
499
|
+
const call = callsBySession.get(exec.agent.session)?.get(exec.callId);
|
|
500
|
+
if (call !== void 0) call.observedPath = scope.fs.processPath(target);
|
|
501
|
+
});
|
|
447
502
|
scope.on("agent/pre-step", async ({ agent, messages, signal }, next) => {
|
|
448
503
|
const decision = await next();
|
|
449
504
|
if (decision.kind !== "enter") return decision;
|
|
450
|
-
const seeded = await pending(scope, states, agent, config, maxBytes, signal);
|
|
505
|
+
const seeded = await pending(scope, states, agent, config, maxBytes, signal, decision.messages);
|
|
451
506
|
if (seeded === void 0) return decision;
|
|
452
507
|
signal.throwIfAborted();
|
|
453
508
|
const lastClaimed = decision.messages.findLastIndex((message) => messages.includes(message));
|
|
@@ -455,7 +510,7 @@ function apply(ctx, config = {}) {
|
|
|
455
510
|
...decision,
|
|
456
511
|
messages: decision.messages.toSpliced(lastClaimed + 1, 0, seeded)
|
|
457
512
|
};
|
|
458
|
-
});
|
|
513
|
+
}, { prepend: true });
|
|
459
514
|
});
|
|
460
515
|
}
|
|
461
516
|
/**
|
|
@@ -471,7 +526,7 @@ function apply(ctx, config = {}) {
|
|
|
471
526
|
* @param signal - the turn's cancellation signal.
|
|
472
527
|
* @returns the message to inject, or `undefined`.
|
|
473
528
|
*/
|
|
474
|
-
async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
529
|
+
async function pending(ctx, states, agent, config, maxBytes, signal, messages) {
|
|
475
530
|
const session = agent.session;
|
|
476
531
|
const state = states.get(session) ?? {
|
|
477
532
|
scopes: /* @__PURE__ */ new Map(),
|
|
@@ -481,9 +536,12 @@ async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
|
481
536
|
const evaluatedTouches = new Set(state.touched);
|
|
482
537
|
let retryTouches = false;
|
|
483
538
|
const scope = ctx.multiRootScope.resolve(ctx.sandboxPolicy.resolve({ session }));
|
|
484
|
-
const granted = new Set(scope.additionalRoots);
|
|
485
|
-
const revoked = [...new Set([...state.scopes.values()].map((entry) => entry.root))].filter((root) => !granted.has(root));
|
|
486
539
|
const rendered = [];
|
|
540
|
+
const nativeCopy = nativeInstructionCopies(messages, effectiveDirectory(ctx, session));
|
|
541
|
+
for (const [key, entry] of state.scopes) if (entry.nativePath !== void 0 && nativeCopy.withdraws(entry.nativePath)) state.scopes.set(key, {
|
|
542
|
+
...entry,
|
|
543
|
+
digest: void 0
|
|
544
|
+
});
|
|
487
545
|
const gone = [];
|
|
488
546
|
const updates = [];
|
|
489
547
|
if (scope.additionalRoots.length > 0) {
|
|
@@ -492,14 +550,29 @@ async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
|
492
550
|
let remaining = maxBytes;
|
|
493
551
|
for (const root of scope.additionalRoots) {
|
|
494
552
|
signal.throwIfAborted();
|
|
495
|
-
const plan = await planRoot(ctx, api, root, config, state,
|
|
553
|
+
const plan = await planRoot(ctx, api, root, config, state, signal);
|
|
496
554
|
gone.push(...plan.gone);
|
|
497
555
|
if (plan.deliveries.length === 0) continue;
|
|
498
556
|
if (remaining <= 0) {
|
|
499
557
|
retryTouches = true;
|
|
500
558
|
continue;
|
|
501
559
|
}
|
|
502
|
-
const
|
|
560
|
+
const deliveries = plan.deliveries.filter((entry) => {
|
|
561
|
+
const nativePath = nativeCopy.pathOf(entry.file);
|
|
562
|
+
if (nativePath === void 0) return true;
|
|
563
|
+
updates.push({
|
|
564
|
+
key: entry.key,
|
|
565
|
+
scope: {
|
|
566
|
+
root,
|
|
567
|
+
path: entry.file.absolutePath,
|
|
568
|
+
digest: digestOf(entry.file.content),
|
|
569
|
+
nativePath
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
return false;
|
|
573
|
+
});
|
|
574
|
+
if (deliveries.length === 0) continue;
|
|
575
|
+
const { text, omitted, truncated } = api.render(deliveries.map((entry) => entry.file), { maxBytes: remaining });
|
|
503
576
|
const omittedPaths = new Set(omitted.map((file) => file.absolutePath));
|
|
504
577
|
const truncatedPaths = new Set(truncated.map((file) => file.displayPath));
|
|
505
578
|
if (omitted.length > 0 || truncated.length > 0 || text === "") retryTouches = true;
|
|
@@ -509,7 +582,7 @@ async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
|
509
582
|
root,
|
|
510
583
|
text
|
|
511
584
|
});
|
|
512
|
-
for (const entry of
|
|
585
|
+
for (const entry of deliveries) {
|
|
513
586
|
if (omittedPaths.has(entry.file.absolutePath)) continue;
|
|
514
587
|
updates.push({
|
|
515
588
|
key: entry.key,
|
|
@@ -523,17 +596,20 @@ async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
|
523
596
|
}
|
|
524
597
|
}
|
|
525
598
|
}
|
|
526
|
-
const
|
|
599
|
+
const current = new Set(ctx.multiRootScope.resolve(ctx.sandboxPolicy.resolve({ session })).additionalRoots);
|
|
600
|
+
const revoked = [...new Set([...state.scopes.values()].map((entry) => entry.root))].filter((root) => !current.has(root));
|
|
601
|
+
const liveUpdates = updates.filter((entry) => current.has(entry.scope.root));
|
|
602
|
+
const text = composeInstructionMessage(rendered.filter((entry) => current.has(entry.root)), revoked, gone.filter((entry) => current.has(entry.scope.root)).map((entry) => entry.scope));
|
|
527
603
|
const message = text === void 0 ? void 0 : await createInstructionMessage({
|
|
528
604
|
text,
|
|
529
605
|
plugin: PLUGIN_SOURCE
|
|
530
606
|
});
|
|
531
607
|
signal.throwIfAborted();
|
|
532
608
|
if (!retryTouches) for (const touch of evaluatedTouches) state.touched.delete(touch);
|
|
533
|
-
if (text !== void 0) {
|
|
609
|
+
if (text !== void 0 || liveUpdates.length > 0) {
|
|
534
610
|
for (const root of revoked) for (const [key, entry] of state.scopes) if (entry.root === root) state.scopes.delete(key);
|
|
535
611
|
for (const entry of gone) state.scopes.delete(entry.key);
|
|
536
|
-
for (const entry of
|
|
612
|
+
for (const entry of liveUpdates) state.scopes.set(entry.key, entry.scope);
|
|
537
613
|
}
|
|
538
614
|
return message;
|
|
539
615
|
}
|
|
@@ -554,11 +630,10 @@ async function pending(ctx, states, agent, config, maxBytes, signal) {
|
|
|
554
630
|
* @param root - the canonical additional root.
|
|
555
631
|
* @param config - the row's configuration.
|
|
556
632
|
* @param state - the session's delivery state.
|
|
557
|
-
* @param session - the session, for the cwd a relative touch resolves against.
|
|
558
633
|
* @param signal - the turn's cancellation signal.
|
|
559
634
|
* @returns the files to send and the deliveries to withdraw.
|
|
560
635
|
*/
|
|
561
|
-
async function planRoot(ctx, api, root, config, state,
|
|
636
|
+
async function planRoot(ctx, api, root, config, state, signal) {
|
|
562
637
|
const delivered = /* @__PURE__ */ new Map();
|
|
563
638
|
for (const [key, entry] of state.scopes) if (entry.root === root) delivered.set(key, entry);
|
|
564
639
|
const dirs = /* @__PURE__ */ new Set([root]);
|
|
@@ -567,7 +642,7 @@ async function planRoot(ctx, api, root, config, state, session, signal) {
|
|
|
567
642
|
if (dir !== root) dirs.add(dir);
|
|
568
643
|
}
|
|
569
644
|
for (const touched of state.touched) {
|
|
570
|
-
const parent = parentDirectoryOf(touched
|
|
645
|
+
const parent = parentDirectoryOf(touched);
|
|
571
646
|
if (parent === void 0 || !isCanonicallyUnder(parent, root)) continue;
|
|
572
647
|
let dir = parent;
|
|
573
648
|
for (;;) {
|
|
@@ -678,16 +753,21 @@ async function loadDirectory(fs, api, root, config, signal, cwd) {
|
|
|
678
753
|
}
|
|
679
754
|
const files = [];
|
|
680
755
|
for (const candidate of candidates) {
|
|
756
|
+
if (isAbsolute(candidate.displayPath) || resolve(root, candidate.displayPath) !== resolve(candidate.absolutePath)) continue;
|
|
681
757
|
if (!isCanonicallyUnder(canonicalPath(candidate.absolutePath), root)) continue;
|
|
682
758
|
try {
|
|
683
759
|
const target = await fs.resolve(candidate.absolutePath);
|
|
760
|
+
const path = fs.processPath(target);
|
|
761
|
+
if (!isCanonicallyUnder(path, root)) continue;
|
|
684
762
|
const info = await fs.stat(target, signal);
|
|
685
763
|
if (info === void 0 || info.type !== "file") continue;
|
|
686
764
|
if (info.size !== void 0 && info.size > maxSourceBytes) continue;
|
|
765
|
+
const content = await fs.readText(target, signal);
|
|
766
|
+
if (canonicalPath(candidate.absolutePath) !== path || !isCanonicallyUnder(path, root)) continue;
|
|
687
767
|
files.push({
|
|
688
768
|
...candidate,
|
|
689
769
|
displayPath: candidate.absolutePath,
|
|
690
|
-
content
|
|
770
|
+
content
|
|
691
771
|
});
|
|
692
772
|
} catch {
|
|
693
773
|
continue;
|
package/lib/instructions.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as composeInstructionMessage, i as apply, n as DEFAULT_MAX_SOURCE_BYTES, o as inject, r as PLUGIN_SOURCE, s as touchedPathOfToolCall, t as DEFAULT_MAX_BYTES } from "./instructions-
|
|
1
|
+
import { a as composeInstructionMessage, i as apply, n as DEFAULT_MAX_SOURCE_BYTES, o as inject, r as PLUGIN_SOURCE, s as touchedPathOfToolCall, t as DEFAULT_MAX_BYTES } from "./instructions-DfzN-wVf.js";
|
|
2
2
|
export { DEFAULT_MAX_BYTES, DEFAULT_MAX_SOURCE_BYTES, PLUGIN_SOURCE, apply, composeInstructionMessage, inject, touchedPathOfToolCall };
|
package/lib/lsp.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { t as aroundTool } from "./tool-context-CpgIFcCB.js";
|
|
2
|
+
import { n as routeWorkspacePath, t as wrapMethod } from "./method-wrapper-Zc-JlDP3.js";
|
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
//#region src/lsp.ts
|
|
5
|
+
const inject = ["multiRootCompat", "multiRootScope"];
|
|
6
|
+
function apply(ctx) {
|
|
7
|
+
const callers = new AsyncLocalStorage();
|
|
8
|
+
aroundTool(ctx, (exec, next) => exec.agent === void 0 ? next() : callers.run(exec.agent.session, next));
|
|
9
|
+
ctx.inject(["lsp", "fs"], (scope) => {
|
|
10
|
+
const service = scope.get("lsp");
|
|
11
|
+
scope.effect(() => wrapMethod(service, "query", (original) => async function(request, signal) {
|
|
12
|
+
const session = callers.getStore();
|
|
13
|
+
const primary = session === void 0 ? request.workspaceRoot : session.header.cwd ?? scope.get("sandboxPolicy")?.resolve({ session }).workspaceRoot;
|
|
14
|
+
if (primary === void 0) throw new Error("multi-root workspace: LSP caller has no session origin or deployment policy");
|
|
15
|
+
if (scope.multiRootScope.scopeOf(primary).length === 0 && session === void 0) return original.call(this, request, signal);
|
|
16
|
+
const route = await routeWorkspacePath(scope, primary, request.filePath, signal, request.workspaceRoot);
|
|
17
|
+
if (route === void 0) {
|
|
18
|
+
const target = await scope.fs.resolve(request.filePath, {
|
|
19
|
+
cwd: request.workspaceRoot,
|
|
20
|
+
...signal === void 0 ? {} : { signal }
|
|
21
|
+
});
|
|
22
|
+
return original.call(this, {
|
|
23
|
+
...request,
|
|
24
|
+
workspaceRoot: primary,
|
|
25
|
+
filePath: scope.fs.processPath(target)
|
|
26
|
+
}, signal);
|
|
27
|
+
}
|
|
28
|
+
if (session === void 0 && route.workspaceRoot === route.primaryRoot) return original.call(this, request, signal);
|
|
29
|
+
return original.call(this, {
|
|
30
|
+
...request,
|
|
31
|
+
workspaceRoot: route.workspaceRoot,
|
|
32
|
+
filePath: route.absolutePath
|
|
33
|
+
}, signal);
|
|
34
|
+
}), "multi-root:lsp routing");
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { apply, inject };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
2
|
+
//#region src/workspace-routing.ts
|
|
3
|
+
/** Resolve relative paths against the primary cwd; never search other roots by basename. */
|
|
4
|
+
async function routeWorkspacePath(ctx, primaryRoot, path, signal, cwd = primaryRoot) {
|
|
5
|
+
signal?.throwIfAborted();
|
|
6
|
+
const primary = canonicalPath(primaryRoot);
|
|
7
|
+
const roots = [primary, ...ctx.multiRootScope.scopeOf(primary)];
|
|
8
|
+
const target = await ctx.fs.resolve(path, {
|
|
9
|
+
cwd,
|
|
10
|
+
...signal === void 0 ? {} : { signal }
|
|
11
|
+
});
|
|
12
|
+
for (const root of roots) {
|
|
13
|
+
const rootTarget = await ctx.fs.resolve(root, signal === void 0 ? {} : { signal });
|
|
14
|
+
if (!ctx.fs.contains(rootTarget, target)) continue;
|
|
15
|
+
if (root !== primary && !ctx.multiRootScope.scopeOf(primary).includes(root)) return void 0;
|
|
16
|
+
signal?.throwIfAborted();
|
|
17
|
+
return {
|
|
18
|
+
primaryRoot: primary,
|
|
19
|
+
workspaceRoot: root,
|
|
20
|
+
target,
|
|
21
|
+
absolutePath: ctx.fs.processPath(target)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/method-wrapper.ts
|
|
27
|
+
/** Wrap a public method on a Cordis service without capturing a caller-context proxy. */
|
|
28
|
+
function wrapMethod(target, key, wrap) {
|
|
29
|
+
const own = Object.getOwnPropertyDescriptor(target, key);
|
|
30
|
+
let prototype = target;
|
|
31
|
+
let descriptor;
|
|
32
|
+
while (prototype !== null && descriptor === void 0) {
|
|
33
|
+
descriptor = Object.getOwnPropertyDescriptor(prototype, key);
|
|
34
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
35
|
+
}
|
|
36
|
+
if (typeof descriptor?.value !== "function") throw new Error(`multi-root: ${String(key)} is not a public method`);
|
|
37
|
+
const value = wrap(descriptor.value);
|
|
38
|
+
Object.defineProperty(target, key, {
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
enumerable: own?.enumerable ?? false,
|
|
42
|
+
value
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
if (Object.getOwnPropertyDescriptor(target, key)?.value !== value) return;
|
|
46
|
+
if (own === void 0) Reflect.deleteProperty(target, key);
|
|
47
|
+
else Object.defineProperty(target, key, own);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { routeWorkspacePath as n, wrapMethod as t };
|