I am LAZY bones?
AN ancient AND boring SITE

2026年 08月 30日 的归档

给 Claude 的 5 小时窗口掐个点

Claude Code 的都知道,额度是按 5 小时一个窗口算的,而这个窗口的起点,是你某个周期内发出的第一条消息。

为了充分利用这些窗口,我就想让这几个窗口卡在我自己定的点上——5:58、10:59、16:00、21:01,一天四个,首尾正好接得上。

办法也很土:到点了自动发一句 hello 过去,窗口就开了。但我平时是在MacBook和Mac mini上跑claude,”到点自动跑”这件事,在 macOS 上还真有个坑,值得写一篇,分享给有需要的朋友。

别用 cron,用 launchd

第一反应肯定是 crontab,写四行就完事。但 Mac 是会睡觉的。cron 在机器睡眠期间错过的任务,醒来之后不会补——那一条 hello 就这么静悄悄地没了,窗口也就没开成。

launchd 不一样。StartCalendarInterval 错过的时间点,会在唤醒后立刻补执行一次。对”激活窗口”这个目的来说,这个行为正合适。

而且四个时间点不用建四个 plist,StartCalendarInterval 直接收一个数组:

plist 放 ~/Library/LaunchAgents/,然后 load 一下就生效:

最后那行输出里,中间那个数字是上次的退出码,是 0 就说明跑通了。plist 的其它字段没什么特别的,launchd.info 上都有,照抄即可。

脚本本身:记得用 haiku

被调起来的脚本没什么内容,核心就一行:

这里的 --model haiku 是关键。5 小时窗口是账号级的,任何模型的第一条消息都会把它打开,那当然挑最便宜的来开——总不能为了开个窗口,先请 Opus 出来打个招呼吧,哈哈。

几个注意点

1. 路径必须写全。 launchd 拉起来的进程不走你的 shell 配置,~/.local/bin 根本不在默认 PATH 里,脚本里直接写 claude 是找不到的。要么在 plist 的 EnvironmentVariables 里把路径补上,要么脚本里写死绝对路径。我两个都做了,保险。

2. 补跑是把双刃剑。 前面夸 launchd 会补跑,但反过来说:如果 5:58 的时候机器是睡着的,八点你才掀开盖子,那次补跑就发生在八点,窗口也就从八点开始算——精心排的那套首尾相接的节奏,当天就错位了。这个绕不过去,属于”别漏掉”和”要准时”之间必须二选一。

3. 日志记得轮转。 一天四次,每次都往同一个文件里追加 Claude 的回复,攒几个月也挺可观。脚本开头加一句超过 1MB 就 mv 成 .1,两行的事儿。

两天后才发现:其实一直在假装成功

上线两天后回头翻日志,好家伙——除了第一次,后面十次全认证失败,报错是 OAuth session expired and could not be refreshed。但脚本每次都记着”退出码=0″,日志表面干干净净,窗口压根没开成也看不出来。

根子是 launchd 起的进程摸不到交互终端那份浏览器登录状态,用 launchctl start 手动触发一次就能稳定复现。解法是换成不依赖这份易失效登录的长期令牌:

因为我把令牌存在了 .zshrc 里,但 launchd 不会加载它,脚本里得自己挖出来:

一个教训:退出码不能全信,认证失败时它照样是 0。真要验证,就拿 launchctl start 手动戳一次,肉眼看日志内容。

顺带

这套东西是我让 Claude Code 自己写的——描述完需求,脚本、plist、load 命令一条龙全办了,中间它还顺手看了眼我已有的那个备份任务的 plist,把格式风格对齐了过来(EnvironmentVariables 那段的写法就是照抄我自己之前写的)。让 AI 给自己写定时唤醒脚本,这个循环还挺有意思的。