2026: PATH OpenClaw, префиксы Homebrew и сбой запуска серверов MCP под launchd на Mac mini в ProxyMac
Команды, которые ведут OpenClaw на арендованных Mac mini M4 в Гонконге, Японии, Корее, Сингапуре и США, регулярно присылают логи, где дочерние серверы MCP печатают env: node: No such file or directory или uvx: command not found, хотя в Терминале тот же вызов срабатывает. Этот материал — контракт по переменной PATH между интерактивной login-shell и launchd: почему brew --prefix на Apple Silicon и Intel расходятся, как читать LaunchAgent-файл без магии, и пятишаговая лестница проверок, согласованная с настройкой серверов MCP, устранением сбоев при выкатке и гайдом по установке и развёртыванию. Внутри — широкая таблица симптомов, готовый фрагмент EnvironmentVariables в XML, который в прод вносят только после ревью, и кнопки на справку плюс страницу хаба OpenClaw без «маркетинговой пены». Если вы уже ввели SRE-процесс для brew upgrade Node, но ночной перезапуск агента снова ломает execve, чаще всего виноват не сетевой слой, а таблица путей, которую ssh-сессия видела, а launchd — нет. Зафиксируйте гипотезу в тикете: до правки plist путь which npx и путь, который видит launchctl print для Job-объекта, должны совпасти побитно.
Дополнительный контекст: при совместимости с GitOps для конфига каждое изменение brew или asdf должно порождать отдельный pull request с обновлённой строкой PATH, иначе ревьюер не поймёт, почему в логах ENOENT всплыло через две недели, когда коллега обновил только клиент. На выделенном mini путь /opt/homebrew живёт на NVMe рядом с рабочим каталогом; не предполагайте, что CI на GitHub, где brew стоит в ~/actions-runner, дублирует ту же схему. Документированный PATH — единственный способ согласовать SRE, безопасность и владельца продукта, когда HK / JP / KR / SG / US смотрят в один runbook, но в каждом ЦОДе свои образы с разными минорными версиями Node.
Терминал и launchd: две разные вселенные
Интерактивные оболочки в macOS исполняют /etc/zprofile, ~/.zprofile и ~/.zshrc, часто с eval "$(/opt/homebrew/bin/brew shellenv)" в этих rc-файлах. Агенты launchd по умолчанию наследуют консервативную среду: PATH схлопывается в /usr/bin:/bin:/usr/sbin:/sbin, пока plist явно не расширяет. Шлюзы OpenClaw, поднимающие MCP, видят урезанный набор, где npx и pnpm в виде shims исчезают, хотя which npx в Терминале пишет /opt/homebrew/bin/npx. Сбой не в том, что OpenClaw «потерял MCP» — это ENOENT в execve, потому что ядро не находит бинарник-интерпретатор, указанный в shebang.
Когда в логе виден env: python3: No such file or directory, сначала проверьте, не ссылается ли wrapper на путь, который существует только в профиле пользователя-разработчика, а супервайзор запущен в другом UID. На ProxyMac-инстансах агент обычно крутится в GUI-домене той учётки, под которой подписан ssh, но LaunchAgent всё равно не читает ~/.zshrc — только явные EnvironmentVariables плюс наследуемый минимум. Снимите printenv PATH в однократной сессии ssh mini 'launchctl print gui/$(id -u)/…' и сравните с интерактивной zsh -l, чтобы цифры попали в инцидент, а не в переписку.
- Доказать раскол: сравнить
printenv PATHв однократномsshс выводомlaunchctl print gui/…для вашегоLabel. - Наружный бинарник MCP пока указывайте по абсолютному пути, пока PATH для вспомогательных инструментов не стабилизирован.
- Дрейф версий ведите в Git по образцу версионирования конфига, чтобы
brew upgradeне вращал корень путей незаметно.
Матрица префиксов Homebrew: Apple Silicon и Intel (три колонки, иная логика, чем в статье про Wi-Fi)
Смешивайте Intel x86_64 и arm64 только осознанно: Rosetta вводит в заблуждение при чтении file бинарника, тогда как обёртки npx, собранные на ноутбуке M3, смотрят в arm64, а node в /usr/local остаётся x86. Каждую бинарь в конфиге MCP сопровождайте шестнадцатеричной подписью и выводом file — иначе тикет в ЦОД скажет «у меня всё падает», а ваша локаль так и останется зелёной. Это снимает вопрос, почему мини пингует иначе, чем 14-дюймовый ноутбук, с которого вы копировали JSON.
| Поколение железа | Типовой префикс brew | Типовой симптом при отсутствии в PATH |
|---|---|---|
| Mac mini M4 на Apple Silicon | /opt/homebrew | node не найден, при этом /opt/homebrew/bin/node -v печатает v22.x |
| Mac mini на Intel (наследие) | /usr/local | В JSON всё ещё /opt/homebrew/bin/uvx, перенесённый с портативного Mac |
| Гетерогенный парк | Оба префикса на диске | Агенты берут неверный порядок, если /usr/local/bin стоит раньше /opt/homebrew/bin |
Если в одной Plist-строке PATH перечислены сразу …/usr/local/bin и …/opt/homebrew/bin, согласуйте порядок с политикой пакетного менеджера: brew doctor на целевом хосте — обязателен перед мёрджем. На fleet из mini в разных дата-центрах нельзя полагаться на «дефолт, который везде Apple Silicon» — запасной Intel для CI всё ещё встречается, и grep репозитория на строку /opt/homebrew в шаблонах — полезный предохранитель в CI.
Шаблон EnvironmentVariables в plist, переживающий перезагрузки
К LaunchAgent, которому принадлежит OpenClaw или супервайзор MCP, добавьте словарь EnvironmentVariables. Сначала — shims Homebrew, потом системные пути, затем каталоги вроде ~/.local/bin для uv и uvx. После правки всегда launchctl bootout gui/$(id -u)/… с последующим kickstart; на macOS 14 и новее доверять одному launchctl unload нельзя. Сопрягите сигнал с руководством по мониторингу, чтобы регресс по PATH поймали в течение трёх минут, иначе бизнес-агенты будут снова писать «всё сломалось ночью», когда brew молча перенёс node на другой путь. Любая правка plist в проде сопровождается ссылкой на тикет, откат к предыдущему plist из openclaw-version-upgrades-rollback-launchctl-mac-mini-2026.html остаётся в скоупе релиз-менеджера.
Пример фрагмента (в прод после проверки)
<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>
PATH тянуть исполняемый файл из ~/Downloads после вредоносной распаковки.
Если Library/LaunchAgents в Git хранит несколько plists (отдельно для n8n, отдельно для туннеля), согласуйте порядок загрузки: LimitLoadToSessionType и ThrottleInterval влияют на то, в каком порядке PATH окажется в процессе супервайзера OpenClaw. Мелочь, но она всплывает, когда первый субагент смотрит на ещё пустой PATH до bootout второго.
Пять проверок, прежде чем с нуля переписывать конфиги MCP
- Хэш бинарника:
shasum -a 256 $(which uvx)в Терминале сравнить с путём, который зашьёте в JSON MCP (абсолютный путь, не$(which)в конфиге). - Среда launchd:
launchctl print user/$(id -u)/limitплюс домен агента, убедиться, чтоPATHреально подтянут из plist, а не кэш старой сессии. - Нелогиновая среда:
env -i PATH=/usr/bin:/bin /opt/homebrew/bin/npx --version— выживает ли сам npx при минимальномPATH(тест на корректность shebang и вложенных вызовов). - Корреляция с JSONL:
grep ENOENTв скользящих логах по диагностике JSONL — сопоставить времяbrew upgradeи смену inode бинарника. - Документация отката: снять снапшот LaunchAgent до массовой раскатки на флоты HK / JP / KR / SG / US, согласно роллбэку и canary-задачи на регион, а не big-bang в пике.
Node, uv и раскладка шимов: почему менеджеры версий рвутся под launchd
Разработчики подключают fnm, mise и asdf, чтобы на одном хосте держать несколько рантаймов Node, но эти утилиты почти всегда меняют PATH в ~/.zshrc, который процессы launchd не читают. Если в JSON MCP указано npx @scope/server, сначала сам исполняемый файл npx должен быть доступен по абсолютному пути; только затем подключаются каталоги вроде ~/.npm. Типичный перенос конфигурации с ноутбука на новый ProxyMac mini: в интерактивной оболочке после corepack enable утилита pnpm попадает в PATH, а plist по-прежнему отдаёт shebang #!/usr/bin/env node, который упирается в системную заглушку (на части образов v18) вместо цепочки v22 из lockfile. В логах это не обязательно явное «файл не найден» — чаще всплывает ERR_PNPM_UNSUPPORTED_ENGINE глубоко в повторных попытках OpenClaw.
С uv та же схема: uvx после установки через curl-скрипт обычно оказывается в ~/.local/bin или ~/.cargo/bin — в Терминале путь есть, в урезанном PATH у launchd часто нет. Вместо бесконечного наращивания plist разумнее положить компактный обёрточный сценарий в /usr/local/bin/mcp-env.sh (владелец root:wheel, без каталогов с общей записью в начале PATH), один раз экспортировать окружение и exec настоящую точку входа MCP. Аудиторам проще смотреть один согласованный файл, чем два десятка разрозненных правок. Держите скрипт в Git наравне с фрагментами из перезапуска шлюза и восстановления через launchctl, чтобы сравнивать соседние выкладки.
| Среда выполнения | Типичное место установки (интерактив) | Что видит launchd без правок |
|---|---|---|
| Node через Homebrew | /opt/homebrew/bin/node | Остаётся разбор через /usr/bin/env; без шима Homebrew → ENOENT |
| Цепочка uv | ~/.local/bin/uvx | Тильда в строке plist сама не раскрывается |
| pnpm через Corepack | шим рядом с node | Согласованно только если каталог node в PATH стоит раньше /usr/bin |
/opt/homebrew/bin/node -e "console.log(process.version)" и передаёт строку стандартного вывода в ваш канал JSONL; после brew upgrade номер версии изменится раньше, чем это заметят пользователи MCP.
Когда ssh на мини вручную показывает рабочий uvx, а OpenClaw — нет, убедитесь, что ssh стартует интерактивную оболочку: неисполняемая команда ssh mini env ближе к тому, что увидит агент, чем ssh -t mini bash -l. Это избавит от сценария «команда работает, когда я набираю её руками».
Вопросы и ответы
Обернуть всё в /bin/zsh -lc? Сработает, но скроет настоящие ошибки и ухудшит обработку сигналов. Пока нет критичной зависимости от функций login-shell, оставьте прямой exec и явный PATH.
Рушит ли Rosetta brew MCP? Только если бинарь x86_64, а нативный агент ждёт arm64 — сопоставьте file $(which node) с архитектурой, под которой собрали OpenClaw и его зависимости.
Где ещё бывают залипания stdio? PATH чинит запуск, буферизация — каналы. Если процесс стартует, но дальше тишина, читайте статью про stdio и сравнение построчной буферизации с launchd и npx-обёртками, которые пишут по строкам, пока stdout уже закрыт с другой стороны трубы.
Почему на ProxyMac Mac mini удобно «законсервировать» контракт по PATH
Выделенный Mac mini M4 на арендной стойке — это железо на одного арендатора: валидированный /opt/homebrew остаётся на NVMe рядом с рабочим пространством OpenClaw, и никакой сосед по Jenkins не сделает brew uninstall критичного рантайма. Нативные бинарники arm64 уменьшают сюрпризы Rosetta, unified memory снимает борьбу за NUMA у параллельных воркеров MCP, пять регионов дают репликацию рядом с требованием к данным, при этом git-traced plist остаётся идентичным. Когда PATH снова «скучный» и в логах нет ENOENT неделями, масштабируйте конкурентность по гайду по параллельным агентам, бронируйте ёмкость на странице тарифов и ведите людей в центр справки, если GUI-диалоги невозможны без VNC к сессии на мини. Это завершает петлю: PATH зафиксировали, наблюдаемость есть, сценарий доступа согласован с безопасностью.
Долгий хвост инцидентов по ENOENT исчезает, если коммит plists относите к тому же репозиторию, что и JSON определения MCP, а ревьюер смотрит на оба дифа единовременно. Внутренний аудит ProxyMac в духе help и внешние поставщики реже задают вопрос «где ваша схема путей», если таблица соответствия хост ↔ префикс brew публикуется рядом с openclaw-mcp-tool-servers-mac-mini-setup-2026.html. Сохраняйте холодный стенд: одна копия mini с образом до обновлений, чтобы воссоздать execve без плясок с brew switch в пике.
Зафиксировать PATH один раз — везде запускайте OpenClaw
HK / JP / KR / SG / US · Apple Silicon M4