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

161 lines
7.7 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.
# Секция «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` не видны до перезапуска. Порядок:
```bash
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` первый):
```yaml
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` работает в любом варианте.
---
## Активация (одна команда)
```bash
rm -rf ~/Developer/eagle-dash/__pycache__ && \
launchctl kickstart -k gui/$(id -u)/com.eagle.dashboard
```
Затем:
```bash
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` — архитектура дашборда, вкладки, кеш байткода