给 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 直接收一个数组:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<key>StartCalendarInterval</key> <array> <dict> <key>Hour</key><integer>5</integer> <key>Minute</key><integer>58</integer> </dict> <dict> <key>Hour</key><integer>10</integer> <key>Minute</key><integer>59</integer> </dict> <dict> <key>Hour</key><integer>16</integer> <key>Minute</key><integer>0</integer> </dict> <dict> <key>Hour</key><integer>21</integer> <key>Minute</key><integer>1</integer> </dict> </array> |
plist 放 ~/Library/LaunchAgents/,然后 load 一下就生效:
|
1 2 3 |
plutil -lint ~/Library/LaunchAgents/com.lly.claude-keepalive.plist launchctl load ~/Library/LaunchAgents/com.lly.claude-keepalive.plist launchctl list | grep claude-keepalive |
最后那行输出里,中间那个数字是上次的退出码,是 0 就说明跑通了。plist 的其它字段没什么特别的,launchd.info 上都有,照抄即可。
脚本本身:记得用 haiku
被调起来的脚本没什么内容,核心就一行:
|
1 |
"$CLAUDE" -p "hello" --model haiku >> "$LOG" 2>&1 |
这里的 --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 手动触发一次就能稳定复现。解法是换成不依赖这份易失效登录的长期令牌:
|
1 |
claude setup-token # 生成一个 sk-ant-oat01-... 开头的长期令牌,存进 .zshrc ,据说这个令牌能管1年。 |
因为我把令牌存在了 .zshrc 里,但 launchd 不会加载它,脚本里得自己挖出来:
|
1 |
eval "$(grep '^export CLAUDE_CODE_OAUTH_TOKEN=' ~/.zshrc)" |
一个教训:退出码不能全信,认证失败时它照样是 0。真要验证,就拿 launchctl start 手动戳一次,肉眼看日志内容。
顺带
这套东西是我让 Claude Code 自己写的——描述完需求,脚本、plist、load 命令一条龙全办了,中间它还顺手看了眼我已有的那个备份任务的 plist,把格式风格对齐了过来(EnvironmentVariables 那段的写法就是照抄我自己之前写的)。让 AI 给自己写定时唤醒脚本,这个循环还挺有意思的。
发表回复