14 KiB
Executable File
Фаза 4 — исполнитель на Mac + ~/agents + секция «Agents» в Eagle Dashboard
План. Шаг 1 выполнен (2026-09-26): образ собран, контейнер
balda-executorUp (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).
Сборка образа (сработавшая команда)
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. Открыто
Провайдер исполнителя — claude или codex→ claude (оба в образе, переключениеEXECUTOR_PROVIDER)Нужен ли→ приask-апрув в будущемallow_allне нужен; при потребности начинать с варианта B (апрув в мосте, ~0.5 дня)- Hostname для моста (
agents-bridge.qentra.top?) — ждёт шага 3 - ⏳ Апрув на копирование auth-файлов CLI в
~/cli-auth— блокирует завершение шага 1 - Порт моста
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— архитектура дашборда