dsh-win-bash
English | 中文
Windows 上用 Git Bash 执行命令的 DeepSeek Harness 插件。
它做什么
DSH 出厂组合按平台开关两套 shell:Windows 上 host 组的是
@deepseek-ai/dsh-pwsh-sandbox(PowerShell),各 agent preset 挂
@deepseek-ai/dsh-tool-pwsh;bash 那一套默认禁用——因为 dsh-bash-local
直接 spawn PATH 上的 bash,而 Windows 上 PATH 里的 bash.exe 是
C:\Windows\system32\bash.exe,即 WSL 启动器,不是 Git Bash。WSL 是另一套
文件系统视图、不认 C:\ 风格路径,不能当 Windows shell 直接替。
本插件补上缺的那块:
- 一个
ctx.shellprovider,spawn 明确解析出来的 Git Bash
(\bin\bash.exe -c),并完整继承上游机制:托管进程组、
超时/中止分类、有界输出与落盘、模型友好环境(NO_COLOR、TERM=dumb、
PAGER=cat)、后台任务、文件沙箱; - 出厂自带的模型侧
bash工具,接到这个 executor 上。
它是旁挂在 pwsh 栈旁边,而不是替换:loader 行声明
isolate: { shell: 'win-bash' },把 executor 和工具放进一个私有服务领域。
host 的 ctx.shell(pwsh)既不被替换也不会重复注册,preset 的 pwsh
工具原样不动。
安装
dsh plugin --profile desktop add dsh-win-bash
从本地目录装(开发/未发布版本)就用 link: 指向那个目录:
dsh plugin --profile desktop add link:/path/to/dsh-win-bash
该命令写入 profile 依赖,并把 dsh-win-bash 追加进 dsh.profile.bundles;
loader 行由插件自己的 cordis.patch.yml 提供。宿主在下次启动时加载新
bundle(应用运行中时,profile reconcile 会热加载该行)。
换个 profile 也用同样命令、换个 profile 名即可。
配置
所有开关都在 win-bash 这一行上(在 profile 的 cordis.patch.yml 里用
- id: win-bash / config: {...} 覆盖——patch 会整份替换该行 config):
| 键 | 默认值 | 含义 |
|---|---|---|
bashPath |
'' |
bash.exe 绝对路径;留空 = 自动探测 |
promptHint |
true |
是否加一条「优先用 bash」的系统提示段 |
sandboxFallback |
auto |
宿主上「已证实的受限 spawn 失败」意味着什么——见下文 |
timeoutMs |
120000 |
前台默认超时(上游默认值) |
maxTimeoutMs |
600000 |
单次超时覆盖的上限 |
maxOutputBytes |
64000 |
单流内存上限,超出落盘 |
maxSpillBytes |
67108864 |
单流完整输出落盘上限 |
graceMs |
3000 |
kill 升级与退出后管道 drain 宽限期 |
cwd |
(不设) | 默认工作目录;单次调用以会话工作区为准 |
今天的受限 Git Bash 起不来(sandboxFallback)
Windows 上上游的 ACL runner(@deepseek-ai/dsh-sandbox-windows-acl)在
WRITE_RESTRICTED 受限令牌下起受限子进程,而 Git Bash 在这个令牌下无法完成
初始化。已知两条路通向同一个死法——STATUS_DLL_INIT_FAILED、退出码
0xC0000142,工具层表现为 (no output) [exit code: 3221225794]:
- 宿主没有控制台。 runner 起控制台子进程靠继承宿主控制台(它自己的
文档:「Console isolation is unavailable」,CREATE_NO_WINDOW/
CREATE_NEW_CONSOLE变体在受限令牌下会死)。从 Explorer/开始菜单双击启动的
DSH 桌面版是 GUI 进程,整棵进程树没有控制台,控制台子进程在 DLL init
阶段死亡。真实桌面版安装方的报告里,出厂的pwsh工具同样这样静默死掉。 - msys 共享对象(所有宿主都中招)。 Git Bash 背后的 msys 运行时在初始化时
要创建\BaseNamedObjects\msys-*,受限令牌拒掉这个创建(0xC0000022),
bash 带着一坨 fatal error 栈信息死在 stderr 上。妾身用真发货 runner 在
开发机上复现了:同样的 spawn,cmd.exe和powershell.exe都活着(有无
控制台都一样)——只有 bash 死。从终端启动 DSH 躲不过这条路。
同一条命令放到 danger-full-access 下立刻正常,因为它压根不进沙箱链路。所以
在 Windows 上,下面的降级不是权宜之计——它就是在「带文件沙箱的会话」里让
bash 工具能用的那个配置。
sandboxFallback 决定这份「已证实的初始化死亡」怎么处理:
| 值 | 行为 |
|---|---|
fail |
抛出带根因和出路明确的显式错误;绝不非受限运行 |
unconfined |
压根不调沙箱:每条命令直接跑 Git Bash,结果标 enforcement: 'none'。不弹授权——这是操作员的站立授权,给没有审批通道的宿主(CI、headless)用 |
auto(默认) |
行激活时跑一次只读受限探针:拿到 0xC0000142 阳性签名后,每条受限调用(含第一条)都以沙箱拒绝应答,命令不执行。出厂的 bash 工具随即渲染 [sandbox: file access denied under mode] 和升级提示;模型带 sandbox_permissions + 一句理由重试同一条命令,由 harness 的审批通道问用户。只有「允许本次」真正执行——以批准的更宽模式非受限跑。探针判不明时,第一条真实受限调用拿到阳性签名后锁存同样的拒绝行为。 |
auto 永远不会绕过能用的沙箱:只有拿到 0xC0000142 阳性签名才走这条路——
真跑起来的受限子进程产生不了这个签名(cmd 和 pwsh 在同样的 spawn 下活着;某条
命令在非受限下也这样崩的话,问用户只是同一个结果的诊断,不是误报)。终端里跑的
dsh / dsh web 保留上游能用的部分——但按上面第 2 条,受限 Git Bash 在
Windows 上跟启动方式无关地坏,所以 auto 在那儿照样拒绝,这是正当行为。
auto 下用户看到什么: 第一条受限 bash 调用返回 (no output),带拒绝标记和
提示——用 sandbox_permissions(最窄的够用的更宽模式)加一句理由重试同一条命令,
「授权框会问用户」。重试拉起一个审批请求(允许本次操作使用 权限:…);
批准后命令非受限执行,结果的 sandbox 事实是
{ mode: 'danger-full-access', denied: false }——诚实的标记,没有 bypass 戳。
拒绝则抛错:「the user rejected escalating this command … it stays denied, so
stop and explain instead of working around it」。宿主日志里有一条一次性
win-bash: … 警告写明根因。探针在行激活时已证死的话,系统提示段也会写明,模型
就直接要 danger-full-access,不用一格一格爬梯子。
上游(不归本插件): 第 1 条要 runner 在宿主无控制台时改用
DETACHED_PROCESS(或先判 GetConsoleWindow() === NULL 再选 flags)——harness
仓库目前没有这个分支。第 2 条 runner 压根修不了:WRITE_RESTRICTED 令牌没法给
msys 运行时在 \BaseNamedObjects 里开对象创建的口子,所以 Windows 上的受限
Git Bash 要么用本插件的降级,要么等上游换沙箱机制。
Git Bash 探测顺序
- 行配置里的
bashPath; - 环境变量
DSH_GIT_BASH_PATH; - 注册表(
HKLM/HKCU/WOW6432Node 的SOFTWARE\GitForWindows→
InstallPath)——F:\ssdpath\Git\2331这种带版本号的安装就是这样找到的; - 常见安装位置(
%ProgramFiles%\Git、%ProgramFiles(x86)%\Git、
%LOCALAPPDATA%\Programs\Git、Chocolatey、scoop、MSYS2); PATH按序找,跳过C:\Windows\System32下的 WSL 启动器。
显式的 bashPath(或环境变量)排在最前且不受 WSL 过滤限制,所以确实想用
WSL bash 的人可以显式指过去;但它仍然必须存在——显式路径不存在时会继续往下
探测(与 dsh-pwsh-local 一致)。自动探测永远不会选 WSL 启动器——找不到就
报一个带修复指引的错,不猜。
验证
会话里让模型(或你)执行:
bash: echo "$BASH_VERSION"; uname -s
输出 MINGW64_NT-* 就是 Git Bash;若是 Linux 说明跑到了 WSL。插件页里
这一行显示为 win-bash(dsh-win-bash),宿主启动日志会打印
win-bash: using Git Bash at <路径>。
回滚
dsh plugin --profile desktop remove dsh-win-bash
或禁用该行(plugin_manager → win-bash,或在 profile patch 里
disabled: true)。两种方式都让宿主精确回到出厂状态:pwsh executor、
preset 的 pwsh 工具、没有 bash 工具。组合里别的东西一律不动——插件从不
在自己的领域之外写任何东西。
已知限制
- 仅 Windows。 行上带了
process.platform !== 'win32'门;POSIX 上出厂
bash 栈本来就在跑,再挂一个bash工具会重名。 pwsh仍然可用。 要删它就得整份重述某个 agent preset 的插件列表,而那
份快照会在每次 DSH 升级后悄悄过期(其中一个包改名就会让该 preset 挂载
失败、影响所有会话)。promptHint提示段负责把模型引向bash;绝不
该看到 pwsh 的会话,用一个不含它的 preset 即可。- Git Bash 的路径语义就是 MSYS 语义。
/f/...风格路径,带反斜杠的
Windows 路径可能需要加引号——这是 Git Bash 的常态,和任何 Git Bash 用户
面对的模型侧界面相同。 - 受限沙箱路径是继承而非重写。
workspace-write/read-only模式下由
上游的 windows-acl runner 包裹我们的 argv,变的只是被包裹的可执行文件。 - 今天的 Windows 宿主没有一个能沙箱 Git Bash。 两条独立的路都在命令跑起来之前
杀死受限子进程:宿主没有控制台(runner 自己的文档局限——从终端启动 DSH 只能躲过
这一条),以及受限令牌下 msys 运行时建不了\BaseNamedObjects\msys-*对象——
这条跟 DSH 怎么启动无关,台台都中。两者都是 ACL runner 的上游局限,不是换个
shell 能绕的——第一条路上pwsh死得一模一样。sandboxFallback: auto下,插件
对这样的调用以拒绝应答,让用户去授权更宽的模式。 - 受限 Git Bash 在每一台 Windows 宿主上都坏,不止桌面版。 runner 的受限令牌下
msys 运行时建不了\BaseNamedObjects\msys-*共享对象(0xC0000022→ bash 死
0xC0000142);妾身用真发货 runner 复现过,同样的 spawn 下 cmd、pwsh 都活。
所以sandboxFallback是 Windows 上的工作配置——auto之下,它让每一个这样
的调用都经过用户授权,而不是悄悄非受限跑掉。