🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

原生 Bash 执行器 SpookyWaste/dsh-bash-native

DeepSeek Harnessv0.3.1
SpookyWaste5 天前有提交已提供中文文档扫描:含敏感能力

Rust 实现的 Windows 原生 POSIX bash 执行器:给 DSH 提供一个真正原生的 bash不依赖…

36/100
综合分
9
GitHub 星标
446
npm 周下载
—/5
用户评分

数据截至 2026/10/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-bash-native
npm 源 · dsh-bash-native 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · security
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 5 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-bash-native @ 0.3.1
✓Node 引擎要求 >=22.12.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/28 02:53:04

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件
查看扫描证据(共 34 处,展示前 34 条)
  • 读取本地文件
    lib/index.js:33
    return statSync(path).isFile();
  • 读取本地文件
    lib/index.js:55
    return readdirSync(path, { withFileTypes: true })
  • 读取本地文件
    lib/index.js:213
    existing = readFileSync(file, 'utf8');
  • 写入本地文件
    lib/index.js:221
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    lib/index.js:222
    writeFileSync(file, contents);
  • 读取本地文件
    lib/index.js:305
    const digest = createHash('sha256').update(readFileSync(engine.path)).digest('hex');
  • 读取本地文件
    lib/artifact.js:221
    return statSync(path).isFile();
  • 读取本地文件
    lib/artifact.js:227
    readText: (path) => readFileSync(path, 'utf8'),
  • 读取本地文件
    lib/artifact.js:230
    const stats = statSync(path);
  • 读取本地文件
    lib/artifact.js:237
    sha256: (path) => createHash('sha256').update(readFileSync(path)).digest('hex'),
  • 写入本地文件
    lib/artifact.js:239
    mkdirSync(path, { recursive: true });
  • 写入本地文件
    lib/artifact.js:241
    writeText: (path, text) => writeFileSync(path, text),
  • 读取本地文件
    lib/shim.js:108
    const stats = statSync(path);
  • 写入本地文件
    lib/shim.js:116
    copy: (from, to) => copyFileSync(from, to),
  • 写入本地文件
    lib/shim.js:118
    mkdirSync(path, { recursive: true });
  • 读取本地文件
    lib/toolchain-artifact.js:284
    return statSync(path).isFile();
  • 读取本地文件
    lib/toolchain-artifact.js:290
    readText: (path) => readFileSync(path, 'utf8'),
  • 读取本地文件
    lib/toolchain-artifact.js:293
    const stats = statSync(path);
  • 读取本地文件
    lib/toolchain-artifact.js:300
    sha256: (path) => createHash('sha256').update(readFileSync(path)).digest('hex'),
  • 写入本地文件
    lib/toolchain-artifact.js:301
    copy: (from, to) => copyFileSync(from, to),
  • 写入本地文件
    lib/toolchain-artifact.js:304
    mkdirSync(path, { recursive: true });
  • 删除本地文件递归
    lib/toolchain-artifact.js:307
    rmSync(path, { recursive: true, force: true });
  • 写入本地文件
    lib/toolchain-artifact.js:309
    writeText: (path, text) => writeFileSync(path, text),
  • 读取本地文件
    lib/toolchain-artifact.js:312
    return readdirSync(path);
  • 执行外部命令来自配置/变量
    lib/verify.js:226
    result = spawnSync(path, [...args], { stdio: capture.stdio, timeout: 10_000, windowsHide: true });
  • 读取本地文件
    lib/verify.js:234
    const text = `${readFileSync(stdoutPath, 'utf8')}${readFileSync(stderrPath, 'utf8')}`.trim();
  • 删除本地文件递归
    lib/verify.js:245
    rmSync(directory, { recursive: true, force: true });
  • 读取本地文件
    lib/preset-source.js:115
    text = await readFile(path, 'utf8');
  • 读取本地文件
    lib/state.js:34
    return lstatSync(path).isDirectory() && !lstatSync(path).isSymbolicLink();
  • 读取本地文件
    lib/state.js:42
    return readdirSync(path, { withFileTypes: true }).map((entry) => ({
  • 读取本地文件
    lib/state.js:57
    const found = lstatSync(path);
  • 删除本地文件递归
    lib/state.js:68
    remove: (path) => rmSync(path, { recursive: true, force: true }),
  • 读取本地文件
    lib/state.js:92
    const lock = JSON.parse(readFileSync(join(packageRoot, 'engine.lock.json'), 'utf8'));
  • 读取本地文件
    lib/state.js:103
    const text = readFileSync(join(packageRoot, 'toolchain', 'win32-x64', 'manifest.json'), 'utf8');
扫描于 2026/11/2(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-bash-local@deepseek-ai/dsh-sandbox@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-jobs-local@deepseek-ai/dsh-sandbox-local@deepseek-ai/dsh-sandbox-policy@deepseek-ai/dsh-shell@deepseek-ai/dsh-shell-env@deepseek-ai/dsh-subprocess@deepseek-ai/dsh-subprocess-local@deepseek-ai/dsh-system-prompt
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

2 张 · 点击查看大图

功能亮点

  • 不依赖WSL或MSYS
  • 支持三档权限策略
  • 后台作业

Rust 实现的 Windows 原生 POSIX bash 执行器:给 DSH 提供一个真正的 bash(brush 引擎,不依赖 WSL 或 MSYS兼容层),支持 DSH 的三档权限策略、后台作业和一节模型可见的环境契约

README

由 DeepSeek 最新模型翻译生成
🐚 dsh-bash-native Rust 实现的 Windows 原生 POSIX bash 执行器 给 DSH 的 shell 提供一个真正原生的 bash —— rubash 引擎,不依赖 WSL,也不需要 MSYS 兼容层 适配三档权限策略 · 后台作业 · 支持持久化终端 Listed on dsh-plugin.org 中文 · English 🐟 为什么需要它 避免肥鱼被 cmd 咬,也规避了子系统和 MSYS 兼容层的性能开销 0.3.1:引擎由brush更换为rubash,修复多项已知缺陷,提供更好的原生 POSIX 兼容 方案命令在哪里执行每条命令的代价 Git for Windows MSYS2 兼容层上的移植 bash,POSIX 语义由运行时模拟兼容层每次起子 shell 或外部命令都要完整 fork 一个 MSYS2 进程(挂起线程、另起进程、经管道搬运内存),开销随子 shell 启动频率线性放大 WSL 命令在 Linux VM 的发行版里执行每条命令都要经 Windows/Linux 边界往返一次,延迟对高频短命令尤为不利 本插件rubash 引擎 + 工具链纯 Rust 实现的原生二进制,无模拟层、无 VM grep、sed、awk 等来自随包工具链,其余由引擎在进程内处理,没有兼容层或 VM 边界
展开完整文档(README 剩余 1 节)
🐚 dsh-bash-native

Rust 实现的 Windows 原生 POSIX bash 执行器

给 DSH 的 shell 提供一个真正原生的 bash —— rubash 引擎,不依赖 WSL,也不需要 MSYS 兼容层

适配三档权限策略 · 后台作业 · 支持持久化终端

Listed on dsh-plugin.org

中文 · English

🐟 为什么需要它

避免肥鱼被 cmd 咬,也规避了子系统和 MSYS 兼容层的性能开销

0.3.1:引擎由brush更换为rubash,修复多项已知缺陷,提供更好的原生 POSIX 兼容

方案命令在哪里执行每条命令的代价

Git for Windows
MSYS2 兼容层上的移植 bash,POSIX 语义由运行时模拟兼容层每次起子 shell 或外部命令都要完整 fork 一个 MSYS2 进程(挂起线程、另起进程、经管道搬运内存),开销随子 shell 启动频率线性放大

WSL
命令在 Linux VM 的发行版里执行每条命令都要经 Windows/Linux 边界往返一次,延迟对高频短命令尤为不利

本插件rubash 引擎 + 工具链纯 Rust 实现的原生二进制,无模拟层、无 VM grep、sed、awk 等来自随包工具链,其余由引擎在进程内处理,没有兼容层或 VM 边界

⚡ 实测:启动与子 shell 的代价

同一台机器(Windows 11 / x64)上的同一组脚本,每项取 5 次最小值,负载只用 shell 内建与一次外部进程,排除工具链差异。
复现:仓库里的 node scripts/bench-engines.mjs。

| 工作负载 | rubash(本插件) | Git Bash / MSYS2 |
| bash -c :(启动到退出) | 7 ms | 25 ms |
| echo hi | 6 ms | 26 ms |
| 算术循环 10k 次 | 65 ms | 75 ms |
| [ ] 判定 20k 次 | 167 ms | 181 ms |
| 命令替换 300 次 | 11 ms | 3052 ms |
| 子 shell 300 次 | 25 ms | 2497 ms |
| 外部进程 20 次(cmd /c exit) | 192 ms | 560 ms |

差距来自 fork:MSYS2 每次命令替换与子 shell 都要 fork 一个兼容层进程(约 8–10 ms),rubash 两样都做在进程内(每次约 0.04 ms),300 次就相差 2–3 秒。

🧭 路径不会被“自动替换”

Git Bash / MSYS2 把参数交给原生程序时会猜哪些像路径并改写(/flag → C:/Program Files/Git/flag,于是脚本和 agent 被迫记着 MSYS_NO_PATHCONV=1);连数据位也一起改:用 MSYS 调原生 awk.exe 跑 awk '/x/{print}',程序位到手是 X:/{print},直接语法错误、进错路径。
rubash 不猜:按子进程方言,整条 argv 要么原样交给带 POSIX 层的程序,要么统一翻成 Win32 拼写;随包工具的脚本、模式、程序位永不改写,极大减少了找不到路径的情况。

📦 安装

CLI 安装

以 web profile 为例

dsh plugin --profile web add dsh-bash-native

dsh 网页版 / 桌面版安装

dsh 插件页面 → 右上角 添加插件 → 填入插件名 dsh-bash-native 或仓库地址:

https://github.com/SpookyWaste/dsh-bash-native

重启后,在 Settings → General 里把 Native Bash (Windows) 设为默认 preset(或在会话里切过去)。

持久化终端组件默认关闭,在插件管理中可以打开。见持久终端组件。

平台 Windows x64

DSH
=0.2.0-rc.2 <0.3.0-0(0.2.0-rc.2 - 0.2.1-rc.3实测)

持久终端组件需要 DSH >=0.2.1-alpha.1才能开启

⚙️ 引擎

引擎是 rubash(unixwin/rubash,Rust 写的 bash 5.3 兼容 shell,MIT),本仓库从源码构建它、按 engine.lock.json 里的 commit 应用三笔补丁,产物 engine/win32-x64/rubash.exe(约 11 MB)随包提交。

兼容 MSYS 的 uname -s 身份(MSYS_NT-…),所以脚本里按 MSYS 分支的逻辑也能成立;并发、取消与作业语义由引擎自己实现,跟 MSYS 运行时无关。

🧩 补丁清单(点击展开)

补丁做了什么

0001-data-primary-positions-every-child
“数据位”的判定从 POSIX-aware 子进程推广到所有子进程:grep/sed/awk 的模式、脚本与程序文本,find 的模式主项,basename/dirname 的位置参数都原样交给程序;文件操作数照常翻成 Windows 形态。

0002-drop-undecodable-environment-entries
启动时丢掉 Win32 能传入、但 Unicode 表示不了的环境项(std::env::vars() 遇到它会 panic);代价是那一条变量,而不是每条命令都在建 shell 时 abort。

0003-file-commands-from-path
新增 RUBASH_FILE_COMMANDS_FROM_PATH:由 shell 指定哪些「进程内文件命令」改从 PATH 上的程序执行(all / none / 名字列表),并从 shell 自己的变量表读取,所以 --rcfile 也能设置;顺带让内联管道分支不再无视宿主的「关掉进程内副本」开关。

💡 每次解析都按 lock 里的 sha256 校验产物,并按盖章跳过重复校验:产物的 size 与 mtime 记进 %LOCALAPPDATA%\dsh-bash-native\verified\,没变就不再读那 11 MB(verifyArtifacts: 'always' 可恢复每次都哈希)。

🔧 实现

- 注册 ctx.shell:ShellExecutor 的 Service Provider,构建在 @deepseek-ai/dsh-bash-local 之上,每条命令交给解析到的 rubash 引擎;引擎与工具链都随包分发,并在解析时按 lock 与 manifest 校验。
- 服从 DSH 的三档文件策略(read-only / workspace-write / danger-full-access):受限档位把引擎 argv 交给 ctx.sandbox,不限档位直接 spawn 并如实报告档位。
- 后台作业经 ctx.jobs 汇报,超出内存上限的输出落到 spill 文件、路径随结果返回;echo、cat、sort 等空 PATH 也能跑,因为随包工具链先于宿主 PATH。
- Web 面用自带的 agent preset 挂载,agent 面用 overlay 显式启用;它不替换宿主层的 ctx.shell,因此其他 preset 的 pwsh 工具不受影响。

🎛 两个预设0.2.0:静态挂载改为跟随官方预设替换

cordis.patch.yml 只有一行注册器(dsh-bash-native/presets),两个 preset 由插件在运行时注册:启动时读本机安装的 presets/{standard,minimal}.patch.yml,把 shipped 的 shell 面换成插件自己的 realm group;它们各自带一个 isolate: { shell: true, terminals: true } 的 realm,宿主层与其他 preset(包括用 pwsh 的那些)不受影响。

预设挂载什么什么时候选

Native Bash (Windows)id bash-native,order 5 读本机 harness 那条线的 standard,只换 shell 面:每次调用都是全新的 bash,文件、作业、委派等全套工具跟着那一版上游走日常使用

Native Bash (Windows, minimal)id bash-native-minimal,order 6 读本机那条线的 minimal,只换 shell 面:一个 persona 与一个持久 bash。
只要一个 shell 的轻量会话

📟 持久终端组件

插件页面里 dsh-bash-native 提供两个可分别开关的组件:dsh-bash-native/presets(两个预设,默认开)与 dsh-bash-native/terminal(提供 6 个交互式持久终端工具,默认关)。

该组件和内置的 持久终端(@deepseek-ai/dsh-experimental-terminal-bundle)注册同名工具,不能也不需要同时开启。 (tool "terminal_open" is already registered)

🛠 配置

除下表外,执行预算继承自 @deepseek-ai/dsh-bash-local:cwd、timeoutMs、maxTimeoutMs、maxOutputBytes、maxSpillBytes、graceMs,默认值与上限由那一侧的 schema 单一出处提供。

键默认含义

confine
true
是否服从 ctx.sandbox;关闭即放弃文件策略与拒绝标记

requireEngineOnLoad
false
无引擎时是加载失败,还是每次调用失败(两个预设都设 true)

toolsDir
''
POSIX 工具链目录;空 = 先看每用户目录里自建的那份(%LOCALAPPDATA%\dsh-bash-native\tools\bin),没有程序就用随包工具链。填了则照用,空目录也算数(不会悄悄换成随包那份)

fileCommandsFromPath
['sed','rmdir']
改从工具链程序执行的引擎内建文件命令(仅限引擎实现的 12 个名字);工具链没有提供该程序、或工具链目录不在 PATH 上时自动忽略,进程内副本继续生效。见下面的「已知限制」

rcFile
''
持久 PTY 会话读的启动文件;空 = %LOCALAPPDATA%\dsh-bash-native\bash-native-rc.sh

denialSignatureAdditions
['os error 5']
追加的拒绝签名,默认值让非英文 Windows 也标得出拒绝

shellEnvOverrides
{}
追加环境变量,层叠在模型友好默认值之上

verifyArtifacts
'stamped'
校验力度:stamped 只在产物或运行副本的 size/mtime 变化时重新哈希;always 每次解析都哈希(引擎 11 MB、工具链 37 MB)

🧰 工具链

工具链随包分发,装完即用。

📚 101 个名字,公布其中 20 个

| 类别 | 内容 |
| 公布(20) | grep、sed、awk、jq、find、xargs、diff、cmp、which、timeout、stat、ps,以及 tty、nohup、nice、uptime、hostid、pathchk、locate、updatedb |
| 只装不公布(81) | uutils coreutils 的 88 个程序(cat、ls、rm、cp、head、tail、wc、sort、seq 等——引擎不再自带这些),以及 shell 内建要用的程序形态 echo、printf、pwd、test、true、false、kill |

- 支持与引擎相同的 /tmp 与盘符别名,「程序再启动程序」的链同样成立:printf '/tmp/f\n' | xargs awk '{print}' 与 printf '/tmp/x\n' | xargs rm 都拿到真实路径。
- 数据位由两边同一张表保护:开关 -e/-f 的值、grep/sed/awk 的首个位置操作数、find 的 -name/-path 类主项、basename/dirname 的位置参数,都不翻译——引擎启动的子进程与工具链里「程序再启动程序」的那一半用的是同一份清单。
- 顺带把 bash 与 sh 放到 PATH 上(工具链目录在前,名字目录紧随其后,两者都排在宿主原有 PATH 之前):

src/shim.ts 在 %LOCALAPPDATA%\dsh-bash-native\shim\<引擎 sha256>\ 里把这两个名字指向已校验引擎——同卷时是硬链接,两个名字共用引擎那一份字节。

⚠️ 已知限制

- 两条与进程内副本无关的平台差异:

| 形态 | 实测 |
| cd /absent 2>err.txt \| cat | 诊断既不在 err.txt 也不在 shell 的 stderr 上 |
| printf … \| head -c 1 的 PIPESTATUS[0] | 内建写端不报断管,0 而不是 141 |

- 断管(读端提前关闭)时,工具与外部程序用它们自己的措辞和退出码——这是 Windows 没有 SIGPIPE 的后果(每个工具各自决定断管怎么办):

| 命令 | 写端退出码 | 备注 |
| seq 1 200000 \| head -c 1 | 0 | stderr 打 write error: Broken pipe |
| yes \| head -c 1 | 0 | 不吭声 |
| cat big \| head -c 1 | 13 | — |
| ls -R 某目录 \| head -c 1 | 1 | — |
| 引擎自己的内建 | 0 | 不报断管(见上一条 PIPESTATUS[0]) |

- { …; } | head 在单行超过约 100 KB 时会挂起(rubash 1.4.3,实测:60 KB 单行正常、200 KB 单行超时):读端读走一部分就退出,写端不中止;把这条管道的输出接到文件时是挂起,接到另一个管道时则整段输出丢失。绕开办法:{ …; } | cat | head,或先写到文件再读。记录在 docs/research.md §72,test/test-brush-regressions.mjs 每次运行都会打印现状。
- 不认识的程序分不清数据与路径:把一个「像路径」的数据词交给引擎不认识的原生程序(node -e '…' '/x/{print}'),它会被当作路径操作数翻成 Win32 拼写——MSYS 同样如此;区别是随包工具的脚本、模式、程序位有确定的数据位表,永不被改写。

🧹 磁盘足迹与清理

运行时状态都在 %LOCALAPPDATA%\dsh-bash-native 下,并且按内容寻址:每个引擎构建一个 shim/<引擎 sha256>/。

| 目录 | 谁写的 | 大小 | 删掉会怎样 |
| shim/<摘要>/ | 每次解析 | 引擎大小(同卷硬链接时约 0) | 下次解析重建 |
| toolchain/<摘要>/ | 每次解析 | 约 36 MB(同卷硬链接时约 0) | 下次解析重建 |
| verified/ | 每次校验 | 几十 KB | 只会多一次哈希 |

显式清理(停掉 DSH 之后运行):将<dsh_profile>替换为你的profile路径,例如:%USERPROFILE%\.dsh\profiles\<profile>

node <dsh_profile>/node_modules/dsh-bash-native/bin/clean-state.mjs # 只报告,不动文件
node <dsh_profile>/node_modules/dsh-bash-native/bin/clean-state.mjs --yes # 删掉被取代的 shim/ 与 toolchain/ 缓存

📄 许可

| 内容 | 许可 | 声明与扫描 |
| 本插件 | MIT | LICENSE |
| rubash 引擎 | MIT | engine/LICENSE.rubash、依赖扫描 engine/THIRD-PARTY.md |
| 随包工具链 | MIT | uutils coreutils/findutils/grep/sed、jaq、goawk、仓库内 posix-extra;toolchain/LICENSES/ 与 toolchain/THIRD-PARTY.md |

🙏 参考与致谢

为智能体提供原生 bash 这条路上,oh-my-pi 是最早的先行实现之一,本插件早期针对 brush 的诊断与修法也受益于它留下的记录。
预览分享卡片长什么样 ↓SpookyWaste/dsh-bash-native
分享 · DeepSeek Harness Hub
SpookyWaste/dsh-bash-native

Rust 实现的 Windows 原生 POSIX bash 执行器:给 DSH 提供一个真正原生的 bash不依赖…

回到顶部