ESC
开源 1 分钟阅读

codenotch:将 Claude Code、Cursor、Codex 与 Antigravity 用量限额固定在屏幕边缘的 macOS 应用

开源 macOS 应用 codenotch 在屏幕边缘固定一个小刘海,实时显示 Claude Code、Cursor、Codex 等 AI 编程工具的会话用量与限额,并提示 Claude 是在工作中、已完成还是在等待用户。项目已实现 Claude 官方用量接口读取及 Cursor、Codex 本地读取,数据每 60 秒刷新,采用 Swift 6 开发,支持自动更新,为重度使用 AI 编程工具的开发

来源:GitHub

Codenotch

工作名称——并非最终定名。

一款 macOS 代理应用,在屏幕右边缘固定一个小小的黑色刘海(notch),显示你已用掉各家 LLM 会话限额的多少,以及是否已经触顶。悬停在某个服务商上,可查看其各自的限额窗口和重置时间——并让你一眼看出 Claude 是仍在工作、已完成,还是在等待你。圆环始终显示当前会话,与 Claude 自家用量面板首推的窗口一致,因此两者永远不会相互矛盾。

收起状态带悬停提示的刘海

设置界面会显示每个读数所属的账号,每个服务商各有一个开关。打开开关会带你前往该账号的登录——对于 Codenotch 自己持有会话的服务商是打开一个窗口,对于 Cursor 和 Codex 则是打开对应的应用。关闭开关会停止读取凭据,并忘记从它那里取得的读数,因此这些数字不会在下次启动时再次出现。它做不到的是让你退出 Claude Code 或 Cursor 的登录:那些会话属于它们自己,界面上会明确说明这一点,而不是让你自己去摸索。

设置入口位于刘海下方的一个小球体中——静止时是角落里的一段圆弧,伸手去够时变成齿轮。当你不看它时它会收起:静止时它是屏幕边缘上的一个小胶囊,当指针靠近时便展开。点击即可将其固定展开。

刘海可以位于四条边的任意一条。左、右两侧保持竖排;上、下两侧则将读数并排排列,因为四个单元格竖向堆叠会垂下四分之一屏幕。它会将自己固定在可用边缘上,因此底部刘海会停在 Dock 之上——当 Dock 隐藏或移动时它也会跟随。在自带硬件刘海的 Mac 上,顶部放置会采用硬件刘海自身的形状——两侧笔直、下方圆润、融入顶部边框——因此刘海只是显得更宽更深,而不是在下面再停一条横条。收起时它就是刘海本身,伸手去够时刘海便会变大。

Codenotch 支持自动更新。Sparkle 每天检查一次,并在后台静默安装、不做提示,下次启动应用时应用新版本;设置面板中有相应说明,也可以将其关闭。每个更新都经过 EdDSA 签名,因此任何不是在此构建的东西都无法安装。

状态

刘海已经构建完成,与设计稿一致,并显示你真实的 Claude 用量——与 Claude 自家用量面板报告的会话和每周百分比相同,每 60 秒刷新一次。make run 即可将其显示在屏幕上,悬停会弹出详情卡片。

它还能回答**“Claude 是否还在工作?”**——在 Claude 圆环内部,会话工作时有一道细弧旋转,当某个会话被阻塞、等待你时则变成脉动的琥珀色圆环。悬停可查看每个活跃会话的名称、运行位置以及它需要什么。

Cursor 和 Codex 也已接通,均为本地读取:Cursor 从其 SQLite 状态存储中借用编辑器自身的会话,Codex 则读取它写入自己 rollout 日志中的限流快照——无需凭据,无需联网。Perplexity 的适配器保留但未注册。使用 CODENOTCH_DEMO=1 运行可查看设计稿中的三服务商布局及其数字。参见 TASKS.md。

UI 中的每个尺寸都依据设计稿测量,并以设计稿像素表示(Design.px(186)),因此布局在比例上是精确的。Design.scale 是唯一决定绝对尺寸的常量;它以规范中的 44pt 圆环为锚点,这使得刘海的尺寸为 70 x 401pt。

技术栈

Swift 6 / SwiftUI + AppKit,macOS 26,由 XcodeGen 生成的项目,代理应用(LSUIElement,无 Dock 图标)。约定与 ~/notch-app 相同。

构建

brew install xcodegen   # once
make run                # generate, build, launch
make test               # unit tests

坦诚的告诫

没有任何 LLM 厂商提供一个干净的"你的会话限额已用 N%“API。数据层是一组按服务商划分的适配器,各自声明其数据保真度——.official(官方)、.derived(推导)或 .manual(手动)——而 UI 绝不会把推导出的数字当作官方数字来展示。

Claude 属于 .official:ClaudeOAuthProvider 读取 Claude Code 保存在登录钥匙串中的 OAuth 令牌,并调用 GET /api/oauth/usage——Claude 自家的 /usage 面板也是从这里获取数字的。这并非公开 API——它可能在不另行通知的情况下变更——因此响应结构由测试固定,任何失败都会降级为可见的状态(stale、needsAuth、error),而不是编造出一个百分比。

~/.claude/projects 下的本地会话文件是最初的方案。它们包含 token 计数,但没有限额、也没有窗口元数据,因此从它们得出的百分比需要一个凭空构造的分母。如果该端点消失,它们仍将作为 .derived 的后备方案。

钥匙串: 该应用使用 Developer ID 身份签名,因此一次性的"始终允许"授权在重新构建后仍然有效。未签名的构建在每次 make run 后都会重新弹出授权提示。

限流: 如果轮询过于频繁,端点会返回 429,并回答 Retry-After: 0。退避机制仅把该提示当作下限参考——从 60 秒起步,每连续一次 429 翻倍,上限 15 分钟。最后一次成功的读数会在多次启动之间保留,因此被拒绝的请求会显示带时间戳的旧数据而非一片空白;圆环会变暗,悬浮提示的标题会说明数据有多旧。退避截止时间也会被持久化,因此在惩罚期内重新启动应用会选择等待,而不会浪费一次尝试机会。当没有会话在运行时,轮询频率降为每 5 分钟一次,右键点击刘海可选择立即刷新。

日志: 该应用是一个无窗口的代理应用,因此任何值得诊断的信息都会写入统一日志。

/usr/bin/log stream --predicate 'subsystem == "com.vinz.codenotch"' --level debug