阿德拉菲尼尔:仅在AI agent工作时阻止Mac睡眠的菜单栏工具
Show HN: 阿德拉菲尼尔——仅在药物起效期间保持“盖子紧闭的Mac”处于清醒状态
Adrafinil 是一款 macOS 菜单栏应用,仅在 Claude Code、Codex、Cursor、Gemini CLI、Aider、Hermes、OpenCode、Cline、Pi 等 9 种 AI coding agent 持有活跃会话时阻止系统睡眠(包括合盖睡眠)。无 agent 工作时,合盖后 Mac 正常睡眠。它通过各 agent 的钩子系统调用 CLI,往返延迟低于 50ms,支持引用计数断言、热切出(温度阈值强制释放)、空闲释放及进程嗅探。需要 macOS Tahoe 26.4,Xcode 26+ 构建,以签名公证的磁盘映像提供。
阿德拉菲尼尔对macOS唤醒工具做了一次有趣的重新思考,不是一直醒着,而是只在AI代理工作时醒着,合盖也能跑长任务,对用Claude Code或Cursor的开发者是实用的开源伴侣。
![]()
adrafinil
处方编号 006 ・ a·draf·i·nil /əˈdræfɪnɪl/ ・ 为机器而生的促醒剂 ♡
![]() awake ・ 有智能体正在工作 | ![]() idle ・ 没有智能体,正常睡眠 | ![]() hold ・ 由你自己让它保持唤醒 — 15 分钟至 ∞ |
服用注意 ・ 为那些在你入睡后仍替你值守的机器而设。
现在是凌晨 3 点。你睡着了。智能体没有——它仍在几小时前你启动的那个会话中思考到一半,而你合上盖子,就像一只怎么也闭不严的眼睑。
caffeinate和 Amphetamine 是兴奋剂:它们让机器 永远保持亢奋,不管有没有人在家。Adrafinil 则是促醒剂。在智能体获取它之前,它 什么都不做,只在那项工作存续期间让你的 Mac 在合盖状态下保持唤醒,并在最后一个会话释放的瞬间清除。它只为工作而醒来——然后你们双双入睡。♡
让你的 Mac 保持唤醒 仅在 AI 智能体工作时。
Adrafinil 是一款 macOS 菜单栏应用,可防止系统休眠——包括合盖(盖子关闭)休眠——仅在 AI 编程智能体有活跃会话时生效。当没有智能体在工作时,休眠行为不受影响:合上盖子,Mac 正常休眠。
它与 caffeinate 或 Amphetamine 这类常开唤醒工具正好相反。Adrafinil 只在智能体(Claude Code、Codex、Cursor……)执行任务期间介入,一旦工作完成便立即让路。
它保持唤醒的三种方式:
- 自动——智能体钩子会告知 Adrafinil 一个回合何时开始与结束,因此它只在智能体实际工作时才阻止休眠。(可选的进程嗅探也能发现正在运行但未安装钩子的智能体。)
- 智能体驱动——智能体可以刻意让 Mac 保持唤醒超出其回复时间以应对长时间的构建或部署,通过捆绑的 MCP 工具或
adrafinil holdCLI。 - 手动 — 菜单栏中的 保持唤醒 按钮会设置一个限时的自我保持,即使 没有智能体在运行 也有效;让它休眠 会清除一切。
⚠️ 特权睡眠控制。覆盖翻盖睡眠需要 root 权限。Adrafinil 将其隔离在一个小巧、经过审计的辅助程序中,该程序仅暴露
setSleepBlocked(Bool)—— 所有策略逻辑都运行在一个非特权守护进程中。它持有一个标准的IOPMAssertion用于空闲睡眠,并使用pmset disablesleep用于翻盖(合盖)睡眠,此前已在设备上验证更干净的私有IOPMrootDomain路径不会让无显示器的合盖 Mac 保持唤醒。参见 Docs/ARCHITECTURE.md §2。
功能
- 感知智能体,而非始终开启。仅当 ≥1 个智能体会话持有断言时才阻止睡眠。零会话 → 正常睡眠,包括合盖。
- 为 9 个智能体提供 Hook 集成。一键安装程序将 Adrafinil 接入 Claude Code、Codex、Cursor、Gemini CLI、Aider、Hermes、OpenCode、Cline 和 Pi 的 hook 系统。
- 低于 50ms 的 CLI。
adrafinil acquire/release从智能体 hook 中调用,与守护进程的往返时间低于 50ms,因此绝不会阻塞智能体的工作流。 - 引用计数的断言。重叠的会话可以干净地叠加;只有当最后一个会话释放时,睡眠才会解除阻止。
- 温度熔断。如果合盖时外壳/CPU 温度越过阈值,所有断言将被强制释放,这样装在包里的 Mac 就不会把自己烤熟。
- 空闲释放。 如果持有断言的进程已死亡,或 CPU 空闲达到 N 分钟,断言会被自动丢弃。
- 进程嗅探(可选)。 当守护进程看到已知的智能体二进制文件在运行时,即使没有安装钩子,也能自动获取断言。
- 手动保持唤醒(无需智能体)。 菜单栏上的 保持唤醒 按钮可按需启动一段限时保持——用于长时间下载、本地任务,或任何不涉及智能体的场景——随后 让它休眠 会将其释放。
- 合盖音频 + 开盖摘要。 合盖时会有提示音确认断言已被持有(此时屏幕已关闭,因此不会弹出通知);重新打开后会显示你离开期间运行了什么、峰值温度,以及热保护切断是否触发过。
- 干净卸载。 会移除它在所有智能体配置中添加的每一条钩子条目。
环境要求
- macOS Tahoe 26.4。 这是我构建和测试所用的版本;它很可能也能在更早的 26.x 上运行,但我没有在那里测试过。
- Xcode 26+ 用于构建,并启用了 Swift 6 严格并发。
- 标准安装需要管理员权限(特权辅助程序通过
SMAppService安装)。非管理员安装路径会将 CLI 放到~/.local/bin,而不是/usr/local/bin。
下载
下载 Adrafinil —— 一个已签名、已公证的磁盘映像。打开它,将 Adrafinil 拖入 Applications,然后启动。首次启动会请求一次管理员权限以注册特权辅助程序。需要 macOS 26.4 或更高版本。
更想自己构建?参见 构建。
构建
git clone https://github.com/kageroumado/adrafinil.git
cd adrafinil
open Adrafinil.xcodeproj在 Xcode 中,选择 Adrafinil scheme 并运行。你需要设置一个开发团队用于代码签名 —— 守护进程(LaunchAgent)和辅助程序(LaunchDaemon)会被嵌入到 app bundle 中,并在 app 启动时向系统注册。(源码中没有内置任何 Team ID;XPC 调用方检查会在运行时读取你自己的签名团队,因此在任何 Developer ID 下重新构建都会授权其自身组件,无需修改代码。)
如需在没有本地签名身份的情况下进行无头编译检查:
xcodebuild -project Adrafinil.xcodeproj -scheme Adrafinil -configuration Debug \ -destination 'generic/platform=macOS' \ CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY='' build
共享逻辑可作为 Swift 包独立构建和测试:
cd AdrafinilShared swift test
工作原理
智能体并不直接与 Adrafinil 通信。每个智能体的 hook 系统会调用捆绑的 CLI:
adrafinil acquire <session-key> --tool claude-code --reason "long build" # when a turn starts adrafinil release <session-key> # when the agent goes idle
持有是以活动为作用域的,而非以会话为作用域:Claude Code 在 UserPromptSubmit 时获取,在 Stop 时释放,因此 Mac 只在智能体实际工作时才保持唤醒——在提示符处打开但空闲的会话会让它正常休眠。
该守护进程按会话键进行引用计数,并在计数非零时请求辅助程序阻止休眠。
智能体还可以通过一个有时间的持有,为一个比其回复存活更久的后台任务(长时间构建或部署)保持 Mac 唤醒——既可以直接调用 adrafinil hold,对于支持 MCP 的智能体,也可以通过 adrafinil mcp 所提供的捆绑 MCP 工具来实现:
adrafinil hold --for 30m --reason "deploy" # keep awake up to 30 min, then auto-release adrafinil mcp # speak the Model Context Protocol on stdio (for agents)
你完全不需要智能体:菜单栏的保持唤醒按钮会手动放置同样类型的有时间的持有——用于长时间下载或任何非智能体任务——而让它休眠会释放一切。因此 Adrafinil 覆盖了全部三种方式:它通过 hook 自动检测智能体活动,让智能体通过 MCP/CLI 显式驱动它,并且在你需要时可以手动开启。
其他子命令:status、install-hooks、uninstall-hooks、daemon-status、version。
添加你自己的智能体
Adrafinil 为常见智能体提供了集成,但 CLI 对任何工具都适用——守护进程接受来自任意同一用户调用方的任意 --tool 标签,因此要接入一个 Adrafinil 从未听说过的工具,无需做任何改动。设置 → 智能体 → 添加你自己的智能体会根据你输入的名称生成确切的代码片段;选择与你智能体相匹配的形态:
-
它有钩子 / 事件——在启动时添加
acquire,在停止时添加release,以你智能体的会话 id 为键,这样每一轮都能干净地括起来:adrafinil acquire "$SESSION_ID" --tool my-agent # on start / prompt submit adrafinil release "$SESSION_ID" --tool my-agent # on stop / finish
-
它没有钩子——把命令包裹起来,让保持状态覆盖整个运行过程(以 shell 的
$$为键),或者为后台任务放置一个单次定时保持:adrafinil acquire $$ --tool my-agent && my-agent "$@"; adrafinil release $$ --tool my-agent adrafinil hold --for 2h --pid $$ --reason "my-agent session"
自定义智能体不会被自动检测或进程监控,因此要把每一个 acquire 与一个可靠的 release 配对——空闲释放超时和每个保持的时间限制就是万一遗漏时的安全网。
架构
横跨三个权限层级的四款产品(完整细节,包括 Xcode 项目布局,见 Docs/ARCHITECTURE.md):
┌──────────────────────────────────────────────────────────────┐
│ Adrafinil.app (menu bar app, user-facing) │
│ • Status item, settings, installer GUI, lid-open summary │
└─────────────────────────────┬────────────────────────────────┘
│ XPC
▼
┌──────────────────────────────────────────────────────────────┐
│ AdrafinilDaemon (LaunchAgent, runs as user, always-on) │
│ • Reference-counted assertion registry │
│ • Process watchers (kqueue NOTE_EXIT + periodic sweep) │
│ • Thermal monitor (SMC) • Lid-state monitor (IORegistry) │
│ • Lid-close chime • CLI socket at …/Adrafinil/cli.sock │
└─────────────────────────────┬────────────────────────────────┘
│ XPC (privileged Mach service)
▼
┌──────────────────────────────────────────────────────────────┐
│ AdrafinilHelper (SMAppService LaunchDaemon, root) │
│ • The ONLY component that touches sleep-blocking APIs │
│ • setSleepBlocked(Bool) + read-only state/version │
│ • Verifies caller's code-signing requirement │
└──────────────────────────────────────────────────────────────┘
adrafinil (CLI, ships inside the .app, symlinked onto PATH)
• acquire / release / hold / mcp / status / install-hooks / uninstall-hooks
• Connects to the daemon socket; <50ms round-trip
AdrafinilShared—— 一个在所有 target 之间共享的 Swift 包:数据模型(AgentKind、Assertion)、IPC 传输格式、AssertionRegistry、CallerVerifier、hook 安装规范,以及 CLI 参数解析器。单元测试就放在这里。- Helper 保持易于审计。它不承载任何策略——引用计数、温度、空闲和合盖逻辑全都位于 daemon 中。特权接口面只有一个可变端点加上只读自省。
- Daemon 是唯一事实来源。App 是纯粹的视图层;它可以自由退出并重新启动,而不影响已持有的 assertion。
值得了解的怪癖
- 公开的 IOPM 断言无法胜过翻盖式睡眠。
IOPMAssertionCreateWithName配合公开类型(因此也包括caffeinate)不会让合盖的 Mac 保持唤醒。Adrafinil 的 v1 使用pmset disablesleep 1,这种方式很粗暴(它还会禁用空闲睡眠),并且必须在关机时清除,否则会泄漏——辅助进程在重新应用状态之前,会在重生时重置为disablesleep 0。 - 守护进程处理程序运行在任意队列上。 XPC 和 socket 回调可能到达任何 dispatch 队列,因此断言注册表和共享状态会相应地同步。修改守护进程时,在并发方面要小心行事。
- CLI 受制于延迟预算。
acquire/release处于每个智能体会话的热路径上,因此采用静态查找(例如AgentKind.allBinaryNames)以及轻量级 socket 协议,而非在 CLI ↔ 守护进程这一跳上使用完整的 XPC。
许可证
MIT。随你怎么用,不提供任何担保。
致谢
由 @kageroumado 构建,发布于 kagerou.glass。这个名字是在向 adrafinil 致敬——一种促进觉醒的前体药物——因为这款应用只会在机器确实有活要干时才让它保持唤醒。
来源:Hacker News 热门(buzzing.cc 中文翻译) · github.com


