把自己的工作流放进菜单栏:OmniPilot 项目介绍

我一直觉得,菜单栏应用最适合做两类事情:把经常用的小功能放在手边,以及替自己照看那些需要长期运行的任务。于是我做了 OmniPilot——一个常驻 macOS 菜单栏的个人助手。

它不是想取代某个成熟工具,也不打算做成什么都管的“大杂烩”。OmniPilot 只收纳那些确实贴合我自己日常习惯的功能。目前,它主要做两件事:管理 SSH 隧道,以及保存文本剪贴板历史。

从 SSH 隧道开始

我在 ~/.ssh/config 里已经配置好了服务器和端口转发,但每次开机、待机或网络切换之后,都需要手动启动隧道,还得留意连接有没有断。

OmniPilot 直接读取 SSH 配置,找出声明了 LocalForward 或 RemoteForward 或 DynamicForward 的 Host,并为每个 Host 启动一个 ssh -N <alias> 进程。隧道意外退出时,应用会按 1、2、5、10、30 秒的间隔自动重连;连续失败则通过系统通知提醒我。整个过程不修改 SSH 配置,也不接管密钥或认证,仍然使用系统已有的 ssh-agent。

这种设计的好处是,连接信息仍由 OpenSSH 管理,OmniPilot 只负责“启动、监督和展示状态”。菜单栏里可以查看每条隧道及其转发信息,也可以单独或批量启停。

再加一个常见的小功能

剪贴板历史是我后来加入的第二个功能。它每 0.8 秒检查一次系统剪贴板的变化,只有内容变化时才读取,并且当前只保存文本。

菜单栏面板里可以查看历史、删除单条记录,或清空全部内容。历史条数可以选择 10、20、30、50 或 100 条,默认 30 条。点击一条历史记录会把文本写回系统剪贴板,之后我可以在目标应用里按 ⌘V 粘贴;它不会替我模拟键盘输入。

剪贴板内容保存在 ~/Library/Application Support/OmniPilot/clipboard-history.json,条数设置则通过 UserDefaults 保存。历史文件是普通 JSON,没有加密,所以它可能包含敏感文本;应用会过滤常见的隐藏、临时和密码管理器标记,并提供单条删除和清空操作,但这不等于安全存储。

菜单栏之外的代码结构

这个项目是我学习 Xcode 和 Swift 开发时逐步搭起来的。代码按“应用外壳”和“可测试的核心逻辑”分成两层:

  • Sources/OmniPilot 是 macOS App 本身,放菜单栏界面、剪贴板监控、通知和登录项等系统集成。
  • Packages/OmniPilotKit 是本地 Swift Package,放 SSH 配置解析、隧道进程管理,以及剪贴板历史模型和持久化逻辑。
  • project.yml 描述 Xcode 工程,由 XcodeGen 生成 OmniPilot.xcodeproj。

把逻辑放进 Swift Package 的一个直接收益是可以独立测试。例如剪贴板历史的去重、条数限制、持久化和清空行为,都可以通过 swift test 验证,不需要启动菜单栏界面。

构建和试用

项目面向 Apple Silicon,最低支持 macOS 13。安装 Xcode 和 XcodeGen 后,可以这样构建:

brew install xcodegen
Scripts/build-app.sh
open build/OmniPilot.app

运行核心逻辑测试:

cd Packages/OmniPilotKit
swift test

应用日志写入 ~/Library/Logs/OmniPilot/omnipilot.log。打包脚本支持生成 zip 或 dmg;更完整的签名、公证和 Gatekeeper 说明可以查看 Docs/distribution.md。

一个只为自己服务的小工具

OmniPilot 的方向不是“菜单栏里还能塞什么”,而是“我自己的工作流里,有什么值得放到这里”。它不试图替代其他成熟,常见的菜单栏应用,也不追求功能数量;它只把 SSH 隧道和剪贴板历史这类我确实需要的东西,放到一个随手可用的位置。

后续要不要增加新功能,我也会沿用同一个标准:只有它能解决我自己的真实麻烦,且具备个性化的需求,才值得进入 OmniPilot。

项目地址

发表评论