182 lines
9.3 KiB
Markdown
182 lines
9.3 KiB
Markdown
# Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard
|
||
|
||
> План. **Работа НЕ начата** (образ не собран, контейнер не запущен).
|
||
> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фазы 4-5).
|
||
> Предыдущее: [[personal/projects/balda/balda-kraken-mm-deploy]] (Фаза 3 ✅ — бот отвечает Alex).
|
||
|
||
---
|
||
|
||
## 1. Решения
|
||
|
||
| # | Вопрос | Решение |
|
||
|---|---|---|
|
||
| 1 | Папка для проектов исполнителя | `~/agents` |
|
||
| 2 | Подтверждения | Full access → `permissions.mode: allow_all` |
|
||
| 3 | Управление линками в `~/agents` | Новая секция «Agents» в Eagle Dashboard |
|
||
| 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents` |
|
||
| 5 | Сеть Kraken → Mac | Cloudflare Tunnel (существующий cloudflared на Mac) |
|
||
|
||
---
|
||
|
||
## 2. Ограничения macOS Docker (проверено тестами)
|
||
|
||
| Ограничение | Итог |
|
||
|---|---|
|
||
| Симлинк на внешний путь | битый в контейнере — цель резолвится от корня контейнера |
|
||
| Live `mount --bind` внутри VM | контейнер не видит (virtiofs строит маппинг на старте) |
|
||
| Сетевая шара SMB/NFS | в контейнер не протянуть (тот же virtiofs) |
|
||
| Hardlink на каталог | физически невозможен (ядро запрещает) |
|
||
| Hardlink через границу ФС | `EXDEV` |
|
||
|
||
**Следствие:** внешние проекты подключаются **только bind-mount'ом через `-v`** +
|
||
`docker compose up -d` (2-3 сек). Hardlink — только для файлов внутри одного тома.
|
||
|
||
Детали механизма: [[personal/tech/macos-docker-virtiofs-mount-limits]].
|
||
|
||
---
|
||
|
||
## 3. Архитектура
|
||
|
||
```
|
||
Mattermost (Alex)
|
||
│ «почини баг X в project-y»
|
||
▼
|
||
Балда-оркестратор (Kraken, DeepSeek, allow_all)
|
||
│ читает Obsidian, формирует ТЗ, /goalkeeper → work → validate
|
||
▼
|
||
Cloudflare Tunnel: agents-bridge.qentra.top → localhost:8790
|
||
│ (существующий cloudflared на Mac, PID + LaunchAgent)
|
||
▼
|
||
Мост (Mac, FastAPI :8790)
|
||
│ idempotency key, таймаут, лимит 1-2 параллельных задач, shared secret
|
||
▼
|
||
Контейнер-исполнитель (Mac, Docker)
|
||
│ -v ~/agents:/agents:rw
|
||
│ -v ~/Developer/<proj>:/agents/<name>:rw ← внешние проекты
|
||
│ -v ~/cli-auth/claude:/root/.claude:rw ← auth на хосте
|
||
│ -v ~/cli-auth/codex:/root/.codex:rw ← auth на хосте
|
||
│ Node 24 + codex + claude + git
|
||
▼
|
||
diff + тесты + коммит → Балда → Mattermost
|
||
```
|
||
|
||
### Auth CLI на хосте
|
||
|
||
| Данные | Хост | В контейнере | Права |
|
||
|---|---|---|---|
|
||
| Claude auth | `~/cli-auth/claude` | `/root/.claude` | 0700 |
|
||
| Codex auth | `~/cli-auth/codex` | `/root/.codex` | 0700 |
|
||
| OAuth-токен Claude | `~/Docker/balda-executor/.env` | env | 0600 |
|
||
|
||
Пересборка образа и обновление CLI auth не сбрасывают. Токены вне `/agents` → агент их не видит.
|
||
|
||
---
|
||
|
||
## 4. Секция «Agents» в Eagle Dashboard
|
||
|
||
Правки: `main.py` (роуты) · `templates/index.html` (вкладка `?tab=agents`) ·
|
||
`services.yaml` (запись `agents-bridge`, `health.type: http`) · `health.py`.
|
||
|
||
| Функция | Что делает |
|
||
|---|---|
|
||
| Список | содержимое `~/agents`: имя, тип, размер, mtime, статус линка |
|
||
| Создать | `mkdir ~/agents/<name>` |
|
||
| Подключить внешний | запись в `agents-mounts.yaml` → `compose up -d` |
|
||
| Hardlink | `ln -F` для файлов, проверка `st_dev` |
|
||
| Отключить | убрать из манифеста + `up -d`; **файлы на диске целы** |
|
||
| Терминал | ссылка на вкладку `files` |
|
||
|
||
**API:**
|
||
```
|
||
GET /api/agents → список
|
||
POST /api/agents {name} → mkdir
|
||
POST /api/agents/{name}/mount {host_path} → bind + recreate
|
||
DELETE /api/agents/{name}/mount → отвязать (файлы целы)
|
||
POST /api/agents/{name}/hardlink {file, name} → ln -F со st_dev
|
||
GET /api/agents/{name}/usage → размер
|
||
```
|
||
|
||
**Не делает:** не удаляет файлы, не убивает процессы, не создаёт symlink на внешние цели.
|
||
|
||
---
|
||
|
||
## 5. Подтверждения
|
||
|
||
Режимы Balda: `allow_all` (выбран) · `ask` · `deny_all`.
|
||
`ask` поддержан **только для Telegram и Slackagent**; для Mattermost фейлится закрыто.
|
||
Сценарий «подтвердить из чата» требует: **A** — дописать `ask` в MM (~1-2 дня, расширение PR #8),
|
||
либо **B** — апрув в мосте (~0.5 дня). При `allow_all` не нужно; начинать с B, когда понадобится.
|
||
|
||
---
|
||
|
||
## 6. Шаги
|
||
|
||
### Артефакты, уже созданные на диске (2026-09-26)
|
||
|
||
Созданы **до** подтверждения плана — статус «черновик», при старте сверить и доделать:
|
||
|
||
| Путь | Что |
|
||
|---|---|
|
||
| `~/agents` | рабочая область (drwxr-xr-x, `admin:staff`) |
|
||
| `~/cli-auth/{claude,codex}` | auth CLI, 0700, **вне** `~/agents` |
|
||
| `~/Docker/balda-executor/Dockerfile` | Node 24 + claude + codex + git, HEALTHCHECK |
|
||
| `~/Docker/balda-executor/docker-compose.yml` | 4 тома, сеть `executor-net`, лимит 8G |
|
||
| `~/Docker/balda-executor/.env` | ⏳ не создан (токен) |
|
||
|
||
`docker-compose.yml` ссылается на `compose.override.yaml`, который генерирует
|
||
`tools/render-mounts.py` из `agents-mounts.yaml` — **override вручную не править**.
|
||
Ни образ не собран, ни контейнер не запущен.
|
||
|
||
**1. `~/agents` + `~/cli-auth` + контейнер (~2 ч)**
|
||
`mkdir ~/agents`, `~/cli-auth/{claude,codex}` 0700 → `~/Docker/balda-executor/Dockerfile`
|
||
(Node 24 + claude + codex + git) → `docker-compose.yml` с 4 томами → `claude setup-token` в `.env` 0600 →
|
||
проверка `claude --version`, `codex --version`, повторный `up -d` остаётся залогинен.
|
||
|
||
**2. Мост (~3-4 ч)**
|
||
`~/Developer/agents-bridge/`, FastAPI :8790, `POST /task` (idempotency), `GET /task/{id}`, `GET /healthz`;
|
||
запуск CLI headless в контейнере; лимит 1-2 задач; shared secret.
|
||
|
||
**3. Сеть (~1 ч)**
|
||
Cloudflare Tunnel: добавить published route `agents-bridge.qentra.top` → HTTP `localhost:8790` +
|
||
Access Application. Токен и процесс cloudflared не трогаются. Проверка: `curl` извне на `/healthz`.
|
||
⚠️ hostname — **одноуровневый** (двухуровневые дают TLS handshake failure).
|
||
|
||
**4. Секция «Agents» (~3-4 ч)**
|
||
6 роутов + вкладка + запись в `services.yaml` → `__pycache__` + `launchctl kickstart -k`.
|
||
|
||
**5. Связка Балда → мост (~2 ч)**
|
||
MCP-инструмент в Балде → `POST /task`; проба `echo ok > /agents/test/probe.txt`.
|
||
|
||
**6. Приёмка (~1 день)**
|
||
Сквозной тест **от аккаунта Alex** в MM: `@balda` → задача → diff в ответе.
|
||
Замер расходов (DeepSeek + подписка), документация отката.
|
||
|
||
---
|
||
|
||
## 7. Риски
|
||
|
||
| Риск | Оценка | Митигация |
|
||
|---|---|---|
|
||
| Мост опубликован в интернет | высокий | Access Application + shared secret |
|
||
| Полный доступ агента к `~/agents` | средний | бэкапы; `git rm`-семантика вместо `rm` |
|
||
| Бан подписки за «unusual traffic» | средний | 1-2 параллельных задачи, официальный бинарь, без прокси токена |
|
||
| Hardlink через ФС → `EXDEV` | высокий | проверка `st_dev` в UI |
|
||
| Mac спит → туннель и мост недоступны | средний | задачи в durable-очереди Балды, идемпотентность |
|
||
|
||
---
|
||
|
||
## 8. Открыто
|
||
|
||
1. Провайдер исполнителя — claude или codex
|
||
2. Нужен ли `ask`-апрув в будущем
|
||
3. Hostname для моста (`agents-bridge.qentra.top`?)
|
||
|
||
## Связанные заметки
|
||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅)
|
||
- [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8)
|
||
- [[personal/tech/macos-docker-virtiofs-mount-limits]] — ограничения macOS Docker
|
||
- [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы, дельта-апплаер
|
||
- [[family/how-to/kraken-portainer-access]] — cloudflared, routes
|
||
- скилл `eagle-dashboard` — архитектура дашборда
|