问题:常开机器上 Claude Code 崩了,你不在现场
把 Claude Code 放在新加坡常开的 Mac mini 上跑长任务,最怕的不是「慢」,而是进程自己退出了。可能是某次异常退出、依赖崩溃、会话挂掉后没有人再开一次。你第二天用 UU 远程连接或 SSH 回去,发现昨晚其实早就停了。
防休眠、tmux、限速重试各自解决一类问题,但它们不会在「进程已经死掉」时自动再拉起一次。这时更适合用 macOS 自带的 launchd:用用户级 LaunchAgent,打开 KeepAlive,让指定命令在退出后按策略重新启动,并把标准输出写进日志,方便事后核对。
这套做法适合什么、不适合什么
适合:
- 你已经有一个可重复启动的入口:例如一个 shell 脚本,会
cd到固定目录、再启动 Claude Code(或你自己的包装命令)。 - 目标是「进程挂了能自动回来」,而不是「替你点确认、替你合并代码」。
- 机器是常开、登录会话稳定的新加坡 Mac mini(整机独享)。
不适合:
- 把发布、删库、改密钥等高风险操作交给无人值守自动重跑。
- 指望自动重启解决额度用尽、鉴权失效、交互式确认卡住——那些要换策略,不是多拉几次进程。
- 和「定时任务手册」「限速重试」混为一谈:定时是按钟点触发;限速重试是请求失败退避;本文只谈进程级保活。
步骤一:先写一个「干净可重启」的启动脚本
不要让 LaunchAgent 直接塞一长串难维护的参数。先在用户目录放一个脚本,例如 ~/bin/claude-code-keepalive.sh:
#!/bin/zsh
set -euo pipefail
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
cd "$HOME/Projects/your-repo"
# 按你的实际入口改:官方 CLI 名称与参数以你本机文档为准
exec claude --print "继续执行仓库内约定好的无人值守清单"
要点:
- 脚本必须幂等:重复启动不应叠出十个互相抢锁的实例;需要时在脚本开头用
flock或 pid 文件互斥。 - 工作目录固定,PATH 写全,避免「人手开终端能跑、launchd 起来却找不到命令」。
- 不要在脚本里塞密钥明文;登录态继续用你自己账号已完成的官方登录方式。
chmod +x ~/bin/claude-code-keepalive.sh,在普通终端先手动跑通一次。
步骤二:写用户级 LaunchAgent(KeepAlive)
在 ~/Library/LaunchAgents/ 新建 plist,例如 ai.zumac.claude-code-keepalive.plist(标签名可自定,保持唯一即可):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ai.zumac.claude-code-keepalive</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/YOUR_USER/bin/claude-code-keepalive.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>30</integer>
<key>StandardOutPath</key>
<string>/Users/YOUR_USER/Logs/claude-code-keepalive.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOUR_USER/Logs/claude-code-keepalive.err.log</string>
<key>WorkingDirectory</key>
<string>/Users/YOUR_USER/Projects/your-repo</string>
</dict>
</plist>
说明:
- 用户级
~/Library/LaunchAgents即可,不必动系统级目录。 KeepAlive为 true 时,进程退出后 launchd 会再拉起;ThrottleInterval用来避免崩溃风暴(示例 30 秒,可按需要加大)。- 把
YOUR_USER换成你的实际用户名;提前mkdir -p ~/Logs。 - 若只想在「非零退出」时重启,可用字典形式的
KeepAlive(例如按SuccessfulExit条件配置);先从简单的全局 KeepAlive 验收,再收紧。
加载:
launchctl bootout gui/$(id -u)/ai.zumac.claude-code-keepalive 2>/dev/null || true
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.zumac.claude-code-keepalive.plist
launchctl enable gui/$(id -u)/ai.zumac.claude-code-keepalive
launchctl kickstart -k gui/$(id -u)/ai.zumac.claude-code-keepalive
(命令在较新 macOS 上常用 bootstrap / bootout;若你环境仍用旧的 load/unload,以本机 man launchctl 为准,原则相同:加载用户 Agent → 确认标签在跑。)
步骤三:怎么验证「崩了会再起来」
- 看状态:
launchctl print gui/$(id -u)/ai.zumac.claude-code-keepalive,确认状态为 running,路径与 plist 一致。 - 人工杀掉进程(只杀你的 Claude Code / 包装脚本对应进程,不要乱杀系统进程):记下 PID,结束后观察约
ThrottleInterval秒,PID 应换成新的,任务重新进入。 - 看日志:
tail -f ~/Logs/claude-code-keepalive.out.log与.err.log,核对是否在预期工作目录启动、有无鉴权失败或找不到命令。 - 重启机器后再查一次:登录后 Agent 是否随
RunAtLoad拉起;若你依赖图形登录会话,确认自动登录或你的远程接入方式能回到同一用户会话。 - 和
tmux分工:tmux保的是「会话不断线可接回」;LaunchAgent 保的是「进程没了能再起」。长交互仍可进tmux;需要崩溃自愈的入口交给 launchd。
步骤四:日志与告警,避免「静默重试一百次」
保活不是免检通行证。建议同时做:
- 轮转日志:日志目录定期清理或用
newsyslog/定时脚本截断,避免磁盘被刷爆(常开机尤其要注意)。 - 统计重启次数:简单做法是每次脚本启动往
~/Logs/claude-code-keepalive.restarts.log追加一行时间戳;一天内超过阈值就停掉 Agent,改人工排查。 - 失败要可见:鉴权过期、工作区锁死、依赖缺失会导致「拉起 → 立刻退出 → 再拉起」。这时应
bootout停掉保活,先修根因。 - 权限仍要你自己确认:自动改代码可以,合并、发布、动密钥必须你本人确认——保活只恢复进程,不扩大授权。
和已有方案怎么叠,而不是互相替代
| 手段 | 主要防什么 | |------|------------| | 节能 / 防休眠 | 机器睡着导致任务停 | | tmux | SSH 断线后会话丢失 | | 限速重试 / Retry Watchdog | 请求层 429 等瞬时错误 | | LaunchAgent KeepAlive | 进程崩溃或异常退出后无人拉起 |
先保证机器真常开、入口脚本可手动跑通,再挂 KeepAlive。顺序反了,只会得到一串快速失败的重启日志。
什么时候适合放到新加坡整机环境
笔记本合盖、出门断电,保活没有意义。更适合:需要固定新加坡时区与网络环境、笔记本不能 24 小时开着、希望整机独享且环境配完能挂整月的场景。
Zumac.Ai 在新加坡提供 Mac mini 整机独享,接入新加坡家庭宽带。新客户整机租用 ¥999/月 起;季付、半年付、年付有折扣。交付后可用 UU 远程连接;SSH 按你的安全策略自行开启。Claude 账号与订阅仍由你自行准备。咨询可 Telegram @singatao。
小结
常开 Mac mini 上跑 Claude Code,除了防休眠和 tmux,还缺一层进程崩溃自动拉起。用用户级 LaunchAgent + KeepAlive + 可重启脚本 + 日志与重启计数,就能在你不在现场时把入口捞回来。先手动跑通脚本,再加载 Agent,用「杀进程看是否复起」验收;一旦陷入崩溃风暴,先停保活再查根因。套餐与交付见 zumac.ai。
