dsh-win-multi-bash 0.1.0 → 0.1.1

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.i18n.yaml CHANGED
@@ -4,5 +4,5 @@
4
4
  # along and re-record with the harness docs tooling (not shipped with this
5
5
  # package):
6
6
  # node scripts/verify-docs.mjs --write <dir> # run from the deepseek-harness checkout
7
- README.md: 2511078d6aae9388bfcf5047ef64746796717d80
8
- README.zh.md: 976e708f2c1451006117826ca136ed46529d3f30
7
+ README.md: ec808f83664fbe3c6096c9268cf027809ee69f52
8
+ README.zh.md: 83e60cea590d3763f89c80bb3d955895513e9d0d
package/README.md CHANGED
@@ -14,7 +14,7 @@ A Windows multi-bash plugin for DeepSeek Harness: `git_bash` / `wsl_bash` model
14
14
 
15
15
  - `shell-select` occupies the single `ctx.shell` seat and routes `request.shell ?? default` to one backend; `default` stays `pwsh`.
16
16
  - Executable resolution and sandbox probing are lazy: a host without Git Bash or WSL does not affect pwsh; failures are loud at first use.
17
- - Git Bash is found automatically, in order: an explicit `gitBash.bashPath`, the well-known Program Files locations, `bash.exe` on PATH (the Windows WSL launcher `System32\bash.exe` is **never** selected — this tool is MSYS, not WSL), Git install roots inferred from `git.exe` layout directories on PATH (so an install reachable through `git` is found without a pin), and finally the `HKLM\SOFTWARE\GitForWindows` install path (which the Git for Windows installer always records, covering custom-drive and portable installs).
17
+ - Git Bash is found automatically, in order: an explicit `gitBash.bashPath`, Git install roots inferred from `git.exe` layout directories on PATH (so an install reachable through `git` is found without a pin, even outside the well-known locations), the well-known Program Files layout on every fixed drive (`C:\Program Files\Git`, `D:\Program Files\Git`, ...), `bash.exe` on PATH, and finally the `HKLM\SOFTWARE\GitForWindows` install path (which the Git for Windows installer always records, covering portable installs). The Windows WSL launcher `System32\bash.exe` and `WindowsApps` app-execution alias directories are **never** selected, and candidates must be real regular files — symlinks/reparse points are rejected — so a stale WSL `bash.exe` alias can never shadow a real Git Bash (this tool is MSYS, not WSL).
18
18
  - Sandbox `auto`: Git Bash probes the windows-acl runner, WSL probes `bwrap` inside the distro; a failed probe degrades honestly to an unconfined run with no sandbox facts. An explicit `sandbox: bwrap` with bubblewrap missing fails loudly at the first `wsl_bash` command (never at boot), leaving the other backends untouched.
19
19
  - All rows register host-plane: every session sees the tools regardless of its agent preset.
20
20
 
@@ -140,7 +140,7 @@ Boots a real composition over the profile runtime (modifying nothing), verifies
140
140
  | New sessions lack `git_bash` / `wsl_bash` | Check the managed block exists in the profile patch, the profile `node_modules/dsh-win-multi-bash` junction exists, and the package-local `node_modules/@deepseek-ai` junction exists (re-run install.ps1); confirm the running `dsh web` hot-reloads the profile patch |
141
141
  | Boot fails with `duplicate loader entry id` | Both plug paths are active; remove one of them |
142
142
  | Boot fails with `Cannot find package '@deepseek-ai/...'` | The package-local `node_modules/@deepseek-ai` junction is missing (re-run install.ps1), or the profile runtime lacks the base packages |
143
- | `git_bash` reports bash not found | Git Bash is outside the default probe paths: re-run install.ps1 (registry auto-detect) or set `gitBash.bashPath` manually |
143
+ | `git_bash` reports bash not found (or spawns the WSL `WindowsApps\bash.exe` alias with `spawn ... ENOENT`) | Git Bash is outside the probe paths, or a stale WSL app-execution alias shadows resolution: delete `%LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe` (Settings → Apps → Advanced app settings → App execution aliases), re-run install.ps1 (registry auto-detect), or set `gitBash.bashPath` manually |
144
144
  | `wsl_bash` errors | Check `wsl.exe --status` for a default distro; set `wslBash.wslDistro` to name one |
145
145
  | `wsl_bash` fails with `bwrap was not found` | `sandbox: bwrap` is set but bubblewrap is missing inside the distro: install it per “Sandbox behavior → Enabling the bwrap sandbox for `wsl_bash`” (`sudo apt-get install -y bubblewrap`) and restart `dsh web`, or use `sandbox: auto` / `none` |
146
146
  | `wsl_bash` sandbox reports a runner failure on bwrap | The bwrap workspace root is the Linux side of a Windows drive path (`/mnt/<drive>/...`): a UNC workspace root fails loud, and a distro with a custom automount root (wsl.conf `automount.root`) needs a matching configuration |
package/README.zh.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  - `shell-select` 占据唯一的 `ctx.shell` 席位,按 `request.shell ?? default` 路由;`default` 保持 `pwsh`。
16
16
  - 可执行文件解析与沙箱探测全部惰性化:未安装 Git Bash / WSL 不影响 pwsh,首次使用时才响亮报错。
17
- - Git Bash 自动查找,顺序为:显式 `gitBash.bashPath` → 常见 Program Files 位置 → PATH 上的 `bash.exe`(**绝不选** Windows 的 WSL 启动器 `System32\bash.exe`——本工具是 MSYS 而非 WSL)→ 从 PATH 上 `git.exe` 布局目录反推的 Git 安装根(因此通过 `git` 可达的安装无需钉定即可找到)→ 最后读取 `HKLM\SOFTWARE\GitForWindows` 注册表安装路径(Git for Windows 安装器必写该键,覆盖自定义盘符与便携安装)。
17
+ - Git Bash 自动查找,顺序为:显式 `gitBash.bashPath` → 从 PATH 上 `git.exe` 布局目录反推的 Git 安装根(因此通过 `git` 可达的安装无需钉定即可找到,即使不在常见位置)→ 每个固定盘上的常见 Program Files 布局(`C:\Program Files\Git`、`D:\Program Files\Git` 等)→ PATH 上的 `bash.exe` → 最后读取 `HKLM\SOFTWARE\GitForWindows` 注册表安装路径(Git for Windows 安装器必写该键,覆盖便携安装)。Windows 的 WSL 启动器 `System32\bash.exe` 与 `WindowsApps` 应用执行别名目录**绝不入选**,且候选必须是真实普通文件——符号链接 / reparse point 一律拒绝——因此失效的 WSL `bash.exe` 别名永远无法遮蔽真实 Git Bash(本工具是 MSYS 而非 WSL)。
18
18
  - 沙箱 `auto`:Git Bash 探测 windows-acl runner,WSL 探测发行版内 `bwrap`;探测失败如实降级为无限制运行并如实报告。显式 `sandbox: bwrap` 而发行版缺少 bubblewrap 时,在首次执行 `wsl_bash` 命令时响亮报错(不会拖垮启动),其余后端不受影响。
19
19
  - 所有行在 host 平面注册:无论会话使用哪个 agent preset,都能看到这两个工具。
20
20
 
@@ -140,7 +140,7 @@ powershell -ExecutionPolicy Bypass -File .\smoke\run.ps1
140
140
  | 新会话看不到 `git_bash` / `wsl_bash` | 检查 profile patch 里 managed 块存在、profile `node_modules/dsh-win-multi-bash` junction 存在、包内 `node_modules/@deepseek-ai` junction 存在(重跑 install.ps1);确认运行中 `dsh web` 热重载生效 |
141
141
  | 引导失败 `duplicate loader entry id` | 两种插拔方式混用了;先卸载其中一种 |
142
142
  | 引导失败 `Cannot find package '@deepseek-ai/...'` | 包内 `node_modules/@deepseek-ai` junction 缺失(重跑 install.ps1),或 profile 运行时基础包不完整 |
143
- | `git_bash` 执行报找不到 bash | Git Bash 不在默认探测路径:重跑 install.ps1(注册表自动检测),或手动设置 `gitBash.bashPath` |
143
+ | `git_bash` 执行报找不到 bash(或去 spawn WSL 的 `WindowsApps\bash.exe` 别名并报 `spawn ... ENOENT`) | Git Bash 不在探测路径,或失效的 WSL 应用执行别名遮蔽了解析:删除 `%LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe`(设置 → 应用 → 高级应用设置 → 应用执行别名),重跑 install.ps1(注册表自动检测),或手动设置 `gitBash.bashPath` |
144
144
  | `wsl_bash` 执行报错 | `wsl.exe --status` 是否有默认发行版;可在 `wslBash.wslDistro` 指定发行版名 |
145
145
  | `wsl_bash` 报 `bwrap was not found` | 已配置 `sandbox: bwrap` 但发行版内没有 bubblewrap:按上方「沙箱行为 → 为 `wsl_bash` 启用 bwrap 沙箱」安装(`sudo apt-get install -y bubblewrap`)并重启 `dsh web`,或改用 `sandbox: auto` / `none` |
146
146
  | `wsl_bash` 沙箱报 bwrap runner 失败 | bwrap 的工作区根取 Windows 盘符路径的 Linux 侧(`/mnt/<盘符>/...`):UNC 工作区根会响亮报错;发行版自定义了 automount 根(wsl.conf `automount.root`)时需要相应配置 |
package/cordis.patch.yml CHANGED
@@ -42,8 +42,9 @@
42
42
  # Optional per-machine executable pins (restate the whole config when
43
43
  # uncommenting — a patch replaces the row's entire config). The values
44
44
  # below are placeholders only — omit the pins entirely and let
45
- # resolution probe automatically (well-known locations → PATH, the WSL
46
- # launcher excluded, git.exe layout inference):
45
+ # resolution probe automatically (git.exe layout inference → well-known
46
+ # Program Files on every fixed drive → PATH, with the WSL launcher and
47
+ # WindowsApps aliases excluded):
47
48
  # gitBash:
48
49
  # bashPath: '<Git 安装目录>\usr\bin\bash.exe'
49
50
  # wslBash:
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { LocalBashExecutor } from "@deepseek-ai/dsh-bash-local";
4
4
  import { classifyDenial, classifyRunnerFailure, isRunnerSpawnFailure, matchesSignature } from "../vendor/helpers.js";
5
- import { lstatSync } from "node:fs";
5
+ import { existsSync, lstatSync } from "node:fs";
6
6
  import { basename, dirname, join } from "node:path";
7
7
  /**
8
8
  * Derive a config schema from a base object schema WITHOUT mutating it.
@@ -86,44 +86,95 @@ function gitRootCandidates(env = process.env) {
86
86
  return roots;
87
87
  }
88
88
  /**
89
- * Well-known Git for Windows install locations plus PATH entries. The Windows
90
- * WSL launcher (`System32\bash.exe`) is deliberately excluded: this executor is
91
- * MSYS Git Bash, and silently routing it into WSL would make a non-WSL tool
92
- * depend on WSL — resolution then either finds a real Git Bash or fails loud
93
- * with the `bashPath` hint. PATH entries shaped like Git layout directories
94
- * with a real `git.exe` additionally contribute their `<root>\usr\bin` and
95
- * `<root>\bin` bash candidates, so an install reachable through `git` on PATH
96
- * is found automatically.
89
+ * The well-known Git for Windows layout under one Program Files root
90
+ * (`<root>\Git\bin\bash.exe` and `<root>\Git\usr\bin\bash.exe`). Shared by the
91
+ * env `ProgramFiles` probes and the per-drive probes so an install on a
92
+ * non-C: drive (e.g. `D:\Program Files\Git`) is found without a pin.
93
+ * @param programFilesRoot - one Program Files root (e.g. `C:\Program Files`).
94
+ * @returns the two layout-shaped bash candidates under that root.
95
+ */
96
+ function gitCandidatesUnder(programFilesRoot) {
97
+ return [
98
+ join(programFilesRoot, "Git", "bin", "bash.exe"),
99
+ join(programFilesRoot, "Git", "usr", "bin", "bash.exe")
100
+ ];
101
+ }
102
+ /**
103
+ * The drive letters to probe for the well-known Git layout. An explicit
104
+ * `GitProbeDrives` env value (comma/space/`;`-separated letters) replaces the
105
+ * default of every existing fixed drive; the empty string disables drive
106
+ * probing entirely. Test fixtures use that override so resolution stays a pure
107
+ * function of the injected env.
108
+ * @param env - the environment to probe; defaults to the process environment.
109
+ * @returns existing drive roots in probe order (e.g. `C:\`, `D:\`).
110
+ */
111
+ function probeDrives(env = process.env) {
112
+ const override = env.GitProbeDrives;
113
+ const letters = override !== void 0 && override.trim().length > 0
114
+ ? override.split(/[,;\s]+/).map((s) => s.trim().replace(/:$/, "")).filter((s) => s.length > 0)
115
+ : override !== void 0 ? [] : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
116
+ const drives = [];
117
+ for (const letter of letters) {
118
+ const root = `${letter.charAt(0).toUpperCase()}:\\`;
119
+ if (existsSync(root)) drives.push(root);
120
+ }
121
+ return drives;
122
+ }
123
+ /**
124
+ * Git Bash candidate paths in resolution order: Git roots inferred from PATH
125
+ * `git.exe` layout dirs first (the strongest signal — a real install, however
126
+ * it got on PATH), then the well-known Program Files layout under every fixed
127
+ * drive (covers non-C: installs), then every PATH entry. The Windows WSL
128
+ * launcher (`System32\bash.exe`) and the `WindowsApps` alias directories are
129
+ * deliberately excluded, and candidates are only accepted when they are real
130
+ * regular files (symlinks/reparse points rejected), so a stale WSL
131
+ * app-execution alias can never shadow a real Git Bash. PATH entries shaped
132
+ * like Git layout directories with a real `git.exe` additionally contribute
133
+ * their `<root>\usr\bin` and `<root>\bin` bash candidates via the first step,
134
+ * so an install reachable through `git` on PATH is found automatically.
97
135
  * @param env - the environment to probe; defaults to the process environment.
98
136
  * @returns candidate `bash.exe` paths in resolution order.
99
137
  */
100
138
  function candidateBashPaths(env = process.env) {
101
- const programFiles = env.ProgramFiles ?? "C:\\Program Files";
102
- const programFilesX86 = env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
103
139
  const wslLauncher = join(env.SystemRoot ?? "C:\\Windows", "System32", "bash.exe").toLowerCase();
104
- const candidates = [
105
- join(programFiles, "Git", "bin", "bash.exe"),
106
- join(programFiles, "Git", "usr", "bin", "bash.exe"),
107
- join(programFilesX86, "Git", "bin", "bash.exe")
108
- ];
140
+ const candidates = [];
141
+ for (const root of gitRootCandidates(env)) {
142
+ for (const candidate of [join(root, "usr", "bin", "bash.exe"), join(root, "bin", "bash.exe")]) {
143
+ if (!candidates.includes(candidate)) candidates.push(candidate);
144
+ }
145
+ }
146
+ for (const pf of [env.ProgramFiles ?? "C:\\Program Files", env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)"]) {
147
+ for (const candidate of gitCandidatesUnder(pf)) {
148
+ if (!candidates.includes(candidate)) candidates.push(candidate);
149
+ }
150
+ }
151
+ for (const drive of probeDrives(env)) {
152
+ for (const candidate of gitCandidatesUnder(join(drive, "Program Files"))) {
153
+ if (!candidates.includes(candidate)) candidates.push(candidate);
154
+ }
155
+ }
109
156
  for (const entry of (env.PATH ?? "").split(";")) {
110
157
  const trimmed = entry.trim().replace(/^"|"$/g, "");
111
158
  if (trimmed.length === 0) continue;
112
159
  const candidate = join(trimmed, "bash.exe");
113
160
  if (candidate.toLowerCase() === wslLauncher) continue;
114
- candidates.push(candidate);
115
- }
116
- for (const root of gitRootCandidates(env)) {
117
- for (const candidate of [join(root, "usr", "bin", "bash.exe"), join(root, "bin", "bash.exe")]) {
118
- if (!candidates.includes(candidate)) candidates.push(candidate);
119
- }
161
+ if (basename(trimmed).toLowerCase() === "windowsapps") continue;
162
+ if (!candidates.includes(candidate)) candidates.push(candidate);
120
163
  }
121
164
  return candidates;
122
165
  }
166
+ /**
167
+ * Whether a candidate path is a real regular file. Symlinks and reparse
168
+ * points — the shape of broken Windows app-execution aliases such as a stale
169
+ * `WindowsApps\bash.exe` — are rejected, so a dead WSL alias never resolves as
170
+ * a Git Bash.
171
+ * @param candidate - the absolute candidate path.
172
+ * @returns true only for an existing non-link regular file.
173
+ */
123
174
  function candidateExists(candidate) {
124
175
  try {
125
176
  const stat = lstatSync(candidate);
126
- return stat.isFile() || stat.isSymbolicLink();
177
+ return stat.isFile();
127
178
  } catch {
128
179
  return false;
129
180
  }
@@ -379,4 +430,4 @@ var GitBashExecutor = class extends LocalBashExecutor {
379
430
  }
380
431
  };
381
432
  //#endregion
382
- export { GitBashExecutor, GitBashExecutor as default, candidateBashPaths, gitRootCandidates, gitToolPath, registryBashPaths, resolveBashPath };
433
+ export { GitBashExecutor, GitBashExecutor as default, candidateBashPaths, candidateExists, gitCandidatesUnder, gitRootCandidates, gitToolPath, probeDrives, registryBashPaths, resolveBashPath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-win-multi-bash",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "author": "Dinosaur_MC",
5
5
  "repository": {
6
6
  "type": "git",