Fastify + single-page frontend on shared @nexusai/core. Reads the same SQLite journal and media dir. Features: usage stats (by provider/capability/day), content gallery (image/audio/video with local file serving + path-traversal guard), provider CRUD with encrypted keys (masked in UI), enable/disable, set-default, and real key validation via zero-cost /media/models call. Adds Journal.getById and NordRouterMediaProvider.checkKey to core.
9.5 KiB
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
Установка
cd NexusAI
npm install
npm run build
better-sqlite3собирается нативно. Если сборка прервалась:npm rebuild better-sqlite3.
Для STT нужен uv и ffmpeg. Зависимости whisper uv ставит автоматически при первом запуске.
Подключение к opencode
.opencode/opencode.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 |
траты и история из журнала |
Единый формат вызова
{
"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 (с лимитом размера).
Единый формат ответа
{
"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→ pollingGET /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 → БД:
- Env (приоритет) —
NORDROUTER_API_KEY(и<NAME>_API_KEYдля других провайдеров). Просто для CI и локального запуска. - Зашифрованная БД — управляется из будущей админки. Ключи хранятся в SQLite, зашифрованные AES-256-GCM на мастер-ключе
NEXUS_MASTER_KEY(сам мастер-ключ — только в env, в БД его нет).
Свойства слоя:
- В git/логи/БД сырой ключ не попадает; в UI показывается только маска
sk-…last4. - Бэкап БД не раскрывает ключи (нужен мастер-ключ).
- Ротация
NEXUS_MASTER_KEYинвалидирует зашифрованные ключи — их нужно ввести заново. - Без
NEXUS_MASTER_KEYработают только env-ключи (сервер предупреждает в stderr).
Программное управление (пока до админки) — через @nexusai/core:
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-каталог.
npm run build
NEXUS_MASTER_KEY=$(openssl rand -hex 32) \
NEXUS_MEDIA_DIR=./media-output \
npm run admin
# → http://127.0.0.1:4123
Возможности:
- Потребление — дашборд трат: суммарно, по провайдерам, по возможностям, по дням (из журнала).
- Галерея — просмотр сгенерированного контента (изображения/аудио/видео), фильтр по возможности.
- Провайдеры — добавить/включить/выключить, назначить default, ввести ключ (шифруется), проверить ключ реальным вызовом. Ключи показываются только маской.
Переменные: NEXUS_ADMIN_HOST (default 127.0.0.1), NEXUS_ADMIN_PORT (default 4123), NEXUS_MASTER_KEY (для ключей).
Важно: изменения провайдеров/ключей в админке подхватываются MCP-сервером при рестарте (hot-reload — в планах). По умолчанию админка слушает только localhost.
Расширение: новый провайдер
- Реализовать
MediaProvider/SearchProvider/SttProviderиз@nexusai/core. - Зарегистрировать в
packages/mcp-server/src/context.ts.
Инструменты и схемы менять не нужно — они провайдер-агностичны.
Roadmap
- Фаза 2: веб-админка (отдельный пакет
admin, Hono/Fastify + лёгкий фронт) на общемcore: галерея контента, дашборд трат, управление провайдерами. - Доп. провайдеры: RouterAI, GPTunnel, OpenRouter; облачный STT.