★
Decaf
一个 macOS 菜单栏应用——只在编程 agent 真正干活时让 Mac 保持清醒,agent 一旦在等你,就放它去睡。
- 类型
- 其他
- 角色
- 独立开发
- Status
- 进行中
- Tech
- Swift 6 SwiftUI AppKit IOKit FSEvents Swift Package Manager XcodeGen Swift Testing GitHub Actions Homebrew Cask
- Started
- 2026年8月
Decaf 是一个懂得何时停下的 caffeinate 菜单栏版本。编程 agent 真正在干活时,它让 Mac 保持清醒;agent 一结束——或者只是在等你回话——它立刻松手。
唯一的规则:跑二十分钟的工具调用是工作;屏幕上一个没人回答的提示不是。
整个应用就是这个菜单:什么在跑,以及你能对它做什么。
为什么做这个
这个品类里的每一个防休眠工具,本质都是一个你必须记得关掉的开关。忘了关,Mac 整夜不睡;关得太早,一次长构建死在半路。
难点从来不是”阻止休眠”——那只是一次 IOKit 调用。难的是判断:把”agent 正在思考”和”agent 正在等你”区分开,而且要稳到误报(Mac 永远不睡)和漏报(构建到一半睡过去)都不常发生。
安装
brew install --cask AlanY1an/decaf/decaf
或者直接从 最新 release 下载已签名并公证的 DMG。需要 macOS 14 或更高版本。
四种菜单栏状态。第一种下 Mac 会正常休眠。
它怎么判断
三层检测,从最精确的开始。它们不是互相替代的方案——下层专门补上层的盲区。
| 层 | 信号 | 精度 | 角色 |
|---|---|---|---|
| L1 · hooks | Claude Code 在 SessionStart、UserPromptSubmit、Notification、Stop、SessionEnd、PostToolUse 上执行的辅助程序 | 精确到这一轮对话 | 主路径。经你明确同意后安装,深度合并,不动你已有的 hooks |
| L2 · 文件活动 | ~/.claude 上的 FSEvents,5 分钟空闲窗口 | 约 5 分钟 | 零配置兜底。看不到一轮的边界,只知道还有东西在写 |
| L3 · CPU 采样 | proc_pid_rusage | 持续 | 只作证,不触发。它只能推翻一个 hold,永远不能发起一个 |
其中两个推断承担了大部分判断:
- 权限弹窗。 你点”允许”时 Claude Code 什么都不发,所以”批准过了”的证据是工具自己跑完了——一个标记为等待权限的会话收到
PostToolUse,就直接回到工作态。工具不可能在自己的弹窗没被回答时完成,这个推断因此站得住。 - 等待信号。 当 agent 声明自己要等——定时唤醒、带超时的监听——这个声明会以工具调用记录的形式落进 transcript。Decaf 读出那个时间点,陪它一起等,而不是在这段空档里睡过去。
一次二十分钟的构建既不写文件也不打印任何东西。必须四个证人都同意某个会话确实安静下来了,hold 才会释放——这正是它能撑过长构建的原因。
Hook 程序的契约
一个行为不当的 hook 会拖垮 Claude Code 本身,所以 decaf-bridge 被刻意做成仓库里最小的东西:读 stdin,解析五个头部字段,连 UNIX socket,写一行 JSON,退出码 0。任何失败都静默地以 0 退出——不写 stdout(那会被当成上下文注入你的对话),不写 stderr,绝不返回非零,整个过程被一个 90 ms 的看门狗兜住。它只允许 import 一个模块,CI 脚本会检查链接库白名单,确保这条约束一直成立。
安全护栏
- 每个 assertion 都带 30 分钟的
TimeoutActionRelease——万一 Decaf 崩了,powerd会释放它,而不是让 Mac 永远醒着。 - 续期是先建后放,在超时时间的一半处执行,从不用
IOPMAssertionSetProperty延长。 - 低电量模式、快速用户切换、电量过低都会释放 hold。合盖永远优先。
隐私
Decaf 监听 ~/.claude,这件事需要一个直接的回答:它不发任何网络请求——没有遥测,没有统计,连更新检查都没有;它从不读你的对话内容,只读时间戳、会话 id 和 token 计数。解析器是封闭枚举,字段被测试钉死,所以想放宽它会直接让构建失败,而不是指望 code review 抓住。
它写入 ~/.claude/settings.json 之前,会把要合并的 JSON 原样给你看;设置里可以一键撤销。
发布
v0.1.0 已签名并公证发布,DMG 之外还配了 Homebrew tap,brew install --cask 开箱即用。Xcode 工程由 XcodeGen manifest 生成且不入库,CI 在每次 push 上跑 package 测试。
它不在 Mac App Store 上,以后也不会——沙盒让 Decaf 做的事变成不可能,而不只是不方便。
名字
这个品类里所有东西都以兴奋剂命名。这个以”知道何时停下”命名。副标题保留 caffeinate 这个词,因为那是你已经认识的命令;但应用本身刻意不叫这个名字,这样它永远不会遮住 /usr/bin/caffeinate。