# 让 Claude 半夜自己干活，只有两条路

> 两篇文章给了同一道题两个相反的答案：一个人抹了台闲置 Mac 全开权限，一个人在 GitHub Actions 里把工具收成只读白名单。它们其实用的是同一个公式——爆炸半径 = 你丢不起的东西 × agent 碰到它的概率。附几个自己摸绝对要栽的坑。

- 原文链接: https://laojin.blog/blog/20260729_unattended_claude_two_paths
- 作者: 老金
- 发布日期: 2026-07-29
- 标签: Claude Code, 无人值守, 自动化, 权限管理, GitHub Actions

---

![让 Claude 半夜自己干活，只有两条路](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260729/00_cover.png)

看到两篇文章，发现它们其实是同一道题的两个答案，而且答得完全相反。

一篇是个人博客。作者翻出一台闲置 Mac，抹盘，建一个不登 Apple ID 的干净账号，打开 SSH，配好免密 sudo，把 Claude Code 装上去，所有会话统一带 `--dangerously-skip-permissions` 跑。他管这台机器叫 the box。

另一篇是国内一个独立开发者的实测，把 Claude 挂进 GitHub Actions 审 PR。他的做法正相反：工具收成白名单，只给 `Read,Grep,Glob`，连 `Edit` 都不给；容器 `--network none` 直接断网；单次预算 `--max-budget-usd 0.50` 兜底。

一个人在拼命放权，一个人在拼命收权。可他们想干的事一模一样：让 Claude 在自己睡觉的时候干活。

---

## 门槛从来不是"跑起来"

技术门槛只有一行：

```bash
claude -p "分析 src/ 目录的安全漏洞"
```

`-p` 是 `--print` 的短写，跑完直接退出，不进交互式 REPL。到这儿就结束了。

真正的门槛在下一句：你敢去睡吗。

想清楚这件事得先想清楚交互式模式到底在干什么。你每次按下的那个回车都是一次授权判断：它要不要动这个文件、要不要跑这条命令、要不要装这个包，全靠你那一下。这个权限系统长在你手上，所以你从来没意识到它存在。

无人值守就是把回车键拿掉。拿掉之后那个位置不能空着，得有别的东西补上。

补的方式只有两种：换一台机器，或者换一套参数。

---

## 路线一：换一台没什么可失去的机器

第一篇文章的思路很干脆——不去限制 agent 能干什么，而是让它干什么都不要紧。作者原话是"在一台没什么可失去的机器上运行拥有广泛权限的代理更安全"。

![](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260729/01_empty_box.png)

他为此明确拒绝了容器方案，理由有两条，都挺实在：容器仍然跑在你的宿主机上，它发的网络请求还是从你的 IP 出去；而且容器里跑不了只有 macOS 才有的东西——他想让 agent 开 Unity 做游戏开发，想让它点击拖拽真实的 App 界面，容器给不了。

这条路的关键动作按顺序是：抹盘 → 建一个不登 Apple ID 的本地管理员账号 → `sudo systemsetup -setremotelogin on` 开 SSH → 写 `/etc/sudoers.d/` 免密 sudo → `ssh-copy-id` 配免密登录 → 装 Claude Code。

真正值钱的是里面几个你自己摸绝对要栽的坑：

它会睡过去。macOS 插着电闲置十分钟也照样睡，一睡就断网，你半夜派的活直接停在那儿。三条命令按住：

```bash
sudo pmset -c sleep 0          # 插电时永不系统睡眠
sudo pmset -c disablesleep 1   # 合盖也不睡（翻盖模式）
sudo pmset -c displaysleep 0   # 显示器保持开启
```

PATH 要写 `.zshenv`，不是 `.zshrc`。因为 `.zshenv` 每个 zsh 都读，包括非交互式的那种——你从源机 `ssh 目标机 'claude -p ...'` 走的就是非交互式，写在 `.zshrc` 里它找不到 claude。

**被授权的是 tmux，不是 claude。** 这条最反直觉。你想让盒子上的 Claude 截屏、点鼠标，macOS 把屏幕录制和辅助功能权限绑在 GUI 登录会话上，SSH 进程摸不到显示器。变通办法是让一个 LaunchAgent 在 GUI 会话内部维持一个固定 socket 的 tmux 服务器，claude 作为它的子进程跑，于是继承了 GUI 会话。而 macOS 把捕获权限归给链条上的"负责进程"——所以你要去系统设置里给 `/opt/homebrew/bin/tmux` 授权，不是给 claude。授完还得重启 tmux 服务器，因为进程在启动时就缓存了自己的权限状态。

GitHub 用小号。作者建议盒子上 `gh auth login` 登一个单独的 GitHub 账户，不是主账户。这一条我强烈同意，它是这套方案里最便宜的一道保险。

出口 IP 也顺手分开。他在盒子上跑 Proton VPN，让 agent 的流量不从自己家 IP 出去。

配完之后他有两个我挺眼馋的用法。一个是 `clip send` / `clip get`，拿 `pbcopy` 和 `pbpaste` 对着 SSH 管道一接，剪贴板就在两台机器之间走，端到端，不过第三方服务，图片也能传。另一个是从手机控制——`claude remote-control`，路上用 Claude App 就能给盒子派活。

还有个更狠的：因为盒子上所有会话都挂在同一个 tmux socket 上，你主力机上的 Claude 可以直接 `send-keys` 给盒子上的 Claude 派活、再 `capture-pane` 读回复。agent 对 agent。

```bash
ssh <user>@<host> "tmux -S /tmp/cc-tmux.sock send-keys -t <session> '切到 main 拉一下，PR #4 已经合了' Enter"
sleep 2
ssh <user>@<host> "tmux -S /tmp/cc-tmux.sock send-keys -t <session> Enter"
ssh <user>@<host> "tmux -S /tmp/cc-tmux.sock capture-pane -t <session> -p" | tail -30
```

那两个 Enter 不是笔误。长提示里跟文本同批发过去的 Enter 不算提交，得再补一下。

---

## 路线二：机器动不了，那就收权限

第二篇的处境不一样。他要让 Claude 在 GitHub Actions 里审 PR，那台 runner 上有他的仓库、他的 secrets、他的合并权限——机器换不掉。

所以他反过来做，把交互式里那些"人按回车"的隐式约定全部显式化成参数：

```bash
# 输出格式
claude -p "审查 PR" --output-format json           # 给机器解析
claude -p "分析架构" --output-format stream-json   # 一行一事件，长任务实时盯

# 成本护栏
claude -p "审查代码" --max-turns 5 --max-budget-usd 0.50

# 安全边界
claude -p "审查代码变更" --allowedTools "Read,Grep,Glob"   # 只读，最安全
claude -p "检查 git 历史" \
  --allowedTools "Read,Grep,Glob,Bash(git log *),Bash(git diff *)"  # 细粒度白名单

# 执行控制
claude -p "代码审查" --model claude-sonnet-5 --fallback-model claude-haiku-4-5
```

![](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260729/02_allowed_tools.png)

`--allowedTools "Read,Grep,Glob"` 这一条是整条路线的地基。审查任务本来就不需要写文件，那就一个写权限都别给。这比任何"请不要修改代码"的提示词都硬——提示词它可以不听，工具不给它就是真的没有。

他还有几笔账我觉得值得抄走：

一是 `concurrency` 必配。同一个 PR 连推三个 commit，不配这段就是三份账单：

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true
```

二是分级跑。阶段一用 Haiku 查格式，`needs` 依赖它过了，阶段二才上 Sonnet 做安全扫描——格式都过不了的 PR 没必要烧贵模型的钱。他报的数是一条 PR 从单一强模型全量审查的 ~0.15 美元压到 ~0.06 美元。单条审查落在 0.03~0.08 美元这个区间。

三是 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`。一个变量关掉自动更新、遥测和错误上报，否则 CI 跑着跑着 Claude 自己开始检查更新，日志里全是噪音。

四是 `--fallback-model`。主模型 429 的时候自动切便宜的那档，不至于让整条管道挂在过载上。

---

## 两条路，一把尺子

| | 换机器 | 收权限 |
|---|---|---|
| 手段 | 隔离宿主 | 白名单工具 |
| 权限姿态 | 全开（`--dangerously-skip-permissions`） | 最小（连 Edit 都不给） |
| 前期成本 | 一台闲置 Mac + 半天配置 | 十几行 YAML |
| 边际成本 | 几乎为零（吃订阅额度） | 按次计费，需要预算护栏 |
| 适合 | 探索性、要装东西、要点 GUI 的活 | 结论明确、可重复、要卡门禁的活 |
| 失败长什么样 | 盒子被搞坏，重装 | 报告不准，人工复核 |

两条路看着不像，其实用的是同一个公式：

> 爆炸半径 = 这台机器上你丢不起的东西 × agent 能碰到它的概率

![](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260729/03_blast_radius.png)

换机器那条路把第一项清零，所以它敢完全不管第二项。收权限那条路第一项动不了，只能死磕第二项。

于是最危险的那种状态就自己浮出来了：**第一项和第二项都拉满**——在你自己的主力机上开 `--dangerously-skip-permissions`。那台机器上有你的 SSH 私钥、云控制台的登录态、生产库连接串、没提交的代码、家人的照片。

这是现在绝大多数人的状态。包括我。

---

## 拿这把尺子量我自己

第一篇里有一节标题就叫"为什么不使用 OpenClaw 之类的工具"，我看到有点被点到，因为我这个仓库的自动化现在就是 OpenClaw 在跑——每天的 translate、ingest、compile 都是它触发的，我没用 launchd 也没写 cron。

作者不用它的理由是三条：想用 Claude Code 的完整最新功能，想从手机控制，想吃订阅额度而不是 API 计费。三条我都认，但这不改变我的选择——因为按上面那把尺子量，我这套东西根本不在同一个风险档位上。

它跑在我主力机上，动的只有这个仓库的 markdown 文件，没有 secrets，没有发布权限。最坏情况是把 wiki 写脏，`git checkout .` 一把回滚。第一项接近零，所以它天然落在安全那边，我不需要为它做任何额外的事。

真该动的是另一个：我每天手工敲的那些 Claude Code 会话。同一台机器，同一堆私钥，权限还开得更大。我一直觉得"有我盯着"就是安全，可我盯着的方式，就是那个我从来没写下过标准的回车键。

---

## 无人值守真正的账，记在别处

上周那篇《如何给 Loop 造个验证器》里我钉了一条：验证器的价值全在于它不听 agent 的话。无人值守把这件事从"最好有"变成了"必须有"——你不在场，就没有"中途叫停"这个选项了，只剩事前限权和事后验证两道。

第二篇里那个 Gate 就是在做事后那道：

```yaml
- name: Gate on critical issues
  run: |
    if echo "$RESULT" | grep -qi "critical"; then
      echo "::error::Critical issues found"
      exit 1
    fi
```

评审结果里出现 critical 就让 CI 挂掉。它能真的拦住 merge，而不只是产一份等人去读的报告，方向完全对。不过 `grep -qi critical` 这个实现挺脆，Claude 换个措辞写成"严重问题"就漏了。真要用，让它用 `--json-schema` 输出结构化的 severity 字段，卡枚举值，别卡关键词。

另一笔账记在《AI 编码的认知债》那边。无人值守最贵的代价不是它搞坏什么，是你不知道它干过什么——早上起来只看到一个结论。所以下面这行不是运维洁癖，是在还这笔债：

```bash
claude -p "任务" --output-format json | tee -a ~/claude-audit.jsonl
```

谁、什么时候、跑了多少轮、花了多少钱，全在这一行里。配上 `stream-json` 实时盯长任务，你至少还留着一条能回去读的轨迹。

---

## 今天就能动手的几件事

1. 先列单子，再挑教程。打开你现在跑 Claude 那台机器，列出上面所有你丢不起的东西：SSH 私钥、云控制台登录态、生产库连接串、未提交的代码、私人照片。这张单子决定你走哪条路——它长就去收权限，它空就可以放权限。反过来先挑教程，两条路你都会配错。

2. 今晚就能有的最小无人值守，不用买机器。找一件你已经在手工重复做的活，用只读白名单接上去。比如我这个仓库的：

```bash
claude -p "读 raw/_registry.md 和 me.md，列出所有 status 为 pending 的素材，按我的关注主题排优先级，只输出前三条和理由" \
  --output-format json \
  --allowedTools "Read,Grep,Glob" \
  --max-turns 5 \
  --max-budget-usd 0.10 \
  | jq -r '.result'
```

   跑通了再谈自动化。这条命令里没有一个工具能改你的文件，所以你现在就可以按回车。

3. 把审计打开。任何无人值守的任务，输出一律走 `json`，一律 `tee` 落盘。这条现在不做，等你想查的时候已经没东西可查了。

4. 要走放权那条路，记住授权对象是 tmux。屏幕录制和辅助功能都授给 tmux 二进制，授完重启 tmux 服务器。我把这条单独列出来，是因为它是整篇指南里唯一一个你自己摸半天也摸不出来的地方。

---

那份标准得先写出来：哪些命令我一向直接放过、哪些一定要先看一眼 diff、哪些不管多晚都该停下来等我。我按了几万次回车，依据的规则一条都没落在文件里，全在手上。

机器怎么配、参数怎么给，都是有了这份东西之后的下游活。
