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

14 KiB
Executable File
Raw Blame History

Фаза 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).

Сборка образа (сработавшая команда)

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)

Связанные заметки