dsh-nu 强制 DeepSeek Harness 把所有 shell 命令都交给 Nushell 执行,同时禁用…
31.3/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/23 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ryan4yin/dsh-nu该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/22) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件
查看扫描证据(共 38 处,展示前 29 条)
- 写入本地文件eval/judge-summary.mjs:24
mkdirSync(out, { recursive: true }) - 读取本地文件eval/judge-summary.mjs:28
for (const file of readdirSync(dir).filter((name) => name.startsWith('judge-') && name.endsWith('.json'))) { - 读取本地文件eval/judge-summary.mjs:29
const payload = JSON.parse(readFileSync(join(dir, file), 'utf8')) - 写入本地文件eval/judge-summary.mjs:66
writeFileSync(join(out, 'judge-summary.json'), JSON.stringify(summary, null, 2) + '\n') - 写入本地文件eval/judge-summary.mjs:79
writeFileSync(join(out, 'judge-summary.md'), lines.join('\n') + '\n') - 读取本地文件eval/judge.mjs:41
const cases = JSON.parse(readFileSync(join(here, 'cases.json'), 'utf8')) - 写入本地文件eval/judge.mjs:45
mkdirSync(out, { recursive: true }) - 读取本地文件eval/judge.mjs:55
if (!existsSync(teachingOff)) writeFileSync(teachingOff, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 写入本地文件eval/judge.mjs:55
if (!existsSync(teachingOff)) writeFileSync(teachingOff, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 读取本地文件eval/judge.mjs:55
if (!existsSync(teachingOff)) writeFileSync(teachingOff, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 读取本地文件eval/judge.mjs:73
const summary = JSON.parse(readFileSync(join(dir, 'summary.json'), 'utf8')) - 读取本地文件eval/judge.mjs:76
if (!existsSync(samplePath)) continue - 读取本地文件eval/judge.mjs:77
const raw = JSON.parse(readFileSync(samplePath, 'utf8')) - 执行外部命令来自配置/变量eval/judge.mjs:107
const result = spawnSync(dsh, args, { env, cwd: out, encoding: 'utf8', timeout: timeoutMs, input: prompt, maxBuffer: 32 * 1024 * 1024 }) - 写入本地文件eval/judge.mjs:134
writeFileSync(join(out, 'judge-' + label + '.json'), JSON.stringify(summary, null, 2) + '\n') - 读取本地文件eval/run.mjs:40
const cases = JSON.parse(readFileSync(join(here, 'cases.json'), 'utf8')).filter((item) => onlyCases.length === 0 || onlyCases.includes(item.id)) - 读取本地文件eval/run.mjs:43
if (!existsSync(dshHome)) throw new Error('dsh home does not exist: ' + dshHome) - 读取本地文件eval/run.mjs:44
if (!existsSync(modelPatch)) throw new Error('model patch does not exist: ' + modelPatch) - 写入本地文件eval/run.mjs:46
mkdirSync(out, { recursive: true }) - 写入本地文件eval/run.mjs:54
mkdirSync(env.PNPM_HOME, { recursive: true }) - 写入本地文件eval/run.mjs:55
mkdirSync(env.XDG_DATA_HOME, { recursive: true }) - 写入本地文件eval/run.mjs:56
mkdirSync(env.XDG_CACHE_HOME, { recursive: true }) - 读取本地文件eval/run.mjs:58
if (!existsSync(teachingOffPatch)) writeFileSync(teachingOffPatch, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 写入本地文件eval/run.mjs:58
if (!existsSync(teachingOffPatch)) writeFileSync(teachingOffPatch, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 读取本地文件eval/run.mjs:58
if (!existsSync(teachingOffPatch)) writeFileSync(teachingOffPatch, readFileSync(join(here, 'teaching-off.yml'), 'utf8')) - 读取本地文件eval/run.mjs:60
if (!existsSync(teachingCompactPatch)) writeFileSync(teachingCompactPatch, readFileSync(join(here, 'teaching-compact.yml'), 'utf8')) - 写入本地文件eval/run.mjs:60
if (!existsSync(teachingCompactPatch)) writeFileSync(teachingCompactPatch, readFileSync(join(here, 'teaching-compact.yml'), 'utf8')) - 执行外部命令来自配置/变量eval/run.mjs:64
const result = spawnSync(command, args, { - 执行外部命令来自配置/变量eval/run.mjs:84
const child = spawn(command, args, {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-bash-sandbox@deepseek-ai/dsh-pwsh-sandbox@deepseek-ai/dsh-sandbox@deepseek-ai/dsh-sandbox-policy@deepseek-ai/dsh-shell@deepseek-ai/dsh-subprocess@deepseek-ai/dsh-system-prompt@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
把 Nushell 作为 DeepSeek Harness(dsh)唯一的 shell:每次 shell 调用都运行 nu,外来 shell 在启动前就被拒绝,模型也会被教会 Nushell。
README
由 DeepSeek 最新模型翻译生成dsh-nu
dsh-nu 强制 DeepSeek Harness 把所有 shell 命令都交给 Nushell 执行,同时禁用 bash / pwsh 执行器。它做三件事:
- 替换执行器:借助 DeepSeek Harness 暴露的底层扩展点,在运行时把 bash / pwsh 执行器换成 Nushell 执行器(命令以 nu --no-config-file -c "<command>" 运行)。dsh 看到的仍是同一个 bash / pwsh 工具,调用约定也不变,所以 Nushell 执行器能接入 dsh,并与其他插件和功能保持兼容。别扭之处只在外观:模型调用名为 bash / pwsh 的工具,传进去的却是 Nushell 脚本。
- 教会模型:在系统提示词里加一小节 Nushell 规则和语法指南。这段教学内容会占用一些上下文,但很关键:关掉它,模型调用 shell 工具时就会频繁出错。
- 拒绝转交:Nushell 命令把活儿交给别的 shell 时,守卫会在进程启动前拒绝。Nushell 不接受或会悄悄读错的 Bash 和 PowerShell 语法,由语法规则拒绝,报错里给出 Nushell 的写法。
状态
0.6.1 已发布到 npm。
展开完整文档(README 剩余 1 节)
dsh-nu
dsh-nu 强制 DeepSeek Harness 把所有 shell 命令都交给 Nushell 执行,同时禁用 bash / pwsh 执行器。它做三件事:
- 替换执行器:借助 DeepSeek Harness 暴露的底层扩展点,在运行时把 bash / pwsh 执行器换成 Nushell 执行器(命令以 nu --no-config-file -c "<command>" 运行)。dsh 看到的仍是同一个 bash / pwsh 工具,调用约定也不变,所以 Nushell 执行器能接入 dsh,并与其他插件和功能保持兼容。别扭之处只在外观:模型调用名为 bash / pwsh 的工具,传进去的却是 Nushell 脚本。
- 教会模型:在系统提示词里加一小节 Nushell 规则和语法指南。这段教学内容会占用一些上下文,但很关键:关掉它,模型调用 shell 工具时就会频繁出错。
- 拒绝转交:Nushell 命令把活儿交给别的 shell 时,守卫会在进程启动前拒绝。Nushell 不接受或会悄悄读错的 Bash 和 PowerShell 语法,由语法规则拒绝,报错里给出 Nushell 的写法。
状态
0.6.1 已发布到 npm。测试过的组合是 dsh 0.2.1-rc.2 和 Nushell 0.115.1,记录在 package.json 的 testedWith 字段里。
兼容性
dsh-nu 只支持这一组合。peer 范围覆盖 dsh 系列,启动时如果安装的 dsh 或 Nushell 版本不同,日志会给出警告。其他版本未经验证。
安装
需要 Node 22.19+、PATH 上有 pnpm(dsh plugin 安装器会调用它)、dsh 0.2.1-rc.2,以及 PATH 上有 Nushell 0.115.1。
把 bundle 加到 dsh 配置档:
dsh plugin --profile tui add dsh-nu@0.6.1
浏览器应用改用 --profile web。首次调用 dsh plugin 会创建配置档;bundle 的补丁禁用平台 shell 执行器,并挂载 Nushell 执行器和教学层。用 dsh tui(或 dsh web)启动配置档。
如果 pnpm 对新发布的版本报 ERR_PNPM_NO_MATURE_MATCHING_VERSION,说明它正在应用 minimumReleaseAge。等过截止时间,或在同一条命令里加上 --config.minimumReleaseAge=0。
开发
仓库自带 flake,内含测试过的 Node 和 Nushell 版本:
nix develop
进入 shell 后,或用你自己的 Node 24 和 Nushell 0.115.1:
npm install
node dev/link-peers.mjs --root <dsh-install-root>
npm run typecheck
npm test
npm run build
dev/link-peers.mjs 把 dsh 的 peer 包链接进 node_modules,这样测试不用发布就能驱动真实的接缝。
测试套件跑在 Node 24 上。包允许 Node 22.19 及更新版本,但 24 以外的版本未经验证。
真实模型教学评测与 npm test 分开进行;三臂测试框架、跨模型评判和带日期的结果见 eval/README.md。
Nix
dsh-nu 不打包 dsh 本身。这个 flake 从代码检出构建插件,dsh 安装则来自打包它的 flake。下面完整示例使用 nur-ryan4yin,它的 dsh 还提供 withPlugins 包装器,用来应用 bundle 补丁:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
dsh itself, plus its withPlugins wrapper.
nur-ryan4yin = {
url = "github:ryan4yin/nur-packages";
inputs.nixpkgs.follows = "nixpkgs";
};
The bundle, built from this repository's flake. Its nixpkgs is not
followed: that pin supplies the tested Nushell.
dsh-nu.url = "github:ryan4yin/dsh-nu";
};
outputs = { self, nixpkgs, nur-ryan4yin, dsh-nu }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
dsh from nur-ryan4yin; withPlugins applies the bundle patch.
dsh = nur-ryan4yin.packages.${system}.dsh;
dshWithNu = dsh.withPlugins [
(dsh-nu.lib.mkDshNuPackage { inherit pkgs dsh; })
];
in {
packages.${system}.default = dshWithNu;
Install dshWithNu wherever dsh would go, for example:
environment.systemPackages = [ dshWithNu ];
home.packages = [ dshWithNu ];
};
}
nix build 会产出那个 dsh 二进制。mkDshNuPackage 只需要 dsh 安装的 @deepseek-ai 对等包,所以 llm-agents.nix 的 dsh 也能用;应用 bundle 仍需一个能加 --patch 的包装器,nur-ryan4yin 的 withPlugins 就提供这个。用 nix flake update dsh-nu 升级 bundle。
配置
bundle 的补丁挂载一行 executor 和一行 teaching;配置档补丁按 id 覆盖其中任意一行。下面这个配置档补丁写全了每个字段及其默认值:
- id: nu-executor
name: dsh-nu/executor
config:
nuPath: ""
nuArgs: ["--no-config-file", "-c"]
timeoutMs: 60000
enforceNushellOnly: true
syntaxLint: true
syntaxHints: true
requireNu: true
verifyNu: true
verifyTimeoutMs: 10000
foreignShellAllowlist: []
- id: nu-teaching
name: dsh-nu/teaching
config:
guide: full # full | compact | off
rules: true
rewriteToolDescriptions: true
toolNames: ["bash", "pwsh"]
| 字段 | 默认值 | 含义 |
| nuPath | $DSH_NU_PATH,然后 nu | Nushell 可执行文件:先看字段,再看环境变量,最后看 PATH |
| nuArgs | ["--no-config-file","-c"] | Argv 前缀;命令追加在最后,所以必须以 -c 或 --commands 结尾 |
| enforceNushellOnly | true | 拒绝把命令交给其他 shell |
| foreignShellAllowlist | [] | 豁免两项预检的正则;每条都必须匹配整条命令 |
| syntaxLint | true | 启动前拒绝 bash/PowerShell 语法 |
| syntaxHints | true | 识别到失败或弃用警告时追加一条 Nushell hint (…),退出码为 0 的情况也算 |
| guide | full | 提示词指南:full、compact 或 off |
| rules | true | 在开头加上 shell 规则段落 |
| rewriteToolDescriptions | true | 替换 bash / pwsh 工具描述里关于 shell 的说法,宿主自己的契约文本保持不变 |
| toolNames | ["bash","pwsh"] | 需要改写 shell 文本的工具 schema |
| timeoutMs | 60000 | 每次调用的默认超时;maxTimeoutMs 给覆盖值设上限 |
| requireNu / verifyNu | true / true | 解析不到 nu、拿不到版本号、或预检跑不起来时,启动直接失败 |
| verifyTimeoutMs | 10000 | 该探测的超时上限 |
两项检查都通过两个只读探测读取 Nushell 的解析器,探测跑在私有的临时脚本上。verifyNu 打开、且两项检查至少开一项时,启动会跑一次 token 与诊断金丝雀:默认 requireNu 为真,失败即启动失败;requireNu: false 时改为拒绝每次调用,而不是跳过检查直接运行(fail closed)。错误信息会点名 enforceNushellOnly: false 和 syntaxLint: false 这两个逃生口,并指出经过测试的 Nushell 版本。
按目标选指南:
- off 只关掉指南;除非 rules: false,规则仍然开着。这是诊断用的基线:评测里记录了大量预检拒绝和额外重试,正常会话别用。
- compact 覆盖常见写法,占用的提示词 token 更少。评测样本大多没被拒绝。
- full 保留详细语法参考,应对不常见语法和边界情况。
沙箱预算(cwd、timeoutMs 等)留在 nu-executor 这一行,不放 dsh 共享的 shell 配置命名空间。要覆盖某一行,就在配置档自己的 cordis.patch.yml 里按 id 改。examples/preset-nu-shell.md 列出了预设层面需要关掉的 shell 绕过项。
「只用 Nushell」管不到的地方
这道防线不是沙箱:它只检查能读到的命令位置,其余一概够不着。
- 普通引号字符串里的 shell 名是数据,不是命令位置:'bash -c "echo hi"' 求值成一个字符串,不会运行 bash。$"…" 和 $'…' 会做插值,所以里面每个括号表达式都按代码检查;其中的引号文本仍是数据。
- 字面量 nu -c '' 或 nu -e '' 会递归检查,最多四层。运行时拼出来的脚本值不检查。
- Nushell 脚本文件不读:nu script.nu、source、use 加载的代码防线根本看不到,所以写进文件再那样跑起来的交接够不着。
- 运行时算出来的 shell 名,或者自己启动 shell 的程序(语言运行时、编辑器),都够不着。防线只跟字面量 shell 名、固定的启动器表(含 nix shell / nix develop --command 和 nix run)、run-external,以及字面量 nu -c 脚本。
- 工具自己运行参数文本的情况,有意不纳入范围:hyperfine、watch、make、npm run 和 git 钩子都不属于 shell 交接,所以守卫放行它们,即使它们内部可能启动 sh。拒绝它们会弄坏这些工具,而不是让部署保持 Nu-only。你传给这类工具的命令是 Nushell 时,要指明 shell:hyperfine -S nu 'ls | length'、hyperfine -N 'rg foo'(完全不用 shell),或 watch -x rg foo。Nushell 不能作为 watch 自己启动的 shell,所以 watch 'a && b' 跑的是 bash,不是 Nushell。
- 自带 shell 的预设行,比如自定义插件直接调用 ctx.subprocess.spawn,或 PTY 持久 shell 组。Nu-only 部署要禁用它们;见 examples/preset-nu-shell.md。
- 其他 shell 接缝。钩子桥、持久终端和 tmux 上下文各用自己的服务或脚本,不在本 bundle 内。严格部署时保持关闭。
- dsh-nu 不改变沙箱行为:danger-full-access 直接运行,受限模式通过 ctx.sandbox.confine() 包装 Nushell argv,mode / denied / enforcement 的上报方式不变。
- 预检的解析器探测(nu --ide-ast 和 nu --ide-check)在沙箱外运行,先于命令。它们只解析命令、不执行,但 source 或 use 指定的文件会从磁盘读取并解析;文件内容不会返回给模型。
文档
- 为什么只用 Nushell 作为 shell
- 故障排查
- 验证
- Nushell 摩擦点评审
- 分发与支持(ADR)
- 基于解析器的预检(ADR)
- 术语表
许可证
MIT。见 LICENSE。
致谢
本项目的 Nu-only shell、守卫和教学思路来自 YMRwithNoworry/dsh-nushell-only(MIT),这些思路的原始实现。
感谢 DeepSeek Harness 项目提供本 bundle 扩展的 shell 接缝。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
ryan4yin/dsh-nu
dsh-nu 强制 DeepSeek Harness 把所有 shell 命令都交给 Nushell 执行,同时禁用…
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9