[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md
This commit is contained in:
1 parent
269f2b189a
commit
66d4d6266b
1 file changed
+107
-16
@@ -42,8 +42,49 @@ Mac Контейнер-исполните
|
|||||||
src -> ~/Developer/proj-x ← СИМЛИНК → ✗ БИТЫЙ
|
src -> ~/Developer/proj-x ← СИМЛИНК → ✗ БИТЫЙ
|
||||||
```
|
```
|
||||||
|
|
||||||
**Правило:** внутрь `~/agents` — только **реальные каталоги** или **hardlink**.
|
**Правило:** внутрь `~/agents` — только **реальные каталоги**. Симлинки не работают
|
||||||
Симлинки допустимы **только если цель лежит внутри того же смонтированного корня**.
|
на внешние цели, hardlink'ом каталог подключить нельзя (см. §2) → внешние проекты —
|
||||||
|
только **bind-mount**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1a. 🔴 ПРОВЕРЕНО ЖИВЫМ ТЕСТОМ: live `mount --bind` на macOS Docker НЕ работает
|
||||||
|
|
||||||
|
Alex предложил: если bind на ходу возможен — делать и live, и в compose, чтобы контейнер
|
||||||
|
не пересоздавать. **Проверено тремя прогонами — на macOS невозможно.**
|
||||||
|
|
||||||
|
```
|
||||||
|
VM (nsenter -t 1 -m): /tmp/bindtest2/agents/existing/f.txt = "DATA2" ✓ файл есть
|
||||||
|
mount --bind ... → BIND_OK
|
||||||
|
контейнер (без рестарта): cat /agents/existing/f.txt
|
||||||
|
→ No such file or directory ✗ не видит
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверено в трёх вариантах — результат одинаков:
|
||||||
|
1. Каталог создан **после** старта контейнера + live bind → в контейнере пусто.
|
||||||
|
2. Ожидание 5 с (гипотеза «кэш атрибутов virtiofs») → пусто.
|
||||||
|
3. Каталог существовал **до** старта контейнера + bind поверх него → **пусто**.
|
||||||
|
|
||||||
|
**Причина:** на macOS Docker Desktop отдаёт контейнеру файлы не через Linux `mount`,
|
||||||
|
а через **virtiofs** — отдельный демон, который строит маппинг путей **в момент старта
|
||||||
|
контейнера**. `mount`, сделанный внутри VM через `nsenter`, для virtiofs невидим:
|
||||||
|
изнутри VM всё корректно, до контейнера не доходит. Это не кэш — рестарт не поможет.
|
||||||
|
|
||||||
|
📌 **Следствие:** live-bind был бы возможен на Linux-хосте (там Docker нативный).
|
||||||
|
На macOS — **только через `-v` в compose + reproduce контейнера**.
|
||||||
|
`docker compose up -d` с новым `-v` = 2-3 сек, `--privileged` не нужен.
|
||||||
|
|
||||||
|
📌 **Тот же корень, что и с симлинками:** virtiofs-маппинг понимает только пути
|
||||||
|
**мира macOS**, известные ему на старте. Всё, что появляется в VM позже (`mount`,
|
||||||
|
шара, новый каталог) — в контейнер не проецируется.
|
||||||
|
|
||||||
|
### Сетевые шары (SMB/NFS) — тот же вывод
|
||||||
|
|
||||||
|
Смонтировать шару в macOS и дать `-v /Volumes/share:/data` **не сработает** (virtiofs
|
||||||
|
не видит `mount_smbfs`-точку). Единственный рабочий вариант — mount внутрь VM через
|
||||||
|
`nsenter`, но он не переживает рестарт Docker и требует `--privileged`.
|
||||||
|
**Рекомендация: не тянуть шару в контейнер.** Если данные на NAS — `rsync` в `~/agents`
|
||||||
|
по требованию вместо прямого монтирования.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -90,14 +131,43 @@ Mattermost (Alex)
|
|||||||
│ идемпотентность + таймаут + лимит 1-2 параллельных задач
|
│ идемпотентность + таймаут + лимит 1-2 параллельных задач
|
||||||
▼
|
▼
|
||||||
Контейнер-исполнитель (Mac, Docker)
|
Контейнер-исполнитель (Mac, Docker)
|
||||||
│ -v ~/agents:/agents:rw
|
│ -v ~/agents:/agents:rw ← рабочая область, локальный диск
|
||||||
│ -v ~/Developer/<proj>:/agents/<name> ← для внешних проектов (bind, не линк)
|
│ -v ~/Developer/<proj>:/agents/<name> ← внешние проекты (bind, не линк)
|
||||||
|
│ -v ~/cli-auth/claude:/root/.claude:rw ← auth CLI на ХОСТЕ (вне /agents!)
|
||||||
|
│ -v ~/cli-auth/codex:/root/.codex:rw ← auth CLI на ХОСТЕ
|
||||||
│ Node 24 + codex + claude + git
|
│ Node 24 + codex + claude + git
|
||||||
│ CLAUDE_CODE_OAUTH_TOKEN из env
|
│ CLAUDE_CODE_OAUTH_TOKEN из .env
|
||||||
▼
|
▼
|
||||||
возврат: diff + вывод тестов + коммит → Балда → MM
|
возврат: diff + вывод тестов + коммит → Балда → MM
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Данные CLI — на хосте, не в образе
|
||||||
|
|
||||||
|
🔴 Требование Alex: **данные codex/claude живут в хостовой папке**, чтобы не ушли
|
||||||
|
при пересборке образа, обновлении CLI или пересоздании контейнера.
|
||||||
|
|
||||||
|
**Расположение — `~/cli-auth`, ВНЕ `~/agents`** (решение принято 2026-09-26):
|
||||||
|
агент-исполнитель монтирует `/agents` на запись, и если auth лежит внутри, агент
|
||||||
|
получает доступ к своим же токенам. Вынос наружу закрывает это.
|
||||||
|
|
||||||
|
| Данные | Хост-путь | В контейнере | Права |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Claude auth / сессии / конфиг | `~/cli-auth/claude` | `/root/.claude` | 0700 |
|
||||||
|
| Codex auth / конфиг | `~/cli-auth/codex` | `/root/.codex` | 0700 |
|
||||||
|
| OAuth-токен Claude | `~/Docker/balda-executor/.env` | env `CLAUDE_CODE_OAUTH_TOKEN` | 0600 |
|
||||||
|
| Ключ провайдера codex (если нужен) | `~/Docker/balda-executor/.env` | env | 0600 |
|
||||||
|
|
||||||
|
**Почему так:**
|
||||||
|
- пересборка образа `docker build` **не трогает** bind-mounted каталоги → auth переживает
|
||||||
|
всё; новый образ поднимается уже залогиненным;
|
||||||
|
- обновление версии CLI внутри образа не сбрасывает сессию;
|
||||||
|
- откат образа (`--force-recreate` на старый тег) не требует повторного логина;
|
||||||
|
- токен не попадает в слои образа → не утечёт при `docker save`/шаринге;
|
||||||
|
- агент **не видит** свои токены (они вне смонтированного `/agents`).
|
||||||
|
|
||||||
|
⚠️ `~/cli-auth` обязан быть на **том же томе**, что и корень Docker-маппинга, иначе
|
||||||
|
проект упрётся в virtiofs-ограничения (см. §1a).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Секция «Agents» в Eagle Dashboard
|
## 4. Секция «Agents» в Eagle Dashboard
|
||||||
@@ -117,13 +187,14 @@ Mattermost (Alex)
|
|||||||
### Что делает новая секция
|
### Что делает новая секция
|
||||||
|
|
||||||
1. **Список содержимого `~/agents`** — по каждому проекту: имя, тип (реальная папка /
|
1. **Список содержимого `~/agents`** — по каждому проекту: имя, тип (реальная папка /
|
||||||
symlink / hardlink), размер, дата изменения, **статус линка** (валиден / битый).
|
mount / битый symlink), размер, дата изменения, **статус**.
|
||||||
2. **Создать проект** — `mkdir ~/agents/<name>`.
|
2. **Создать проект** — `mkdir ~/agents/<name>`.
|
||||||
3. **Подключить внешний проект** — **bind-mount-запись**: сохраняется в
|
3. **Подключить внешний проект** — запись в `~/Developer/agents-mounts.yaml`
|
||||||
`~/Developer/agents-mounts.yaml`, мост перезапускает контейнер с новым `-v`.
|
(`name` + `host_path`) → `docker compose up -d` (**2-3 сек, без privileged**).
|
||||||
⚠️ **Не symlink** — см. §1.
|
⚠️ **Только bind-mount.** Ни symlink (битый), ни hardlink (каталоги не линкуются).
|
||||||
4. **Hardlink-операции** — `ln -F <file> ~/agents/<proj>/<name>` (только файлы,
|
⚠️ Live `mount --bind` **недоступен на macOS** (см. §1a) — recreate обязателен.
|
||||||
только внутри тома; в UI показывать `st_dev` и блокировать при разных ФС).
|
4. **Hardlink-операции** — `ln -F <file> ~/agents/<proj>/<name>` (только **файлы**,
|
||||||
|
только внутри одного тома; в UI показывать `st_dev` и блокировать при разных ФС).
|
||||||
5. **Удалить из контейнера** — убрать из `agents-mounts.yaml` + `docker compose up -d`
|
5. **Удалить из контейнера** — убрать из `agents-mounts.yaml` + `docker compose up -d`
|
||||||
⚠️ **Файлы на диске не трогать** — только отвязка от контейнера (см. Core Rules).
|
⚠️ **Файлы на диске не трогать** — только отвязка от контейнера (см. Core Rules).
|
||||||
6. **Открыть терминал** — ссылка на существующую вкладку `files`.
|
6. **Открыть терминал** — ссылка на существующую вкладку `files`.
|
||||||
@@ -180,12 +251,22 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры
|
|||||||
|
|
||||||
## 6. План работ (по шагам)
|
## 6. План работ (по шагам)
|
||||||
|
|
||||||
### Шаг 1 — `~/agents` + контейнер-исполнитель (~2 ч)
|
### Шаг 1 — `~/agents` + `~/cli-auth` + контейнер-исполнитель (~2 ч)
|
||||||
1. `mkdir ~/agents` + `.gitkeep`, права `admin:staff`.
|
1. `mkdir ~/agents`, права `admin:staff`; `~/cli-auth/{claude,codex}` (0700).
|
||||||
2. `~/Docker/balda-executor/Dockerfile` — Node 24 + `codex` + `claude` + `git`.
|
2. `~/Docker/balda-executor/Dockerfile` — Node 24 + `codex` + `claude` + `git`.
|
||||||
3. `docker-compose.yml` — `-v ~/agents:/agents:rw`, `CLAUDE_CODE_OAUTH_TOKEN` из `.env`.
|
3. `~/Docker/balda-executor/docker-compose.yml`:
|
||||||
4. Авторизация: `claude setup-token` на Mac → токен в `~/Docker/balda-executor/.env` (0600).
|
```yaml
|
||||||
5. Проверка: `docker exec ... claude --version` + `codex --version`.
|
volumes:
|
||||||
|
- /Users/admin/agents:/agents:rw
|
||||||
|
- /Users/admin/cli-auth/claude:/root/.claude:rw # auth на хосте
|
||||||
|
- /Users/admin/cli-auth/codex:/root/.codex:rw # auth на хосте
|
||||||
|
```
|
||||||
|
+ `agents-mounts.yaml` подключается генератором override-файла
|
||||||
|
(`compose.override.yaml`), чтобы composefile не правился скриптом вручную.
|
||||||
|
4. Авторизация один раз: `claude setup-token` на Mac → токен в
|
||||||
|
`~/Docker/balda-executor/.env` (0600); `codex` — свой flow в `~/cli-auth/codex`.
|
||||||
|
5. Проверка: смонтированные auth видны (`docker exec ... ls /root/.claude`),
|
||||||
|
`claude --version` + `codex --version` работают, повторный `up -d` остаётся залогинен.
|
||||||
|
|
||||||
### Шаг 2 — Мост на Mac (~3-4 ч)
|
### Шаг 2 — Мост на Mac (~3-4 ч)
|
||||||
1. `~/Developer/agents-bridge/` — FastAPI, порт **8790**.
|
1. `~/Developer/agents-bridge/` — FastAPI, порт **8790**.
|
||||||
@@ -220,9 +301,12 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры
|
|||||||
| Риск | Оценка | Митигация |
|
| Риск | Оценка | Митигация |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Симлинк в `~/agents` на внешний путь → битый | **подтверждён** | bind-mount вместо линка; UI помечает битые линки |
|
| Симлинк в `~/agents` на внешний путь → битый | **подтверждён** | bind-mount вместо линка; UI помечает битые линки |
|
||||||
|
| Live `mount --bind` на macOS → контейнер не видит | **подтверждён** | только `-v` + `compose up -d`; live-bind вычеркнут из плана |
|
||||||
|
| Сетевую шару (SMB/NFS) в контейнер протянуть нельзя | **подтверждён** | не тянуть: `rsync` в `~/agents` по требованию |
|
||||||
| Hardlink через границу ФС → `EXDEV` | высокий | проверять `st_dev` перед `ln`; UI блокирует |
|
| Hardlink через границу ФС → `EXDEV` | высокий | проверять `st_dev` перед `ln`; UI блокирует |
|
||||||
| Бан подписочного аккаунта за «unusual traffic» | средний | 1-2 параллельных задачи, официальный бинарь, без прокси токена |
|
| Бан подписочного аккаунта за «unusual traffic» | средний | 1-2 параллельных задачи, официальный бинарь, без прокси токена |
|
||||||
| Полный доступ (`allow_all`) + MCP → агент может снести `~/agents` | **средний** | бэкапы + `git rm`-семантика вместо `rm`; правки только через bind-mount-манифест |
|
| Полный доступ (`allow_all`) + MCP → агент может снести `~/agents` | **средний** | бэкапы + `git rm`-семантика вместо `rm`; правки только через bind-mount-манифест |
|
||||||
|
| Агент читает/пишет свои же auth-данные | средний | `~/cli-auth` **вне** `~/agents` → агент их не видит |
|
||||||
| Мост открыт наружу | высокий | только WireGuard-подсеть + shared secret; не публиковать порт в LAN |
|
| Мост открыт наружу | высокий | только WireGuard-подсеть + shared secret; не публиковать порт в LAN |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -232,11 +316,18 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры
|
|||||||
1. Порт моста — **8790** (взят свободный; проверить `lsof -i :8790` перед стартом).
|
1. Порт моста — **8790** (взят свободный; проверить `lsof -i :8790` перед стартом).
|
||||||
2. Провайдер исполнителя — **claude** (подписка уже залогинена) или **codex**?
|
2. Провайдер исполнителя — **claude** (подписка уже залогинена) или **codex**?
|
||||||
3. Нужен ли `ask`-апрув в будущем, или `allow_all` достаточно постоянно?
|
3. Нужен ли `ask`-апрув в будущем, или `allow_all` достаточно постоянно?
|
||||||
|
4. `~/cli-auth` (auth вне `~/agents`, агент не видит) vs `.cli-auth` внутри — по умолчанию **вне**.
|
||||||
|
|
||||||
## История
|
## История
|
||||||
- **2026-09-26**: создан. Решения Alex: папка `~/agents`, full access, управление
|
- **2026-09-26**: создан. Решения Alex: папка `~/agents`, full access, управление
|
||||||
линками — секция в Eagle Dashboard. Живым тестом доказано, что симлинк в Docker
|
линками — секция в Eagle Dashboard. Живым тестом доказано, что симлинк в Docker
|
||||||
не читается → внешние проекты подключать bind-mount'ом, не линком.
|
не читается → внешние проекты подключать bind-mount'ом, не линком.
|
||||||
|
- **2026-09-26 (продолжение)**: два уточнения доказаны живыми тестами.
|
||||||
|
**Live `mount --bind` на macOS Docker НЕ работает** (virtiofs видит VM-mount'ы только
|
||||||
|
на старте контейнера) → recreate обязателен, live-bind вычеркнут (3 прогона, все
|
||||||
|
отрицательные). **Сетевые шары** — тот же корень, в контейнер не протянуть.
|
||||||
|
Добавлен раздел про **данные CLI на хосте** (`~/cli-auth` вне `~/agents`) по
|
||||||
|
требованию Alex: auth переживает пересборку образа и обновление CLI.
|
||||||
|
|
||||||
## Связанные заметки
|
## Связанные заметки
|
||||||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
||||||
|
|||||||
Reference in new issue
Block a user