2026 OpenClaw PATH、Homebrew 前缀,以及 launchd 下 ProxyMac Mac mini 的 MCP 可执行失败
在香港、日本、韩国、新加坡或美国的租用 Mac mini M4 上跑 OpenClaw 时,团队常把日志里 env: node: No such file or directory 或 uvx: command not found 整段贴过来,可同一命令在 终端 里明明成功。本文是登录 shell 与 launchd 之间的 PATH 契约:讲清 Apple 芯片上 brew --prefix 为何不同、如何不猜就读 LaunchAgent plist,并给出与 MCP 部署、部署排障、安装与上线 能接上的五步验证阶梯。你会看到一张和 Wi‑Fi 文风格不同的三列表、可在评审后粘贴的 EnvironmentVariables 片段、以及面向 帮助 与 OpenClaw 专题 的按钮。若你来自混合机型笔记本 fleet,还请在工单里写清是 arm64 还是 x86_64 解释器,避免把 Rosetta 指错当作「OpenClaw 坏掉」;也建议在变更窗口前先用只读域打印 launchctl,防止误把别人的 GUI 域当成系统域。
再补三条实务边界:第一,sudo -i 与 ssh 非交互会话各自继承的环境并不相同,别拿 root 的 echo $PATH 去判 launchd 子进程。第二,若 OpenClaw 以 LaunchDaemon 跑在 daemon 域,gui/$(id -u) 里看到的 PATH 完全无关,应改查对应 bootstrap 与 plist 的 ProgramArguments 是否多包了一层 env。第三,Node 通过 #!/usr/bin/env node 解析时,缺的是 env 的解析路径,而不只是 node 本体,记得把 /usr/bin 留在串里。把上述备注附在回滚单上,能显著减少「我本地 OK」类来回,尤其在港日新美韩 多区并行放量时。若 CI 在 macOS 13 与 15 上同时验过,也请在表头标最低支持版本,避免老镜像把 PATH 又洗回旧式 /usr/local 优先。若你使用 asdf 或 nvm 之类工具,额外确认 LaunchAgent 不会吃到交互式初始化脚本——那些只在终端登录时执行。
终端对 launchd:两套宇宙
macOS 下交互式 shell 会跑 /etc/zprofile、~/.zprofile、~/.zshrc,常含 eval "$(/opt/homebrew/bin/brew shellenv)"。launchd 代理拿到的是偏保守环境:PATH 常落在 /usr/bin:/bin:/usr/sbin:/sbin 这一档,除非 plist 里加长。网关注入 MCP 子进程时,于是活在裁过的宇宙里,npx 或 pnpm 垫片根本不存在,尽管终端里 which npx 会印 /opt/homebrew/bin/npx。不是「OpenClaw 把 MCP 弄丢」——execve 返回 ENOENT,内核解析不到 shebang 或解释器路径。把问题定性准确后,排障会快一个数量级,也不会误切 stdio 缓冲文章;PATH 没对齐时先修可执行,再谈管道。遇到「仅周末批次失败」这种间歇案,别急着怀疑 cron——多半是维护窗口里有人 brew upgrade 换了软链顺序,用 hash 对拍能在一分钟内结案。另若 mini 上同时开 Docker Desktop,有时会把 docker 的 CLI 也塞进争用同一段 PATH 的自动更新脚本,记得在问题描述里显式写「物理机直跑」还是「在容器里跑 OpenClaw」,不要混在一句里。
- 先证明裂口:在一枪式
ssh mini 'launchctl print gui/…'里记printenv PATH,与登录 shell 对比。 - 外侧 MCP 二进制优先用绝对路径,同时把助手工具的 PATH 稳定住。
- 在 Git 里跟漂移:照 配置版本化 模式记快照,系统升级时 brew 根目录别悄悄变。
Apple 芯片与 Intel:Homebrew 前缀矩阵(与 Wi‑Fi 文不同的三列表)
| 机器代际 | 默认 brew 前缀 | 缺 PATH 时的典型症状 |
|---|---|---|
| Apple 芯片 M4 Mac mini | /opt/homebrew | 报 node 找不到,可 /opt/homebrew/bin/node -v 仍出 v22.x |
| Intel Mac mini(旧机) | /usr/local | MCP JSON 仍指从笔记本抄来的 /opt/homebrew/bin/uvx |
| 混编设备队 | 磁盘上两套都有 | PATH 把 /usr/local/bin 放在 /opt/homebrew/bin 前时,错 shim 先命中 |
能扛重启的 plist EnvironmentVariables 模式
在托管 OpenClaw 或 MCP 监督进程的 LaunchAgent 里加 EnvironmentVariables 字典。顺序有讲究:先 Homebrew 垫片,再系统路径,后接 ~/.local/bin 之类的语言工具链。改完走 launchctl bootout gui/$(id -u)/… 再 kickstart;在 macOS 14 以后单靠旧式 unload 往往不靠谱。与 监控指南 的探活拼在一起,可在 PATH 抽风后约三分钟内触发告警。若你放在团队共享的 LaunchDaemon,记得用合适的 bootstrap 域并在文档里写回滚,避免半套 PATH 在凌晨滚动发布里扩散到港日新美韩 全 fleet。
示例片段(上线前再校验)
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
若 plist 由 MDM 统一下发,别忘「描述档覆盖」会比你手工 edit 的 LaunchAgent 先合并环境——这时应以 MDM 宣告为准,在工单写设备序列号与描述档版本号,别只贴本机 plutil -p 片段。对多用户登录的实验机,gui/501 与 gui/502 的 PATH 也会分叉,复现时务必锁定同一 UID。最后,LaunchAgent 以登录用户跑,LaunchDaemon 以 root 跑,混用时 OpenClaw 与 MCP 子进程要落在哪一侧必须在一开始就定案,别半夜把 GUI 域 plist 指到本属于系统的路径。
整份重写 MCP 之前五项检查
- 哈希比对:在终端对
shasum -a 256 $(which uvx),与即将写进 MCP JSON 的绝对路径对拍。 - 倒出 launchd 环境:用
launchctl print user/$(id -u)/limit与代理域确认 PATH 真加载。 - 模拟非登录:跑
env -i PATH=/usr/bin:/bin /opt/homebrew/bin/npx --version看最小 PATH 下 npx 自己能否活。 - 对 JSONL:在 JSONL 诊断 里筛
ENOENT。 - 留可回退快照:按 升级回滚 在批量改 PATH 前逐台 plist 打标签。
Node、uv 与垫片:版本管理在 launchd 下为何会崩
开发者常用 fnm、mise、asdf 在多个仓库间切换多版本 Node,而这些工具几乎总在 ~/.zshrc 里改 PATH;launchd 拉起的子进程却不会去读。MCP 里写 npx @scope/server 时,npx 本体必须先以绝对路径能被执行,它才会再去看 ~/.npm 等缓存。将笔记本上抄来的配置粘到 全新 ProxyMac mini 时,很典型的一幕是:交互式 shell 里 corepack enable 让 pnpm 出现在 PATH,而 plist 仍让 shebang 走 /usr/bin/env node,结果命中系统自带存根(部分镜像为 v18),与锁文件假定的 v22 不一致。失配往往不会刷成整行「可执行文件缺失」,而是藏在 OpenClaw 重试里,以 ERR_PNPM_UNSUPPORTED_ENGINE 这类晦涩症状露面。
uv 这条线同样如此:uvx 经 curl 装好后常在 ~/.local/bin 或 ~/.cargo/bin,即便终端能跑,launchd 的 PATH 仍可能缺这些目录。与其把 plist 里 PATH 再叠十几项,更常见是落地一只精简的包装脚本到 /usr/local/bin/mcp-env.sh(属主 root:wheel、避免全局可写位),在脚本里统一导出 PATH 再 exec 真正入口——对审计算账而言,可 diff 的单一文件优于二十处 plist 碎改。将脚本与 网关 launchctl 重启与恢复 片段同样纳入 Git,才能对齐周二与周四之间到底改了什么。
| 运行时 | 人常装在这里 | 不修补时 launchd 实际看到 |
|---|---|---|
| 经 Homebrew 的 Node | /opt/homebrew/bin/node | 多停留在 /usr/bin/env 一层解析;缺 brew 垫片则 ENOENT |
| uv 工具链 | ~/.local/bin/uvx | plist 字符串中的波浪线号不会自己展开,除非你显式处理 |
| 经 Corepack 的 pnpm | 与 node 同目录的垫片 | 仅当该目录在 PATH 中排在 /usr/bin 之前时体系才一致 |
/opt/homebrew/bin/node -e "console.log(process.version)",把标准输出那一行并入 JSONL 管线;brew 升级替换了可执行件时,版本号会先于用户侧体感而跳变。
常见问题
要不用 /bin/zsh -lc 包一切? 能跑,但会吞错、也搅乱信号处理——PATH 能显式就别绕登录函数。
Rosetta 的 brew 会搞挂 MCP 吗? 当二进位是 x86_64 而智能体以 arm64 编出来时会——用 file $(which node) 对拍架构与 OpenClaw 构建。
stdio 还卡? PATH 管执行前;管执行后的是管道——若进程已起来仍挂,见 stdio 缓冲。
为何 ProxyMac Mac mini 适合把 PATH 合同冻住
专用 Mac mini M4 给每租户自己的金属边界:一旦 /opt/homebrew 验过,就躺在 NVMe 上挨着你的 OpenClaw 工作区,别的工程师不会顺手 brew uninstall 共享 CI 可执行文件。原生 arm64 二进位少踩 Rosetta,统一内存 让并行 MCP worker 少争 NUMA,五个区域 可在合规近处布副本、Git 里 plist 却保持同一。PATH 变无聊之后,用 并行智能体 提并发,在 定价 约节点,让人走 帮助中心,GUI 提示必须人工点时再看 VNC 文档。把三份链接放进值班手册首页,能少一半误报为「区不对」的通宵。对需要审计的团队,还可把「PATH 与 plist shasum」当成季度健康检查项,与 密钥卫生 的轮换节奏对齐。若未来迁到可重复的 Ansible 或 Nix 层,记得仍保留 launchd 为事实来源,否则抽象层一升级又会出现「能跑但没人知道为何能跑」的隐形债。