Files
obsidian-vault/personal/tech/eagle-dashboard-skill.md
T

126 lines
7.3 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` — источник истины