@lemoncat7/dsh-ssh
v1.3.3
Published
Secure SSH, FTP/FTPS/SFTP file transfer, browser terminals, proxies, port forwarding, and session-scoped AI access for DeepSeek Harness
Maintainers
Readme
dsh-ssh
面向 DeepSeek Harness 的远端工作插件。它把 SSH 会话管理、浏览器终端、FTP/FTPS/SFTP 文件传输、代理、端口转发和 AI 会话授权放在同一个 DSH 工作区里。
浏览器 UI、服务端运行时、会话授权边界和终端生命周期的职责说明见 ARCHITECTURE.md。
安装
dsh plugin --profile web add @lemoncat7/dsh-ssh@latest安装或升级后重启对应的 DSH Profile。桌面端与 Docker 部署使用相同的插件包。
1.2.0 更新
- 浏览器端按活动侧栏、连接编辑、主机树、SFTP、终端和共享 UI 分层;弹窗统一支持焦点锁定、Escape 关闭和焦点归还。
- SSH 活动与端口转发状态改为完成后调度,后台页面自动降频,避免慢请求造成轮询重叠。
- 新建与编辑连接按「连接信息 / 身份认证 / 连接路径」分组,并统一按钮、焦点、按压反馈和减少动态效果。
- SSH 面板改为「主机 / 固定项目目录」树,主工作区同时展示左侧终端和右侧 SFTP。
- 主机前的授权选择与底部「当前会话权限」共同控制 AI 可见范围,不再使用独立的会话注入检查器。
- 每台主机可以保存多个固定远端目录;当前会话最多显式绑定其中一个,终端、SFTP 和 AI 命令共享该工作目录。选中后目录项会明确显示当前会话已固定到该路径。
- 从固定远端目录新建会话时,会先选择会话归属的 DSH 本地项目;默认使用当前项目,其次使用最近项目,并明确区分本地项目路径与 SSH 远端工作目录。
- 固定目录后的新增按钮通过 DSH 官方 Workspace 接口创建并直接打开新会话,同时写入对应主机、目录与权限绑定;成功后自动关闭 SSH 管理面板,目录下不展示历史会话清单。
- 撤销主机访问会关闭该会话在对应主机上的 AI 终端;降级为仅命令会关闭全部交互终端。
能力
- SSH 密码、私钥和 SSH Agent 认证
- HTTP CONNECT、SOCKS5 和 SSH 跳板代理
- 本地转发(
-L)、远程转发(-R)和动态 SOCKS5 转发(-D) - 浏览器交互终端,支持输入、增量输出、窗口尺寸同步和主动断开
- 当前会话右侧栏支持多个 AI 终端切换与键盘输入
- AI 调用
ssh_terminal_open创建终端时,当前会话会自动展开 SSH 右栏并切换到新终端 - SFTP 文件浏览、文本/图片/PDF 预览和流式下载;支持拖放上传、目录内移动、行末删除,并可从内嵌预览打开独立放大窗口
- 独立「文件传输」工作区,支持 2~4 个 FTP、FTPS 或 SFTP 窗格与多个任务页
- FTP、显式 FTPS、隐式 FTPS;FTP 控制连接与每条被动数据连接统一支持 HTTP CONNECT 和 SOCKS5 代理
- FTP/FTPS/SFTP 跨协议流式互传,不把完整文件暂存到 DSH 磁盘;支持多文件、递归目录、冲突策略、进度、取消和失败状态
- 有序多主机跳板链,兼容已有单跳板配置
- 独立密钥库集中保存常用用户名、密码和私钥,SSH 连接仅保存引用
- 独立代理库集中保存 HTTP CONNECT 与 SOCKS5 代理,多台主机可复用同一连接路径
- 按 DSH 会话授权连接;未勾选的主机对 AI 完全不可见
- 保存固定远端项目目录,并从目录直接创建和打开新 DSH 会话
- AI 可执行一次性命令,或打开、读取、操作和关闭独立的交互终端
- 左侧远端区域提供右侧栏开关;目录页使用真实 SFTP 浏览远端文件,终端页使用单一终端画面观察并操作 AI 终端
- 插件服务与 Web 客户端兼容 Windows、macOS 和 Linux,不依赖本机
ssh或sftp命令 - 首次连接主机指纹确认、输出上限、命令超时和公开端口绑定保护
界面
侧栏入口名为「远端」,通过官方 sidebar.footer.action 注册。浏览器兼容层只把这个入口锚定到官方 Workspace 区域上方;它不会替换 sidebar.workspaces,锚点失效时会自动留在官方 Footer。远端标题右侧按钮与折叠栏图标负责开关 SSH 右侧栏;展开后的第一项「SSH 面板」进入完整管理工作区,它下面只显示当前 DSH 会话已授权的 SSH 连接。点击其中一台主机会打开右侧栏并直接切换到该远端。切换到其他会话会自动退出管理工作区,也可以使用工作区左上角的返回按钮。
管理工作区分成两部分:
- 左侧远端树:点击主机只负责选中并展开固定目录,同时自动收起其他主机;右侧独立的「挂载 / 卸载」按钮决定当前 DSH 会话能否访问该主机,因此浏览终端不会意外改变 AI 权限。点击目录整行即可固定,再次点击已固定目录即可取消;每台主机最多固定一个目录。目录后的新增按钮直接创建并打开新会话。底部统一设置「仅命令 / 终端控制」与执行前确认。
- 右侧工作区:默认把终端与当前主机或项目目录的 SFTP 左右并排展示,不需要在两个页面之间切换;SFTP 会等终端首次连接成功后再读取远端目录。拖动两栏之间的分隔线可以调整 SFTP 宽度,双击恢复默认,比例会保存在当前浏览器。另可进入端口转发、密钥库、代理库和设置。SFTP 支持浏览、预览、下载、行末删除,以及拖放或文件选择器触发的多文件顺序上传;文件拖进同一主机的目录时使用原生重命名完成移动,跨端点拖放才创建复制任务,拖回原目录不会产生任务。同名文件逐项确认跳过或覆盖,单文件上限为 512 MB。内嵌文件预览可通过下载旁的放大按钮打开独立预览窗口。
「文件传输」是独立工作区页签。每个任务页默认双窗格,可切换为 2~4 栏。每个窗格先显示统一的 SFTP、FTP、FTPS 连接列表,单击连接后才读取远端目录,并可随时返回连接列表。文件既可以拖到另一窗格或具体目录,也可以选中后使用“传送到下一栏”,因此键盘和触控环境不依赖拖拽。同一端点内拖入目录是移动,跨端点拖放是复制;目录不能移动到自身或子目录。浏览窗格复用自己的协议会话,传输任务使用独立连接,避免大文件占用目录浏览的控制通道;任务完成、失败或取消后只刷新对应目标目录,及时呈现完整或部分写入结果。每个文件或目录行末均可直接打开删除确认,目录采用有深度和数量上限的递归删除,且不会跟随符号链接。关闭页面不会中断服务端任务;DSH 进程停止时会统一取消并释放两端连接。
工作区使用插件内职责单一的自适应壳布局。宽容器使用双栏;窄于 820px 时远端树切换为左侧抽屉,窄于 520px 时进一步压缩 SFTP 次要信息和操作密度。响应式判断使用容器查询,因此手机、桌面分屏和 DSH 窄面板采用同一套行为。
SSH 插件不会向聊天标题栏添加按钮。右侧栏统一从左侧「远端」区域打开;当前会话尚未授权主机时会显示引导。仅执行命令权限显示 SFTP 目录;终端控制权限同时显示 SFTP 目录和 AI 终端。多个终端通过紧凑标签切换,运行中的终端支持键盘输入。进入 SFTP 子目录时,该目录会同步成为后续 ssh_exec 与新终端的工作目录。
新建和编辑连接的表单内提供「测试连接」。测试直接使用尚未保存的表单内容,可验证密钥库凭据、HTTP/SOCKS5 代理和有序跳板链;首次连接的主机指纹也在表单内确认。测试过程不会临时创建 Profile,也不会把凭据写入配置文件。
AI 工具
| 工具 | 用途 |
| --- | --- |
| ssh_list | 只列出当前 DSH 会话已授权的连接 |
| ssh_set_cwd | 设置并验证当前会话在指定主机上的工作目录 |
| ssh_exec | 执行一次性远端命令 |
| ssh_terminal_open | 打开当前 Agent 独占的 SSH 终端 |
| ssh_terminal_send | 向终端发送文本并等待输出稳定 |
| ssh_terminal_read | 分页读取终端回滚缓冲区 |
| ssh_terminal_signal | 发送允许的 POSIX 信号 |
| ssh_terminal_close | 关闭终端 |
| ssh_forward_list | 列出已授权连接的转发规则 |
| ssh_forward_start | 启动已有转发规则 |
| ssh_forward_stop | 停止已有转发规则 |
| file_endpoint_list | 只列出当前 DSH 会话明确授权的 FTP/FTPS/SFTP 端点 |
| file_directory_list | 浏览已授权端点中的远端目录 |
| file_transfer_start | 启动端点之间的异步流式文件或目录传输 |
| file_transfer_status | 查询当前会话拥有的传输任务进度 |
| file_transfer_cancel | 取消当前会话拥有的传输任务 |
工具始终从 exec.agent.session.id 解析会话授权关系。SSH 命令权限与文件权限独立:文件端点必须单独授权,并选择“仅浏览”或“允许跨端传输”。模型不能通过参数绕过授权,也不能枚举其他 DSH 会话的主机、文件端点、终端或传输任务。覆盖目标文件不会被默认推断;file_transfer_start 默认使用 fail 冲突策略。
「执行前确认」依赖 DSH 当前会话的审批策略:Workspace Write 的 Ask 策略会显示确认;Full Access 使用 Never 策略,不显示确认,并会直接拒绝被 SSH 插件标记为需要审批的操作。若希望在 Full Access 下直接执行 SSH,请关闭该开关。
凭据与安全
普通连接资料保存在 statePath 指定的原子 JSON 文件中。以下敏感字段只写入 DSH ctx.credentials 的 dsh-ssh/<profile-id> Grant Record:
- SSH 密码
- 私钥
- 私钥口令
- 代理密码
- FTP 密码
密钥库条目使用独立的 dsh-ssh-vault/<credential-id> Grant Record。SSH Profile 只保存 credentialId,不会复制或读回密钥库中的明文。仍被连接引用的密钥库条目不能删除。
代理库条目保存在状态文件中,代理密码使用独立的 dsh-ssh-proxy/<proxy-id> Grant Record。SSH Profile 选择常用代理后只保存 proxyId;仍被主机引用的代理不能删除。原有内联 HTTP/SOCKS5 配置继续兼容。
FTP Profile 与 SSH Profile 分开保存。FTP Profile 自有密码使用 dsh-ftp/<profile-id> Grant Record,也可以引用已有的密码型密钥库条目。普通 FTP 会在界面中明确标记为未加密;FTPS 默认校验证书。FTP 仅使用被动模式,且忽略 PASV 响应中用于改变目标主机的地址,避免 FTP Bounce/SSRF。
管理 API 和 Web UI只返回是否配置以及字段名,不返回任何凭据值。默认拒绝非回环地址的端口监听;若确实需要监听 0.0.0.0,必须在「远端 → 设置」中显式开启。
首次连接会拒绝未知主机密钥并展示 SHA-256 指纹。用户确认后才把指纹写入 Profile,后续连接严格比对。
DSH 配置
安装包后,bundle 会插入默认配置:
- id: ssh
name: '@lemoncat7/dsh-ssh'
config:
statePath: !!js dshHomePath('ssh/state.json')
exposeWeb: true
apiPrefix: /ssh-local/v1
defaultCommandTimeoutMs: 30000
maxOutputChars: 32000
allowPublicBind: false插件依赖当前 DSH 的 credentials 和 tools 服务。浏览器管理还需要 Web Profile 的 webServer;浏览器终端和 AI 终端由插件内部按所有者隔离管理,不依赖 Host Root 中不存在的 terminals 服务。
开发与打包
npm install
npm test
npm pack --pack-destination dist运行时要求 Node.js 22.19+ 或 Node.js 24+,并与 DSH 0.1.1-rc.2 接口对齐。
终端隔离说明
浏览器终端和 AI 终端共享同一个连接 Profile 与凭据,但不是同一个终端实例:
- 浏览器终端由插件的同源管理 API 持有,页面关闭或空闲超时后清理。
- AI 终端由插件按
sessionId分区持有。Web Profile 的官方 Terminal 服务位于每个 Agent Preset 的私有 Realm,主机插件不能跨 Realm 注册 Backend,因此插件使用相同的 owner-scoped 规则实现 SSH 终端隔离,并在插件卸载或进程退出时统一清理。 - 浏览器终端与右侧「SSH 活动」共用独立终端传输层。输出优先使用支持断线续传的 SSE 长连接实时推送,只有浏览器或反向代理不支持流式响应时才退回 cursor 增量轮询。
- 键盘输入按序号并发发送,由服务端按序写入 TTY;临时网络失败会使用同一序号重试,既减少 HTTP 往返造成的排队延迟,也避免并发输入乱序。
- AI 终端创建事件通过按会话隔离的 SSE 事件流通知 Web 客户端。首次订阅不重放旧终端,断线重连会按事件游标补发遗漏事件。
这种边界避免浏览器用户和模型同时争用同一个 TTY,也遵守 DSH 不允许跨 Agent 共享终端的所有权规则。
会话授权模式会直接约束模型可见工具:选择「仅命令」时隐藏交互终端工具;选择「终端控制」时隐藏 ssh_exec,远端命令必须经 ssh_terminal_open / ssh_terminal_send 执行,因此输入和输出会显示在右侧 SSH 活动中。执行层会再次校验权限,不能通过直接调用绕过。
