dsh-plugin-wsl-env 0.9.0 → 0.10.0
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 +417 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +84 -68
- package/SUPPORT.md +3 -3
- package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
- package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
- package/agent/wsl-agent.sh +83 -16
- package/cordis.patch.yml +41 -35
- package/docs/ARCHITECTURE.md +9 -1
- package/docs/CONFIGURATION.md +7 -4
- package/docs/LIMITATIONS.md +53 -18
- package/docs/PARITY.md +3 -1
- package/docs/PEER-PARITY.md +18 -11
- package/docs/README.zh.md +97 -74
- package/docs/UPSTREAM-FSIO-EXPORT.md +3 -3
- package/docs/UPSTREAM-TERMINAL-TITLE.md +9 -2
- package/examples/preset-wsl-session-sandbox.cordis.patch.yml +178 -0
- package/lib/agent-protocol.d.ts +10 -4
- package/lib/agent-protocol.js +10 -3
- package/lib/agent.d.ts +84 -0
- package/lib/agent.js +167 -15
- package/lib/bwrap.d.ts +8 -0
- package/lib/bwrap.js +4 -1
- package/lib/fs-routing.d.ts +40 -4
- package/lib/fs-routing.js +80 -12
- package/lib/fsio-agent.js +1 -1
- package/lib/index.d.ts +134 -0
- package/lib/index.js +191 -3
- package/lib/native-agent.d.ts +124 -0
- package/lib/native-agent.js +198 -0
- package/lib/ports.d.ts +13 -2
- package/lib/ports.js +18 -5
- package/lib/preflight.js +24 -1
- package/lib/session-runtime.d.ts +161 -0
- package/lib/session-runtime.js +220 -0
- package/lib/session-scope.d.ts +124 -0
- package/lib/session-scope.js +191 -0
- package/lib/shell-env.js +10 -3
- package/lib/watcher.d.ts +25 -12
- package/lib/watcher.js +120 -32
- package/lib/workspace-files-route.d.ts +8 -4
- package/lib/workspace-files-route.js +42 -14
- package/lib/workspace-files-wsl.js +25 -8
- package/package.json +34 -25
package/docs/README.zh.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/dsh-plugin-wsl-env)
|
|
7
7
|
[](../LICENSE)
|
|
8
8
|
|
|
9
|
-
这个插件让 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
9
|
+
这个插件让 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 在 WSL 子系统里干活:模型的命令在子系统里执行,文件工具读写的是子系统的真实文件,文件夹选择器能打开子系统目录,GUI 终端开的也是子系统里的 shell。
|
|
10
10
|
|
|
11
|
-
**只支持 Windows + WSL2。** 安装只需一条命令,插件自身没有依赖。环境按会话生效:打开 Windows
|
|
11
|
+
**只支持 Windows + WSL2。** 安装只需一条命令,插件自身没有依赖。环境按会话生效:打开 Windows 文件夹的会话,用的还是原来的 Windows 工具。
|
|
12
12
|
|
|
13
|
-
[安装](#安装) · [使用](#使用) · [配置](#配置) · [配方](#配方) · [架构](#架构) · [沙箱](#沙箱) · [常见问题](#常见问题) ·
|
|
13
|
+
[安装](#安装) · [使用](#使用) · [方案对比](#方案对比) · [配置](#配置) · [配方](#配方) · [架构](#架构) · [沙箱](#沙箱) · [常见问题](#常见问题) ·
|
|
14
14
|
[开发](#开发) · [文档](#文档)
|
|
15
15
|
|
|
16
16
|
## 安装
|
|
@@ -18,108 +18,128 @@
|
|
|
18
18
|
在 Windows 终端里执行这四条命令:
|
|
19
19
|
|
|
20
20
|
```powershell
|
|
21
|
-
dsh wsl --from-default-profile web --dump-config # 1.
|
|
22
|
-
dsh plugin --profile wsl add dsh-plugin-wsl-env # 2.
|
|
23
|
-
dsh --profile wsl --dump-config # 3.
|
|
21
|
+
dsh wsl --from-default-profile web --dump-config # 1. 创建 profile(dsh 的一套命名配置),模板用 Web
|
|
22
|
+
dsh plugin --profile wsl add dsh-plugin-wsl-env # 2. 安装插件,配置自动带上
|
|
23
|
+
dsh --profile wsl --dump-config # 3. 快速检查:不启动,只看拼好的配置
|
|
24
24
|
dsh --profile wsl # 4. 启动
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
第 3
|
|
27
|
+
第 3 步应该能看到一行 `# == dsh-plugin-wsl-env`——那就是插件的配置已经进了你的 profile。插件自带配置,第 2 步会自动接好,基本安装不需要手动改任何文件。
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**第一条命令之前,先给子系统安装 bubblewrap。** 每条命令都运行在 bubblewrap 沙箱内(默认还包在会话级 systemd scope 里),而多数子系统不预装它。缺失时,命令直接失败,不会脱箱运行:
|
|
30
30
|
|
|
31
31
|
```powershell
|
|
32
32
|
wsl.exe -d <子系统> -u root -- apt-get install -y bubblewrap # Debian/Ubuntu
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
在插件检出目录里,`pnpm run bootstrap -- <子系统>` 检查插件用到的四个工具(bubblewrap、ripgrep、git、inotify-tools),并按子系统的包管理器打印安装命令;加 `--install` 直接执行。若跳过此步,会话打开时会报告缺失的包及确切的安装命令。
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
**默认 preset 还要求 systemd。** 命令在会话级的 systemd user scope 内执行(后台 dev server 存活、`/tmp` 共享、`ps` 能看到整个会话)。在 `/etc/wsl.conf` 里设 `[boot] systemd=true`,重启 WSL,并执行一次 `loginctl enable-linger "$USER"`。缺任何一项,会话会带修复命令拒绝;在 preset 的 `wsl-shell` 上设 `sessionSandbox: false` 可回到每命令沙箱。
|
|
38
|
+
|
|
39
|
+
然后在 GUI 里打开一个 `\\wsl.localhost\<子系统>\...` 下的文件夹。选择器会列出每个已安装的子系统,**New terminal** 开的也是子系统里的 shell。
|
|
38
40
|
|
|
39
41
|
卸载:`dsh plugin --profile wsl remove dsh-plugin-wsl-env`。升级:再执行一次同样的 `add` 命令。
|
|
40
42
|
|
|
41
|
-
> **改了 `lib/`
|
|
43
|
+
> **改了 `lib/` 下的文件?先同步,再重启应用。** 应用加载的是 Windows 侧那份代码副本,所以先跑 `pnpm run sync:windows`。不同步就重启,加载的还是旧代码;运行中的进程还会缓存模块。见[开发](#开发)。
|
|
42
44
|
|
|
43
45
|
## 使用
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
把一个子系统文件夹作为工作区打开——比如 `\\wsl.localhost\ubuntu\home\you\project`——然后问模型:"我现在在什么内核上,`/etc/os-release` 里写了什么?" 它会在子系统里执行 `uname -r` 并读取这个文件。不复制文件,也不经过 `/mnt/c`。
|
|
48
|
+
|
|
49
|
+
- **子系统文件夹自动获得子系统环境。** 会话一打开子系统文件夹,就会被自动配上 WSL 环境。第一条命令就已经是对的。
|
|
50
|
+
- **命令在你期望的地方运行。** 命令在子系统里、用你自己的登录 shell 执行——所以你的 `PATH`、`nvm`、`cargo`、`pyenv` 和 rc 配置全部生效。shell 用的是子系统配置的那个,不是写死的 bash。
|
|
51
|
+
- **文件就是真实的文件。** `/home/you/x` 和 `\\wsl.localhost\ubuntu\home\you\x` 是同一个文件。`/mnt/c/...` 照常通向 Windows 磁盘。
|
|
52
|
+
- **终端也是。** 右侧栏 → *New terminal*:子系统里的 shell,落在会话所在目录。
|
|
53
|
+
- **端口可见性。** 模型能看到子系统内正在监听的端口(约每 10 秒刷新),并给出它启动的 dev server 的确切地址。WSL2 将 localhost 转发给 Windows,浏览器直接可访问。
|
|
54
|
+
- **权限和 Linux 主机一致。** 权限选择器有三档:`read-only`、`workspace-write`(默认)和 `danger-full-access`。有操作被拒绝时,模型会收到一次"用刚好够用的权限重试"的提议。只有 `danger-full-access` 是不带沙箱运行的。
|
|
55
|
+
|
|
56
|
+
## 方案对比
|
|
57
|
+
|
|
58
|
+
Windows 上的编程工具要在 WSL 项目上干活,一共三条路:
|
|
59
|
+
|
|
60
|
+
1. **把工具装进 WSL。** Codex CLI、Claude Code、ZCode CLI 的官方建议都是这条,而且没错——如果一款 CLI 能覆盖你的工作,它就是最简单的路。前提是整个工具都能搬进 WSL;DeepSeek Harness 搬不进去,所以只剩下面两条。
|
|
61
|
+
2. **桌面应用远程连进 WSL。** VS Code(Remote-WSL,开源镜像 [open-remote-wsl](https://github.com/jeanp413/open-remote-wsl))和 [ZCode](https://github.com/zai-org/ZCode) 这类 agent 桌面端把应用留在 Windows,往子系统里装一个服务程序。很成熟。代价:服务目录(`~/.vscode-server`、`~/.zcode/server`)、一个端口,命令无沙箱。
|
|
62
|
+
3. **本插件。** Harness 留在 Windows。子系统内不安装任何内容、不监听端口,命令运行在 bubblewrap 沙箱内。缺失时,会话打开即报告缺失项与安装命令。
|
|
46
63
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
| | 第二条(桌面远程) | 第三条(本插件) |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| 往子系统里装了什么 | 一个服务目录 | 什么都不装 |
|
|
67
|
+
| 是否监听端口 | 是 | 否 |
|
|
68
|
+
| 命令沙箱 | 无 | bubblewrap + systemd 会话 scope |
|
|
69
|
+
| 卸载之后 | 手动删除服务目录 | 无残留 |
|
|
70
|
+
| WSL 升级出了问题 | 服务可能需要修复 | 下一条命令自动恢复 |
|
|
71
|
+
|
|
72
|
+
**各方案的取舍。** 桌面远程重开工作区是即时的——其服务常驻运行;VS Code 的远程有多年生产打磨。装进 WSL 的 CLI 不需要任何额外机制。本插件只针对一种场景:DeepSeek Harness 运行在 Windows 上,同时要求 WSL 内的工作在沙箱中执行。
|
|
54
73
|
|
|
55
74
|
## 配置
|
|
56
75
|
|
|
57
|
-
|
|
76
|
+
每个设置都挂在一个有名字的"行"上。想改哪个,就在 `$DSH_HOME/profiles/<名字>/cordis.patch.yml` 里加一行同 id 的覆盖。值得知道的键:
|
|
58
77
|
|
|
59
78
|
| 行 | 键 | 默认值 | 说明 |
|
|
60
79
|
|---|---|---|---|
|
|
61
|
-
| `wsl-shell` | `distro` | `''` |
|
|
62
|
-
| | `sandbox` | `true` |
|
|
63
|
-
| | `
|
|
80
|
+
| `wsl-shell` | `distro` | `''` | 用哪个子系统;空表示 WSL 的默认子系统 |
|
|
81
|
+
| | `sandbox` | `true` | 命令跑在 `bubblewrap` 沙箱里;`false` 关掉沙箱 |
|
|
82
|
+
| | `sessionSandbox` | `true` | 会话内的命令跑在同一个常驻 systemd user scope 里:后台 dev server 跨命令存活、`/tmp` 共享、`ps` 能看到整个会话。需要 systemd 为 PID 1、user manager 可达、`Linger` 打开;预检失败会带修复命令拒绝。设 `false` 回到每命令沙箱 |
|
|
83
|
+
| | `agentBackend` | `auto` | 会话 runtime 的 in-distro agent:`auto` 在存在已校验来源(打包二进制或 `DSH_WSL_AGENT_NATIVE` 开发覆盖)时用 native,否则用 shell;`native` 准备失败时让首条命令失败而不是静默回退;`shell` 强制 shell。命令和受限写入共用该选择。native 是默认后端,shell agent 是回退 |
|
|
84
|
+
| | `maskWindowsDrive` | `false` | 让沙箱里的命令看不到 `/mnt`,收窄互操作这个洞(只是收窄,关不死,见[沙箱](#沙箱)) |
|
|
64
85
|
| `wsl-fs` | `distro` | `''` | 同上 |
|
|
65
|
-
| | `restrictToDistro` | `true` |
|
|
66
|
-
| | `sandbox` | `true` |
|
|
67
|
-
| | `
|
|
68
|
-
| `
|
|
86
|
+
| | `restrictToDistro` | `true` | 拒绝属于**别的子系统**的路径(`/mnt/c` 属于本子系统,不受影响)。拒绝码是 `FS_OUTSIDE_DISTRO`,放宽权限也解不开 |
|
|
87
|
+
| | `sandbox` | `true` | 文件写入按同一份策略检查 |
|
|
88
|
+
| | `sessionSandbox` | `true` | 受限写入跑在与命令相同的会话 runtime 里(同一个真实 `/tmp`、同一份 workspace mount);需要同样的 systemd/linger 配置;`false` 回到每策略受限 resident |
|
|
89
|
+
| | `substrate` | `agent` | 文件操作如何到达子系统。只剩一种:子系统内的 agent,读写直接落在子系统自己的文件系统上(原生符号链接与权限位)。旧 `"share"` 选项走 9p 共享,启动时即被拒绝 |
|
|
90
|
+
| `directory-picker-wsl` | `includeHostHome` | `true` | 选择器里同时列出 Windows 家目录 |
|
|
69
91
|
| `subprocess-wsl` | `distro` | `''` | GUI 终端开在哪个子系统 |
|
|
70
92
|
|
|
71
|
-
`wsl-shell`
|
|
93
|
+
`wsl-shell` 和 `wsl-fs` 这两行**按 id 覆盖不到**:它们嵌在 `wsl` preset 自己的配置里(preset 是 dsh 能发给会话的一套命名环境),在自己的覆盖文件里写 `- id: wsl-shell` 不会生效——loader 只会警告 `patch: entry "wsl-shell" not found`,实际值不变。要改,就把整个 `preset-wsl` 行复制过去改写;配方见 [docs/CONFIGURATION.md](CONFIGURATION.md#overriding-the-rows-inside-preset-wsl)。
|
|
72
94
|
|
|
73
|
-
[`cordis.patch.yml`](../cordis.patch.yml) 是每个随包值的注释参考。[docs/CONFIGURATION.md](CONFIGURATION.md)
|
|
95
|
+
[`cordis.patch.yml`](../cordis.patch.yml) 是每个随包值的注释参考。[docs/CONFIGURATION.md](CONFIGURATION.md) 列出其余键(`shell`、`loginShell`、`cwd`、`timeoutMs`、`preferredDistro`、`maxEntries` 等);[examples/profile.cordis.patch.yml](../examples/profile.cordis.patch.yml) 是一份可以照抄修改的本机配置。
|
|
74
96
|
|
|
75
97
|
## 配方
|
|
76
98
|
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- **路径与性能**:模型看到并操作的是子系统内的 Linux 路径(`/home/...`),写入子系统自身的 ext4;`/mnt/c` 通向 Windows 磁盘但走 9p,大批量小文件操作明显慢——重 IO 的项目请放在子系统文件系统内。`pnpm run bootstrap <子系统>` 会一并报告 ripgrep、git、inotifywait(搜索、快照与监视的后端)是否就位。
|
|
81
|
-
- **WSLENV 透传**:WSL 只导入 `WSLENV` 中列出的变量。本插件按前缀放行托管的 `DSH_*` 命名空间,其中带 Windows 路径的两个(`DSH_HOME`、`DSH_PROFILE_DIR`)加 `/p` 让 WSL 翻译成 `/mnt/c/...`。`PATH` 故意不透传——否则 Windows 的 PATH 会覆盖子系统自身的 PATH。
|
|
99
|
+
- **让子系统里的 git 用 Windows 的已存凭据**:`git config --global credential.helper "/mnt/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe"`(路径按你 Windows 侧 Git 的安装位置调整)。设置后,子系统里 `git push` 用的就是 Windows 保存的那份凭据,不用再输密码。
|
|
100
|
+
- **项目放在子系统的磁盘上。** 模型操作的是子系统自己磁盘上的 Linux 路径(`/home/...`),快。它也能通过 `/mnt/c` 访问 Windows 文件,但那条桥在大量小文件时明显慢。`pnpm run bootstrap <子系统>` 会告诉你 ripgrep、git、inotifywait(分别用于搜索和文件监视)装没装。
|
|
101
|
+
- **环境变量**:WSL 只转发 `WSLENV` 里点名的变量。本插件转发自己的 `DSH_*` 值,其中两个装着 Windows 路径的(`DSH_HOME`、`DSH_PROFILE_DIR`)会被翻译成 Linux 路径。你的 `PATH` 永远不转发——转发了会盖住子系统自己的 PATH。
|
|
82
102
|
|
|
83
103
|
## 架构
|
|
84
104
|
|
|
85
|
-
一个 DSH
|
|
105
|
+
一个 DSH 进程可以同时服务两类会话:工作区在 Windows 目录的,和工作区在子系统里的。插件的部件分两层放置,原因很简单——有的东西属于某一个会话,有的属于整个应用:
|
|
86
106
|
|
|
87
107
|
```text
|
|
88
108
|
组合层(composition,每个进程一份)
|
|
89
|
-
├─ subprocess-wsl GUI
|
|
109
|
+
├─ subprocess-wsl GUI 终端的执行环境
|
|
90
110
|
│ 子系统目录会话 → 子系统 shell,落在会话的 Linux 目录
|
|
91
111
|
│ Windows 目录会话 → powershell.exe,落在会话自己的目录
|
|
92
112
|
├─ directory-picker-wsl 在 Windows 家目录旁列出已安装的子系统
|
|
93
|
-
├─ workspace-files-wsl GUI
|
|
94
|
-
├─ fs-routing
|
|
95
|
-
│ 子系统 UNC →
|
|
113
|
+
├─ workspace-files-wsl 子系统工作区的 GUI 文件树与预览,由子系统内部提供
|
|
114
|
+
├─ fs-routing 根级文件系统,按路径坐标分流
|
|
115
|
+
│ 子系统 UNC → 常驻助手;盘符路径 → 宿主原生后端
|
|
96
116
|
├─ wsl-shell-env 向模型暴露 DSH_WSL_DISTRO / _SHELL / _HOME / _PORTS
|
|
97
|
-
└─ auto-preset
|
|
117
|
+
└─ auto-preset 会话打开子系统目录时,自动绑定 wsl preset
|
|
98
118
|
|
|
99
|
-
preset-wsl(wsl agent preset
|
|
119
|
+
preset-wsl(wsl agent preset;这个环境里的服务相互独立)
|
|
100
120
|
├─ wsl-shell ctx.shell — wsl.exe --exec <登录 shell>,在子系统内经 bubblewrap 约束
|
|
101
|
-
└─ wsl-fs ctx.fs —
|
|
121
|
+
└─ wsl-fs ctx.fs — 子系统的真实文件,经常驻助手落在 ext4
|
|
102
122
|
```
|
|
103
123
|
|
|
104
|
-
**为什么分两层。** `wsl-shell` 和 `wsl-fs`
|
|
124
|
+
**为什么分两层。** `wsl-shell` 和 `wsl-fs` 是随会话变化的两件事,所以放在 `wsl` preset 里:会话打开子系统目录时,`auto-preset` 给它绑上这个 preset;打开 Windows 目录的会话继续用原生 Windows 工具。一个进程,每个会话各有各的环境。GUI 终端是例外:它读的是应用层的配置,永远看不到 preset,所以 `subprocess-wsl` 挂在应用层。GUI 文件树和根级文件系统正好反过来——每个会话都用它们,单个 preset 拥有不了——所以 `workspace-files-wsl` 和 `fs-routing` 也挂在应用层。它们替换了随包的两行;根级路由的设计见 [docs/root-fs-routing.md](root-fs-routing.md)。
|
|
125
|
+
|
|
126
|
+
**命令的执行路径。** 一个会话有一个常驻 agent,运行在 systemd user scope 内。默认它是 native `dsh-wsl-agent`——按 distro 架构选择的包内静态 musl 二进制,由 `agentBackend: auto` 选中;二进制缺失或 copy/digest/exec 校验失败时回退到 runtime 的 shell agent,`agentBackend: shell` 强制 shell,`native` 则拒绝回退。agent 在你的登录 shell 中、bubblewrap mount profile 内执行每条命令。scope 比命令活得久,所以后台 dev server 存活、`/tmp` 是子系统真实 `/tmp`、`ps` 能看到整个会话。设 `sessionSandbox: false` 时,agent 仍是每子系统一个、每条命令一套 bwrap profile(`--unshare-pid`、临时 `/tmp`),后台进程不存活,且不使用 native 后端。agent 不可用时(或 `agent: false`),改走一次性 `wsl.exe`——结果一致、略慢、仍是每命令隔离。
|
|
105
127
|
|
|
106
|
-
|
|
128
|
+
**文件操作的执行路径。** 读、写、搜索同样发生在子系统内部:使用子系统自己的磁盘与工具。子系统工作区的搜索运行子系统内的 `rg`,不使用 Windows 侧副本走 9p 共享。文件写入与命令沙箱使用同一份策略检查;`wsl-fs` 的 `sessionSandbox` 打开时(默认),受限写入跑在与命令相同的会话 runtime 里(同一个 native/shell agent),看到相同的 `/tmp` 与 workspace mount。读操作走共享的 shell agent。
|
|
107
129
|
|
|
108
130
|
## 沙箱
|
|
109
131
|
|
|
110
|
-
|
|
132
|
+
命令跑在子系统内的 `bubblewrap` 沙箱里,文件写入按同一份策略检查。Windows 自带的沙箱在这里用不上:它的受限账户根本连不到 WSL。
|
|
111
133
|
|
|
112
134
|
| 模式 | 子系统里的命令可以做什么 |
|
|
113
135
|
|---|---|
|
|
114
|
-
| `read-only` |
|
|
115
|
-
| `workspace-write` |
|
|
116
|
-
| `danger-full-access` |
|
|
136
|
+
| `read-only` | 整个子系统只读;会挂一个新建的 `/dev`,`/dev/null` 和 `/dev/shm` 可用,其余不可写 |
|
|
137
|
+
| `workspace-write` | 上面全部,加上会话工作区可写;`/tmp` 在会话 runtime 里是子系统真实 `/tmp`(`sessionSandbox: false` 时每命令 profile 挂临时 tmpfs) |
|
|
138
|
+
| `danger-full-access` | 不加沙箱;用于你批准的放宽权限请求 |
|
|
117
139
|
|
|
118
|
-
**bubblewrap
|
|
140
|
+
**bubblewrap 是必需项,缺了就宁可不做。** 没有它,每条受限命令都报 `SANDBOX_UNAVAILABLE`——命令不会脱着沙箱运行。想关掉沙箱,在管这件事的行上设 `sandbox: false`:命令归 `wsl-shell`,文件写入归 `wsl-fs`,根级写入归顶层的 `fs-routing`(应用自己用的那套文件系统)。(前两行要经 `preset-wsl` 改,见[配置](#配置)。)关掉后,模型会如实收到"这些操作没有沙箱"的说明。
|
|
119
141
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
`maskWindowsDrive`(在 `wsl-shell`、`wsl-fs` 与 `fs-routing` 三个行上,默认 `false`)是这套 profile 能给出的唯一收窄手段:它为每个受限 profile 用空 tmpfs 遮蔽 `/mnt`,Windows 磁盘上的文件读不到也带不走,其上的可执行文件也无法经 interop 启动;但这只是收窄而非关闭——命令仍可把可执行文件写进工作区再运行(`binfmt` 按内容分发),所以 enforcement 仍是 `partial`,结果里如实上报 `windowsDrive: "masked"`。完全关闭是子系统级的:`wsl.conf` 里的 `[interop] enabled=false`,见 [docs/CONFIGURATION.md](CONFIGURATION.md)。
|
|
142
|
+
**已知边界。** 子系统内的命令仍可启动 Windows 程序(`/mnt/c/.../*.exe`),bubblewrap 不覆盖 Windows 进程。`pnpm run probe:sandbox` 会在你的机器上演示这条边界。在三个行上设 `maskWindowsDrive: true` 能收窄它:`/mnt` 会从命令的视野里消失,Windows 磁盘的文件读不到、程序启动不了。但并未完全关闭——命令仍可将 Windows 程序复制进工作区后运行——因此上报仍为 `partial`。彻底关闭的方法在子系统层面:`wsl.conf` 里设 `[interop] enabled=false`(见 [docs/CONFIGURATION.md](CONFIGURATION.md))。
|
|
123
143
|
|
|
124
144
|
设计见 [docs/ARCHITECTURE.md](ARCHITECTURE.md#sandbox),插件不做的事情见 [docs/LIMITATIONS.md](LIMITATIONS.md)。
|
|
125
145
|
|
|
@@ -127,43 +147,45 @@ preset-wsl(wsl agent preset;其服务运行在 isolate realm 内)
|
|
|
127
147
|
|
|
128
148
|
| 现象 | 原因 | 处理 |
|
|
129
149
|
|---|---|---|
|
|
130
|
-
| 每条命令都报 `SANDBOX_UNAVAILABLE` | 子系统里没有 `bubblewrap
|
|
131
|
-
|
|
|
132
|
-
|
|
|
133
|
-
| 重复执行 `dsh plugin add` 没有任何输出,`--dump-config`
|
|
134
|
-
|
|
|
135
|
-
| 改了 `lib/` 但不生效 |
|
|
136
|
-
| 用 `link:\\wsl.localhost\...` 安装后符号链接是坏的 | pnpm 无法链接 UNC 路径 |
|
|
137
|
-
| `glob`/`grep` 很慢 |
|
|
138
|
-
| 终端活动显示 `unknown` |
|
|
139
|
-
| 结果里出现 `FS_*` 码 |
|
|
150
|
+
| 每条命令都报 `SANDBOX_UNAVAILABLE` | 子系统里没有 `bubblewrap`,或者装了但坏的——错误消息会告诉你是哪种、该怎么办 | 按错误消息里的指引处理,或 `pnpm run bootstrap -- <子系统> --install`,或在 `wsl-shell`(命令)与 `wsl-fs`(写入)上都设 `sandbox: false`(根级写入的 `fs-routing` 行另有一份;前两行怎么改见[配置](#配置)) |
|
|
151
|
+
| 命令或写入在会话目录外被拒绝 | 正常现象:`workspace-write` 只允许写会话目录 | 接受工具给出的放宽权限提示,或把会话开在你需要的目录上 |
|
|
152
|
+
| 会话目录内的写入也被拒绝 | 会话处于 `read-only` 模式 | 切换权限选择器 |
|
|
153
|
+
| 重复执行 `dsh plugin add` 没有任何输出,`--dump-config` 也没变化 | 插件已经装过了,没有新东西可报告——这是成功,不是失败 | 不用做任何事:层已经就位,`dsh --profile wsl --dump-config` 仍能看到 `# == dsh-plugin-wsl-env` |
|
|
154
|
+
| 终端打开后还是 `cmd.exe` | 这一层里的 `terminal-controller` 行没有生效 | 用 `dsh --profile wsl --dump-config` 确认能看到 `shell: { path: wsl.exe, name: WSL }` |
|
|
155
|
+
| 改了 `lib/` 但不生效 | 应用跑的是 Windows 侧那份代码副本,运行中的进程还会缓存模块 | 先 `pnpm run sync:windows` 更新副本,再重启应用 |
|
|
156
|
+
| 用 `link:\\wsl.localhost\...` 安装后符号链接是坏的 | pnpm 无法链接 UNC 路径 | 改链接 Windows 路径;在子系统里开发请用运行时镜像,见[开发](#开发) |
|
|
157
|
+
| `glob`/`grep` 很慢 | 子系统里没装 `rg`;Windows 目录的搜索不受影响 | 运行 `pnpm run bootstrap -- <子系统> --install`(装 ripgrep);子系统工作区的搜索永远用子系统里的 rg,不走慢桥 |
|
|
158
|
+
| 终端活动显示 `unknown` | 仅在子系统内 agent 暂时不可用时出现——distro 终端是从子系统内部观察的(`/proc` 里扫 `DSH_TERMINAL_ID` 标记:只有 shell 是 `idle`,在跑任何命令是 `busy`) | 确认子系统在运行;空闲终端会在控制器的空闲超时(默认 2 小时)后自动关闭,`terminalIdleReclaim: false` 可以改成手动关 |
|
|
159
|
+
| 结果里出现 `FS_*` 码 | 码本身说明了是谁拒绝的、怎么解除 | 见 [docs/ARCHITECTURE.md](ARCHITECTURE.md#error-codes) 的错误码表 |
|
|
140
160
|
|
|
141
161
|
## 开发
|
|
142
162
|
|
|
143
163
|
```bash
|
|
144
|
-
pnpm run build # 从 src/ 生成 lib
|
|
145
|
-
pnpm test #
|
|
146
|
-
pnpm run sync:windows #
|
|
164
|
+
pnpm run build # 从 src/ 生成 lib/(产物提交进仓,改了 src/ 必须重新构建)
|
|
165
|
+
pnpm test # 风格、语法、构建一致性、类型检查、单元测试
|
|
166
|
+
pnpm run sync:windows # 把检出复制到应用加载的 Windows 侧副本(改 lib/ 后必跑,再重启应用)
|
|
147
167
|
pnpm run test:coverage # 带覆盖率阈值的单元测试(需 Node 22.8+)
|
|
148
168
|
pnpm run diagnose # 只读诊断报告(可直接贴进 issue):版本、工具、bwrap 探测
|
|
149
169
|
pnpm run bootstrap -- <子系统> # 检查四个子系统侧工具;加 --install 安装缺失项
|
|
150
|
-
pnpm run probe:sandbox # 在子系统里实测 bubblewrap
|
|
170
|
+
pnpm run probe:sandbox # 在子系统里实测 bubblewrap 能管什么、管不了什么
|
|
151
171
|
pnpm run probe # 文件系统探针,需要真实子系统(仅 Windows + WSL)
|
|
152
172
|
pnpm run probe:sandbox-shell # 启动真实 harness,驱动受限执行器
|
|
153
173
|
pnpm run probe:terminal # 通过终端 provider 打开一个 PTY
|
|
154
|
-
pnpm run probe:substrate # 用真实 wsl.exe
|
|
155
|
-
pnpm run probe:watch #
|
|
156
|
-
pnpm run probe:agent #
|
|
157
|
-
pnpm run probe:exec # agent
|
|
174
|
+
pnpm run probe:substrate # 用真实 wsl.exe 驱动文件助手(在子系统内运行)
|
|
175
|
+
pnpm run probe:watch # 在真实目录上装子系统内监视器(在子系统内运行)
|
|
176
|
+
pnpm run probe:agent # 常驻路径与回退路径的对比(在子系统内运行)
|
|
177
|
+
pnpm run probe:exec # agent 执行:超时、杀停、cwd 失败(在子系统内运行)
|
|
158
178
|
pnpm run probe:missing-wsl # 启动一个 wslPath 无法启动的 profile(仅 Windows + WSL)
|
|
159
179
|
pnpm run probe:picker # 列出选择器的根级、拒绝路径与上限(仅 Windows + WSL)
|
|
160
|
-
pnpm run probe:mode # 哪些 POSIX
|
|
161
|
-
pnpm run probe:sandbox-off # 验证 sandbox: false
|
|
180
|
+
pnpm run probe:mode # 哪些 POSIX 权限信息能穿过共享层(需 Windows node 与已装的 harness,不启动 profile)
|
|
181
|
+
pnpm run probe:sandbox-off # 验证 sandbox: false 确实关掉两侧沙箱(仅 Windows + WSL)
|
|
162
182
|
```
|
|
163
183
|
|
|
164
|
-
|
|
184
|
+
应用跑的是 Windows 侧的代码副本,不是你的检出。改了 `lib/` 下任何文件,先 `pnpm run sync:windows` 再重启应用——否则重启加载的还是旧代码。`pnpm run build` 负责从 `src/` 生成 `lib/`;产物提交进仓,`pnpm test` 会检查两者一致。
|
|
185
|
+
|
|
186
|
+
首次运行前 `pnpm install` 一次装好开发依赖。测试用到钉版的 `@deepseek-ai/*` 包;插件本身发布时不带任何依赖。CI 在 Linux 和 Windows 上用 Node 22、24 跑测试,Node 24 另跑覆盖率。
|
|
165
187
|
|
|
166
|
-
[docs/ARCHITECTURE.md](ARCHITECTURE.md) 有文件布局、挂载方式和沙箱设计;[CONTRIBUTING.md](../CONTRIBUTING.md)
|
|
188
|
+
[docs/ARCHITECTURE.md](ARCHITECTURE.md) 有文件布局、挂载方式和沙箱设计;[CONTRIBUTING.md](../CONTRIBUTING.md) 有开发循环、镜像、完整质量门清单和已验证内容。
|
|
167
189
|
|
|
168
190
|
## 文档
|
|
169
191
|
|
|
@@ -172,6 +194,7 @@ pnpm run probe:sandbox-off # 验证 sandbox: false 确实解除两侧约束
|
|
|
172
194
|
| [docs/CONFIGURATION.md](CONFIGURATION.md) | 每个配置键,以及如何覆盖 |
|
|
173
195
|
| [docs/ARCHITECTURE.md](ARCHITECTURE.md) | provider、挂载方式、路径坐标、沙箱、测试分层、文件布局 |
|
|
174
196
|
| [docs/LIMITATIONS.md](LIMITATIONS.md) | 插件不做的事情,以及原因 |
|
|
197
|
+
| [docs/VALUE.zh.md](VALUE.zh.md) | 本插件的价值、代价,以及什么情况下不该用它 |
|
|
175
198
|
| [CONTRIBUTING.md](../CONTRIBUTING.md) | 开发循环、质量门、约定、已验证内容 |
|
|
176
199
|
| [SECURITY.md](../SECURITY.md) | 如何私下上报漏洞 |
|
|
177
200
|
| [SUPPORT.md](../SUPPORT.md) | 支持范围,以及去哪里提问 |
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Status: draft, not yet filed upstream — no discussion link yet (the filing
|
|
4
4
|
convention is [UPSTREAM-TERMINAL-TITLE.md](UPSTREAM-TERMINAL-TITLE.md)).
|
|
5
|
-
Targeting `deepseek-harness` @ `packages/fs/fs-local`, the `0.2.
|
|
6
|
-
this plugin pins exactly (`@deepseek-ai/dsh-fs-local@0.2.
|
|
5
|
+
Targeting `deepseek-harness` @ `packages/fs/fs-local`, the `0.2.1-rc.x` line
|
|
6
|
+
this plugin pins exactly (`@deepseek-ai/dsh-fs-local@0.2.1-rc.2`, in both the
|
|
7
7
|
dev and peer dependency blocks of `package.json`). This is the Shape-A
|
|
8
8
|
upstream half of the in-distro I/O refactor: with the fsio text mechanics
|
|
9
9
|
importable, `dsh-plugin-wsl-env` deletes its copies of them.
|
|
@@ -67,7 +67,7 @@ same surface.)
|
|
|
67
67
|
Both files are generated; the change itself belongs in the sources that emit
|
|
68
68
|
them (`src/index.ts` re-exporting from the fsio module, and that module
|
|
69
69
|
exporting `detectLineEndings`). The artifact diffs are shown because they are
|
|
70
|
-
what a consumer resolves — and they apply as-is to the published `0.2.
|
|
70
|
+
what a consumer resolves — and they apply as-is to the published `0.2.1-rc.2`
|
|
71
71
|
files.
|
|
72
72
|
|
|
73
73
|
`detectLineEndings` is the one genuinely new name: it is not in
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Status: proposed upstream as
|
|
4
4
|
[deepseek-ai/deepseek-harness#8769](https://github.com/deepseek-ai/deepseek-harness/discussions/8769)
|
|
5
|
-
(targeting `packages/api/terminal-controller`, version line `0.2.
|
|
5
|
+
(targeting `packages/api/terminal-controller`, version line `0.2.1-rc.x`; the
|
|
6
|
+
gap was re-checked against `dsh-v0.2.1-rc.2` on 2026-10-17 and is unchanged —
|
|
7
|
+
`packages/api/terminal-controller/src/index.ts:327` still builds the terminal
|
|
8
|
+
info with `title: shell.name`, and the spawn spec still carries no display-name
|
|
9
|
+
field). The
|
|
6
10
|
plugin worked around the execution half of this gap — a Windows-folder
|
|
7
11
|
session's terminal runs a host shell (`subprocess-wsl.hostSessions`) — and
|
|
8
12
|
could not work around the title half. This is the record: the GUI titles every
|
|
@@ -63,7 +67,10 @@ Three escape hatches were evaluated against `0.2.0-rc.2` and each is closed:
|
|
|
63
67
|
@deepseek-ai/dsh-client-ui-sidebar-terminal: pending (waiting for service: webTerminals)
|
|
64
68
|
```
|
|
65
69
|
|
|
66
|
-
Verified live against `dsh-desktop 0.2.0-rc.2` (Electron 44.0.0)
|
|
70
|
+
Verified live against `dsh-desktop 0.2.0-rc.2` (Electron 44.0.0); the
|
|
71
|
+
row-id and `inject` facts the argument rests on are readable in the
|
|
72
|
+
`dsh-v0.2.1-rc.2` tree and did not change with the terminal controller's
|
|
73
|
+
0.2.1 rework (`attach`/`write` → sequenced `TerminalInput`).
|
|
67
74
|
3. **Per-session config.** The `shell` profile is composition state; `!!js`
|
|
68
75
|
expressions evaluate once at config resolution and reach no Session
|
|
69
76
|
identity.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# A profile-patch fragment for `$DSH_HOME/profiles/<name>/cordis.patch.yml`.
|
|
2
|
+
# It is the plugin's ENTIRE `preset-wsl` row (a row's config is replaced whole,
|
|
3
|
+
# never deep-merged) with `sessionSandbox: true` on the `wsl-shell` AND
|
|
4
|
+
# `wsl-fs` plugins.
|
|
5
|
+
#
|
|
6
|
+
# Paste it in place of any existing `- id: preset-wsl` entry in that profile
|
|
7
|
+
# layer, then restart the app. Regenerate it from the plugin's
|
|
8
|
+
# `cordis.patch.yml` whenever the shipped preset changes.
|
|
9
|
+
|
|
10
|
+
- id: preset-wsl
|
|
11
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
12
|
+
config:
|
|
13
|
+
id: wsl
|
|
14
|
+
order: 2
|
|
15
|
+
plugins:
|
|
16
|
+
- id: wsl-env
|
|
17
|
+
name: cordis:group
|
|
18
|
+
group: true
|
|
19
|
+
isolate:
|
|
20
|
+
shell: true
|
|
21
|
+
fs: true
|
|
22
|
+
config:
|
|
23
|
+
# ── the WSL environment for this preset only ────────────────────
|
|
24
|
+
# No `cwd`: an empty default workdir means the distro user's home,
|
|
25
|
+
# asked of the distro itself. Pin a directory here to override it —
|
|
26
|
+
# a value that is right on one machine is wrong on every other.
|
|
27
|
+
#
|
|
28
|
+
# Commands run in one persistent systemd user scope per session
|
|
29
|
+
# (ADR 0001): a background dev server survives the command that
|
|
30
|
+
# started it, `/tmp` is the distro's real `/tmp`, and `ps` sees the
|
|
31
|
+
# session. This needs systemd as PID 1 (`[boot] systemd=true` in
|
|
32
|
+
# /etc/wsl.conf) and a lingering user manager (`loginctl
|
|
33
|
+
# enable-linger <user>`); the preflight refuses with those remedies
|
|
34
|
+
# when one is missing. Set `sessionSandbox: false` for the old
|
|
35
|
+
# per-command shape. Bubblewrap is still required for the mount
|
|
36
|
+
# profile; without it the providers fail closed.
|
|
37
|
+
- id: wsl-shell
|
|
38
|
+
name: 'dsh-plugin-wsl-env'
|
|
39
|
+
config:
|
|
40
|
+
distro: ''
|
|
41
|
+
loginShell: true
|
|
42
|
+
timeoutMs: 120000
|
|
43
|
+
maxTimeoutMs: 600000
|
|
44
|
+
sessionSandbox: true
|
|
45
|
+
agentBackend: auto
|
|
46
|
+
- id: wsl-fs
|
|
47
|
+
name: 'dsh-plugin-wsl-env/fs'
|
|
48
|
+
config:
|
|
49
|
+
distro: ''
|
|
50
|
+
restrictToDistro: true
|
|
51
|
+
# agent (the default): reads/writes/identities run inside the
|
|
52
|
+
# distro over the resident agent (lib/fs-substrate.js) —
|
|
53
|
+
# native symlinks and mode bits, the write guard survives to
|
|
54
|
+
# publication, and confined mutations are kernel-enforced.
|
|
55
|
+
# share: the opt-out — the Windows-side host fs stack over
|
|
56
|
+
# the 9p share.
|
|
57
|
+
substrate: agent
|
|
58
|
+
# Confined writes ride the session runtime (ADR 0001), so they
|
|
59
|
+
# share the command's real /tmp and workspace mounts.
|
|
60
|
+
sessionSandbox: true
|
|
61
|
+
- id: persona
|
|
62
|
+
name: '@deepseek-ai/dsh-persona'
|
|
63
|
+
config:
|
|
64
|
+
prefix: You are a coding agent powered by the {{model}} model.
|
|
65
|
+
- id: agent-instructions
|
|
66
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
67
|
+
config:
|
|
68
|
+
maxBytes: 65536
|
|
69
|
+
- id: time-context
|
|
70
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
71
|
+
- id: tool-bash
|
|
72
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
73
|
+
disabled: false
|
|
74
|
+
- id: tool-pwsh
|
|
75
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
76
|
+
disabled: true
|
|
77
|
+
- id: tool-fs
|
|
78
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
79
|
+
- id: tool-fs-search
|
|
80
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
81
|
+
config:
|
|
82
|
+
sampleOverCapGlobResults: false
|
|
83
|
+
- id: tool-jobs
|
|
84
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
85
|
+
- id: tool-schedule
|
|
86
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
87
|
+
- id: skill-filesystem
|
|
88
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
89
|
+
- id: tool-skill
|
|
90
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
91
|
+
- id: command-goal
|
|
92
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
93
|
+
- id: tool-goal
|
|
94
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
95
|
+
- id: planning
|
|
96
|
+
name: cordis:group
|
|
97
|
+
group: true
|
|
98
|
+
isolate:
|
|
99
|
+
planMode: true
|
|
100
|
+
config:
|
|
101
|
+
- id: plan-mode
|
|
102
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
103
|
+
config:
|
|
104
|
+
section: |
|
|
105
|
+
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.
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
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.
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
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.
|
|
114
|
+
|
|
115
|
+
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.
|
|
116
|
+
- id: compaction
|
|
117
|
+
name: cordis:group
|
|
118
|
+
group: true
|
|
119
|
+
isolate:
|
|
120
|
+
compaction: true
|
|
121
|
+
toolResultPruner: true
|
|
122
|
+
config:
|
|
123
|
+
- id: compaction-basic
|
|
124
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
125
|
+
- id: command-compact
|
|
126
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
127
|
+
- id: tool-result-pruner
|
|
128
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
129
|
+
config:
|
|
130
|
+
thresholdChars: 8192
|
|
131
|
+
headChars: 4096
|
|
132
|
+
tailChars: 1024
|
|
133
|
+
- id: delegation
|
|
134
|
+
name: cordis:group
|
|
135
|
+
group: true
|
|
136
|
+
isolate:
|
|
137
|
+
workflowEngine: true
|
|
138
|
+
config:
|
|
139
|
+
- id: tool-subagent-control
|
|
140
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
141
|
+
- id: tool-subagent-list-agents
|
|
142
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
143
|
+
- id: tool-subagent
|
|
144
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
145
|
+
config:
|
|
146
|
+
provider: spawn
|
|
147
|
+
toolName: subagent
|
|
148
|
+
modelSelectionSettings: true
|
|
149
|
+
- id: tool-subagent-fork
|
|
150
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
151
|
+
config:
|
|
152
|
+
provider: fork
|
|
153
|
+
toolName: subagent_fork
|
|
154
|
+
- id: workflow-ptc
|
|
155
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
156
|
+
config:
|
|
157
|
+
provider: spawn
|
|
158
|
+
- id: tool-workflow
|
|
159
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
160
|
+
- id: tool-ask-user
|
|
161
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
162
|
+
- id: tool-todo
|
|
163
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
164
|
+
config:
|
|
165
|
+
allowParallelInProgress: true
|
|
166
|
+
- id: tool-web
|
|
167
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
168
|
+
config:
|
|
169
|
+
fetch: true
|
|
170
|
+
searchTimeoutMs: 60000
|
|
171
|
+
- id: present
|
|
172
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
173
|
+
- id: tool-plugin-manager
|
|
174
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
175
|
+
disabled: true
|
|
176
|
+
- id: tool-plugin-market
|
|
177
|
+
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
178
|
+
disabled: true
|
package/lib/agent-protocol.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module dsh-plugin-wsl/agent-protocol
|
|
15
15
|
*/
|
|
16
16
|
/** The protocol version this host side speaks; the agent must match. */
|
|
17
|
-
export declare const PROTOCOL_VERSION =
|
|
17
|
+
export declare const PROTOCOL_VERSION = 5;
|
|
18
18
|
/**
|
|
19
19
|
* Upper bound on one `read` op's payload, set by the HOST looping over windowed
|
|
20
20
|
* requests. Keeping each frame's decoded payload here means the agent's RES
|
|
@@ -127,15 +127,21 @@ export interface FsFrameRequest {
|
|
|
127
127
|
*/
|
|
128
128
|
export declare function encodeFsFrame({ id, op, args, timeoutMs }: FsFrameRequest): string[];
|
|
129
129
|
/**
|
|
130
|
-
* `HELLO|name|version|digest` — `digest` is the sha256 of the script file
|
|
131
|
-
* agent is executing (empty when it could not hash it); the host compares
|
|
132
|
-
* against its own shipped copy.
|
|
130
|
+
* `HELLO|name|version|digest|pid` — `digest` is the sha256 of the script file
|
|
131
|
+
* the agent is executing (empty when it could not hash it); the host compares
|
|
132
|
+
* it against its own shipped copy. `pid` is the agent shell's PID inside the
|
|
133
|
+
* distro, present from protocol 5 on: the host uses it to deliver an
|
|
134
|
+
* out-of-band `USR1` cancellation while the agent is blocked in `wait`, which
|
|
135
|
+
* the serial stdin loop cannot do by frame. It is absent when the agent
|
|
136
|
+
* itself runs inside a PID namespace (the confined resident), where that PID
|
|
137
|
+
* is not addressable from a second `wsl.exe`.
|
|
133
138
|
*/
|
|
134
139
|
export interface HelloMessage {
|
|
135
140
|
type: "hello";
|
|
136
141
|
name: string;
|
|
137
142
|
version: number;
|
|
138
143
|
digest: string;
|
|
144
|
+
pid?: number;
|
|
139
145
|
}
|
|
140
146
|
/** `PONG` — the liveness answer to `PING`. */
|
|
141
147
|
export interface PongMessage {
|
package/lib/agent-protocol.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module dsh-plugin-wsl/agent-protocol
|
|
15
15
|
*/
|
|
16
16
|
/** The protocol version this host side speaks; the agent must match. */
|
|
17
|
-
export const PROTOCOL_VERSION =
|
|
17
|
+
export const PROTOCOL_VERSION = 5;
|
|
18
18
|
/**
|
|
19
19
|
* Upper bound on one `read` op's payload, set by the HOST looping over windowed
|
|
20
20
|
* requests. Keeping each frame's decoded payload here means the agent's RES
|
|
@@ -124,8 +124,15 @@ export function encodeFsFrame({ id, op, args, timeoutMs = 0 }) {
|
|
|
124
124
|
*/
|
|
125
125
|
export function parseAgentLine(line) {
|
|
126
126
|
if (line.startsWith("HELLO|")) {
|
|
127
|
-
const [, name, version, digest] = line.split("|");
|
|
128
|
-
|
|
127
|
+
const [, name, version, digest, pid] = line.split("|");
|
|
128
|
+
const agentPid = Number(pid);
|
|
129
|
+
return {
|
|
130
|
+
type: "hello",
|
|
131
|
+
name: name ?? "",
|
|
132
|
+
version: Number(version),
|
|
133
|
+
digest: digest ?? "",
|
|
134
|
+
...(Number.isSafeInteger(agentPid) && agentPid > 0 ? { pid: agentPid } : {}),
|
|
135
|
+
};
|
|
129
136
|
}
|
|
130
137
|
if (line === "PONG")
|
|
131
138
|
return { type: "pong" };
|