@dsh-electron/dsh-plugin-multi-root-workspace 0.1.0
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/LICENSE +21 -0
- package/README.en.md +156 -0
- package/README.md +156 -0
- package/cordis.patch.yml +44 -0
- package/lib/client.js +6512 -0
- package/lib/client.js.map +1 -0
- package/lib/command-BSkrYOAf.js +727 -0
- package/lib/command.js +2 -0
- package/lib/fs-D7UoCb7T.js +152 -0
- package/lib/fs.js +2 -0
- package/lib/index.js +17 -0
- package/lib/registry.js +466 -0
- package/lib/roots-DyT2jgEP.js +345 -0
- package/lib/sandbox-BhuMTDgH.js +243 -0
- package/lib/sandbox.js +2 -0
- package/lib/scope.js +143 -0
- package/lib/types/client/WorkspaceFoldersAction.d.ts +43 -0
- package/lib/types/client/index.d.ts +28 -0
- package/lib/types/client/locales.d.ts +118 -0
- package/lib/types/client/panel-client.d.ts +59 -0
- package/lib/types/client/styles.d.ts +14 -0
- package/lib/types/command.d.ts +66 -0
- package/lib/types/containment.d.ts +36 -0
- package/lib/types/contract.d.ts +164 -0
- package/lib/types/dialects.d.ts +92 -0
- package/lib/types/fs.d.ts +90 -0
- package/lib/types/index.d.ts +42 -0
- package/lib/types/registry.d.ts +293 -0
- package/lib/types/roots.d.ts +238 -0
- package/lib/types/sandbox.d.ts +65 -0
- package/lib/types/scope.d.ts +120 -0
- package/package.json +145 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The multi-root workspace scope: the plugin's single home for the question
|
|
3
|
+
* "which directories belong to this workspace".
|
|
4
|
+
*
|
|
5
|
+
* The primary root is never stored here — it is the session's immutable cwd as
|
|
6
|
+
* resolved by the upstream `ctx.sandboxPolicy` service. This service owns the
|
|
7
|
+
* additional roots, the canonical key that indexes them, and the one model-facing
|
|
8
|
+
* statement of that topology, so every enforcing provider (fs fence,
|
|
9
|
+
* kernel-sandbox dialects) and the prompt snapshot all resolve the same scope.
|
|
10
|
+
*
|
|
11
|
+
* The root table is still filled by the registry's own API (tests and smoke
|
|
12
|
+
* scripts today; plugin storage in M3). Keeping the empty case on the same code
|
|
13
|
+
* path as the populated one is deliberate — the pass-through safety net must
|
|
14
|
+
* exercise the real provider wiring, not a stub that later gets replaced.
|
|
15
|
+
*
|
|
16
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/scope
|
|
17
|
+
*/
|
|
18
|
+
import { Context, Service } from '@deepseek-ai/cordis';
|
|
19
|
+
import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox';
|
|
20
|
+
declare module '@deepseek-ai/cordis' {
|
|
21
|
+
interface Context {
|
|
22
|
+
multiRootScope: MultiRootScopeService;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** One configured additional root, as the registry persists it. */
|
|
26
|
+
export interface AdditionalWorkspaceRoot {
|
|
27
|
+
/** Stable registry identity (opaque; M3 brands it). */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Canonical absolute directory, as spelled in the registration. */
|
|
30
|
+
path: string;
|
|
31
|
+
/**
|
|
32
|
+
* The canonical directory this registration was granted for (the `realpath`
|
|
33
|
+
* captured when the operator registered it). The root is granted only while
|
|
34
|
+
* `canonicalPath(path)` still equals this value: re-resolving a path is NOT
|
|
35
|
+
* re-authorizing it, because whoever can replace the directory (or a symlink
|
|
36
|
+
* in its chain) could otherwise move the grant to a directory no operator
|
|
37
|
+
* ever registered.
|
|
38
|
+
*/
|
|
39
|
+
recordedPath: string;
|
|
40
|
+
/** Optional display alias. */
|
|
41
|
+
alias?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The resolved scope of one confined call. `primaryRoot` equals the policy's
|
|
45
|
+
* workspace root; `additionalRoots` are canonical, deduplicated, never equal to
|
|
46
|
+
* the primary root, and empty in M1.
|
|
47
|
+
*/
|
|
48
|
+
export interface FilesystemScope {
|
|
49
|
+
/** The session's workspace root (`session.header.cwd`, canonical). */
|
|
50
|
+
primaryRoot: string;
|
|
51
|
+
/** Extra writable roots granted inside the same sandbox mechanism. */
|
|
52
|
+
additionalRoots: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Canonicalize and sanitize one root list against a primary root: drop the
|
|
56
|
+
* primary itself, duplicates, missing/non-directory paths, and any root whose
|
|
57
|
+
* current resolution no longer matches the directory it was registered for;
|
|
58
|
+
* preserve registry order.
|
|
59
|
+
*
|
|
60
|
+
* The last rule is the security-relevant one. `canonicalPath` is `realpath`, so
|
|
61
|
+
* a registered directory that has since been replaced by a symlink resolves to
|
|
62
|
+
* a different directory — granting that would hand out a directory nobody
|
|
63
|
+
* registered, on nothing more than a local replace. A registration whose
|
|
64
|
+
* resolution moved is therefore withheld here (the registry reports it as
|
|
65
|
+
* `redirected`), and it comes back only when the path resolves to the recorded
|
|
66
|
+
* directory again.
|
|
67
|
+
*
|
|
68
|
+
* @param primaryRoot - the canonical primary root to exclude.
|
|
69
|
+
* @param roots - candidate roots, in registry order.
|
|
70
|
+
* @returns the sanitized additional roots.
|
|
71
|
+
*/
|
|
72
|
+
export declare function sanitizeAdditionalRoots(primaryRoot: string, roots: readonly AdditionalWorkspaceRoot[]): string[];
|
|
73
|
+
/**
|
|
74
|
+
* The name of the runtime-context contribution that tells the model which extra
|
|
75
|
+
* roots belong to this session's workspace.
|
|
76
|
+
*/
|
|
77
|
+
export declare const MULTI_ROOT_CONTEXT_NAME = "multi-root:scope";
|
|
78
|
+
/**
|
|
79
|
+
* Render the workspace topology the model is told about. Only roots are listed
|
|
80
|
+
* (never files), and the sentence states the two facts a denied write would
|
|
81
|
+
* otherwise have to teach: that the additional roots are part of the SAME
|
|
82
|
+
* workspace, and that the session cwd is unchanged (requirement §5, §8).
|
|
83
|
+
* @param primaryRoot - the canonical session workspace root.
|
|
84
|
+
* @param additionalRoots - canonical additional roots, in scope order.
|
|
85
|
+
* @returns the stable topology sentence.
|
|
86
|
+
*/
|
|
87
|
+
export declare function renderWorkspaceRootsContext(primaryRoot: string, additionalRoots: readonly string[]): string;
|
|
88
|
+
/**
|
|
89
|
+
* The `ctx.multiRootScope` service. Providers ask it — never the filesystem —
|
|
90
|
+
* what the current scope is, which is what keeps the fs fence and every kernel
|
|
91
|
+
* dialect on one permission world (requirement §13).
|
|
92
|
+
*/
|
|
93
|
+
export declare class MultiRootScopeService extends Service {
|
|
94
|
+
/** Additional roots keyed by canonical primary root; empty until roots are registered. */
|
|
95
|
+
private readonly rootsByPrimary;
|
|
96
|
+
constructor(ctx: Context);
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the scope of one confined call.
|
|
99
|
+
* @param policy - the upstream policy whose workspace root is the primary root.
|
|
100
|
+
* @returns the canonical scope; `additionalRoots` is empty when none are registered.
|
|
101
|
+
*/
|
|
102
|
+
resolve(policy: SandboxExecutionPolicy): FilesystemScope;
|
|
103
|
+
/**
|
|
104
|
+
* The additional roots registered for one canonical primary root.
|
|
105
|
+
* @param primaryRoot - canonical primary root (or any spelling thereof).
|
|
106
|
+
* @returns the canonical additional roots; empty when none are registered.
|
|
107
|
+
*/
|
|
108
|
+
scopeOf(primaryRoot: string): readonly string[];
|
|
109
|
+
/**
|
|
110
|
+
* Replace the roots registered for one primary root.
|
|
111
|
+
*
|
|
112
|
+
* M1 uses this from tests and smoke scripts only; M3 feeds it from plugin
|
|
113
|
+
* storage. It is a plain setter so the transition swaps the data source
|
|
114
|
+
* without touching any provider.
|
|
115
|
+
* @param primaryRoot - canonical primary root to register roots under.
|
|
116
|
+
* @param roots - the additional roots, in display order.
|
|
117
|
+
*/
|
|
118
|
+
setAdditionalRoots(primaryRoot: string, roots: readonly AdditionalWorkspaceRoot[]): void;
|
|
119
|
+
}
|
|
120
|
+
export default MultiRootScopeService;
|
package/package.json
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-electron/dsh-plugin-multi-root-workspace",
|
|
3
|
+
"description": "Multi-root Workspace for DeepSeek Harness: adds N additional roots to the workspace sandbox scope as an out-of-tree bundle",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./fs": {
|
|
14
|
+
"types": "./lib/types/fs.d.ts",
|
|
15
|
+
"default": "./lib/fs.js"
|
|
16
|
+
},
|
|
17
|
+
"./sandbox": {
|
|
18
|
+
"types": "./lib/types/sandbox.d.ts",
|
|
19
|
+
"default": "./lib/sandbox.js"
|
|
20
|
+
},
|
|
21
|
+
"./scope": {
|
|
22
|
+
"types": "./lib/types/scope.d.ts",
|
|
23
|
+
"default": "./lib/scope.js"
|
|
24
|
+
},
|
|
25
|
+
"./registry": {
|
|
26
|
+
"types": "./lib/types/registry.d.ts",
|
|
27
|
+
"default": "./lib/registry.js"
|
|
28
|
+
},
|
|
29
|
+
"./command": {
|
|
30
|
+
"types": "./lib/types/command.d.ts",
|
|
31
|
+
"default": "./lib/command.js"
|
|
32
|
+
},
|
|
33
|
+
"./client": {
|
|
34
|
+
"types": "./lib/types/client/index.d.ts",
|
|
35
|
+
"default": "./lib/client.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"lib/*.js",
|
|
41
|
+
"lib/*.js.map",
|
|
42
|
+
"lib/types/**/*.d.ts",
|
|
43
|
+
"cordis.patch.yml",
|
|
44
|
+
"README.md",
|
|
45
|
+
"README.en.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/cherrchen/dsh-plugin-multi-root-workspace.git"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@deepseek-ai/cordis": ">=4.0.1 <5.0.0",
|
|
61
|
+
"@deepseek-ai/dsh-client-connection": ">=0.1.2-alpha.4 <0.2.0",
|
|
62
|
+
"@deepseek-ai/dsh-commands": ">=0.1.2-alpha.4 <0.2.0",
|
|
63
|
+
"@deepseek-ai/dsh-fs": ">=0.1.2-alpha.4 <0.2.0",
|
|
64
|
+
"@deepseek-ai/dsh-fs-local": ">=0.1.2-alpha.4 <0.2.0",
|
|
65
|
+
"@deepseek-ai/dsh-sandbox": ">=0.1.2-alpha.4 <0.2.0",
|
|
66
|
+
"@deepseek-ai/dsh-sandbox-local": ">=0.1.2-alpha.4 <0.2.0",
|
|
67
|
+
"@deepseek-ai/dsh-sandbox-policy": ">=0.1.2-alpha.4 <0.2.0",
|
|
68
|
+
"@deepseek-ai/dsh-storage-domain": ">=0.1.2-alpha.4 <0.2.0",
|
|
69
|
+
"@deepseek-ai/dsh-subprocess": ">=0.1.2-alpha.4 <0.2.0"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
73
|
+
"@deepseek-ai/dsh": "0.1.5-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-agent": "0.1.5-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-app-boot": "0.1.5-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-base": "0.1.5-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.5-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-workspace": "0.1.5-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-commands": "0.1.5-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-fs": "0.1.5-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-fs-local": "0.1.5-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-fs-sandbox": "0.1.5-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-sandbox": "0.1.5-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-sandbox-local": "0.1.5-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.5-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-storage": "0.1.5-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.5-rc.2",
|
|
95
|
+
"@deepseek-ai/dsh-storage-json": "0.1.5-rc.2",
|
|
96
|
+
"@deepseek-ai/dsh-subprocess": "0.1.5-rc.2",
|
|
97
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2",
|
|
98
|
+
"@deepseek-ai/dsh-workspace": "0.1.5-rc.2",
|
|
99
|
+
"@testing-library/react": "^16.3.3",
|
|
100
|
+
"@types/node": "^22.0.0",
|
|
101
|
+
"@types/react": "~18.3.31",
|
|
102
|
+
"clsx": "^2.0.0",
|
|
103
|
+
"jsdom": "^29.1.1",
|
|
104
|
+
"oxlint": "^1.76.0",
|
|
105
|
+
"react": "^18.3.1",
|
|
106
|
+
"react-dom": "^18.3.1",
|
|
107
|
+
"tsdown": "^0.22.0",
|
|
108
|
+
"typescript": "^6.0.3",
|
|
109
|
+
"vitest": "^4.1.8",
|
|
110
|
+
"yaml": "^2.9.0"
|
|
111
|
+
},
|
|
112
|
+
"dsh": {
|
|
113
|
+
"bundle": {
|
|
114
|
+
"patch": "./cordis.patch.yml"
|
|
115
|
+
},
|
|
116
|
+
"client": {
|
|
117
|
+
"platform": "web",
|
|
118
|
+
"inject": [
|
|
119
|
+
"@deepseek-ai/dsh-client-connection",
|
|
120
|
+
"@deepseek-ai/dsh-client-locale",
|
|
121
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
122
|
+
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"dependencies": {
|
|
127
|
+
"zod": "^4.6.2"
|
|
128
|
+
},
|
|
129
|
+
"scripts": {
|
|
130
|
+
"build": "pnpm run build:types && pnpm run bundle",
|
|
131
|
+
"build:types": "tsc -p tsconfig.host.json && tsc -p tsconfig.client.json",
|
|
132
|
+
"bundle": "tsdown",
|
|
133
|
+
"docs:check": "node scripts/check-docs.mjs",
|
|
134
|
+
"kernel:probe": "node scripts/check-kernel-runner.mjs",
|
|
135
|
+
"lint": "oxlint",
|
|
136
|
+
"release": "node scripts/bump-version.mjs",
|
|
137
|
+
"smoke": "pnpm smoke:compose && pnpm smoke:behavior && pnpm smoke:journey",
|
|
138
|
+
"smoke:behavior": "node scripts/smoke-behavior.mjs",
|
|
139
|
+
"smoke:compose": "node scripts/smoke-compose.mjs",
|
|
140
|
+
"smoke:journey": "node scripts/smoke-journey.mjs",
|
|
141
|
+
"test": "vitest run",
|
|
142
|
+
"test:watch": "vitest",
|
|
143
|
+
"typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.client.json --noEmit"
|
|
144
|
+
}
|
|
145
|
+
}
|