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 +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/cordis.patch.yml +3 -2
- package/lib/bash-git/index.js +75 -24
- package/package.json +1 -1
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:
|
|
8
|
-
README.zh.md:
|
|
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`,
|
|
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
|
|
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` →
|
|
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
|
|
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 (
|
|
46
|
-
#
|
|
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:
|
package/lib/bash-git/index.js
CHANGED
|
@@ -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
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
106
|
-
join(
|
|
107
|
-
|
|
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
|
-
|
|
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()
|
|
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 };
|