dsh-wsl-workspace 0.7.2 → 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 +74 -0
- package/README.zh.md +16 -0
- package/TESTING.md +66 -15
- package/lib/client.js +4 -4
- package/lib/client.js.map +1 -1
- package/lib/fs.js +2 -2
- package/lib/index.js +127 -20
- package/lib/index.js.map +1 -1
- package/lib/{links-BQxdJOMy.js → links-DQ8GgpMj.js} +19 -19
- package/lib/links-DQ8GgpMj.js.map +1 -0
- package/lib/shell.js +2 -2
- package/lib/{wsl-CECXiGZH.js → wsl-BTA7XT8X.js} +72 -12
- package/lib/wsl-BTA7XT8X.js.map +1 -0
- package/lib/{wsl-credentials-BJXc9tfi.js → wsl-credentials-gFHTqaiN.js} +2 -2
- package/lib/{wsl-credentials-BJXc9tfi.js.map → wsl-credentials-gFHTqaiN.js.map} +1 -1
- package/lib/wsl-relay.js +1 -1
- package/lib/wsl-search.js +2 -2
- package/package.json +10 -4
- package/src/client/locales.ts +4 -4
- package/src/index.ts +222 -25
- package/src/shared/links.ts +20 -11
- package/src/shared/wsl.ts +99 -15
- package/lib/links-BQxdJOMy.js.map +0 -1
- package/lib/wsl-CECXiGZH.js.map +0 -1
package/README.md
CHANGED
|
@@ -43,6 +43,80 @@ 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
|
+
|
|
84
|
+
### 0.7.3 — 2026-09-23
|
|
85
|
+
|
|
86
|
+
- **The plugin loads on DSH `0.1.7-rc.1` again, and its modes come back.** The
|
|
87
|
+
`0.1.7` line renamed the host preset face — `read()` became `readDocument()`, which
|
|
88
|
+
returns a document (`{agentPreset, content, name, description}`) instead of the
|
|
89
|
+
composition text, and `AgentPreset` lost its `path` — so the variant generator
|
|
90
|
+
threw `agentPresets.read is not a function` on every boot and no `wsl-*` mode
|
|
91
|
+
ever reached the picker. The roster face is now probed by capability and both
|
|
92
|
+
generations are served: `readDocument()` where it exists, `read()` behind it.
|
|
93
|
+
- **A variant is a declaration row on that line.** `0.1.7` stopped scanning
|
|
94
|
+
`$DSH_HOME/.agent-presets/` — a preset there is a declarative
|
|
95
|
+
`@deepseek-ai/dsh-agent-preset` row, and the directory a variant used to be
|
|
96
|
+
written to is read by nothing at all. The generator now expands the composed
|
|
97
|
+
variant back into an entry list and publishes it through
|
|
98
|
+
`ctx.agentPresets.register()`; the plugin's effect owns the returned disposers, so
|
|
99
|
+
an unload or hot reload retires the variants instead of leaving orphans the next
|
|
100
|
+
apply could not replace (`Duplicate agent preset: wsl-<mode>`). Earlier releases
|
|
101
|
+
keep the directory channel, unchanged.
|
|
102
|
+
- **The world's own providers are named as `file:` URLs on that channel.** A preset
|
|
103
|
+
mounted from a declaration is imported by the registry's entry tree, which —
|
|
104
|
+
unlike the boot-time Include — does not turn an absolute path into a `file:` URL.
|
|
105
|
+
Without the rewrite the provider rows never started, the audit reported each as
|
|
106
|
+
`never started`, and the whole variant was refused as unusable.
|
|
107
|
+
- **The PTC variant is named again on `0.1.7`.** The label table that gives the
|
|
108
|
+
shipped modes their bilingual `WSL · …` names carried the mode's older id
|
|
109
|
+
(`code`) but not `ptc`, the id it has used since `0.1.1`. That stayed invisible
|
|
110
|
+
as long as the release published its own display name, because the lookup fell
|
|
111
|
+
through to it — but `0.1.7` publishes none, so the mode reached the picker as
|
|
112
|
+
`WSL · ptc` with the generic `WSL execution world for ptc: …` description. Both
|
|
113
|
+
ids are covered now, and the variant reads `WSL · PTC mode(PTC 模式)` on every
|
|
114
|
+
channel, like the other three shipped modes.
|
|
115
|
+
- `dsh.compatibility.dshReleases` declares `0.1.7-rc.1`. The two modules the
|
|
116
|
+
declaration channel needs (`@deepseek-ai/cordis-plugin-include`, `js-yaml`) are
|
|
117
|
+
resolved at call time and declared as **optional** peers, so a release that lacks
|
|
118
|
+
them fails one variant rather than refusing to load the plugin.
|
|
119
|
+
|
|
46
120
|
### 0.7.2 — 2026-09-21
|
|
47
121
|
|
|
48
122
|
- **The WSL skill catalog is no longer re-walked on the request path (issue #25).**
|
package/README.zh.md
CHANGED
|
@@ -44,6 +44,22 @@ 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
|
+
|
|
55
|
+
### 0.7.3 — 2026-09-23
|
|
56
|
+
|
|
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()`。
|
|
58
|
+
- **在这条线上,变体是一条声明行**。`0.1.7` 不再扫描 `$DSH_HOME/.agent-presets/`——那里的预设是一条声明式的 `@deepseek-ai/dsh-agent-preset` 行,而插件原先写变体的那个目录已经**没有任何代码会读**。生成器现在把组合好的变体展开回 entry list,通过 `ctx.agentPresets.register()` 发布;注销器由插件的 effect 持有,因此卸载或热重载会注销这些变体,而不是留下下一次 apply 无法替换的孤儿(`Duplicate agent preset: wsl-<mode>`)。更早的版本仍走原来的目录通道,行为不变。
|
|
59
|
+
- **该通道下,世界自己的提供者要以 `file:` URL 命名**。由声明挂载的预设由注册表自己的 entry tree 导入,而它——与启动期的 Include 不同——不会把绝对路径翻译成 `file:` URL。没有这层改写时那些提供者行根本不会启动,审计会逐条报 `never started`,整个变体被判为不可用而拒绝。
|
|
60
|
+
- **`0.1.7` 上 PTC 变体重新有名字了**。给已发布模式拼双语 `WSL · …` 名字的那张表里,PTC 这个模式只登记了旧 id(`code`),没有 `0.1.1` 起在用的 `ptc`。只要版本自己发布了显示名就看不出来——查不到会落到那个名字上——但 `0.1.7` 根本不发布显示名,于是模式以 `WSL · ptc` 和通用描述 `WSL execution world for ptc: …` 进了选择器。现在两个 id 都登记了,变体在各条通道上都显示为 `WSL · PTC mode(PTC 模式)`,与其余三个已发布模式一致。
|
|
61
|
+
- `dsh.compatibility.dshReleases` 增加 `0.1.7-rc.1`。声明通道需要的两个模块(`@deepseek-ai/cordis-plugin-include`、`js-yaml`)在调用时动态解析,并声明为**可选**同伴依赖:万一某个版本没有它们,只让一个变体失败,而不是让整个插件无法加载。
|
|
62
|
+
|
|
47
63
|
### 0.7.2 — 2026-09-21
|
|
48
64
|
|
|
49
65
|
- **WSL 技能目录不再在请求路径上重走(issue #25)**。宿主会在请求期间重建技能目录并等待每个 provider 的 `list()`,而本插件只把答案保留 10 秒——于是每次重新收集(新会话、新作用域,或距上次查询超过 10 秒)都会让那个请求付一次整棵工作区的走查,而且是逐个目录:每目录两次 `stat` 加一次 `readdir`。本机 `\\wsl.localhost\…` 9P 共享的单次 `readdir` 实测 3~16 毫秒,走查预算固定 4096 个目录,所以大工作区那次走查是 20.4 秒、并且付在请求路径上。现在已发布的目录直接照原样返回,只有插件自己的变更探针能丢弃它:重复查找 1~3 毫秒、完全不碰文件系统。新鲜度契约不变——新增的嵌套技能目录仍在 30 秒内出现,新增、删除或改写技能仍在 3 秒内生效。
|
package/TESTING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Testing
|
|
2
2
|
|
|
3
|
-
This document describes how to verify `dsh-wsl-workspace` after a change or before a release. The suite covers unit tests, the preset-
|
|
3
|
+
This document describes how to verify `dsh-wsl-workspace` after a change or before a release. The suite covers unit tests, the two preset-channel integration tests (the retired directory generation and the declaration generation), a real-WSL smoke test, and a post-build lib verification gate.
|
|
4
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
@@ -39,7 +39,19 @@ Boots the host plugin's `apply()` against a fake context with `DSH_HOME` pointed
|
|
|
39
39
|
node tests/host-materialize.mjs
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
This covers variant generation, opaque source-directory mirroring (third-party assets travel with the variant), atomic publication (a failed regeneration preserves the previous complete variant), stale-variant cleanup, and legacy `wsl` preset removal.
|
|
42
|
+
This covers variant generation, opaque source-directory mirroring (third-party assets travel with the variant), atomic publication (a failed regeneration preserves the previous complete variant), stale-variant cleanup, and legacy `wsl` preset removal. Its fake roster face is the **directory generation** (`list()` reporting a `path`, `read()` returning text), i.e. the channel every release up to `0.1.5-rc.2` uses.
|
|
43
|
+
|
|
44
|
+
## Preset declaration integration test
|
|
45
|
+
|
|
46
|
+
Boots the same `apply()` against the **declaration generation** of the roster face — `list()` reporting metadata without a directory, `readDocument()` returning a composition document, `register()` publishing a declaration — which is the channel `0.1.7-alpha.1` and later use, because that line stopped scanning `$DSH_HOME/.agent-presets/`:
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
node tests/host-declare.mjs
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This covers the capability switch, one declaration per healthy source (broken sources and existing `wsl-*` presets skipped), display name/description/order taken from the roster face rather than a `preset.yml`, the declaration's row list being an importable entry list (the world group, its isolating realm, the `!!js` disabled expression that must round-trip as an expression node, config values such as the relay and interpreter paths that must *not* become `file:` URLs), the world's own providers named as `file:` URLs pointing at real built files, that the retired root is neither written nor left holding stale leftovers, and that disposing the plugin retires every declaration it published.
|
|
53
|
+
|
|
54
|
+
Run both before a release: the two files are the two halves of the same generator, and a change to either channel must not silently break the other.
|
|
43
55
|
|
|
44
56
|
## Real-WSL smoke test
|
|
45
57
|
|
|
@@ -87,7 +99,7 @@ The WSL skill provider publishes `.dsh/skills` / `.agents/skills` from nested pr
|
|
|
87
99
|
node scripts/repro-e2e.mjs
|
|
88
100
|
```
|
|
89
101
|
|
|
90
|
-
The script hardcodes `\\wsl.localhost
|
|
102
|
+
The script hardcodes `\\wsl.localhost\<distro>\home\<user>\repro-ws-root`; adjust the two paths at the top when running as another user or distro.
|
|
91
103
|
3. In the running harness, open a session on the repro workspace and ask the agent to load the nested skills (`brainstorming`, `systematic-debugging`, `writing-plans`) through its skill tool — each must load with the `wsl-workspace` provider attribution, and no duplicate entries may appear. In a non-WSL workspace session the same skills must be "unknown".
|
|
92
104
|
4. Clean-install check (simulates another user): `npm pack`, `npm install <tarball>` in an empty temp project (peers must resolve), then `dsh plugin --profile web add <extracted tarball dir>`, restart `dsh web`, and repeat the end-to-end checks below plus the nested-skill probe above.
|
|
93
105
|
|
|
@@ -96,24 +108,58 @@ The WSL skill provider publishes `.dsh/skills` / `.agents/skills` from nested pr
|
|
|
96
108
|
After installing the plugin into a profile and restarting `dsh web`:
|
|
97
109
|
|
|
98
110
|
1. The **W** button appears beside Settings at the sidebar foot.
|
|
99
|
-
2. Open "Add WSL workspace…"
|
|
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.
|
|
100
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`).
|
|
101
119
|
4. `read`/`write`/`edit` operate on WSL files; Windows files stay reachable under `/mnt/<drive>`.
|
|
102
120
|
5. Switch modes (Standard / PTC / Minimal / Creative) — each lands on its WSL variant and the tool catalog matches the mode.
|
|
103
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`.
|
|
104
147
|
|
|
105
148
|
## Release checklist
|
|
106
149
|
|
|
107
150
|
1. `pnpm build` — clears `lib/`, rebuilds it, and runs the verification gate.
|
|
108
151
|
2. `node --experimental-strip-types --test tests/*.test.ts` — all green (locales, variants, paths, shell, fs execution context, fs policy, wsl skills, wsl search).
|
|
109
|
-
3. `node tests/host-materialize.mjs` — all assertions pass.
|
|
110
|
-
4. `node
|
|
111
|
-
5. `node
|
|
112
|
-
6. `node scripts/
|
|
113
|
-
7. `
|
|
114
|
-
8. `npm run
|
|
115
|
-
9.
|
|
116
|
-
10.
|
|
152
|
+
3. `node tests/host-materialize.mjs` — all assertions pass (the directory channel).
|
|
153
|
+
4. `node tests/host-declare.mjs` — all assertions pass (the declaration channel).
|
|
154
|
+
5. `node --experimental-strip-types tests/smoke.ts` — real-WSL round-trip passes.
|
|
155
|
+
6. `node scripts/check-rank-parity.mjs` — host rank constants still match our copies.
|
|
156
|
+
7. `node scripts/repro-e2e.mjs` (after `scripts/repro-setup.sh`) — nested skill-catalog assertions pass.
|
|
157
|
+
8. `npm pack --dry-run` — confirm the tarball carries only live `lib/` chunks, `src/`, `cordis.patch.yml`, READMEs, `LICENSE`, and `NOTICE`.
|
|
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.
|
|
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.
|
|
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`.
|
|
117
163
|
|
|
118
164
|
### The multi-release check harness
|
|
119
165
|
|
|
@@ -130,6 +176,11 @@ node .test-runs/harness.mjs <runId> --checks 0.1.5-rc.2 # re-check an exist
|
|
|
130
176
|
Four checks need a live WSL distribution (`skills-real`, `fs-real`, `relay-real`,
|
|
131
177
|
`search-real`); they build their own fixtures under `/tmp/dsh-wsl-compat` (override
|
|
132
178
|
with `WSL_COMPAT_ROOT`, and the distribution with `WSL_COMPAT_DISTRO`) and remove
|
|
133
|
-
them again. `
|
|
134
|
-
|
|
135
|
-
|
|
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.
|
|
1140
|
-
"help.news.body": "
|
|
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.
|
|
1184
|
-
"help.news.body": "
|
|
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",
|