@dsh-electron/dsh-plugin-multi-root-workspace 0.1.5 → 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 +13 -1
- package/CHANGELOG.md +13 -1
- package/README.en.md +12 -10
- package/README.md +11 -11
- 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-82Y27Iom.js → compat-B6ZqbQBy.js} +21 -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-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/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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { a as splitConfined, i as DialectUnrecognizedError, n as widenWindowsProfile, o as widenProfileArgs } from "./windows-profile-CiFsFYIV.js";
|
|
2
|
+
import { SandboxUnavailableError } from "@deepseek-ai/dsh-sandbox";
|
|
3
|
+
import { LocalSandboxProvider } from "@deepseek-ai/dsh-sandbox-local";
|
|
4
|
+
//#region src/compat/sandbox-confine.ts
|
|
5
|
+
/** Whether a value is a thenable, i.e. whether the installed `confine` is async. */
|
|
6
|
+
function isThenable(value) {
|
|
7
|
+
return typeof value?.then === "function";
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Call the upstream `confine` and widen its result, preserving sync/async shape.
|
|
11
|
+
*
|
|
12
|
+
* @param base - the upstream `confine`, already bound to its provider.
|
|
13
|
+
* @param widen - the pure transformation to apply to the upstream result. It
|
|
14
|
+
* runs exactly once per call, on whichever release; it may throw, and the
|
|
15
|
+
* throw surfaces synchronously or as a rejection to match the base shape.
|
|
16
|
+
* @param argv - the caller's argv, forwarded verbatim.
|
|
17
|
+
* @param policy - the per-call policy, forwarded verbatim.
|
|
18
|
+
* @param signal - forwarded when the installed release accepts it.
|
|
19
|
+
* @returns the widened result, in the shape the installed release uses.
|
|
20
|
+
*/
|
|
21
|
+
function widenConfined(base, widen, argv, policy, signal) {
|
|
22
|
+
const confined = base(argv, policy, signal);
|
|
23
|
+
return isThenable(confined) ? confined.then(widen) : widen(confined);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/sandbox.ts
|
|
27
|
+
/**
|
|
28
|
+
* `MultiRootSandboxProvider`: the kernel-sandbox dialect provider mounted as
|
|
29
|
+
* `ctx.sandbox`.
|
|
30
|
+
*
|
|
31
|
+
* Every confined execution in this composition funnels through `confine()` —
|
|
32
|
+
* the bash executor wraps `['bash','-c',command]`, and the terminal backend
|
|
33
|
+
* wraps its PTY spawn argv — so widening the grant here is what gives bash, PTY,
|
|
34
|
+
* and the fs fence one and the same root set (requirement §13). The bash
|
|
35
|
+
* executor itself is therefore left upstream: it never computes roots.
|
|
36
|
+
*
|
|
37
|
+
* POSIX widening lives in `./dialects.ts`, Windows capabilities in
|
|
38
|
+
* `./windows-profile.ts` and `./windows-runner.ts`: the upstream profile builders
|
|
39
|
+
* are unreachable in the published package, so the additional roots are grafted
|
|
40
|
+
* onto the profile `super.confine` actually produced (recognize the dialect
|
|
41
|
+
* structurally, clone its grant spelling, fail loudly when the shape is not
|
|
42
|
+
* known). This class owns only the policy that decides WHEN to graft:
|
|
43
|
+
*
|
|
44
|
+
* 1. with no additional roots, or under any mode other than `workspace-write`,
|
|
45
|
+
* the upstream result is returned ELEMENT FOR ELEMENT — same argv, same
|
|
46
|
+
* `enforcement`, same `denialSignatures`, same `runnerFailureRules`;
|
|
47
|
+
* 2. `enforcement`, `denialSignatures`, and `runnerFailureRules` are NEVER
|
|
48
|
+
* recomputed here, even when the argv is widened — the bash executor derives
|
|
49
|
+
* its denial and enforcement reporting from them;
|
|
50
|
+
* 3. only `argv` may differ, and a dialect whose shape cannot be extended fails
|
|
51
|
+
* loudly (`SandboxUnavailableError`) instead of silently confining to the
|
|
52
|
+
* primary root alone.
|
|
53
|
+
*
|
|
54
|
+
* @module @dsh-electron/dsh-plugin-multi-root-workspace/sandbox
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Local process-sandbox provider over the multi-root scope. Registers as
|
|
58
|
+
* `ctx.sandbox`; the runner chain, probing, denial dialects, and Windows ACL
|
|
59
|
+
* grant mechanics stay the upstream implementation's.
|
|
60
|
+
*/
|
|
61
|
+
var MultiRootSandboxProvider = class extends LocalSandboxProvider {
|
|
62
|
+
static inject = [
|
|
63
|
+
"multiRootCompat",
|
|
64
|
+
"sandboxPolicy",
|
|
65
|
+
"multiRootScope"
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Wrap `argv` so it executes confined under `policy` on this host, granting
|
|
69
|
+
* the scope's additional roots inside the SAME dialect profile.
|
|
70
|
+
*
|
|
71
|
+
* Under `read-only` nothing is added (the mode denies every write, additional
|
|
72
|
+
* roots included), and `danger-full-access` never reaches here at all: the
|
|
73
|
+
* bash executor and the PTY backend both hand their argv straight to the local
|
|
74
|
+
* runtime in that mode. Widening therefore only ever ADDS the scope's
|
|
75
|
+
* additional roots to an upstream `workspace-write` profile.
|
|
76
|
+
*
|
|
77
|
+
* The return shape is the INSTALLED upstream provider's: synchronous on a
|
|
78
|
+
* release with a synchronous `confine`, a promise on a release with an
|
|
79
|
+
* asynchronous one. `widenConfined` preserves whichever it is — see
|
|
80
|
+
* `./compat/sandbox-confine.ts`.
|
|
81
|
+
* @param argv - the exact argv the caller is about to spawn.
|
|
82
|
+
* @param policy - the file-effect policy this execution runs under.
|
|
83
|
+
* @param signal - cancellation, forwarded to releases that accept it.
|
|
84
|
+
* @returns the argv to spawn instead, plus the selected backend's facts.
|
|
85
|
+
*/
|
|
86
|
+
confine(argv, policy, signal) {
|
|
87
|
+
const base = super.confine;
|
|
88
|
+
return widenConfined(base.bind(this), (confined) => this.widenForScope(confined, argv, policy), argv, policy, signal);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Graft the scope's additional roots onto one upstream `confine` result.
|
|
92
|
+
*
|
|
93
|
+
* Pure with respect to the upstream call: it only ever reads the scope and
|
|
94
|
+
* rewrites `argv`. `enforcement`, `denialSignatures` and `runnerFailureRules`
|
|
95
|
+
* are passed through untouched, because the bash executor derives its denial
|
|
96
|
+
* and enforcement reporting from them.
|
|
97
|
+
* @param confined - the upstream result for this call.
|
|
98
|
+
* @param argv - the caller's argv, needed to re-split the wrapped form.
|
|
99
|
+
* @param policy - the per-call policy.
|
|
100
|
+
* @returns the upstream result element for element, or one with a widened argv.
|
|
101
|
+
*/
|
|
102
|
+
widenForScope(confined, argv, policy) {
|
|
103
|
+
if (policy.mode !== "workspace-write") return confined;
|
|
104
|
+
const scope = this.ctx.multiRootScope.resolve(policy);
|
|
105
|
+
if (scope.additionalRoots.length === 0) return confined;
|
|
106
|
+
try {
|
|
107
|
+
const shape = splitConfined(confined.argv, argv);
|
|
108
|
+
if (shape.dialect === "windows-acl") return {
|
|
109
|
+
...confined,
|
|
110
|
+
argv: [
|
|
111
|
+
...widenWindowsProfile(shape.profileArgs, scope.additionalRoots, scope.primaryRoot),
|
|
112
|
+
"--",
|
|
113
|
+
...argv
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
const profileArgs = widenProfileArgs(shape.dialect, shape.profileArgs, policy, scope.additionalRoots);
|
|
117
|
+
return {
|
|
118
|
+
...confined,
|
|
119
|
+
argv: [
|
|
120
|
+
...profileArgs,
|
|
121
|
+
"--",
|
|
122
|
+
...argv
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (!(error instanceof DialectUnrecognizedError)) throw error;
|
|
127
|
+
throw new SandboxUnavailableError(policy.mode, `multi-root workspace: cannot grant the additional workspace roots [${scope.additionalRoots.join(", ")}] under the primary root ${scope.primaryRoot} — ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
//#endregion
|
|
132
|
+
export { widenConfined as n, MultiRootSandboxProvider as t };
|
package/lib/sandbox.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as MultiRootSandboxProvider } from "./sandbox-
|
|
1
|
+
import { t as MultiRootSandboxProvider } from "./sandbox-BRXl_FpF.js";
|
|
2
2
|
export { MultiRootSandboxProvider, MultiRootSandboxProvider as default };
|
package/lib/scope.js
CHANGED
|
@@ -71,7 +71,7 @@ const MULTI_ROOT_CONTEXT_NAME = "multi-root:scope";
|
|
|
71
71
|
* @returns the stable topology sentence.
|
|
72
72
|
*/
|
|
73
73
|
function renderWorkspaceRootsContext(primaryRoot, additionalRoots) {
|
|
74
|
-
return `Current DSH workspace roots: ${JSON.stringify(additionalRoots)} are additional roots of this session's workspace. Under workspace-write they may be modified like the session workspace; the session
|
|
74
|
+
return `Current DSH workspace roots: ${JSON.stringify(additionalRoots)} are additional roots of this session's workspace. Under workspace-write they may be modified like the session workspace; the session origin remains the primary root (${JSON.stringify(primaryRoot)}); changing the working directory does not change these grants.`;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* The `ctx.multiRootScope` service. Providers ask it — never the filesystem —
|
|
@@ -81,6 +81,14 @@ function renderWorkspaceRootsContext(primaryRoot, additionalRoots) {
|
|
|
81
81
|
var MultiRootScopeService = class extends Service {
|
|
82
82
|
/** Additional roots keyed by canonical primary root; empty until roots are registered. */
|
|
83
83
|
rootsByPrimary = /* @__PURE__ */ new Map();
|
|
84
|
+
listeners = /* @__PURE__ */ new Set();
|
|
85
|
+
/** Observe registry publications; consumers still resolve authority from scopeOf(). */
|
|
86
|
+
subscribe(listener) {
|
|
87
|
+
this.listeners.add(listener);
|
|
88
|
+
return () => {
|
|
89
|
+
this.listeners.delete(listener);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
84
92
|
constructor(ctx) {
|
|
85
93
|
super(ctx, "multiRootScope");
|
|
86
94
|
ctx.inject(["systemPrompt", "sandboxPolicy"], (scope) => {
|
|
@@ -132,11 +140,9 @@ var MultiRootScopeService = class extends Service {
|
|
|
132
140
|
*/
|
|
133
141
|
setAdditionalRoots(primaryRoot, roots) {
|
|
134
142
|
const key = canonicalPath(primaryRoot);
|
|
135
|
-
if (roots.length === 0)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
this.rootsByPrimary.set(key, [...roots]);
|
|
143
|
+
if (roots.length === 0) this.rootsByPrimary.delete(key);
|
|
144
|
+
else this.rootsByPrimary.set(key, [...roots]);
|
|
145
|
+
for (const listener of this.listeners) listener();
|
|
140
146
|
}
|
|
141
147
|
};
|
|
142
148
|
//#endregion
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/compat/session-directory.ts
|
|
2
|
+
function effectiveDirectory(ctx, session) {
|
|
3
|
+
const service = ctx.get("workingDirectory");
|
|
4
|
+
if (service === void 0) return session.header.cwd;
|
|
5
|
+
if (typeof service.get !== "function") throw new Error("multi-root workspace: unsupported workingDirectory service");
|
|
6
|
+
return service.get(session);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { effectiveDirectory as t };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Lazy additional-root tree and bounded text previews; requests carry a session and exact root. */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import type { RootEntryView } from '../contract.ts';
|
|
4
|
+
import type { PanelClient } from './panel-client.ts';
|
|
5
|
+
import type { Translate } from './WorkspaceFoldersAction.tsx';
|
|
6
|
+
export interface RootFileTreeProps {
|
|
7
|
+
panel: PanelClient;
|
|
8
|
+
entry: RootEntryView;
|
|
9
|
+
sessionId: () => string | undefined;
|
|
10
|
+
t: Translate;
|
|
11
|
+
}
|
|
12
|
+
export declare function RootFileTree(props: RootFileTreeProps): ReactNode;
|
|
@@ -36,8 +36,11 @@ export interface WorkspaceFoldersActionProps {
|
|
|
36
36
|
* than a value because the selection changes while the panel stays mounted.
|
|
37
37
|
*/
|
|
38
38
|
readonly sessionId?: () => string | undefined;
|
|
39
|
+
readonly subscribeSession?: (listener: () => void) => () => void;
|
|
39
40
|
/** Whether the sidebar renders wide content (`false` in the icon rail). */
|
|
40
41
|
readonly wide?: boolean;
|
|
42
|
+
/** Compact launcher in the upstream Files tab's public actions slot. */
|
|
43
|
+
readonly compact?: boolean;
|
|
41
44
|
}
|
|
42
45
|
/** The sidebar footer action plus the dialog it opens. */
|
|
43
46
|
export declare function WorkspaceFoldersAction(props: WorkspaceFoldersActionProps): ReactNode;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Browser half of the multi-root workspace plugin.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Sidebar footer and optional Files actions open the Workspace Folders dialog.
|
|
5
|
+
* Everything it needs arrives through services the composition already
|
|
6
6
|
* provides — `slots` to contribute, `locale` for its copy, `connection` for the
|
|
7
7
|
* channel to the host half, and (optionally) `uiWorkspace` for the composed
|
|
8
8
|
* directory picker and the client Session Controller for the current session.
|
|
@@ -16,6 +16,17 @@
|
|
|
16
16
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
17
17
|
import { type PanelClient } from './panel-client.ts';
|
|
18
18
|
import { type Translate } from './WorkspaceFoldersAction.tsx';
|
|
19
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
20
|
+
interface SlotMap {
|
|
21
|
+
'sidebar.right.tab.files.actions': {
|
|
22
|
+
kind: 'list';
|
|
23
|
+
scope: 'session';
|
|
24
|
+
owner: {
|
|
25
|
+
readonly absolutePath: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
19
30
|
/** The footer-action cell this plugin owns. */
|
|
20
31
|
export declare const SLOT_ID = "multi-root-folders";
|
|
21
32
|
/** Services the client half needs before it may activate. */
|
|
@@ -12,12 +12,19 @@
|
|
|
12
12
|
export declare const NS = "multiRootWorkspace";
|
|
13
13
|
/** Chinese copy (the canonical shape). */
|
|
14
14
|
export declare const zh: {
|
|
15
|
+
'files.browse': string;
|
|
16
|
+
'files.reload': string;
|
|
17
|
+
'files.empty': string;
|
|
18
|
+
'files.truncated': string;
|
|
19
|
+
'files.previewTruncated': string;
|
|
15
20
|
'action.label': string;
|
|
16
21
|
'action.title': string;
|
|
17
22
|
'panel.title': string;
|
|
18
23
|
'panel.subtitle': string;
|
|
19
24
|
'panel.primary': string;
|
|
20
25
|
'panel.primaryNote': string;
|
|
26
|
+
'panel.cwd': string;
|
|
27
|
+
'panel.cwdNote': string;
|
|
21
28
|
'panel.additional': string;
|
|
22
29
|
'panel.empty': string;
|
|
23
30
|
'panel.emptyHint': string;
|
|
@@ -66,12 +73,19 @@ export declare const zh: {
|
|
|
66
73
|
export type Key = keyof typeof zh;
|
|
67
74
|
/** English copy, pinned to the Chinese key set. */
|
|
68
75
|
export declare const en: {
|
|
76
|
+
'files.browse': string;
|
|
77
|
+
'files.reload': string;
|
|
78
|
+
'files.empty': string;
|
|
79
|
+
'files.truncated': string;
|
|
80
|
+
'files.previewTruncated': string;
|
|
69
81
|
'action.label': string;
|
|
70
82
|
'action.title': string;
|
|
71
83
|
'panel.title': string;
|
|
72
84
|
'panel.subtitle': string;
|
|
73
85
|
'panel.primary': string;
|
|
74
86
|
'panel.primaryNote': string;
|
|
87
|
+
'panel.cwd': string;
|
|
88
|
+
'panel.cwdNote': string;
|
|
75
89
|
'panel.additional': string;
|
|
76
90
|
'panel.empty': string;
|
|
77
91
|
'panel.emptyHint': string;
|
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
* @module @dsh-electron/dsh-plugin-multi-root-workspace/client/styles
|
|
12
12
|
*/
|
|
13
13
|
/** The stylesheet text; every class is prefixed `mrfw-` to stay collision-free. */
|
|
14
|
-
export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-description {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mrfw-rootName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
|
|
14
|
+
export declare const STYLES = "\n/* Sidebar footer row, mirroring the host ui-settings-general trigger: the row\n * wrapper overhangs each side by 2px (width calc(100% + 4px) against the\n * shell's 12px inline padding) so the icon's ink line lands at 18px from the\n * column edge \u2014 exactly where the Settings gear below sits. */\n.mrfw-triggerRow {\n flex: none;\n display: flex;\n align-items: center;\n gap: 8px;\n width: calc(100% + 4px);\n margin: 4px -2px;\n}\n\n.mrfw-triggerRow.mrfw-railRow {\n width: 36px;\n margin: 8px 0 10px;\n}\n\n.mrfw-trigger {\n box-sizing: border-box;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n width: auto;\n height: 42px;\n margin: 0;\n padding: 0 10px 0 8px;\n border: none;\n border-radius: 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-triggerIcon {\n flex: none;\n display: inline-flex;\n}\n\n.mrfw-triggerLabel {\n overflow: hidden;\n white-space: nowrap;\n}\n\n.mrfw-triggerRail {\n flex: none;\n width: 36px;\n height: 36px;\n margin: 0;\n padding: 0;\n gap: 0;\n border-radius: 50%;\n justify-content: center;\n}\n\n.mrfw-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n}\n\n.mrfw-mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n.mrfw-dialog {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n width: min(640px, 100%);\n max-height: 100%;\n overflow: hidden;\n border: none;\n border-radius: 24px;\n background: var(--dsw-alias-bg-layer-2);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n outline: none;\n}\n\n.mrfw-header {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 22px 14px 12px 24px;\n}\n\n.mrfw-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-close {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 8px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-close:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-body {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n padding: 0 24px 24px;\n}\n\n.mrfw-description {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-alert {\n margin: 0 0 8px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.mrfw-section {\n display: flex;\n flex-direction: column;\n}\n\n.mrfw-section + .mrfw-section {\n margin-top: 8px;\n}\n\n.mrfw-sectionTitleRow {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0 4px;\n}\n\n.mrfw-sectionTitle {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-sectionNote {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.mrfw-rowBare {\n border-bottom: none;\n}\n\n/* A root entry's two lines: the display name on top, the absolute path under\n it in the tertiary voice (the workspace rows recipe of the host sidebar). */\n.mrfw-rootText {\n display: flex;\n flex: 1 1 240px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mrfw-rootName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.mrfw-rootPath {\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-note {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.mrfw-stateWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.mrfw-btn {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 14px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.mrfw-btnGhost:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnGhost:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.mrfw-btnDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n.mrfw-btnOutline {\n border: 0.5px solid var(--dsw-alias-border-l3);\n background: transparent;\n}\n\n.mrfw-btnOutline:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mrfw-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.mrfw-btnPrimary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n/* One icon-only action: a square of the capsule family, meaning in title.\n Declared after the .mrfw-btn family so its padding wins the cascade tie:\n the capsule's 0 10px padding would otherwise squeeze the icon glyph. */\n.mrfw-iconBtn {\n width: 28px;\n padding: 0;\n border-radius: 8px;\n}\n\n.mrfw-input {\n flex: 1 1 220px;\n min-width: 0;\n height: 32px;\n padding: 0 8px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n outline: none;\n transition: border-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);\n}\n\n.mrfw-input:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.mrfw-input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n.mrfw-inputInline {\n flex: 1 1 auto;\n}\n\n.mrfw-aliasEditor {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex-basis: 100%;\n}\n\n.mrfw-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n/* One control row, one height: the capsule buttons match the 32px input. */\n.mrfw-actions .mrfw-btn {\n height: 32px;\n border-radius: 16px;\n}\n\n.mrfw-fileTree { flex-basis: 100%; min-width: 0; }\n.mrfw-fileLevel { list-style: none; padding-left: 16px; margin: 4px 0; }\n.mrfw-fileEntry { display: block; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; text-align: left; cursor: pointer; padding: 4px; overflow-wrap: anywhere; }\n.mrfw-fileEntry:focus-visible { outline: 1px solid var(--dsw-alias-brand-primary); }\n.mrfw-filePreview { max-height: 320px; overflow: auto; padding: 8px; background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-size: 12px; }\n.mrfw-actions .mrfw-iconBtn {\n width: 32px;\n}\n";
|
|
@@ -30,3 +30,5 @@
|
|
|
30
30
|
* @returns the session id, or `undefined` when no main view holds one.
|
|
31
31
|
*/
|
|
32
32
|
export declare function currentSessionIdOf(sessions: unknown): string | undefined;
|
|
33
|
+
/** Observe native catalog changes when this surface provides a subscribable store. */
|
|
34
|
+
export declare function subscribeSessions(sessions: unknown, listener: () => void): () => void;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* the upgrade smoke passed on that exact version — a green CI run on an
|
|
29
29
|
* unlisted release is evidence, not authorization (ADR-0009).
|
|
30
30
|
*/
|
|
31
|
-
export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2"];
|
|
31
|
+
export declare const SUPPORTED_DSH_RELEASES: readonly ["0.1.5-rc.2", "0.1.6-alpha.1", "0.1.6-alpha.2", "0.1.7-alpha.1", "0.1.7-alpha.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2", "0.2.1-alpha.1", "0.2.1-alpha.2"];
|
|
32
32
|
/** One release this plugin claims to support. */
|
|
33
33
|
export type SupportedDshRelease = typeof SUPPORTED_DSH_RELEASES[number];
|
|
34
34
|
/**
|
|
@@ -42,9 +42,11 @@ export declare const REQUIRED_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@
|
|
|
42
42
|
* Absence is fine; a version that DISAGREES with the required packages is not —
|
|
43
43
|
* that is the mixed-installation failure this contract exists to catch.
|
|
44
44
|
*/
|
|
45
|
-
export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
45
|
+
export declare const OPTIONAL_CORE_PACKAGES: readonly ["@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
46
46
|
/** Every package the contract inspects, required first. */
|
|
47
|
-
export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
47
|
+
export declare const CORE_PACKAGES: readonly ["@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-fs-local", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-local", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-working-directory", "@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-api-workspace-files", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-sandbox-windows-acl", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt"];
|
|
48
|
+
/** Services looked up structurally; older runtimes never install these modules. */
|
|
49
|
+
export declare const HOST_RESOLVED_PACKAGES: readonly ["@deepseek-ai/dsh-working-directory"];
|
|
48
50
|
/** How this plugin reacts to an installation it does not support. */
|
|
49
51
|
export type CompatEnforcement = 'enforce' | 'warn';
|
|
50
52
|
/** The environment variable that relaxes the gate. */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import type { LoadedInstructionFile } from './agent-instructions.ts';
|
|
3
|
+
export interface NativeInstructionCopies {
|
|
4
|
+
pathOf: (file: LoadedInstructionFile) => string | undefined;
|
|
5
|
+
withdraws: (displayPath: string) => boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function nativeInstructionCopies(messages: readonly UserMessage[], cwd: string | undefined): NativeInstructionCopies;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Optional upstream workingDirectory lookup; identity always stays in the Session header. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
4
|
+
export declare function effectiveDirectory(ctx: Context, session: Session): string | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The around-dispatch seam is optional on older hosts; do not import their tool runtime. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
4
|
+
export interface ToolCaller {
|
|
5
|
+
readonly agent?: Agent;
|
|
6
|
+
readonly callId: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function aroundTool(ctx: Context, listener: (exec: ToolCaller, next: () => Promise<unknown>) => Promise<unknown>): void;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Old releases watch a session; newer ones watch a path. Probe arity, never version. */
|
|
2
|
+
import type { WorkspaceFiles, WorkspaceFileScope, WorkspaceFileWatchFrame } from '@deepseek-ai/dsh-api-workspace-files';
|
|
3
|
+
type PathWatch = (this: WorkspaceFiles, scope: WorkspaceFileScope, path: string, signal: AbortSignal) => AsyncIterable<WorkspaceFileWatchFrame>;
|
|
4
|
+
export declare function adaptPathWatch(service: WorkspaceFiles, wrap: (original: PathWatch) => PathWatch): () => void;
|
|
5
|
+
export {};
|
package/lib/types/contract.d.ts
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* - **One shape per endpoint.** {@link PanelResponseMap} is the single mapping
|
|
13
13
|
* from endpoint to response type, so the panel cannot silently decode one
|
|
14
14
|
* endpoint's answer as another's. `reveal` answers with
|
|
15
|
-
* {@link RevealedView};
|
|
16
|
-
* {@link RootsView}
|
|
15
|
+
* {@link RevealedView}; root-management endpoints answer with
|
|
16
|
+
* {@link RootsView}, and browsing returns {@link FilesView} or {@link FilePreview}.
|
|
17
|
+
* The host builds those answers in `command.ts` and the
|
|
17
18
|
* panel parses them here — one definition, two consumers.
|
|
18
19
|
* - **Runtime validation at the boundary.** The payload arrives from a browser
|
|
19
20
|
* and the answer arrives from the host; neither is trusted to match the
|
|
@@ -30,9 +31,21 @@
|
|
|
30
31
|
/** The absolute logical channel both halves speak over. */
|
|
31
32
|
export declare const PANEL_CHANNEL = "/multi-root-workspace";
|
|
32
33
|
/** Endpoint names, relative to {@link PANEL_CHANNEL}. */
|
|
33
|
-
export type PanelEndpoint = 'list' | 'add' | 'remove' | 'alias' | 'move' | 'reveal';
|
|
34
|
+
export type PanelEndpoint = 'list' | 'add' | 'remove' | 'alias' | 'move' | 'reveal' | 'files' | 'readFile';
|
|
34
35
|
/** Every endpoint name, for exhaustive iteration in tests and callers. */
|
|
35
36
|
export declare const PANEL_ENDPOINTS: readonly PanelEndpoint[];
|
|
37
|
+
export interface FilesView {
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly entries: readonly {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly type: 'file' | 'directory' | 'other';
|
|
42
|
+
}[];
|
|
43
|
+
readonly truncated: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface FilePreview {
|
|
46
|
+
readonly text: string;
|
|
47
|
+
readonly eof: boolean;
|
|
48
|
+
}
|
|
36
49
|
/**
|
|
37
50
|
* How one registered root currently stands, as the panel renders it. Kept in
|
|
38
51
|
* step with the root vocabulary in `src/roots.ts` deliberately: this module is
|
|
@@ -64,7 +77,7 @@ export interface RootEntryView {
|
|
|
64
77
|
readonly path: string;
|
|
65
78
|
readonly addedAt: string;
|
|
66
79
|
}
|
|
67
|
-
/** The panel's whole view
|
|
80
|
+
/** The panel's whole view, returned by root-management endpoints. */
|
|
68
81
|
export interface RootsView {
|
|
69
82
|
/** The canonical workspace root these registrations belong to. */
|
|
70
83
|
readonly primaryRoot: string;
|
|
@@ -74,6 +87,8 @@ export interface RootsView {
|
|
|
74
87
|
* basename.
|
|
75
88
|
*/
|
|
76
89
|
readonly primaryName?: string;
|
|
90
|
+
/** Execution directory only; never a registry key or an authorization grant. */
|
|
91
|
+
readonly effectiveCwd?: string;
|
|
77
92
|
/** The additional roots, in registry order. */
|
|
78
93
|
readonly roots: readonly RootView[];
|
|
79
94
|
/** Set when the store itself could not be read; the panel shows this verbatim. */
|
|
@@ -92,6 +107,8 @@ export interface PanelResponseMap {
|
|
|
92
107
|
readonly alias: RootsView;
|
|
93
108
|
readonly move: RootsView;
|
|
94
109
|
readonly reveal: RevealedView;
|
|
110
|
+
readonly files: FilesView;
|
|
111
|
+
readonly readFile: FilePreview;
|
|
95
112
|
}
|
|
96
113
|
/**
|
|
97
114
|
* The fields a request may carry. `sessionId` is required on every endpoint:
|
|
@@ -106,7 +123,7 @@ export interface PanelRequest {
|
|
|
106
123
|
readonly entry?: RootEntryView;
|
|
107
124
|
/** Compatibility reference; accepted only when the id is unique. */
|
|
108
125
|
readonly id?: string;
|
|
109
|
-
/** Candidate directory (add). */
|
|
126
|
+
/** Candidate directory (add), or root-relative browsing path (files/readFile). */
|
|
110
127
|
readonly path?: string;
|
|
111
128
|
/** Display alias (add/alias); empty clears it. */
|
|
112
129
|
readonly alias?: string;
|
|
@@ -135,6 +152,8 @@ export type Parsed<T> = {
|
|
|
135
152
|
readonly ok: false;
|
|
136
153
|
readonly message: string;
|
|
137
154
|
};
|
|
155
|
+
export declare function parseFilesView(value: unknown): Parsed<FilesView>;
|
|
156
|
+
export declare function parseFilePreview(value: unknown): Parsed<FilePreview>;
|
|
138
157
|
/**
|
|
139
158
|
* Validate the payload the host received for one endpoint.
|
|
140
159
|
* @param endpoint - the endpoint the channel dispatched (the RPC method).
|
package/lib/types/index.d.ts
CHANGED
|
@@ -44,8 +44,8 @@ export { availableRoots, canonicalRoot, classifyStoredRoots, expandRootInput, in
|
|
|
44
44
|
export type { AdditionalRootId, RegisteredRoot, RootRef, RootState, RootStatus, RootValidationCode, } from './roots.ts';
|
|
45
45
|
export { COMMAND_NAME, parseFoldersCommand, renderRootsReport, resolvePanelPrimaryRoot, revealArgv } from './command.ts';
|
|
46
46
|
export type { FoldersCommand } from './command.ts';
|
|
47
|
-
export { PANEL_CHANNEL, PANEL_ENDPOINTS, parsePanelCall, parseRevealedView, parseRootsView } from './contract.ts';
|
|
48
|
-
export type { PanelCall, PanelEndpoint, PanelFailure, PanelRequest, PanelResponseMap, RevealedView, RootView, RootsView, } from './contract.ts';
|
|
47
|
+
export { PANEL_CHANNEL, PANEL_ENDPOINTS, parsePanelCall, parseRevealedView, parseRootsView, parseFilesView, parseFilePreview } from './contract.ts';
|
|
48
|
+
export type { FilesView, FilePreview, PanelCall, PanelEndpoint, PanelFailure, PanelRequest, PanelResponseMap, RevealedView, RootView, RootsView, } from './contract.ts';
|
|
49
49
|
/**
|
|
50
50
|
* Carrier plugin face for the bare-package-name loader row (see the module
|
|
51
51
|
* doc). It deliberately provides nothing: every host responsibility is owned
|
package/lib/types/sandbox.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* and the fs fence one and the same root set (requirement §13). The bash
|
|
9
9
|
* executor itself is therefore left upstream: it never computes roots.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* POSIX widening lives in `./dialects.ts`, Windows capabilities in
|
|
12
|
+
* `./windows-profile.ts` and `./windows-runner.ts`: the upstream profile builders
|
|
12
13
|
* are unreachable in the published package, so the additional roots are grafted
|
|
13
14
|
* onto the profile `super.confine` actually produced (recognize the dialect
|
|
14
15
|
* structurally, clone its grant spelling, fail loudly when the shape is not
|
|
@@ -38,8 +39,6 @@ export type { Config };
|
|
|
38
39
|
*/
|
|
39
40
|
export declare class MultiRootSandboxProvider extends LocalSandboxProvider {
|
|
40
41
|
static inject: string[];
|
|
41
|
-
/** Whether the Windows ACL limitation has already been reported (once per provider). */
|
|
42
|
-
private warnedAboutWindowsAcl;
|
|
43
42
|
/**
|
|
44
43
|
* Wrap `argv` so it executes confined under `policy` on this host, granting
|
|
45
44
|
* the scope's additional roots inside the SAME dialect profile.
|
|
@@ -73,13 +72,5 @@ export declare class MultiRootSandboxProvider extends LocalSandboxProvider {
|
|
|
73
72
|
* @returns the upstream result element for element, or one with a widened argv.
|
|
74
73
|
*/
|
|
75
74
|
private widenForScope;
|
|
76
|
-
/**
|
|
77
|
-
* Report the documented first-release limitation exactly once: the Windows ACL
|
|
78
|
-
* rung grants through per-workspace write SIDs rather than argv paths, so
|
|
79
|
-
* additional roots stay unenforced for confined commands there while the
|
|
80
|
-
* in-process filesystem fence still allows them.
|
|
81
|
-
* @param rootCount - how many additional roots the scope carries.
|
|
82
|
-
*/
|
|
83
|
-
private warnAboutWindowsAcl;
|
|
84
75
|
}
|
|
85
76
|
export default MultiRootSandboxProvider;
|
package/lib/types/scope.d.ts
CHANGED
|
@@ -93,6 +93,9 @@ export declare function renderWorkspaceRootsContext(primaryRoot: string, additio
|
|
|
93
93
|
export declare class MultiRootScopeService extends Service {
|
|
94
94
|
/** Additional roots keyed by canonical primary root; empty until roots are registered. */
|
|
95
95
|
private readonly rootsByPrimary;
|
|
96
|
+
private readonly listeners;
|
|
97
|
+
/** Observe registry publications; consumers still resolve authority from scopeOf(). */
|
|
98
|
+
subscribe(listener: () => void): () => void;
|
|
96
99
|
constructor(ctx: Context);
|
|
97
100
|
/**
|
|
98
101
|
* Resolve the scope of one confined call.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** A root-set capability: removing or adding a root changes the token's identity. */
|
|
2
|
+
export declare function workspaceScopeSid(roots: readonly string[]): string;
|
|
3
|
+
export interface WindowsProfile {
|
|
4
|
+
workspace: string;
|
|
5
|
+
temp: string;
|
|
6
|
+
mode: 'workspace-write';
|
|
7
|
+
additionalRoots: string[];
|
|
8
|
+
writeSid?: string;
|
|
9
|
+
tempWriteSid?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Strictly recognize the upstream public runner contract, failing closed on new flags. */
|
|
12
|
+
export declare function parseWindowsProfile(args: readonly string[], allowAdditional?: boolean): WindowsProfile;
|
|
13
|
+
export declare function widenWindowsProfile(profile: readonly string[], roots: readonly string[], primaryRoot?: string): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Shared, canonical root selection for read-only workspace consumers. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { FsTarget } from '@deepseek-ai/dsh-fs';
|
|
4
|
+
export interface WorkspaceRoute {
|
|
5
|
+
primaryRoot: string;
|
|
6
|
+
workspaceRoot: string;
|
|
7
|
+
target: FsTarget;
|
|
8
|
+
absolutePath: string;
|
|
9
|
+
}
|
|
10
|
+
/** Resolve relative paths against the primary cwd; never search other roots by basename. */
|
|
11
|
+
export declare function routeWorkspacePath(ctx: Context, primaryRoot: string, path: string, signal?: AbortSignal, cwd?: string): Promise<WorkspaceRoute | undefined>;
|