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

Nushell 执行器 ryan4yin/dsh-nu

DeepSeek Harness
ryan4yin10 天前有提交已提供中文文档扫描:含敏感能力

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
装得上吗
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成

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

验证判据与等级说明 →
⚠︎ 实装验证未通过(unknown · 2026/10/22) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
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 接缝。
预览分享卡片长什么样 ↓ryan4yin/dsh-nu
分享 · DeepSeek Harness Hub
ryan4yin/dsh-nu

dsh-nu 强制 DeepSeek Harness 把所有 shell 命令都交给 Nushell 执行,同时禁用…

回到顶部