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

7.7 KiB
Executable File
Raw Blame History

Секция «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"

Связанное