Skip to content

dsh-remote

Verified

@jackguo0310/dsh-remote · v0.3.0 · MIT · Web UI

Lightweight remote development for DeepSeek Harness: pick a remote workspace over SSH and let the agent work on it with the SAME local tools — file, shell, and search providers are routed to the remote host by the plugin. No extra model tools, no third-pa

Install

dsh plugin add @jackguo0310/dsh-remote

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

@jackguo0310/dsh-remote

English | 中文

DeepSeek Harness Web GUI 中的 @jackguo0310/dsh-remote:远程工作区会话与右侧文档预览

远程开发设置:已保存的机器、认证方式与标记颜色

摘要

本插件为 DeepSeek Harness 提供轻量化远程开发:注册一台 SSH 机器,把远程目录选为会话工作区之后,agent 即可用与本地完全相同的工具(文件工具、shell、搜索)在该远程工作区上工作。插件不新增任何面向模型的工具,也不引入第三方 UI 插件:它把文件系统、子进程、bash 三个 provider 替换为路由版本,将本地工具调用翻译为经 SSH 的远程执行;同时在 Web GUI 中贡献一个设置分区和一个工作区目录选择对话框。

核心特性

  • 同构工具集。 远程工作区不增加任何工具调用:agent 的工具集与本地工作区完全一致,由插件在底层把调用翻译为远程执行。
  • 远程零依赖。 远程机器不需要任何额外的服务端,一条 SSH 连接即可使用。
  • Web GUI 集成。 连接管理覆盖密码 / 私钥 / SSH agent、跳板机、主机密钥 TOFU 与连接测试;支持为不同远程工作区设置自定义颜色标记。

目录


安装

先确认宿主的 dsh 版本:

dsh -V

再按兼容性选插件版本。

你的 dsh 插件版本 安装命令
≥ 0.1.7-rc.1, < 0.3.0 v0.3.x dsh plugin add --profile web @jackguo0310/dsh-remote
≥ 0.1.7-rc.1, < 0.2.0 v0.2.x dsh plugin add --profile web @jackguo0310/[email protected]
0.1.2-rc.1 – 0.1.5-rc.x v0.1.x dsh plugin add --profile web github:jackguo0310/dsh-remote#v0.1.0

npm 安装取的是 latest dist-tag,会跟随新版本发布。需要锁定具体版本时:

dsh plugin add --profile web @jackguo0310/[email protected]

npm 会在安装时就解析并校验插件的 peer 依赖,因此 dsh 版本不受支持会当场报错并列出不满足的包,而不是等到加载时才失败。

v0.1.x 早于 npm 发版,只以 Git tag 形式提供。该版本一律带 #<tag> 安装:不带 ref 的 github: 安装取默认分支 HEAD,会随仓库漂移,而插件与宿主必须同代才能运行。

lib/ 构建产物同时随 npm tarball 和每个 Git tag 发布,所以两种方式都无需构建,也不会触发 pnpm 对 prepare 脚本的 allowBuilds 拦截。

换版本:用新的 spec 重新 add 即覆盖;彻底移除则 dsh plugin remove --profile web @jackguo0310/dsh-remote。

开发模式则链接本地检出:

cd dsh-remote
pnpm install          # 自包含 workspace;store 位于 .pnpm-store/
pnpm run build        # 产出 lib/index.js(宿主半)与 lib/client.js(浏览器半)
dsh plugin add --profile web link:/absolute/path/to/dsh-remote

link: 安装把 profile 指向检出目录,之后每次 pnpm run build 重启 harness 即生效,无需重新 add。

兼容性

dsh 在 0.1.7-alpha.1 对 shell seam 做了两处破坏性改动,都没有保留兼容层:执行入口收敛为 resolve() + execute()(run()/start() 被删除),本地执行器的 Config 改为活配置访问器(Volatile,并新增 pwshPath)。因此插件与宿主按代对应:

  • v0.3.x → dsh ≥ 0.1.7-rc.1, < 0.3.0,@deepseek-ai/dsh 声明为 peerDependencies: >=0.1.7-rc.1 <0.3.0,各 dsh-* 包为 >=0.1.7-alpha.1 <0.3.0。v0.3.0 的构建已对齐 0.2.1-alpha.1 宿主 API(这次唯一的硬变化就是 peer 上界)。
  • v0.2.x → dsh ≥ 0.1.7-rc.1, < 0.2.0。
  • v0.1.x → dsh 0.1.2-rc.1 – 0.1.5-rc.x(run()/start() seam)。

宿主在加载插件前会校验每个 @deepseek-ai/dsh* peer,范围不满足即拒绝,并列出不满足的包名。@deepseek-ai/cordis 不在宿主校验的包之列。

npm 安装会在插件加载前就拒绝版本不匹配的组合,并列出不满足的包。github: 与 link: 安装没有这道检查,其他典型表现:插件比宿主旧时,启动即抛 TypeError: Cannot read properties of undefined (reading 'get')(构造本地执行器时读 config.pwshPath);插件比宿主新时,启动在本地执行器构造处校验失败(pwsh-local: timeoutMs must be a positive finite number),命令执行时抛 ctx.shell.run is not a function。

宿主发布新一代 alpha 时,peer 范围需要相应更新(semver 的 prerelease 规则不会自动放行新的 alpha)。

使用

只需三步:

  1. 添加机器。 在 Web GUI 的 @jackguo0310/dsh-remote 设置分区填入 host、port、用户名,选择密码 / 私钥 / SSH agent 认证(可选跳板机),先点测试连接再保存。已保存的机器可在它自己的卡片上重新测试。
  2. 选择远程目录。 在工作区目录流(hero 的「选择目录」对话框或侧边栏工作区选择器)打开远程页签,浏览机器目录,把某个远程目录设为会话工作区。
  3. 照常工作。 到此为止。文件工具、shell、bash、搜索仍以同样的调用方式执行,只是落在远程机器上;模型的工作目录就是远程路径,不需要任何额外说明,也不会多出任何新工具。所选目录之外的路径保持本地行为,既有会话不受影响。

其底层机制:设置远程工作区会在 $DSH_HOME/remote-workspaces/<host>-<user>-<port>/<basename> 下创建一个锚点——一个真实本地目录,元数据记录远程坐标。不存在「当前机器」或默认目标,锚点独自决定其会话工具的执行位置。若该机器随后被删除,工作区上的操作会以明确的「机器已失配(no longer configured)」错误失败,而不是悄悄换到别的机器执行——重新添加机器即可恢复,或删除该工作区目录。


理解设计

三个路由 provider 替换同一服务的基座行,所有本地工具继续可用,只有传输层改变:

  • RoutingFileSystem(替换沙箱文件系统)——路径解析到远程根的读写、编辑、列目录经 SFTP 完成;其余调用经 super() 委托本地基座。
  • RoutingSubprocessRuntime(替换本地子进程运行时)——以锚点为 cwd 的 spawn 在远程主机上经 SSH exec 通道执行;搜索工具使用的打包 ripgrep 会被改写为远程 rg 二进制。路由只看工作目录,所有宿主平台行为一致。
  • RoutingBashExecutor / RoutingPwshExecutor(替换沙箱 bash/pwsh 执行器)——工作目录在锚点下的命令经远程 bash -c 执行;后台进程通过进程组 kill 协议拿到真实远程 PID。宿主平台决定挂载哪个执行器——只有本地回退与平台相关(POSIX 用本地 bash,Windows 用本地 pwsh);远程方言始终由远程主机的 POSIX shell 决定。
  • 按 agent 的工具可见性。 patch 同时挂载两套 shell 工具栈,并按会话安装作用域限制,隐藏该会话工作区不应使用的方言:远程会话只见 bash 工具(不见 pwsh),Windows 本机会话只见 pwsh(不见插件补入的 bash)。POSIX 本机会话两个世界都用 bash,与基础组合一致。
  • 每机器一条共享 SFTP 会话。 SFTP 协议在单一子系统通道上多路复用所有请求,全部文件操作共享一条会话,而不是每次调用开一条(并泄漏)通道——服务器对单连接的会话数有上限,耗尽后所有打开请求都以通道失败告终。
  • 远程工作区在文件树中有标记。 client 会为远程工作区重新着色文件夹图标:host 将每个锚点与其机器的标记颜色(Machine 字段,可在设置中编辑)联接,client 把这份清单转成文件树 data-files-* 钩子上的属性选择器并以样式表注入。每台机器一个颜色;留空则回退到主题强调色。侧边栏的工作区行没有数据钩子,其规则经行内 aria-label(:has())命中,匹配锚点工作区采用目录基名得到的标题。两台机器挂载同一个远程目录时会得到同样的标题,同一条行规则就会把两行都涂成最后一台机器的颜色;因此 host 会为这类锚点定出一个唯一标题(追加机器名,如 myapp (build-box);两台机器同名时退回到远程路径哈希),client 再把工作区改名为该标题。操作者自己改过的标题不会被覆盖;若标题仍然歧义,则该行规则完全不生成,这些行保持主题默认色而不是错色。
  • 远程工作区显示远程路径。 shell 展示工作区目录的位置——文件树的根头部与工作区列表的 hover 卡片——显示的是会话工作目录,而远程工作区的会话工作目录是插件的本地锚点目录。client 维护一张别名表(锚点目录 → 远程路径,与标记颜色共用同一份 /status 联接),经 mutation observer 把这些展示文本改写为远程路径,只修改既有文本节点的值,React 的协调器因此保留它拥有的每个元素。底层的 data-files-* 坐标保持本地:文件树仍按它们导航。

模型看不到锚点句柄。 harness 的系统提示会报告会话工作目录;远程会话下插件按 agent 覆盖该变量为远程路径,模型可见的 cwd 即命令真正运行的目录。作为兜底,命令文本中的锚点目录写法(绝对路径、~、$HOME、${HOME})会在执行前改写为远程路径——仅限同机锚点,stdin 保持原样。本地会话不受影响。

远程变更策略与本地沙箱一致:read-only 模式拒绝远程写入;workspace-write 仅允许写远程工作区根与远程 /tmp。远程写入按文件串行化、原子发布(临时文件 + rename),并对过期版本与歧义编辑返回与本地文件系统相同的错误码。

主机密钥采用 TOFU(默认 accept-new):首次见到的密钥被记录,变更的密钥以中间人原因拒绝;每台机器可选 verify/off 模式。


配置

机器在设置分区中管理;插件自身通过 cordis.yml 接受配置默认值(均可选):

字段 默认值 含义
commandTimeoutMs 20000 单命令超时;SIGTERM 宽限后关闭通道。
connectTimeoutMs 15000 SSH 连接建立超时。
maxOutputChars 200000 超出后命令输出保留头尾。
maxFileBytes 52428800 SFTP 单次读写的最大文件体积。
hostKeyMode accept-new accept-new、verify 或 off。
remoteRipgrep rg 打包 ripgrep 改写到的远程二进制。
anchorRoot $DSH_HOME/remote-workspaces 锚点目录的根目录。
auditLog 开启 每次远程执行的追加式审计,每行 时间戳 | 用户@主机:端口 | 命令 | 退出码。

已知限制与延期工作

  • Windows 远程机延期支持。 远程机器必须运行 POSIX shell;uname 探测会以明确错误拒绝 Windows 目标。适配预留到后续阶段。
  • Windows 本机支持远程 shell 路由。 Windows 宿主挂载基于 pwsh 的路由执行器:本地工作目录保持沙箱 pwsh 执行器,锚点工作目录跨到远程主机的 bash。模型在远程会话看到 bash 工具,本地会话看到 pwsh 工具。
  • 不支持持久终端会话。 终端工具会返回明确的"not supported by @jackguo0310/dsh-remote"错误,而不是让 agent 自行尝试;远程命令请使用 bash 工具。持久 shell 工具仍仅限本地:指向远程工作区的持久工具会以同样错误拒绝。
  • 远程会话不支持 @ 文件引用。 远程会话中输入 @ 会给出单条明确的"暂不支持"候选,而不是静默失败;引用源接口已预留到后续阶段。
  • 没有镜像或同步层。 锚点目录只保存元数据,不保存文件副本;每次读写都经 SSH,受 maxFileBytes 限制。
  • 文件树标记依赖 in-box 的 data-files-* 钩子。 着色目标是文件树的数据属性,它们不是声明过的公开契约;dsh 若重命名这些属性,着色会静默消失(纯展示层,不影响其他功能)。工作区行的标记还依赖工作区标题匹配:重命名工作区、或 dsh 更改侧边栏 aria-label 文案,会让行标记失效(文件树标记不受影响)。
  • 路径别名依赖展示结构。 远程路径改写针对树根头部的 span 形状与 hover 卡片的 portal 形状,两者都未声明;dsh 若更改任一形状,本地锚点路径会静默恢复显示(纯展示层——导航、寻址与复制操作不受影响,仍使用真实的本地目录)。
  • SSH 走纯 JS 而非原生加密。 打包的 ssh2 不会加载可选原生加速件,大文件 SFTP 传输的吞吐低于原生构建版本。
  • 搜索依赖远程 ripgrep。 远程机器上必须存在 rg 二进制(可用 remoteRipgrep 配置);否则搜索工具在远程路径上失败。
  • 内建目录选择流是被覆盖而非替换。 两个目录流注册以不同优先级共存(本插件使用 -1,最低者优先渲染);卸载本插件后槽位交还给内建选择器。
  • 「本机」页签跟随宿主组合的 picker 能力。 宿主在启动时解析一次目录选择器后端:WSL 缺少 zenity/kdialog、经 SSH 启动、绑定非回环地址或无显示会话的 Linux 都会组合出 browse 后端(只有 list/createDirectory 原语,没有 OS 选择器)。插件的「本机」页签据此分流——native 打开 OS 选择器,browse 改用宿主的网页目录浏览器;在此之前的版本「本机」页签硬编码 pick,在这类启动下会以 directory-picker/unavailable 失败。
  • 删除机器后其工作区被有意搁置。 锚点在机器删除后仍然存在,所有工具面对它都会以同样的「机器已失配」错误拒绝(fs、bash、subprocess,提示词的 cwd 变量回退为本地句柄),而不是换一台机器或在本机执行。

开发说明

插件目录是自包含的 pnpm workspace(packages: [- .]、storeDir: .pnpm-store),阻断 pnpm 向上探测 harness 仓库的 workspace。dsh 框架包声明为 peerDependencies(各 dsh-* 包为 >=0.1.7-alpha.1 <0.3.0,@deepseek-ai/dsh 为 >=0.1.7-rc.1 <0.3.0,均由宿主 profile 提供),并在 devDependencies 中精确锁同版本用于本地类型与构建;依赖图内不存在相对 link: 依赖,因此该目录可在任意位置独立构建。

0.2.x 各包声明的 peer 必须全部出现在 devDependencies 里,而不只是本源码直接 import 的那些。0.2.x 把 dsh-bash-local、dsh-util-values、dsh-scope 等声明为钉同版本的 peer,漏掉时 pnpm 会解析到旧副本,TypeScript 看不到基类,报错便表现为 API 破坏(SandboxBashExecutor.Config 报不存在、FsError 报缺 name/message)而非缺 peer。

命令:pnpm run build(tsdown,双半)、pnpm run typecheck、pnpm run test(node:test 经 tsx;无需 SSH 服务器——连接池接受注入的 client 工厂,SFTP 表面使用假件)。

ssh2 位于 devDependencies,因为它是构建输入而非运行时依赖:pnpm run build 会把它打进 lib/index.js。任何源码改动都要连同重新构建的 lib/ 一起提交,否则 GitHub 安装拿到的是旧代码。

第三方代码

lib/index.js 内含 ssh2(MIT)、asn1(MIT)、safer-buffer(MIT)、tweetnacl(Unlicense)、bcrypt-pbkdf(BSD-3-Clause)的打包副本,许可证全文见 THIRD-PARTY-NOTICES.md。

打包把安全更新的责任移到了本仓库:ssh2 的安全公告不再经用户自己的 pnpm update 到达他们。修补方式是 pnpm update ssh2 && pnpm run build,然后把结果提交到这里。

许可证与致谢

MIT,见 LICENSE。

Copyright (c) 2026 CJYLZS,本插件原作者。Copyright (c) 2026 JackGuo0310 为分支维护及其后的改动。npm 包 @jackguo0310/dsh-remote 是原 dsh-remote-development 项目的分支;原作及其作者仍适用同样的 MIT 条款。

本分支的改动见仓库历史,每笔提交都标注了产出它的工具。