【免费下载链接】Insomnia
A macOS menu bar app that safely keeps your MacBook awake with the lid closed, for timed agent sessions.
Insomnia 是一款 macOS 菜单栏应用,专为 AI 编程代理(agent)的长时任务设计,能让 MacBook 在合盖状态下安全地保持唤醒而不休眠。但"让电脑不休眠"这件事本身就动了系统的电源策略,一旦应用中途崩溃,机器可能一直卡在"禁止睡眠"的状态。Insomnia 为此设计了 recovery journal(恢复日志)与 launchd backstop(兜底代理)的双保险恢复机制,本文带你完整看懂它的工作原理。
为什么防休眠应用最需要"恢复机制"
Insomnia 工作时会对机器做一系列系统级改动:
- 通过
sudo pmset关闭睡眠(disablesleep 1) - 必要时开启低电量模式(Low Power Mode)
- 合盖时调暗屏幕与键盘背光、静音音频
- 暂停(freeze)冻结列表中的应用进程,降低功耗
这些改动在正常情况下会随会话结束自动撤销。但问题在于:应用崩溃、被强杀、甚至整台机器重启时,谁负责撤销?如果没人管,MacBook 会一直禁止睡眠、一直耗电。
Insomnia 的答案是:不信任内存,只信任磁盘。
第一重保险:recovery journal 恢复日志
先写日志,再动手改机器
Insomnia 的核心不变式只有一条:每一次改动,都先写进磁盘上的恢复日志,然后才真正执行。
会话信息存在session.json,改动记录存在state.json,位置在~/Library/Application Support/Insomnia/(完整路径定义见 Paths.swift)。state.json就是所谓的 recovery journal,它的结构定义在 RuntimeState.swift:
| 日志条目 | 含义 | 恢复方式 |
|---|---|---|
sleepDisabledByUs | 我关掉了系统睡眠 | 恢复时执行sudo pmset -a disablesleep 0 |
lowPowerSetByUs | 我开启了低电量模式 | 恢复时执行sudo pmset -b lowpowermode 0 |
frozenProcesses | 我冻结了哪些进程(含启动时间、boot session 身份指纹) | 校验身份后发送 SIGCONT 恢复 |
savedDisplayBrightness/savedKeyboardBrightness | 合盖前保存的亮度 | 开盖或恢复时还原 |
savedAudioOutputs/savedMuted | 合盖前保存的音量与静音状态 | 重新打开应用后还原 |
appNapOverrides | 我为哪些 agent 应用改过 App Nap 设置 | 用defaults写回原值 |
只要日志里还有未撤销的条目(代码里叫isDirty),恢复流程就会继续尝试;每条条目必须"撤销成功"之后才从日志中清除。这一步的顺序保证了:写日志失败时,对应的改动根本不会执行。
日志坏了怎么办:宁可保留,绝不删除
恢复机制最怕两件事:误删证据、误判状态。Insomnia 的选择是——
- 撤销失败的条目保留在日志中,下一轮继续重试;
- 无法解析的日志文件原样保留,当作"需要人工处理",而不是当作"一切干净";
- 无法解析的
session.json会被重命名为session.json.unreadable-<时间戳>挪到一边(永不删除、永不覆盖),文件字节完整保留作为证据。
第二重保险:launchd backstop 每分钟巡场
独立于应用的 launchd 代理
就算应用进程已经消失了,磁盘上的session.json里仍然写着会话的截止时间。Insomnia 在安装时(install.sh)会注册一个独立的 launchd 用户代理com.insomnia.backstop(plist 位于~/Library/LaunchAgents/),它的行为很简单粗暴:
- 加载后立即运行一次,之后每 60 秒再运行一次;
- 每次运行只做一件事:读
session.json和state.json,判断会话是否已到期、日志是否"脏"; - 如果会话还有效且未到期,什么都不做,直接退出;
- 如果会话已到期(或应用已不在)而日志还有未撤销的条目,就按日志逐条撤销,并在全部干净后原子地发布新的日志文件。
也就是说:应用的正常运行路径和崩溃路径,走的是同一份日志、同一套撤销逻辑,backstop 只是"应用没空管时"的替补。调度逻辑的 Swift 侧实现在 LaunchdBackstop.swift。
签名校验:被篡改的脚本永远跑不起来
backstop 脚本backstop.sh(源码在 scripts/backstop.sh)在安装时被封入应用包内(Insomnia.app/Contents/Resources/backstop.sh),并在签名之前完成拷贝。launchd 每次运行它之前,会先用codesign --verify --strict校验整个应用包的签名是否与 plist 中固定的代码要求(code requirement)一致——校验不过,脚本一行都不会执行,只会在~/Library/Logs/Insomnia/insomnia.log里记一条错误日志。
这意味着任何人(或任何恶意软件)在磁盘上偷偷修改 backstop.sh,都只会让 backstop 静默罢工并留下日志,而不可能借此获得每分钟一次的执行机会。
双保险如何配合:同一把锁下的事务
应用和 backstop 可能同时想动机器,怎么办?它们共用同一把文件锁.recovery.lock(内核级flock,见 RecoveryLock.swift):
- 每次运行都是一笔完整事务:拿锁 → 读
session.json与state.json→ 决策 → 撤销 → 原子发布新日志 → 释放锁; - 锁文件永不删除,双方永远锁的是同一个 inode,避免"各锁各的";
- 锁由内核持有在打开的文件描述符上,进程崩溃时内核自动释放,永远不会死锁;
- backstop 等锁 10 秒拿不到就退出码 75,一分钟后再来;
- 一个仍在运行的特权命令(如卡住的
sudo pmset)会自己持有锁,双方都得等它结束。
还有一处精妙设计:卡住的sudo pmset只会被发 SIGTERM,永远不发 SIGKILL——因为杀掉 sudo 可能留下一个以 root 运行、脱离事务管控的 pmset,反而会在日志"翻篇"后继续改电源设置。此时宁可让恢复流程整体暂停,等命令自然退出。
崩溃后的真实恢复流程
以"应用被强杀、会话已到期"为例,完整链路是这样的:
- 60 秒内,launchd 启动 backstop,先
codesign校验应用包签名; - 拿到
.recovery.lock,读session.json发现endsAt已过,读state.json发现有未撤销条目; - 按日志逐条恢复:
sleepDisabledByUs→sudo -n pmset -a disablesleep 0,lowPowerSetByUs→ 关掉低电量模式; - 对
frozenProcesses条目,backstop自己不直接发信号,而是调用已安装应用二进制的一次性模式Insomnia --resume-frozen(实现见 ResumeFrozenCommand.swift):逐条比对进程的启动时间(精确到微秒)和 boot session,确认"这个 pid 确实是我冻结的那个进程"后才发 SIGCONT; - 全部撤销成功后原子发布干净的日志,退出码 0。任何一步失败则退出码 1,保留日志,下一分钟自动重试。
恢复机制的边界:哪些情况需要人工
Insomnia 的态度很诚实:恢复不是"一切总能被自动撤销"。以下情况需要你留意(详见 docs/recovery.md):
- 🔊音频恢复需要应用本体:backstop 能保留音量/静音条目,但还原 CoreAudio 状态必须重新打开 Insomnia;
- 🧊无法证实身份的冻结进程不会被自动恢复:宁可让它保持暂停,也不盲发信号给一个来路不明的 pid,需要你自己核实;
- 🔋backstop 不监测电池与温度:定时结束是目标,不是安全保证;
- ⚠️菜单栏出现恢复警告时,先处理再离开电脑。
这些真实 Mac 上的崩溃、重启、升级检查记录在 release-validation.md,设计目标与不变式则写在 spec.md 第 8 节。
相关文件索引
- 恢复机制完整文档:docs/recovery.md
- backstop 脚本源码:scripts/backstop.sh
- launchd 代理调度:Sources/Insomnia/Core/LaunchdBackstop.swift
- 共享恢复锁:Sources/Insomnia/Core/RecoveryLock.swift
- 日志结构定义:Sources/Insomnia/Model/RuntimeState.swift
- 冻结进程恢复模式:Sources/Insomnia/Core/ResumeFrozenCommand.swift
- 合盖行为细节:docs/lid-close.md
总结
Insomnia 的恢复设计可以浓缩成三句话:改动前先写日志、应用和 backstop 共用一把锁、拿不准就保留证据等重试。recovery journal 让"改了什么"永远可查,launchd backstop 让"谁来改回去"永远有主,两者配合让一个会动系统电源策略的菜单栏小工具,在崩溃面前也守住了底线。
【免费下载链接】Insomnia
A macOS menu bar app that safely keeps your MacBook awake with the lid closed, for timed agent sessions.
相关推荐
Captura崩溃恢复机制:自动保存与会话恢复设计
Captura崩溃恢复机制:自动保存与会话恢复设计 引言:录屏工作者的噩梦与解决方案 你是否经历过这样的场景:花费数小时录制教程或直播内容,突然软件崩溃,所有未
桌面应用屏幕录制音视频Nativefier崩溃恢复机制:自动保存与会话恢复
Nativefier崩溃恢复机制:自动保存与会话恢复 你是否曾遇到过这样的情况:正在使用Nativefier将网页转换为桌面应用时,突然程序崩溃,所有设置和登录
CLI桌面应用开发工具wewe-rss 完整教程:10 分钟部署私有的微信公众号 RSS 订阅服务
wewe rss 完整教程:10 分钟部署私有的微信公众号 RSS 订阅服务 wewe rss 是一个支持私有化部署的微信公众号 RSS 生成服务,它借助微信读
后端前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考