ShellRS
基于 GPUI Kit 的中文 SSH 主机管理工具,提供持久化的主机与分组、可在多台主机间共用的登录凭据、本地终端、SSH 多终端、对标 WinSCP 的 SFTP 双栏浏览器和 SSH 端口转发。
标题栏「ShellRS」后面的三个图标切换左侧栏显示的内容:主机列表、端口转发列表或凭据列表。侧栏隐藏时点其中一个会把它重新展开。
ShellRS 同一时间只运行一个:已经开着时再打开一次,会把已有的窗口调到前面,新启动的那个随即退出。两个 ShellRS 各自把主机读进内存再写回同一个数据库,会互相覆盖对方的改动。(用 SHELLRS_DATA_DIR 指到不同数据目录的算不同的 ShellRS,可以同时运行。)要并排看两个终端或 SFTP,把标签拖成分栏即可。
主机列表
左侧栏里保存的每一项是一台主机:地址、端口和认证方式。同一台机器用不同用户登录,就保存成两台主机。
主机对话框的「认证方式」有三种:
- 密码:填用户名和密码,密码保存在系统钥匙串;密码留空则每次连接时询问。只用密码登录,不会先拿 SSH Agent 或私钥去试。
- 使用凭据:选一条保存好的凭据,用户名和登录方式都来自凭据(见下面「凭据」),对话框里不再填用户名和密码。「测试连接」同样使用凭据保存的密码。要用私钥登录,就建一条密钥凭据。
- 无密码:只填用户名。依次尝试服务器免认证、SSH Agent 和
~/.ssh下的默认私钥(id_ed25519、id_ecdsa、id_rsa),和命令行ssh默认的做法一样。服务器要求密码时连接失败并说明原因,不会询问;服务器在密钥之后再要验证码时照样询问。
无论选哪种,连接时都先问服务器是否允许免认证登录,允许就直接登录。
主机对话框「分组」下面的「连接方式」决定怎么连到这台主机,终端、SFTP、端口转发、外部 CLI 和「测试连接」都照它走:
- 直接连接:默认,本机直接连主机的地址和端口。
- SSH 跳板:经过别的已保存主机连过去,支持多级。框里一排标签写出整条路(「本机 → 阿里云99 → 禅道 → 当前主机」),下面每台跳板一行,「×」移除,「添加跳板主机」可按名称或地址搜索;要调顺序就移除后重新添加。连接时先登录第 1 台,由它连第 2 台,依此类推,最后一台连到主机。每台跳板都用它自己的地址、端口、认证方式(密码、凭据或无密码)和主机信任记录,首次连接各问一次主机密钥,询问框里写明是哪台跳板主机;跳板主机自己的「连接方式」在这里不生效,要多级就在这里按顺序列出。主机的地址按跳板主机那边的网络来填,例如内网地址。
- 代理连接:经由 HTTP 代理(
CONNECT)或 SOCKS5 代理连接,在下拉框里选。填代理的地址和端口(例如本机的127.0.0.1:7890);代理需要认证时再填用户名和密码,密码保存在系统钥匙串,经由同一个代理、同一个用户名的主机共用。主机的地址交给代理去解析。代理要求认证却没有密码、或者拒绝了用户名和密码时,连接失败并说明原因,不会询问。
改了一台主机的地址或认证方式,经由它跳转的主机也会重新连接(只改名称不会)。删除一台被别的主机用作跳板的主机,确认框写明有几台主机在用它;删除后那些主机的跳板列表里留下一行「已删除的主机」,连接时直接报错,不会悄悄改成直连或少跳一级,编辑时移除或换一台即可。已经打开的连接不受影响。
尚未实现:跳板和代理叠加使用、拖动调整跳板顺序、HTTPS 代理、在主机列表里标出连接方式。
对话框最后是「备注」,可以写多行(回车换行),记下机器的用途、负责人、到期时间之类,只保存在本地数据库里,不影响连接。
鼠标停在主机上,行的右侧会显示 用户@地址:端口,主机有备注时下面接着显示备注(太长的只显示前 6 行)。右键主机可连接、打开 SFTP、编辑、复制、复制 ID 或删除。「复制 ID」复制的是主机的 16 位随机标识(形如 Jwg5rHvXCxw89paM),供其他工具标识这台主机:它在主机创建时生成,改名、改地址都不变,复制出来的主机得到新的 ID,删掉的 ID 不会再分配。没有打开任何标签时,中间区显示「最近连接」列表,双击或回车连接;右键其中的主机弹出同一个菜单,并选中该主机。
拖动分组或主机到同类项目的上侧 / 下侧可调整同级顺序;主机拖到分组会移入该分组,分组拖到另一个分组的中部会成为子分组。拖到列表空白处可移回根层级。分组始终排在同级主机之前,顺序与分组的展开状态会保存到本地数据库。搜索时暂停拖动排序。
端口转发
点标题栏的端口转发图标,左侧栏换成转发列表。每条转发规则经由一台已保存的主机,启动时为自己建立一条 SSH 连接,不依赖该主机的终端或 SFTP 标签:关掉标签、对主机「断开连接」都不影响它,主机的连接状态也不把它算在内。有转发在运行时,标题栏的端口转发图标旁显示运行数量。
三种转发,都按「入口 → 出口 → 目标」理解,连接从入口进、经 SSH 隧道到出口、再由出口去连目标:
| 类型 | 入口(监听) | 出口 | 用途 |
|---|---|---|---|
本地转发(ssh -L) |
本机 | 服务器 | 在本机访问服务器那边的服务,例如用本机的客户端连只对服务器开放的数据库 |
远程转发(ssh -R) |
服务器 | 本机 | 让服务器那边访问本机的服务,例如把本机开发中的站点给对方看、接收 Webhook |
动态转发(ssh -D) |
本机(SOCKS 代理) | 服务器 | 把服务器当代理,目标由应用自己指定,例如让浏览器经由服务器上网 |
- 新建和编辑:列表标题栏的「+」打开对话框。上方一行三张卡片选类型,中间的示意图随表单实时填入地址、标出入口在哪台机器上,下面一句话说明这条规则的效果(如「在本机连接 127.0.0.1:8080,就等于从 SSH 服务器连接 db.internal:3306。」)。示意图和这句话里的「SSH 服务器」就是「经由主机」选的那一台,图里按它扮演的角色称呼,不写主机的名称。示意图上有一个圆点沿连接方向缓缓移动:先过 SSH 隧道,再到目标,停一下,周而复始;切换类型时从新的入口重新出发。名称可以不填,列表里显示转发摘要(
8080 → db.internal:3306、9000 ← localhost:3000、SOCKS 1080)。 - 启动和停止:每行末尾的按钮,或双击该行、选中后回车;右键菜单里还有编辑和删除。「创建」只保存规则,不会自动启动。勾选「启动 ShellRS 时自动开启」的规则随应用启动。
- 状态:行尾显示正在连接、运行中(及当前连接数)、正在重连或已停止的原因。某个连接转发不了(例如服务器连不上目标)只在该行提示,转发本身继续运行。
- 登录:使用主机的配置、钥匙串里保存的密码和 ShellRS 自己的主机信任记录。首次连接的主机、需要输入的密码会弹出和终端一样的询问框,并注明是哪条转发在连接;取消询问等于停止这条转发。
- 断线重连:连接中断后按 1、3、10 秒自动重连三次,重连期间本机监听端口保留,新进来的连接会被直接关闭。自动重连不弹任何询问:需要重新输入密码、确认主机密钥时停止并写明原因,手动启动即可。三次都失败也会停止,并弹出通知。
- 修改后:编辑运行中规则的类型、主机、监听或目标,或编辑它所经由的主机的地址、认证方式和连接方式(包括这台主机所用凭据的用户名、类型或私钥文件,以及它的跳板主机),这条转发会按新设置重新启动;只改名称或自动开启不会。
- 删除主机:删除主机(或包含它的分组)会连同经由它的转发规则一起删除,运行中的先停止,确认框里写明数量。
- 动态转发支持 SOCKS5(无认证,
CONNECT,IPv4、域名、IPv6)和 SOCKS4 / 4a。域名交给服务器解析。 - 监听地址默认
127.0.0.1。填其他地址时,本地和动态转发会被同一网络中的其他设备使用;远程转发需要服务器的sshd_config设置GatewayPorts clientspecified或yes,其他机器才连得上;默认是no,这时转发照常运行、不报错,但只监听服务器自己的127.0.0.1(和手写ssh -R一样)。对话框在填了非回环地址时会提示这一点。1024 以下的端口需要管理员(服务器上是 root)权限。不支持端口 0(由系统挑选端口)。
尚未实现:通过外部 CLI 管理转发、规则排序。系统的「减少动态效果」设置目前不会关掉示意图的动画。
凭据
点标题栏的凭据(钥匙)图标,左侧栏换成凭据列表。一条凭据是一套可以让多台主机共用的登录信息,包括名称、用户名和登录方式:
| 类型 | 保存什么 | 登录时 |
|---|---|---|
| 密码 | 用户名和密码 | 用凭据保存的密码;没存或被拒绝时询问 |
| 密钥 | 用户名、私钥和口令 | 只用这把私钥;口令按私钥文件保存,用同一个文件的凭据共用 |
| SSH Agent | 用户名 | 只用 SSH Agent 中的密钥:macOS / Linux 上是 SSH_AUTH_SOCK 指向的 Agent,Windows 上是 OpenSSH Authentication Agent 服务 |
- 新建和编辑:列表标题栏的「+」、空列表里的「新建凭据…」或空白处的右键菜单打开对话框;双击凭据、选中后回车或右键「编辑凭据…」编辑。密码和私钥口令存进系统钥匙串,数据库里只有名称、用户名、类型和私钥路径。
- 密钥从哪里来:密钥凭据的「私钥」有三种来源。
- 本机文件:选一个私钥文件,只保存它的路径;文件移动或删除后需要重新选择。
- 粘贴:贴进 OpenSSH、PEM 或 PuTTY 格式的私钥。贴成公钥或认不出的内容时会说明原因。私钥加了口令时才显示口令栏。
- 生成新密钥:选 Ed25519(默认)或 RSA 4096,选中就生成;对话框里显示公钥,旁边可以复制。口令可留空;填了就用它加密私钥,口令存进钥匙串。生成的密钥以凭据名称作为注释。
- 粘贴和生成的私钥由 ShellRS 写成数据目录下
keys/里的文件(随机文件名,只有自己的账户能读),凭据引用这个文件,编辑时显示为「本机文件」和它的路径。之后再粘贴或生成会直接覆盖这个文件,已连接的主机不会因此重连;改成别的文件或别的类型、或删除凭据时,这个文件连同它的口令一起删除,对话框会先提示。用户自己的私钥文件 ShellRS 从不改动或删除。 - 空列表和空白处的右键菜单里另有「生成密钥…」,打开的是同一个对话框,已经选好「密钥 · 生成新密钥」。密钥凭据的右键菜单有「复制公钥」,用来把公钥加到服务器的
~/.ssh/authorized_keys。
- 主机跟着凭据变:改凭据的用户名,所有使用它的主机的用户名一起变。改用户名、类型或私钥文件时,使用它的已连接主机会重新连接(只改名称不会);对话框里写明有几台主机在用。
- 删除:右键「删除」。有主机在用时确认框写明数量和它们之后怎么登录:用密码凭据的主机改为「密码」,连接时询问;用密钥或 SSH Agent 凭据的主机改为「无密码」。用户名不变,密码和私钥不会复制过去。已经打开的连接不受影响,凭据的密码随凭据一起从钥匙串删除。
- SSH Agent 凭据连不上 Agent、Agent 里没有密钥或服务器不接受其中任何一把时,会直接说明原因,不会改为询问密码。「无密码」的主机也会尝试同一个 Agent,连不上时静默跳过,接着试默认私钥(Windows 上同样如此)。
尚未实现:在主机对话框里直接新建凭据、通过外部 CLI 管理凭据、凭据排序、自定义 SSH Agent 的套接字、把公钥自动装到主机上、导出 ShellRS 保存的私钥。
设置
侧栏最下方的「设置」按钮(或 ⌘,,其他平台 Ctrl+,)在中间区打开「设置」标签页;已经打开时切换到该标签,不会重复打开。设置页分两栏:左边是分类(外观、终端、外部 CLI、关于),上方可搜索,栏宽可拖动;右边是所选分类的设置。设置改动立即生效并保存在数据目录的 settings.json 里。
- 外观 → 常规 → 界面语言:跟随系统、简体中文、English,默认简体中文。英文界面尚在翻译中,目前只有 GPUI Kit 组件自带的文字(如搜索框提示)会切换。
- 外观 → 常规 → 应用外观:深色、浅色、跟随系统,默认跟随系统;选「跟随系统」时随系统的深浅色切换。标题栏的主题按钮也会改写这一项。
- 终端 → 字体配置:字体(只列出本机安装的等宽字体,默认是系统的等宽字体,macOS 上为 Menlo)、字号(8–32 像素,默认 13)、行高(字号的 1.0–2.0 倍,默认 1.54,即原来的 20 像素行高)。改动立即作用于所有已打开的终端。下方的预览按终端的排法显示字母、数字、易混字符、符号、中文、制表符和一行命令。设置文件里的字体在本机没有安装时,使用默认字体。
- 外部 CLI:见下一节。
- 关于:「应用更新」一组:当前版本和检查更新、更新渠道、自动升级,见「下载与更新」。
外部 CLI
shellrs 命令让 Codex、Claude Code、OpenCode 等 AI Agent 在 ShellRS 里已保存的主机上执行远程命令、传输文件。命令本身不接触密码和私钥,也不读数据库:它把请求交给正在运行的 ShellRS,由 ShellRS 用保存的配置和钥匙串去连接。请求走本机的进程间通信:macOS、Linux 上是数据目录下的套接字 cli.sock,Windows 上是命名管道 \\.\pipe\shellrs-cli-<数据目录的散列>。
在 设置 → 外部 CLI 中:
- 启用外部 CLI:默认关闭。关闭时 ShellRS 仍在监听,但拒绝所有请求,命令会提示去这里打开。开启后,本机当前用户下的任何程序都能这样使用已保存的主机。套接字只对当前用户可读写,连接时还会核对对方的用户身份;Windows 的管道同样只允许当前用户、只接受本机连接,命令在发请求之前还会确认管道属于当前用户(或管理员),防止别人抢注同名管道。
- CLI 二进制:把
shellrs放进 PATH。- macOS:链接到
/usr/local/bin/shellrs,目录不可写时弹系统授权框。 - Linux:链接到
~/.local/bin/shellrs。 - macOS、Linux 上「移除」只删除指向 shellrs 的链接,同名的其他文件不会被改动。
- Windows:图形程序没有控制台,命令是单独的控制台程序
shellrs-cli.exe(与shellrs.exe放在一起)。「安装」把它复制成%LOCALAPPDATA%\ShellRS\bin\shellrs.exe,并把这个文件夹加入当前用户的 PATH(不需要管理员),重新打开的终端和 Agent 才能找到它。ShellRS 升级后启动时会自动更新这份副本;更新不了时显示「更新」。副本正被使用时也能更新。「移除」删掉副本并把文件夹移出 PATH。
- macOS:链接到
- Agent Skills:把教 Agent 使用
shellrs的 skill 写到通用目录(~/.agents/skills)或 Codex、Claude Code、OpenCode 各自的 skills 目录;内容和当前版本不同时显示「更新」。「复制 skills」把 skill 全文复制到剪贴板。
命令(shellrs --help 查看完整说明):
shellrs list [--query <关键词>] [--json] # 列出主机;输出不是终端时自动用 JSON
shellrs exec <ID> "<命令>" # 或 --stdin 从标准输入读命令
shellrs upload <ID> <本地路径> <远程路径>
shellrs download <ID> <远程路径> <本地路径>
<ID>是 16 位的主机 ID,即shellrs list的id,也就是主机右键「复制 ID」复制的内容。exec每次临时建立连接,执行完即断开。远程命令没有标准输入,退出码就是远程命令的退出码;被信号终止时是 128 加信号编号。- 上传和下载沿用 SFTP 标签的传输引擎(递归、
.filepart、断线重连),目标路径按 scp 的规则:目标是已存在的目录就放进去并保留原名,否则目标就是副本自己的路径,其父目录必须存在。已存在的文件直接覆盖;新建的文件保留源文件的可执行权限。~表示登录目录。 - 失败时在标准错误输出
shellrs: [错误码] 说明,退出码 255。错误码有not_running(ShellRS 未运行)、not_enabled(未启用外部 CLI)、host_not_found、host_key_unknown(还没在 ShellRS 里连过这台主机)、host_key_changed、missing_credential(没有保存密码或口令)、connect_failed、transfer_failed、bad_request、version_mismatch。CLI 不弹任何询问:陌生主机、缺少密码都直接失败,请先在 ShellRS 里连接一次。传输完成但有项目失败时退出码为 1。 - 支持 macOS、Linux 和 Windows。Windows 的 PowerShell 里,多行或带引号的命令用 here-string 经
--stdin传入(@'…'@ | shellrs exec <ID> --stdin);输出有中文时先执行$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::new()。命令文本会去掉开头的 BOM、把 CRLF 换成 LF,远程 shell 不会看到多余的回车符。
标签页
远程终端标签和 SFTP 标签都以主机系统徽章为图标(与主机列表相同)。右键标签可重命名(仅本次运行有效,留空恢复为主机的名称,用于区分同一台主机的多个连接)、在新标签页中连接、打开 SFTP、复制 IP 地址 / 主机名、重新连接、编辑主机,以及关闭当前、左侧、右侧、其他或全部标签。SFTP 标签的菜单是「重命名标签…」(留空恢复为「主机的名称 · SFTP」)、打开 SFTP、复制 IP 地址 / 主机名、重新连接加关闭命令,标签栏右侧和远程终端一样有「SFTP」和「重新连接」按钮;本地终端标签是「重新启动」加关闭命令。标签栏右侧「…」按钮的菜单与当前标签的右键菜单相同。双击任一标签的标题可隐藏或显示左侧的侧栏(同 ⌘B 和标题栏的按钮)。远程终端运行时和 SFTP 标签已连接时,标签栏右侧显示这条 SSH 连接的往返延迟,每 5 秒更新一次:低于 100 ms 为绿色,100–200 ms 为黄色,高于 200 ms 或超时为红色。SFTP 传输进行中也照常测量,读数会随传输占满带宽而升高。批量关闭时,正在上传的 SFTP 标签仍会先确认。
终端
右键终端内容区可复制、粘贴、查找、清屏;远程终端另有打开 SFTP、重新连接和断开连接。断开连接只断开这一个标签,标签保留,可随时重新连接,同一台主机的其他标签和 SFTP 不受影响。
- 查找(⌘F,其他平台 Ctrl+Shift+F):在终端右上角打开查找条,按字面量搜索屏幕和回滚历史。查询全为小写时不区分大小写,含大写字母时区分。所有匹配高亮显示,当前项颜色更深,查找条显示「当前项/总数」。回车或 ⌘G(F3)跳到下一项(更新的输出),Shift+回车或 ⌘⇧G(Shift+F3)跳到上一项(更早的输出),到头后循环。Esc 关闭。打开时如有选中文字,会作为查询词。
- 清屏(⌘K,其他平台 Ctrl+Shift+K;焦点不在终端时 ⌘K 仍是聚焦侧栏的搜索框):清掉屏幕和回滚历史,只留下光标所在的提示符行,包括已输入但未回车的内容。清屏只在本地进行,不向服务器发送任何内容。vim、top 等全屏程序运行时不可用。
SFTP
打开主机的「SFTP」标签即可浏览本地与远程目录;和终端一样,每次「打开 SFTP」都新开一个标签,同一台主机可以开多个,各自独立连接、各看各的目录。操作方式对标 WinSCP Commander。SFTP 使用独立 SSH 连接,仅请求 SFTP 子系统,不要求服务器提供 Shell;关闭终端不会中止传输。
工具栏
每一侧面板都有两行工具栏,下方是路径标签。面板太窄放不下时,按钮从右往左整个隐藏,不会换行(对应的命令在右键菜单和快捷键里仍然可用)。新开的 SFTP 标签本地、远程两侧各占一半;两侧和队列之间的分隔线拖到哪里,在这个标签关闭之前都会记住,切到别的标签再回来也不变:
- 第一行(导航):
- 「目录列表」:列出从根到当前目录的每一级,本地另有主目录、桌面、文稿、下载,选中即前往。
- 书签:点按钮打开「打开目录/书签」(见下);旁边的下拉箭头列出这台主机这一侧的书签,点击即前往,也可添加或移除当前路径。书签保存在本地数据库,删除主机时一并删除。
- 上级目录、根目录、主目录、刷新、后退、前进。
- 第二行(文件):
- 远程是「下载…」,本地是「上传…」,下拉里还有「选择文件上传…」。
- 删除、重命名…、属性…、新建(文件夹… / 文件…)。
路径标签
与 WinSCP 的路径标签相同:
- 显示当前目录的完整路径,例如
/home/tester/。 - 每一级都可以点:悬停时从根到该级高亮,单击即前往。
- 当前使用的一侧(最近点过的一侧)底部有一条深蓝色线。
- 路径放不下时保留根目录和末尾几级,中间折成
…,点…前往被折叠的最后一级。 - 单击当前目录、双击路径右侧空白处打开「打开目录/书签」。
- 右键:前往、刷新、添加路径到书签、复制路径、打开目录/书签…。
打开目录/书签
与 WinSCP 的「打开目录」对话框相同,可以从路径标签、书签按钮、⌘O(其他平台 Ctrl+O;macOS 另有 ⌘⇧G)或路径标签的右键菜单打开:
- 「目录」预填当前目录并已选中,直接输入即可替换。
~表示主目录,相对路径从当前目录算起。本地侧另有「浏览…」。 - 下方是这台主机这一侧的书签。与目录相同的书签处于选中状态;单击书签把它填入目录,双击直接打开。
- 「添加」把目录加入书签,「删除」(或在列表中按 Delete)删除选中的书签并选中相邻的一个,「上移」「下移」调整顺序。
- 书签的改动立即保存,按「取消」也不会撤销,与 WinSCP 相同。
- 回车或「打开」前往目录;Esc 或「取消」关闭。
列表下方的状态栏平时显示项目数和已选数量;连接中和读取目录超过 0.3 秒时显示「正在连接 SFTP…」或「正在读取目录…」。读不了目录或连接断开时,原因用红字显示在窗口最左下角(平时显示「已连接 主机的名称」的位置);空闲时断网也会自己发现,不用等到刷新,时机和同一台主机的终端一样;重新连接用标签栏的「重新连接」按钮或标签菜单;断开时在远程列表里做任何操作(刷新、打开目录、删除、上传下载等)会弹出「SFTP 连接已断开」对话框,说明原因,并可直接「重新连接」。这些都不会让列表移动。
列与 WinSCP 相同:本地为名称、大小、类型、修改时间,远程为名称、大小、修改时间、权限、所有者。
- 大小默认以 KB 显示(
4,008,960 KB,向上取整,只有空文件是 0 KB),与 WinSCP 相同。右键「大小」列标题可切换为「字节」(4,105,175,040 B)、「千字节(KB)」或「简短格式」(按大小用 B、KB、MB、GB,如3.8 GB),对所有 SFTP 标签生效并保存在设置里。按大小排序始终按实际字节数。 - 时间形如
2026/4/22 12:44:53。 - 权限形如
rwxr-xr-x。 - 所有者来自服务器的
ls -l行。 - 链接到目录的符号链接显示为带箭头的文件夹,可以双击进入。
选择与右键菜单
- 与 WinSCP 关闭「整行选择」时相同,「名称」格才是项目:鼠标移上去时只有名称格变灰,单击、双击、右键都作用在名称格上,选中时整个名称格高亮;行里的其他列和列表下方都算空白处。
- 鼠标:单击名称格选中(其他选择取消),⌘ 单击(其他平台 Ctrl 单击)增减,Shift 单击选取范围,双击打开。单击空白处取消全部选择(按住 ⌘ 或 Shift 时不取消)。
- 框选:在名称右边的空白、大小、类型、时间等列或列表下方按下拖动,画出选框,框住的行被选中;按住 ⌘ 或 Shift 开始拖动则加到已有选择上。拖到列表上下边缘时自动滚动。拖动文件名才是把文件拖到另一侧传输:按下未选中的文件名时只选中它(其他选择取消),拖走的就是它一个;按下已选中的文件名则拖走全部已选。拖动时只有到了另一侧列表上才显示「上传 / 下载 N 个项目」,在自己的列表或其他不能放下的地方,指针显示为禁止。
- 键盘:↑↓、Home、End、PageUp、PageDown 移动,加 Shift 扩展;空格或 Insert 切换当前行;⌘A 全选;Tab 切到另一侧。
- 右键未选中的名称格会先选中它;右键空白处取消选择并弹出空白处菜单。
- 文件名上的菜单:打开、上传… / 下载…、删除、重命名…、属性…。
- 空白处的菜单:前往(上级 / 根 / 主目录 / 后退 / 前进)、刷新、添加路径到书签、新建。
快捷键
| 命令 | 快捷键 |
|---|---|
| 上传 / 下载(按所在面板) | F5 |
| 重命名 | F2 |
| 新建文件夹 | F7 |
| 删除 | F8、Delete;macOS 另有 ⌘⌫ |
| 属性 | F9;macOS 另有 ⌘I |
| 刷新 | ⌘R / Ctrl+R |
| 上级目录 | Backspace;macOS 另有 ⌘↑ |
| 根目录 | ⌘\ / Ctrl+\ |
| 主目录 | macOS ⌘⇧H,其他平台 Ctrl+H |
| 后退 / 前进 | ⌥← / ⌥→;macOS 另有 ⌘[ / ⌘] |
| 添加路径到书签 | ⌘D / Ctrl+D |
| 打开目录 | Enter;macOS 另有 ⌘↓ |
| 打开目录/书签 | ⌘O / Ctrl+O;macOS 另有 ⌘⇧G |
文件操作
- 删除前会确认,并写明对象和后果:
- 本地项目移到废纸篓。
- 远程项目永久删除,目录连同其中全部内容。
- 删除符号链接只删除链接本身。
- 重命名和新建:
- 按当前列表检查名称;不能为空,不能是
.或..,不能与已有项目同名。 - 重命名不会覆盖已有项目。
- 按当前列表检查名称;不能为空,不能是
- 属性:
- 显示位置、大小、修改时间,远程另有所有者和组。
- 权限用 3×3 复选框和八进制数字编辑,两者同步。
- 多选时,项目之间不一致的权限位保持原样,除非你改动它。
- 含目录时可以递归应用;递归时默认「为目录添加执行权限(X)」,避免目录无法进入。
- 符号链接不参与修改权限。
上传与下载
- 入口:
- 上传:「上传…」、本地列表中的 F5、「选择文件上传…」,或按住本地项目的文件名拖到远程列表,或把 Finder 中的文件拖到远程列表。
- 下载:「下载…」、远程列表中的 F5,或按住远程项目的文件名拖到本地列表。
- 拖到目录行时放进该目录,拖到其余区域时放进当前目录。
- 所有入口都显示确认框,可以修改目标目录;下载可以用「浏览…」选择本机目录。确认框写明数量和种类(如「上传 2 个文件到 …」),来源的所在目录只写一次,每个项目占一行,悬停可看完整路径。
- 确认后来源、端点和目标即固定。每个 SFTP 标签同时运行一个批次(上传和下载共用),批内依次处理;传输进行中还可以继续上传或下载,新的批次排进传输队列,前一批结束后自动开始。其他 SFTP 标签、主机和终端可以继续操作。
- 递归传输包含空目录。符号链接原样创建,不跟随。默认二进制传输,保留普通文件的修改时间。
- 新建项使用目标一侧的默认权限,覆盖时保留目标原有的基本权限。不复制所有者、ACL 或扩展属性。
- 同名目录合并。文件或链接冲突时询问覆盖、跳过或取消,可以应用于本批剩余冲突;目录与非目录冲突不会自动删除。
- 出错时可以重试、跳过或取消,完成后的汇总分别列出成功、跳过和失败。
传输队列
与 WinSCP 的队列面板相同,有传输时出现在双栏下方,高度可拖动,清除全部已结束的批次后收起。
- 每一批一行:操作(上传 / 下载)、来源(一个项目时是它的路径,多个项目时是所在目录)、目标、已传输(单位与「大小」列相同)、时间(剩余时间)、速度(最近几秒)和进度。
- 进行中的批次显示总进度条,下面一行是正在传的文件和它自己的进度条,对应 WinSCP 进度对话框里的两个进度条。
- 进度列:等待中、正在扫描、百分比、等待回答、重连说明、已停止、已完成、部分失败(N 项)、已移出。
- 点行首的箭头展开这一批的逐项结果(完成、已跳过、失败及原因),最新的在最上面。
- 工具栏:继续(停止的批次)、停止(进行中的批次)、移出队列(选中的排队中或已结束的批次,或停止的批次)、丢弃续传进度、清除已完成。
- 停止的批次留在队首,等你继续、丢弃续传进度或移出队列之后,后面的批次才会开始;移出队列不删除续传记录,下次传同样的文件仍会询问是否续传。
- 队列只在本次运行中保留,关闭 SFTP 标签时一起清除;有未完成的批次时关闭标签会先确认。
断点续传和替换
文件先写入 <目标文件名>.filepart。写入请求采用有界流水线,收到每个写入确认并关闭成功后才发布正式文件;服务器支持时先调用 fsync@openssh.com。支持 posix-rename@openssh.com 的服务器使用原子替换;其他服务器先将旧文件移到 .shellrs-<UUID>.backup,再发布新文件,此兼容路径不保证原子性。发布响应丢失时根据受管理临时路径、阶段、类型和大小恢复,无法安全判断则保留恢复文件并报告。
续传记录保存在应用数据目录的 upload-resume/,采用版本化 JSON 和原子写入,不写入密码、口令或私钥,不改变数据库结构。记录用于绑定端点、主机指纹、来源、目标和安全替换阶段。续传方式与 WinSCP 相同:读取远端 .filepart 的当前大小,在本地文件中跳过相同长度并继续传输;使用者需确保来源仍是同一版本。上传过程不会为了校验而回读远端文件内容。
下载同样先写入本机的 <目标文件名>.filepart,完成后设置远程的修改时间、fsync,再在同一文件系统内重命名为正式文件(原子替换)。并发的读取请求可能乱序、短读返回,但临时文件严格按顺序写入,因为续传只信它的长度。下载续传记录保存在 download-resume/,绑定端点、主机指纹、远程来源及其大小与修改时间、本地目标;远程文件变化后不能续传,只能重新下载或跳过。
网络故障每批最多自动重连三次,间隔 1、3、10 秒。耗尽后使用「继续上传」或「继续下载」,手动恢复会重置次数。认证失败、主机密钥变化和主动取消需要用户处理。重连沿用原连接配置,编辑主机不会改变正在传输的目标。
取消、关闭标签、断开连接或退出时保留续传记录;关闭正在传输的标签会先确认。重启后不会自动继续,再次选择相同来源和目标时会询问续传。停止状态下可使用「丢弃续传进度」清理该批受管理的临时文件;存在无法判断的恢复备份时会保留文件并说明原因。成功发布的项目自动清理记录和备份。
文件名不能准确表示时会明确报错。当前 russh-sftp 3.0.0 的远端字符串解码会替换无效 UTF-8,浏览器因此拒绝包含替换字符 U+FFFD 的远端名称,避免访问错误路径。符号链接沿用 OpenSSH 的 SFTP v3 参数顺序。
尚未实现:目录同步、前台传输进度对话框、队列排序、过滤、查找文件、目录树和 Dock 布局持久化。
下载与更新
安装包在 shellrs.com/download:
| 系统 | 首次安装 | 自动更新 |
|---|---|---|
| macOS(Apple Silicon 和 Intel 共用一个包) | DMG,把 ShellRS 拖进「应用程序」 | 支持 |
| Windows x64 | 安装程序,按当前用户安装到 %LOCALAPPDATA%\Programs\ShellRS,不需要管理员 |
支持 |
| Linux x64 | AppImage | 支持 |
ShellRS 启动 30 秒后检查一次有没有新版本,之后每 6 小时检查一次。有新版本就在后台下载并校验,完成后标题栏右上角出现更新图标。点开可以看更新内容,点「重启并安装」就换成新版本并重新打开。重启会断开已打开的连接、停止传输(续传进度保留)和端口转发,对话框里会写明具体有哪些。
「设置 › 关于」里的「应用更新」只有三行:
-
当前版本:下面写着现在的状态(已是最新、正在下载、已下载等),右边是版本号和一个按钮:平时是「检查更新」,下载好之后变成「重启并安装…」。构建号和平台在版本号的悬停提示里。
-
更新渠道:稳定版适合日常使用;Beta 可以提前收到测试版本,稳定版发布时也会收到。切换后马上按新渠道检查(关掉自动升级时等你手动检查)。
-
自动升级:关掉之后只在手动检查时才联网,发现新版本时由你决定是否下载。
-
校验:更新清单由 ShellRS 的发布密钥签名,安装包按清单里的大小和 SHA-256 核对,任何一步对不上都不会安装。macOS 上还会核对新版本的开发者签名。
-
隐私:检查更新只发送 ShellRS 的版本号、操作系统和 CPU 架构,不发送任何能识别这台电脑或用户的信息。
-
不能自动更新的情况:从 DMG 里直接运行、没有放进「应用程序」文件夹、对安装位置没有写权限、Linux 上不是用 AppImage 运行,或者是自己编译的开发构建。这时「关于」页会说明原因,有新版本时给出「前往下载页」。
-
退回旧版本:数据库在升级到新结构之前会备份成
shellrs.db.v<旧版本>.bak。旧版本打开新结构的数据库时会拒绝打开,以免把新版本保存的数据弄乱;需要退回时,把备份改名回shellrs.db。
发布
在 CHANGELOG.md 里写好 ## [x.y.z] 一节,把 Cargo.toml 的版本改成 x.y.z,提交后推送 tag vx.y.z。.github/workflows/release.yml 依次:
- 核对 tag 与
Cargo.toml一致,截出更新说明,确认代码里有发布公钥; - 构建并打包:macOS 合成 universal,签名、公证、staple,产出升级用的
.app.zip和首次安装用的.dmg;Windows 用 Inno Setup 打安装程序;Linux 打 AppImage; - 在
release环境里人工批准后:给每个包签名,上传到 R2(dl.shellrs.com/releases/<版本>/),建 GitHub Release,最后才替换更新清单dl.shellrs.com/update/v1/<通道>.json并清 CDN 缓存。
版本号带 - 的(如 0.3.0-beta.1)只发到 beta 通道;正式版同时成为 beta 通道的最新版。在 Actions 里手动运行这个 workflow 只构建打包、不发布,用来演练。客户端只接受比自己新的版本,所以发错的版本撤不回来:把清单改回上一版能阻止更多人升级,修复要发新的补丁版。
一次性准备:
- 更新签名密钥:
minisign -G -p shellrs-update.pub -s shellrs-update.key生成两对(一对日常用,一对离线保存备用),把两个公钥(.pub文件的第二行)填进src/update/build_info.rs的TRUSTED_KEYS。私钥和口令只放进 GitHub 的release环境,另外离线备份;丢了私钥,已发出去的 ShellRS 就再也收不到更新。换钥匙时先发一版带上新公钥,再换签名用的私钥。 - Cloudflare:shellrs.com 的 DNS 托管在 Cloudflare;建 R2 bucket,绑定自定义域
dl.shellrs.com,关闭r2.dev地址;Cache Rules 让/releases/*和/update/*按源站的 Cache-Control 缓存(默认不缓存.json、.AppImage);一个只能写这个 bucket 的 R2 API Token,一个只能清这个 zone 缓存的 API Token。 - Apple:Developer ID Application 证书(导出 .p12)和 notarytool 用的 App Store Connect API Key。
packaging/macos/Info.plist.in里的 bundle idcom.shellrs.ShellRS和签名的 Team ID 一经发布就不能再改:钥匙串按它们授权,改了之后每条保存的密码都要重新允许。 - 图标:
assets/logo/里的shellrs.icns(macOS)、shellrs.png(1024×1024,Linux,macOS 没有 icns 时也由它生成)和shellrs.ico(Windows 安装程序)。 - GitHub secrets:仓库级的
APPLE_CERTIFICATE_P12(base64)、APPLE_CERTIFICATE_PASSWORD、APPLE_SIGNING_IDENTITY、APPLE_TEAM_ID、APPLE_API_KEY_P8(base64)、APPLE_API_KEY_ID、APPLE_API_ISSUER;release环境(设为需要审批)里的MINISIGN_SECRET_KEY、MINISIGN_PASSWORD、R2_ACCOUNT_ID、R2_ACCESS_KEY_ID、R2_SECRET_ACCESS_KEY、R2_BUCKET、CF_ZONE_ID、CF_API_TOKEN。
清单格式见 packaging/manifest.example.json,由 packaging/make-manifest.sh 生成:一个文件里是清单原文和它的 minisign 签名,签名的 trusted comment 写明通道和版本。格式只增加字段、不改已有的;已发出去的 ShellRS 一直按 /update/v1/ 这个地址检查。
运行和验证
工具链固定 Rust 1.98.1,russh-sftp 固定 3.0.0;保持仓库锁定的 GPUI 与 russh 版本。
cargo run
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --check
数据目录默认是系统应用数据目录中的 shellrs,可通过 SHELLRS_DATA_DIR 覆盖。主机、端口转发规则和凭据(不含密码)保存在 shellrs.db,主机信任记录保存在该目录的 known_hosts,凭据里粘贴或生成的私钥保存在该目录的 keys/ 下,密码和私钥口令使用系统钥匙串(服务名 shellrs;账户名是主机端点的 password:<用户>@<地址>:<端口>、私钥的 passphrase:<路径>、密码凭据的 credential:<随机 ID> 和代理的 proxy:<用户>@<代理地址>:<端口>)。
src/sftp/tests.rs 使用临时目录与故障注入覆盖上传和下载的文件内容、空目录、链接、覆盖、权限、取消、源文件变化、乱序与短读、续传及替换各阶段恢复,以及删除、重命名、新建和递归改权限不跟随链接。Unix 协议测试使用本机 OpenSSH sftp-server(macOS /usr/libexec/sftp-server,Linux /usr/lib/openssh/sftp-server);工作线程测试仅监听 127.0.0.1 的随机端口,使用测试凭据。限制本地套接字的沙箱会跳过该部分,需要在允许回环套接字的环境运行。
src/forward/protocol_tests.rs 用进程内的 SSH 服务器和本机的回显服务测试真实的转发工作线程:三种转发的往返、目标不可达、服务器禁止转发、端口被占用、空闲断线后重连、重试耗尽和停止后的清理,同样只监听 127.0.0.1 的随机端口。
tests/ui/ 里的 UI 测试驱动真实 Workspace,通过 Workspace::new_with_services 注入 SFTP、本地目录、终端和端口转发服务,不连接用户服务器或真实钥匙串;在线升级的测试给 workspace.updater() 注入假的更新服务器和安装器,不联网、不改任何安装。Finder 的系统原生拖放仍需在真实 macOS 窗口中补充人工验收;无头测试覆盖原生文件拖入事件和面板内部拖放。
开发构建(cargo run)不检查更新。要在本机走一遍更新流程,用 debug 构建加环境变量 SHELLRS_UPDATE_CHANNEL=beta 编译,运行时用 SHELLRS_UPDATE_URL=http://127.0.0.1:8000/{channel}.json 指向本地的静态服务器(python3 -m http.server),清单用 packaging/make-manifest.sh 和自己生成的测试密钥签名,测试公钥放在环境变量 SHELLRS_UPDATE_PUBLIC_KEY 里。release 构建不认这两个环境变量。
上传交互参考 WinSCP 上传流程,协议适配参考 russh-sftp 请求接口 与 OpenSSH 扩展规范。