dsh-wsl-workspace 0.7.3 → 0.7.4

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/README.md CHANGED
@@ -43,6 +43,44 @@ Click "Create & open" to start a new session in the workspace. In the new sessio
43
43
 
44
44
  ## Changelog
45
45
 
46
+ ### 0.7.4 — 2026-09-28
47
+
48
+ - **The dialog works in DSH Desktop again (issues #35, #36).** The Desktop host
49
+ wraps `child_process` before the plugin is loaded: it installs plain
50
+ `exec`/`execFile` wrappers and re-exports them with
51
+ `syncBuiltinESMExports()`. That copy of `execFile` carries no
52
+ `util.promisify.custom`, so `promisify(execFile)` fell back to the generic
53
+ implementation, which resolves with the **first** callback argument only —
54
+ the stdout string — and `result.stdout` was `undefined` on every call. The
55
+ distro lookup read `.stdout` and threw
56
+ `Cannot read properties of undefined (reading 'includes')`, the frontend
57
+ caught it and rendered an empty picker: the dialog opened, the distribution
58
+ list stayed empty, and "Create & open" could not be completed, while
59
+ `wsl.exe -l -q` in a terminal listed the distribution normally. All three
60
+ call sites now use the **callback** form of `execFile`, which no wrapper can
61
+ reshape.
62
+ - **The `wsl.exe` lookup is a candidate list.** `wsl.exe` is tried on `PATH`
63
+ first, then the absolute `%SystemRoot%\System32\wsl.exe`, and a lookup that
64
+ fails now names every candidate it tried and the error each one produced,
65
+ instead of a type error from inside the decoder.
66
+ - `tests/exec-shape.mjs` reproduces the Desktop wrapper (plain wrappers plus
67
+ `syncBuiltinESMExports()`), spawns a probe with and without `--import`,
68
+ asserts the wrapped shape is broken and the new helpers are right, and runs
69
+ the real `listDistros`/`defaultDistro`/`resolveLinuxSymlink` through both
70
+ shapes. It is registered as the `exec-shape` check in
71
+ `scripts/compatibility/Run-Checks.ps1` so a future refactor cannot
72
+ reintroduce the promisified call.
73
+ - `dsh.compatibility.dshReleases` declares `0.1.7-rc.2` as well, so this build
74
+ claims ten releases instead of nine.
75
+ - **Verification**: fifteen harness checks on each of the ten declared releases
76
+ (13/15 everywhere — the two failures are the documented baseline: `typecheck`
77
+ and `host-api`, which needs a *running* frontend and passes 12/12 when pointed
78
+ at one), plus the runbook's six-item frontend pass on all ten (dialog distro
79
+ list, create & open, write, read, one-shot bash, persistent bash, skills) with
80
+ `web.err` at 0 bytes and the file re-read independently on the Linux side.
81
+ The Desktop-wrapper reproduction is in `docs/compatibility-evidence.md`,
82
+ together with the pack-identity hash and the plain-npm install gate.
83
+
46
84
  ### 0.7.3 — 2026-09-23
47
85
 
48
86
  - **The plugin loads on DSH `0.1.7-rc.1` again, and its modes come back.** The
package/README.zh.md CHANGED
@@ -44,6 +44,14 @@ dsh plugin --profile web add D:\path\to\dsh-wsl-workspace
44
44
 
45
45
  英文完整历史见 [README.md](README.md),本节为对应中文记录(0.4.3 及更早为摘要)。
46
46
 
47
+ ### 0.7.4 — 2026-09-28
48
+
49
+ - **在 DSH Desktop 上对话框又能用了(issue #35、#36)**。Desktop 宿主会在插件加载**之前**包装 `child_process`:它装上普通的 `exec`/`execFile` 包装函数,再用 `syncBuiltinESMExports()` 把它们回写到内建模块。这份 `execFile` 上没有 `util.promisify.custom`,于是 `promisify(execFile)` 退回通用实现——它只用回调的**第一个**参数 resolve,也就是 stdout 字符串——结果每次调用拿到的 `result.stdout` 都是 `undefined`。发行版查询去读 `.stdout`,抛出 `Cannot read properties of undefined (reading 'includes')`,前端把它吞掉、渲染出一个空的发行版列表:对话框能打开、下拉框里却一个发行版都没有,「创建并打开」走不完;而同一台机器上 `wsl.exe -l -q` 在终端里列得好好的。三处调用现在都改用 `execFile` 的**回调**形式,任何包装都改不了这个签名。
50
+ - **`wsl.exe` 的查找改成候选列表**。先试 `PATH` 上的 `wsl.exe`,再试绝对路径 `%SystemRoot%\System32\wsl.exe`;查找失败时报出它试过的每一个候选以及各自的原因,而不是从解码器里冒出一个类型错误。
51
+ - 新增 `tests/exec-shape.mjs`:它复刻 Desktop 的包装方式(普通包装 + `syncBuiltinESMExports()`),分别带与不带 `--import` 起一个探针进程,断言被包装的形态确实是坏的、新助手是对的,并把真实的 `listDistros`/`defaultDistro`/`resolveLinuxSymlink` 在两种形态下各跑一遍。它注册为 `scripts/compatibility/Run-Checks.ps1` 里的 `exec-shape` 检查,避免以后有人又把它改回 promisify 写法。
52
+ - `dsh.compatibility.dshReleases` 也声明了 `0.1.7-rc.2`,这份构建声明的版本从九个变成十个。
53
+ - **验证**:十个已声明版本各跑十五项 harness 检查(一律 13/15,两项失败是既有基线:`typecheck`,以及需要**运行中前端**的 `host-api`——对着真实实例单独跑是 12/12);十个版本各跑一遍 runbook 的六项前端验收(发行版下拉、创建并打开、写、读、一次性 bash、持久 bash、skills),`web.err` 全为 0 字节,文件在 Linux 侧独立复核。Desktop 包装的复现过程、pack 一致性哈希与纯 npm 安装门禁都记在 `docs/compatibility-evidence.md`。
54
+
47
55
  ### 0.7.3 — 2026-09-23
48
56
 
49
57
  - **插件在 DSH `0.1.7-rc.1` 上重新可加载,模式也回来了**。`0.1.7` 这条线改了宿主预设接口——`read()` 变为 `readDocument()`,返回的是文档(`{agentPreset, content, name, description}`)而不是组合文本,且 `AgentPreset` 不再有 `path`——于是变体生成器每次启动都抛 `agentPresets.read is not a function`,选择器里一个 `wsl-*` 模式都不会出现。现在改按**能力探测** roster 接口,两代都服务:有 `readDocument()` 就用它,否则回退 `read()`。
package/TESTING.md CHANGED
@@ -108,11 +108,42 @@ The WSL skill provider publishes `.dsh/skills` / `.agents/skills` from nested pr
108
108
  After installing the plugin into a profile and restarting `dsh web`:
109
109
 
110
110
  1. The **W** button appears beside Settings at the sidebar foot.
111
- 2. Open "Add WSL workspace…", browse to a directory (e.g. `/home`), and click "Create & open" — the workspace must be created without a "path does not exist" error.
111
+ 2. Open "Add WSL workspace…" and check the **distribution picker really lists the
112
+ distributions of this machine**. An empty picker, or one stuck on "Loading…", is a
113
+ failure: the frontend swallows a rejected `listDistros` and renders the dialog anyway
114
+ (that is exactly how issues #35/#36 looked — the dialog opened, the picker was empty,
115
+ and `wsl.exe -l -q` worked fine in a terminal). Browse to a directory (e.g. `/home`)
116
+ and click "Create & open" — the workspace must be created without a "path does not
117
+ exist" error.
112
118
  3. In the new session, the mode picker shows the WSL variant (e.g. `WSL · Standard mode(标准模式)`); the bash tool runs inside the distribution (`pwd` returns a Linux path, `uname -s` returns `Linux`).
113
119
  4. `read`/`write`/`edit` operate on WSL files; Windows files stay reachable under `/mnt/<drive>`.
114
120
  5. Switch modes (Standard / PTC / Minimal / Creative) — each lands on its WSL variant and the tool catalog matches the mode.
115
121
  6. The plugin API responds correctly: `POST /wsl-workspace/api` with `{"method":"check","params":{"distro":"<distro>","path":"/home"}}` returns `{"ok":true,"value":{"exists":true,"isDirectory":true}}`.
122
+ 7. **Open the WSL workspace from the frontend** — the workspace must be visible in the
123
+ sidebar, clicking it must enter a session, and a **page reload (F5) must still open
124
+ it**. `POST /wsl-workspace/api` with `{"method":"listWorkspaces","params":{}}` must
125
+ report the `\\wsl.localhost\<distro>\…` path, and neither the console nor `web.err` may
126
+ contain a `wsl-workspace:` error. This is the only check that covers the client half
127
+ end to end; a script-level harness cannot see it.
128
+
129
+ ### The compatibility pass on every declared release
130
+
131
+ Whenever compatibility testing is requested, each release under test must cover **all six
132
+ items**, not just the harness checks: (1) the dialog lists the distributions and
133
+ "Create & open" produces a session, (2) a file-tool write, (3) a file-tool read,
134
+ (4) `bash` one-shot *and* its persistence across two separate calls (record which of the
135
+ two it is — `0.1.0-rc.7` has no Windows process inspector and keeps a one-shot shell),
136
+ (5) `skills` loading a fixture skill and reporting its token, and (6) the frontend
137
+ open/reload check in step 7 above. Re-read the written file independently on the Linux
138
+ side (`wsl.exe … cat`), and keep `web.err` at 0 bytes. The version *count* is conditional
139
+ (spread it out before a release or when asked); the six items are not.
140
+
141
+ Because issue #35/#36 only reproduce under the Desktop host, a release that touches the
142
+ `wsl.exe` call path should also be exercised with the wrapper simulated: load
143
+ `.test-runs/child-process-hide.mjs` (plain `exec`/`execFile` wrappers plus
144
+ `syncBuiltinESMExports()`, the same shape as the Desktop hook) into a real `dsh web` via
145
+ `NODE_OPTIONS="--import file:///…/child-process-hide.mjs"`, and compare the published
146
+ release against the fixed build through `POST /wsl-workspace/api`.
116
147
 
117
148
  ## Release checklist
118
149
 
@@ -127,6 +158,8 @@ After installing the plugin into a profile and restarting `dsh web`:
127
158
  9. `npm run verify:install` — packs the tree and installs the tarball with **plain npm** into a scratch directory, with no pnpm and no host packages present. This is the gate that would have caught 0.7.0, whose `peerDependencies` made npm auto-install an unpublished package (`E404 @deepseek-ai/dsh-retention`): every other check and every real session goes through `dsh plugin add` (pnpm), which only *warns* about unmet peers and installs anyway. `prepublishOnly` runs it, so `npm publish` now refuses to ship a package that npm users cannot install.
128
159
  10. Install the tarball into a clean profile (`dsh plugin --profile web add <tarball>`), restart `dsh web`, and run the end-to-end checks above plus the nested-skill probe. When the compatibility manifest changes, also run `scripts/verify-dsh-compat.sh` for every declared release.
129
160
  11. For a release, install the *published* version by name into one isolated case per declared release and confirm each boots (the launcher only reports ready once the plugin's API route answers) — the check that proves the artifact on the registry, not just the local tree.
161
+ 12. For a release, drive the **six-item frontend pass** on every declared release (see "The compatibility pass on every declared release" above), and — when the `wsl.exe` call path changed — the Desktop-wrapper comparison as well.
162
+ 13. Confirm the artifact identity before publishing: the tarball from the release path, a fresh `npm pack`, and `npm pack --ignore-scripts` over the committed `lib/` must hash identically, and `npm run verify:install` must print `verify-install: OK`.
130
163
 
131
164
  ### The multi-release check harness
132
165
 
@@ -143,6 +176,11 @@ node .test-runs/harness.mjs <runId> --checks 0.1.5-rc.2 # re-check an exist
143
176
  Four checks need a live WSL distribution (`skills-real`, `fs-real`, `relay-real`,
144
177
  `search-real`); they build their own fixtures under `/tmp/dsh-wsl-compat` (override
145
178
  with `WSL_COMPAT_ROOT`, and the distribution with `WSL_COMPAT_DISTRO`) and remove
146
- them again. `host-api` needs a running `dsh web` for the case, so it is expected to
147
- fail in a sweep. The `typecheck` baseline is two pre-existing errors from the
148
- harness's own type declarations.
179
+ them again. `exec-shape` reproduces the DSH Desktop `child_process` wrapper (plain
180
+ `exec`/`execFile` wrappers + `syncBuiltinESMExports()`, which strips
181
+ `util.promisify.custom`) in a probe process and asserts both the wrapped and the plain
182
+ shapes produce a correct `{ stdout, stderr }`. `host-api` needs a running `dsh web` for
183
+ the case, so it is expected to fail in a sweep — point it at a live instance's
184
+ `runtime.json` instead (an absolute path; it is 12/12 there). The `typecheck` check exits
185
+ non-zero because of the pre-existing `tsc --noEmit` errors in this tree; the gate is that
186
+ the count does not grow.
package/lib/client.js CHANGED
@@ -1136,8 +1136,8 @@ const zh = {
1136
1136
  "help.compat.versionLabel": "插件版本",
1137
1137
  "help.compat.unknown": "未能读取版本与兼容声明(宿主端没有响应)",
1138
1138
  "help.compat.body": "上方是这份构建声明兼容的 DSH 版本,每一条都在隔离实例上实测过(独立 DSH_HOME、依赖固定到该版本、跑满十项门禁)。\n插件在运行时自动识别 DSH 版本并选用对应的 API;两边都不支持时会明确报错,而不是留下一个空工作区。\n版本不在列表里通常仍然可用,但未经验证。",
1139
- "help.news.title": "本次更新(0.7.3)",
1140
- "help.news.body": "插件在 DSH 0.1.7 上重新可加载、模式也回来了:那条线把预设平面改成了声明式的 @deepseek-ai/dsh-agent-preset 行,read() 变成 readDocument()、AgentPreset 不再有 path,于是变体生成器每次启动都抛 agentPresets.read is not a function,选择器里一个 wsl-* 模式都不会出现。现在按能力探测两代接口,两代都服务。\n在 0.1.7 上变体改为通过 ctx.agentPresets.register() 发布成一条声明;注销器由插件的 effect 持有,卸载或热重载会注销这些变体,而不是留下下一次 apply 无法替换的孤儿。更早的版本仍走原来的目录通道,行为不变。\n该通道下世界自己的 provider 行必须以 file: URL 命名:由声明挂载的预设由注册表自己的 entry tree 导入,它不会像启动期的 Include 那样把绝对路径翻译成 file: URL,否则那些行根本不会启动、整个变体被判为不可用而拒绝。relay 与解释器这类配置值仍保持原生路径。\n修掉了 PTC 变体的显示名:标签表里只登记了旧 id code,而 0.1.7 不发布显示名,于是模式显示为 WSL · ptc、描述也退化成 WSL execution world for ptc: …;现在两条通道都显示为 WSL · PTC mode(PTC 模式)。\n兼容性清单增加 0.1.7-rc.1;声明通道需要的两个模块(@deepseek-ai/cordis-plugin-include、js-yaml)在调用时解析,并声明为可选同伴依赖。",
1139
+ "help.news.title": "本次更新(0.7.4)",
1140
+ "help.news.body": "在 DSH Desktop 上对话框又能用了(issue #35、#36)。Desktop 宿主会在插件加载之前包装 child_process:装上普通的 exec / execFile 包装函数,再用 syncBuiltinESMExports() 把它们回写到内建模块。\n这份 execFile 上没有 util.promisify.custom,于是 promisify(execFile) 退回通用实现——它只用回调的第一个参数 resolve——结果每次调用拿到的 result.stdout 都是 undefined。发行版查询去读它就抛 Cannot read properties of undefined (reading 'includes'),前端把异常吞掉、渲染出一个空的发行版列表:对话框能打开、下拉框里却一个发行版都没有,也创建不出工作区;而同一台机器上 wsl.exe -l -q 在终端里列得好好的,看上去像\"插件找不到 WSL2\"。\n三处调用(列发行版、默认发行版、符号链接解析)现在都改用 execFile 的回调形式,任何包装都改不了这个签名,输出再由 textOf 统一取文本。\nwsl.exe 的查找改成候选列表:先试 PATH 上的 wsl.exe,再试绝对路径 %SystemRoot%\\System32\\wsl.exe;都失败时报出试过的每一个候选以及各自的原因,而不是从解码器里冒出一个类型错误。\n新增 tests/exec-shape.mjs:复刻 Desktop 的包装形态(分别带与不带 --import 起一个探针进程),断言被包装的形态确实是坏的、新助手是对的,并把真实的三个函数在两种形态下各跑一遍;它注册为 Run-Checks 里的 exec-shape 检查,免得以后又被改回 promisify。",
1141
1141
  "help.usage.title": "用法与特性",
1142
1142
  "help.usage.body": "点击侧边栏底部的 W 按钮 → 选发行版 → 输入或浏览 Linux 路径 → 检查 → 创建并打开。\n创建出的会话里,bash 与文件工具都落在该发行版内,模型看到的路径全是 Linux 路径;Windows 盘可从会话内通过 /mnt/<盘符> 访问。\n四种模式(标准 / PTC / 极简 / 创造)各有 WSL 变体,在模式选择器里直接选即可,名字形如 WSL · Standard mode(标准模式)。\n用户名可选,等价于 wsl.exe -u <用户名>,只改变 bash 与 persistent-bash 的运行身份;文件工具走 Windows 侧共享,不受它影响。\n技能目录从会话 cwd 最近的 .git 祖先开始向下扫描 .dsh/skills 与 .agents/skills(含嵌套项目),上限 4 层目录 / 64 个技能目录 / 4096 个已访问目录,并按扫描根缓存 10 秒。9P 解不开的符号链接交给发行版 readlink 解析出真实路径(每次查找最多 32 条)后继续扫描,链接进来的项目与它下面的嵌套项目都能扫到。\n目录不再冻结:对 UNC 关闭文件监视之后,插件每 3 秒重查一次已发布的技能目录、并比对每个技能文件的修改时间与大小,所以新增、删除与改写都会在下一个回合生效;完整重新发现每 30 秒一次,用于找到此前不存在的技能目录(技能正文始终实时读取)。\n文件搜索由发行版内的 grep / glob 提供,源预设没有这两个工具的模式(极简)不会多出来:grep 用 GNU grep -E(\\d、\\w、(?i) 可用,环视与反向引用不支持),跳过隐藏项与 node_modules,不读 .gitignore;glob 用 GNU find 列文件、按 gitignore 风格匹配(* 不跨目录、** 跨、{a,b}、前导 ! 取反),按修改时间从旧到新排序。\n文件工具在链接处按真实路径工作,并按真实路径判策略:链接指向工作区外就等于工作区外。\nbash 由 PTY 承载的持久 shell 提供:登录环境、起始目录就是会话工作区,cd / export / venv / 后台任务跨调用保留(它取代了原先一次性 bash)。\n需要可跟踪的后台任务时用 bash_background:它立刻返回 job id,job_list / job_output(增量读取)/ job_kill 都作用于它;bash 本身没有 run_in_background 参数,传了会被忽略。job id 只在本次 DSH 进程内唯一,重启后会重新编号,引用前先用 job_list 确认。\nbash 与文件工具的 shell 都在发行版内运行、不受 DSH 文件策略约束;文件工具(read/write/edit)受策略约束,工作区内修改模式下只能写工作区内。",
1143
1143
  "help.known.title": "已知问题",
@@ -1180,8 +1180,8 @@ const en = {
1180
1180
  "help.compat.versionLabel": "Plugin version",
1181
1181
  "help.compat.unknown": "Version and compatibility declaration unavailable (the host side did not answer)",
1182
1182
  "help.compat.body": "The chips above are the DSH releases this build declares, each verified on an isolated instance (own DSH_HOME, dependencies pinned to that release, full check suite).\nThe plugin detects the DSH generation at runtime and picks the matching API; a release exposing neither fails loudly instead of leaving an empty workspace.\nA release outside the list usually still works, but is unverified.",
1183
- "help.news.title": "What's new in 0.7.3",
1184
- "help.news.body": "The plugin loads on DSH 0.1.7 again, and its modes come back: that line turned the preset plane into declarative @deepseek-ai/dsh-agent-preset rows, renamed read() to readDocument() and dropped AgentPreset.path.\nThe variant generator therefore threw agentPresets.read is not a function on every boot and no wsl-* mode reached the picker; both roster generations are now probed by capability.\nOn 0.1.7 a variant is published as a declaration through ctx.agentPresets.register(); the effect installed by the plugin owns the returned disposers, so an unload or hot reload retires the variants instead of leaving orphans the next apply could not replace.\nEarlier releases keep the directory channel, unchanged.\nOn that channel the provider rows of the world must be named as file: URLs: a preset mounted from a declaration is imported by the entry tree of the registry, which does not translate an absolute path the way the boot-time Include does.\nWithout that rewrite those rows never started and the whole variant was refused as unusable. Config values such as the relay and the interpreter stay native paths.\nThe PTC variant is named again: the label table carried only the older id code, and 0.1.7 publishes no display name, so the mode read WSL · ptc with the generic description WSL execution world for ptc: …; both channels now show WSL · PTC mode(PTC 模式).\nThe compatibility manifest adds 0.1.7-rc.1, and the two modules the declaration channel needs (@deepseek-ai/cordis-plugin-include, js-yaml) are resolved at call time and declared as optional peers.",
1183
+ "help.news.title": "What's new in 0.7.4",
1184
+ "help.news.body": "The dialog works in DSH Desktop again (issues #35, #36). The Desktop host wraps child_process before the plugin loads: it installs plain exec / execFile wrappers and re-exports them with syncBuiltinESMExports().\nThat copy of execFile carries no util.promisify.custom, so promisify(execFile) fell back to the generic implementation, which resolves with the first callback argument only - the stdout string - and result.stdout was undefined on every call.\nThe distro lookup read it and threw \"Cannot read properties of undefined (reading 'includes')\"; the frontend caught that and rendered an empty distro picker: the dialog opened, the list stayed empty and no workspace could be created, while wsl.exe -l -q listed the distribution normally in a terminal.\nAll three call sites (list distros, default distro, symlink resolution) now use the callback form of execFile, which no wrapper can reshape; textOf takes the text out of either stream shape.\nThe wsl.exe lookup is a candidate list: wsl.exe on PATH first, then %SystemRoot%\\System32\\wsl.exe, and a failure now names every candidate it tried with the error each produced instead of a type error from inside the decoder.\ntests/exec-shape.mjs reproduces the Desktop wrapper (a probe process with and without --import), asserts the wrapped shape is broken and the new helpers are right, and runs the three real functions through both shapes; it is registered as the exec-shape check in Run-Checks so nobody refactors it back.",
1185
1185
  "help.usage.title": "Usage and features",
1186
1186
  "help.usage.body": "Click the W button at the sidebar foot, pick a distribution, type or browse to a Linux path, press Check, then Create & open.\nIn that session the bash tool and the file tools run inside the distribution, so every path the model sees is a Linux path; Windows drives stay reachable as /mnt/<drive>.\nEach mode (Standard / PTC / Minimal / Creator) has a WSL variant in the mode picker, named like WSL · Standard mode.\nThe optional username behaves like wsl.exe -u <user> for bash and persistent-bash; the file tools go through the Windows-side share and are unaffected.\nThe skill catalog is discovered from the nearest .git ancestor of the session cwd downwards (.dsh/skills and .agents/skills, nested projects included), bounded to 4 levels / 64 skill directories / 4096 visited directories, and cached per scan root for 10 seconds.\nA link the share cannot follow is resolved through the distribution (wsl.exe readlink, at most 32 per lookup) and the scan continues at the real path, so a linked-in project and its own nested projects are found too.\nThe catalog is not frozen: published skills directories are re-checked every 3 seconds (skill file mtime + size), so an add, remove or edit appears on the next turn; a 30-second walk finds a skills directory that did not exist before.\nFile search comes from grep / glob inside the distribution, and a mode that mounts no search suite (Minimal) gains none. grep is GNU grep -E (no lookaround or backreferences), skips hidden entries and node_modules, and does not read .gitignore; glob matches gitignore-style patterns here, oldest first.\nThe file tools work at the resolved real path of a link, and the policy is judged there too: a link out of the workspace is an outside write.\n`bash` is a PTY-backed stateful shell: login environment, starting directory the session workspace, and cd / exports / background jobs survive between calls.\nFor a tracked background job use bash_background: it returns a job id immediately, and job_list / job_output (incremental) / job_kill act on it.\n`bash` itself has no run_in_background parameter and ignores one; job ids are unique within this DSH process, so confirm with job_list before acting on one.\nBoth it and the file tools' shell run inside the distribution, outside the DSH file policy; read/write/edit are inside it, and workspace-write only writes inside the workspace.",
1187
1187
  "help.known.title": "Known issues",