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

WSL 工作区 dsh-wsl-workspace-maintainers/dsh-wsl-workspace

DeepSeek Harnessv0.7.11
dsh-wsl-workspace-maintainers3 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness 的 WSL 工作区支持——无缝使用 WSL 工作区,不用在 WSL 里再装一个 dsh,装好这个插件就能在 GUI 里直接添加 WSL 工作区。

59.9/100
综合分
103
GitHub 星标
—
npm 周下载
—/5
用户评分

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

自动兼容检查未通过:engines.node 要求 >=24,不满足 Node 22.19.0。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-wsl-workspace-maintainers/dsh-wsl-workspace
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

engines.node 要求 >=24,不满足 Node 22.19.0

展开查看检查明细 ↓

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

✓npm 包dsh-wsl-workspace @ 0.7.11
✗Node 引擎要求 >=24 · 基线 Node 22.19 不满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

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

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

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

可执行外部命令读写/删除本地文件
查看扫描证据(共 45 处,展示前 33 条)
  • 读取本地文件
    lib/index.js:1209
    readdir: async (path, options) => readdir(path, options),
  • 读取本地文件
    lib/index.js:1210
    readFile: async (path, options) => readFile(path, options),
  • 读取本地文件
    lib/index.js:1211
    stat: async (path) => stat(path),
  • 读取本地文件
    lib/index.js:1907
    if (statSync(`${letter}:\\`).isDirectory()) entries.push({
  • 读取本地文件
    lib/index.js:1921
    const entries = readdirSync(readPath, { withFileTypes: true }).slice(0, 1e3).map((dirent) => {
  • 读取本地文件
    lib/index.js:2126
    const raw = readFileSync(new URL("../package.json", import.meta.url), "utf8");
  • 写入本地文件
    lib/index.js:2394
    if (!existsSync(dest) && existsSync(previous)) renameSync(previous, dest);
  • 删除本地文件
    lib/index.js:2395
    if (existsSync(previous)) rmSync(previous, {
  • 写入本地文件
    lib/index.js:2399
    if (existsSync(dest)) renameSync(dest, previous);
  • 写入本地文件
    lib/index.js:2401
    renameSync(staging, dest);
  • 写入本地文件
    lib/index.js:2403
    if (!existsSync(dest) && existsSync(previous)) renameSync(previous, dest);
  • 删除本地文件
    lib/index.js:2406
    rmSync(previous, {
  • 删除本地文件
    lib/index.js:2569
    rmSync(join(userRoot, entry.name), {
  • 删除本地文件
    lib/index.js:2614
    rmSync(staging, {
  • 写入本地文件
    lib/index.js:2622
    writeFileSync(join(staging, "agent.cordis.yml"), transformed, "utf8");
  • 写入本地文件
    lib/index.js:2635
    writeFileSync(join(staging, "preset.yml"), `name: ${yamlScalar(name)}\n` + orderLine + `description: ${yamlScalar(variantDescription())}\n`, "utf8");
  • 读取本地文件
    lib/fs.js:51
    await lstat(destPath);
  • 写入本地文件
    lib/fs.js:55
    await rename(tempPath, destPath);
  • 写入本地文件
    lib/fs.js:65
    await rename(tempPath, destPath);
  • 读取本地文件
    lib/fs.js:299
    async lstat(path, opts, signal) {
  • 读取本地文件
    lib/wsl-credentials-CVlu6XOL.js:23
    const parsed = JSON.parse(readFileSync(storePath(), "utf8"));
  • 读取本地文件
    lib/wsl-credentials-CVlu6XOL.js:44
    const stat = statSync(path);
  • 写入本地文件
    lib/wsl-credentials-CVlu6XOL.js:56
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    lib/wsl-credentials-CVlu6XOL.js:57
    writeFileSync(file, JSON.stringify(store, null, 2) + "\n", "utf8");
  • 写入本地文件
    lib/wsl-credentials-CVlu6XOL.js:130
    mkdirSync(dirname(path), { recursive: true });
  • 写入本地文件
    lib/wsl-credentials-CVlu6XOL.js:131
    writeFileSync(path, JSON.stringify(store, null, 2) + "\n", "utf8");
  • 执行外部命令来自配置/变量
    lib/wsl-DN2-D6tr.js:37
    execFile(file, [...args], options, (error, stdout, stderr) => {
  • 执行外部命令固定命令
    lib/wsl-DN2-D6tr.js:201
    const value = execFileSync("reg.exe", [
  • 执行外部命令固定命令
    lib/wsl-DN2-D6tr.js:212
    const name = execFileSync("reg.exe", [
  • 执行外部命令来自配置/变量
    lib/wsl-relay.js:136
    const child = spawn(argv[0] ?? "wsl.exe", argv.slice(1), {
  • 执行外部命令来自配置/变量
    lib/wsl-search.js:709
    execFile(wslPath, [...argv], {
  • 启用终端会话(PTY)
    lib/wsl-terminal-tool.js:204
    const spawned = await terminals.spawn(owner, {
  • 启用终端会话(PTY)
    lib/wsl-terminal-tool.js:220
    current = await terminals.spawn(owner, {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-fs@deepseek-ai/dsh-fs-local@deepseek-ai/dsh-sandbox@deepseek-ai/dsh-shell@deepseek-ai/dsh-subprocess@deepseek-ai/dsh-timeout@deepseek-ai/dsh-tool-fs-search@deepseek-ai/dsh-tools@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

2 张 · 点击查看大图

功能亮点

  • 直接添加WSL工作区
  • 无需在WSL内安装

README

由 DeepSeek 最新模型翻译生成
dsh-wsl-workspace dsh.so security dsh.so install 仓库内路径说明。 本包发布的是 lib/、cordis.patch.yml、两份 README、TESTING.md、两份 CHANGELOG、LICENSE、NOTICE 与 docs/driving.(另有 docs/README.md,npm 会打包任何名为 README 的文件,一并带上);src/、tests/、scripts/、ci/ 以及 docs/ 的其余部分只存在于仓库里、不随包发布——本文里点它们的名字是在给坐标,不是在给一个能从 tarball 里打开的文件。 English · · 日本語 · 한국어 · Français · Deutsch · Español · Português · Русский alt text 在 DeepSeek Harness Web GUI 中「添加 WSL 工作区」:让 agent 会话的 bash 命令与文件读写都运行在本机 WSL 发行版里,路径均为 Linux 形式,WSL 内无需安装任何工具链。会话可同时访问 WSL 与 Windows 两个系统——bash 命令在 WSL 发行版内执行,Windows 文件随时可通过 /mnt/<drive>(如 /mnt/c/Users/...)访问。
展开完整文档(README 剩余 1 节)
dsh-wsl-workspace

dsh.so security dsh.so install

仓库内路径说明。 本包发布的是 lib/、cordis.patch.yml、两份 README、TESTING.md、两份 CHANGELOG、LICENSE、NOTICE 与 docs/driving.(另有 docs/README.md,npm 会打包任何名为 README 的文件,一并带上);src/、tests/、scripts/、ci/ 以及 docs/ 的其余部分只存在于仓库里、不随包发布——本文里点它们的名字是在给坐标,不是在给一个能从 tarball 里打开的文件。

English · · 日本語 · 한국어 · Français · Deutsch · Español · Português · Русский alt text 在 DeepSeek Harness Web GUI 中「添加 WSL 工作区」:让 agent 会话的 bash 命令与文件读写都运行在本机 WSL 发行版里,路径均为 Linux 形式,WSL 内无需安装任何工具链。会话可同时访问 WSL 与 Windows 两个系统——bash 命令在 WSL 发行版内执行,Windows 文件随时可通过 /mnt/<drive>(如 /mnt/c/Users/...)访问。

安装

三种方式任选其一,然后重启 dsh web:

1) npm 包
dsh plugin --profile web add dsh-wsl-workspace

2) GitHub 仓库(仓库内已含预构建 lib/,无需本地构建)
dsh plugin --profile web add https://github.com/dsh-wsl-workspace-maintainers/dsh-wsl-workspace

3) 本地目录(开发/自用)
dsh plugin --profile web add D:\path\to\dsh-wsl-workspace

重启 dsh web 后,工作区区头「添加工作区」右侧出现 W 按钮;侧栏折叠成轨道时,它仍位于底部 Settings 旁。

兼容性

这份构建声明兼容下列 DSH 版本,每一条都在隔离实例上实测过(独立 DSH_HOME、依赖固定到该版本、跑满整套门禁):

0.1.0-rc.7 · 0.1.0-rc.8 · 0.1.1-rc.1 · 0.1.1-rc.2 · 0.1.2-rc.1 · 0.1.3-alpha.2 · 0.1.5-rc.1 · 0.1.5-rc.2 · 0.1.7-rc.1 · 0.1.7-rc.2 · 0.2.0-rc.2 · 0.2.1-rc.2

这份列表与 package.json 里的 dsh.compatibility.dshReleases 一一对应,单测会在两者不一致时失败。其中 0.2.1-rc.2 就是 DSH Desktop 0.2.1-rc.2 自带的 DSH 版本——判决取自该应用自己打包的那份宿主(resources/app.asar 内),不只是 npm 上那份——所以桌面版由同一份声明覆盖;应用内帮助面板(对话框右上角「?」)会显示同一组 chip 以及插件版本。插件在运行时自动识别 DSH 版本并选用对应的 API;两边都不支持时会明确报错,而不是留下一个空工作区。版本不在列表里通常仍然可用,但未经验证。

使用

点这个 W 按钮,或在「添加工作区」菜单里直接选「添加WSL工作区」,打开「添加 WSL 工作区」对话框。先从下拉框选择一个发行版,再浏览目录树或直接输入 Linux 绝对路径(如 /home/me/proj),可以点「检查」确认路径存在。对话框文案跟随 DSH 界面语言。用户名是可选项:留空则以该发行版的默认用户运行,填写该发行版里的某个 Linux 用户名则以该用户运行(等价于 wsl.exe -u <用户名>)。用户名只影响 bash 命令的运行身份,文件工具通过 Windows 侧的 WSL 共享访问、不受其影响;每个工作区填写的用户名保存在 <dshHome>/wsl-workspaces.json,删除对应条目(或重开对话框重建工作区)即可恢复默认用户。

点「创建并打开」后,新会话随即运行在 WSL:bash 工具在所选发行版内执行命令,read/write/edit 读写 WSL 文件,模型看到的所有路径都是 Linux 形式。模式选择器照常可用——标准、PTC、极简、创造都会自动落到对应的 WSL 变体(选择器里的 WSL 变体条目为中英双语,如 WSL · Standard mode(标准模式));会话内仍可通过 /mnt/<drive>(如 /mnt/c/Users/...)访问 Windows 文件。对话框右上角的「?」按钮会展开一页说明:这份构建声明兼容的 DSH 版本、插件的用法与特性,以及它无法绕过的已知限制。
alt text

行为与权限说明

- bash 工具:以配置的用户名在 WSL 发行版内运行(留空 = 发行版默认用户,通常为 root),可对发行版内任意路径读写。Windows 的 ACL 沙箱无法包裹 wsl.exe(子进程运行在 Linux 内核侧),WSL 自身即隔离边界,DSH 文件策略不作用于 bash。
- 文件工具(read/write/edit):经 Windows 侧的 WSL 9P 共享访问;用户名设置不影响它们。宿主原本会提供的两处能力,现在由 WSL 世界自己在内部补上——变体在预设的 isolate realm 里挂自己的 fs 提供者,宿主的 fs-sandbox 包装层不在调用路径上。符号链接:共享只列得出链接、解不开目标,链接路径以前会被当成不存在的文件;现在 resolve/lstat 会向发行版问一次真实路径(wsl.exe … readlink -f)并从那里继续,链接本身绝不会被普通文件替换。访问模式:write/edit 完全按宿主后端的做法由 ctx.sandboxPolicy 围栏——同一份 writableRoots 白名单(另加发行版的 /tmp,即会话所在世界的临时区)、同样的 FS_SANDBOX_DENIED(工具层会渲染成拒绝)、同样的 sandboxMode(工具据此声明升级)。由于围栏在解析之后执行,判定的是真实路径:链接指向工作区外就按工作区外处理,workspace-write 下会被拒绝。完全没有策略服务的部署则不围栏,与宿主一致。文件引用(issue #49):会话里的文件链接、工具行的行号引用、回合末尾的「已改动文件」都走右侧栏,地址里带的是模型原样写下的 Linux 路径。宿主用 node:path.resolve(cwd, path) 解析它,而 POSIX 绝对路径在 Windows 上是「当前盘根目录下的相对路径」,所以文档面板以前报文件不存在:/mnt/d/x 落到工作区所在的盘(D:\mnt\d\x),发行版内的路径落到 cwd 共享的根下,而 drvfs 挂载在 9P 下服务不了、报 EPERM。客户端半边现在先把地址翻译好再交给侧栏——/mnt/<盘符>/… 换回 X:\…,其余 Linux 路径走该发行版的 UNC 共享(发行版取自会话的 UNC 工作区,或 /mnt/<盘符> 工作区注册时存下的记录)——tab 的内容与同一地址下的元数据读取一并修好。插件不认为是 WSL 会话的地址原样不动;逐字节重建不出来、或发行版未知的地址也原样透传,不做猜测:宿主平面没有可用的钩子——fs 与 workspaceFiles 端点属于别的插件,cordis 也不允许第二个 provide 抢别人 fiber 持有的名字。另外,只有 0.1.5-rc.1 及以后才有右侧栏与文档预览,六个更早的已声明版本上这个钩子根本不会安装。
- 文件搜索(grep / glob):宿主的搜索套件驱动的是打包进来的 Windows ripgrep,模型交给它的路径却全是 Linux 路径,所以 WSL 变体一直把这一行丢掉、让模型自己在 shell 里搜。现在世界挂上自己的同类实现(src/host/wsl-search.ts → lib/wsl-search.js),搜索在发行版内执行:工具名、参数 schema、条数上限、输出 schema(Line N: 分组、命中数表头、超限尾部提示)、搜索卡片与超限结果落盘全部沿用 @deepseek-ai/dsh-tool-fs-search 自己导出的部件,所以模型看到的东西与宿主一致。grep 用发行版自带的 GNU grep(-rnIEH -Z,POSIX ERE:\d、\w、\b、(?i) 可用,环视与反向引用不支持),像 ripgrep 默认那样跳过隐藏文件/目录与 node_modules,不读 .gitignore,{a,b} 会展开成多个 --include,含 / 的 include 在插件进程里按相对路径匹配(ripgrep 语义);发行版没有 GNU grep 时明确报错,而不是给出错位的记录。glob 用 GNU find 列出文件(-printf 直接带出修改时间,不必逐个 stat),在插件里按 gitignore 风格匹配( 不跨分隔符、* 跨、?、[...]、{a,b},前导 ! 取反),并按修改时间从旧到新排序——与 rg --sort=modified 一致。两者都直接搜 Linux 真实目录(符号链接、权限、不理会忽略规则),不走 9P 共享;都跳过版本库目录,也都不跟进递归中遇到的符号链接(同样是 ripgrep 的默认)。源预设没有搜索套件的模式(极简)不会凭空多出这两个工具。
- 技能目录(skill catalog):从会话 cwd 最近的 .git 祖先开始(没有 .git 祖先则用 cwd 本身)向下扫描 .dsh/skills 与 .agents/skills(含嵌套项目),上限为 4 层目录、64 个技能目录、4096 个已访问目录。建议把工作区注册在你实际工作的项目根;若注册目录本身在更大的 git 仓库里,扫描会从该仓库根开始(与宿主规则一致),同级项目可能一并出现。Windows 侧共享解不开的 Linux 符号链接会改由发行版解析(wsl.exe … readlink -f,每次查找最多 32 条、并发 4 条),解析后从真实路径继续扫描,因此 ln -s 链进来的项目、以及它下面的嵌套项目都能被发现,并按真实路径去重。技能正文始终实时读取。生成预设会把 skill-filesystem 行的 watch 固定为 false(对 \\wsl.localhost\... 开监视会失败),插件改为自己轮询,分两档:轻量档每 3 秒重查一次已发布的技能目录、并给每个技能文件记一次修改时间与大小,所以会话中途新增、删除或改写技能都会在下一个回合生效——改写这条正是时间戳的意义:模型看到的目录只在注册表修订号变化时重建,而光看目录列表分不出一个被改写的 SKILL.md 和没动过的文件。完整重新发现每 30 秒一次,因为只有走一遍才可能找到此前不存在的技能目录(比如新项目里的第一个 .dsh/skills)。两档都不重读技能正文。
- shell 的生命周期:bash 现在有状态——cd、export 的变量、激活的 venv、后台任务都跨调用保留。它是发行版里一只长命的 bash --norc -i,由本插件启动(src/host/wsl-bash-tool.ts + src/host/wsl-bash-session.ts → lib/wsl-bash-tool.js)并经管道驱动:每条命令以 base64 送出,shell 用一条 NUL 分隔的记录回答,记录里带着这次调用独有的 nonce 和退出码——完成是一个字节通道上的事件,而不是在终端画面上匹配一行字。这是有意替换宿主的 PTY 持久 shell:它的判据是"在终端文本里找到哨号行,并要求退出码后面紧跟换行",而交互式 Linux shell 会重绘那一行,把擦掉的格子留成哨号之后的空格,于是判定永远不成立——在真实的 DSH Desktop 0.2.x 会话里,连续三次调用各挂 303.8 秒,之后宿主把 shell 重置(issue #51)。登录环境没有丢:会话启动时依次 source /etc/profile、~/.profile、/etc/bash.bashrc、~/.bashrc,把它们自己的输出丢掉,并关闭 history expansion,于是命令里的 ! 就是模型写的那个意思。PTY 那一档用 DSH_WSL_PTY_SHELL=1 仍可挂载以便对照——那一档挂的是宿主 PTY 后端指向本插件的中继脚本(src/host/wsl-relay.ts → lib/wsl-relay.js,中继把 PTY 交给 wsl.exe … bash -i);世界同时隔离并提供自己的 no-op sandbox 能力(src/host/wsl-sandbox.ts)——宿主的 Windows ACL 运行器读不了 \\wsl.localhost\… 工作区根的安全描述符。命令都在发行版内运行,不受 DSH 文件策略约束——WSL 自身就是隔离边界。包装带来的两个后果写在工具描述里:整轮会话只有这一只 shell,所以某次调用里的 cd 决定下一次从哪里开始——请用绝对路径或在命令开头显式 cd;命令以 & 结尾会在调用内部被后台化——调用立刻返回 exit code 0 且没有输出,真正的输出晚到,甚至落进下一次调用的输出里。后台任务请写成单独一行的 ( 长任务 > log 2>&1 ) &,或使用后台任务工具。终端是按进程实际在做什么给的,不再按命令的名字:一旦命令卡在键盘上,工具会在它卡住的过程中把它看出来、停下来,并在同一次调用里用属于它自己的伪终端重跑一遍——伪终端由会话内部的 script -qec 起,而记录仍由外层帧在 script 之外写出,所以升级碰不到协议(机械部分在 src/host/wsl-bash-tty.ts,那份读数本身在 src/host/wsl-bash-starve.ts)。判别靠从会话外面向发行版看第二眼:进程状态、/proc/<pid>/wchan、它的描述符里有没有终端、以及它在不在烧 CPU——把"在等一次按键"和"在干活"分开的就是这几列,而不是命令名。需要从管道拿输入的命令也能拿到:调用的 stdin 参数随同一帧送进发行版、落成命令读取的文件(上限 32 KiB;更大的输入会被点名拒绝且什么都不执行——被喂了半份输入的程序会以看起来像它自己的错的方式失败);不传 stdin 时命令读 /dev/null,这正是命令自己的读操作不会吃掉结束调用的那些字节的原因。tty: true 仍可一上来就要终端,tty: false 则否决那次重跑;一次在终端上失败、又不落在已知形状里的调用会自己说明是哪一层、以及一发就能定案的对照(见 docs/tty-triage.md,该件为英文)。2026-10-05 实测的几发(maintainer-local,不随包发布):sh -c 'read x < /dev/tty'——程序名是 sh,过去任何一份名单里都没有它——在 628 毫秒被停下、1 760 毫秒带回它自己的 GOT=;同样安静、同样不耗 CPU 的 sleep 4 一步没被碰;sudo 要密码时会把自己的 /proc 条目藏起来(它清掉了 dumpable 标志),于是改由发行版的 root 权限读同一枚探针——root 看得到它自己的用户看不到的那次等待——2 412 毫秒变成 2 412 毫秒,带回 sudo 自己的话;一条为等待键盘的请求写了 60 000 毫秒期限的调用 1 733 毫秒就返回了。旧规则为同样的形状付出的是默认两分钟外加一次会话重建,而之所以改用名单:本票里量着了两件事——ssh-copy-id 因为扫描停在连字符上而永远匹配不上,printf x; vim note.txt 因为只读第一个词而烧掉 121 703 毫秒。重跑就是第二次执行,所以正文会明说:一条在走到它的提示之前已经做过些什么的命令,现在把那部分做了两遍。万一某条命令把 shell 卡住,会话会被重建,工作目录和 export 的变量回放进新会话——而只是跑得久的命令不会被第二次执行。边界是一只 agent 一只 shell:第二只 agent 的 cd、export 的变量、别名与脱离进程都属于它自己,同时发出的两次调用各自带回各自的答案,重建时的进程清扫只停携带本会话令牌的那些进程,而一只 agent 自己的作用域结束时它的 shell 随之停止——于是一个窗口里几轮会话不会攒出没人能关掉的 shell。有一件事最好在 agent 需要 root 之前就知道:这只 shell 是非交互的,所以会要密码的 sudo 在这里没人能答——它约 100 毫秒失败,带回 sudo 自己的话和两条出路(人在产品右栏的终端标签里跑一次,那里能输密码;或给会话用户配 sudoers 的 NOPASSWD,或用 DSH_WSL_USER 把会话直接跑成 root)。本插件没有任何设置能让密码提示变得可回答,也不跑探测去假装知道结论:sudoers 的授权是按命令给的,一句"这个用户有没有 NOPASSWD"对刚失败的那条命令而言就是猜测。这只 shell 与宿主自己的 bash 工具所有不同之处都逐行列在 docs/bash-parity.md(该表与兼容性证据同为英文),每行写明是谁的行为、用户会看到什么、以及判决;这张表被两枚闸读:tests/wsl-bash-parity.test.ts 拿装着的宿主包比对双方声明的参数与输出字段,scripts/compatibility/bash-parity-real.mjs 用同一份探针脚本在真发行版上把两个工具各问一遍、再逐字段对答案——于是"没写进台账的差异"与"写了但已消失的差异"都是构建红,而不是用户意外。
- 可敲键的终端(wsl_terminal):管道无法被敲键,所以会提问的程序——sudo 要密码、ssh 首次连接的指纹、REPL、编辑器、TUI——过去没有 agent 能给的答案:它被诊断、停下、改用一次性伪终端重跑,只为让模型读到程序自己的抱怨,剩下的只能由人接手。现在世界多挂了一扇 agent 能敲进去的门:open 开一个交互终端,send 打文字(默认回车,submit: false 只打字不回车),read 翻看保留的屏幕,signal 发 SIGINT/SIGTERM/SIGKILL/SIGTSTP/SIGHUP(SIGINT 就是 Ctrl-C),close 关闭,list 列出开着的。它就是宿主的 PTY 注册表与 dsh-terminal-bash 后端指向本插件的中继脚本——右栏终端标签背后那套机械,只是键盘换到了 agent 手上——而且它起的 shell 跑在工作区配置的用户下:宿主自己构造 PTY 子进程的环境、会把 DSH_ 全部丢掉,所以用户是从工作区存储里读回来的。其余一切仍走上面那枚管道 bash:管道给的是每个字节,终端屏幕只是 160 列渲染加有限滚动缓冲。三件事写在明处:后端的静默窗口从默认 3000 毫秒下调到 1200 毫秒(来源注在 src/host/variants.ts),于是宿主没认出提示符的那一发约 1.2–1.8 秒返回、而不是约 3.0–3.6 秒;一发 send 会说明自己落在哪一档,而不是把安静的屏幕说成提示符;敲进去的一切都会进对话记录,所以密码只能来自用户。真发行版实测(两档平面 × root/ruler,bash-session-real 66/66 格):open 477–490 毫秒、打进去的命令有回答、敲键能到达阻塞在 /dev/tty 上的程序(GOT=…)、submit: false 只打字不执行而下一次回车执行它、SIGINT 结束 sleep 30 且 shell 存活、close 之后不留 bash -i。
- 可跟踪的后台任务:bash_background 把一条命令放到后台并立刻返回注册表里的 job id,之后宿主的 job_list / job_output(增量读取、状态流转、完成通告)/ job_kill 都照常工作。之所以需要这一行:持久 shell 的 schema 只声明了 command,没有生产者时 job_list 永远回答"没有后台任务",而传给 bash 的 run_in_background: true 会被静默忽略——参数 schema 不禁止额外属性,所以没有任何地方报错。这是真实会话里被发现的问题。该工具只在持久 shell 存在时挂载;保留一次性 bash 行的世界本来就有 run_in_background。
- 老宿主回退到一次性 shell:持久 shell 是宿主的代码,而在 Windows 上它需要一个平台进程检查器,那个东西从 0.1.0-rc.8 才有——在 0.1.0-rc.7 里 spawnTerminal 会在启动任何进程之前抛 subprocess-local: terminal inspection is unsupported on platform win32,于是该版本里每一次 bash 调用都直接失败(不碰 PTY 的 grep/glob 仍正常)。因此插件在启动时探测底座而不是假设:它把一个不存在的程序交给 spawnTerminal,这只会走到检查器那一步、不会真的创建进程,报错信息就能说明是哪一半失败。答案是否时,生成的世界保留一次性的 dsh-tool-bash 行(走本插件自己的 ctx.shell,不经 PTY),模型得到的是一个能用的无状态 shell,而不是每次调用都报错。八个已声明版本里只有 0.1.0-rc.7 是这种状态,之后的版本都拿到持久 shell。
- wsl.exe 在发行版尚未启动时向 stderr 打印的 localhost 端口转发提示(乱码但无害)可忽略。

从别的 agent 驱动它

docs/driving.zh.md(随包发布)回答新电脑 + 新 agent 猜不出来的五件:怎么无头起一枚宿主(以及必须删掉的那一枚环境变量——不删会以退出码 0 静默退出)、登录 URL 与它的 token 从哪来(实测:进程存活期间 token 不会用掉,要作废就重启实例)、本插件的路由与怎么在运行时枚举它的方法(describe)、怎么把文字送进 composer 并发出去(composer 是 Lexical:只发一发 beforeinput,绝不用 execCommand)、以及默认 provider 报 401 时改哪一处。同样的内容也在插件的 describe 方法里(route/loopbackOnly/methods/installProof/driving),随 HTTP 面一起走。

更新日志

完整版本历史(最新在前)已抽到单独的 CHANGELOG.zh.md(英文:CHANGELOG.md);本文只保留上面「行为与权限说明」的当前口径。

相关文档

docs/ 绝大多数是仓库内文档(不在 package.json#files 里;随包发布的是 docs/driving.zh.md、docs/driving.md 与 docs/README.md——最后一枚是因为 npm 会打包任何名为 README 的文件)。在仓库里,各文档的位置、语言与口径见 docs/README.md(英文索引):设计方案记录、支撑 dsh.compatibility.dshReleases 声明的逐版本兼容性证据,以及已被取代、原文保留在 docs/archive/ 的历史结论。改动或发布前的验证步骤见 TESTING.md。

许可与出处

MIT,详见 LICENSE 与 NOTICE,NOTICE 精确列明:

- 改编/继承源码:DeepSeek Harness(MIT)的 dsh-bash-local(执行器机制)、dsh-fs-local(WslFileSystem 子类化)、shipped agent presets(变体生成读取/变换);
- 设计参考(未复制源码):dsh-bash-terminal(MIT,wsl argv/WSLENV 思路)、dsh-side-panel(BSD-3-Clause,Host 路由模式)、vpshub(MIT,路线图参考)。

发布/再分发时请保留 LICENSE 与 NOTICE。

致谢

特别感谢 dsh-deep-whale(DSH Web 鲸鱼娘皮肤系列 · 深海女仆工坊 maid-atelier,CC BY-NC-SA 4.0):鲸鱼娘皮肤插件为 DeepSeek Harness Web 界面带来了一整套可爱的皮肤,让 DSH 的日常使用更有温度。
预览分享卡片长什么样 ↓dsh-wsl-workspace-maintainers/dsh-wsl-workspace
分享 · DeepSeek Harness Hub
dsh-wsl-workspace-maintainers/dsh-wsl-workspace

DeepSeek Harness 的 WSL 工作区支持——无缝使用 WSL 工作区,不用在 WSL 里再装一个 dsh,装好这个插件就能在 GUI 里直接添加 WSL 工作区。

回到顶部