@dsh-electron/dsh-plugin-multi-root-workspace 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.en.md +77 -0
- package/CHANGELOG.md +77 -0
- package/README.en.md +94 -20
- package/README.md +95 -19
- package/cordis.patch.yml +22 -1
- package/lib/client.js +102 -58
- package/lib/client.js.map +1 -1
- package/lib/{command-BSkrYOAf.js → command-DyOWujPo.js} +24 -61
- package/lib/command.js +2 -2
- package/lib/compat-nFYqN5wt.js +267 -0
- package/lib/compat.js +2 -0
- package/lib/{fs-D7UoCb7T.js → fs-D_Sw3o5z.js} +7 -3
- package/lib/fs.js +1 -1
- package/lib/index.js +8 -6
- package/lib/instructions-BRGoC82G.js +635 -0
- package/lib/instructions.js +2 -0
- package/lib/registry-DySMbjgW.js +793 -0
- package/lib/registry-lease-win32-dy8TQMD9.js +93 -0
- package/lib/registry.js +2 -466
- package/lib/{sandbox-BhuMTDgH.js → sandbox-D8Fw95FM.js} +51 -4
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +1 -1
- package/lib/types/client/locales.d.ts +6 -0
- package/lib/types/command.d.ts +17 -0
- package/lib/types/compat/agent-instructions.d.ts +138 -0
- package/lib/types/compat/dsh-version.d.ts +119 -0
- package/lib/types/compat/llm-message.d.ts +34 -0
- package/lib/types/compat/sandbox-confine.d.ts +58 -0
- package/lib/types/compat.d.ts +85 -0
- package/lib/types/contract.d.ts +6 -8
- package/lib/types/index.d.ts +19 -5
- package/lib/types/instructions.d.ts +124 -0
- package/lib/types/registry-lease-win32.d.ts +29 -0
- package/lib/types/registry-lease.d.ts +62 -0
- package/lib/types/registry.d.ts +130 -19
- package/lib/types/roots.d.ts +1 -1
- package/lib/types/sandbox.d.ts +22 -2
- package/package.json +60 -13
- package/lib/{roots-DyT2jgEP.js → roots-DasWEBJk.js} +1 -1
package/cordis.patch.yml
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
#
|
|
14
14
|
# The last two rows are additive and self-softening: `multi-root-registry` waits
|
|
15
15
|
# for the storage domain (absent only in compositions that mount no storage at
|
|
16
|
-
# all)
|
|
16
|
+
# all) and takes a store-wide kernel lease beside the JSON document so two DSH
|
|
17
|
+
# processes sharing $DSH_HOME cannot each grant a stale snapshot (ADR-0007);
|
|
18
|
+
# `multi-root-command` registers the `/workspace-folders` command
|
|
17
19
|
# wherever a command registry exists and the browser panel channel wherever a
|
|
18
20
|
# host Connection and web server exist (see
|
|
19
21
|
# docs/troubleshooting/panel-channel-http-405.md for why both must be declared
|
|
@@ -30,6 +32,12 @@
|
|
|
30
32
|
- id: sandbox
|
|
31
33
|
disabled: true
|
|
32
34
|
- insert:
|
|
35
|
+
# The compatibility gate comes first, and every security-relevant row below
|
|
36
|
+
# injects `multiRootCompat`. If the installed DSH release is not on this
|
|
37
|
+
# plugin's allowlist, this row throws and none of them start, rather than
|
|
38
|
+
# widening a sandbox profile whose shape was never verified (ADR-0009).
|
|
39
|
+
- id: multi-root-compat
|
|
40
|
+
name: '@dsh-electron/dsh-plugin-multi-root-workspace/compat'
|
|
33
41
|
- id: multi-root-fs
|
|
34
42
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/fs'
|
|
35
43
|
- id: multi-root-sandbox
|
|
@@ -38,6 +46,19 @@
|
|
|
38
46
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/scope'
|
|
39
47
|
- id: multi-root-registry
|
|
40
48
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/registry'
|
|
49
|
+
config:
|
|
50
|
+
# Store-wide kernel lease beside the JSON domain document. A custom
|
|
51
|
+
# storage-json.root must override this path in lockstep (ADR-0007).
|
|
52
|
+
leasePath: !!js dshHomePath('storages/multi_root_workspace.lock')
|
|
53
|
+
# Upstream `agent-instructions` only discovers instruction files from the
|
|
54
|
+
# session cwd upward, so an additional root's own AGENTS.md is invisible to
|
|
55
|
+
# the model. This row closes that gap for the additional roots only; the
|
|
56
|
+
# primary root and the user-global file stay upstream's. The budget is the
|
|
57
|
+
# total for ALL additional roots, not per root.
|
|
58
|
+
- id: multi-root-instructions
|
|
59
|
+
name: '@dsh-electron/dsh-plugin-multi-root-workspace/instructions'
|
|
60
|
+
config:
|
|
61
|
+
maxBytes: 65536
|
|
41
62
|
- id: multi-root-command
|
|
42
63
|
name: '@dsh-electron/dsh-plugin-multi-root-workspace/command'
|
|
43
64
|
- id: multi-root-client
|
package/lib/client.js
CHANGED
|
@@ -5139,8 +5139,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5139
5139
|
/** The absolute logical channel both halves speak over. */
|
|
5140
5140
|
const PANEL_CHANNEL = "/multi-root-workspace";
|
|
5141
5141
|
object({
|
|
5142
|
-
|
|
5143
|
-
sessionId: string().optional(),
|
|
5142
|
+
sessionId: string().min(1),
|
|
5144
5143
|
entry: object({
|
|
5145
5144
|
ordinal: number().int().positive(),
|
|
5146
5145
|
id: string(),
|
|
@@ -5283,6 +5282,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5283
5282
|
"panel.additional": "附属工作目录",
|
|
5284
5283
|
"panel.empty": "还没有附属工作目录。",
|
|
5285
5284
|
"panel.emptyHint": "添加后,Agent 可在同一会话中读写该目录;macOS/Linux 的受限 bash 同步生效,Windows 当前仅覆盖 fs 写路径。",
|
|
5285
|
+
"panel.noSession": "当前没有活动会话",
|
|
5286
5286
|
"panel.loading": "正在读取…",
|
|
5287
5287
|
"panel.retry": "重试",
|
|
5288
5288
|
"panel.close": "关闭",
|
|
@@ -5316,9 +5316,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5316
5316
|
"error.not-found": "没有找到对应的登记项。",
|
|
5317
5317
|
"error.invalid-ref": "请求缺少必要的标识。",
|
|
5318
5318
|
"error.storage-unavailable": "根目录登记的存储不可用,请检查 $DSH_HOME/storages 下的文件。",
|
|
5319
|
+
"error.registry-contended": "根目录登记正由另一个 DSH 进程占用;本进程不会授予附加根。关闭另一个进程后点重试。",
|
|
5319
5320
|
"error.reveal-unavailable": "无法打开系统的文件管理器;登记项不受影响,路径仍可复制。",
|
|
5320
5321
|
"error.unavailable": "无法连接到 dsh 主进程。",
|
|
5321
5322
|
"error.copy-failed": "无法写入剪贴板,请检查浏览器的剪贴板权限。",
|
|
5323
|
+
"error.session-not-found": "找不到该会话;请先打开一个工作区会话。",
|
|
5322
5324
|
"error.fallback": "操作失败。"
|
|
5323
5325
|
};
|
|
5324
5326
|
/** English copy, pinned to the Chinese key set. */
|
|
@@ -5332,6 +5334,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5332
5334
|
"panel.additional": "Secondary working directories",
|
|
5333
5335
|
"panel.empty": "No secondary working directories yet.",
|
|
5334
5336
|
"panel.emptyHint": "Once added, the agent can use the folder in this session; confined bash follows on macOS/Linux, while Windows currently covers fs writes only.",
|
|
5337
|
+
"panel.noSession": "No active session",
|
|
5335
5338
|
"panel.loading": "Loading…",
|
|
5336
5339
|
"panel.retry": "Retry",
|
|
5337
5340
|
"panel.close": "Close",
|
|
@@ -5365,9 +5368,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5365
5368
|
"error.not-found": "No registration matches that reference.",
|
|
5366
5369
|
"error.invalid-ref": "The request is missing a required identity.",
|
|
5367
5370
|
"error.storage-unavailable": "The root registry store is unavailable; check the files under $DSH_HOME/storages.",
|
|
5371
|
+
"error.registry-contended": "The root registry is owned by another DSH process; this process grants no additional roots. Close the other process, then retry.",
|
|
5368
5372
|
"error.reveal-unavailable": "Could not open the system file manager; the registration is untouched, and the path can still be copied.",
|
|
5369
5373
|
"error.unavailable": "Could not reach the dsh host process.",
|
|
5370
5374
|
"error.copy-failed": "Could not write to the clipboard; check the browser's clipboard permission.",
|
|
5375
|
+
"error.session-not-found": "That session is not active; open a workspace session first.",
|
|
5371
5376
|
"error.fallback": "The operation failed."
|
|
5372
5377
|
};
|
|
5373
5378
|
//#endregion
|
|
@@ -5956,7 +5961,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5956
5961
|
const [state, setState] = (0, react.useState)({
|
|
5957
5962
|
view: void 0,
|
|
5958
5963
|
error: void 0,
|
|
5959
|
-
busy: false
|
|
5964
|
+
busy: false,
|
|
5965
|
+
noSession: false
|
|
5960
5966
|
});
|
|
5961
5967
|
const [manualPath, setManualPath] = (0, react.useState)("");
|
|
5962
5968
|
const [aliasFor, setAliasFor] = (0, react.useState)(void 0);
|
|
@@ -5972,7 +5978,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5972
5978
|
}, []);
|
|
5973
5979
|
const requestBase = (0, react.useCallback)(() => {
|
|
5974
5980
|
const current = props.sessionId?.();
|
|
5975
|
-
return current === void 0 ?
|
|
5981
|
+
return current === void 0 || current === "" ? void 0 : { sessionId: current };
|
|
5976
5982
|
}, [props.sessionId]);
|
|
5977
5983
|
(0, react.useEffect)(() => {
|
|
5978
5984
|
dialogRef.current?.focus();
|
|
@@ -6008,29 +6014,43 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6008
6014
|
setState({
|
|
6009
6015
|
view: void 0,
|
|
6010
6016
|
error: new PanelError("unavailable", "no connection"),
|
|
6011
|
-
busy: false
|
|
6017
|
+
busy: false,
|
|
6018
|
+
noSession: false
|
|
6019
|
+
});
|
|
6020
|
+
return;
|
|
6021
|
+
}
|
|
6022
|
+
const base = requestBase();
|
|
6023
|
+
if (base === void 0) {
|
|
6024
|
+
setState({
|
|
6025
|
+
view: void 0,
|
|
6026
|
+
error: void 0,
|
|
6027
|
+
busy: false,
|
|
6028
|
+
noSession: true
|
|
6012
6029
|
});
|
|
6013
6030
|
return;
|
|
6014
6031
|
}
|
|
6015
6032
|
const epoch = ++requestEpoch.current;
|
|
6016
6033
|
setState((previous) => ({
|
|
6017
6034
|
...previous,
|
|
6018
|
-
busy: true
|
|
6035
|
+
busy: true,
|
|
6036
|
+
noSession: false
|
|
6019
6037
|
}));
|
|
6020
6038
|
try {
|
|
6021
|
-
const view = await panel.call("list",
|
|
6039
|
+
const view = await panel.call("list", base);
|
|
6022
6040
|
if (!mounted.current || epoch !== requestEpoch.current) return;
|
|
6023
6041
|
setState({
|
|
6024
6042
|
view,
|
|
6025
6043
|
error: void 0,
|
|
6026
|
-
busy: false
|
|
6044
|
+
busy: false,
|
|
6045
|
+
noSession: false
|
|
6027
6046
|
});
|
|
6028
6047
|
} catch (error) {
|
|
6029
6048
|
if (!mounted.current || epoch !== requestEpoch.current) return;
|
|
6030
6049
|
setState({
|
|
6031
6050
|
view: void 0,
|
|
6032
6051
|
error: asPanelError(error),
|
|
6033
|
-
busy: false
|
|
6052
|
+
busy: false,
|
|
6053
|
+
noSession: false
|
|
6034
6054
|
});
|
|
6035
6055
|
}
|
|
6036
6056
|
}, [panel, requestBase]);
|
|
@@ -6044,21 +6064,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6044
6064
|
*/
|
|
6045
6065
|
const mutate = (0, react.useCallback)(async (endpoint, payload) => {
|
|
6046
6066
|
if (panel === void 0) return false;
|
|
6067
|
+
const base = requestBase();
|
|
6068
|
+
if (base === void 0) {
|
|
6069
|
+
setState({
|
|
6070
|
+
view: void 0,
|
|
6071
|
+
error: void 0,
|
|
6072
|
+
busy: false,
|
|
6073
|
+
noSession: true
|
|
6074
|
+
});
|
|
6075
|
+
return false;
|
|
6076
|
+
}
|
|
6047
6077
|
const epoch = ++requestEpoch.current;
|
|
6048
6078
|
setState((previous) => ({
|
|
6049
6079
|
...previous,
|
|
6050
|
-
busy: true
|
|
6080
|
+
busy: true,
|
|
6081
|
+
noSession: false
|
|
6051
6082
|
}));
|
|
6052
6083
|
try {
|
|
6053
6084
|
const view = await panel.call(endpoint, {
|
|
6054
|
-
...
|
|
6085
|
+
...base,
|
|
6055
6086
|
...payload
|
|
6056
6087
|
});
|
|
6057
6088
|
if (!mounted.current || epoch !== requestEpoch.current) return false;
|
|
6058
6089
|
setState({
|
|
6059
6090
|
view,
|
|
6060
6091
|
error: void 0,
|
|
6061
|
-
busy: false
|
|
6092
|
+
busy: false,
|
|
6093
|
+
noSession: false
|
|
6062
6094
|
});
|
|
6063
6095
|
return true;
|
|
6064
6096
|
} catch (error) {
|
|
@@ -6105,14 +6137,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6105
6137
|
/** Reveal one root: the answer is the revealed path, not a new view. */
|
|
6106
6138
|
const reveal = (0, react.useCallback)(async (root) => {
|
|
6107
6139
|
if (panel === void 0) return;
|
|
6140
|
+
const base = requestBase();
|
|
6141
|
+
if (base === void 0) {
|
|
6142
|
+
setState({
|
|
6143
|
+
view: void 0,
|
|
6144
|
+
error: void 0,
|
|
6145
|
+
busy: false,
|
|
6146
|
+
noSession: true
|
|
6147
|
+
});
|
|
6148
|
+
return;
|
|
6149
|
+
}
|
|
6108
6150
|
const epoch = ++requestEpoch.current;
|
|
6109
6151
|
setState((previous) => ({
|
|
6110
6152
|
...previous,
|
|
6111
|
-
busy: true
|
|
6153
|
+
busy: true,
|
|
6154
|
+
noSession: false
|
|
6112
6155
|
}));
|
|
6113
6156
|
try {
|
|
6114
6157
|
await panel.call("reveal", {
|
|
6115
|
-
...
|
|
6158
|
+
...base,
|
|
6116
6159
|
entry: entryOf(root)
|
|
6117
6160
|
});
|
|
6118
6161
|
if (!mounted.current || epoch !== requestEpoch.current) return;
|
|
@@ -6189,7 +6232,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6189
6232
|
})]
|
|
6190
6233
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6191
6234
|
className: "mrfw-body",
|
|
6192
|
-
children: [
|
|
6235
|
+
children: [state.noSession ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6236
|
+
className: "mrfw-note",
|
|
6237
|
+
children: t("panel.noSession")
|
|
6238
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
6193
6239
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6194
6240
|
className: "mrfw-description",
|
|
6195
6241
|
children: t("panel.subtitle")
|
|
@@ -6373,49 +6419,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6373
6419
|
}, key);
|
|
6374
6420
|
})
|
|
6375
6421
|
]
|
|
6376
|
-
}),
|
|
6377
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6378
|
-
className: "mrfw-actions",
|
|
6379
|
-
children: [
|
|
6380
|
-
props.pickDirectory === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
|
|
6381
|
-
variant: "outline",
|
|
6382
|
-
disabled: state.busy,
|
|
6383
|
-
onClick: () => {
|
|
6384
|
-
addViaPicker();
|
|
6385
|
-
},
|
|
6386
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {}),
|
|
6387
|
-
label: t("panel.add")
|
|
6388
|
-
}),
|
|
6389
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6390
|
-
className: "mrfw-input",
|
|
6391
|
-
value: manualPath,
|
|
6392
|
-
placeholder: t("panel.addManual"),
|
|
6393
|
-
onChange: (event) => {
|
|
6394
|
-
setManualPath(event.target.value);
|
|
6395
|
-
},
|
|
6396
|
-
onKeyDown: (event) => {
|
|
6397
|
-
if (event.key === "Enter") addManualPath();
|
|
6398
|
-
}
|
|
6399
|
-
}),
|
|
6400
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
|
|
6401
|
-
onClick: () => {
|
|
6402
|
-
addManualPath();
|
|
6403
|
-
},
|
|
6404
|
-
disabled: state.busy || manualPath.trim() === "",
|
|
6405
|
-
variant: "primary",
|
|
6406
|
-
children: t("panel.addConfirm")
|
|
6407
|
-
}),
|
|
6408
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
|
|
6409
|
-
onClick: () => {
|
|
6410
|
-
refresh();
|
|
6411
|
-
},
|
|
6412
|
-
disabled: state.busy,
|
|
6413
|
-
variant: "outline",
|
|
6414
|
-
children: t("panel.retry")
|
|
6415
|
-
})
|
|
6416
|
-
]
|
|
6417
6422
|
})
|
|
6418
|
-
]
|
|
6423
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6424
|
+
className: "mrfw-actions",
|
|
6425
|
+
children: [
|
|
6426
|
+
state.noSession || props.pickDirectory === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
|
|
6427
|
+
variant: "outline",
|
|
6428
|
+
disabled: state.busy,
|
|
6429
|
+
onClick: () => {
|
|
6430
|
+
addViaPicker();
|
|
6431
|
+
},
|
|
6432
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {}),
|
|
6433
|
+
label: t("panel.add")
|
|
6434
|
+
}),
|
|
6435
|
+
state.noSession ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6436
|
+
className: "mrfw-input",
|
|
6437
|
+
value: manualPath,
|
|
6438
|
+
placeholder: t("panel.addManual"),
|
|
6439
|
+
onChange: (event) => {
|
|
6440
|
+
setManualPath(event.target.value);
|
|
6441
|
+
},
|
|
6442
|
+
onKeyDown: (event) => {
|
|
6443
|
+
if (event.key === "Enter") addManualPath();
|
|
6444
|
+
}
|
|
6445
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
|
|
6446
|
+
onClick: () => {
|
|
6447
|
+
addManualPath();
|
|
6448
|
+
},
|
|
6449
|
+
disabled: state.busy || manualPath.trim() === "",
|
|
6450
|
+
variant: "primary",
|
|
6451
|
+
children: t("panel.addConfirm")
|
|
6452
|
+
})] }),
|
|
6453
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
|
|
6454
|
+
onClick: () => {
|
|
6455
|
+
refresh();
|
|
6456
|
+
},
|
|
6457
|
+
disabled: state.busy,
|
|
6458
|
+
variant: "outline",
|
|
6459
|
+
children: t("panel.retry")
|
|
6460
|
+
})
|
|
6461
|
+
]
|
|
6462
|
+
})]
|
|
6419
6463
|
})]
|
|
6420
6464
|
})]
|
|
6421
6465
|
});
|
|
@@ -6493,7 +6537,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6493
6537
|
* and the workspace UI service are composed by the surface, not by this
|
|
6494
6538
|
* plugin, and neither is a dependency it may assume (the footer action exists
|
|
6495
6539
|
* on surfaces where they do not). Absence is a supported state — the panel then
|
|
6496
|
-
*
|
|
6540
|
+
* shows "No active session" and does not ask the host to guess a workspace root.
|
|
6497
6541
|
* @param ctx - the client context.
|
|
6498
6542
|
* @param name - the service key to read.
|
|
6499
6543
|
* @returns the service, or `undefined` when this composition has none.
|