7.7 KiB
Executable File
Секция «Agents» в Eagle Dashboard — реализация
Создан 2026-09-26. Шаг 4 Фазы 4 personal/plans/balda-mm-phase4-agents. Статус: код написан, НЕ активирован — дашборд работает на старом байткоде. Активация =
__pycache__+launchctl kickstart -k(апрув не получен).
Расположение
| Что | Путь |
|---|---|
| Роутер | ~/Developer/eagle-dash/agents.py (новый файл) |
| Подключение | ~/Developer/eagle-dash/main.py → app.include_router(agents_router) |
| Реестр сервисов | ~/Developer/eagle-dash/services.yaml → запись agents_bridge |
| Бэкап реестра | services.yaml.bak-agents-20260926-185641 |
| Манифест монтирований | ~/Docker/balda-executor/agents-mounts.yaml |
| Генератор override | ~/Docker/balda-executor/tools/render-mounts.py |
API (prefix=/api/agents)
GET /api/agents → список ~/agents + манифест + статус контейнера
POST /api/agents {name} → mkdir ~/agents/<name>
POST /api/agents/{name}/mount {host_path, mode} → запись в манифест + recreate
DELETE /api/agents/{name}/mount → отвязать (файлы на диске целы)
POST /api/agents/{name}/hardlink {file, link_name} → ln -F с проверкой st_dev
GET /api/agents/{name}/usage → du -sk
GET /api/agents/mounts → содержимое манифеста
POST /api/agents/mounts/render → перегенерировать override без recreate
GET /api/agents/bridge → состояние моста :8790 + `launchctl list`
Что секция НЕ делает (осознанно)
| Запрещено | Почему |
|---|---|
Удалять файлы в ~/agents |
правило Alex: только отвязка от контейнера |
| Создавать symlink на внешний путь | в Docker битый (virtiofs) — см. personal/tech/macos-docker-virtiofs-mount-limits |
| Hardlink на каталог | ядро запрещает; вернётся ошибка с объяснением |
| Hardlink через границу ФС | st_dev сравнивается до ln, при различии — 400 с текстом про EXDEV |
Живой mount --bind |
на macOS не виден контейнеру — только docker-compose up -d |
Ключевые решения реализации
Роутер в отдельном файле, не в main.py
main.py — 889 строк; секция «Agents» вынесена в agents.py (~430 строк) как
APIRouter(prefix="/api/agents"). Подключение одной строкой в main.py — минимизирует
правку большого файла и не ломает существующие роуты.
Валидация host_path — белый список запретов
Путь должен быть абсолютным, существующим каталогом, и НЕ одним из:
/, /Users, /Users/admin, /Users/admin/agents. Имя агента — ^[a-z0-9][a-z0-9._-]{0,62}$.
Это защита от монтирования ~ целиком в контейнер.
Откат манифеста при провале
mount/unmount пишут манифест → вызывают render-mounts.py → docker-compose up -d.
Если любой шаг падает, манифест возвращается к прежнему состоянию (_write_manifest(mounts)),
чтобы файл не расходился с реально смонтированным.
Запись манифеста — атомарная
tmp → replace(). Заголовок с правилами перезаписывается константой MANIFEST_HEADER,
чтобы пояснения не терялись при правках из UI.
du -sk вместо обхода дерева
Список агентов считает размер через rglob (для UI), а /{name}/usage — через
du -sk. Выбор du для usage: не блокирует event loop на больших каталогах дольше
необходимого и не следует по монтированиям вглубь без нужды.
Питфоллы
1. Дашборд кеширует байткод
Правки .py не видны до перезапуска. Порядок:
rm -rf ~/Developer/eagle-dash/__pycache__
launchctl kickstart -k gui/$(id -u)/com.eagle.dashboard
kickstart без -k переиспользует процесс и новый код не подхватывает.
2. docker compose vs docker-compose
На этой машине compose — отдельный бинарь /opt/homebrew/bin/docker-compose (v5.1.3).
docker compose → docker: unknown command. В agents.py все вызовы через docker-compose.
3. launchd не читает shell PATH
Дашборд под launchd имеет PATH=/usr/bin:/bin. Поэтому:
lsofвызывается полным путём/usr/sbin/lsof;agents.pyизPATHдёргаетdocker,docker-compose,du,launchctl— на/usr/binи/binиз них попадают толькоduиlaunchctl;docker/docker-composeиз/opt/homebrew/binпод launchd не найдутся.
⚠️ Не проверено живьём (дашборд не перезапущен). Если mount/unmount будут возвращать
500 с текстом «команда не найдена» — заменить в agents.py литералы на абсолютные пути
/opt/homebrew/bin/docker-compose и /opt/homebrew/bin/docker.
4. services.yaml — запись agents_bridge
Добавлена в начало секции services: (файл отсортирован по id, agents_bridge первый):
agents_bridge:
access: localhost
category: orchestration
critical: false
health:
type: http
url: http://127.0.0.1:8790/healthz
icon: 🌉
label: com.agents.bridge
log: /Users/admin/Library/Logs/agents-bridge.err.log
name: Agents Bridge
port: 8790
type: daemon
working_dir: /Users/admin/Developer/agents-bridge
⚠️ type: daemon заставит supervisor дашборда взять сервис под управление и
запустить свой процесс — а мост уже управляется собственным launchd (com.agents.bridge).
Возможен конфликт за порт 8790. Ревизия при активации: либо type: system +
health.type: launchctl (label com.agents.bridge), либо type: none.
health.type: http на :8790/healthz работает в любом варианте.
Активация (одна команда)
rm -rf ~/Developer/eagle-dash/__pycache__ && \
launchctl kickstart -k gui/$(id -u)/com.eagle.dashboard
Затем:
curl -s http://127.0.0.1:8880/api/agents | /usr/bin/jq
open "http://127.0.0.1:8880/?tab=agents"
Связанное
- personal/plans/balda-mm-phase4-agents — план Фазы 4
- personal/tech/agents-bridge — мост :8790
- personal/tech/macos-docker-virtiofs-mount-limits — ограничения macOS Docker
- скилл
eagle-dashboard— архитектура дашборда, вкладки, кеш байткода