定位:十个
ssh_workspace_*工具的用途、参数与返回。
这些工具都不复制文件能力:文件内容由 ctx.fs(SFTP)服务,read / write /
edit 已经能在远端工作区工作;会话工作区为 ssh:// 时,pwsh / bash 工具的
命令也由本插件路由到远端执行,glob / grep 的 ripgrep 与工作区变更追踪的 git
同样(走 ctx.subprocess 接管)。工具补齐的是主机发现、挂载/卸载、状态,
以及不依赖挂载的远端命令执行。
列出可用主机:存储条目 + ~/.ssh/config 的具体 Host 块。
| 参数 | 必填 | 说明 |
|---|---|---|
query |
否 | 大小写不敏感,匹配 alias / host / user / description / tags |
返回 hosts[],每项含 alias、host、port、user、auth
(key / password / agent)、keyReady、source(store / ssh-config)、
description、tags。
把远端目录挂载为工作区。挂载前会先 stat 验证目录真实存在且是目录,
失败返回明确错误(no such directory on <alias>: <path>)。
| 参数 | 必填 | 说明 |
|---|---|---|
alias |
是 | 来自 ssh_workspace_hosts |
remotePath |
是 | 远端绝对目录路径,如 /home/user/project |
title |
否 | 工作区显示名(默认取目录名) |
register |
否 | 是否同时登记为持久工作区(默认 true) |
返回 ok、workspacePath(ssh:// 拼写,可直接用作会话工作区)、title、
registered、workspaceId、entryCount、error。
登记失败时仍挂载在进程内,error 给出登记失败的原因。
停用一个已挂载的远端目录。默认不删除任何东西:工作区记录保留(侧边栏那一行 仍在,置灰且按钮不可点),会话也保留不归档——记录还在,那个分组就还在,把会话藏起来 等于把用户特意保留的分组清空。
停用后该工作区内的所有操作都被拒绝——读文件、列目录、写、变更监视、执行命令、
开终端、跑 glob / grep / git、传输文件;SSH 连接本身保留(主机面板、
不带 cwd 的 ssh_workspace_exec、端口转发都照常可用)。
| 参数 | 必填 | 说明 |
|---|---|---|
workspacePath |
是 | ssh_workspace_mount 返回的 ssh:// 路径 |
mode |
否 | detach(默认,保留工作区记录)或 deregister(同时删除记录) |
archiveSessions |
否 | 是否归档会话。默认跟随模式:detach 为 false,deregister 为 true。显式传值优先 |
返回 ok、mode、wasMounted、deregistered、archived[](成功归档的会话 id)、
archiveFailed[{ sessionId, error }]、error。
两个模式的作用范围(这是最容易搞错的一点):
| 模式 | 工作区记录 | 侧栏那一行 | 面板「已停用」那一行 | 会话 |
|---|---|---|---|---|
detach(默认) |
保留 | 保留,置灰且按钮不可点 | 保留,可「启用」 | 保留,不归档 |
deregister |
删除 | 消失 | 消失 | 归档 |
「保留记录却不保留会话」是自相矛盾的:detach 留住侧栏那个分组,就是为了让用户还能
点开它;归档等于把分组内容清空——行还在,点开却是空的,比删掉更让人困惑。所以归档
跟随「删除记录」,而不是一个独立开关。
deregister 会同时清掉持久停用清单与运行时围栏两处标记。只删记录而不清
标记,会留下一个「面板里还挂着启用按钮、实际任何地方都不存在」的工作区,而这个
路径的运行时围栏又拒绝一切操作——用户无从恢复。forget 作为旧拼写被接受并归一化
为 deregister,两者不会各自漂移。
归档是逐个进行的,失败不中断:有正在跑回合 / 子智能体 / 后台任务的会话会被
registry 拒绝(这是它的文档化行为,本工具不传 stopActivity,因为卸载工作区不等于
要求中止正在进行的对话)。被拒的会话留在列表里,并逐条出现在 archiveFailed。
本工具没有「删除会话」选项。DSH 没有公开的会话删除 API,绕过服务层直接 unlink 会话日志会留下孤儿数据,所以归档是这里能给出的最强且可恢复的语义。
把一个被停用的工作区重新挂载,并取消归档它的会话。这是 detach 的逆操作。
| 参数 | 必填 | 说明 |
|---|---|---|
workspacePath |
是 | 之前停用的 ssh:// 路径 |
unarchiveSessions |
否 | 是否取消归档会话(默认 true) |
返回 ok、workspacePath、registered、unarchived[]、
unarchiveFailed[{ sessionId, error }]、error。
启用前会先 stat 验证远端目录仍然存在——否则会得到一个「看起来在服务、实际
ENOENT」的工作区。工作区记录若已被 deregister 模式删掉,会重新
createCanonical 建一条。
无参数。返回当前挂载点 mounted[]、已停用的工作区 disabled[]、活动连接
connections[{ alias, state, inFlight }](state 为 connected / broken)、
打开中的端口转发 forwards[]、持久会话 shells[{ alias, session, alive }],
以及 fsPatched(远端服务是否在役)。
mounted 与 disabled 是互斥的两个集合:停用会把路径从前者移到后者。
在远端执行 shell 命令,不依赖工作区挂载:主机巡检、挂载前查看目录、
会话工作区之外的路径都走它(工作区内的日常命令直接用 pwsh / bash 工具,
ssh:// workdir 已被路由到远端)。
每次调用是一个全新进程:cd、导出的变量、后台任务都不会保留。需要保留
状态时用 ssh_workspace_shell。
| 参数 | 必填 | 说明 |
|---|---|---|
alias |
是 | 主机别名 |
command |
是 | 远端 shell 命令 |
cwd |
否 | 远端工作目录 |
timeoutMs |
否 | 超时毫秒(默认 60000);run_in_background 时忽略 |
run_in_background |
否 | 作为后台任务启动,立即返回 job id |
返回 success、exitCode(整数或 null)、timedOut、stdout、stderr、
truncated、durationMs、error;后台模式下额外返回 jobId,用
job_output / job_kill 收集与终止。部署没有 ctx.jobs(或该 owner 无控制器)
时自动内联执行,不会返回无人能读的 id。
cwd 落在已停用的工作区内时调用被拒绝;不带 cwd(登录目录)或 cwd 在工作区
之外照常执行——这正是「停用工作区但保留 SSH 连接」的含义。
在持久的远端 shell 里执行命令:同一 (主机, 会话名) 上维持一个 POSIX sh,
cd、导出的变量与后台任务跨调用保留,等同于一直使用同一个终端。
远端 shell 固定是 POSIX sh,即使本机是 Windows 也请写 POSIX 语法。
部署自带的 persistent bash / pwsh 工具按本机方言构造命令行(Windows 上是
PowerShell),无法驱动远端主机,因此本插件单独提供这个工具而不是接管它们。
| 参数 | 必填 | 说明 |
|---|---|---|
alias |
是 | 主机别名 |
command |
否 | POSIX shell 命令;close: true 时可省 |
session |
否 | 会话名(默认 default),不同名字互不干扰 |
close |
否 | 关闭该会话并释放,而不是执行命令 |
timeoutMs |
否 | 等待命令结束的毫秒(默认 60000) |
返回 ok、output、exitCode、session、reused(本次调用是否复用了已有
会话);关闭时返回 closed(关闭的会话数)。
退出码通过命令行末尾追加的标记行取回:PTY 没有独立退出通道,$? 被立即
捕获并随标记一起打印,因此输出里的标记行不会混进 output。
在本机与远端之间经 SFTP 双向拷贝文件,字节不经过对话内容。适合推送构建 产物、拉取日志;不用于编辑文件(挂载工作区里的 read / write / edit 已经 直接读写远端)。
| 参数 | 必填 | 说明 |
|---|---|---|
alias |
是 | 主机别名 |
direction |
是 | upload(本机 → 远端)或 download(远端 → 本机) |
localPath |
是 | 本机绝对路径(上传的源 / 下载的目标) |
remotePath |
是 | 远端绝对路径(上传的目标 / 下载的源) |
recursive |
否 | 上传整个本地目录树(仅 upload) |
返回 ok、bytes、files(递归时)、error。
上传先写同级暂存名再 rename 发布:目标文件在被替换之前,读到的始终是旧 内容或完整新内容,不会是半截文件。上传失败会清掉暂存文件并保留目标原内容。 下载读至 EOF 而非依赖 stat 时的长度,因此传输期间变长的文件会被完整取回。 单文件上限 1 GiB。
浏览器端另有 GET /download?alias=&path= 流式路由:文件选择对话框里每个文件行
有下载按钮,走这条路由把字节直接写进响应(不是 JSON),交给浏览器保存。它自行
施加回环来源与 Host 头双重校验,只接受 GET。
把远端端口映射到本机(等价 ssh -L):远端 dev server 变成可打开的
http://localhost:<port>。映射骑在连接池里的 SSH 连接上,连接断开即自动关闭;
重复映射同一目标返回已有条目。注意远端应用可能有自己的 host / 回调白名单
(Vite 的 server.allowedHosts、Next 的 allowedDevOrigins、OAuth 的
redirect URI),需要一并放行 localhost。
| 参数 | 必填 | 说明 |
|---|---|---|
alias |
是 | 主机别名 |
remotePort |
是 | 远端端口 |
localPort |
否 | 本地端口,默认同远端;被占用时自动另选,传 0 由系统分配 |
remoteHost |
否 | SSH 服务端拨号的目标(默认 127.0.0.1) |
localHost |
否 | 本地监听地址(默认 127.0.0.1,仅本机;0.0.0.0 暴露到局域网) |
关闭一条端口转发并释放本地端口。
| 参数 | 必填 | 说明 |
|---|---|---|
id |
是 | 来自 ssh_workspace_forward 或 ssh_workspace_status |
- 路径一律使用
ssh://实际拼写(不含显示用的@); - 错误语义与
ctx.fs同一套(FS_*码,另加远端专用的FS_OUTSIDE_WORKSPACE), 写入围栏对工具触发的写同样生效;read-only会话会拒绝远端write/edit(FS_SANDBOX_DENIED),ssh_workspace_transfer的上传同样是写入,也受该模式约束 (工具自己从会话解析sandboxPolicy,因为它不经ctx.fs);下载只读远端、写本机, 因此在read-only下照常可用; - 命令执行同样受模式约束:
ssh_workspace_exec、ssh_workspace_shell在read-only下直接拒绝(返回success: false/ok: false,文案自述原因与出路),因为本机沙箱跨不过 SSH,没有可用的围栏手段。ssh_workspace_shell的close: true不受限——关闭不改变远端任何东西。 这三处与write/edit/ 上传共用lib/mode-gate.js的判定,不会各自漂移; - glob / grep 在远端工作区调用远端主机的 ripgrep(需远端已安装
rg, 缺失时命令以退出码 127 失败并附安装提示);工作区变更追踪用远端 git (未安装时该功能自动降级,不阻断会话)。本地工作区不受影响。 - 挂载时探测
rg/git/inotifywait,缺失项在挂载结果与面板里直接提示; 远端有inotifywait时变更监视走事件推送,否则回落到轮询。 - 已停用的工作区对所有读写 / 命令 / 传输一律拒绝,错误信息会点名 "the remote workspace is detached";详见 unmount-modes.md。