前言¶
在 DeepSeek Harness 中开发智能体时,经常需要在 Web UI 里操作远端服务器。常见的做法是手动 SSH 登录,但这会中断当前会话,而且文件系统不透明。ya-b/dsh-ssh-workspace 插件提供了另一种方式:在 Web UI 里先选机器,再选目录,打开后的工作区其文件读写、终端和命令执行全部发生在远端。
这是什么¶
ya-b/dsh-ssh-workspace 是 DeepSeek Harness 的远程工作区插件,由维护者 ya-b 开发。它把 SSH 变成数据管道,远端常驻一个 workspace daemon,客户端断线不会中断远端命令;重连后会按偏移续取输出与终态。写操作经过 opId 幂等门,不会重放副作用。
核心功能¶
远端执行¶
在 Web UI 中打开某个目录后,该目录成为远程工作区,会话中的 pwd、文件读写、终端和命令都在远端执行。SSH 只是数据管道,远端 daemon 负责处理所有操作。
模型侧工具¶
远程工作区的 agent 会额外注册 remote_read、remote_write、remote_edit、remote_read_image 和 remote_bash,并注入“本项目是远程项目”的系统提示词。这些工具复用官方工具的参数、审批、输出渲染与超时。文件工具路径和 remote_bash 的 workdir 只接受当前远程工作区内的路径;相对路径按远程当前目录解析。
断线续传¶
远端 daemon 独立于客户端连接,客户端断线不会中断远端命令。重连后按偏移续取输出与终态。
幂等保证¶
写操作经过 opId 幂等门,不会重放副作用。这保证了文件修改不会因为重试而重复执行。
环境要求¶
| 位置 | 要求 |
|---|---|
| 宿主 | Node ≥ 20;dsh 0.2.1-rc.2;可用的 OpenSSH 客户端;可读的 ~/.ssh/config |
| 远端 | Linux x64;Node ≥ 20;POSIX sh;tar;OpenSSH 服务端 |
| 认证 | 密钥或 ssh-agent 等非交互登录;不支持交互式密码 |
| SSH | OpenSSH ≥ 8.2(客户端需支持 ControlMaster / -O forward) |
| 终端 | 归档内含 linux-x64 的 node-pty;其他平台缺少 node-pty 时仅终端不可用,文件与命令正常 |
安装与启用¶
从 Git 安装(推荐):
dsh plugin --profile web add https://github.com/ya-b/dsh-ssh-workspace
这条命令会:
- 把
@dsh-ssh-workspace/bundle安装进webprofile; - 把该 bundle 追加到 profile 的
dsh.profile.bundles; - 按包内
cordis.patch.yml装入ssh-workspace-mount、api-ssh-workspace、ui-ssh-workspace三行。
安装后重启实例生效。
配置¶
SSH 主机¶
机器列表来自本机 + ~/.ssh/config 中所有具体的 Host 别名(递归跟随 Include;通配符和取反规则会被跳过,因为它们不是具体机器)。普通 OpenSSH 配置全部有效:HostName、Port、User、IdentityFile、ProxyJump、ProxyCommand 等。
# ~/.ssh/config
Host devbox
HostName 192.0.2.10
User root
Port 22
IdentityFile ~/.ssh/id_ed25519
Include ~/.ssh/config.d/*
要求 ssh devbox true 能在不询问密码的情况下成功(密钥、agent 或 ControlMaster)。修改 ~/.ssh/config 后刷新浏览器页面,主机列表会重新枚举。
不想在弹窗中显示某些别名时,用 api-ssh-workspace 的 hiddenHosts 隐藏:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: api-ssh-workspace
config:
hiddenHosts:
- automation
- ci-runner
插件参数¶
ssh-workspace-mount 的可配置项:
| 项 | 默认值 | 说明 |
|---|---|---|
daemonDir |
~/.dsh-workspace |
远端 daemon 状态目录;~ 由远端 shell 展开 |
node |
node |
远端 Node 可执行文件;非交互 SSH 的 PATH 常找不到 nvm 的 Node,建议写绝对路径,如 /usr/local/bin/node |
install |
true |
首次使用某主机时投递内置 daemon 归档;按 (host, daemonDir, 归档摘要) 去重 |
autoUpgrade |
true |
连接时若运行中的 daemon 加载的 bundle 与已安装的 workspaced.js 摘要不一致,且无子进程在跑,就自动重启它;忙则保留旧版,下次连接再试 |
requestTimeoutMs |
30000 |
安装、准入、打开等管理请求的超时 |
requestTimeoutRetries |
5 |
管理超时后对可重放操作(读或带 opId 的写)的额外重试次数;调用方取消不重试 |
requestTimeoutRetryBaseMs |
10000 |
超时重试的首次等待,逐次翻倍(10s → 20s → 40s …),上限 2147483647 |
reconnectBaseMs |
250 |
SSH 链路重连的退避初始值 |
reconnectMaxMs |
15000 |
重连退避上限 |
controlDir |
临时目录 | 本地 SSH ControlMaster socket 目录;不填则使用 /tmp/dsh-ws-* |
statePath |
${DSH_HOME:-~/.dsh}/storages/ssh-workspaces.json |
本地工作区归属侧映射路径 |
api-ssh-workspace 的可配置项:
| 项 | 默认值 | 说明 |
|---|---|---|
probeTimeoutMs |
8000 |
主机连通性预检的单次超时 |
hiddenHosts |
[] |
不显示在机器列表中的 SSH 别名 |
prefix |
/ssh-workspace |
HTTP 路由前缀;浏览器半固定使用 /ssh-workspace,不要修改 |
同一 id 的 config 会整体替换 bundle 中的配置,未写的字段使用 schema 默认值,建议把需要的项写全。
远端 daemon¶
首次在某个 (主机, daemonDir) 上打开目录时,插件会通过 SSH 把归档解压到远端 daemonDir,并执行远端 Node 版本检查。之后:
- daemon 状态在远端
~/.dsh-workspace:sock、pid、lock、running.digest、config.json、streams/、daemon.log; config.json的roots是允许作为工作区打开的目录白名单,由客户端在打开前写入,保留最近 64 条;- daemon 已运行时不重复启动;
- daemon 启动时把自己的 bundle 摘要写进
running.digest;连接时若与已安装的workspaced.js不一致,且它没有子进程在跑,bootstrap 会优雅重启它以运行新代码(autoUpgrade: false可关闭);旧版本 daemon 没有该文件,首次连接时同样会被升级; - 远端 Node 缺失或过旧会在建连阶段报
remote workspace daemon preflight failed: <node> is not a usable Node >= 20。
典型用法¶
- 打开 Web UI,侧边栏「添加工作区(+)」→「添加工作区…」。
- 在弹窗顶部的主机下拉中选择本机或某个 SSH 主机。
- 选择目录(可「上级」回溯、开关隐藏文件),点「打开」。
- 远端工作区默认命名为
<目录名>@<主机别名>;会话中的pwd、文件读写、终端和命令均在远端执行。
升级与卸载¶
升级 bundle:
dsh plugin --profile web update @dsh-ssh-workspace/bundle
# 或重新安装并锁定提交
dsh plugin --profile web add 'https://github.com/ya-b/dsh-ssh-workspace#<sha>'
重启 dsh 实例后新代码生效。远端已运行的 daemon 不会自动换代码;必要时杀掉远端 daemon 进程,让下一次使用重新投递并启动:
ssh <host> 'kill "$(cat ~/.dsh-workspace/pid)" 2>/dev/null || true'
卸载:
dsh plugin --profile web remove @dsh-ssh-workspace/bundle
卸载只移除依赖、bundle 选择和它带来的三行 patch,不会删除本地侧映射或远端 ~/.dsh-workspace。需要彻底清理时,先停掉远端 daemon,再删除数据:
rm -f "${DSH_HOME:-$HOME/.dsh}/storages/ssh-workspaces.json"
ssh <host> 'kill "$(cat ~/.dsh-workspace/pid)" 2>/dev/null || true; rm -rf ~/.dsh-workspace'
适用场景与注意¶
这个插件适合需要在 Web UI 中操作远端服务器的智能体开发场景。它要求宿主和远端都有 Node ≥ 20,远端必须是 Linux x64(其他平台仅终端不可用,文件与命令正常)。认证必须是非交互式的(密钥或 ssh-agent),不支持交互式密码。
插件以当前 dsh 进程权限运行,安装前应检查源码与许可证(MIT)。
结尾¶
ya-b/dsh-ssh-workspace 让 DeepSeek Harness 的智能体可以在 Web UI 中透明地操作远端机器,文件系统、终端和命令执行都在远端完成,断线不中断,写操作幂等。需要更细的构建、回滚与运维说明,可以查看 GitHub 仓库的 INSTALL.md、PLAN.md 和 PROGRESS.md。
- 目录页:https://www.skillhub.cn/plugins/ya-b/dsh-ssh-workspace
- GitHub:https://github.com/ya-b/dsh-ssh-workspace