@zhangfengshun/dsh-remote-ssh
English | 中文
类 VSCode Remote-SSH 的 DSH 插件:通过 SSH 连接远程超算 / 服务器,在 DSH 内置「文件」「终端」页签中直接操作远程文件和终端。
目录:功能 · 截图 · 安装 · 使用 · 代码示例 · 模型工具 · 命令超时与恢复 · 兼容性 · 故障排查 · 原理 · 缓存与一致性 · 许可证
功能
| 能力 | 说明 |
|---|
| 🔌 SSH 连接 | 密钥 / 密码认证,ProxyJump 跳板机,~/.ssh/config 一键导入 |
| 📂 远程文件 | 内置「文件」页签直接 SSH 读写远程文件,无需同步 |
| 💻 远程终端 | 内置「终端」页签自动检测远程工作区,SSH 交互式终端,落在工作区对应的远程目录(与 VSCode Remote-SSH 一致) |
| 🌐 远程工作区 | 选择远程目录创建原生工作区,一键进入远程环境 |
| 🤖 模型工具 | 13 个 remote_ssh_* 工具,会话感知免填连接参数;命令级超时 + remote_ssh_kill 兜底恢复 |
🗂️ @ 引用补全 | 远程工作区会话里 @ 补全列远端文件(git 仓库走 git ls-files,实测 0.1s;非 git 用有界 find;索引缓存 + 900ms 查询预算,超时降级不卡输入框) |
| ⚡ 打开提速 | 单往返合并读 + raw 文本快路径 + 结果缓存(LRU + 5s TTL):首开 ≈1.31×,TTL 内重复打开 0 往返,过期复验 ≈5×(真实超算实测);remote_ssh_exec 连接复用 ≈15× |
截图
设置 → 远程连接:连接配置(密钥 / 密码 / ProxyJump 跳板机)· 连接测试 · 从 ~/.ssh/config 一键导入
设置:远程连接
内置「文件」页签:直接浏览远程主机文件(右侧文件树即远程目录,编辑保存直写远程)
内置文件页签直接浏览远程文件
内置「终端」页签:自动 SSH 到远程超算(图为 SLURM 作业调度环境);左侧会话即模型免填调用 remote_ssh_* 工具
内置终端页签自动 SSH 远程超算
安装
前置要求
| 项 | 要求 |
|---|
| DSH | ≥ 0.1.5-rc.1,含 0.2.0-rc.1(最新官方线;0.1.2 稳定线请用 v0.18.1 时代的插件版本)。插件声明的 peer 范围显式列出每条已验证版本线,因此新版 harness 的兼容性门(见安装)不会拦住它 |
| dsh-better-sidebar | ≥ 0.15(本插件依赖其 /sidebar/api/ 文件 API 与 0.24 的 open.external「打开方式」端点;0.23 起文件树改为批量 fs.trees 并新增 fs.mkdir,2.4.18 起已适配)。可以只装本插件:2.4.18 起未安装它时不再卡住 web boot(见下方说明),但「文件」页签的远程读写与编辑器页签也随之不可用 |
| 本机 SSH 客户端 | Windows:系统自带 OpenSSH(%SystemRoot%\System32\OpenSSH\ssh.exe);Linux/macOS:openssh-client |
| 远程主机 | 任意标准 sshd(超算 / 服务器 / 跳板机均可) |
一条命令安装(无需 token、API Key 或额外配置):
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@2.4.44
安装后重启 DSH。@zhangfengshun/dsh-remote-ssh 必须在 bundles 列表中排在 dsh-better-sidebar 之后。
只装本插件、不装 dsh-better-sidebar(或它加载失败)会怎样? 2.4.18 起可以正常启动:客户端只把内核服务(slots/locale)声明为硬依赖,better-sidebar 改走子 fiber 软注入 —— 装了才注册远程文件编辑器页签,没装就静默降级。此时能用:设置页「远程连接」(SSH 连接与远程工作区)、模型工具(remote_ssh_*)、远程终端集成、@ 远程文件补全;不能用:「文件」页签的远程读写与编辑器页签 —— 那套 /sidebar/api/fs.* 通道属于 better-sidebar(内核原生侧边栏走的是它自己的 /api/*,本插件不接管)。1.0.0 之前的旧版插件在这里会永久 pending,前端直接报 web boot: 1 entry did not activate / pending (waiting for service: betterSidebar)(issue #18)。另外 better-sidebar 缺席时宿主会打一条 patch: entry "better-sidebar" not found 的 warn —— dsh-app-boot 对缺席的 patch 目标无条件告警,属正常,可忽略。
DSH 0.2.0-rc.1 起:装不上 / 插件列表里不出现? 新版 harness 会按插件自己声明的 DSH peer 版本范围判定兼容性,范围覆盖不到当前运行时版本时会拒绝安装/激活(提示 Plugin … is incompatible with dsh <版本>),并在插件管理器里给你一个「接受风险」的逐版本豁免。2.4.16 起本插件的 peer 范围已显式列出所有已验证版本线(^0.1.0-rc.6 || ^0.1.5-rc.1 || ^0.1.7-rc.2 || ^0.2.0-rc.1),不需要任何豁免即可安装。若你用的是更早的插件版本,请升级而不是点"接受风险"。(判定是严格 semver:预发布运行时只被"版本线显式列出"的范围覆盖 —— 这也是为什么范围里要逐条列 0.1.5-rc.1 这类版本。)
卸载:
dsh plugin --profile <name> remove @zhangfengshun/dsh-remote-ssh
⚠️ dsh-better-sidebar 版本兼容性(2026-09 实测):0.18.1 / 0.19.0 / 0.19.1 在 DSH Desktop v2.0.9(DSH 0.1.5-rc.1)上主机半边无法加载(它们以值方式导入 SessionLogOffset,桌面版模块面只提供该类型声明)→ 侧边栏文件页签显示「这类内容还没有可用的查看方式。」。请使用 0.18.0,或使用已修复该导入的构建;本插件对 0.18(4 端点)与 0.19(6 端点,含 fs.rename/fs.remove)两套契约均已适配。
使用
三步上手
- 设置 → 远程连接 → 添加连接(主机 / 端口 / 用户 / 密钥)→ 点「测试连接」验证;已有
~/.ssh/config 可直接一键导入
- 添加工作区 → 选「选择远程目录…」→ 选连接 → 浏览并选择远程目录(该目录会成为原生 DSH 工作区);目录还不存在时点「📁 新建目录」就地创建(本地 / 远程 tab 均支持),创建后自动进入新目录
- 进入该工作区会话:内置「文件」页签直接显示远程文件(编辑保存直写远程),「终端」页签自动 SSH 到该工作区的远程目录(仅密钥认证)
会话内直接对模型说(远程工作区会话中免填连接参数):
看看 /home/user/project 下有什么,然后把 train.py 的第 20 行改掉
跑一下 squeue -u $USER,把排队情况整理成表格
把这个目录的 *.log 里含 ERROR 的行抓出来
代码示例
示例 1 · 执行远程命令(remote_ssh_exec,默认 120s 超时):
{
"command": "sinfo -h -o '%P %a %D %t %N' | head -20",
"timeoutMs": 30000
}
返回 { ok, exitCode, stdout, stderr, error, truncated, isTimeout }——例如:
{ "ok": true, "exitCode": 0, "stdout": "cpu* up 12 idle 8 ...\n", "stderr": "", "error": "", "truncated": false, "isTimeout": false }
示例 2 · 文件读写(无需同步):
{ "path": "~/project/config.yaml", "content": "lr: 0.001\nepochs: 50\n" }
{ "path": "~/project/train.py" }
remote_ssh_cat 走 base64 传输(二进制安全),remote_ssh_write 为原子写入(写临时文件再 rename)。
示例 3 · 长时任务与挂起恢复(构建 / 训练显式放宽,卡住可强杀会话):
{ "command": "cd ~/project && bash run_train.sh", "timeoutMs": 0 }
{ "all": true }
timeoutMs: 0 禁用本次超时;环境变量 DSH_REMOTE_SSH_CMD_TIMEOUT_MS=600000 可改全局默认。超时后池化会话自动丢弃重建,remote_ssh_kill 是随时可用的手动兜底。
远端语义(2.4.44 起写清):timeoutMs 只是本地等待预算 —— 到点只放弃等待并丢弃会话,不会终止远端命令(远端进程可能继续运行,输出不再回传;断开连接 / 退出 DSH 同理)。需要远端硬时限请用远端自己的 timeout <秒> <命令>。按停止键取消与 remote_ssh_kill 才代表「终止」:插件会给远端 shell 所在的进程组发 SIGHUP(最多等 4s;与关终端同义,nohup / setsid 起的任务不受影响)。
示例 4 · 远程工作区内的工具调用(免填 profileId,相对路径基于工作区远程目录):
{ "path": "configs/exp1.yaml" }
示例 5 · 从 ~/.ssh/config 导入连接:设置 → 远程连接 → 「导入 SSH config」→ 勾选主机 → 自动填充 host / user / port / keyPath / ProxyJump。
示例 6 · 本地镜像同步与回推(离线批改后再一次性上传):
{ "workspaceId": "w_xxx" }
{ "workspaceId": "w_xxx" }
remote_ssh_sync 把远端拉进本地镜像目录,remote_ssh_push 把镜像改动推回远端(tar over ssh,批量高效)。
2.4.20 起同步是「有门槛的整树操作」,顺序固定为 体积哨兵 → 解包到临时目录 →(整树时)比对「只存在于镜像的文件」→ 原子替换,任何一步失败都不碰旧镜像:
| 参数 | 作用 |
|---|
paths | 只同步这些子路径(相对远端根),例如 ["src","docs"]。局部同步只覆盖对应子路径,不删除镜像里任何其它文件。 |
exclude | 排除规则(相对远端根,支持 *),下推为 tar --exclude=./x,例如 ["data","*.log"] |
force | 显式确认:跳过体积哨兵与「仅存在于镜像的文件」保护(确认要整树全量复制时才用) |
maxBytes | 本次同步的体积上限,覆盖工作区设置与默认 2 GiB |
timeoutMs | 本次同步的墙钟预算,默认 10 分钟(0 = 不限) |
{ "workspaceId": "w_xxx", "paths": ["src"], "exclude": ["data", "*.log"] }
- 体积哨兵:删除任何东西之前先
du -sb -c -- <本次范围>(15 秒预算,只测要同步的范围)。超过上限、探测超时、远端无 du、输出不可解析 —— 一律拒绝,要求显式 force: true。默认上限 2 GiB(syncMaxBytes / 环境变量 DSH_REMOTE_SSH_SYNC_MAX_BYTES 可调)。
- 镜像独有文件默认不删:整树同步前比对旧镜像与拉取结果,发现「远端没有、镜像里有」的文件就拒绝并列出清单(最多 20 条),提示先
remote_ssh_push 补推,或确认放弃后加 force: true。插件自己写的 .remote-ssh.json 与占位 README.md 不算(按内容指纹识别)。
push 不污染远端根:始终排除 .remote-ssh.json(连接信息绝不推到远端),并排除插件写的占位 README.md(你自己写的同名文件不受影响)。
- ⚠️
exclude 不抑制被显式列进 paths 的路径(tar 对命令行点名的成员不做 --exclude 过滤):要排除大目录,请把 paths 提到它的父目录,让大目录成为「成员」而不是「目标」。另外,不匹配任何内容的 exclude 模式不会报错也不会提示(tar 的语义),别把"传了没反应"当成参数被丢弃的证据。
- 镜像目录是"出站缓冲区",不是远端快照:agent 的
write 会同时写镜像与远端对应文件;remote_ssh_push 是这些改动的回推通道。要在会话里读远端最新内容请用「文件」页签或 remote_ssh_cat —— 镜像里的内容可能已经过期。
- 写回桥(issue #24):
write/edit 落本地镜像后会把同一份内容推到远端。2.4.24 起:
- 超过 4 MiB 的内容也照推(分块 base64 追加 + 远端字节数校验)—— 旧版这里是确定性失败,"写日志、模型看不见",于是模型以为远端已有该文件;
- 推送失败会明确报错,消息里写清"本地镜像已写入成功、只是没同步到远端",并给出
remote_ssh_push 这条补救路径。想恢复"只写日志、不影响工具返回"的旧行为:设置 writeBackFailure: "log"(或环境变量 DSH_REMOTE_SSH_WRITEBACK_FAILURE=log)——2.4.26 起这种模式下被护栏 B 拦下时,那份带补救步骤的完整报告也会写进日志(此前只记一行短提示,等于"只报告、不推送"却没有报告)。
- 不想让草稿跟着进远端:工作区可设
writeBack: false 整体关闭,或用 writeBackExclude 排除路径(镜像相对路径,支持 * / ** / ?,结尾带 / 视为目录前缀),例如 ["_patch/**", "*.log"]。
- 护栏 B(2.4.25 起,默认开):推之前先看远端有没有被别人改过。推送成功后插件记下远端的
size:mtime(~/.dsh/remote-ssh/writeback-mtimes.json),下次推同一路径前复查:不一致或远端那份已消失 → 不推送,并报错说明"本地镜像已写入成功、只是没推到远端",给出 remote_ssh_sync(拉下来比对)/ remote_ssh_push(确认以本地为准)两条出路。共享集群上最常见的"另一个写入者"就是你自己刚跑完的作业,这条护栏挡的正是"作业改了远端、镜像还是旧版、agent 再写一次把改动覆盖掉"。
- 只在远端自己的时间轴上自比(不与本地 mtime 比:跨机时钟偏移、tar/base64 重写都会让两边不可比);读不到 ≠ 被改过 ——
stat 失败、ssh 抖动都归为"状态未知"照常推送,只留一行日志。
模型工具
| 工具 | 用途 |
|---|
remote_ssh_profiles | 列出连接配置 + 当前会话远程工作区上下文 |
remote_ssh_exec | 执行远程命令(默认 120s 本地等待预算,timeoutMs 可放宽/禁用;按停止键取消会 SIGHUP 远端进程组) |
remote_ssh_kill | 强制关闭池化 SSH 会话,并尽力给远端进程组发 SIGHUP(挂起命令的兜底恢复) |
remote_ssh_ls | 列举远程目录 |
remote_ssh_cat | 读取远程文件 |
remote_ssh_write | 写入远程文件 |
remote_ssh_grep | 搜索远程文件内容 |
remote_ssh_glob | 查找远程文件 |
remote_ssh_mkdir | 创建远程目录 |
remote_ssh_delete | 删除远程文件/目录 |
remote_ssh_move | 移动/重命名 |
remote_ssh_sync | 远端同步到本地镜像(整树:可用 paths/exclude 收窄,体积超限会被拒,镜像独有文件默认不删) |
remote_ssh_push | 本地镜像推送回远端(自动排除插件自有的 .remote-ssh.json 与占位 README.md) |
远程工作区会话中调用工具可免填 profileId 等连接参数;全部文件/命令类工具走持久 SSH 会话池 + 结果缓存,remote_ssh_exec 单命令实测 ≈15× 提速。
@ 文件引用补全
远程工作区会话里输入 @,候选来自远端(与「文件」页签的树一致),而不是本地镜像目录:
- 索引来源:git 仓库用
git ls-files --cached --others --exclude-standard(尊重 .gitignore、含未跟踪文件;真实超算实测 0.117s / 137 条),非 git 目录回退有界 find(maxdepth 5 + 剪枝,实测 1.57s / 3121 条);
- 排除在远端、截断之前(2.4.8 修复):
git ls-files --cached --others 的输出不是全局字典序(未跟踪文件按 readdir 顺序先输出),node_modules/ 这类目录可能占满前两万行把配额吃光——因此排除目录由远端 grep -vE 在 head 之前完成(与 -prune 同源,正则由同一份排除表生成),客户端过滤仅作双保险;索引达到上限时会打一条 warn 提示可能漏文件;
- 查询语义与官方 provider 逐条对齐:
@ 与 @src/ 走远端目录列举;@read 走索引模糊匹配(同名 > 前缀 > 名称子串 > 路径子串 > 子序列,目录加权 +25);
- 不卡输入框:索引按工作区缓存 60 秒(写文件/执行命令后自动失效),单次补全只等 900ms——超时先用旧索引作答、重建在后台进行;连接异常时自动回退到本地行为;
- 本地工作区不受影响:非远程会话直接委托宿主原实现,索引与排序完全没有改动。
命令超时与恢复
所有 SSH 命令默认 120 秒超时(issue #5):一条挂起的远端命令(网络卡顿、远端进程僵死、等待 stdin 的 cat)不会再永久占用会话、拖死后续命令。
- 超时后自动恢复:池化会话超时即被丢弃并自动重建,后续命令照常执行;一次性连接超时即终止 SSH 进程;
- 显式放宽:
remote_ssh_exec 传 timeoutMs(毫秒)覆盖单次预算,0 禁用超时(长时构建/训练);环境变量 DSH_REMOTE_SSH_CMD_TIMEOUT_MS 覆盖全局默认;
- 手动兜底:
remote_ssh_kill(或 all: true)强制关闭某个/全部池化会话,挂起命令随时可清理;
- 取消 / 超时 / kill 的远端语义:按停止键取消与
remote_ssh_kill 会尽力终止远端进程组(SIGHUP,最多等 4s);timeoutMs 超时、断连只放弃等待,远端命令可能继续运行(需要硬时限请用远端 timeout);
- 超时命令不做自动重试(重试一条挂起的命令只会再次挂起),由模型决定是否改用
remote_ssh_kill 或换命令重试。
兼容性
实测矩阵(2026-09-12,均为真机验证):
| 组件 | 版本 | 状态 |
|---|
| DSH | 0.1.5-rc.1(DSH Desktop v2.0.9) | ✅ 主机服务 / settings / tools / slot / 上传下载拦截全部咬合 |
| DSH | 0.2.1-rc.1(DeepSeek Harness 桌面端 nightly,2026-10-17 实测) | ✅ 2.4.32 起适配:信任源从已被移除的 ctx.webRuntime.trustedHosts 迁到 ctx.webStartup.trustedHosts(两个都软注入,老宿主自动退回旧源、都没有则 loopback-only),--trusted-host 的远程访问路径因此真正生效;三个 DSH peer 显式补上 ^0.2.1-rc.1,不再依赖宿主是否传 includePrerelease;逐 API 复核 webServer/subprocess/settings/slots/locale/defineTool/cordis 生命周期全部未变(13 个工具注册通过,testConnection 实测 200),本插件零 warn/零 error。⚠️ 宿主新增的"监听绑定 IP 即受信"例外本插件不跟随(有意更严:本插件路由在宿主浏览器令牌认证之外,需要远程访问请显式 --trusted-host) |
| DSH | 0.2.0-rc.1(DeepSeek Harness 桌面端 nightly,2026-09-28) | ✅ 2.4.16 起适配并真机验证:peer 范围显式列出该版本线(否则 harness 会拒绝安装/激活);settings 新 API 与旧数据迁移继续有效(实测升级后首次启动即恢复全部连接与工作区);终端 patch 仍命中 terminal-controller。客户端注入清单同步移除运行时已不提供的 dsh-client-runtime;2.4.17 修好浅色主题下"工作区地球角标不可见"(颜色改为取图标自身计算色) |
| DSH | 0.1.7-rc.2(DSH Desktop v2.0.15) | ✅ 2.4.15 起适配:settings 新 API(configure/describe/update,数据存于本插件 entry 的 Config,字段标 .volatile())+ 旧 settings.yaml 一次性迁移(连接/工作区 ID 与 mirrorPath 全保留,无需重建);侧边栏终端改由宿主原生 terminal-controller 管理,patch 已同时覆盖它与 better-sidebar |
| DSH | 0.1.5-rc.1 / 0.1.6 线 / 0.1.0-rc.6 线 | ✅ 主机服务 / settings / tools / slot / 上传下载拦截全部咬合(peer 范围继续覆盖);⚠️ 2.4.40 及更早在这一代上会误判 settings 代际(两套接口都在 → 走 0.1.7+ 新分支 → 读恒空、写抛 namespace not registered,表现为「连接列表永远为空、导入与保存都没反应」,issue #34),2.4.41 起修复 |
| DSH | 0.1.2-rc.1 稳定线 | ✅(插件 2.3.x 时代基线) |
| dsh-better-sidebar | 0.15.0 – 0.18.0 | ✅ fs.tree/fs.read/fs.write + fs.search({ matches: cwd 相对 '/'-分隔路径, truncated } 契约,2.4.11 起;此前只回 entries 会让「按文件名搜索」崩掉整块页签) |
插件不修改 DSH 源码、不注入 profile 依赖树,全部能力经官方 cordis.patch.yml + profile 机制挂载。
故障排查
| 现象 | 原因与解法 |
|---|
「测试连接」报 Permission denied (publickey) | ① 私钥带口令:插件以批处理模式运行(BatchMode=yes),无法交互输口令——先用 ssh-add 加载,或去掉密钥口令;② Windows host 且用户在 Administrators 组时,公钥须写入 C:\ProgramData\ssh\administrators_authorized_keys;③ 用户名的写法(user / .\user / user@domain)要与手动连接一致 |
从 git-bash 启动 dsh web 后密钥认证失败 | 2.3.9 起已修复:Windows 下 ssh 解析固定为系统 OpenSSH 绝对路径(此前会误用 Git 自带的 MSYS2 ssh) |
| 侧边栏文件页签显示「这类内容还没有可用的查看方式。」 | dsh-better-sidebar 主机半边未加载:0.18.1 / 0.19.0 / 0.19.1 在 DSH Desktop 上会因 SessionLogOffset 运行时导入失败——降到 0.18.0 或使用修复版(上游 PR #641) |
前端整屏报 Failed to load plugins / web boot: 1 entry did not activate / @zhangfengshun/dsh-remote-ssh: pending (waiting for service: betterSidebar),Desktop 还提示「插件恢复」 | ≤ 2.4.17 已知问题(issue #18):本插件当时把 better-sidebar 的客户端服务声明为硬依赖,未安装它的用户前端会停在 pending(cordis 的 fiber 只要一个 inject 键缺失就整体不激活)。升级到 2.4.18 即可;或临时装上 dsh-better-sidebar(注意版本线要与内核匹配)并重启 |
| 远程项目的侧边栏「文件」页签显示的是本地目录(不是远程目录) | 2.4.18 起已修复:dsh-better-sidebar 0.23+ 把文件树从「逐层 fs.tree」改成「一次 fs.trees 批量列举可见集」,而插件当时只注册了 fs.tree 的 exact 路由 → 列举请求绕过拦截、落到 better-sidebar 自己的宿主实现(读本地 fs)。升级插件到 2.4.18 并重启 DSH 即可;本地工作区不受影响(两条分支都实现了 fs.trees)。0.24 新增的 fs.mkdir(新建目录)同理,此前只会建在本地镜像里 |
| 右键「打开方式 / 在文件管理器中显示」打开的是本地镜像目录(不是远端) | 2.4.18 起已修复:该菜单把客户端已知的绝对路径直接交给本机打开器(explorer.exe /select,<路径> / rundll32 url.dll,FileProtocolHandler <url>),远程工作区里客户端只有镜像路径。现在拦截 open.external:远程工作区改开 vscode://vscode-remote/ssh-remote+<别名><远端路径>(别名取自 ~/.ssh/config;reveal 打开的是该文件所在的远端目录),本地工作区行为不变 |
| 远程工作区里「用 VS Code 打开」没反应 / 提示连不上 | 需要 ~/.ssh/config 里有与连接一致的别名(Host 同名或 HostName 相同、且端口与用户名一致)——插件据此生成 ,VS Code 会复用该条目的 ///。没有别名且端口不是 22 时插件并在 DSH 日志里打一条 warn(避免静默失败)。另外:在 better-sidebar 的 里手填主机——那条分支由客户端自行打开、路径仍是本地镜像路径,交给本插件处理才对 |
原理
插件注册 9 个 exact 路由(/sidebar/api/fs.tree、fs.read、fs.write、fs.search,better-sidebar 0.19 新增的 fs.rename、fs.remove,0.23+ 新增的 fs.trees、fs.mkdir,以及 0.24 的 open.external),在 better-sidebar 的 prefix 路由之前拦截。会话 cwd 含 .remote-ssh.json 时走 SSH,否则走本地 fs。客户端看到的是本地镜像路径,Host 自动转换为远程路径——对客户端完全透明。
fs.trees 是 0.23+ 的批量列举端点(一次请求带上「工作区根 + 所有已展开目录」,≤64 条;旧版是逐层 fs.tree)。远程分支用 remoteListDirsBatch() 一次 SSH 往返列举全部目录(逐目录标记行 + find -printf,单层失败只影响该层),命中目录缓存(TTL 5s)的层 0 RTT。少注册这一个路由,新版客户端的整棵文件树就会静默回落到宿主的本地实现 —— 这正是 2.4.18 修的问题。
open.external 是 0.24 的「打开方式 / 在文件管理器中显示」端点,宿主侧用本机打开器执行(Windows:explorer.exe /select,<路径>、rundll32 url.dll,FileProtocolHandler <url>)。远程工作区里客户端只有镜像路径,本插件因此把路径翻译成远端路径并改开 vscode://vscode-remote/ssh-remote+<~/.ssh/config 别名><远端路径>:url 保留客户端选中的 scheme(vscode / cursor / zed),reveal 打开该文件所在的远端目录。别名按「Host 同名或 HostName 相同 + 端口一致 + 用户兼容」匹配(纯函数 remoteEditorAuthority),让 VS Code 复用该条目的端口/用户/密钥/跳板机;~ 由插件自己展开(URL 不过 shell,~/run/... 必须先换成远端 home 的绝对路径 —— 用 printf %s "$HOME" 查一次并按 profile 缓存 10 分钟),无可用别名或拿不到 home 时回退本机行为并打 warn。不要在 better-sidebar 里填 openWith.sshHost——那条分支由客户端自行打开、路径仍是镜像路径。
模型侧的文件工具(agent 的 write/edit)走的是进程内 ctx.fs(宿主 base bundle 挂的是本地 fs-sandbox),不经过任何 HTTP 路由,因此 2.4.13 及以前只落本地镜像——用户在远端机器上找不到文件,只能人工 remote_ssh_push。2.4.14 起插件包装 ctx.fs 的 writeText/editText:原写入照旧(镜像内容、沙箱围栏、写意图语义全不变),成功后把同一份内容定向推回远端对应的那个文件(不是整镜像 tar,避免用旧镜像覆盖远端其它文件),写前自动 mkdir -p 远端父目录。推送失败只记一条 warn——本地写入已成功,桥接层不会让写操作变成失败;包装不可用时(服务缺失/被替换)退回旧行为并在日志提示。
远程读取采用单往返合并读:一条池化命令同时返回 size/mtime 帧与文件内容(文本类扩展名优先 raw 直传,字节长 + U+FFFD 双校验失败自动回退 base64,结果逐字节一致);配合主机侧结果缓存与变更失效(见下节)。
Shell wrapper(~/.dsh/remote-ssh/dsh-remote-shell[.cmd])检测工作区 .remote-ssh.json,自动 ssh -tt 连接远程,使内置「终端」页签透明接入。
缓存与一致性
远程读取与目录列举结果在主机侧缓存(读 LRU 32 条 + 列举 LRU 64 条,TTL 5 秒;单条 >1MiB 不缓存、总量 32MB 字节预算,防止大文件驻留拖慢宿主):TTL 内重复打开或切回页签 0 网络往返;过期后先做一次轻量 mtime+size 复验,未变化则免重传。写、删除、移动、建目录、上传、推送(push)、成功的远端 exec 与变更类 git 子命令(add/reset/commit/checkout/revert/cherry-pick)会自动失效相关缓存,并以每 profile 缓存代(epoch)兜底「同秒同 size 写」等粒度盲区。
已知限制:
- 集成终端(
ssh -tt)与远端其它进程改动的文件依赖 TTL + 复验兜底,最多 5 秒陈旧;
- 镜像是"本地优先"的读影子(2.4.35 起,issue #29):
ctx.fs 的读方法(stat / lstat / resolve / readText / streamText / readBytes / readByteRange / listDir)现在本地有就照旧读镜像,镜像里没有才回远端取(内置预览、agent 的 read 都因此能读到只在远端存在的文件)。两点仍未覆盖:① 文件在远端被其它人改动、而镜像里也有旧版本时,读到的仍是镜像那份(本地优先;用 remote_ssh_sync 重拉镜像即最新);② 远端文件的变更不会触发实时刷新(ctx.fs.watch 对远端影子是空实现),预览不会自动跟新;另外远端整文件读受 4 MiB 单次上限约束;
/sidebar/file 下载池化路径有效上限约 6.29MB,更大文件自动退回一次性连接下载(可成功,多一次重连开销);
- 二进制内容伪装成文本扩展名时会多一次 base64 回退往返(结果正确)。
❤️ 七夕快乐
本项目是送给 zhangyi 的七夕礼物。
愿它像连接起一台台远方的超算一样,也把我们紧紧连在一起。七夕快乐 ❤️
—— 2026 年 8 月 18 日
更新日志
版本历史与每版修复细节见 CHANGELOG.md(最近:2.4.3 适配 better-sidebar 0.19 端点、2.4.2 修复设置图标闪现、2.4.0 命令级超时与 remote_ssh_kill)。
许可证
MIT
如果这个插件帮到了你,欢迎在 GitHub 上点个 ⭐ Star,或到 DSH Market 收藏——这会帮助更多需要远程超算开发的人找到它。