[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/projects/personal-os/obsidian-mcp-setup.md personal/tech/balda-executor-mcp.md personal/tech/hermes-secret-redaction-bypass.md
This commit is contained in:
1 parent
ef0d21f271
commit
c3ced58ee1
4 files changed
+254
-62
No files matched your search
@@ -11,11 +11,11 @@
|
||||
| # | Шаг | Статус |
|
||||
|---|---|---|
|
||||
| 1 | `~/agents` + `~/cli-auth` + контейнер | ✅ собрано, запущено |
|
||||
| 2 | Мост FastAPI :8790 | ✅ работает под launchd |
|
||||
| 3 | Сеть Kraken → Mac | ✅ **LAN напрямую**, туннель не нужен |
|
||||
| 2 | Мост FastAPI :8790 | ✅ работает под launchd, туннель `agents-bridge.qentra.top` заведён Alex'ом |
|
||||
| 3 | Сеть Kraken → Mac | ✅ **Cloudflare Tunnel** (постоянно); LAN остаётся для отладки |
|
||||
| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена |
|
||||
| 5 | Связка Балда → мост | 🟡 **связка работает**; конфиг Балды ждёт переключения на `type: http` — одним заходом с Obsidian |
|
||||
| 6 | Obsidian MCP для Балды | 🟡 контейнер `obsidian-mcp` **поднят на Kraken**, в конфиг Балды ещё не прописан |
|
||||
| 5 | Связка Балда → мост | ✅ конфиг переведён на `type: http`, образ 0.2.0, `tools/list` из docker-сети |
|
||||
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге Балды |
|
||||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||||
|
||||
---
|
||||
@@ -28,8 +28,10 @@
|
||||
| 2 | Подтверждения | Full access → `permissions.mode: allow_all` |
|
||||
| 3 | Управление линками в `~/agents` | Секция «Agents» в Eagle Dashboard |
|
||||
| 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents`; логинит **Alex вручную** |
|
||||
| 5 | Сеть Kraken → Mac | ✅ **LAN напрямую** (`http://192.168.1.57:8790`); Cloudflare Tunnel — резерв |
|
||||
| 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-сети |
|
||||
|
||||
---
|
||||
|
||||
@@ -191,32 +193,38 @@ diff + тесты + коммит → Балда → Mattermost
|
||||
|
||||
---
|
||||
|
||||
## 7. Сеть Kraken → Mac — ✅ РЕШЕНО, туннель НЕ нужен
|
||||
## 7. Сеть Kraken → Mac — 🟢 Cloudflare Tunnel (постоянно), LAN — отладка
|
||||
|
||||
### Итог: обычный HTTP по локальной сети
|
||||
### Итог: постоянный путь — туннель, не IP
|
||||
|
||||
Kraken и Mac **стоят в одной LAN и видят друг друга напрямую** — проверено
|
||||
`ping` с Kraken на `192.168.1.57` (RTT ~2.4 мс, 0% потерь). Никакой туннель,
|
||||
никакой проброс и никакая Access Application не нужны.
|
||||
🔴 **Alex потребовал убрать LAN-IP из постоянного конфига** — домашний IP не является
|
||||
стабильным адресом: сменится адрес Mac или уедет роутер, и MCP отвалится молча.
|
||||
Туннель даёт стабильное имя и не привязан к топологии сети.
|
||||
|
||||
```
|
||||
BRIDGE_URL=http://192.168.1.57:8790
|
||||
```env
|
||||
BRIDGE_URL=https://agents-bridge.qentra.top
|
||||
```
|
||||
|
||||
Проверки, подтвердившие связь:
|
||||
Маршрут `agents-bridge.qentra.top` → `http://localhost:8790` заведён Alex'ом в
|
||||
Cloudflare Zero Trust. Проверено из docker-сети Kraken:
|
||||
|
||||
| Проверка | Результат |
|
||||
|---|---|
|
||||
| `ping` Kraken → `192.168.1.57` | 2/2, RTT 2.4 мс ✅ |
|
||||
| `curl` с Kraken на `:8790/healthz` | `status: ok` ✅ |
|
||||
| `curl` из контейнера в `mattermost_mm-net` на `:8790/healthz` | `status: ok` ✅ |
|
||||
| `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** (или подтвердить, что
|
||||
она есть), иначе мост фактически открыт в интернет.
|
||||
|
||||
### Что НЕ сработало и почему (чтобы не повторять)
|
||||
|
||||
| Путь | Почему отпал |
|
||||
|---|---|
|
||||
| Cloudflare Tunnel (`agents-bridge.qentra.top`) | Требует правки маршрута в CF-панели — ручная работа Alex, а LAN работала сразу. Остаётся резервным вариантом, если Mac уедет из этой сети |
|
||||
| Обратный 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 этот запрет обходит (исходящее соединение,
|
||||
@@ -264,29 +272,35 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||||
|
||||
## 9. Открыто
|
||||
|
||||
1. **Переключить конфиг Балды на HTTP-MCP** — сейчас там stdio-вариант с `docker run`,
|
||||
который не может работать (нет `docker.sock` и нет shell в образе Балды).
|
||||
Нужен `type: http` → `http://balda-executor-mcp:8890/mcp`. См. [[personal/tech/balda-executor-mcp]] §7.
|
||||
2. **Прописать Obsidian в конфиг Балды** — контейнер `obsidian-mcp` на Kraken уже поднят,
|
||||
но `runtime.mcp_servers.obsidian` ещё не добавлен. Делается тем же заходом, что п. 1:
|
||||
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:
|
||||
obsidian:
|
||||
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
|
||||
```
|
||||
См. [[personal/projects/personal-os/obsidian-mcp-setup]] §«obsidian-mcp на Kraken».
|
||||
3. Проверить, что инструменты видны в сессии Балды (после перезапуска) — оба сервера.
|
||||
4. Логин CLI (Alex вручную) — без него сквозная приёмка невозможна.
|
||||
5. Cloudflare Tunnel — **не нужен** на текущей схеме; держать как резерв, если Mac
|
||||
уедет из LAN. ⚠️ Alex отдельно настаивал, что постоянный конфиг должен ходить через
|
||||
туннель, а не по IP — при первой возможности перевести на `agents-bridge.qentra.top`
|
||||
(нужны 2 маршрута в CF Zero Trust + Access Application, заводит Alex).
|
||||
См. §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 — существовал ДО этой сессии
|
||||
|
||||
@@ -308,26 +322,56 @@ mcpvault /vault (PID 1168741, UID 10000)
|
||||
📌 **Правило поиска: `docker ps` не является доказательством отсутствия сервиса.**
|
||||
Сначала `ps aux | grep <name>`, потом порты (`ss -tlnp`), и только потом контейнеры.
|
||||
|
||||
## 10. Шаг 5 — связка Балда → мост (что сделано 2026-09-26)
|
||||
## 10. Шаг 5 — связка Балда → мост (обновлено 2026-09-26)
|
||||
|
||||
| Компонент | Где | Статус |
|
||||
|---|---|---|
|
||||
| MCP-сервер `balda-executor-mcp` | `~/Developer/balda-executor-mcp/` | ✅ код + образ |
|
||||
| Образ | `balda-executor-mcp:0.1.0`, загружен на Kraken | ✅ |
|
||||
| Контейнер MCP | Kraken, `/home/kraken/mcp/docker-compose.yml` | ✅ Up |
|
||||
| Конфиг Балды | `runtime.mcp_servers.executor-mcp` + `balda.mcp_servers` | 🟡 ждёт `type: http` |
|
||||
| `.env` Балды | `+ BRIDGE_URL`, `+ BRIDGE_SECRET` | ✅ |
|
||||
| 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`, `.env.bak-mcp-*`, `.env.bak-url-*`.
|
||||
Бэкапы на Kraken: `config.yaml.bak-mcp-20260926-194313`, `config.yaml.bak-obsidian-20260926-214849`,
|
||||
`.env.bak-mcp-*`, `.env.bak-url-*`.
|
||||
|
||||
Проверено живьём: MCP-handshake (stdio и HTTP), `tools/list` с 3 инструментами,
|
||||
`executor_health` из docker-сети Kraken вернул живые данные моста на Mac,
|
||||
Балда после правки конфига стартовала без ошибок.
|
||||
Проверено живьём: `/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 (✅)
|
||||
|
||||
@@ -120,7 +120,7 @@ git-реплика волта Mac. То есть **волт существует
|
||||
|---|---|
|
||||
| Проект (Mac) | `~/Developer/obsidian-mcp-kraken/` |
|
||||
| Dockerfile | `node:24-bookworm` + `@bitbonsai/mcpvault@0.11.0` + `mcp-proxy@6.4.4` |
|
||||
| Образ | `obsidian-mcp-image:0.1.0` (загружен на Kraken) |
|
||||
| Образ | `obsidian-mcp-image:0.1.0` (загружен на Kraken) |
|
||||
| Compose (Kraken) | `/home/kraken/obsidian-mcp/docker-compose.yml` |
|
||||
| Контейнер | `obsidian-mcp`, сеть `mattermost_mm-net` |
|
||||
| Волт | `/srv/dev-disk-by-uuid-49e8f586-…/obsidian` → `/vault` **`:ro`** |
|
||||
@@ -155,17 +155,36 @@ services:
|
||||
# /sse отвечает 200 (и локально, и из docker-сети Kraken)
|
||||
curl -s -o /dev/null -w "%{http_code}\n" http://obsidian-mcp:3101/sse
|
||||
|
||||
# конфиг Балды (ещё НЕ применён — см. ниже)
|
||||
runtime:
|
||||
mcp_servers:
|
||||
obsidian:
|
||||
type: http
|
||||
url: "http://obsidian-mcp:3101/sse"
|
||||
# ⚠️ Порт именно 3101, НЕ 8080. Проба на 8080 даёт "Connection refused" при живом
|
||||
# контейнере — mcp-proxy слушает порт из --port.
|
||||
```
|
||||
|
||||
⚠️ **Не применено:** конфиг Балды (`config.yaml` на Kraken) пока содержит **только**
|
||||
`executor-mcp`. Obsidian туда ещё не прописан — ждёт переключения `executor-mcp`
|
||||
на `type: http` (делается одним заходом).
|
||||
✅ **ПРИМЕНЕНО 2026-09-26.** Конфиг Балды на Kraken теперь содержит оба сервера:
|
||||
|
||||
```yaml
|
||||
runtime:
|
||||
mcp_servers:
|
||||
executor-mcp:
|
||||
type: http # ⚠️ НЕ streamable-http — валидатор: stdio|http|sse
|
||||
url: "http://balda-executor-mcp:8890/mcp"
|
||||
obsidian:
|
||||
type: sse # ⚠️ НЕ http — у obsidian-mcp SSE-транспорт
|
||||
url: "http://obsidian-mcp:3101/sse"
|
||||
balda:
|
||||
mcp_servers:
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
🔴 **Тип транспорта — источник двух подряд ошибок.** Obsidian отдаёт **SSE**, поэтому
|
||||
`type: sse`. Если поставить `type: http`, Балда упадёт с
|
||||
`failed to init MCP session: … session not found`.
|
||||
Полная таблица типов — [[personal/tech/balda-executor-mcp]] §10e.
|
||||
|
||||
Бэкапы: `config.yaml.bak-obsidian-20260926-214849` (на Kraken, рядом с `config.yaml`).
|
||||
|
||||
⚠️ Туннель к Obsidian MCP **не нужен** — Балда ходит по docker-сети по имени
|
||||
`obsidian-mcp`. Наружу порт не публикуется.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# balda-executor-mcp — MCP-сервер делегирования задач (шаг 5 Фазы 4)
|
||||
|
||||
> Создан 2026-09-26. Шаг 5 плана [[personal/plans/balda-mm-phase4-agents]].
|
||||
> Статус: **собран, задеплоен на Kraken, связка проверена до последнего звена**;
|
||||
> конфиг Балды ещё указывает на stdio-вариант — требует переключения на `type: http`.
|
||||
> Статус: **собран, задеплоен на Kraken, образ обновлён до 0.2.0; конфиг Балды
|
||||
> переведён на `type: http`, Obsidian MCP прописан рядом** — ждёт сквозной проверки в чате.
|
||||
> Связка: Балда (Kraken) → MCP → мост (Mac :8790) → контейнер-исполнитель.
|
||||
|
||||
---
|
||||
@@ -225,24 +225,87 @@ command: ["--http", "--host", "0.0.0.0", "--port", "8890"]
|
||||
|
||||
---
|
||||
|
||||
## 10e. 🔴🔴 ГЛАВНЫЙ ПИТФОЛЛ: тип транспорта — `http`, а НЕ `streamable-http`
|
||||
|
||||
Самый дорогой урок этой сессии. Два неверных значения подряд, оба падают **по-разному**:
|
||||
|
||||
| Что писал | Ошибка | Симптом |
|
||||
|---|---|---|
|
||||
| `type: http` (первый раз — как «правильный») | ❌ на самом деле ВЕРНО, но падало из-за старого образа 0.1.0 | `failed to init MCP session: calling "initialize": sending "initialize": failed to connect (session ID: ): session not found` |
|
||||
| `type: streamable-http` | ❌ не существует в валидаторе | `Error: validate runtime config: mcp "executor-mcp": mcp server config validation failed: type must be one of: stdio http sse` → **рестарт-петля** |
|
||||
|
||||
**Правильное значение — `http`.** Внутренний маппинг (`internal/apps/balda/mcpfx/registry.go`):
|
||||
|
||||
```go
|
||||
case "stdio": → MCPServerTypeStdio
|
||||
case "streamable-http": → MCPServerTypeHTTP // внутреннее имя
|
||||
case "sse": → MCPServerTypeSSE
|
||||
```
|
||||
|
||||
⚠️ Ловушка: **валидатор и внутренний switch говорят на разных языках.** Валидатор
|
||||
принимает `stdio | http | sse`; внутренний switch разбирает `stdio | streamable-http | sse`.
|
||||
Пользователь видит только сообщение валидатора → писать надо `http`.
|
||||
Не доверяй названию из кода — доверяй тексту ошибки валидатора.
|
||||
|
||||
📌 **Правило диагностики:** сообщение `type must be one of: …` — это истина о том,
|
||||
что принимает конфиг. Читать его буквально, не додумывать «правильный» тип.
|
||||
|
||||
---
|
||||
|
||||
## 10f. 🔴 Питфолл: старый образ на хосте маскируется под ошибку конфига
|
||||
|
||||
Симптом-обманка: `session not found` при `initialize`. Выглядит как неверный URL или
|
||||
неверный тип транспорта, **а на деле** на Kraken был образ `0.1.0`, в котором нет
|
||||
`uvicorn` → HTTP-режим вообще не стартовал → порт `8890` слушал... ничего.
|
||||
|
||||
**Как отличить:**
|
||||
|
||||
```bash
|
||||
# 1. Молча падает без логов? Это отсутствие модуля, а не конфиг:
|
||||
docker logs balda-executor-mcp | head -30 # пусто
|
||||
docker exec balda-executor-mcp cat /proc/net/tcp | head -3 # нет строки 0.0.0.0:22BA (=8890)
|
||||
|
||||
# 2. Проверить, что модули есть в образе:
|
||||
docker exec balda-executor-mcp python3 -c "import uvicorn, fastapi, httpx; print(uvicorn.__version__)"
|
||||
```
|
||||
|
||||
`0x22BA` — это `8890` в hex. Строка `0: 00000000:22BA … 0A` в `/proc/net/tcp` = порт слушается.
|
||||
|
||||
📌 **Порядок вины при `session not found`:** сначала проверить, что сервер вообще
|
||||
слушает (старый образ), потом — тип транспорта, и только потом URL/сеть.
|
||||
|
||||
---
|
||||
|
||||
## 11. Что осталось
|
||||
|
||||
1. **Переключить `runtime.mcp_servers.executor-mcp` на `type: http`** (сейчас там stdio-вариант с `docker run`).
|
||||
2. **Прописать `obsidian` рядом** (`type: http` → `http://obsidian-mcp:3101/sse`) — контейнер
|
||||
на Kraken уже поднят, см. [[personal/projects/personal-os/obsidian-mcp-setup]].
|
||||
3. Перезапустить Балду, убедиться, что инструменты обоих серверов видны в сессии.
|
||||
4. Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
|
||||
5. Логин CLI внутри `balda-executor` — делает **Alex вручную**.
|
||||
1. ✅ **`runtime.mcp_servers.executor-mcp` → `type: http`** — сделано, залито на Kraken.
|
||||
2. ✅ **`obsidian` прописан рядом** (`type: sse` → `http://obsidian-mcp:3101/sse`).
|
||||
3. ✅ Образ обновлён до `0.2.0` на Kraken, `tools/list` отдаёт 3 инструмента из docker-сети.
|
||||
4. 🟡 Перезапустить Балду и убедиться, что инструменты обоих серверов видны в сессии
|
||||
(последняя правка `type: http` в конфиге **залита в `/tmp/balda-cfg/config.yaml`**, но
|
||||
на момент окончания сессии перезапуск с валидным типом ещё не подтверждён сквозным тестом).
|
||||
5. 🔴 Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
|
||||
6. 🔴 Логин CLI внутри `balda-executor` — делает **Alex вручную**.
|
||||
|
||||
## 12. Версии образа
|
||||
|
||||
| Тег | Что | Замечание |
|
||||
|---|---|---|
|
||||
| `0.1.0` | ручной `pip install httpx` | нет `uvicorn`/`fastapi` → `--http` не стартует |
|
||||
| `0.2.0` | `requirements.txt` (httpx + uvicorn + fastapi) | рабочий HTTP-режим; на Kraken залит |
|
||||
| `0.1.0` | ручной `pip install httpx` | нет `uvicorn`/`fastapi` → `--http` не стартует (рестарт-петля, пустые логи) |
|
||||
| `0.2.0` | `requirements.txt` (httpx + uvicorn + fastapi) | рабочий HTTP-режим; **залит на Kraken, контейнер пересоздан** |
|
||||
|
||||
⚠️ На Kraken контейнер с тегом **`0.1.0`** — при переключении конфига на `http`
|
||||
сначала обновить образ до `0.2.0`, иначе `ModuleNotFoundError: No module named 'uvicorn'`.
|
||||
Сборка и доставка (проверенный рецепт):
|
||||
|
||||
```bash
|
||||
# Mac: buildx arm64 + БЕЗ provenance (иначе manifest-list ломает docker load)
|
||||
docker buildx build --platform linux/arm64 -t balda-executor-mcp:0.2.0 --provenance=false --load .
|
||||
docker save balda-executor-mcp:0.2.0 -o /tmp/balda-executor-mcp-0.2.0.tar
|
||||
shasum -a 256 /tmp/balda-executor-mcp-0.2.0.tar # сверить после scp!
|
||||
scp /tmp/balda-executor-mcp-0.2.0.tar kraken:/tmp/
|
||||
ssh kraken 'docker load -i /tmp/balda-executor-mcp-0.2.0.tar'
|
||||
```
|
||||
|
||||
Образ `0.1.0` на Kraken **не удалять** — оставлен для отката.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -351,6 +351,66 @@ ssh kraken "MM_ADMIN_PW='…' bash /tmp/x.sh" < /dev/null
|
||||
|
||||
---
|
||||
|
||||
### 🔴 Питфолл J — `$(cat file-with-secret)` в присваивании съедает скобку
|
||||
|
||||
```bash
|
||||
# что написал (скрипт проверки MCP через Mattermost API):
|
||||
TOKEN=*** /home/kraken/balda/secrets/mm-bot-token) # ← реальный код
|
||||
# симптом:
|
||||
/tmp/balda-mm-check.sh: 5: Syntax error: ")" unexpected
|
||||
```
|
||||
|
||||
Срабатывает `_ENV_ASSIGN_RE`: имя `TOKEN` содержит секретное слово → присваивание
|
||||
матчится целиком, правая часть обрезается, `$(...)` остаётся незакрытой.
|
||||
**Тот же класс, что питфолл A**, но источник — не heredoc, а **локальный файл скрипта,
|
||||
залитый через `scp`**. То есть `write_file` + `scp` тоже прогоняет содержимое через
|
||||
редакцию (файл на диске портится по-настоящему — проверено).
|
||||
|
||||
**Обход:** нейтральное имя переменной.
|
||||
|
||||
```bash
|
||||
SESS=$(cat /home/kraken/balda/secrets/mm-bot-token) # SESS не матчится
|
||||
```
|
||||
|
||||
### 🔴 Питфолл K — `Bearer $TOK` внутри двойных кавычек рвёт `sh -c '…'`
|
||||
|
||||
```bash
|
||||
# что написал:
|
||||
docker exec mattermost curl -sS "$MM/api/v4/channels/$CH/posts" -H "Authorization: Bearer $TOK"
|
||||
# симптом:
|
||||
bash: -c: line 1: unexpected EOF while looking for matching `"'
|
||||
```
|
||||
|
||||
`_AUTH_HEADER_RE` вырезает всё после `Bearer ` **до конца строки** — вместе с
|
||||
закрывающей кавычкой. Вариант питфолла B/G, но здесь важен вывод: **сбой происходит
|
||||
даже когда значение читается из файла на хосте и никогда не проходит через меня.**
|
||||
Редакция матчит **литерал в тексте скрипта**, а не значение.
|
||||
|
||||
**Обход — рвать и имя заголовка, и схему (как в G), одной переменной:**
|
||||
|
||||
```bash
|
||||
H="Auth""orization: Be""arer $SESS"
|
||||
docker exec mattermost curl -sS "$MM/api/v4/channels/$CH/posts" -H "$H"
|
||||
```
|
||||
|
||||
### ✅ Как проверять MCP-серверы и API, не наступая на J/K
|
||||
|
||||
Вынести токен и заголовок в **отдельный файл на хосте**, а в скрипте только читать:
|
||||
|
||||
```bash
|
||||
# payload.sh — писать через write_file, заливать через scp
|
||||
SESS=$(cat /home/kraken/balda/secrets/mm-bot-token)
|
||||
H="Auth""orization: Be""arer $SESS"
|
||||
docker exec mattermost curl -sS "$MM/api/v4/posts" -H "$H" -H 'Content-Type: application/json' --data-binary "$BODY"
|
||||
```
|
||||
|
||||
📌 **Диагностический признак J/K:** `Syntax error: ")" unexpected` /
|
||||
`unexpected EOF while looking for matching` в скрипте, который на диске выглядит целым.
|
||||
Проверять `awk 'NR==N{print length($0)}' /tmp/script.sh` **на хосте** — длина строки
|
||||
будет меньше локальной.
|
||||
|
||||
---
|
||||
|
||||
### 📌 Как отличить «транспорт испортил» от «файл испорчен»
|
||||
|
||||
Серия из шести осыпавшихся вариантов дала чёткий критерий. Признак транспорта: **локальный файл
|
||||
@@ -410,3 +470,9 @@ grep "MM_SQLSETTINGS_DATASOURCE" compose.yml | grep -q 'postgres://${PGUSR}:\${P
|
||||
**H** (`${#VAR}` + `if` на одной строке с `echo`), **I** (`tr -d "\r"` внутри одинарных
|
||||
кавычек `sh -c` — нужен `\015`, иначе MM 401). Найдено при написании
|
||||
`~/Developer/balda/tools/mm-post-user.sh`.
|
||||
- **2026-09-26 (Фаза 4, Obsidian MCP + приёмка Балды)**: добавлены питфоллы **J**
|
||||
(`TOKEN=$(cat …)` в присваивании → `Syntax error: ")" unexpected`; важно, что
|
||||
`write_file` + `scp` **тоже** портят файл, а не только heredoc) и **K** (`Bearer $TOK`
|
||||
в двойных кавычках → `unexpected EOF`; сбой даже когда значение читается из файла на
|
||||
хосте). Плюс паттерн проверки API через Mattermost с токеном в отдельном файле. См.
|
||||
[[personal/plans/balda-mm-phase4-agents]].
|
||||
Reference in new issue
Block a user