常开机器解决了「在线」,没解决「放心」
把 Claude Code 装在一台常开的 Mac mini 上,长任务不再因为笔记本合盖而中断,这一步很多人已经做到了。真正跑起来之后,新的问题马上出现:
- 一过夜就卡住:任务停在一个权限确认上,等你早上醒来才点「允许」,一整晚白白过去。
- 口头约定不可靠:在对话里叮嘱「不要 push」「不要动配置文件」,上下文一压缩,这句话可能就不在模型眼前了。
- 额度或上下文用完,进度断档:重构做到一半停下,下一次打开不知道做到了哪一步。
- 出门在外不知道状态:任务是做完了、报错了,还是在等你确认,手机上看不到。
这几件事的共同答案是:把「能做什么、不能做什么」写进配置,而不是写在聊天里;把「做到哪了」写进文件,而不是留在会话里。下面按步骤在一台新加坡 Mac mini 上配好。
准备清单
- 一台常开的 Mac mini(整机租用或自有设备托管),已装好 Claude Code 并用你自己的账号登录
- 一个专门给 Claude Code 干活的项目目录,代码已在 Git 仓库里
- 远程方式:SSH 用来看日志、接回
tmux;UU 远程用来处理需要图形界面的操作 - 建议先在有人看着的情况下完整跑通一次,再改成无人值守
步骤一:用 settings.json 写清「允许」和「禁止」
Claude Code 支持在设置文件里写权限规则。常见的位置有三处:用户级 ~/.claude/settings.json、项目级 .claude/settings.json(随仓库共享)、本机专用的 .claude/settings.local.json(不提交)。规则的具体语法以你所装版本的官方文档为准,在 Claude Code 里输入 /permissions 可以查看当前生效的规则。
一个偏保守的起点,可以写在这台 Mac mini 的 .claude/settings.local.json 里:
``json { "permissions": { "allow": [ "Bash(npm run test:*)", "Bash(npm run lint:*)", "Bash(git status)", "Bash(git diff:*)", "Bash(git add:*)", "Bash(git commit:*)" ], "deny": [ "Bash(git push:*)", "Bash(rm -rf:*)", "Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)" ] } } ``
思路很简单:允许列表只放跑测试、看差异、本地提交这类可回退的动作;禁止列表放推送、删除和读取密钥文件。推送和合并留给你自己早上看过差异再做。
步骤二:用 hook 做一道硬拦截
权限规则之外,还可以加一个 PreToolUse hook:每次 Claude Code 要执行命令前,先跑你写的脚本,脚本以退出码 2 结束就会拦下这次调用,并把原因反馈给 Claude。比如在 ~/.claude/hooks/guard.sh 里写:
``bash #!/bin/bash cmd=$(python3 -c 'import sys,json;print(json.load(sys.stdin).get("tool_input",{}).get("command",""))') case "$cmd" in *"git push"*|*"--force"*|*"security find-"*) echo "此操作需要人工确认,已拦截:$cmd" >&2 exit 2;; esac exit 0 ``
然后在设置文件里登记:
``json { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/guard.sh" } ] } ] } } ``
记得 chmod +x ~/.claude/hooks/guard.sh。这样即使对话里的叮嘱被压缩掉,推送、强制覆盖和读取钥匙串这类操作仍然会被挡住。hook 的输入字段以官方文档为准,改完先手动触发一次看是否生效。
步骤三:把长期规则写进 CLAUDE.md
在项目根目录放一个 CLAUDE.md,写下每次都要遵守的规则,比如:
- 测试命令是
npm run test,每完成一个小步骤就跑一次 - 不改
config/production/下的任何文件 - 每做完一项,在
PROGRESS.md里追加一行:做了什么、下一步是什么、有没有卡住
CLAUDE.md 每次会话都会读到,比在聊天里反复叮嘱稳定。它是行为约定,硬保证仍然靠步骤一和步骤二。
步骤四:用 claude -p 加 launchd 定时运行
claude -p 是非交互模式:给一段指令,跑完输出结果就退出,适合交给定时任务。先写一个脚本 ~/jobs/nightly.sh:
``bash #!/bin/bash cd ~/work/myapp || exit 1 git pull --ff-only claude -p "读 PROGRESS.md,继续完成下一项未完成的任务;每完成一项就跑测试并在本地提交,最后更新 PROGRESS.md" \ --output-format json > ~/jobs/logs/nightly-$(date +%F).json 2>&1 ``
然后在 ~/Library/LaunchAgents/ 下放一个 plist,用 StartCalendarInterval 指定每天凌晨两点运行,再用 launchctl load 加载。注意三点:
- LaunchAgent 跑在你的登录会话里,Mac mini 需要保持登录状态并关闭自动睡眠。
- 第一次务必手动执行一遍脚本,确认 Claude Code 的登录状态在这种方式下可用。
- 无人值守时遇到未被允许的操作,应该让它停下或跳过,而不是去放宽规则。宁可早上多处理一个提问,也不要给它全开。
步骤五:进度接力和状态通知
- 进度文件:
PROGRESS.md是下一轮的起点。额度用完或上下文压缩后,新会话读这个文件就知道接着做什么。 - 日志留档:每次运行的输出存成按日期命名的文件,早上用 SSH 登录
ls ~/jobs/logs就能看到结果。 - 完成通知:在脚本最后加一行,把运行结果用你自己的 Telegram 机器人或邮件发给自己,手机上就知道是做完了还是卡住了。
- 交互任务放 tmux:需要你随时接管的任务,依然放在
tmux里跑,出门用手机 SSH 连上tmux attach就能回到现场。
上线前自检
/permissions里能看到你的 allow 和 deny 规则。- 故意让 Claude Code 执行一次
git push,确认被拦下。 - 手动运行
nightly.sh,日志里有完整输出,本地提交正常。 - 合上远程连接、过一晚,第二天日志和
PROGRESS.md都更新了。 - FileVault、防火墙和自动锁屏仍然开启,项目里没有明文密钥。
为什么放在新加坡的整机上
无人值守最怕环境变来变去:今天在家里网络、明天在公司、后天在手机热点,登录状态和网络都不一样。把 Claude Code、项目仓库和定时任务固定在同一台常开的物理机上,网络环境稳定、时区固定,排查问题时也只需要看一台机器。
SgRent 在新加坡提供 Mac mini M6 整机独享(一人一台物理机),接入新加坡家庭宽带原生 IP,适合作为 Claude Code 的常驻工作机。新客户整机租用 ¥699/月 起,已有 Mac 也可以托管,¥299/月 起;季付、半年付、年付都有折扣。通常付款后 24 小时内交付,交付后可用 UU 远程连接,SSH 按你的安全需要自行开启。Claude 账号与订阅由你自己准备。
查看方案与费用,或在帮助中心了解连接方法;有问题可以在 Telegram 联系 @singatao。
小结
让 Claude Code 放心地无人值守,靠的不是更聪明的提示词,而是三样东西:写进配置的权限边界、写进 hook 的硬拦截、写进文件的进度记录。把它们和 Claude Code 一起放在一台常开的新加坡 Mac mini 上,晚上交给它做可回退的工作,早上你来做推送和上线的决定。
前往 panel.zumac.ai 订购 → · 查看方案与费用 · 帮助中心
