Skip to content

dsh-presence

Verified

@rjfan/dsh-presence · v0.19.0 · MIT · Web UI

Work on any machine in your ssh config as if it were local: persistent connections, native file editing, search, exec and terminals, with nothing installed on the remote host.

Install

dsh plugin add @rjfan/dsh-presence

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

Source

Tags

Creators

Readme

Presence icon

Presence

把 SSH 机器变成本地标签页。

给 coding agent 用的持久远程工作区、原生文件编辑、搜索、命令执行、终端和端口转发 —— 远端不需要安装任何东西。

English · 简体中文 · 版本记录 · MCP server

npm plugin npm mcp license awesome-dsh-plugin

Presence — 远程机器就是本地标签页


为什么不是「agent 跑在远端」

在每台服务器上装 agent,意味着装 N 次、升级 N 次、逐台配代理;让本地 agent 每次 ssh host '<command>',意味着每条命令一次握手、用不上原生文件工具、上下文不连续。

Presence 让 agent 留在本机,通过每台机器一条常驻 SSH 连接把能力伸到远端。远端 仍然只是普通的 sshd:SFTP、exec 和一个 PTY。

方案 代价
每台机器装 agent N 次安装、N 次升级、逐台代理配置
每个操作 ssh host '<cmd>' 每条命令一次握手、无文件工具、无会话
Presence 每台机器一条连接、原生工具、远端零安装

特性

  • 远端零安装 —— 只用 sshd 自带能力:SFTP 子系统、exec 通道和 PTY。
  • 一次握手 —— 连接池为每台机器保持一条连接,之后的调用都是这条连接上的请求。
  • 用目标机的原生工具 —— rg/grep 在目标机跑,命令在目标 shell 里执行;路径、权限、编码都是远端的语义。
  • 接近本地的编辑 —— 原子写、保留 mode、锚点歧义拒绝、读后写守卫、presence_edit 的 dry-run diff。
  • 真正的远程工作区 —— 远端目录注册成普通 DSH 工作区,文件树、终端都开在远端机器上。
  • 可恢复的删除 —— 危险 shell 命令先 dry-run;presence_trash 移入远端回收站并可恢复。
  • @ 能补全远端文件 —— 远程工作区里,文件引用补全直接索引目标机(rg --files,回退 find),不是本地镜像。
  • 文件操作不只是读 —— presence_mkdir / move / copy / chmod,文件树右键菜单直接操作;删除走可恢复回收站。
  • Diff 与实时日志 —— 两个远端文件、或远端与本地文件之间的统一 diff;面板里直接跟随日志,不用常驻 tail -f 任务。
  • 转发会自己回来 —— 保存转发并自动恢复;端口面板发现新监听端口后可一键转发。
  • 任务完成通知 —— 后台任务结束时弹 toast(及桌面通知),也可配置本地 notifyCommand 推送到手机或守护进程。
  • 两种外壳,同一个内核 —— DSH 插件与独立 MCP server(presence-mcp,20 个核心工具)共用同一套内核。

快速开始

1. 安装 DSH 插件

dsh plugin add @rjfan/[email protected]

从源码安装:

dsh plugin add /path/to/dsh-presence

2. 选一台机器,打开工作区

打开 设置 → Presence → 新建,从 ~/.ssh/config 选一台机器(或直接粘贴一条 ssh 命令),再选一个远端目录。它会以工作区出现:左侧文件树、终端和 agent 工具 都使用这个远端目录。

3. 开始用

你:把远程 8000 端口转发到本地,然后打开看看
Agent: presence_forward { remotePort: 8000 }
       → http://127.0.0.1:8000

MCP server

同样的能力也提供给装不了 DSH 插件的 agent(Codex CLI、Claude Code、OpenCode、Cursor 等)。

npx -y presence-mcp            # 20 个工具,走 stdio
// Claude Desktop / OpenCode 风格
{
  "mcpServers": {
    "presence": {
      "command": "npx",
      "args": ["-y", "presence-mcp"],
      "env": {
        "PRESENCE_HOST": "server",
        "PRESENCE_WORKSPACE": "server:/srv/app"
      }
    }
  }
}

Codex TOML、Claude Code、OpenCode、Cursor/Windsurf 的完整配置见 mcp/README.md。

工作原理

┌───────────────────────────────┐
│  DSH 插件  /  MCP server      │  两种外壳,同一个内核
└──────────────┬────────────────┘
               │
        Presence 内核
        ├── ssh-config  从 ~/.ssh/config 读主机、端口、用户、密钥、跳板
        ├── connection  一条常驻连接:SFTP / exec / PTY + 通道池 + 取消
        └── host pool   每台机器一条连接,按需建立、闲置回收
               │
        SSH(沿用你已有的密钥、known_hosts、跳板、代理)
               │
        目标机:Linux / macOS,原生 sshd
Agent 操作 通道 远端依赖
读 / 写 / 编辑 / 列目录 / stat SFTP 子系统 sshd 自带 sftp-server
执行命令、搜索(rg/grep)、git exec 通道 目标机已有的命令
交互式终端 shell 通道 + PTY 目标机的登录 shell

Linux 主机上的实测:

操作 延迟
ssh host '<cmd>'(每次新连接) 520–540 ms
Presence 建连(一次握手,含密钥认证) 145 ms
Presence sftp stat p50 0.6 ms / p95 0.9 ms
Presence exec true(新通道 + shell) p50 7.6 ms
8 个 exec 通道并发 合计 15 ms

必须保持 TCP_NODELAY:否则请求/响应会撞上 delayed ACK,p95 从 0.9 ms 劣化到 48 ms。

工具

DSH 插件提供 24 个工具。相对路径相对当前远程工作区;在工作区里 host 可省略。执行时接受 参数别名(path/file_path/filePath、old/oldString、new/newString、 cwd/workdir 等),习惯写法都能用。

展开全部工具
工具 说明
presence_hosts 列出 SSH 主机、连接状态和默认主机(支持 format:"json");workspace:true 标记当前会话的机器,degraded 行带最后一次错误。
presence_connect 建立/复用连接并返回远端身份。
presence_exec 执行远端命令;pipefail、background、waitForPort、结构化 JSON、saveStdout;超时默认 300 秒;progress 归一化 \r 进度条;超长输出落盘到远端文件并返回路径(tailLines);危险命令先 dry-run —— confirm:true 执行整条命令,confirm:"partial" 只执行安全分段。
presence_jobs 后台任务:list、status、wait(含 timedOut)、stop(alive/verified/portReleased)。
presence_read 读远端文件,带行号、offset/limit,raw:true 返回逐字节原样内容。
presence_read_many 一次读 1–50 个文件,支持每文件字节/行数上限。
presence_write 原子写;保留 mode;返回真实 mode、字节数和完整 sha256;ifMatchSha256 乐观锁。
presence_edit 精确文本替换或行号区间替换,带紧凑 diff、dryRun 和 JSON 字段。
presence_ls 列目录;recursive、totalBytes、JSON 输出。
presence_stat 批量 stat 1–100 个路径:类型、大小、mode、mtime、可选 sha256;缺失路径返回 exists:false。
presence_mkdir 创建远端目录(默认递归);已存在时幂等成功。
presence_move 同一台机器内重命名/移动;目标已存在时除非 overwrite:true 否则拒绝。
presence_copy 复制远端文件(SFTP 流,大文件走 cp -p),目录需 recursive:true。
presence_chmod 按八进制 mode 改权限(755/0644),可递归。
presence_diff 两个远端文件、或远端与本地文件的统一 diff;支持上下文行数和 JSON 统计。
presence_tail 读远端文件最后 N 行,或在限定时间内跟随(follow:true、timeoutMs)。
presence_search 远端 rg/grep 搜索,countOnly,JSON matches。
presence_glob 远端 glob 找文件,带 size/mtime。
presence_import 本地 → 远端二进制安全上传,可设 mode,返回 sha256。
presence_export 远端 → 本地真实文件(内容寻址),供预览或原生程序打开。
presence_forward 远端端口转发到 127.0.0.1;open/list/close 支持 JSON。
presence_port_probe 检查最多 20 个端口的监听与 TCP 可达性;`expect:open
presence_http_probe 等端口 → 开转发 → 本地 HTTP 断言 → 关转发 → 验证本地端口已释放。
presence_terminal 持久 PTY:open 可带 cwd/command,write(ackOnly)、read、close;tmux 会话可跨重连。
presence_trash 可恢复删除:移入远端回收站、list/restore;支持批量 paths[];返回 sourceRemoved 和 restoreHint。

路径与工具怎么选

路径在哪 用什么 说明
工作区挂载目录下(~/.dsh/presence/mounts/...) 内置 read / write / edit / grep / glob 都直达远端机器;grep/glob 会自动路由过去
工作区之外的远端绝对路径(/data、/scratch、GPFS) presence_read / write / edit / ls / stat / search / glob 内置工具只看得到挂载目录,看不到整台机器
本地文件(预览或 Office) 先 presence_export,再 present / 原生打开 挂载路径是虚拟的,只有真实本地文件能被原生程序打开

长时间任务

presence_exec 会等命令结束,所以训练、构建、起服务都不该直接用它。推荐流程:

1. presence_exec { command: "python train.py --lr 3e-4", background: true, label: "train-3e-4" }
   → { id: "j7", pid: 4242, logPath: "/tmp/presence-jobs/....log" }
2. presence_jobs { action: "wait", jobId: "j7" }        # 阻塞等待,不要 sleep+poll
3. presence_tail { path: "/tmp/presence-jobs/....log", follow: true }   # 看进度
  • presence_jobs { action: "status" } 取快照;stop 结束进程组并验证端口已释放。
  • 交互式任务:presence_terminal { action: "open", session: "train" },tmux 会话跨重连存活。
  • 不要在前台 presence_exec 里用 nohup、tmux new-session 或结尾 & 跑长任务:登录节点 会话结束时,集群可能清理残留进程。background: true 本身用 setsid 脱离;调度器管理的集群 请用集群自己的启动器(rlaunch -d …、salloc/sbatch),让调度器持有任务。

安全与恢复

  • 危险命令不会直接执行。 rm -r/-f、git clean -fdx、find -delete、shred、 truncate、dd of= 会先用 no-op shim 重放以展开真实目标;未设置变量和受保护路径 (工作区根、家目录、/、.git)直接拒绝。必须再带 confirm:true 才会执行。被拒绝的 混合命令还会列出可单独执行的安全分段(Safe segments you can run separately:), confirm:"partial" 只执行这些分段;工作区内对单个文件的字面量 rm -f <路径> 免检 (它不可能删掉一棵树)。
  • 工作区内删除优先 presence_trash:移动到 ~/.dsh/presence-trash/<时间戳>/ 并返回恢复提示。
  • 写入是原子的,保留原文件 mode;presence_edit 锚点不唯一时报错而不是猜。
  • 读后写守卫:内置 read/write/edit 共享读后写守卫;presence_write 也会记账,两套 API 可混用。

配置

插件从宿主进程环境变量读取:

变量 默认 含义
PRESENCE_HOST — 默认 SSH 主机(~/.ssh/config 别名或手动主机)。
PRESENCE_WORKSPACE — host:/abs/path,绑定一个远程工作区。
PRESENCE_PASSWORD_<ALIAS> — 某台机器的密码;<ALIAS> 大写、非字母数字换成 _。
PRESENCE_MAX_CHANNELS 8 每连接并发通道数。
PRESENCE_CONNECT_TIMEOUT_MS 25000 连接尝试超时。
PRESENCE_IDLE_TIMEOUT_MS 900000 空闲连接回收时间(持终端连接不回收)。
PRESENCE_READ_ONLY false 拒绝写入/编辑;读取、搜索、执行不受影响。

完整配置与路径/权限矩阵见 docs/VERIFICATION.md 和 docs/HANDOFF.md。

开发

npm install
npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
  @rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
  @codemirror/theme-one-dark
node scripts/build-client.mjs
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.19.0
# → dist/presence-plugin-0.19.0.zip

验证脚本需要一台可 SSH 的主机(标注“不需要真机”的除外):

node scripts/regression.mjs server     # 传输层、符号链接、超时、二进制
node scripts/job-probe.mjs server      # 后台任务 + 端口转发
node scripts/feature-probe.mjs server  # wait、会话、pgid、就绪、搜索
node scripts/import-probe.mjs server   # 本地→远端二进制、mode、sha256
node scripts/safety-probe.mjs server   # 危险命令护栏 + 观测状态
node scripts/p0-guard-probe.mjs        # 危险命令分段/单文件豁免(不需要真机)

已知边界

  • 挂载路径(~/.dsh/presence/mounts/...)是虚拟的:对 ctx.fs / agent 工具有效, 但 Explorer、Office、宿主 Python 看不到 —— 需要先用 presence_export。
  • presence_exec 超时默认 300 秒;更长的任务必须用 background: true(前台输出超限会 落盘到远端文件,tailLines 保留长报告末尾)。后台任务按会话跟踪,不跨 DSH 重启持久化。
  • 登录节点可能在会话结束时清理残留进程:不要在前台 presence_exec 里用 nohup / tmux new-session / & 跑长任务。用 background: true(setsid 脱离)或 presence_terminal(tmux);集群上让调度器持有任务(rlaunch -d、salloc/sbatch)。
  • 参数别名可用(path/file_path/filePath、old/oldString、new/newString、 replaceAll/replace_all、dryRun/dry_run、cwd/workdir);schema 里记录的是规范名。
  • sshd 的 MaxSessions(默认 10)限制并发通道;连接池最多用 8 个。
  • PTY 终端跟随连接存活;跨重连的 shell 需要目标机有 tmux(presence_terminal { session } 自动使用)。
  • ProxyJump / ProxyCommand 会被解析并提示,但目前不用于实际拨号。
  • @ 文件补全按工作区惰性索引远端树(约 10 秒缓存);超大目录首次查询可能要几秒。
  • 端口自动恢复在连接重建时执行;若保存的本地端口已被占用,会回退到其它空闲端口。

参与贡献

欢迎 issue 和 PR。新功能请先开 issue 描述你的远程工作流;修 bug 请保持 diff 聚焦, 行为变化时在 scripts/ 下补一个探针。

License

MIT

Star History

Star History Chart