편집자 노트

UynajGI/dsh-ssh

SSH remote-execution plugin for DeepSeek Harness: ProxyJump chain, SFTP filesystem, subprocess and PTY over ssh2

이것은 DeepSeek Harness(DSH) 플러그인입니다. 이 사이트는 GitHub README, 설치 정보, 유지보수 상태, 공개 보안 시그널을 모아 보여줍니다.

dsh-ssh

English | 中文

npm 版本 许可证 Node 版本 CI 状态 GitHub Stars dsh-plugin

DeepSeek Harness 的 SSH 远程开发插件。把 Bash / 文件工具 / PTY 终端 / LSP 整体切到远程主机,支持跳板链(ProxyJump)、SFTP 上传下载、远程 subprocess 与交互终端。基于 ssh2

dsh-plugin 生态中第一个(截至 2026-08 唯一)SSH 远程开发插件。已通过真实跳板环境(双跳、密钥认证、SFTP 读写)端到端验证。

架构:本地大脑,远程手脚

你的本机 (deepseek-harness)                    远程主机
┌───────────────────────────────────┐   SSH   ┌──────────────────────┐
│ agent loop(模型编排、会话、日志) │◄────────►│  bash / 命令执行      │
│ LLM API 调用(本机直连,不出网)   │ exec    │  文件系统 (SFTP)      │
│ 凭证 / 配置 / 会话状态             │ pty     │  PTY 交互终端         │
│ ctx.subprocess → dsh-ssh          │ sftp    │  LSP / git / 编译     │
│ ctx.fs → dsh-ssh                  │         │                      │
└───────────────────────────────────┘         └──────────────────────┘

不需要把 dsh 部署到远程。 dsh-ssh 实现 deepseek-harness 两个能力缝隙(capability seam)的远程 provider——ctx.subprocess(远程进程)与 ctx.fs(远程文件)。框架里所有消费这两个缝隙的工具(bash、文件读写、终端、LSP、子代理进程)零改动自动切到远端执行:模型在本地思考,命令在远程跑,结果回传本地进模型上下文。

安装

npm i dsh-ssh

快速开始(cordis.yml)

一行挂载全部——共享连接 + 两个远程 provider:

- id: ssh-remote
  name: dsh-ssh
  config:
    host: 10.0.0.5            # 目标主机(必填)
    port: 22
    username: root            # 必填
    privateKey: ~/.ssh/id_ed25519   # 私钥文件路径,或直接写 PEM 内容
    # password: 'xxx'               # 密码认证(可与 privateKey 并存)
    # agent: 'pageant'              # Windows Pageant;Unix 填 SSH_AUTH_SOCK 路径
    cwd: /root/workspace           # 远程工作目录(必填,绝对 POSIX 路径)
    # --- 跳板链(可选,按序:先连第一个跳板,最后连目标)---
    jump:
      - host: 47.xx.xx.1
        # port: 22             # 缺省跟随目标机
        # username: ubuntu     # 缺省跟随目标机
        privateKey: ~/.ssh/id_ed25519
      # - host: 第二级跳板 ...
    # --- 连接与安全 ---
    readyTimeout: 20000        # 等价 ConnectTimeout(毫秒,默认 20s)
    keepaliveInterval: 0       # 等价 ServerAliveInterval(毫秒,0 禁用)
    keepaliveCountMax: 3       # 等价 ServerAliveCountMax
    strictHostKeyChecking: false   # true 时校验主机指纹
    knownHosts:                    # strictHostKeyChecking: true 时必填
      - 'SHA256:xxxxxxxx...'

聚合行等价于三个子路径行——只有需要单独组合 provider 时才分开挂载:

- id: ssh
  name: dsh-ssh/ssh            # ctx.ssh 连接(上面的 config)
- id: subprocess-ssh
  name: dsh-ssh/subprocess     # ctx.subprocess 远程 provider
- id: fs-ssh
  name: dsh-ssh/fs             # ctx.fs 远程 provider(SFTP)

界面上的「添加工作区」走 SSH(Web GUI)

Web 界面的添加工作区流程(对话首屏的工作区选择器、侧边栏的工作区浏览) 通过 ctx.directoryPicker 能力接缝浏览目录。dsh-ssh/picker 基于共享 SFTP 通道实现该接缝的 browse 能力,于是界面自带的选择工作区目录对话框直接 浏览远程目录、可在远程新建文件夹(SFTP mkdir),选中的远程路径会成为工作区 路径——dsh-ssh 的 provider 本来就能识别它。

  • Windows 主机上两种目录共用一个选择器:本机浏览完全不变,远程主机以 「远程主机」入口钉在本地主目录级别的顶部(名字可用 remoteLabel 自定义)。 路径路由与 resolveRemoteCwd 一致:盘符/UNC 路径指向本机磁盘,POSIX 绝对 路径指向远程主机。
  • POSIX 主机上选择器仅远程:任何绝对路径都是远程路径,本机文件系统与 远程共用同一套路径词汇,无法并存。

该接缝每个上下文只注册一个 ctx.directoryPicker;而补丁层的 name校验字段(名字对不上会跳过整条补丁,不是替换),所以要用 disabled 按 id 关掉 Web 包默认挂载的 @deepseek-ai/dsh-host-directory-picker-auto 行(它动态挂载的界面随之消失),再用自己的 id 插入 SSH 后端。在 Web profile($DSH_HOME/profiles/web/cordis.patch.yml)中:

# 关闭启动时自动选择的 picker(它动态挂载的界面一起消失)
- id: directory-picker
  name: '@deepseek-ai/dsh-host-directory-picker-auto'
  disabled: true

- insert:
    - id: ssh-remote
      name: dsh-ssh
      config: { ...同快速开始的 config... }

    # 提供 ctx.directoryPicker 的 SSH browse 后端
    - id: directory-picker-ssh
      name: dsh-ssh/picker
      config:
        # maxEntries: 1000            # 可选:单层目录行数上限(超出截断)
        # remoteLabel: '远程主机'      # 可选:钉住的远程入口名字(默认 Remote host user@host)

    # 官方自带的应用内目录浏览器(原本由 -auto 行自动挂载)
    - id: ui-directory-picker-browse
      name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'

在对话框里选中远程目录后,工作区路径就是远程路径(如 /home/user/project); 因为 ctx.fsctx.subprocess 都是 dsh-ssh 的远程 provider,该工作区里的 会话完全运行在远程主机上。

选择器配置(dsh-ssh/picker

字段类型默认说明
maxEntriesnumber1000单层目录行数上限(隐藏行计入;超出时 truncated 标记截断)
remoteLabelstringRemote host user@host本地主目录里远程入口的显示名(仅 Windows 主机)

钉住的远程入口打开的是远程主目录(取自远程登录环境,取不到时回退到配置的 远程 cwd)。POSIX 主机上选择器直接以远程主目录作为初始目录。

配置参考(dsh-ssh/ssh

字段类型默认说明
hoststring目标主机(必填)
portnumber22目标 SSH 端口
usernamestring登录用户(必填)
passwordstring密码认证
privateKeystringPEM 私钥内容或本地私钥文件路径
passphrasestring加密私钥的密码
agentstringssh-agent socket 路径或 pageant
jumpJumpConfig[][]跳板链,每级可独立配 port/username/认证
cwdstring远程工作目录(必填,绝对 POSIX 路径)
readyTimeoutnumber20000连接超时(毫秒)
keepaliveIntervalnumber0SSH 层保活间隔(毫秒)
keepaliveCountMaxnumber3保活失败判定次数
strictHostKeyCheckingbooleanfalse是否校验主机指纹
knownHostsstring[][]信任的主机指纹(SHA256:…)或原始 base64 公钥

OpenSSH ~/.ssh/config 映射

OpenSSH 配置dsh-ssh 字段
HostName / Port / Userhost / port / username
IdentityFile / IdentitiesOnlyprivateKey(路径或 PEM)
PasswordAuthenticationpassword
ForwardAgentagent
ProxyJump(逗号分隔多级)jump 数组(逐级)
ConnectTimeoutreadyTimeout
ServerAliveInterval / ServerAliveCountMaxkeepaliveInterval / keepaliveCountMax
StrictHostKeyChecking + UserKnownHostsFilestrictHostKeyChecking + knownHosts
RemoteCommand / RequestTTYspawnTerminal(PTY 由消费者请求)

能力

能力实现
跳板链jump 数组,多级跳板(direct-tcpip,等价 OpenSSH ProxyJump),每级独立认证
认证密码、私钥(PEM 内容或路径)、passphrase、ssh-agent / Pageant
近端上传SFTP 原子写(同目录临时文件 + rename,保留原 mode)
远端下载fs provider 全套:read / streamText(流式解码)/ readBytes(限量)/ listDir / stat / lstat
远程命令subprocess provider:collect(tail 保留 + 本地 spill 文件)、pipe、inherit、批量 stdin
交互终端PTY(spawnTerminal),输入输出 + TERM→KILL 清理
添加工作区 GUIdsh-ssh/picker:directory-picker 接缝的 browse 后端(走 SFTP)——界面添加工作区对话框直接浏览远程主机(Windows 上钉入口)
环境隔离远端登录环境 scrub(剔除 DSH_* 与凭据形变量)+ 显式 env 覆盖,env -i 启动
并发安全fs 写操作按 targetKey 串行化(防并发写同一文件)
主机校验strictHostKeyChecking + knownHosts(SHA256 指纹或原始公钥)

性能

  • 连接复用:三个 provider 共享一个 SSH 连接(含跳板链);SFTP 通道懒打开、复用,断线自动失效重建。
  • 环境缓存:远程登录环境只读一次并缓存(env -0 一次开销),每次 spawn 不再重复探测。
  • 输出本地 spill:collect 模式的内存 tail + 本地 spill 文件,与官方本地 provider 同语义。
  • 零轮询:spawn 一条 exec 通道完成命令(cd && exec env -i -- …),无轮询、无中间状态文件。

可靠性

  • 退出事实权威:exit code / signal 来自 SSH channel close 事件(真实远端进程事实)。
  • UTF-8 安全:exec 输出整段 buffer 后统一解码,SSH 分包不会损坏多字节字符。
  • 失败即报错:连接失败、认证失败、跳板失败、SFTP 错误都 fail loud,携带可读信息。
  • 清理兜底:插件卸载时终止全部活动进程/终端并关闭连接;临时文件(staging dir、spill)随写失败清理。

故障排查

症状原因与处理
All configured authentication methods failed认证配置错误:核对 username / privateKey 路径 / passphrase;私钥权限过宽(chmod 600)
Cannot read private keyprivateKey 不是 PEM 内容且文件路径不存在
跳板连接超时检查跳板 host/port 可达性、readyTimeout;跳板机的 User/认证单独核对
Host key verification failedstrictHostKeyChecking: trueknownHosts 未含目标指纹;用 ssh-keyscan 获取后填入
exec 返回 127远程命令不存在;确认远程 PATH(scrubbed 环境保留远端 PATH)
写文件报 FS_NOT_OBSERVED文件已存在且用了 createIfAbsent(防覆盖语义,非 bug)

已知限制

  • 远端 pid 不可见:SSH channel 不暴露远端 pid,SubprocessHandle.pid 恒为 -1
  • 终止不保证进程树terminate 通过 channel 信号(SIGTERM → grace → SIGKILL)作用于远程直接进程,不保证覆盖其子进程树(SSH 协议固有,与本地 provider 的进程组语义有差距)。
  • 终端前台进程组inspectForeground 返回 undefinedsignalForeground 不可用(SSH channel 无法解析远端前台进程组)。
  • 单连接不重连:连接断开后需重启插件。
  • POSIX 主机上选择器仅远程:任何绝对路径都是远程路径,本机文件系统无法与远程共用选择器(Windows 主机通过盘符/UNC 路由两者共存)。
  • streamText 仅文本:二进制文件抛 FS_NOT_TEXT(与官方 provider 一致)。

开发

npm i
npm run typecheck
npm run build       # 产出 lib/ —— harness 加载器实际导入的编译产物
  • Git hooks(husky):pre-commit 跑 typecheck;commit-msg 强制 Conventional Commitspre-push 拒绝与 package.json 版本不一致的版本 tag。
  • CI(GitHub Actions):每次 push/PR 跑 typecheck + 发布载荷检查。
  • 发布(GitHub Actions):推送版本 tag 自动发布 npm 并生成 GitHub Release:
npm version patch -m "chore(release): v%s"   # 改版本 + 提交 + 打 tag 一步完成
git push origin main && git push origin --tags

tag 必须与 package.jsonversion 字段一致(本地 hook 与 release workflow 双重强制)。发布使用仓库的 NPM_TOKEN secret(npm Automation token,CI 发布可绕过 2FA)。

License

MIT

REPOSITORY SIGNALS

보안 및 설치 증거

이 점수는 공개 저장소 메타데이터와 이 사이트에 등록된 설치 증거에만 기반하며, 코드 보안 감사와 다릅니다.

출처 추적 가능

공개 플러그인 카탈로그에서 왔으며, 공개 GitHub 저장소로 연결됩니다.

라이선스

저장소가 MIT 라이선스를 선언했습니다.

유지보수 활동

최근 180일 내 코드 업데이트가 있습니다.

설치 증거

재현 가능한 정확한 설치 메타데이터가 아직 등록되지 않았습니다. 저장소 설명에 따라 직접 확인하세요.

설치 라이프사이클 스크립트

prepare이(가) 감지되었습니다. 설치 전에 스크립트를 검토하세요.

주의 사항lifecycle-scripts