255 lines
12 KiB
Markdown
255 lines
12 KiB
Markdown
# herdr-hub
|
||
|
||
`herdr-hub` 是一个 Linux 下的 Herdr session 切换器。它让 tmux 做最外层路由,
|
||
把每个本地或 `herdr --remote` attach 放进独立 tmux window,并在 Herdr 画面顶部
|
||
显示可点击的 Sessions 条。
|
||
|
||
它不是 Herdr 插件,也不会修改 Herdr core。Herdr 0.8.2 的插件 API 不能向原生
|
||
sidebar 插入 UI,也不能让 thin client 从一个 server 原地改连另一个 server;
|
||
所以顶部状态栏横跨整个终端,而不是只嵌在 Spaces 上方。
|
||
|
||
## 功能
|
||
|
||
- 自动读取 `herdr session list --json`,导入本机正在运行的 sessions。
|
||
- 记住使用过的远端,并通过 SSH 实时读取运行中及已停止的 Herdr sessions。
|
||
- 远端查询兼容 Windows OpenSSH 与 Linux/macOS shell;Windows target 自动改用并持久记住 SSH TTY 模式。
|
||
- 在 Linux `/proc/*/cmdline` 中查找仍在运行的 `herdr --remote …` clients,并自动记住目标。
|
||
- 首次接管已有 remote client 时安全地再 attach,不结束原进程。
|
||
- 一个 endpoint 对应一个 tmux window,隐藏 window 中的连接继续在线。
|
||
- 顶部 Sessions 条在窄屏下也为每个 Tab 显示主机优先的短名称,并支持鼠标点击、数字切换和无需 fzf 的动态 popup 选择。
|
||
- 可隐藏不再需要的 Tab,而不停止底层本地或远端 Herdr Session。
|
||
- 可在 Sessions 管理弹窗中为 Tab 设置持久化显示名称,或恢复默认名称。
|
||
- dead window 留在顶栏,可原地重连。
|
||
- 不修改 `~/.tmux.conf`,不保存 SSH 凭据或远端 Session 缓存。
|
||
|
||
## 依赖
|
||
|
||
- Linux
|
||
- Python 3.11+
|
||
- tmux 3.4+
|
||
- Herdr 0.8.2+
|
||
- OpenSSH client
|
||
|
||
检查当前环境:
|
||
|
||
```bash
|
||
./bin/herdr-hub doctor
|
||
```
|
||
|
||
## 安装
|
||
|
||
直接从仓库运行:
|
||
|
||
```bash
|
||
./bin/herdr-hub
|
||
```
|
||
|
||
或把启动器链接到 `~/.local/bin`:
|
||
|
||
```bash
|
||
make install
|
||
herdr-hub doctor
|
||
```
|
||
|
||
安装脚本只创建一个指向本仓库的符号链接。移动或删除仓库后需重新安装。
|
||
|
||
## 第一次接管
|
||
|
||
如果本地和 remote Herdr clients 已经分别开在其他终端中:
|
||
|
||
1. 保持这些 clients 运行。
|
||
2. 从一个新的、未嵌套在 tmux 中的终端执行 `herdr-hub`。
|
||
3. Hub 扫描现有 clients,并为每个 endpoint 创建一个新的 attach window。
|
||
4. 确认 Hub 中可以正常操作后,手动退出原来的 clients。
|
||
|
||
tmux 不能把另一个终端里已经运行的进程搬进 window,所以首次接管会短暂存在两个
|
||
clients;它们连接的是同一个 Herdr server,不会复制 server 或 panes。
|
||
|
||
只有一个终端时,本地 Herdr 可以先 detach,因为 `herdr session list` 仍能发现后台
|
||
server。remote target 在 client 退出后没有本地记录,因此直接使用:
|
||
|
||
```bash
|
||
herdr-hub open --remote orangepi
|
||
herdr-hub open --remote orangepi --session work
|
||
herdr-hub open --remote ssh://me@server:2222 --remote-keybindings server
|
||
herdr-hub open --remote windows-host --transport ssh
|
||
```
|
||
|
||
打开本地 named session:
|
||
|
||
```bash
|
||
herdr-hub open --session work
|
||
```
|
||
|
||
`open` 会把 endpoint 保存在当前 tmux hub 的 window metadata 中。远端 target 还会
|
||
同时加入 `~/.config/herdr-hub/remotes.json`,以后可直接从 Sessions
|
||
弹窗浏览这台机器上的其他 Session。
|
||
|
||
## 日常操作
|
||
|
||
Hub 使用独立 tmux socket `herdr-hub`,不会接管普通 tmux server。外层前缀固定为
|
||
`Ctrl+A`,Herdr 原来的 `Ctrl+B` 可以直接使用。
|
||
|
||
| 操作 | 快捷键 / 命令 |
|
||
| --- | --- |
|
||
| 点击切换 | 鼠标点击顶部 session |
|
||
| 打开 Session 弹窗 | 点击左上角 `Sessions` 或 `Ctrl+A g` |
|
||
| 上一个/下一个 window | `Ctrl+A p` / `Ctrl+A n` |
|
||
| 按编号切换 | `Ctrl+A 0..9` |
|
||
| 扫描新增 sessions | `Ctrl+A r` 或 `herdr-hub refresh` |
|
||
| 强制重连当前 window | `Ctrl+A R` |
|
||
| 关闭/隐藏当前 Tab | `Ctrl+A q`,确认 `y`;也可在 Sessions 弹窗中选择 |
|
||
| 关闭并停止当前远端 Session | Sessions 弹窗 → `关闭并停止当前 Session`;最后一个 Tab 会保留为断线状态 |
|
||
| 修改 Tab 显示名称 | Sessions 弹窗 → `管理 Session 显示名称…` |
|
||
| detach Hub | `Ctrl+A d` |
|
||
| 重新进入 Hub | `herdr-hub` |
|
||
| 停止 Hub clients | `herdr-hub stop` |
|
||
|
||
## 远端 Session 弹窗
|
||
|
||
弹窗由 tmux `display-popup` 和 Hub 内置选择器组成,不依赖 fzf。它会立即显示已打开的
|
||
window,同时在后台并行查询所有已登记远端;每台机器的结果就绪后原地更新。
|
||
|
||
状态与选择行为:
|
||
|
||
| 状态 | 选择后的行为 |
|
||
| --- | --- |
|
||
| `▶` 当前 / `●` 已打开 | 跳转到已有 window |
|
||
| `×` 已断线 | 自动重连并跳转 |
|
||
| `○` 远端运行 | 新建 attach window 并跳转 |
|
||
| `■` 远端已停止 | Enter/单击启动;命名 Session 可按 `d` 或 `D` 删除 |
|
||
| `↻` Windows 旧窗口 | 原地切换为 SSH TTY 模式并重连 |
|
||
|
||
弹窗靠前位置提供“关闭当前 Tab”;它只结束该 Tab 中的 Hub client,底层 Herdr
|
||
Session 和工作现场继续运行。当前 Tab 是远端 Session 时还会显示“关闭并停止当前
|
||
Session”;二次确认后会在后台停止远端 Herdr Session、中断其中正在运行的任务并关闭
|
||
对应 Tab,但保留为 `■ 已停止`,以后仍可从弹窗重新启动。若它是 Hub 中最后一个 Tab,
|
||
停止后会保留该 Tab 为断线状态,避免销毁承载弹窗的最后一个 window。
|
||
“管理 Session 显示名称”可给已打开的本地或远端 Tab
|
||
设置最多 48 字符的名称;输入留空可恢复默认。每个远端下方都有“新建 Session”入口。
|
||
弹窗底部还可添加普通远端、明确添加“Windows/SSH 远端”,或管理已登记远端;移除远端只删除
|
||
记忆记录,不关闭已有 window,也不停止远端 Herdr server。手机端单击条目即可执行,
|
||
键盘支持上下键、Enter、输入搜索、Backspace、`Ctrl+R` 刷新和 Esc 返回/关闭。
|
||
|
||
远端查询失败时,该远端行支持 Enter 重试,也支持按 `d` 或 `D` 二次确认后从 Hub 的
|
||
历史列表移除。这只删除本机保存的远端登记,不会连接或修改不可达主机上的数据;需要时
|
||
可再次通过“添加远端”登记。
|
||
|
||
选中已停止的命名远端 Session 后,按 `d` 或 `D` 会进入二次确认;无论是否仍有关联
|
||
Hub Tab 都可以删除。可点击“确认删除”、按 Enter,或再按一次 `d`/`D`。确认后在后台
|
||
通过 SSH 执行 `herdr session delete`,永久删除该 Session 的工作现场和历史数据;若仍有
|
||
关联 Tab,也会一并关闭,然后留在弹窗中继续管理。运行中的 Session 不允许删除;Herdr
|
||
自身不支持删除永久保留的 `default` Session。若关联 Tab 是 Hub 的最后一个 Tab,Session
|
||
仍会成功删除,但该 Tab 会保留为断线状态,以免整个 Hub 随弹窗一起退出。
|
||
|
||
Herdr 0.8.2 不支持 Windows 作为原生 `herdr --remote` 的目标主机。Hub 查询到 Windows
|
||
Session 后会标记为“Windows · SSH 模式”,并自动改为 `ssh -tt target herdr`;命名
|
||
Session 会在远端运行 `herdr --session NAME`。这种模式仍支持 Tab 切换、detach、断线
|
||
重连和远端工作现场持久化,但使用远端 Herdr 的 keybindings,也没有原生 thin client
|
||
提供的本地图片剪贴板桥接。空 Windows 主机或平台检测失败时,可在弹窗选择
|
||
“添加 Windows/SSH 远端”,也可手动执行 `herdr-hub open --remote TARGET --transport ssh`。
|
||
|
||
也可通过 CLI 管理远端:
|
||
|
||
```bash
|
||
herdr-hub remote list
|
||
herdr-hub remote add orangepi
|
||
herdr-hub remote add ssh://me@server:2222 --remote-keybindings server
|
||
herdr-hub remote add windows-host --transport ssh
|
||
herdr-hub remote remove orangepi
|
||
```
|
||
|
||
注册表默认位于 `~/.config/herdr-hub/remotes.json`(遵循 `$XDG_CONFIG_HOME`),权限为
|
||
`0600`。它只保存 SSH target、remote keybinding 模式和首选 transport,不保存凭据。
|
||
|
||
## Session 显示名称
|
||
|
||
Herdr 0.8.2 没有底层 Session rename 命令,因此 Hub 的“重命名”是安全的显示名称:
|
||
顶部 Tab 和 Sessions 弹窗显示新名称,attach、重连和工作现场仍绑定原来的 Session ID。
|
||
名称会跨终端断线、Hub detach 和 `herdr-hub stop` 保留,并在重新打开 endpoint 时自动
|
||
应用。配置保存在 `~/.config/herdr-hub/aliases.json`(遵循 `$XDG_CONFIG_HOME`),
|
||
权限为 `0600`;留空保存会删除该 endpoint 的显示名称并恢复 `local/work`、
|
||
`host/work` 等默认格式。
|
||
|
||
## 复制与剪贴板
|
||
|
||
Hub 启用 tmux 的 OSC 52 剪贴板透传。Herdr 触发的自动复制,以及 tmux 接管后的
|
||
鼠标拖选,都会尝试写入当前 Hub 访问端的本地剪贴板。远程 session 中复制的
|
||
文本也回到访问端,不是远程机器的剪贴板。
|
||
|
||
访问端的终端需要支持并允许 OSC 52。如果终端或 Web terminal 禁止了这项能力,
|
||
可按住 `Shift` 再拖选,绕过 tmux 并使用终端原生复制。
|
||
|
||
当访问端宽度小于 80 列时,Hub 会自动进入窄屏模式:隐藏右侧快捷键说明,
|
||
所有 Tab 都显示状态和最多 12 列的短名称,并优先显示主机:默认 Session 的本地与
|
||
远端分别显示 `local`、`host`,非默认 Session 才显示 `local/work`、`host/work`。
|
||
宽度小于 50 列时左上角标题会缩成 `S`,继续给 Tab 留空间。点击 `Sessions`/`S`
|
||
可打开占 90% 屏幕的完整选择器。
|
||
|
||
`refresh` 是 add-only:它添加新发现的 endpoint,不自动删除已有 window,也不会改变
|
||
当前焦点。通过 `Ctrl+A q` 或弹窗关闭的 endpoint 会在当前 Hub 生命周期内记为已隐藏,
|
||
刷新和断开终端后重新进入都不会让它自动回来。要恢复它,打开 Sessions 弹窗并重新选择;
|
||
也可以执行对应的 `herdr-hub open` 命令。远端通过 SSH 目录重新选择,本地隐藏的运行中
|
||
Session 会列在“本机”下。Hub 至少保留一个 Tab;要全部退出请使用 `herdr-hub stop`。
|
||
|
||
当 endpoint 连接进程退出时,window 会保留:顶栏将它标记为 `× [断开]`,
|
||
当前 pane 底部会显示退出状态和 `Ctrl+A R` 重连提示。这与正常 detach Hub
|
||
不同;仅关闭外层终端后,直接再次执行 `herdr-hub` 即可。
|
||
|
||
`herdr-hub stop` 只结束 tmux 中的 client processes。本地及远端 Herdr servers 和
|
||
它们的 panes 会继续运行。
|
||
|
||
## CLI
|
||
|
||
```text
|
||
herdr-hub
|
||
herdr-hub start
|
||
herdr-hub refresh
|
||
herdr-hub open [--session NAME]
|
||
herdr-hub open --remote TARGET [--session NAME]
|
||
[--remote-keybindings local|server] [--transport native|ssh]
|
||
herdr-hub choose
|
||
herdr-hub remote list
|
||
herdr-hub remote add TARGET [--remote-keybindings local|server]
|
||
[--transport native|ssh]
|
||
herdr-hub remote remove TARGET
|
||
herdr-hub reconnect [--window ID]
|
||
herdr-hub close [--window ID]
|
||
herdr-hub doctor
|
||
herdr-hub stop
|
||
```
|
||
|
||
## 自动发现与远端查询的边界
|
||
|
||
零配置的首次发现只覆盖:
|
||
|
||
- 当前用户正在运行的本地 Herdr servers;
|
||
- 当前用户、同一 PID namespace 中仍然存活的 `herdr --remote` processes。
|
||
|
||
发现或手动添加一次后,remote target 会被记住,不再要求本地 client 持续存活。弹窗先
|
||
通过非交互 SSH 执行跨平台的 `herdr session list --json`;Linux/macOS 的非交互 PATH
|
||
找不到 Herdr 时,再自动尝试 `$HOME/.local/bin/herdr`。Windows OpenSSH 返回的本地代码页
|
||
错误文本也会被容错解码。SSH key/passphrase 应已由 ssh-agent 处理;未知 host key、需要
|
||
密码、远端不可达或命令失败时,该主机显示“查询失败 · 单击重试”,不会关闭或卡住整个
|
||
弹窗。首次安装或 host-key 确认仍可在实际新建的 Herdr window 中交互完成。
|
||
|
||
首次使用且尚未登记时,以下情况仍无法自动找回 remote target:
|
||
|
||
- remote client 已经退出,但远端 server 仍在后台运行;
|
||
- client 位于另一个 container/PID namespace;
|
||
- `/proc` 使用了阻止读取同用户 argv 的安全配置;
|
||
- 可执行文件不叫 `herdr`。
|
||
|
||
这些场景可在弹窗中选择“添加远端”,或使用 `herdr-hub open --remote …`。为了避免嵌套 tmux 与前缀冲突,
|
||
`herdr-hub` 应从普通终端启动;若当前已经在另一个 tmux server 中,它会拒绝再次
|
||
attach。
|
||
|
||
## 开发与测试
|
||
|
||
```bash
|
||
make test
|
||
```
|
||
|
||
测试使用伪造的 `/proc` 和 Herdr executable;tmux 集成测试使用唯一 socket 和
|
||
`/dev/null` 配置,不接触真实 Herdr sessions 或现有 tmux server。
|