AI 停了才让 Mac 睡,这个开源工具把保活做精了
- Adrafinil 是一个 macOS 菜单栏工具,让 Mac 只在 AI 编程 Agent(Claude Code、Cursor 等)活跃执行任务时阻止休眠,Agent 停止后立即恢复正常睡眠行为,包括合盖休眠。
- 支持 9 款主流 Agent,通过各自 Hook 系统自动调用 acquire / release,CLI 往返时延低于 50ms。
- 多 Agent 并发时采用引用计数:每个会话各自 +1 / −1,计数归零才解除保活,最后一个任务结束才让 Mac 睡。
- 内置热保护:皮肤 / CPU 温度超阈值时强制释放所有保活断言,防止合盖散热不足烤机。
- 架构分三层权限,root 权限只集中在最小的 Helper 组件,仅暴露一个布尔接口 setSleepBlocked(Bool),其余逻辑全在用户态运行。
你合盖去睡,Agent 还在跑
开源开发者 kageroumado 近日发布了 Adrafinil,一个 macOS 菜单栏工具,让 Mac 只在 AI 编程 Agent 活跃工作时阻止休眠,Agent 停工后自动恢复正常睡眠。
凌晨 3 点,你睡了,Agent 没睡。它还在几个小时前你起的那个会话里运转,而你像合上眼皮一样合上了笔记本盖子,可这只眼睛没真正闭上。矛盾就在这里:Agent 的任务周期和 Mac 的睡眠策略互相不知道对方在干什么。盖子一合,系统按惯例休眠,任务就断在半途。
名字本身就是它的设计说明。Adrafinil 借自一种促醒药(eugeroic)。和兴奋剂(stimulant)不同,促醒剂只在需要时提神,不需要时不干预,这正是它和 caffeinate 那类「永远清醒」工具的根本区别:只在有活干时保活。
旧办法为什么不够用
在 Adrafinil 之前,想让 Mac 跑长任务时别睡,只有两个极端,都对不准「任务真正在跑」这段时间。
caffeinate 和 Amphetamine 是兴奋剂式的,开了就全程清醒,不管有没有活在干。任务早结束了,机器还在空转耗电发热。反过来,什么都不装,合盖即睡,长任务直接中断。下面把三种情况摆在同一条时间轴上看:保活窗口和任务窗口对不对得齐,一眼就清楚。
caffeinate 的保活条从头亮到尾,任务结束后那一大段是纯浪费;什么都不装的那行,合盖处一个红叉,任务还没跑完就被掐断。只有 adrafinil 的亮条精确贴住任务窗口,前后不多撑一秒。
跟着 Agent 的呼吸走
Adrafinil 的核心很简单:Agent 开始干活就申请保活,停手就释放。Mac 只在 Agent 真正执行任务的那段时间保持清醒。菜单栏图标也只有这两个状态。


Agent 不直接和 Adrafinil 对话,而是通过各自的 Hook 系统调用它自带的命令行:
以 Claude Code 为例,整条信号链是这样流动的:
关键在于它保的是「正在干活」这段,不是「程序开着」这段。Claude Code 在 UserPromptSubmit 时 acquire,在 Stop 时 release,所以一个开着但停在输入框等你打字的会话,Mac 照样正常睡。这叫活动范围保活(activity-scoped),区别于会话级保活(session-scoped)。
对开发者的直接结果:你可以放心合盖离开,让 Agent 跑构建、测试、部署,任务一结束 Mac 自动恢复睡眠,不用记得手动关 caffeinate,也不用担心忘了关一直空耗。
同时跑好几个也不乱
多个 Agent 一起跑怎么办?Adrafinil 用引用计数:每个会话开始 +1,结束 −1,计数归零才解除保活。
办公室最后一个人离开才关灯,不是随便谁走都关。中间任何一个任务结束,都不会误伤其他还在跑的任务。
下面这个计数环就是整页的招牌。点亮任意几个 Agent 试试:只要环里数字大于 0,Mac 就保活(蓝色脉冲);把它们逐个关掉,数字归零的那一刻,环变灰,Mac 才真正可以睡。
点击启动 / 停止任意 Agent。环里的数字 = 当前持有保活的会话数;只有归零才解除保活。
合盖也不会烤机
合盖跑长任务,最怕的是塞进包里散热不够,把机器闷坏。Adrafinil 内置热保护,这也是让人敢合盖离开的关键兜底。
除了温度,还有第二道自动释放:持有断言的进程已经死了、或者 CPU 空闲超过 N 分钟,对应的保活会被自动丢弃,兜住「程序崩了但保活没撤」的漏洞。
合盖时屏幕已灭弹不了通知,所以它用一声提示音确认保活已生效;重新开盖时给你一份摘要:离开期间跑了什么、峰值温度多少、有没有触发过热保护。
进程嗅探(可选)
即使你没装 Hook,Daemon 也能在发现已知 Agent 程序正在运行时自动 acquire。这是个可选项,给不方便配 Hook 的场景兜底。
root 只做一件事
阻止合盖休眠绕不开 root 权限,这是 macOS 的硬规定。Adrafinil 的做法是把 root 的接触面压到极小。
先解释为什么非要 root。Mac 有两种防休眠:阻止「闲置休眠」用普通 API(IOPMAssertion)就够;但阻止「合盖休眠」(clamshell sleep)必须用 root 级的 pmset disablesleep。Adrafinil 两种都做,合盖跑任务才撑得住。
普通员工能把办公室灯的定时器延长(闲置休眠),但要解锁整栋楼的门禁让人不被困住(合盖休眠),得动用管理员权限。
它的解法是把这一点 root 权限关进一个最小的盒子。整套架构三层,纵向看下来:
策略全在用户态。引用计数、热监控、进程监控、CLI socket 全跑在没有特权的 Daemon 里;唯一以 root 运行的 Helper 只暴露一个会修改状态的接口 setSleepBlocked(Bool),只负责执行最后一步开关。要审计这部分代码,量小得多。
LaunchAgent vs LaunchDaemon,以及 pmset 的副作用
LaunchAgent 在用户登录后以当前用户身份启动;LaunchDaemon 开机即以 root 身份启动。Adrafinil 故意把 Daemon 做成 LaunchAgent(用户权限),只把最小的 Helper 做成 LaunchDaemon(root),以此缩小特权面。
另外两点工程注意:公共的 IOPM 断言(也就是 caffeinate 用的那套)压根挡不住合盖休眠,所以 v1 用了更粗暴的 pmset disablesleep 1,它会连闲置休眠一起禁掉,且必须在关机时清掉否则会泄漏,Helper 因此在重启时先把状态重置为 disablesleep 0 再重新应用。
支持哪 9 款,怎么装上
一条命令把 Hook 写进所有 Agent 的配置里。先看支持名单:
装上它
下载签名并公证过的 dmg,打开拖进 Applications,启动。首次启动会申请一次 admin 权限注册那个特权 Helper。之后一条 install-hooks 命令,把 Hook 配置写进上面所有 Agent;不想用了,uninstall-hooks 会清掉它加过的每一条 Hook 条目。
系统要求
macOS Tahoe 26.4 以上(更早的 26.x 可能能跑,但作者没测)。想自己编译需要 Xcode 26 以上,开启 Swift 6 严格并发。非管理员安装会把 CLI 放进 ~/.local/bin 而不是 /usr/local/bin。
还有两个命令值得知道
对会比回复活得更久的后台任务(一次长构建或部署),可以用带超时的 hold 命令撑一段,到点自动释放;支持 MCP 的 Agent 还能直接通过 adrafinil mcp 提供的工具调用:
Adrafinil only intervenes when an agent (Claude Code, Codex, Cursor, …) is mid-task, and gets out of the way the moment that work finishes. kageroumado · adrafinil README(GitHub)