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 專題 的按鈕。若你來自混合機型筆電艦隊,還請在工單寫清是 arm64 還是 x86_64 解譯器,避免把 Rosetta 指錯當成「OpenClaw 壞掉」;也建議在變更窗前先以唯讀域列印 launchctl,免誤把別人的 GUI 域當成系統域。若週期性滾版會帶到 brew 升級,記得在變更單上勾「PATH 不變」的驗收項,讓 SRE 不用靠猜釘死責任歸屬。
再補三條實務邊界:第一,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 沒對齊前先把可執行修掉,再談管線。若你曾用 GUI 的「在終端機打開」驗路徑,也請在文件註明那是人的脈絡,以免自動化團隊照抄到 plist。遇見「僅週末批次失敗」這種間歇案,別急著怪 cron——多半是維護窗裡有人 brew upgrade 換了軟連順序,用雜湊對拍能在一分鐘內結案。另若 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 在凌晨滾式發佈裡灑到港日星美韓 全艦隊。變更窗結束前跑一筆 launchctl print 歸檔,年底稽核要調閱也拿得出。若你由 MDM 統一下發描述檔,別忘「覆寫層」會先於你手動改過的 LaunchAgent 合併環境——此時以 MDM 宣告為準,工單要寫裝置序號與描述檔版號,別只貼本機 plutil -p 片段。多使用者共用的實驗機上,gui/501 與 gui/502 的 PATH 也會分岔,重演問題時要鎖定同一 UID。最後,LaunchAgent 以登入使用者執行,LaunchDaemon 以 root 執行,混用時 OpenClaw 與 MCP 子程序落哪一側務必一開始就定案,別半夜把 GUI 域 plist 指到屬於系統域的路徑。
範例片段(正式上線前再驗證)
<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>
整份改寫 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 等快取。把筆電貼上來的 JSON 裝到 全新 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 再疊一長串,不如放一支精簡包裝腳本到 /usr/local/bin/mcp-env.sh(root:wheel、不要設成眾人可寫),在腳本裡一次匯出 PATH 再 exec 真正入口——稽核寧可審一個可 diff 的檔。把腳本與 閘道 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 工作者少爭 NUMA,五個區域 可在法遵近處佈副本、Git 裡 plist 卻保持同一份。PATH 變乏味之後,用 平行代理 提併發,在 定價 約節點,讓人走 說明中心,GUI 提示必須人工點時再看 VNC 文件。把三個連結放進值勤手冊首頁,能少一半被誤報成「機房不對」的徹夜。若你還有合規審 log,也請把 launchctl print 的時間戳一併附給稽核,省得年後被問「那時到底哪個 PATH 生效」卻沒有證物。對要稽核的團隊,還可把「PATH 與 plist shasum」當成季節性健康檢查項,與 金鑰衛生 的輪替節奏對齊。若未來遷到可重複的 Ansible 或 Nix 層,記得仍保留 launchd 為事實來源,否則抽象層一升級又會出現「能跑但沒人知道為何能跑」的隱性負債。