Files
obsidian-vault/personal/plans/balda-mm-phase4-agents.md
T

245 lines
14 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.
# Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard
> План. **Шаг 1 выполнен** (2026-09-26): образ собран, контейнер `balda-executor` Up (healthy),
> `claude 2.1.283` + `codex 0.157.1` внутри, монтирования проверены в обе стороны.
> ⏳ Осталось в шаге 1: логин CLI (нужен апрув на копирование auth-файлов).
> Часть [[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) |
| 6 | Провайдер исполнителя (открытый п.1) | `claude` — оба CLI стоят в образе, переключение через `EXECUTOR_PROVIDER` в `.env` |
---
## 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 |
📌 **Основной путь авторизации — не `claude setup-token`, а копия auth-файлов** из
`~/.claude`/`~/.codex`. Токен в `.env` — запасной вариант для headless без монтирования.
Выбранный вариант: копируются `~/.claude/.credentials.json` + `~/.claude/.claude.json` и
`~/.codex/auth.json` + `~/.codex/config.toml`. Полные каталоги **не** копируются:
`~/.codex` содержит ~640 МБ sqlite-логов/состояний (`logs_2.sqlite`, `state_5.sqlite`,
`memories_1.sqlite`, `queue_1.sqlite`) — исполнителю не нужны, а вес и шум дают.
Пересборка образа и обновление 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. Шаги
### ✅ Шаг 1 ВЫПОЛНЕН (2026-09-26)
Артефакты на диске (все сверены и рабочие; черновики от 15:36 доведены до состояния «собрано и запущено»):
| Путь | Что | Состояние |
|---|---|---|
| `~/agents` | рабочая область (`admin:staff`) | ✅ монтируется в `/agents`, запись в обе стороны проверена |
| `~/cli-auth/{claude,codex}` | auth CLI, 0700, **вне** `~/agents` | ✅ монтируются в `/root/.claude`, `/root/.codex` |
| `~/Docker/balda-executor/Dockerfile` | `node:24-bookworm` + claude + codex + git + `tini` | ✅ исправлен (см. ниже) |
| `~/Docker/balda-executor/docker-compose.yml` | 3 тома, сеть `executor-net`, `build:` | ✅ исправлен (см. ниже) |
| `~/Docker/balda-executor/agents-mounts.yaml` | манифест внешних проектов (пустой список) | ✅ создан |
| `~/Docker/balda-executor/tools/render-mounts.py` | генератор `compose.override.yaml` | ✅ создан, `--check` работает |
| `~/Docker/balda-executor/compose.override.yaml` | **генерируется**, руками не править | ✅ сгенерирован |
| `~/Docker/balda-executor/.env` | 0600, `EXECUTOR_PROVIDER=claude` | ✅ создан (без секретов) |
**Что уже работает (проверено):**
```
balda-executor Up (healthy) balda-executor:0.1.0
claude → 2.1.283 (Claude Code)
codex → codex-cli 0.157.1
```
`~/agents` ↔ `/agents` пишется в обе стороны; рестарт контейнера состояние сохраняет;
auth-каталоги хоста видны внутри.
#### 🔴 Три исправленных дефекта черновика (не повторять)
| Дефект | Симптом | Фикс |
|---|---|---|
| `env_file: .env` без файла | `docker compose up` падает на старте | `.env` создаётся **первым** шагом, 0600 |
| Нет `compose.override.yaml` | `up` падает на монтировании несуществующего пути | `tools/render-mounts.py` запускается **до** `up` |
| `FROM node:24-bookworm` без явных CA | — | `ca-certificates` + `update-ca-certificates` в Dockerfile (условие работы HTTPS у CLI) |
#### ⚠️ На macOS этой машины — `docker-compose`, НЕ `docker compose`
Docker CLI 29.4.1 + buildx как плагин (`~/.docker/cli-plugins/`), а compose — **отдельный бинарь**
`/opt/homebrew/bin/docker-compose` (v5.1.3) с симлинком в `/opt/homebrew/lib/docker/cli-plugins/`.
`docker compose` → `docker: unknown command: docker compose`; `docker compose config --quiet` →
`unknown flag: --quiet`.
📌 **Все команды в этом проекте — через `docker-compose`**, включая `docker-compose config` (без `--quiet`).
#### Сборка образа (сработавшая команда)
```bash
cd ~/Docker/balda-executor
python3 tools/render-mounts.py --check # 0 записей, ошибок нет
python3 tools/render-mounts.py # → compose.override.yaml
docker-compose config --services # → executor (валидация)
docker buildx build --builder arm64builder --platform linux/arm64 \
--provenance=false --load -t balda-executor:0.1.0 .
docker-compose up -d
docker-compose restart # проверка переживания рестарта
```
⚠️ Билдер — `arm64builder` (colima, `docker-container` driver). Дефолтный билдер
(`default` → `/var/run/docker.sock`) в статусе `error` — не использовать.
⚠️ `deploy.resources.limits.memory: 8G` из черновика **убран**: в не-swarm compose
эта секция молча игнорируется, лимит создавал ложное ощущение защиты.
#### ⏳ Осталось в шаге 1: логин CLI (ждёт апрува)
`~/cli-auth/claude` пуст, в `~/cli-auth/codex` только `tmp/`. Копировать **только auth-файлы**,
не целые каталоги (`~/.codex` ≈ 640 МБ служебных sqlite — агенту не нужны):
| CLI | Хост | Куда |
|---|---|---|
| claude | `~/.claude/.credentials.json`, `~/.claude/.claude.json` | `~/cli-auth/claude/` |
| codex | `~/.codex/auth.json`, `~/.codex/config.toml` | `~/cli-auth/codex/` |
📌 В `~/cli-auth` кладётся **копия** (оригиналы на месте, `~/.claude` не переезжает):
у агента-исполнителя свои токены, изолированные от рабочих. Права `~/cli-auth` = 0700.
**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~~ → **claude** (оба в образе, переключение `EXECUTOR_PROVIDER`)
2. ~~Нужен ли `ask`-апрув в будущем~~ → при `allow_all` не нужен; при потребности начинать с варианта B (апрув в мосте, ~0.5 дня)
3. Hostname для моста (`agents-bridge.qentra.top`?) — ждёт шага 3
4. ⏳ **Апрув на копирование auth-файлов CLI** в `~/cli-auth` — блокирует завершение шага 1
5. Порт моста `8790` — проверено, свободен (`lsof -nP -i :8790`)
## Связанные заметки
- [[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` — архитектура дашборда