Skip to content

dsh-wsl-tool

Verified

dsh-wsl-tool · v1.8.0 · MIT

Model-facing WSL tools for DeepSeek Harness: run Linux commands from Windows with background jobs, stdin, path translation, destructive-command guard and WSL capability diagnostics.

Install

dsh plugin add dsh-wsl-tool

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-wsl

English | 简体中文

面向模型(model-facing)的 WSL 工具插件,用于 DeepSeek Harness(DSH)。它让智能体直接通过 wsl.exe 执行 Linux 命令——无需手写 .sh 脚本或 pwsh 包装。

工具

插件注册三个工具:

工具 用途
wsl 执行 Linux 命令,返回带退出码、超时与截断标记的 stdout/stderr
wsl-path 通过 wslpath 在 Windows 与 WSL 路径间互转
wsl-env 汇总 WSL 环境(发行版、内核、CPU、内存、磁盘)

wsl

执行形式:

wsl.exe [-d <distro>] -e bash -lc "cd <workdir> && <command>"

返回 stdout/stderr,并附加 [exit code: N] / [killed by signal: ...] / [timed out after Nms; the command was killed] / [output truncated: ...] 标记。

<distro> 取调用参数 distro,其次 DSH_WSL_DISTRO,都没有时完全不传 -d, 由 wsl.exe 使用系统默认发行版——这正是本插件能装到没有 Ubuntu-22.04 的机器上的原因。

超过 Windows 命令行上限(32767 字符)的脚本会改为通过 stdin 交给 wsl.exe [-d <distro>] -e bash -ls 执行,因此长命令没有体积上限。

stdin 可以把文本喂给命令(默认是 /dev/null,交互式命令会立刻 EOF);传 runInBackground: true 则把命令交给宿主的任务注册表,模型随后用已有的 job_output 工具读它、用 job_kill 停它——不需要任何新工具。后台任务需要 preset 组合里有 @deepseek-ai/dsh-tool-jobs;没有时会直接报错说明,而不是悄悄退化成 前台执行。

wsl-path

双向转换路径:C:\Users\me\a.txt/mnt/c/Users/me/a.txt,或 /home/me/a.txt\\wsl.localhost\Ubuntu-22.04\home\me\a.txt。方向自动识别, 也可用 direction: 'win' | 'linux' 强制指定。

wsl-env

返回发行版列表、被探测的发行版、内核与架构、CPU 数、内存与磁盘占用,以及这台机器 实际能做什么

distro: Ubuntu-22.04 (system default)
Linux 6.6.87.2-microsoft-standard-WSL2 x86_64
nproc: 24
Ubuntu 22.04.5 LTS · WSL2 · cgroup v2
systemd: yes · docker: not installed
GPU: /dev/dxg present (GPU passthrough enabled) · nvidia-smi: GPU 0: NVIDIA GeForce RTX 5070 Laptop GPU
drives: /mnt/c /mnt/d
/etc/wsl.conf: [boot];systemd=true;[user];default=xiny; · .wslconfig: not set
launcher: WSL 版本: 2.6.3.0 · 内核版本: 6.6.87.2-1 · WSLg 版本: 1.0.71 · Windows: 10.0.26200.9457

让智能体在动手前就知道有什么可用:WSL1 还是 WSL2、服务是否由 systemd 托管、cgroup 版本 (容器相关)、GPU 直通、docker(未装/只有 CLI/守护进程版本)、挂载了哪些盘,以及 /etc/wsl.conf 与 Windows 侧 .wslconfig 的配置。可传可选参数 distro

每一项都是可选的,且如实降级:探针没跑成的行会被省略,读到了但为空的值会写明 (docker: not installed.wslconfig: not set),探测失败会写进摘要而不是被丢掉, 发行版不存在则直接报错,而不是给出一份残缺答案。注意 wsl --version 输出是本地化的, 因此其标签按启动器原样透传、不按名称解析;Direct3D/MSRDC/DXCore 版本作为噪声被省略。

安装

发布到 npm 的包名是 dsh-wsl-tool,不是 dsh-wsl:registry 判定 dsh-wsl 与既有包 is-wsl 过于相似而拒绝,换 token 或改设置都无法绕过。仓库名、插件名与市场条目仍沿用 dsh-wsl。组合包的 patch 用相对路径指向自身入口,因此 node_modules 下的文件夹名可以 不同——必须一致的是你安装时使用的那个名字。

  1. 将本包加入 DSH 的 profile(profiles/<profile>/package.json):

    { "dependencies": { "dsh-wsl-tool": "file:<path-to-this-repo>" } }
    

    或运行 dsh plugin add --profile <profile> dsh-wsl-tool 从 npm 安装,或用 dsh plugin add --profile <profile> file:<path-to-this-repo> 从本地检出安装 (依赖名会取本包自身的名字)。

  2. 在某个 agent preset 的 agent.cordis.yml 中加入 tool-wsl 行:

    - id: tool-wsl
      name: 'dsh-wsl-tool'
    

    name 即安装后的包名;若你用了别的依赖名,就填那个名字。

  3. 重启 DSH。

wsl 参数

参数 必填 类型 说明
command string 要执行的 Linux 命令
description string 简短的界面说明文字
workdir string Linux 路径(/home/me~/src)或 Windows 路径,默认 ~
timeoutMs number 超时毫秒数,默认 600000(10 分钟);到点后杀进程并把结果标记为超时
distro string WSL 发行版;默认使用系统默认发行版
env object 要导出的额外环境变量(键必须是合法 shell 变量名)
stdin string 在命令运行前写入其 stdin 的文本(UTF-8)
runInBackground boolean 作为后台任务运行并立即返回任务 id;用 job_output 读、job_kill
allowDangerous boolean true 才允许执行危险命令
translatePaths boolean 默认 true;置 falsecommand 原样传入,不做路径改写

配置

环境变量 默认值 作用
DSH_WSL_DISTRO (系统默认) 为所有调用固定发行版
DSH_WSL_TIMEOUT_MS 600000 模型命令的默认超时;单次调用可用 timeoutMs 覆盖
DSH_WSL_MAX_TIMEOUT_MS 86400000 单次 timeoutMs 的上限,对齐平台 shell 工具的 maxTimeoutMs;默认超时也受它约束
DSH_WSL_MAX_OUTPUT_BYTES 65536 每条流的内存窗口(1 KiB – 8 MiB);设到 64 MiB 以上时也会抬高落盘上限
DSH_WSL_WORKDIR home 不传 workdir 时的起点:home(Linux 的 ~)、session(会话工作目录,Windows 检出对应 /mnt/<盘>/...),或任意显式路径

无法解析或越界的值会回退到默认值——一个写错的环境变量不该让三个工具一起挂掉。 配置在挂载时读取一次,改动需重启 DSH 生效。

注意事项

  • 每次调用都在全新的 shell 中执行——cwd / 变量 / 函数不会在调用间保留。

  • stdin 默认是 /dev/null,除非传 stdin:交互式命令(readcat、不带 -Ssudo 密码提示)否则会立刻收到 EOF,无法等待输入;本插件不会、也无法弹出任何提示。 通过 stdin 传的密码会被记进会话记录。

  • workdir 默认 ~;设 DSH_WSL_WORKDIR=session 可改为从会话工作目录开始(见"配置")。

  • 发行版取调用参数 → DSH_WSL_DISTRO → 系统默认,代码里不再硬编码兜底名称。

  • 发行版不存在时会给出明确报错(distribution "X" is not registered),而不是 一个原始 -1 退出码;其他启动器错误(Wsl/Service/WSL_E_*)也会带错误码上报, 不会被当成命令自身的退出状态。

  • 后台任务可以活得比这次调用久runInBackground: true 立即返回任务 id(wsl-N) 并把工作登记进宿主任务注册表,job_output 读它(标记与前台一致)、job_kill 停它。 后台模式下 10 分钟默认超时不适用,但显式 timeoutMs 仍然生效;非零退出与前台一样 报成 completed 并把退出码写进 detail。

  • commandworkdir 中的 Windows 路径会自动转换为 /mnt/...

    • C:\Users\me\a.txt/mnt/c/Users/me/a.txt;含空格、括号的路径与一行多个路径都支持 (C:\Program Files\GitC:/Program Files/GitC:\Program Files (x86)\Steamcp C:\a.txt D:\b.txt 两个路径都会转换);
    • \\wsl.localhost\<发行版>\home\x\\wsl$\<发行版>\home\x/home/x
    • 只是"看起来像"盘符的文本不会被动:单个小写字母后跟 /(如 a:/b)、其他表达式里的 盘符(如 sed "s/C:\x/y/")、URL 里的疑似盘符段;
    • 当路径要交给Windows 程序(经 interop 调用)时请传 translatePaths: false: WSL 不会把 /mnt/c/... 反向翻译,notepad.exe C:\file.txt 需要原始写法。 该开关只影响 commandworkdir 始终会被转换。
  • workdirwsl-path 参数中的 ~ 会被 shell 展开(~/my dir 可用)。其余路径 一律单引号包裹,因此路径里的 $VAR 不会展开。

  • 每条流输出上限 64 KiB:保留尾部,标记中会给出保存完整输出的落盘文件路径, 信息不会不可恢复:

    [stdout truncated: at most the last 65536 of 1288895 bytes were kept; full stream: C:\...\stdout.log]
    
  • timeoutMs 默认 10 分钟,避免卡死的 wsl.exe 永久挂住调用;需要长时间运行的命令可以传更大的值, 但上限是 DSH_WSL_MAX_TIMEOUT_MS(默认 24 小时)——手滑不会变成"永不超时"。结果里回报的始终是 实际生效的那个期限。到点会连 Linux 侧进程一起杀掉(Windows 上由 provider 使用 taskkill /T /F)。

  • 宿主 shell 的环境事实会转发进发行版(WSL 默认不跨边界传 Windows 环境变量):DSH_SESSION_IDDSH_SHELL、以及翻译成 /mnt/... 形式的 DSH_HOME 会在命令前 export,脚本因此能看到与平台自带 shell 工具一致的会话事实。这些值按次从宿主的 shellEnv 注册表解析(与其他 shell 工具同一个来源), 因为它们属于会话而非宿主常量——直接读宿主 process.env 会什么都读不到、静默地什么都不转发。 DSH_WEB_URL 故意不转发——它是 Windows 侧服务的 127.0.0.1 地址, 而默认 NAT 模式下 WSL 访问不到 Windows 回环(实测 127.0.0.1 与主机 IP 均返回 HTTP 000,服务本身 也只绑回环),转进去只会给一个打不开的 URL。显式传入的 env 条目总是覆盖转发值。

  • wsl-env 还会报出会话文件所在的位置以及它是否落在 Windows 盘挂载上:

    workspace: /mnt/d/DSHworkarea (Windows drive mount /mnt/d — builds, installs and git are much slower here; prefer a path under /home when it matters)
    

    这个提示值得当真。作者机器实测:128 MB 顺序写在 ext4 上约 2.1 GB/s,在 /mnt/d 上约 247 MB/s; 创建 400 个小文件 ext4 不到 10 ms/mnt/d0.72 s

  • 危险命令默认被拒绝,除非调用时传 allowDangerous: true

    • 任何递归删除——rm -rrm -rfrm -r -frm -R --forcerm --recursive—— 因为 stdin 指向 /dev/null 时不会产生任何提示,rm -r tree 会静默删除整棵树。 每次 rm 调用按其所在命令段单独判定,所以 rm a -f; rm b -r 不能靠拼接标志蒙过去;
    • 往块设备 ddmkfs、分区/擦除类工具(fdiskpartedwipefsmkswap …)、 电源控制(shutdownrebootsystemctl reboot …)、重定向到块设备、fork 炸弹;
    • 防护容忍命令词的各种写法(sudo rm -r -fbash -c "rm -rf /"find . -exec rm -rf {} +rm$IFS-rf\rm -rf$(which rm) -rf),但设备/电源类 工具只在命令位置匹配,因此查看它们是允许的:man fdiskgrep -rn reboot /var/log/syslogecho "the mkfs tool formats disks" 都能正常执行。
  • stderr 中重复出现的启动器噪声会被过滤:localhost 代理警告与 procps 的 screen size is bogus 行。

  • 使用 wsl.exe -e--exec),引号与 $VAR 展开行为与普通 shell 一致; 默认的 -- 透传会破坏单引号和变量。

沙箱边界

DSH 的文件沙箱在两个点实施:shell 执行器(@deepseek-ai/dsh-bash-sandbox-pwsh-sandbox,把 argv 包一层过 ctx.sandbox)和文件系统服务(@deepseek-ai/dsh-fs-sandbox,对两个写操作加策略栅栏)。 wsl 两者都不经过——它通过宿主 subprocess 服务直接拉起 wsl.exe,位于那一层之下。因此 workspace-write 策略约束不到它:它能写 Linux 侧能写的任何位置,也能写 /mnt/<盘> 下 Windows 允许的任何位置。

这不是靠"接入沙箱"能补上的缺口。Windows 上的沙箱最终落到 ACL / 受限令牌,而文件操作发生在 Linux 内核里:写 /home/... 动的是发行版自己的文件系统镜像,任何 Windows 令牌都够不着;写 /mnt/c/... 要经文件系统桥,ACL 是否生效不可依赖。把 wsl.exe 包起来只能约束"启动器",约束不了写入 ——而给出虚假的隔离感,比明说不隔离更危险。(平台自己的 dsh-fs-sandbox 也坦白它的边界: "containment, not a security boundary"。)

wsl 实际拥有的保护是上面那套危险命令守卫:一份确定性的拒绝清单,不是内核边界。请把它当作 "能碰到你的 WSL 安装能碰的一切"来授予权限。

从插件列表安装

本包声明了 dsh.bundle manifest(见 package.json),因此仓库被列表收录后可按 名称安装,例如 dsh plugin add dsh-wsl-tool,市场(storefront)也会提供一键安装。 上文 file: 的本地安装方式仍然有效。

工作原理

插件是一个 cordis 模块,注入宿主平面的 toolssubprocess 注册表。index.js 只是入口,实现按职责拆分:

模块 职责
lib/config.js 默认值与环境变量覆盖,挂载时解析一次
lib/paths.js shell 引号处理与 Windows → WSL 路径转换
lib/guard.js 危险命令规则
lib/result.js 启动器噪声过滤、截断事实、标记渲染
lib/diagnostics.js wsl-env 的能力探针、解析器与输出行
lib/runner.js 唯一的 spawn 路径与启动器错误分类
lib/tools/*.js 三个工具定义(schema / execute / presentCall)
  • apply() 解析配置并注册三个工具,每个都有 JSON-schema 参数定义、输出 schema、 render 钩子与异步 execute
  • 所有调用都走同一条 spawn 路径(runner.runWsl):通过宿主 subprocess 执行 wsl.exe -d <distro> -e bash -lc "<exports; cd workdir && command>",stdout/stderr 上限由配置决定(超出最多落盘 64 MiB),abort 后有 3 秒宽限期,超时默认 10 分钟。 插件自身的探测调用另有 30 秒硬上限,避免 WSL 服务卡死时永久挂住工具调用。
  • env 条目在命令前部以 export 形式注入,确保可靠到达 Linux 侧;Windows 盘符 路径在构造命令前先改写为 /mnt/...
  • 输出为 { exitCode, signal, timedOut, timeoutMs, truncated, stdout, stderr, stdoutTotalBytes, stdoutDroppedBytes, stderrTotalBytes, stderrDroppedBytes, stdoutSpillPath, stderrSpillPath, jobId }render 钩子将其格式化为文本并附加上述 标记。截断标记引用窗口大小本身而不是由解码文本推算的数字——窗口起点落在多字节字符 中间时后者会差一两个字节。
  • jobId 只有后台启动才会赋值,其余路径一律 null,因此两种情况下声明形状都成立。
  • 启动与结算被拆成两步(runner.launch / settle):后台任务要同步交给注册表一对 cancel/done,而前台调用只是 await 同一个 settle。被取消的任务会把 provider 的 "target 启动前即被终止"拒绝映射成 killed——JobHooks.done 不允许 reject。
  • 危险命令防护把命令按 ;&|/换行切成段,每次 rm 调用按自身标志单独判定, 设备/电源类工具在命令位置匹配后才拦截。

插件不发布任何自身服务,因此它可以无 realm 地挂在 agent preset 中。

开发

插件是纯 ESM,无构建步骤,除 DSH 宿主平面外无运行时依赖。迭代方式:把 profile 依赖指向本仓库:

{ "dependencies": { "dsh-wsl-tool": "file:/path/to/dsh-wsl" } }

然后重启 DSH,在包含 tool-wsl 行的 preset 会话中调用工具验证。

测试

npm test          # 260+ 项检查,跑在真实 WSL 上,仅用 shim 顶替 ctx.subprocess
npm run test:real # 同一套检查改跑真实 provider,外加 seam 事实套件

npm test 只替换 ctx.subprocess,用一个复刻了 seam 行为(有界尾窗、落盘文件、 终止阶梯)的 shim 驱动三个工具跑在真实 WSL 上,覆盖路径改写、workdir 引号处理、 危险命令防护、发行版选择、退出码/超时/截断标记、wsl-pathwsl-env、参数校验、 配置解析、启动器错误分类、返回结构与各自 output.schema 的一致性,以及模型可见 目录的 token 预算。

npm run test:real 把同一套检查改跑在 LocalSubprocessRuntime 上——shim 不允许与 真实 seam 漂移——然后跑 test/real-seam.mjs,校验 shim 无法担保的事实 (readFrom(0).nextOffset 是否为整条流字节总数、落盘文件是否完整、超时是否真的杀掉 wsl.exe 的 Linux 侧进程),并用 DSH 自己的 assertSupportedJsonSchema 校验全部 schema。它需要一份 DSH 安装:

DSH_SUBPROCESS_LOCAL=/path/to/dsh/node_modules npm run test:real

同步到 profile

file: 依赖是副本,改本仓库不会改变 DSH 实际加载的内容。任何改动之后:

npm run sync                  # 复制到 ~/.dsh/profiles/web/node_modules/dsh-wsl
npm run sync -- /path/to/profiles/<profile>/node_modules/<你的依赖名>

目标目录必须是 profile 依赖名对应的文件夹(上面的默认值就是本检出的依赖名); 插件按相对路径加载自身入口,因此文件夹叫什么并不影响加载。

然后重启 DSH——插件在加载时只导入一次。

收录

本仓库带有 dsh-plugin topic,并已提交至 awesome-dsh-plugin 社区列表的 wsl 分类。