7.3 KiB
eagle-dashboard (скилл) — зеркало знаний
Создан 2026-09-26. Зеркало скилла
eagle-dashboardдля случаев, когда скилл не загружен или нужен текстовый поиск по хранилищу. Источник истины — сам скилл (~/.hermes/hermes-whale/skills/devops/eagle-dashboard/SKILL.md). Дополнять вместе со скиллом, не расходясь.
Что это
Eagle Dashboard — сервис-реестр + health-checks + управление lifecycle на Mac.
Живёт в ~/Developer/eagle-dash/, поднят как launchd-агент, UI на http://localhost:8880.
Карта файлов (проверено 2026-09-26):
| Файл | Строк | Роль |
|---|---|---|
main.py |
889 | FastAPI роуты |
templates/index.html |
1248 | Alpine.js UI + все вкладки |
health.py |
181 | health-чеки |
supervisor.py |
290 | lifecycle процессов |
Вкладки: services · crons · pages · files — роутинг через ?tab=.
Планируется 5-я: agents (управление ~/agents).
Health-типы: http · port · process · launchctl · none.
Регистрация сервиса: services.yaml, поле health.type.
🔴 Критичные правила lifecycle
- НЕ убивать процессы. Ни
kill, ниpkill, ниlsof -ti :8880 | xargs kill. - Рестарт только по команде пользователя:
launchctl kickstart gui/$(id -u)/com.eagle.dashboard. launchctl bootout— запрещено (требует ребута для восстановления).- Hermes gateway (Eagle/Whale) — рестарт только через Dashboard API:
POST /api/services/hermes/restart. Неkill, неlaunchctl. - После рестарта — подождать 5 с, проверить
lsof; если порт пуст — читатьgateway.log, НЕ рестартовать повторно. - Правки кода → снять
__pycache__+launchctl kickstart -k(иначе старый.pyc). - «Стой»/«стоп» от пользователя → ноль действий, даже диагностики.
🔴 Вкладка agents (планируется) — ограничения Docker на macOS
Вкладка управляет ~/agents, который монтируется в контейнер-исполнитель.
Три ограничения доказаны живыми тестами 2026-09-26 (alpine:3.20, linux/arm64):
1. Симлинк в контейнер НЕ проезжает
Симлинк внутри bind-mounted каталога битый в контейнере: цель резолвится относительно корня контейнера, где ничего не смонтировано.
host: -v /tmp/symtest/bind:/mnt с /mnt/target -> /tmp/symtest/outside
контейнер: cat /mnt/target/file.txt
→ No such file or directory
«Подключить внешний проект симлинком» — невозможно.
2. Live mount --bind (nsenter) контейнеру не виден
nsenter -t 1 -m mount --bind внутри VM → BIND_OK, но контейнер видит пусто.
Причина: Docker Desktop на macOS отдаёт файлы через virtiofs, который фиксирует
маппинг путей на старте контейнера. Проверено 3 прогонами (новый каталог, пауза 5 с,
каталог-до-старта) — все отрицательные.
⚠️ Отдельная ловушка: nsenter -t 1 -m заходит в VM, путей мира macOS там нет —
хост-каталоги для VM надо создавать внутри VM.
Следствие: recreate обязателен. docker compose up -d = 2-3 сек, --privileged не нужен.
3. Сетевые шары (SMB/NFS) в контейнер не протянуть
mount_smbfs в macOS → virtiofs его не видит → -v /Volumes/share:/data не работает.
Рекомендация: rsync в ~/agents вместо прямого монтирования.
Матрица выбора
| Способ | Папку? | Через тома? | Видно в контейнере | Итог |
|---|---|---|---|---|
| symlink | да | да | ✗ битый | ❌ |
| hardlink | ✗ нет | ✗ нет | — | ✓ только файлы внутри тома |
live mount --bind |
да | да | ✗ | ❌ на macOS |
-v + recreate |
да | да | ✓ | ✅ рабочий путь |
| rsync-копия | да | да | ✓ | ✓ для шар/NAS |
Hardlink не линкует каталоги и не пересекает ФС (EXDEV). Проверять st_dev.
Правила для вкладки
- Вкладка рулит манифестом монтажей (
~/Developer/agents-mounts.yaml) → генерируетcompose.override.yaml→docker compose up -d. Не создаёт симлинки. - Вкладка никогда не удаляет содержимое
~/agents— только отвязка от контейнера. - Секреты/auth CLI — вне
~/agents(~/cli-auth), отдельным-v, иначе агент с rw на/agentsчитает свои же токены.
Полный разбор: personal/tech/macos-docker-virtiofs-mount-limits. Применение: personal/plans/balda-mm-phase4-agents.
Известные питфоллы (кратко)
| Питфолл | Суть | Фикс |
|---|---|---|
_launchd_toggle() |
launchctl list = 0 даже когда процесс не запущен → кнопка инвертирована |
проверять ключ Disabled в plist |
_msg timeout |
setTimeout держит stale-объект после fetchCrons() |
захватывать source+id в замыкание, искать заново |
_list_launchd_crons() |
таймерные launchd-джобы показывали Resume (pid=None) | "enabled": enabled (plist загружен = включён) |
| EX_CONFIG (78) | launchd-джоб молча не работает | ~ не раскрывается → полный путь; либо скрипт сам отдаёт 78 |
lsof not found |
PATH под launchd = /usr/bin:/bin |
полный путь /usr/sbin/lsof |
__pycache__ |
старый .pyc отдаётся после правки |
снять кэш + kickstart -k |
PID для http/port |
PID берётся через lsof -ti :port |
если пусто — это норма для остановленного сервиса |
Связанные заметки
- personal/tech/macos-docker-virtiofs-mount-limits — virtiofs: что не проезжает в контейнер
- personal/plans/balda-mm-phase4-agents — вкладка
agentsв контексте проекта - скилл
eagle-dashboard— источник истины