dsh-wsl-bridge 0.0.0-stage → 0.4.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.md ADDED
@@ -0,0 +1,112 @@
1
+ # Changelog
2
+
3
+ ## 0.4.6 — 2026-10-19 (unreleased)
4
+
5
+ - A `WSL` session now uses the current distribution's `$HOME` as its default
6
+ workspace. The first bash command, relative paths, and grep/glob without a
7
+ path all start there instead of the Windows session directory. The home path
8
+ is read once; an explicit absolute path does not trigger the lookup. Host-wide
9
+ `wsl_*` tools still default to the session directory.
10
+
11
+ ## 0.4.5 — 2026-10-19 (unreleased)
12
+
13
+ - Fail reads when `head` fails; reject incomplete transport and invalid UTF-8
14
+ edits instead of silently changing file data. Base edit guards on the exact
15
+ bytes read and check just before rename, including deletion detection.
16
+ - Keep replacement dollar sequences literal, preserve existing file modes,
17
+ and refuse directory targets without leaving a temporary file inside them.
18
+ - Stop every command line when the requested working directory cannot be used.
19
+ Background jobs use the session directory even after a persistent shell exists.
20
+ - Search prunes hidden entries and dependency trees before scanning, preserves
21
+ single-file grep paths, anchors its status marker, reports failures, and marks
22
+ the glob scan ceiling as truncated.
23
+ - Make release dry-runs independent of login, check official-registry auth and
24
+ a clean working tree for publication, publish the packed tarball, and fail if
25
+ version verification fails. Include installation/release scripts in the pack.
26
+ - Add regression coverage and opt-in real WSL tests; fix the CI mount test's
27
+ hard-coded local path.
28
+
29
+ ## 0.4.4 — 2026-10-19
30
+
31
+ - **Fix the `WSL` preset failing to load**: the `plan-mode` row was copied
32
+ without its required `config.section`, so
33
+ `@deepseek-ai/dsh-plan-mode` refused activation and the preset showed
34
+ "load failed" in the mode picker while the rest of the plugin worked. The
35
+ shipped standard preset's section text is now carried verbatim; the roster
36
+ probe reports `wsl: broken=null`.
37
+ - Add the preset-roster diagnostic recipe used to find it (see docs/TROUBLESHOOTING.md).
38
+
39
+ ## 0.4.3 — 2026-10-19
40
+
41
+ - Self-check wording: a probe has no live agent, so the job registry's refusal
42
+ now reads as "registry reachable" instead of an error line.
43
+
44
+ ## 0.4.2 — 2026-10-19
45
+
46
+ - **Fix persistent `cd`**: the bash tools passed the session directory as the
47
+ working directory on every call, which reset the persistent shell's cwd. The
48
+ default workdir is now only the shell's initial directory; an explicit
49
+ `workdir` still changes it for this and later calls. Verified in a real
50
+ desktop session (env vars persisted, cwd now does too) and pinned by the
51
+ integration self-check (`tool cd persistence=OK`).
52
+
53
+ ## 0.4.1 — 2026-10-19
54
+
55
+ - Opt-in real background-job self-check (registers a job with the host registry
56
+ and verifies the command actually wrote its file inside the distribution).
57
+
58
+ ## 0.4.0 — 2026-10-19
59
+
60
+ - Add `runInBackground` to `wsl_bash` and the WSL preset's `bash`: the command is
61
+ registered with the host job registry (`job_output` / `job_kill`) and owns its
62
+ own `wsl.exe` process, so the persistent shell's lifecycle cannot kill it.
63
+ Controlled by `backgroundJobs` (default on); refuses clearly when the
64
+ deployment composes no job registry.
65
+ - The destructive guard runs before a background registration.
66
+ - Tests: 87 cases total.
67
+
68
+ ## 0.3.0 — 2026-10-19
69
+
70
+ - Add a deterministic destructive-command guard (`dangerGuard`, default on):
71
+ recursive deletes, block-device writes, power control, `find -delete` and
72
+ `git clean -f/-d/-x` are refused unless the call passes `allowDangerous: true`.
73
+ Applied to both `wsl_bash` and the WSL preset's `bash`.
74
+ - Add `docs/ARCHITECTURE.md`, `docs/SECURITY.md`, `docs/TROUBLESHOOTING.md`.
75
+ - Add `npm test` (78 cases), a CI workflow (Windows, Node 22/24), and
76
+ `scripts/install-desktop.ps1`.
77
+ - Desktop-composition smoke test: an isolated profile replicating the desktop
78
+ bundle set (web-all / market / better-sidebar) boots with both bridge layers
79
+ active.
80
+
81
+ ## 0.2.3 — 2026-10-19
82
+
83
+ - Packaging for publish: MIT LICENSE, bilingual `locale/*.json` display metadata,
84
+ SVG icon, CHANGELOG, and a publish-shaped `files` list; `npm pack` yields 20
85
+ files / ~37 KB with no build step.
86
+ - Fix the integration-test hooks: environment variables (`DSH_WSL_BRIDGE_SELFCHECK`,
87
+ `DSH_WSL_BRIDGE_MARKER`, `DSH_WSL_BRIDGE_WORLD_*`) now override the shipped row
88
+ config instead of being overridden by it.
89
+ - Dispose the shells the self-checks open, so a probe run leaves no `wsl.exe`
90
+ processes behind.
91
+ - Verified a **packed** install (tarball into a fresh isolated profile, not a
92
+ link): host tools mount, the `WSL` preset mounts, and bash/grep/glob execute in
93
+ the distribution.
94
+
95
+ ## 0.2.0 — 2026-10-19
96
+
97
+ - Add the declarative `WSL` agent preset: a session created in this mode mounts
98
+ `dsh-wsl-bridge/wsl-world`, so `bash`, `read`, `write`, `edit`, `grep` and
99
+ `glob` all operate on Linux paths inside the distribution.
100
+ - Add in-distribution `grep` (GNU grep -E) and `glob` (find + glob matching),
101
+ pruning hidden entries and `node_modules`/VCS metadata.
102
+ - Capture the `subprocess` service during `apply()`: a preset mount context is
103
+ inactive after activation, so a late `ctx.subprocess` lookup now never throws.
104
+ - Persona sentence tells the model its working directory is inside WSL and that
105
+ Windows files live under `/mnt/<drive>`.
106
+
107
+ ## 0.1.0 — 2026-10-19
108
+
109
+ - First host-only build: persistent `wsl_bash` (one shell per session, `cd`
110
+ survives between calls), binary-safe `wsl_read`/`wsl_write`, sha256-fenced
111
+ `wsl_edit`, `wsl_path`, `wsl_env`, and a default-on routing prompt section.
112
+ - Zero dependencies, no build step; opt-in self-check for integration testing.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-wsl-bridge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,124 @@
1
+ # dsh-wsl-bridge
2
+
3
+ [中文](README.md)
4
+
5
+ A native WSL (Linux) execution surface for [DSH](https://github.com/deepseek-ai/DeepSeek-Harness) on Windows. It starts no extra web server and installs nothing inside the distribution. Windows sessions and WSL sessions share one desktop app.
6
+
7
+ Version **0.4.6**. Requires **Windows**, a working WSL distribution, and **DSH `>= 0.2.1-rc.2`**. Source: <https://github.com/TGWgulugu/dsh-wsl-bridge>
8
+
9
+ ## Install
10
+
11
+ Run this in a normal terminal, not inside a DSH tool sandbox. If `dsh` is not on `PATH`, use the full path to the `dsh.cmd` that ships with DSH Desktop.
12
+
13
+ ```powershell
14
+ dsh plugin --profile desktop add dsh-wsl-bridge
15
+ ```
16
+
17
+ Before the package is published, or to install the checkout you have open:
18
+
19
+ ```powershell
20
+ dsh plugin --profile desktop add .
21
+ ```
22
+
23
+ From the repository root, `pwsh -File scripts/install-desktop.ps1` does the same. Quit DSH Desktop from the tray and open it again. A page refresh does not load the plugin.
24
+
25
+ After the restart:
26
+
27
+ - Every session gains `wsl_bash`, `wsl_read`, `wsl_write`, `wsl_edit`, `wsl_path`, and `wsl_env`.
28
+ - The new-session mode picker has a **WSL** entry.
29
+
30
+ Uninstall:
31
+
32
+ ```powershell
33
+ dsh plugin --profile desktop remove dsh-wsl-bridge
34
+ ```
35
+
36
+ If the app then fails to start, run `pnpm install` in that profile directory.
37
+
38
+ ## Two ways to use it
39
+
40
+ ### WSL tools in any session
41
+
42
+ These tools are added beside the Windows shell and file tools. They do not replace them.
43
+
44
+ | Tool | What it does |
45
+ |---|---|
46
+ | `wsl_bash` | Runs bash in WSL. One persistent shell per session, so `cd` and `export` survive. Output ends with `[exit code: N]` |
47
+ | `wsl_read` | Reads a Linux path, with optional line `offset` / `limit` |
48
+ | `wsl_write` | Atomically writes a Linux path. `expectedSha256` refuses the write when the file changed after you read it |
49
+ | `wsl_edit` | Replaces a string. A sha256 check ties the write to the bytes that were read |
50
+ | `wsl_path` | Converts Windows paths to Linux paths and back. Uses the distribution's `wslpath` when it can |
51
+ | `wsl_env` | Reports the distribution, kernel, user, and whether node / python3 / git are present |
52
+
53
+ A routing note is on by default: for Linux work, prefer `wsl_bash` and Linux paths; Windows files are at `/mnt/<drive>`. It does not hide or disable the host tools.
54
+
55
+ `wsl_bash` accepts `runInBackground: true`. The command is registered as a host job (`job_output` to read it, `job_kill` to stop it) and owns a separate WSL process. It does not inherit the persistent shell's `cd` or `export`, and it is not killed when that shell exits.
56
+
57
+ ### WSL session mode
58
+
59
+ Choose **WSL** when creating a session. In that session, `bash`, `read`, `write`, `edit`, `grep`, and `glob` run inside Linux and take Linux paths. Persona, skills, goals, plan mode, compaction, subagents, todos, web, and present stay the standard DSH set.
60
+
61
+ **The default workspace is the current distribution's home directory (`$HOME`).** The first `bash` starts there. Relative paths resolve there. `grep` and `glob` search there when `path` is omitted. Pass `/mnt/<drive>/...` when the search should cover a Windows directory.
62
+
63
+ | Tool | What it does |
64
+ |---|---|
65
+ | `bash` | Persistent bash in the distribution. The syntax is bash, not PowerShell |
66
+ | `read` / `write` / `edit` | Linux paths. Relative paths are relative to `$HOME` |
67
+ | `grep` | GNU grep in the distribution. Rows look like `path:line: text`. Omitting `path` searches `$HOME` |
68
+ | `glob` | Lists files matching a glob, oldest first. Omitting `path` lists `$HOME` |
69
+ | `wsl_env` | Distribution facts |
70
+
71
+ `$HOME` is resolved once. Text that a login profile prints is not treated as the path. If the lookup fails, search does not fall back to the Windows session directory.
72
+
73
+ ## Configuration
74
+
75
+ Set these on the plugin row `config` (`cordis.patch.yml`, or a profile patch that overrides it). Every field is optional.
76
+
77
+ | Field | Default | Meaning |
78
+ |---|---|---|
79
+ | `distro` | `''` | Distribution name. An empty string uses the system default |
80
+ | `timeoutMs` | `120000` | Default deadline for `bash` / `wsl_bash`, in milliseconds |
81
+ | `fileTimeoutMs` | `30000` | Deadline for one read, write, or search |
82
+ | `maxFileBytes` | `2097152` | Maximum bytes `read` / `edit` will load |
83
+ | `defaultWorkdir` | `session` for host tools; `home` for WSL mode | `session` translates the Windows session directory to `/mnt/...`. `home` uses the distribution's `$HOME`. An absolute Linux path is also accepted |
84
+ | `dangerGuard` | `true` | Refuses obviously destructive commands unless that call passes `allowDangerous: true` |
85
+ | `backgroundJobs` | `true` | Allows `runInBackground`. Refuses clearly when the host has no job registry |
86
+ | `promptSection` | `true` | Adds the routing note to host sessions |
87
+ | `promptOrder` | `3260` | Sort order of that note |
88
+ | `promptText` | `''` | Replaces the built-in routing note. Empty keeps the built-in text |
89
+ | `selfCheck` | `false` | Probes WSL once on mount. `DSH_WSL_BRIDGE_SELFCHECK=1` does the same |
90
+ | `selfCheckMarker` | `''` | Appends the probe result to this file. Or set `DSH_WSL_BRIDGE_MARKER` |
91
+
92
+ The refusal list covers recursive deletes, block-device writes, power control, `find -delete`, and `git clean -fdx`. It is a fixed list, not a kernel boundary. A different spelling can still get through.
93
+
94
+ ## Limits
95
+
96
+ - Windows only. Both rows disable themselves on any other host.
97
+ - The tools run through `wsl.exe` and are **not** inside DSH's Windows file sandbox. Treat them as able to write anything that WSL user can write, including Windows paths reached through `/mnt/<drive>`. See [docs/SECURITY.md](docs/SECURITY.md).
98
+ - `grep` / `glob` use GNU grep and find. They do not read `.gitignore`. Hidden files, `node_modules`, and VCS metadata are skipped.
99
+ - The folder selected in the desktop UI stays that UI's workspace. WSL-mode tools no longer search it by default. Skill directories and UI file references still use Windows paths.
100
+ - The first call waits for the distribution to start and can take several seconds. `timeoutMs` defaults to 120 seconds.
101
+ - This plugin does not install Node or any other toolchain inside the distribution.
102
+
103
+ ## Docs
104
+
105
+ - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): how the host tools and the WSL mode are split, and why shell / fs services are left alone.
106
+ - [docs/SECURITY.md](docs/SECURITY.md): the sandbox boundary, and what the destructive-command guard does not promise.
107
+ - [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md): nothing showed up, `EPERM`, a missing mode, cold start, uninstall.
108
+
109
+ ## Development
110
+
111
+ ```powershell
112
+ npm test
113
+ npm run test:integration
114
+ pwsh -NoProfile -File test/release.test.ps1
115
+ pwsh -File scripts/release.ps1 -DryRun
116
+ ```
117
+
118
+ `npm test` does not need WSL. `npm run test:integration` runs inside a real distribution and deletes its own temporary directory. The release-script check does not publish. A real publish needs a clean git tree, then `pwsh -File scripts/release.ps1`.
119
+
120
+ The code is plain ESM with no build. `index.js` registers the host tools, `wsl-world.js` registers WSL session mode, and the implementation is in `src/`.
121
+
122
+ ## License
123
+
124
+ [MIT](LICENSE)
package/README.md CHANGED
@@ -1,3 +1,124 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # dsh-wsl-bridge
2
+
3
+ [English](README.en.md)
4
+
5
+ 在 Windows 上给 [DSH](https://github.com/deepseek-ai/DeepSeek-Harness) 一个原生的 WSL(Linux)执行面。不额外启动 web 服务,也不在 WSL 里安装 dsh。Windows 会话和 WSL 会话在同一个桌面端里使用。
6
+
7
+ 当前版本 **0.4.6**。需要 **Windows**、一个可用的 WSL 发行版,以及 **DSH `>= 0.2.1-rc.2`**。源码:<https://github.com/TGWgulugu/dsh-wsl-bridge>
8
+
9
+ ## 安装
10
+
11
+ 在普通终端里执行(不要在 DSH 会话的工具沙箱里跑)。`dsh` 不在 PATH 上时,换成 Desktop 自带的 `dsh.cmd` 完整路径。
12
+
13
+ ```powershell
14
+ dsh plugin --profile desktop add dsh-wsl-bridge
15
+ ```
16
+
17
+ 尚未发布、或要装当前目录的源码时:
18
+
19
+ ```powershell
20
+ dsh plugin --profile desktop add .
21
+ ```
22
+
23
+ 也可以在仓库根目录运行 `pwsh -File scripts/install-desktop.ps1`。装完后从托盘**完全退出** DSH Desktop,再打开。刷新页面不够。
24
+
25
+ 重启后:
26
+
27
+ - 任意会话里出现 `wsl_bash`、`wsl_read`、`wsl_write`、`wsl_edit`、`wsl_path`、`wsl_env`。
28
+ - 新建会话的模式选择器里出现 **WSL**。
29
+
30
+ 卸载:
31
+
32
+ ```powershell
33
+ dsh plugin --profile desktop remove dsh-wsl-bridge
34
+ ```
35
+
36
+ 如果卸载后应用起不来,在该 profile 目录里执行一次 `pnpm install`。
37
+
38
+ ## 两种用法
39
+
40
+ ### 任意会话里的 WSL 工具
41
+
42
+ 这些工具加在现有会话上,不替换 Windows 自己的 shell 和文件工具。
43
+
44
+ | 工具 | 作用 |
45
+ |---|---|
46
+ | `wsl_bash` | 在 WSL 里执行 bash。每个会话一个持久 shell,`cd` 和 `export` 会保留。输出以 `[exit code: N]` 结尾 |
47
+ | `wsl_read` | 读 Linux 路径,支持按行 `offset` / `limit` |
48
+ | `wsl_write` | 原子写入 Linux 路径。可带 `expectedSha256`,文件在你读过之后被改过就拒绝写入 |
49
+ | `wsl_edit` | 按字符串替换。读和写之间用 sha256 核对内容 |
50
+ | `wsl_path` | Windows 路径和 Linux 路径互转。优先用发行版里的 `wslpath` |
51
+ | `wsl_env` | 查看发行版、内核、用户和是否有 node / python3 / git |
52
+
53
+ 默认有一段路由提示:Linux 上的工作优先用 `wsl_bash` 和 Linux 路径,Windows 文件在 `/mnt/<盘符>`。它不隐藏、也不禁用宿主工具。
54
+
55
+ `wsl_bash` 可以传 `runInBackground: true`。命令会登记成宿主后台任务,用 `job_output` 看输出、用 `job_kill` 停止。这个任务独占一个 WSL 进程:不继承持久 shell 的 `cd` / `export`,会话 shell 退出时也不会把它杀掉。
56
+
57
+ ### WSL 会话模式
58
+
59
+ 新建会话时选择 **WSL**。这一会话里的 `bash`、`read`、`write`、`edit`、`grep`、`glob` 都在 Linux 里执行,参数是 Linux 路径。persona、skill、goal、plan、compaction、subagent、todo、web、present 仍用 DSH 标准会话的那一套。
60
+
61
+ **默认工作区是当前发行版的家目录(`$HOME`)。** 第一次 `bash` 从家目录开始;相对路径按家目录解析;`grep` 和 `glob` 不传 `path` 时也搜索家目录。要搜 Windows 目录时,显式传入 `/mnt/<盘符>/...`。
62
+
63
+ | 工具 | 作用 |
64
+ |---|---|
65
+ | `bash` | 发行版里的持久 bash,语法是 bash,不是 PowerShell |
66
+ | `read` / `write` / `edit` | 读写 Linux 路径。相对路径相对 `$HOME` |
67
+ | `grep` | 发行版内的 GNU grep。结果是 `path:line: text`。不传 `path` 时搜 `$HOME` |
68
+ | `glob` | 按 glob 列出文件,旧文件在前。不传 `path` 时列 `$HOME` |
69
+ | `wsl_env` | 发行版信息 |
70
+
71
+ 家目录只查询一次。登录脚本如果往标准输出打字,不会被当成路径。查询失败时,搜索不会退回 Windows 会话目录。
72
+
73
+ ## 配置
74
+
75
+ 改插件行的 `config`(`cordis.patch.yml`,或 profile 里覆盖它的补丁)。字段都可以省略。
76
+
77
+ | 字段 | 默认 | 说明 |
78
+ |---|---|---|
79
+ | `distro` | `''` | 发行版名。空字符串表示系统默认发行版 |
80
+ | `timeoutMs` | `120000` | `bash` / `wsl_bash` 的默认时限(毫秒) |
81
+ | `fileTimeoutMs` | `30000` | 读、写、搜索的单次时限(毫秒) |
82
+ | `maxFileBytes` | `2097152` | `read` / `edit` 最多读取的字节数 |
83
+ | `defaultWorkdir` | 宿主工具是 `session`;WSL 模式是 `home` | `session` 把 Windows 会话目录译成 `/mnt/...`。`home` 使用当前发行版的 `$HOME`。也可以填绝对 Linux 路径 |
84
+ | `dangerGuard` | `true` | 拒绝明显的破坏性命令,除非该次调用带 `allowDangerous: true` |
85
+ | `backgroundJobs` | `true` | 是否允许 `runInBackground`。宿主没有任务注册表时会明确拒绝 |
86
+ | `promptSection` | `true` | 是否加入宿主会话的路由提示 |
87
+ | `promptOrder` | `3260` | 这段提示的排序 |
88
+ | `promptText` | `''` | 用来替换内置路由提示。空字符串表示用内置文本 |
89
+ | `selfCheck` | `false` | 挂载时探测一次 WSL。也可以设环境变量 `DSH_WSL_BRIDGE_SELFCHECK=1` |
90
+ | `selfCheckMarker` | `''` | 把探测结果追加到这个文件。也可以设 `DSH_WSL_BRIDGE_MARKER` |
91
+
92
+ 破坏性命令清单包括递归删除、写块设备、关机、`find -delete`、`git clean -fdx`。这是固定名单,不是内核隔离;换一种写法仍可能绕过。
93
+
94
+ ## 边界
95
+
96
+ - 只支持 Windows。其他系统上这两行配置会禁用。
97
+ - 工具通过 `wsl.exe` 执行,**不受** DSH 的 Windows 文件沙箱约束。把它看成:发行版里该用户能写的路径,以及通过 `/mnt/<盘符>` 能碰到的 Windows 路径,都可以写。详见 [docs/SECURITY.md](docs/SECURITY.md)。
98
+ - `grep` / `glob` 使用 GNU grep 和 find,不读 `.gitignore`。隐藏文件、`node_modules` 和版本库元数据会跳过。
99
+ - 桌面端选中的 Windows 文件夹仍然是界面上的工作区。WSL 模式的工具默认不再搜那个目录。技能目录和界面上的文件引用仍按 Windows 路径解析。
100
+ - 第一次调用要等发行版冷启动,可能要数秒。`timeoutMs` 默认 120 秒。
101
+ - 本插件不在发行版里安装 Node 或其他工具链。
102
+
103
+ ## 文档
104
+
105
+ - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md):宿主工具和 WSL 模式怎么分层,以及为什么不替换 shell / fs 服务。
106
+ - [docs/SECURITY.md](docs/SECURITY.md):沙箱边界和危险命令守卫做不到什么。
107
+ - [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md):装完没出现、`EPERM`、模式缺失、冷启动、卸载。
108
+
109
+ ## 开发
110
+
111
+ ```powershell
112
+ npm test
113
+ npm run test:integration
114
+ pwsh -NoProfile -File test/release.test.ps1
115
+ pwsh -File scripts/release.ps1 -DryRun
116
+ ```
117
+
118
+ `npm test` 不需要 WSL。`npm run test:integration` 会在真实 WSL 里跑,并删掉自己的临时目录。发布脚本的检查不会真的发布。正式发布要先提交干净的工作区,再执行 `pwsh -File scripts/release.ps1`。
119
+
120
+ 代码是纯 ESM,没有构建步骤。`index.js` 注册宿主工具,`wsl-world.js` 注册 WSL 会话模式,实现在 `src/`。
121
+
122
+ ## 许可证
123
+
124
+ [MIT](LICENSE)
@@ -0,0 +1,146 @@
1
+ # Bundle layer for dsh-wsl-bridge.
2
+ #
3
+ # Two rows:
4
+ # 1. `wsl-bridge` — the host-wide bridge (wsl_* tools + routing prompt).
5
+ # 2. `preset-wsl` — the declarative `WSL` agent preset: a session created in
6
+ # this mode works with Linux paths (`bash`/`read`/`write`/`edit`/`grep`/
7
+ # `glob` are the WSL variants mounted from `dsh-wsl-bridge/wsl-world`).
8
+ #
9
+ # Both are disabled outside Windows: the bridge spawns `wsl.exe` and the preset
10
+ # cannot work where WSL does not exist.
11
+ - insert:
12
+ - id: wsl-bridge
13
+ name: 'dsh-wsl-bridge'
14
+ config:
15
+ distro: ''
16
+ timeoutMs: 120000
17
+ promptSection: true
18
+ selfCheck: false
19
+ disabled: !!js process.platform !== 'win32'
20
+ - id: preset-wsl
21
+ name: '@deepseek-ai/dsh-agent-preset'
22
+ config:
23
+ id: wsl
24
+ name: WSL
25
+ description: 'Linux paths inside WSL. The default workspace is that distribution home directory.'
26
+ order: 2
27
+ plugins:
28
+ - id: persona
29
+ name: '@deepseek-ai/dsh-persona'
30
+ config:
31
+ prefix: >-
32
+ You are a coding agent powered by the {{model}} model. Your working
33
+ directory is the home directory of the current WSL distribution.
34
+ bash, read, write, edit, grep and glob take Linux paths. grep and
35
+ glob search that home directory unless you pass another path.
36
+ The Windows filesystem is reachable as /mnt/<drive>. Use bash
37
+ syntax for commands, not PowerShell.
38
+ - id: agent-instructions
39
+ name: '@deepseek-ai/dsh-agent-instructions'
40
+ config:
41
+ maxBytes: 65536
42
+ - id: time-context
43
+ name: '@deepseek-ai/dsh-time-context'
44
+ - id: wsl-world
45
+ name: 'dsh-wsl-bridge/wsl-world'
46
+ config:
47
+ distro: ''
48
+ timeoutMs: 120000
49
+ defaultWorkdir: home
50
+ - id: tool-jobs
51
+ name: '@deepseek-ai/dsh-tool-jobs'
52
+ - id: tool-schedule
53
+ name: '@deepseek-ai/dsh-tool-schedule'
54
+ - id: skill-filesystem
55
+ name: '@deepseek-ai/dsh-skill-filesystem'
56
+ - id: tool-skill
57
+ name: '@deepseek-ai/dsh-tool-skill'
58
+ - id: command-goal
59
+ name: '@deepseek-ai/dsh-command-goal'
60
+ - id: tool-goal
61
+ name: '@deepseek-ai/dsh-tool-goal'
62
+ - id: planning
63
+ name: cordis:group
64
+ group: true
65
+ isolate:
66
+ planMode: true
67
+ config:
68
+ - id: plan-mode
69
+ name: '@deepseek-ai/dsh-plan-mode'
70
+ config:
71
+ section: |
72
+ You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
73
+
74
+ Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
75
+
76
+ The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
77
+
78
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
79
+
80
+ Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
81
+
82
+ When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
83
+ - id: compaction
84
+ name: cordis:group
85
+ group: true
86
+ isolate:
87
+ compaction: true
88
+ toolResultPruner: true
89
+ config:
90
+ - id: compaction-basic
91
+ name: '@deepseek-ai/dsh-compaction-basic'
92
+ - id: command-compact
93
+ name: '@deepseek-ai/dsh-command-compact'
94
+ - id: tool-result-pruner
95
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
96
+ config:
97
+ thresholdChars: 8192
98
+ headChars: 4096
99
+ tailChars: 1024
100
+ - id: delegation
101
+ name: cordis:group
102
+ group: true
103
+ isolate:
104
+ workflowEngine: true
105
+ config:
106
+ - id: tool-subagent-control
107
+ name: '@deepseek-ai/dsh-tool-subagent-control'
108
+ - id: tool-subagent-list-agents
109
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
110
+ - id: tool-subagent
111
+ name: '@deepseek-ai/dsh-tool-subagent'
112
+ config:
113
+ provider: spawn
114
+ toolName: subagent
115
+ modelSelectionSettings: true
116
+ - id: tool-subagent-fork
117
+ name: '@deepseek-ai/dsh-tool-subagent'
118
+ config:
119
+ provider: fork
120
+ toolName: subagent_fork
121
+ - id: workflow-ptc
122
+ name: '@deepseek-ai/dsh-workflow-ptc'
123
+ config:
124
+ provider: spawn
125
+ - id: tool-workflow
126
+ name: '@deepseek-ai/dsh-tool-workflow'
127
+ - id: tool-ask-user
128
+ name: '@deepseek-ai/dsh-tool-ask-user'
129
+ - id: tool-todo
130
+ name: '@deepseek-ai/dsh-tool-todo'
131
+ config:
132
+ allowParallelInProgress: true
133
+ - id: tool-web
134
+ name: '@deepseek-ai/dsh-tool-web'
135
+ config:
136
+ fetch: true
137
+ searchTimeoutMs: 60000
138
+ - id: present
139
+ name: '@deepseek-ai/dsh-tool-present'
140
+ - id: tool-plugin-manager
141
+ name: '@deepseek-ai/dsh-plugin-manager/tools'
142
+ disabled: true
143
+ - id: tool-plugin-market
144
+ name: '@deepseek-ai/dsh-plugin-market/tools'
145
+ disabled: true
146
+ disabled: !!js process.platform !== 'win32'
@@ -0,0 +1,73 @@
1
+ # Architecture
2
+
3
+ `dsh-wsl-bridge` gives DSH on Windows a native Linux execution surface without a
4
+ second web server and without installing anything inside WSL.
5
+
6
+ ## Two layers
7
+
8
+ | Layer | Entry | Rows | What it does |
9
+ |---|---|---|---|
10
+ | Host bridge | `index.js` | `wsl-bridge` | Registers `wsl_bash`/`wsl_read`/`wsl_write`/`wsl_edit`/`wsl_path`/`wsl_env` for **every** session and publishes a routing prompt section. |
11
+ | WSL preset | `wsl-world.js` | `preset-wsl` → `wsl-world` | Declares an agent preset `WSL`; a session created in that mode gets `bash`/`read`/`write`/`edit`/`grep`/`glob`/`wsl_env` whose world is the distribution. |
12
+
13
+ The preset row is a plain `@deepseek-ai/dsh-agent-preset` declaration. Its
14
+ `plugins` list is the shipped `standard` preset with three substitutions:
15
+
16
+ - `tool-bash` / `tool-pwsh` removed (they are the Windows shells);
17
+ - `tool-fs` / `tool-fs-search` removed (host-path file tools and Windows ripgrep);
18
+ - `wsl-world` inserted.
19
+
20
+ Everything else (persona, instructions, skills, goals, plan mode, compaction,
21
+ subagents, todo, web, present) is the shipped set, so a WSL session is a normal
22
+ DSH session whose shell and file world happen to be Linux.
23
+
24
+ ## Why no service replacement
25
+
26
+ DSH exposes `ctx.shell` and `ctx.fs` services, but a composition allows exactly
27
+ one implementation of each. Replacing them would change every session, and a
28
+ preset-scope provider would need an isolate realm to avoid leaking. Instead the
29
+ preset mounts **tools**, which are scoped: the host's tools stay untouched and
30
+ the WSL session's tools shadow nothing globally.
31
+
32
+ ## Execution path
33
+
34
+ ```
35
+ model tool call
36
+ → tool.execute(args, exec)
37
+ → shell pool (one persistent `wsl.exe … bash -l -s` per agent; one-shots for files)
38
+ → ctx.subprocess.spawn({ argv, stdio })
39
+ → wsl.exe → bash inside the distribution
40
+ ```
41
+
42
+ - **Persistent shell**: one frame per call (nonce begin/end markers, `$?`
43
+ captured, stdout/stderr kept separate). `cd` and exports survive; a dead shell
44
+ is reported with `shellRestarted` and rebuilt on the next call.
45
+ - **One-shot calls** (`read`/`write`/`edit`/`grep`/`glob`): `wsl.exe --cd <dir> -e
46
+ bash -lc <command>`. Payloads travel as base64 on stdin, so quoting and binary
47
+ bytes can never change the command; writes go through a sibling temp file and
48
+ `mv -T`, and existing file modes are preserved. Edits hash the exact bytes read
49
+ and recheck the hash just before rename. This is a best-effort stale check,
50
+ not atomic compare-and-swap: an external writer can still race the final
51
+ check/rename. Atomic replacement replaces a target symlink rather than its
52
+ referent; use the resolved file path if the link must be preserved. Ownership,
53
+ ACLs and extended attributes are not copied from the old inode.
54
+ - **Paths**: Windows drive paths map to `/mnt/<drive>`. Host `wsl_*` tools still
55
+ translate the session directory into the default workdir. A `WSL` session
56
+ (`defaultWorkdir: home`) reads the distribution's `$HOME` once and uses that
57
+ path for the initial shell, relative paths, and grep/glob. `wsl_path` prefers
58
+ the distribution's own `wslpath` and falls back to the host-side mapping.
59
+ - **grep/glob** run GNU grep/find inside the distribution (the host search suite
60
+ ships a Windows ripgrep, which cannot see Linux paths). They prune hidden
61
+ entries and `node_modules`/VCS metadata; `.gitignore` is not read (documented).
62
+
63
+ ## Platform and compatibility gates
64
+
65
+ - Windows only: both rows carry `disabled: !!js process.platform !== 'win32'`.
66
+ - `engines.dsh: ">=0.2.1-rc.2"` is the install-time gate the Desktop plugin
67
+ manager reads; the loader itself only composes bundles listed in
68
+ `dsh.profile.bundles`.
69
+ - Zero dependencies and no build step. This is deliberate: on DSH 0.2.1 a preset
70
+ row spelled as a `file:` URL cannot resolve bare `@deepseek-ai/*` imports (the
71
+ module interception layer is keyed on the importer's path), so a package-name
72
+ row from the profile's `node_modules` is the only reliable shape. The bundle
73
+ therefore imports nothing outside `node:` built-ins.
@@ -0,0 +1,33 @@
1
+ # Security and boundaries
2
+
3
+ ## What the bridge can touch
4
+
5
+ The bridge spawns `wsl.exe` through the host `subprocess` service. That path is
6
+ **below** DSH's file sandbox: on Windows the sandbox wraps shell executors and the
7
+ filesystem mutations with an ACL/restricted-token runner, but the Linux side of a
8
+ `wsl.exe` child runs on the Linux kernel and cannot be fenced by a Windows token.
9
+
10
+ Consequences, stated rather than hidden:
11
+
12
+ - A WSL-side write through these tools is not constrained by the session's
13
+ `workspace-write` policy. Treat the bridge as able to write anything the WSL
14
+ user can write, plus whatever the Windows user can reach through `/mnt/<drive>`.
15
+ - The destructive-command guard is a **deterministic refusal list**, not a kernel
16
+ boundary: it stops obvious irreversible commands (`rm -r`, device writes,
17
+ power control, `find -delete`, `git clean -fdx`) unless the call passes
18
+ `allowDangerous: true`. A differently spelled command can still do damage.
19
+ - `distro` is a caller-visible string; no shell interpolation happens on the host
20
+ side (argv arrays only), and paths/payloads are quoted or base64-encoded.
21
+
22
+ ## What it does not do
23
+
24
+ - It opens no network listener and starts no server.
25
+ - It installs nothing in the distribution and asks for no credentials.
26
+ - It does not read the host environment into the distribution: `ctx.subprocess`
27
+ already scrubs `*KEY*`/`*SECRET*`/`*TOKEN*` and `DSH_*` from child environments.
28
+
29
+ ## Reporting
30
+
31
+ This is a community plugin. Report security-relevant issues through
32
+ https://github.com/TGWgulugu/dsh-wsl-bridge/issues and include the minimum
33
+ reproduction, without credentials or personal paths.