@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.
Files changed (39) hide show
  1. package/CHANGELOG.en.md +77 -0
  2. package/CHANGELOG.md +77 -0
  3. package/README.en.md +94 -20
  4. package/README.md +95 -19
  5. package/cordis.patch.yml +22 -1
  6. package/lib/client.js +102 -58
  7. package/lib/client.js.map +1 -1
  8. package/lib/{command-BSkrYOAf.js → command-DyOWujPo.js} +24 -61
  9. package/lib/command.js +2 -2
  10. package/lib/compat-nFYqN5wt.js +267 -0
  11. package/lib/compat.js +2 -0
  12. package/lib/{fs-D7UoCb7T.js → fs-D_Sw3o5z.js} +7 -3
  13. package/lib/fs.js +1 -1
  14. package/lib/index.js +8 -6
  15. package/lib/instructions-BRGoC82G.js +635 -0
  16. package/lib/instructions.js +2 -0
  17. package/lib/registry-DySMbjgW.js +793 -0
  18. package/lib/registry-lease-win32-dy8TQMD9.js +93 -0
  19. package/lib/registry.js +2 -466
  20. package/lib/{sandbox-BhuMTDgH.js → sandbox-D8Fw95FM.js} +51 -4
  21. package/lib/sandbox.js +1 -1
  22. package/lib/scope.js +1 -1
  23. package/lib/types/client/locales.d.ts +6 -0
  24. package/lib/types/command.d.ts +17 -0
  25. package/lib/types/compat/agent-instructions.d.ts +138 -0
  26. package/lib/types/compat/dsh-version.d.ts +119 -0
  27. package/lib/types/compat/llm-message.d.ts +34 -0
  28. package/lib/types/compat/sandbox-confine.d.ts +58 -0
  29. package/lib/types/compat.d.ts +85 -0
  30. package/lib/types/contract.d.ts +6 -8
  31. package/lib/types/index.d.ts +19 -5
  32. package/lib/types/instructions.d.ts +124 -0
  33. package/lib/types/registry-lease-win32.d.ts +29 -0
  34. package/lib/types/registry-lease.d.ts +62 -0
  35. package/lib/types/registry.d.ts +130 -19
  36. package/lib/types/roots.d.ts +1 -1
  37. package/lib/types/sandbox.d.ts +22 -2
  38. package/package.json +60 -13
  39. 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), and `multi-root-command` registers the `/workspace-folders` command
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
- primaryRoot: string().optional(),
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 ? {} : { sessionId: current };
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", requestBase());
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
- ...requestBase(),
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
- ...requestBase(),
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
- * offers a manual path field and lets the host resolve the workspace root.
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.