# NexusAI — универсальный MCP-сервис для агентов Единый MCP-сервер (TypeScript, stdio), дающий агентам провайдер-абстрагированный доступ к: - **TTS** — озвучка текста - **STT** — распознавание речи (локально, faster-whisper) - **Изображения** — генерация и правка - **Видео** — text→video / image→video - **Музыка** — генерация треков - **Веб-поиск** — ответ с цитатами (search-augmented модели) Первый провайдер — **NordRouter**. Архитектура расширяемая: RouterAI, GPTunnel, OpenRouter и облачные STT добавляются как новые адаптеры без изменения MCP-инструментов. Каждая операция логируется в SQLite (провайдер, модель, стоимость, статус, файл) — основа для будущей веб-админки. ## Структура ``` NexusAI/ ├── packages/ │ ├── core/ # провайдеры, media-storage, journal, ключи, типы, registry │ ├── mcp-server/ # stdio MCP-сервер (10 инструментов) │ └── admin/ # веб-админка (Fastify + лёгкий фронт) ├── sidecar/ # faster-whisper STT (PEP 723, запуск через uv) ├── .env.example └── .opencode/opencode.json ``` ## Установка ```bash cd NexusAI npm install npm run build ``` > `better-sqlite3` собирается нативно. Если сборка прервалась: `npm rebuild better-sqlite3`. Для STT нужен [`uv`](https://docs.astral.sh/uv/) и `ffmpeg`. Зависимости whisper `uv` ставит автоматически при первом запуске. ## Подключение к opencode `.opencode/opencode.json`: ```json { "mcp": { "nexusai": { "type": "local", "command": ["node", "/абс/путь/NexusAI/packages/mcp-server/dist/index.js"], "env": { "NORDROUTER_API_KEY": "sk-...", "NEXUS_MEDIA_DIR": "/абс/путь/NexusAI/media-output", "NEXUS_STT_MODEL": "small" } } } } ``` Переменные окружения — см. `.env.example`. ## Инструменты | Инструмент | Назначение | |---|---| | `nexus_tts` | текст → речь (`audio/elevenlabs-tts`, `audio/gemini-3.1-flash-tts`, …) | | `nexus_image` | генерация/правка изображений (`image/*`) | | `nexus_video` | видео из текста/фото (`video/*`) | | `nexus_music` | музыка (`music/*`) | | `nexus_stt` | речь → текст (локально) | | `nexus_web_search` | веб-поиск с цитатами (`perplexity/sonar*`, `search/*`) | | `nexus_media_models` | список моделей провайдера (id, тип, цена) | | `nexus_media_estimate` | оценка стоимости до запуска | | `nexus_media_upload` | загрузка исходника (фото/аудио/видео) | | `nexus_usage_stats` | траты и история из журнала | ### Единый формат вызова ```json { "provider": "nordrouter", "model": "audio/elevenlabs-tts", "input": { "text": "Привет, мир", "voice": "James · Husky, Engaging and Bold" }, "output": { "save": true, "embed": false } } ``` - `provider` опционален (default из `NEXUS_DEFAULT_PROVIDER`). - `input` пропускает любые модель-специфичные поля (`aspect_ratio`, `resolution`, `duration`, `sound`, …). - `output.save` — сохранять файл локально (по умолчанию да). - `output.embed` — встроить image/audio прямо в ответ MCP (с лимитом размера). ### Единый формат ответа ```json { "provider": "nordrouter", "capability": "tts", "model": "audio/elevenlabs-tts", "status": "done", "file_path": "/.../media-output/audio-...wav", "mime_type": "audio/wav", "result_url": "https://nordrouter.com/media/file/...", "result_url_2": null, "cost_usd": 0.0049 } ``` `result_url_2` заполняется для музыкальных моделей, отдающих два трека (Suno). ## Как это работает - **Media** асинхронно: `POST /media/generate` → polling `GET /media/job/:id` → скачивание `result_url` в `NEXUS_MEDIA_DIR`. Таймаут зависит от типа (картинки — быстро, видео/музыка — до минут). - **Web search** через OpenAI-совместимый `/v1/chat/completions` с sonar/`search/*`; цитаты берутся из `message.annotations[].url_citation`. - **STT** локально: TS запускает `uv run sidecar/stt_server.py`; faster-whisper декодирует аудио (mp3/wav/ogg/m4a/webm) и возвращает `{text, language, segments}`. - **Журнал** SQLite (`nexus.sqlite` в media-dir) пишет каждую операцию; `nexus_usage_stats` агрегирует траты. - MCP работает по stdio — **все логи идут в stderr**. ## Ключи провайдеров Ключи можно задавать двумя способами, с приоритетом **env → БД**: 1. **Env (приоритет)** — `NORDROUTER_API_KEY` (и `_API_KEY` для других провайдеров). Просто для CI и локального запуска. 2. **Зашифрованная БД** — управляется из будущей админки. Ключи хранятся в SQLite, зашифрованные **AES-256-GCM** на мастер-ключе `NEXUS_MASTER_KEY` (сам мастер-ключ — только в env, в БД его нет). Свойства слоя: - В git/логи/БД сырой ключ не попадает; в UI показывается только маска `sk-…last4`. - Бэкап БД не раскрывает ключи (нужен мастер-ключ). - Ротация `NEXUS_MASTER_KEY` инвалидирует зашифрованные ключи — их нужно ввести заново. - Без `NEXUS_MASTER_KEY` работают только env-ключи (сервер предупреждает в stderr). Программное управление (пока до админки) — через `@nexusai/core`: ```ts import { KeyCipher, ProvidersStore } from '@nexusai/core'; const cipher = new KeyCipher(process.env.NEXUS_MASTER_KEY); const store = new ProvidersStore('./media-output/nexus.sqlite', cipher); store.upsert({ name: 'nordrouter', type: 'nordrouter', apiKey: 'sk-...', isDefault: true }); store.setEnabled('nordrouter', true); store.listViews(); // masked view for UI ``` Генерация мастер-ключа: `openssl rand -hex 32`. ## STT в ограниченных сетях faster-whisper скачивает модель с HuggingFace. Если `huggingface.co` недоступен: - задать зеркало: `HF_ENDPOINT=https://hf-mirror.com` - либо предзагрузить модель и указать путь: `NEXUS_STT_MODEL=/abs/model-dir` + `NEXUS_STT_LOCAL_ONLY=1` - либо кэш-директорию: `NEXUS_STT_DOWNLOAD_ROOT=./models` ## Веб-админка Отдельный пакет `@nexusai/admin` (Fastify + одностраничный фронт) на общем `core`. Читает тот же SQLite и media-каталог. ```bash npm run build NEXUS_MASTER_KEY=$(openssl rand -hex 32) \ NEXUS_MEDIA_DIR=./media-output \ npm run admin # → http://127.0.0.1:4123 ``` Возможности: - **Тема** — переключатель светлая/тёмная (сохраняется в localStorage). - **Потребление** — дашборд трат: суммарно, по провайдерам, по возможностям, по дням (из журнала). - **Галерея** — просмотр сгенерированного контента (изображения/аудио/видео), фильтр по возможности, пагинация 50/100/150/200, модальное окно на весь экран, удаление контента (файл + запись). - **Провайдеры** — добавить/включить/выключить, назначить default, ввести ключ (шифруется), **проверить ключ** реальным вызовом, типы `nordrouter` и `openai`. Ключи показываются только маской. Переменные: `NEXUS_ADMIN_HOST` (default `127.0.0.1`), `NEXUS_ADMIN_PORT` (default `4123`), `NEXUS_MASTER_KEY` (для ключей). > Важно: изменения провайдеров/ключей в админке подхватываются MCP-сервером **при рестарте** (hot-reload — в планах). По умолчанию админка слушает только localhost. ## Расширение: новый провайдер 1. Реализовать `MediaProvider` / `SearchProvider` / `SttProvider` из `@nexusai/core`. 2. Зарегистрировать в `packages/mcp-server/src/context.ts`. Инструменты и схемы менять не нужно — они провайдер-агностичны. ## Roadmap - Фаза 2: веб-админка (отдельный пакет `admin`, Hono/Fastify + лёгкий фронт) на общем `core`: галерея контента, дашборд трат, управление провайдерами. - Доп. провайдеры: RouterAI, GPTunnel, OpenRouter; облачный STT.