386 lines
24 KiB
Markdown
Executable File
386 lines
24 KiB
Markdown
Executable File
# Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard
|
||
|
||
> План + статус. Старт 2026-09-26.
|
||
> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фазы 4-5).
|
||
> Предыдущее: [[personal/projects/balda/balda-kraken-mm-deploy]] (Фаза 3 ✅ — бот отвечает Alex).
|
||
|
||
---
|
||
|
||
## 0. Статус шагов
|
||
|
||
| # | Шаг | Статус |
|
||
|---|---|---|
|
||
| 1 | `~/agents` + `~/cli-auth` + контейнер | ✅ собрано, запущено |
|
||
| 2 | Мост FastAPI :8790 | ✅ работает под launchd, туннель `agents-bridge.qentra.top` заведён Alex'ом |
|
||
| 3 | Сеть Kraken → Mac | ✅ **Cloudflare Tunnel** (постоянно); LAN остаётся для отладки |
|
||
| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена |
|
||
| 5 | Связка Балда → мост | ✅ конфиг переведён на `type: http`, образ 0.2.0, `tools/list` из docker-сети |
|
||
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге Балды |
|
||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||
|
||
---
|
||
|
||
## 1. Решения
|
||
|
||
| # | Вопрос | Решение |
|
||
|---|---|---|
|
||
| 1 | Папка для проектов исполнителя | `~/agents` |
|
||
| 2 | Подтверждения | Full access → `permissions.mode: allow_all` |
|
||
| 3 | Управление линками в `~/agents` | Секция «Agents» в Eagle Dashboard |
|
||
| 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents`; логинит **Alex вручную** |
|
||
| 5 | Сеть Kraken → Mac | 🟢 **Cloudflare Tunnel `agents-bridge.qentra.top`** — постоянный путь (Alex завёл маршрут); LAN `192.168.1.57:8790` — только для отладки |
|
||
| 6 | Провайдер исполнителя | `claude` (дефолт в `.env`); `codex` собирается в том же образе, переключается переменной |
|
||
| 7 | Тип MCP-транспорта в конфиге Балды | `http` (валидатор: `stdio \| http \| sse`); Obsidian — `sse` |
|
||
| 8 | Туннель для Obsidian MCP | ❌ **не нужен** — контейнер стоя́т на Kraken рядом с Балдой, ходят по docker-сети |
|
||
|
||
---
|
||
|
||
## 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
|
||
▼
|
||
MCP-сервер balda-executor-mcp (Kraken, контейнер в mattermost_mm-net) :8890
|
||
│ инструменты executor_run / executor_status / executor_health
|
||
│ HTTP + X-Bridge-Secret
|
||
▼
|
||
LAN: http://192.168.1.57:8790 ← Kraken и Mac в одной сети, туннель не нужен
|
||
▼
|
||
Мост (Mac, FastAPI :8790) — под launchd com.agents.bridge
|
||
│ idempotency key, таймаут 900 с, лимит 1 задача, shared secret
|
||
▼
|
||
Контейнер-исполнитель balda-executor (Mac, Docker)
|
||
│ -v ~/agents:/agents:rw
|
||
│ -v ~/Developer/<proj>:/agents/<name>:rw ← внешние проекты (override)
|
||
│ -v ~/cli-auth/claude:/root/.claude:rw ← auth на хосте
|
||
│ -v ~/cli-auth/codex:/root/.codex:rw ← auth на хосте
|
||
│ Node 24 + claude 2.1.283 + codex 0.157.1 + git
|
||
▼
|
||
diff + тесты + коммит → Балда → Mattermost
|
||
```
|
||
|
||
Детали MCP-сервера: [[personal/tech/balda-executor-mcp]].
|
||
|
||
### Auth CLI на хосте
|
||
|
||
| Данные | Хост | В контейнере | Права |
|
||
|---|---|---|---|
|
||
| Claude auth | `~/cli-auth/claude` | `/root/.claude` | 0700 |
|
||
| Codex auth | `~/cli-auth/codex` | `/root/.codex` | 0700 |
|
||
|
||
Пересборка образа и обновление CLI auth не сбрасывают. Токены вне `/agents` → агент их не видит.
|
||
|
||
---
|
||
|
||
## 4. Что собрано на диске
|
||
|
||
| Путь | Что |
|
||
|---|---|
|
||
| `~/agents/` | рабочая область исполнителя |
|
||
| `~/cli-auth/{claude,codex}` | 0700, монтируются в контейнер |
|
||
| `~/Docker/balda-executor/Dockerfile` | Node 24 + claude + codex + git + tini + CA |
|
||
| `~/Docker/balda-executor/docker-compose.yml` | 4 тома, сеть `executor-net`, build-секция |
|
||
| `~/Docker/balda-executor/.env` | `EXECUTOR_PROVIDER=claude`, 0600 |
|
||
| `~/Docker/balda-executor/agents-mounts.yaml` | манифест внешних проектов |
|
||
| `~/Docker/balda-executor/tools/render-mounts.py` | манифест → `compose.override.yaml` |
|
||
| `~/Developer/agents-bridge/` | мост: `app.py`, `store.py`, `executor.py`, `config.py` |
|
||
| `~/Library/LaunchAgents/com.agents.bridge.plist` | автозапуск + KeepAlive, `--host 0.0.0.0` |
|
||
| `~/Developer/eagle-dash/agents.py` | роутер секции «Agents» |
|
||
| `~/Developer/balda-executor-mcp/` | MCP-сервер: `server.py`, `Dockerfile`, `docker-compose.yml` |
|
||
| `/home/kraken/mcp/docker-compose.yml` (Kraken) | запуск контейнера MCP |
|
||
|
||
**Известные питфоллы (пройдены):**
|
||
- `docker compose` на Mac **отсутствует** — только `docker-compose` (v5.1.3);
|
||
**на Kraken наоборот**: `docker compose`, а `docker-compose` → `command not found`;
|
||
- `compose.override.yaml` обязателен при старте, иначе `up` падает на монтировании;
|
||
- образ исполнителя: `node:24-bookworm` вместо `scratch` (CA-сертификаты — иначе TLS-фейл);
|
||
- `psutil` не установлен в venv дашборда → supervisor не может трекать мост по PID; health для моста — `type: http`;
|
||
- `--host` берётся из `ProgramArguments` plist, **`.env` его не перебивает**;
|
||
смена аргументов требует `bootout`+`bootstrap`, не `kickstart`;
|
||
- образ Балды из `scratch`: **нет shell**, `docker exec … sh` не работает;
|
||
`docker.sock` в контейнер Балды **не смонтирован** → `docker run` изнутри невозможен;
|
||
- на Kraken в sshd `AllowTcpForwarding no` → обратный SSH-туннель поставить нельзя.
|
||
|
||
---
|
||
|
||
## 5. Секция «Agents» в Eagle Dashboard
|
||
|
||
Правки: `main.py` (include_router) · `agents.py` (новый роутер) · `templates/index.html` (вкладка `?tab=agents`) · `services.yaml` (сервис `agents_bridge`).
|
||
|
||
| Функция | Что делает |
|
||
|---|---|
|
||
| Список | содержимое `~/agents`: имя, размер, mtime, статус линка |
|
||
| Создать | `mkdir ~/agents/<name>` |
|
||
| Подключить внешний | запись в `agents-mounts.yaml` → `render-mounts.py` → `up -d` |
|
||
| Hardlink | `ln -F` для файлов, проверка `st_dev` |
|
||
| Отключить | убрать из манифеста + `up -d`; **файлы на диске целы** |
|
||
| Статус моста | `/api/agents/bridge` — provider, container, active, limit, pid, secret |
|
||
| Терминал | ссылка на вкладку `files` |
|
||
|
||
**API:** `GET/POST /api/agents` · `POST|DELETE /api/agents/{name}/mount` ·
|
||
`POST /api/agents/{name}/hardlink` · `GET /api/agents/{name}/usage` ·
|
||
`GET /api/agents/mounts` · `POST /api/agents/mounts/render` · `GET /api/agents/bridge`.
|
||
|
||
**Не делает:** не удаляет файлы, не убивает процессы, не создаёт symlink на внешние цели.
|
||
|
||
### Приёмка секции (2026-09-26) — 13 проверок, все прошли
|
||
|
||
| Проверка | Результат |
|
||
|---|---|
|
||
| создание агента | 201 ✅ |
|
||
| повторное создание | 409 ✅ |
|
||
| подключение внешнего проекта | 200 + контейнер Recreated ✅ |
|
||
| проект виден внутри контейнера | да, `ls /agents/testproj` → файлы ✅ |
|
||
| отвязка | 200; внутри контейнера пусто, на хосте файлы целы ✅ |
|
||
| hardlink на каталог | 400 (ядро запрещает) ✅ |
|
||
| hardlink на файл в том же томе | 200, `links=2`, одинаковый inode ✅ |
|
||
| слишком широкий `host_path` | 400 ✅ |
|
||
| размер каталога | OK ✅ |
|
||
| `?tab=agents` рендерится | 200, 75 КБ ✅ |
|
||
| JS-целостность шаблона | скобки парны, 8 методов на месте ✅ |
|
||
| Alpine в логе дашборда | ошибок нет ✅ |
|
||
|
||
---
|
||
|
||
## 6. Мост `agents-bridge`
|
||
|
||
`~/Developer/agents-bridge/`. Запуск под launchd: `com.agents.bridge`, лог `~/Library/Logs/agents-bridge.err.log`.
|
||
|
||
| Эндпоинт | Назначение |
|
||
|---|---|
|
||
| `GET /healthz` | живость моста + контейнера |
|
||
| `POST /task` | поставить задачу `{prompt, provider?}` |
|
||
| `GET /task/{id}` | статус + stdout/stderr/exit_code |
|
||
| `GET /tasks` | последние 20 задач |
|
||
|
||
Безопасность: заголовок `X-Bridge-Secret` (иначе 401), `Idempotency-Key` (повтор возвращает тот же `task_id`),
|
||
лимит 1 одновременная задача (иначе 429), таймаут 900 с, обрезка вывода 200 КБ.
|
||
|
||
### Приёмка моста (2026-09-26) — 6 проверок, все прошли
|
||
|
||
| Проверка | Результат |
|
||
|---|---|
|
||
| без секрета / неверный секрет | 401 / 401 ✅ |
|
||
| два POST с одним `Idempotency-Key` | одинаковый `task_id` ✅ |
|
||
| задача дошла до CLI в контейнере | да ✅ |
|
||
| ответ CLI вернулся (`Not logged in · Please run /login`) | да — это ответ Claude Code, конвейер рабочий ✅ |
|
||
| состояние в SQLite (WAL) | ✅ |
|
||
| `/healthz` видит контейнер | `status: ok` ✅ |
|
||
|
||
**Блокер приёмки:** CLI внутри контейнера не авторизован. Логинит **Alex вручную**:
|
||
`claude` / `codex` внутри контейнера, credentials кладутся в `~/cli-auth/{claude,codex}`.
|
||
|
||
---
|
||
|
||
## 7. Сеть Kraken → Mac — 🟢 Cloudflare Tunnel (постоянно), LAN — отладка
|
||
|
||
### Итог: постоянный путь — туннель, не IP
|
||
|
||
🔴 **Alex потребовал убрать LAN-IP из постоянного конфига** — домашний IP не является
|
||
стабильным адресом: сменится адрес Mac или уедет роутер, и MCP отвалится молча.
|
||
Туннель даёт стабильное имя и не привязан к топологии сети.
|
||
|
||
```env
|
||
BRIDGE_URL=https://agents-bridge.qentra.top
|
||
```
|
||
|
||
Маршрут `agents-bridge.qentra.top` → `http://localhost:8790` заведён Alex'ом в
|
||
Cloudflare Zero Trust. Проверено из docker-сети Kraken:
|
||
|
||
| Проверка | Результат |
|
||
|---|---|
|
||
| `curl https://agents-bridge.qentra.top/healthz` из `mattermost` | HTTP 200, `{"status":"ok","container":"balda-executor",…}` ✅ |
|
||
| заголовки ответа | `server: cloudflare`, `cf-ray: …-FRU`, `HTTP/2 200` ✅ |
|
||
| `ping` Kraken → `192.168.1.57` (LAN, резерв) | 2/2, RTT 2.4 мс ✅ |
|
||
|
||
⚠️ **На момент записи Access Application на `agents-bridge` не задан** — туннель
|
||
отвечает 200 без авторизации. Пока защита — `X-Bridge-Secret` и
|
||
непредсказуемое имя поддомена. **Завести Access Application** (или подтвердить, что
|
||
она есть), иначе мост фактически открыт в интернет.
|
||
|
||
### Что НЕ сработало и почему (чтобы не повторять)
|
||
|
||
| Путь | Почему отпал |
|
||
|---|---|
|
||
| Обратный SSH-туннель (`ssh -R`) | 🔴 **На Kraken в sshd стоит `AllowTcpForwarding no`.** Симптом: `Error: remote port forwarding failed for listen port 18790`, в verbose — `debug1: Remote: Server has disabled port forwarding.` Не диагностировать как «порт занят»: порт свободен, форвард выключен администратором |
|
||
| LAN как постоянный адрес | Alex против: IP не стабилен. Годится только для отладки |
|
||
|
||
📌 Если понадобится туннель в будущем — сначала проверить `AllowTcpForwarding` на Kraken,
|
||
потом уже ставить форвард. Cloudflare Tunnel этот запрет обходит (исходящее соединение,
|
||
не SSH-форвард).
|
||
|
||
### Мост теперь слушает всю сеть — это осознанное изменение
|
||
|
||
| Было | Стало | Почему |
|
||
|---|---|---|
|
||
| `BRIDGE_HOST=127.0.0.1` | `BRIDGE_HOST=0.0.0.0` | иначе Kraken не достучится |
|
||
|
||
⚠️ **Порт 8790 теперь открыт в локальной сети.** Защита — `X-Bridge-Secret` (обязателен,
|
||
иначе `POST /task` отдаёт 503). При уходе Mac в недоверенную сеть — вернуть `127.0.0.1`
|
||
и поднимать туннель.
|
||
|
||
🔴 **Питфолл: `.env` не перебивает plist.** Я поменял `BRIDGE_HOST` в `.env`, но мост
|
||
продолжал слушать `127.0.0.1` — потому что `--host` **жёстко задан в `ProgramArguments`**
|
||
plist. Симптом: `lsof` показывает `127.0.0.1:8790` после «перезапуска с новым конфигом».
|
||
Исправлять надо **в plist**, а не в `.env`.
|
||
|
||
```bash
|
||
# применить: правка plist → перезагрузка сервиса (не kickstart!)
|
||
launchctl bootout gui/$(id -u)/com.agents.bridge
|
||
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.agents.bridge.plist
|
||
# проверка: должно быть *:8790, а не 127.0.0.1:8790
|
||
lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||
```
|
||
|
||
`kickstart` перечитывает только программу, но **не** изменённый `ProgramArguments` —
|
||
для смены аргументов нужен `bootout` + `bootstrap`.
|
||
|
||
---
|
||
|
||
## 8. Риски
|
||
|
||
| Риск | Оценка | Митигация |
|
||
|---|---|---|
|
||
| Мост опубликован в интернет | высокий | Access Application + shared secret |
|
||
| Полный доступ агента к `~/agents` | средний | бэкапы; `git rm`-семантика вместо `rm` |
|
||
| Бан подписки за «unusual traffic» | средний | 1 параллельная задача, официальный бинарь, без прокси токена |
|
||
| Hardlink через ФС → `EXDEV` | высокий | проверка `st_dev` в API |
|
||
| Mac спит → туннель и мост недоступны | средний | задачи в durable-очереди Балды, идемпотентность |
|
||
|
||
---
|
||
|
||
## 9. Открыто
|
||
|
||
1. ✅ **Конфиг Балды переведён на HTTP-MCP** — `type: http` →
|
||
`http://balda-executor-mcp:8890/mcp`. Плюс образ на Kraken обновлён до `0.2.0`
|
||
(в `0.1.0` нет `uvicorn` → рестарт-петля). См. [[personal/tech/balda-executor-mcp]] §10e-10f.
|
||
2. ✅ **Obsidian прописан в конфиг Балды** — контейнер `obsidian-mcp` на Kraken поднят,
|
||
`runtime.mcp_servers.obsidian` добавлен. ⚠️ Тип — **`sse`**, не `http`:
|
||
```yaml
|
||
runtime:
|
||
mcp_servers:
|
||
executor-mcp:
|
||
type: http
|
||
url: "http://balda-executor-mcp:8890/mcp"
|
||
obsidian:
|
||
type: sse
|
||
url: "http://obsidian-mcp:3101/sse"
|
||
balda:
|
||
mcp_servers:
|
||
- executor-mcp
|
||
- obsidian
|
||
```
|
||
См. §11 ниже.
|
||
3. 🟡 Проверить, что инструменты видны в сессии Балды — **постом от админа** в
|
||
Mattermost. ⚠️ Пост **от имени самого бота** Балда игнорирует (правильно);
|
||
для теста нужен пост от админского токена (`mattermost mmctl --local token generate admin …`).
|
||
4. 🔴 Логин CLI (Alex вручную) — без него сквозная приёмка невозможна.
|
||
5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель
|
||
отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`.
|
||
6. ⚠️ **`~/Developer/balda-executor-mcp/docker-compose.yml` в репо → `0.2.0`**;
|
||
на Kraken compose лежит в `/home/kraken/balda-executor-mcp/docker-compose.yml`
|
||
(был `/home/kraken/mcp/` — путь изменился, старый compose мёртв).
|
||
|
||
## 9a. 🔴 Obsidian на Kraken — существовал ДО этой сессии
|
||
|
||
Важное исправление ошибочного вывода. Я дважды сказал Alex, что Obsidian MCP
|
||
на Kraken нет / есть только на Mac. **Оба утверждения неверны.**
|
||
|
||
**Почему ошибся:** искал через `docker ps --filter name=obsidian` — контейнера нет.
|
||
Но на Kraken Obsidian MCP запущен **не контейнером**, а как stdio-процесс внутри
|
||
агента Hermes:
|
||
|
||
```
|
||
mcpvault /vault (PID 1168741, UID 10000)
|
||
└─ родитель PID 5039 = hermes gateway (контейнер hermes-kraken)
|
||
```
|
||
|
||
Конфиг — `/opt/data/config.yaml` **внутри** `hermes-kraken`. Волт на Kraken реальный:
|
||
`/home/kraken/obsidian` → `/srv/…-49e8f586…/obsidian` (с `personal/`, `family/`, `.git/`).
|
||
|
||
📌 **Правило поиска: `docker ps` не является доказательством отсутствия сервиса.**
|
||
Сначала `ps aux | grep <name>`, потом порты (`ss -tlnp`), и только потом контейнеры.
|
||
|
||
## 10. Шаг 5 — связка Балда → мост (обновлено 2026-09-26)
|
||
|
||
| Компонент | Где | Статус |
|
||
|---|---|---|
|
||
| MCP-сервер `balda-executor-mcp` | `~/Developer/balda-executor-mcp/` | ✅ код + образ `0.2.0` |
|
||
| Образ на Kraken | `balda-executor-mcp:0.2.0` (0.1.0 оставлен для отката) | ✅ загружен, хеш сверен |
|
||
| Контейнер MCP | Kraken, `/home/kraken/balda-executor-mcp/docker-compose.yml` | ✅ Up, `tools/list` = 3 инструмента |
|
||
| Конфиг Балды | `runtime.mcp_servers.{executor-mcp: http, obsidian: sse}` | ✅ залит |
|
||
| `.env` Балды | `BRIDGE_URL=https://agents-bridge.qentra.top` + `BRIDGE_SECRET` | ✅ туннель, не IP |
|
||
|
||
Бэкапы на Kraken: `config.yaml.bak-mcp-20260926-194313`, `config.yaml.bak-obsidian-20260926-214849`,
|
||
`.env.bak-mcp-*`, `.env.bak-url-*`.
|
||
|
||
Проверено живьём: `/healthz` = `{"status":"ok",…}`, `tools/list` из docker-сети отдаёт
|
||
`executor_run` / `executor_status` / `executor_health`, туннель до моста отвечает 200 через Cloudflare.
|
||
|
||
📌 **Ключевой вывод по архитектуре:** MCP-сервер обязан быть **отдельным контейнером
|
||
с HTTP-транспортом**. Вариант «stdio + `docker run` изнутри Балды» нерабочий:
|
||
в образе Балды нет shell и не смонтирован `docker.sock`.
|
||
|
||
## 11. Obsidian MCP на Kraken — контейнер (сделан в этой сессии)
|
||
|
||
Балде нужен Obsidian **не по туннелю**: контейнер стоит рядом, в той же docker-сети.
|
||
Туннель к Obsidian Alex заводить не надо — это была моя ошибка в предложении.
|
||
|
||
| Что | Значение |
|
||
|---|---|
|
||
| Проект | `~/Developer/obsidian-mcp-kraken/` (Dockerfile) |
|
||
| Образ | `obsidian-mcp-image:0.1.0` (node + `mcp-proxy` + `mcpvault`) |
|
||
| Контейнер на Kraken | `obsidian-mcp`, сеть `mattermost_mm-net` |
|
||
| Внутренний порт | `3101` (`--port 3101 -- mcpvault /vault`), **наружу не публикуется** |
|
||
| Том | `/srv/dev-disk-by-uuid-49e8f586-…/obsidian:/vault:ro` (только чтение) |
|
||
| Endpoint для Балды | `http://obsidian-mcp:3101/sse`, `type: sse` |
|
||
|
||
⚠️ **Порт именно 3101, не 8080.** `mcp-proxy` слушает порт из `--port`; проба на 8080
|
||
даёт `Connection refused` при живом контейнере. Проверять так:
|
||
|
||
```bash
|
||
# из контейнера в mattermost_mm-net (там есть curl)
|
||
docker exec mattermost sh -c 'curl -sS -m 8 -H "Accept: text/event-stream" http://obsidian-mcp:3101/sse'
|
||
# ожидаем: event: endpoint / data: /messages?sessionId=…
|
||
```
|
||
|
||
📌 **`mcpvault` — только stdio.** HTTP-режима у него нет, поэтому и нужен обёртка
|
||
`mcp-proxy` (`ENTRYPOINT mcp-proxy`, `CMD --port 3101 -- mcpvault /vault`).
|
||
Проверено на живом контейнере: `mcp-proxy --port 3101 -- mcpvault /vault`.
|
||
|
||
📌 У `mcp-proxy` **нет флага `--host`** — в отличие от Python-моста. Передавать только
|
||
`--port` + `--`. Лишний `--host` роняет контейнер.
|
||
|
||
## Связанные заметки
|
||
- [[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/balda-executor-mcp]] — MCP-сервер делегирования (шаг 5)
|
||
- [[personal/tech/agents-bridge]] — мост :8790 (шаг 2)
|
||
- [[personal/tech/eagle-dashboard-agents-tab]] — секция «Agents» в дашборде (шаг 4)
|
||
- [[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` — архитектура дашборда
|