dsh-wsl-tool 1.8.0 → 1.8.2

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/PUBLISHING.md ADDED
@@ -0,0 +1,161 @@
1
+ # Publishing
2
+
3
+ Maintainer notes for releasing this plugin. Four channels carry the same commit
4
+ and they are not interchangeable.
5
+
6
+ | Channel | What it is | What ships it |
7
+ |---|---|---|
8
+ | GitHub Release asset | what the plugin market installs from | `.github/workflows/release.yml`, on a `v*` tag |
9
+ | npm | the package `dsh-wsl-tool` | the same workflow, as its last step |
10
+ | Catalog entry | the listing line and the install command the market shows | a PR to `awesome-dsh-plugin/awesome-dsh-plugin` |
11
+ | Your working profile | the copy DSH actually loads | `npm run sync`, then a DSH restart |
12
+
13
+ ## The name split is deliberate
14
+
15
+ The npm package is **`dsh-wsl-tool`**, not `dsh-wsl`. The registry answers
16
+
17
+ ```
18
+ 403 Forbidden - PUT https://registry.npmjs.org/dsh-wsl -
19
+ Package name too similar to existing package is-wsl
20
+ ```
21
+
22
+ for the repository name. A name policy is not a permission problem: no token, no
23
+ 2FA bypass, and no scope changes it, and the name stays unclaimable. The
24
+ repository, the plugin (`tool-wsl`), and the market listing keep the `dsh-wsl`
25
+ name.
26
+
27
+ Nothing else depends on that name, because `cordis.patch.yml` names its entry by
28
+ **relative path**:
29
+
30
+ ```yaml
31
+ - insert:
32
+ - id: tool-wsl
33
+ name: './index.js'
34
+ ```
35
+
36
+ DSH anchors a relative `name:` beside the patch file that declared it
37
+ (`anchorInsertedPluginNames` in `@deepseek-ai/dsh-app-boot`), so this row loads
38
+ this bundle's own `index.js` whatever the install folder is called —
39
+ `node_modules/dsh-wsl-tool` from npm, `node_modules/dsh-wsl` in a local `file:`
40
+ profile. A bare package name would resolve in only one of those layouts, and
41
+ would silently couple this file to the registry name. `test/smoke.mjs` asserts
42
+ both that the entry stays relative and that it resolves.
43
+
44
+ ## Release checklist
45
+
46
+ 1. Green locally:
47
+
48
+ ```sh
49
+ npm test # shim backend, real WSL
50
+ DSH_SUBPROCESS_LOCAL=/path/to/dsh/node_modules npm run test:real
51
+ ```
52
+
53
+ 2. Bump, sync, commit, tag, push:
54
+
55
+ ```sh
56
+ npm version <x.y.z> --no-git-tag-version
57
+ npm run sync # into the profile copy DSH loads
58
+ git add -A && git commit -m "..."
59
+ git tag -a v<x.y.z> -m "..."
60
+ git push origin main && git push origin v<x.y.z>
61
+ ```
62
+
63
+ `npm run sync` defaults to
64
+ `%USERPROFILE%/.dsh/profiles/web/node_modules/dsh-wsl` — the folder the
65
+ profile's dependency key created, which need not match the npm name. Pass a
66
+ path to target another profile. Restart DSH afterwards: the plugin is imported
67
+ once at load.
68
+
69
+ 3. The tag runs the pipeline. The order is deliberate — the release asset goes
70
+ **first** because it is the market's critical path, then npm, so a failing npm
71
+ publish fails the run loudly without withholding the release.
72
+
73
+ The build step asks `npm pack --silent` for the tarball name (it follows
74
+ `package.json`, so it is `dsh-wsl-tool-<version>.tgz`) and copies it to
75
+ `dsh-wsl.tgz`, which is the fixed name the catalog's URL points at. Never
76
+ rename that copy: the market install breaks the moment the asset is not called
77
+ `dsh-wsl.tgz`, and `--latest` is what keeps `releases/latest` resolving to it.
78
+
79
+ 4. Verify each channel.
80
+
81
+ **CI** — a check annotation is readable anonymously, unlike the job log:
82
+
83
+ ```sh
84
+ curl -s "https://api.github.com/repos/XINY11451/dsh-wsl/actions/runs?per_page=5"
85
+ curl -s "https://api.github.com/repos/XINY11451/dsh-wsl/check-runs/<id>/annotations"
86
+ ```
87
+
88
+ A successful publish leaves `published <version> to npm`; a failure leaves
89
+ `npm publish failed: <the npm error lines>`, which is why the workflow turns
90
+ npm's stderr into `::error::` instead of only printing it.
91
+
92
+ **npm**:
93
+
94
+ ```sh
95
+ curl -s https://registry.npmjs.org/dsh-wsl-tool # dist-tags.latest
96
+ npm view dsh-wsl-tool version --registry https://registry.npmjs.org
97
+ ```
98
+
99
+ Fetching `dist.tarball` and unpacking it is the honest check — it proves the
100
+ artifact, not just the metadata. Scripted requests to `npmjs.com`'s HTML hit a
101
+ Cloudflare challenge; the registry API is the source of truth.
102
+
103
+ **Market asset** (this is what users install, and it is independent of npm):
104
+
105
+ ```sh
106
+ curl -sL -o /tmp/a.tgz https://github.com/XINY11451/dsh-wsl/releases/latest/download/dsh-wsl.tgz
107
+ tar -xzOf /tmp/a.tgz package/package.json | grep '"version"'
108
+ ```
109
+
110
+ **Catalog**: the catalog's generated data is at
111
+ <https://awesome-dsh-plugin.com/plugins.json> — look up `dsh-wsl` and read
112
+ `npm`, `version`, `downloads`, `install`, `tarball`.
113
+
114
+ ## The catalog entry
115
+
116
+ The listing lives at `data/plugins/XINY11451__dsh-wsl.yml` in
117
+ `awesome-dsh-plugin/awesome-dsh-plugin`, one file per plugin. Only
118
+ `description.en` is required; `description.zh` is optional. Edit it through a
119
+ one-file PR (the web UI's pencil button forks and proposes the change).
120
+
121
+ Two of its keys are hand-written and must stay:
122
+
123
+ - `tarball:` — the fixed release URL above. It is what the market's `install`
124
+ command points at today.
125
+ - `url` / `name` / `category` — `category: wsl`.
126
+
127
+ Everything else about the entry is **generated** and must never be written by
128
+ hand: `npm`, `version`, `downloads`, `screenshots`, and `install`. In particular
129
+ `scripts/probe-npm.mjs` reads `package.json`'s `name` from the repository's
130
+ `HEAD`, then accepts the package only when the registry document's
131
+ `versions[latest].repository.url` contains `<owner>/<repo>`
132
+ (case-insensitively — that check is what stops name squatting). So keep
133
+ `repository` in `package.json` accurate, or the catalog stops discovering the npm
134
+ package. "Not published" verdicts are re-probed daily, so a freshly published
135
+ version can take a day to appear; when it does, `install` switches from the
136
+ tarball URL to `dsh plugin --profile web add dsh-wsl-tool`.
137
+
138
+ ## Things that bite
139
+
140
+ - **A tag event runs the workflow from the tag's commit.** To change the
141
+ pipeline you must commit *and cut a new tag*; re-running an old run reuses the
142
+ workflow it was created with, and moving a tag is not a fix.
143
+ - **Job logs need authentication; annotations do not.** There is no `gh` and no
144
+ `GITHUB_TOKEN` on the maintainer machine, so anything that must be diagnosable
145
+ without a credential belongs in an annotation.
146
+ - **An interactive `npm publish` asks for a one-time password** and cannot be
147
+ driven from an unattended run. CI authenticates with a granular token that may
148
+ bypass 2FA, stored as the `NPM_TOKEN` secret; with the secret absent the step
149
+ skips with a notice instead of failing. Now that the package exists, OIDC
150
+ trusted publishing is the better replacement.
151
+ - **`publishConfig.registry` in `package.json` wins** over a developer's
152
+ `~/.npmrc` mirror, so a mirror configured for reads cannot intercept a publish.
153
+ - **The profile's dependency key is not the package name** (`dsh-wsl` versus
154
+ `dsh-wsl-tool`). Do not "fix" that by hand: the profile is managed by pnpm, and
155
+ its `pnpm-lock.yaml`, `node_modules/.modules.yaml`, and
156
+ `node_modules/.package-map.json` all record the key. The relative patch entry
157
+ means the difference costs nothing.
158
+ - **Short unscoped names ending in `-wsl` are a minefield.** `dsh-wsl` collides
159
+ with `is-wsl`; longer suffixed names such as `dsh-wsl-workspace` are published
160
+ by other authors and pass. Prefer npm's suggestion (a scope) or a clearly
161
+ suffixed name, and check the registry before tagging.
package/README.md CHANGED
@@ -2,7 +2,18 @@
2
2
 
3
3
  [English](README.md) | [简体中文](README.zh-CN.md)
4
4
 
5
- A model-facing **WSL** tool plugin for DeepSeek Harness (DSH). It lets an agent run Linux commands through `wsl.exe` directly — no hand-written `.sh` scripts or `pwsh` wrappers.
5
+ A model-facing **WSL** tool plugin for DeepSeek Harness (DSH). It lets an agent run
6
+ Linux commands through `wsl.exe` directly — no hand-written `.sh` scripts or `pwsh`
7
+ wrappers — and for command execution it essentially matches a native Linux DSH: a
8
+ real Linux kernel and bash, exit codes and signals, timeouts, truncation with spill
9
+ files, background jobs the built-in job tools read back, stdin, and automatic
10
+ Windows/WSL path translation.
11
+
12
+ Two limits are worth knowing before you rely on it. A `wsl` call runs below DSH's
13
+ sandbox, so a file policy does not confine it (see [Sandboxing](#sandboxing)); and a
14
+ project kept on a Windows drive keeps **Windows** filesystem semantics — no POSIX
15
+ permissions, case-insensitive names, no file-change notifications — at a fraction
16
+ of the speed (see [Notes](#notes)).
6
17
 
7
18
  ## Tools
8
19
 
@@ -96,18 +107,33 @@ specifier you install under.
96
107
  or `dsh plugin add --profile <profile> file:<path-to-this-repo>` from a
97
108
  checkout (which names the dependency after this package).
98
109
 
99
- 2. Add a `tool-wsl` row to an agent preset's `agent.cordis.yml`:
110
+ 2. Nothing else. The package's `cordis.patch.yml` inserts the `tool-wsl` row
111
+ itself when the bundle is loaded, process-wide, so the three tools are
112
+ available to every agent preset without further wiring.
100
113
 
101
- ```yaml
102
- - id: tool-wsl
103
- name: 'dsh-wsl-tool'
104
- ```
105
-
106
- `name` is the installed package name — use your own dependency key if you
107
- installed under a different one.
114
+ Do **not** also list `tool-wsl` in a preset: DSH registers tools by name, and
115
+ the second registration fails with
116
+ `tool "wsl" is already registered in this scope`. One row, from the bundle.
108
117
 
109
118
  3. Restart DSH.
110
119
 
120
+ ## Compatibility
121
+
122
+ Verified against DSH **0.1.7-rc.2** (and 0.1.5-rc.2 before it): the tool schemas
123
+ pass DSH's own `assertSupportedJsonSchema`, the subprocess seam is exercised
124
+ against the real provider rather than a shim, and the background-job path is
125
+ checked against the real job registry, including the session-id ownership fence
126
+ that 0.1.7 tightened. `test/real-seam.mjs` is that check and it re-verifies a host
127
+ in about a minute, so point it at any DSH installation after an upgrade:
128
+
129
+ ```sh
130
+ DSH_SUBPROCESS_LOCAL=/path/to/dsh/node_modules npm run test:real
131
+ ```
132
+
133
+ A background job is owned by the calling session (`owner: exec.agent.id`), which
134
+ is what lets the model read it back with `job_output`/`job_kill` and what keeps
135
+ other sessions out; an execution with no agent starts the job unowned.
136
+
111
137
  ## `wsl` parameters
112
138
 
113
139
  | Param | Required | Type | Notes |
@@ -169,7 +195,21 @@ takes effect on restart.
169
195
  workspace: /mnt/d/DSHworkarea (Windows drive mount /mnt/d — builds, installs and git are much slower here; prefer a path under /home when it matters)
170
196
  ```
171
197
 
172
- It is worth believing. Measured on the author's machine: a 128 MB sequential write ran at ~2.1 GB/s on ext4 against ~247 MB/s on `/mnt/d`, and creating 400 small files took under 10 ms against 0.72 s.
198
+ It is worth believing. Measured on the author's machine: a 128 MB sequential write
199
+ ran at ~2.1 GB/s on ext4 against ~247 MB/s on `/mnt/d`, and creating 400 small
200
+ files took under 10 ms against 0.72 s (a later re-run: 884 vs 116 MB/s, and 13 ms
201
+ against 745 ms — the ratios hold at roughly 8× and 50×).
202
+
203
+ It is not only slower. `/mnt/<drive>` is a 9p (drvfs) mount, so it keeps **Windows**
204
+ filesystem semantics: `chmod`/`chown` do not stick (a `chmod 600` reads back as
205
+ `777`), names are case-insensitive (so a case-sensitive import only fails on Linux
206
+ CI), symlinks and the executable bit are synthetic, and **inotify does not work at
207
+ all** — a watcher inside the distro receives no events for writes from either side
208
+ (measured with an inotify probe on `/mnt/d`: zero events for a Windows-side write
209
+ and for a Linux-side write, while the same probe on ext4 reported create, modify
210
+ and close-write). Dev servers, `--watch` modes and file-watching tests are
211
+ therefore blind on a Windows drive. Keep a project under `/home` when it matters:
212
+ it recovers real semantics, real watch events, and the speed above.
173
213
  - Destructive commands are refused unless the call passes `allowDangerous: true`:
174
214
  - **any recursive delete** — `rm -r`, `rm -rf`, `rm -r -f`, `rm -R --force`, `rm --recursive` — because with stdin on `/dev/null` nothing prompts, so `rm -r tree` deletes silently. Each `rm` invocation is judged on its own command segment, so `rm a -f; rm b -r` cannot combine into a pass;
175
215
  - `dd` onto a block device, `mkfs`, partitioning/wiping tools (`fdisk`, `parted`, `wipefs`, `mkswap`, …), power control (`shutdown`, `reboot`, `systemctl reboot`, …), redirection onto a block device, and fork bombs;
@@ -312,6 +352,9 @@ the folder name itself never matters.
312
352
 
313
353
  then restart DSH — the plugin is imported once at load.
314
354
 
355
+ Releasing — the tag-triggered workflow, the npm name, and the catalog entry — is
356
+ documented in [`PUBLISHING.md`](PUBLISHING.md).
357
+
315
358
  ## Listing
316
359
 
317
360
  The repository carries the `dsh-plugin` topic and is listed under the `wsl`
package/README.zh-CN.md CHANGED
@@ -2,7 +2,14 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- 面向模型(model-facing)的 **WSL** 工具插件,用于 DeepSeek Harness(DSH)。它让智能体直接通过 `wsl.exe` 执行 Linux 命令——无需手写 `.sh` 脚本或 `pwsh` 包装。
5
+ 面向模型(model-facing)的 **WSL** 工具插件,用于 DeepSeek Harness(DSH)。它让智能体直接通过
6
+ `wsl.exe` 执行 Linux 命令——无需手写 `.sh` 脚本或 `pwsh` 包装——并且在**命令执行**这一层基本对齐
7
+ Linux 原生 DSH:真实的 Linux 内核与 bash、退出码与信号、超时、截断与落盘、可用内置 job 工具读回的
8
+ 后台作业、stdin,以及 Windows/WSL 路径自动转换。
9
+
10
+ 有两点限制值得在依赖它之前知道:`wsl` 调用位于 DSH 沙箱层之下,文件策略约束不到它
11
+ (见[沙箱边界](#沙箱边界));项目放在 Windows 盘上时仍是 **Windows** 的文件系统语义——没有 POSIX
12
+ 权限位、文件名大小写不敏感、收不到文件变更通知——速度也低一个数量级(见[注意事项](#注意事项))。
6
13
 
7
14
  ## 工具
8
15
 
@@ -86,17 +93,29 @@ launcher: WSL 版本: 2.6.3.0 · 内核版本: 6.6.87.2-1 · WSLg 版本: 1.0.71
86
93
  `dsh plugin add --profile <profile> file:<path-to-this-repo>` 从本地检出安装
87
94
  (依赖名会取本包自身的名字)。
88
95
 
89
- 2. 在某个 agent preset 的 `agent.cordis.yml` 中加入 `tool-wsl` 行:
90
-
91
- ```yaml
92
- - id: tool-wsl
93
- name: 'dsh-wsl-tool'
94
- ```
96
+ 2. 不必再做别的。本包的 `cordis.patch.yml` 会在组合包加载时**自己插入** `tool-wsl` 行
97
+ (进程级),因此三个工具对所有 agent preset 都可用,无需额外接线。
95
98
 
96
- `name` 即安装后的包名;若你用了别的依赖名,就填那个名字。
99
+ **不要**再在 preset 里列一遍 `tool-wsl`:DSH 按名字注册工具,第二次注册会直接失败
100
+ —— `tool "wsl" is already registered in this scope`。这一行只由组合包提供。
97
101
 
98
102
  3. 重启 DSH。
99
103
 
104
+ ## 兼容性
105
+
106
+ 已在 DSH **0.1.7-rc.2** 上验证(此前为 0.1.5-rc.2):工具 schema 通过 DSH 自己的
107
+ `assertSupportedJsonSchema`;subprocess 接缝是跑在**真实 provider** 上而非替身;后台任务
108
+ 路径跑在**真实 job 注册表**上,包含 0.1.7 收紧的「会话 id 属主围栏」。这套检查就是
109
+ `test/real-seam.mjs`,约一分钟即可重验一个新宿主——升级后把它指向新的 DSH 安装即可:
110
+
111
+ ```sh
112
+ DSH_SUBPROCESS_LOCAL=/path/to/dsh/node_modules npm run test:real
113
+ ```
114
+
115
+ 后台任务由**调用方会话**持有(`owner: exec.agent.id`),这既是模型能用
116
+ `job_output`/`job_kill` 读回它的依据,也是其他会话读不到它的围栏;exec 里没有 agent 时
117
+ 任务则是无主的。
118
+
100
119
  ## `wsl` 参数
101
120
 
102
121
  | 参数 | 必填 | 类型 | 说明 |
@@ -174,8 +193,17 @@ launcher: WSL 版本: 2.6.3.0 · 内核版本: 6.6.87.2-1 · WSLg 版本: 1.0.71
174
193
  workspace: /mnt/d/DSHworkarea (Windows drive mount /mnt/d — builds, installs and git are much slower here; prefer a path under /home when it matters)
175
194
  ```
176
195
 
177
- 这个提示值得当真。作者机器实测:128 MB 顺序写在 ext4 上约 **2.1 GB/s**,在 `/mnt/d` 上约 **247 MB/s**;
178
- 创建 400 个小文件 ext4 **不到 10 ms**,`/mnt/d` 要 **0.72 s**。
196
+ 这个提示值得当真。作者机器实测:128 MB 顺序写在 ext4 上约 **2.1 GB/s**,在 `/mnt/d` 上约
197
+ **247 MB/s**;创建 400 个小文件 ext4 **不到 10 ms**,`/mnt/d` 要 **0.72 s**
198
+ (后来复测:**884 vs 116 MB/s**、**13 ms vs 745 ms**,比值稳定在约 8× 与 50×)。
199
+
200
+ 不只是慢。`/mnt/<盘>` 是 9p(drvfs)挂载,保留的是 **Windows** 的文件系统语义:
201
+ `chmod`/`chown` 不生效(`chmod 600` 读回来是 `777`)、文件名**大小写不敏感**(大小写写错只在
202
+ Linux CI 上才炸)、符号链接与可执行位是合成的,而且 **inotify 完全不工作**——发行版里的 watcher
203
+ 对两侧写入都收不到任何事件(用 inotify 探针实测:`/mnt/d` 上 Windows 侧写入与 Linux 侧写入均为
204
+ **0 事件**,而同一探针在 ext4 上正常报出创建/修改/关闭写入)。因此 dev server、`--watch` 模式
205
+ 与文件监听的测试在 Windows 盘上都是瞎的。需要时把项目放到 `/home` 下:语义、监听事件与上面那档
206
+ 速度一次性都回来。
179
207
  - 危险命令默认被拒绝,除非调用时传 `allowDangerous: true`:
180
208
  - **任何递归删除**——`rm -r`、`rm -rf`、`rm -r -f`、`rm -R --force`、`rm --recursive`——
181
209
  因为 stdin 指向 `/dev/null` 时不会产生任何提示,`rm -r tree` 会静默删除整棵树。
@@ -299,6 +327,8 @@ npm run sync -- /path/to/profiles/<profile>/node_modules/<你的依赖名>
299
327
 
300
328
  然后重启 DSH——插件在加载时只导入一次。
301
329
 
330
+ 发版流程(tag 触发的流水线、npm 包名与目录条目)见 [`PUBLISHING.md`](PUBLISHING.md)。
331
+
302
332
  ## 收录
303
333
 
304
334
  本仓库带有 `dsh-plugin` topic,并已提交至 awesome-dsh-plugin 社区列表的 `wsl` 分类。
package/cordis.patch.yml CHANGED
@@ -1,5 +1,11 @@
1
- # This bundle layer registers the wsl tool plugin in an agent preset.
2
- # Presets can also add the row manually instead of relying on this patch.
1
+ # This bundle layer inserts the wsl tool plugin process-wide, so every agent
2
+ # preset gets the `wsl`/`wsl-path`/`wsl-env` tools without declaring anything.
3
+ #
4
+ # Do NOT also add a `tool-wsl` row to a preset. DSH registers tools by name and
5
+ # the second registration fails with
6
+ # `tool "wsl" is already registered in this scope`. To keep the tools out of a
7
+ # composition, override this row by id in a later patch layer (`- id: tool-wsl`
8
+ # with `disabled: true`) instead of duplicating it.
3
9
 
4
10
  # The entry name is PATH-RELATIVE on purpose. DSH anchors a relative `name:` to
5
11
  # the directory of the patch file that declared it (`anchorInsertedPluginNames`),
package/lib/tools/wsl.js CHANGED
@@ -71,7 +71,15 @@ export function createWslTool({ ctx, config, runner }) {
71
71
  kind: 'wsl',
72
72
  label: jobLabel(command),
73
73
  outputLimitBytes: config.maxOutputBytes,
74
- ...(exec?.agent === undefined ? {} : { owner: exec.agent }),
74
+ // `owner` is the owner's SESSION ID, not the Agent handle: the registry
75
+ // resolves it through the live-agent registry (`agents.get(sessionId)`)
76
+ // and fails with `session "…" has no live agent` for anything else. It
77
+ // is also the fence key — the model's `job_output`/`job_kill` reach this
78
+ // job by passing the same session id as their caller. Bound to
79
+ // `exec.agent.id` exactly as `dsh-tool-bash` and `dsh-tool-pwsh` do.
80
+ // An execution with no agent starts an unowned job, which is open to any
81
+ // caller but never listed for a session.
82
+ ...(exec?.agent === undefined ? {} : { owner: exec.agent.id }),
75
83
  // The spawn happens INSIDE run(): the contract says a throw leaves
76
84
  // nothing registered, so spawning first and registering second could
77
85
  // leak a process if registration failed.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-wsl-tool",
3
- "version": "1.8.0",
4
- "description": "Model-facing WSL tools for DeepSeek Harness: run Linux commands from Windows with background jobs, stdin, path translation, destructive-command guard and WSL capability diagnostics.",
3
+ "version": "1.8.2",
4
+ "description": "Run Linux commands from Windows through WSL, essentially matching a native Linux DSH for command execution: background jobs, stdin, path translation, a destructive-command guard and a WSL capability report. WSL calls run below the DSH sandbox, and a project on a Windows drive keeps Windows filesystem semantics.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "license": "MIT",
@@ -29,6 +29,7 @@
29
29
  "cordis.patch.yml",
30
30
  "README.md",
31
31
  "README.zh-CN.md",
32
+ "PUBLISHING.md",
32
33
  "screenshots.json",
33
34
  "assets",
34
35
  "LICENSE"