跳到正文
北京时间
原文
Hacker News 热门(buzzing.cc 中文翻译)· kageroumado·· 2026-06-28精选AI 评分72

阿德拉菲尼尔:仅在AI agent工作时阻止Mac睡眠的菜单栏工具

Show HN: 阿德拉菲尼尔——仅在药物起效期间保持“盖子紧闭的Mac”处于清醒状态

AI 导读

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的开发者是实用的开源伴侣。

正文 · AI 翻译

Adrafinil icon

adrafinil

处方编号 006 ・ a·draf·i·nil /əˈdræfɪnɪl/ ・ 为机器而生的促醒剂 ♡

Awake — kept awake while an agent works, with Keep awake / Let it sleep controls
awake ・ 有智能体正在工作
Idle — no agents, normal sleep, with a Keep awake button to hold it open yourself
idle ・ 没有智能体,正常睡眠
Hold — a duration picker to keep the Mac awake yourself for 15m, 30m, an hour, or indefinitely
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