产品发布 · 小互解读

AI 停了才让 Mac 睡,这个开源工具把保活做精了

支持 Claude Code 等 9 款 Agent 一键集成,合盖继续跑任务,停工 50ms 内自动释放休眠控制
速览
  • 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),其余逻辑全在用户态运行。
1场景

你合盖去睡,Agent 还在跑

开源开发者 kageroumado 近日发布了 Adrafinil,一个 macOS 菜单栏工具,让 Mac 只在 AI 编程 Agent 活跃工作时阻止休眠,Agent 停工后自动恢复正常睡眠。

凌晨 3 点,你睡了,Agent 没睡。它还在几个小时前你起的那个会话里运转,而你像合上眼皮一样合上了笔记本盖子,可这只眼睛没真正闭上。矛盾就在这里:Agent 的任务周期和 Mac 的睡眠策略互相不知道对方在干什么。盖子一合,系统按惯例休眠,任务就断在半途。

Adrafinil 把防休眠精确绑定到 AI Agent 的工作状态:Agent 真正在跑任务时撑住不睡(包括合盖),任务一停,立刻把休眠控制权还回去。
首个把 macOS 防休眠按 Agent 会话活动状态精确开关的工具。CLI 往返 低于 50ms、一键 Hook 接入 9 款主流 Agent、引用计数处理多会话并发、root 权限面压到 单个布尔接口
<50ms
CLI 的 acquire / release 命令到 Daemon 的往返时延,不拖慢 Agent 工作流
9
支持一键 Hook 安装的 AI Agent 数量
3
权限层数(App/CLI → Daemon → Helper),最小化 root 接触面
1
Helper 暴露的 root 接口数量:setSleepBlocked(Bool)

名字本身就是它的设计说明。Adrafinil 借自一种促醒药(eugeroic)。和兴奋剂(stimulant)不同,促醒剂只在需要时提神,不需要时不干预,这正是它和 caffeinate 那类「永远清醒」工具的根本区别:只在有活干时保活。

2对比

旧办法为什么不够用

在 Adrafinil 之前,想让 Mac 跑长任务时别睡,只有两个极端,都对不准「任务真正在跑」这段时间。

caffeinate 和 Amphetamine 是兴奋剂式的,开了就全程清醒,不管有没有活在干。任务早结束了,机器还在空转耗电发热。反过来,什么都不装,合盖即睡,长任务直接中断。下面把三种情况摆在同一条时间轴上看:保活窗口和任务窗口对不对得齐,一眼就清楚。

夜里起一个长任务 ─────────────▶ 任务跑完
任务实际在跑(基准)
caffeinate / Amphetamine
什么都不装
adrafinil
任务窗口保活(阻止休眠)合盖即睡,任务中断

caffeinate 的保活条从头亮到尾,任务结束后那一大段是纯浪费;什么都不装的那行,合盖处一个红叉,任务还没跑完就被掐断。只有 adrafinil 的亮条精确贴住任务窗口,前后不多撑一秒。

3核心机制

跟着 Agent 的呼吸走

Adrafinil 的核心很简单:Agent 开始干活就申请保活,停手就释放。Mac 只在 Agent 真正执行任务的那段时间保持清醒。菜单栏图标也只有这两个状态。

awake 状态:有 Agent 正在工作
「awake」状态:有 Agent 正在干活,菜单栏图标显示保活中。来源:项目 README
sleeping 状态:无 Agent,正常休眠
「sleeping」状态:没有 Agent 在跑,Mac 正常休眠。来源:项目 README

Agent 不直接和 Adrafinil 对话,而是通过各自的 Hook 系统调用它自带的命令行:

adrafinil acquire <session-key> --tool claude-code --reason "long build" # 一轮开始时 adrafinil release <session-key> # Agent 转入空闲时

以 Claude Code 为例,整条信号链是这样流动的:

UserPromptSubmit你发出指令
acquire计数 +1
Agent 工作中阻止休眠
Stop任务停
release计数 −1
计数归零恢复
Mac 可休眠合盖即睡
Hero · 活动范围保活

关键在于它保的是「正在干活」这段,不是「程序开着」这段。Claude Code 在 UserPromptSubmit 时 acquire,在 Stop 时 release,所以一个开着但停在输入框等你打字的会话,Mac 照样正常睡。这叫活动范围保活(activity-scoped),区别于会话级保活(session-scoped)。

对开发者的直接结果:你可以放心合盖离开,让 Agent 跑构建、测试、部署,任务一结束 Mac 自动恢复睡眠,不用记得手动关 caffeinate,也不用担心忘了关一直空耗。

4多会话

同时跑好几个也不乱

多个 Agent 一起跑怎么办?Adrafinil 用引用计数:每个会话开始 +1,结束 −1,计数归零才解除保活。

打个比方

办公室最后一个人离开才关灯,不是随便谁走都关。中间任何一个任务结束,都不会误伤其他还在跑的任务。

下面这个计数环就是整页的招牌。点亮任意几个 Agent 试试:只要环里数字大于 0,Mac 就保活(蓝色脉冲);把它们逐个关掉,数字归零的那一刻,环变灰,Mac 才真正可以睡。

活跃会话

点击启动 / 停止任意 Agent。环里的数字 = 当前持有保活的会话数;只有归零才解除保活。

5安全兜底

合盖也不会烤机

合盖跑长任务,最怕的是塞进包里散热不够,把机器闷坏。Adrafinil 内置热保护,这也是让人敢合盖离开的关键兜底。

SMC 测温持续监控
皮肤 / CPU 温度超过阈值
强制 release释放全部断言
Mac 恢复正常休眠散热

除了温度,还有第二道自动释放:持有断言的进程已经死了、或者 CPU 空闲超过 N 分钟,对应的保活会被自动丢弃,兜住「程序崩了但保活没撤」的漏洞。

合盖时屏幕已灭弹不了通知,所以它用一声提示音确认保活已生效;重新开盖时给你一份摘要:离开期间跑了什么、峰值温度多少、有没有触发过热保护。

进程嗅探(可选)

即使你没装 Hook,Daemon 也能在发现已知 Agent 程序正在运行时自动 acquire。这是个可选项,给不方便配 Hook 的场景兜底。

6安全架构

root 只做一件事

阻止合盖休眠绕不开 root 权限,这是 macOS 的硬规定。Adrafinil 的做法是把 root 的接触面压到极小。

先解释为什么非要 root。Mac 有两种防休眠:阻止「闲置休眠」用普通 API(IOPMAssertion)就够;但阻止「合盖休眠」(clamshell sleep)必须用 root 级的 pmset disablesleep。Adrafinil 两种都做,合盖跑任务才撑得住。

打个比方

普通员工能把办公室灯的定时器延长(闲置休眠),但要解锁整栋楼的门禁让人不被困住(合盖休眠),得动用管理员权限。

它的解法是把这一点 root 权限关进一个最小的盒子。整套架构三层,纵向看下来:

Adrafinil.app 用户态 · 菜单栏
状态图标、设置、安装向导 GUI、开盖摘要。纯展示层,退出重启都不影响已持有的保活。
XPC
AdrafinilDaemon 用户态 · LaunchAgent
引用计数登记、进程监控(kqueue NOTE_EXIT + 周期巡查)、热监控(SMC)、合盖音效、CLI socket。所有策略都在这里,它才是唯一真相源。
XPC · 特权 Mach service
AdrafinilHelper root · LaunchDaemon
唯一碰防休眠 API 的组件。只暴露 setSleepBlocked(Bool) 加只读状态查询,并校验调用方的代码签名。不含任何策略逻辑。
adrafinil CLI 随 .app 打包,软链到 PATH,直连 Daemon socket,往返 <50ms
Hero · 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 再重新应用。

7支持范围与安装

支持哪 9 款,怎么装上

一条命令把 Hook 写进所有 Agent 的配置里。先看支持名单:

Claude CodeCodexCursorGemini CLIAiderHermesOpenCodeClinePi

装上它

下载签名并公证过的 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 hold --for 30m --reason "deploy" # 最多保活 30 分钟后自动释放 adrafinil mcp # 在 stdio 上讲 MCP 协议,给 Agent 用
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)
来源:本文基于 kageroumado 在 GitHub 发布的 adrafinil 项目说明(README 与 Docs/ARCHITECTURE.md)整理。项目以 MIT 协议开源。文中性能与机制描述均为项目文档所述。