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

多执行面调用 Nesarf/dsh-shell-surfaces

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

调用 WSL、nushell、MSYS2 与真实控制台执行命令

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Nesarf/dsh-shell-surfaces
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件
查看扫描证据(共 32 处,展示前 31 条)
  • 写入本地文件
    lib/index.js:1017
    appendFileSync(file, `${new Date().toISOString()}\tdisposed=${sessionId}\towners=${owners.join(',') || '-'}\n`);
  • 读取本地文件
    lib/surfaces.js:91
    if (!existsSync(file)) return [];
  • 读取本地文件
    lib/surfaces.js:93
    text = readFileSync(file, 'utf8');
  • 读取本地文件
    lib/surfaces.js:743
    if (/[ /]/.test(exe)) return existsSync(exe);
  • 执行外部命令来自配置/变量
    lib/surfaces.js:754
    const result = spawnSync(probe.command, probe.args, { stdio: 'ignore', timeout: 5000 });
  • 读取本地文件
    lib/usage.js:36
    const parsed = JSON.parse(readFileSync(FILE, 'utf8'));
  • 写入本地文件
    lib/usage.js:54
    mkdirSync(dirname(FILE), { recursive: true });
  • 写入本地文件
    lib/usage.js:57
    writeFileSync(temporary, `${JSON.stringify(usage, null, 2)}\n`, 'utf8');
  • 写入本地文件
    lib/usage.js:58
    renameSync(temporary, FILE);
  • 写入本地文件
    lib/runner.js:30
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    lib/runner.js:51
    writeFileSync(file, text, 'utf8');
  • 执行外部命令来自配置/变量
    lib/runner.js:76
    child = spawn(argv[0], argv.slice(1), {
  • 执行外部命令来自配置/变量
    lib/runner.js:247
    const helper = spawn(argv[0], argv.slice(1), { stdio: 'ignore', windowsHide: true });
  • 写入本地文件
    lib/runner.js:322
    writeFileSync(file, command, 'utf8');
  • 执行外部命令来自配置/变量
    lib/runner.js:444
    const spawned = spawnSync(spec.exe, argv, { encoding: 'utf8', timeout: 15000, windowsHide: true });
  • 执行外部命令来自配置/变量
    lib/runner.js:469
    const read = spawnSync(spec.exe, ['cli', 'get-text', '--pane-id', paneId], {
  • 执行外部命令来自配置/变量
    lib/runner.js:481
    spawnSync(spec.exe, ['cli', 'kill-pane', '--pane-id', paneId], { encoding: 'utf8', timeout: 10000, windowsHide: true });
  • 执行外部命令来自配置/变量
    lib/runner.js:536
    const child = spawn(argv[0], argv.slice(1), { windowsHide: true, detached: true, stdio: 'ignore' });
  • 读取本地文件
    lib/runner.js:556
    if (existsSync(out)) {
  • 读取本地文件
    lib/runner.js:558
    const parsed = JSON.parse(readFileSync(out, 'utf8'));
  • 写入本地文件
    lib/runner.js:619
    mkdirSync(dirname(jobFile), { recursive: true });
  • 写入本地文件
    lib/runner.js:620
    writeFileSync(jobFile, JSON.stringify({ id, command: options.command, timeoutMs: options.timeoutMs }), 'utf8');
  • 删除本地文件
    lib/runner.js:635
    unlinkSync(resultFile);
  • 读取本地文件
    lib/runner.js:659
    if (existsSync(resultFile)) {
  • 读取本地文件
    lib/runner.js:661
    const parsed = JSON.parse(readFileSync(resultFile, 'utf8'));
  • 删除本地文件
    lib/runner.js:754
    unlinkSync(file);
  • 执行外部命令来自配置/变量
    lib/sessions.js:36
    const child = spawn(argv[0], argv.slice(1), {
  • 读取本地文件
    lib/discover.js:60
    return readdirSync(dir);
  • 执行外部命令固定命令
    lib/discover.js:141
    const result = spawnSync('wsl.exe', ['-l', '-q'], { encoding: 'buffer', timeout: 4000, windowsHide: true });
  • 执行外部命令固定命令
    lib/discover.js:172
    const result = spawnSync('wsl.exe', ['-d', distro, '--', 'bash', '-l', '-s'], {
  • 读取本地文件
    lib/discover.js:235
    if (!existsSync(path)) continue;
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

机器的非 pwsh 执行面统一成一个 DSH 工具:WSL Linux 用户态、nushell 结构化管道、MSYS2 用户态(bash/zsh/fish/brush),以及给交互式程序用的真实控制台。

README

由 DeepSeek 最新模型翻译生成
dsh-shell-surfaces https://github.com/Nesarf/dsh-shell-surfaces —— 一个 DSH 插件,用 dsh plugin --profile <profile> add github:Nesarf/dsh-shell-surfaces 安装。 一个 DSH 工具,用来调用这台机器上 pwsh 之外的执行面:WSL Linux 用户态、nushell 的结构化管道、MSYS2 用户态(bash / zsh / fish / brush),以及给交互式程序用的真实控制台。 它之所以存在,是因为只写在纸上的规则不算能力。这些执行面的选择规则已经记录了一段时间,但会话想照着做,只能拼一条命令字符串交给 pwsh,然后祈祷引号能活着走完全程。现在它们可以直接调用了。 shell_surfaces(action="surfaces") what exists here, and where each was looked for shell_surfaces(action="run", surface="wsl", command="grep -P '(?
展开完整文档(README 剩余 1 节)
dsh-shell-surfaces

https://github.com/Nesarf/dsh-shell-surfaces —— 一个 DSH 插件,用 dsh plugin --profile <profile> add github:Nesarf/dsh-shell-surfaces 安装。

一个 DSH 工具,用来调用这台机器上 pwsh 之外的执行面:WSL Linux 用户态、nushell 的结构化管道、MSYS2 用户态(bash / zsh / fish / brush),以及给交互式程序用的真实控制台。

它之所以存在,是因为只写在纸上的规则不算能力。这些执行面的选择规则已经记录了一段时间,但会话想照着做,只能拼一条命令字符串交给 pwsh,然后祈祷引号能活着走完全程。现在它们可以直接调用了。

shell_surfaces(action="surfaces") what exists here, and where each was looked for
shell_surfaces(action="run", surface="wsl", command="grep -P '(?<=Linux )[0-9.]+' /proc/version")
shell_surfaces(action="run", surface="nu", command="open data.json | where n > 3 | to json")
shell_surfaces(action="run", surface="msys", command="pacman -Q | wc -l")
shell_surfaces(action="run", surface="pty", command="powershell -File install.ps1", send=["y\n"])

为什么是一个工具,而不是七个

每个注册的工具都要花掉一部分上下文来写自己的描述。七个工具会把同一件事说七遍。选择规则——结构化管道用哪个执行面、只有 Linux 才有的工具用哪个、等待输入的程序用哪个——都写在这一个描述里,会话在挑选的那一刻正好读到。

它不是什么

* 不注入提示词,也不挂消息钩子。 它只注册一个工具,别的什么都不做,所以它不可能成为那个写出畸形消息的插件。
* 不是终端模拟器。 终端模拟器给人画文字,对子进程没有任何用处。唯一需要控制台的那个执行面直接跟 ConPTY 对话。

参数列表背后的事实

下面每一条都在本机实测过,每一条都决定了 argv 长什么样:

| 事实 | 后果 |
| 命令经过 shell 传递时可能被重新解析两次 | 命令以单个 argv 元素传递;脚本文件优先于内联字符串 |
| MSYS2 的 /tmp 跟随 TMPDIR,而本机把它作为 Windows 路径传下去 | msys / zsh / fish 以 login shell 运行,这样环境会被规范化 |
| brush 旁边没有 Cygwin 运行时,所以 /tmp 就是 <drive>:\tmp | brush 不加 login 标志,也不做 POSIX 路径假设;它的 PATH 需要 usr\bin 和 ucrt64\bin,否则每条外部命令都会失败,而 shell 却报告版本正常 |
| PtyProcess.read 会阻塞 | 读取放在自己的线程上;一个同时决定何时停止的读取循环,会阻塞掉那个本该让它停下来的检查 |
| 控制台要的是 Enter,也就是回车符 | send 会把结尾的换行(真实的、转义的或字面的)规范成 CR,只给一个裸答案时则补上一个 |
| wsl.exe 有时会打印结果,然后拒绝退出 | 有输出的超时和没有输出的超时,上报方式不同;wsl.exe --shutdown 是重置手段(实测:冷实例 58 秒才应答,重置后同样的调用 8 秒,热态 0.13 秒) |

目录结构

| 路径 | 说明 |
| lib/index.js | 工具本体:一个 shell_surfaces,动作有 run 和 surfaces |
| lib/surfaces.js | 表:每个 surface 的路径、参数、环境,以及不可用的原因 |
| lib/runner.js | 构造 argv、带溢出的有界输出、超时分类 |
| scripts/pty_runner.py | 控制台:pywinpty、读取线程、剥离 ANSI、输出一份 JSON 报告 |
| scripts/live-check.mjs | 每个 surface 各跑一条真实命令,走插件自己的代码路径 |
| scripts/pty-gate.ps1 | 交互式夹具:为 pty 场景准备一个 Read-Host 提示 |
| test/surfaces-test.mjs | 表、argv 形状、工具契约 |

验证

node test/surfaces-test.mjs # 55 checks, no host needed
node scripts/live-check.mjs # one real command per surface on this machine
node scripts/live-check.mjs wsl nu # or only some of them

每个 surface 也都真跑过(2026-10-15):wsl 返回了内核和 /proc 计数,nu 通过结构化管道返回了一个数字,msys 返回了它的 bash 版本和包数量,zsh 返回了参数展开结果,fish 返回了算术结果,brush 返回了一个转成大写的字符串,pty 用 y 应答了 Read-Host 提示,并打印出它走的分支。

如实说明边界

* Windows 优先。 它认识的 surface 就是这类机器上的:WSL 发行版、MSYS2、nushell、ConPTY。在 POSIX 上,平台本身只带一个 shell,所以这里的大部分东西在那边用不上。
* 它自身不做隔离:命令以 harness 进程的权限运行。文件沙箱和审批策略仍然作用于整个进程树。
* MSYS 系输出按 UTF-8 处理(设置了 LANG=C.UTF-8)。通过 surface 调起的 Windows 程序仍可能用 OEM 代码页应答。
* 如果 MSYS2 不在 D:\msys64,MSYS2 系 surface 需要 DSH_MSYS_ROOT;如果 nu.exe 不在 %LOCALAPPDATA%\Programs\nu\bin,nushell 需要 DSH_NU;pty surface 需要 PATH 上有 python,并且装好 pywinpty。
预览分享卡片长什么样 ↓Nesarf/dsh-shell-surfaces
分享 · DeepSeek Harness Hub
Nesarf/dsh-shell-surfaces

调用 WSL、nushell、MSYS2 与真实控制台执行命令

回到顶部