# NAOMI Cloud

Агентская обёртка над GLM с монетизацией по запросам: клиенты получают агента, который
работает с их ПК (команды, файлы, поиск — как в код-агентах), а все запросы к модели
идут через ваш биллинг-шлюз по предоплаченным ключам.

```
 ПК клиента                                    Ваш сервер
┌─────────────────────────┐          ┌──────────────────────────┐
│  naomi ui (веб-чат/CLI) │          │  gateway (Fastify)       │
│  ├ агентский цикл       │  HTTPS   │  ├ ключи naomi_…, баланс │
│  ├ tools: bash, файлы,  │ ───────► │  ├ тарифы за токены      │
│  │  glob, grep          │          │  ├ плагины (ваши фишки)  │
│  └ approvals (danger ❓)│          │  └──► GLM API (ваш ключ) │
└─────────────────────────┘          └──────────────────────────┘
```

- **`packages/core`** — агентский цикл: LLM ↔ инструменты, подтверждения опасных действий, стриминг. Без зависимостей.
- **`packages/gateway`** — биллинг-шлюз: OpenAI-совместимый `/v1/chat/completions`, ключи, списания, rate limit, админ-API, автозагрузка плагинов.
- **`packages/app`** — клиент: `naomi ui` (веб-интерфейс на localhost), `naomi chat` (терминал), `naomi admin` (управление ключами).

## Быстрый старт

Требуется Node.js ≥ 20.

### 1. Шлюз (на вашем сервере)

```bash
docker compose up -d        # PostgreSQL 16 в контейнере (или впишите свой в DATABASE_URL)
cd packages/gateway
cp .env.example .env        # заполните DATABASE_URL и GLM_API_KEY
npm install
npm start                   # слушает :8080
```

PostgreSQL — основная БД (пул соединений, транзакционные списания). Если
`DATABASE_URL` пуст, шлюз работает на SQLite — удобно для локальных экспериментов.
Свой существующий Postgres: создайте базу `CREATE DATABASE naomi;` и укажите
`DATABASE_URL=postgresql://user:pass@host:5432/naomi`.

При первом старте сгенерируется админ-ключ — он печатается в консоль и сохраняется в
`data/admin.key`. Для China API и международного z.ai просто укажите свой `GLM_BASE_URL`
и список `MODELS`.

### 2. Выдать клиенту ключ

```bash
npm run admin -- create-key 500 "Иван, оплачен до 01.09"
# → key: naomi_ab12... (баланс 500.00)
```

Клиенту отправляете одну строку — **ссылку активации**:

```
https://api.ваш-домен.ru/#key=naomi_ab12...
```

Пополнение: `npm run admin -- topup <ключ> 300`.

**Автовыдача пробных ключей** (клиенты приходят сами): включите в `.env` шлюза
`REGISTRATION_ENABLED=true`, задайте `REG_TRIAL_CREDITS=10` (стартовый баланс) и
`REG_PER_IP_PER_DAY=3` (анти-абуз). В модалке входа клиента появится «Нет ключа?
Получить пробный доступ» — ключ создаётся и активируется автоматически. Такие ключи
видны в админке с меткой `регистрация: Имя`.

**Постоянные аккаунты** (ключ не потеряется): во вкладке «Аккаунт» клиент
регистрируется по email+паролю — ключ привязывается к аккаунту. Потерял ключ /
переустановил систему → просто входит снова, ключ восстанавливается. В профиле —
кнопка «Перевыпустить ключ»: старый ключ удаляется, баланс переносится на новый
(удобно при утечке ключа). Эндпоинты: `POST /v1/account/{register,login,rotate}`.

**Вход через Google**: создайте OAuth-клиент в [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
(Web application, redirect URI `https://api.ваш-домен.ru/auth/google/callback`), впишите
`GOOGLE_CLIENT_ID` и `GOOGLE_CLIENT_SECRET` в `.env` — в модалке входа появится
кнопка «Войти через Google», ключ привяжется к Google-аккаунту.

### 3. Клиент

```bash
npm install                 # в корне репо
npm run app                 # откроет http://127.0.0.1:8420
```

При первом запуске — экран активации: клиент вставляет ключ `naomi_…` или целую
ссылку активации (адрес сервера возьмётся из неё сам). Никаких настроек: сервер
подставляется автоматически, модель выбирается из доступных на шлюзе. Сменить ключ —
кнопка в сайдбаре. Терминальный режим: `npm run chat`.

Адрес сервера по умолчанию задаётся в `packages/app/src/config.js`
(`DEFAULT_SERVER` или env `NAOMI_SERVER_URL`) — пропишите туда свой домен при деплое.

## Монетизация

- Баланс ключа хранится в **кредитах** (1000 милли = 1.00). 1 кредит = ваша валюта.
- Списание за каждый запрос: `BILL_PER_REQUEST + токены/1M × (BILL_PER_1M_INPUT | BILL_PER_1M_OUTPUT)`.
- При нуле — HTTP 402, ключ мгновенно перестаёт работать. Блокировка: `npm run admin -- block <ключ>`.
- Статистика заработка: `npm run admin -- usage`.
- По умолчанию тариф 15/1M вход + 45/1M выход — поставьте свою маржу над закупочной ценой GLM.

### Пополнение (уже встроено)

Клиент сам пополняет баланс: профиль → «Пополнить баланс» → пакет (100/500/1000 ₡,
настраиваются `TOPUP_PACKS`/`PRICE_RATE`) → два режима:

1. **Ручной (работает сразу)**: клиенту показываются реквизиты из `PAYMENT_INFO`,
   он жмёт «Я оплатил» → заявка появляется у вас:
   ```bash
   npm run admin -- topups                # новые заявки
   npm run admin -- topup-approve 5       # подтвердить → баланс клиента пополнится сам
   ```
   Интерфейс клиента узнаёт об оплате автоматически (поллинг).
2. **ЮKassa (автополная)**: после открытия ИП впишите `YOOKASSA_SHOP_ID` и
   `YOOKASSA_SECRET_KEY` в `.env`, пропишите webhook
   `https://api.ваш-домен.ru/payments/yookassa/webhook` в личном кабинете ЮKassa —
   у клиентов появится кнопка «Оплатить картой», деньги будут зачисляться без вас.

⚠️ Перепродажа доступа к API регулируется условиями провайдера — проверьте ToS
Zhipu/z.ai для вашего аккаунта.

## Безопасность

- Агент исполняет команды **на ПК клиента** — под его ответственностью, не на вашем сервере.
- Опасные действия проходят через подтверждение: режимы `safe` (по умолчанию:
  read-only и белый список команд автоматически, остальное — вопрос), `ask` (спрашивать
  всё), `auto` (никогда не спрашивать).
- Чёрный список паттернов: `sudo`, `rm -rf /`, `dd if=`, пайпы в `sh` и т.д. —
  настраивается в `packages/core/src/tools.js`.
- Шлюз: rate limit на ключ, блокировка, лог списаний в SQLite.
- Веб-интерфейс слушает только 127.0.0.1.

## Куда вставлять свои фишки

1. **Плагины шлюза** — `packages/gateway/plugins/*.js`, подгружаются автоматически:
   ```js
   export default {
     name: 'my-feature',
     onRequest({ key, body }) {
       // модифицируйте body (подмешать промпт, ограничить модель…)
       // throw { statusCode: 403, message: 'запрещено' } чтобы отклонить
     },
     onResponse({ key, usage, costMilli }) {
       // вебхук, аналитика, уведомления
     },
   };
   ```
   Пример лежит в `plugins/example-branding.js`.

2. **Свои инструменты агента** — передайте в `buildTools({ customTools: [...] })`:
   ```js
   {
     name: 'my_tool',
     meta: { readOnly: true },           // readOnly = без подтверждения
     definition: { type: 'function', function: { name: 'my_tool', description: '…', parameters: {…} } },
     async run(args) { return { ok: true }; },
   }
   ```

3. **Хуки агента** — `Agent` это EventEmitter: `llm_start`, `delta`, `llm_done`,
   `tool_start`, `tool_end`, `approval`, `done`, `error` — удобно для логов,
   цензуры команд, телеметрии.

4. **Тарифы и лимиты** — переменные `.env` шлюза, можно менять без кода.

## Что делать дальше (дорожная карта)

1. **Реальный GLМ вместо мока**: зарегистрироваться на [z.ai](https://z.ai/model-api) или
   [bigmodel.cn](https://open.bigmodel.cn), вписать ключ в `.env` шлюза, перезапустить.
2. **Деплой шлюза**: VPS + домен + HTTPS (caddy в две строки), `docker compose up -d`
   для Postgres, `NAOMI_SERVER_URL=https://api.ваш-домен.ru`.
3. **Упростить установку клиента**: опубликовать пакет в npm (`npx naomi-cloude ui` —
   установка в одну команду) или собрать Electron-инсталлер (.exe/.dmg) для тех, кто
   не дружит с терминалом.
4. **Автоприём денег**: Telegram-бот (Stars/переводы) → после оплаты сам вызывает
   `POST /admin/keys` — ключ уходит клиенту без вашего участия.
5. **Веб-версия** для массового рынка: тот же шлюз + фронтенд с агентом в
   Docker-песочнице (см. «Веб или приложение?»).

## Масштабирование (цель ~500k клиентов)

Стек уже продовый: **fastify + PostgreSQL** (пул `pg`, списания в транзакции).
Дальше по мере роста:

1. **Redis** для rate limit — интерфейс в `ratelimit.js`, заменить in-memory окна
   (сейчас лимит считается на инстансе — при нескольких репликах он умножается).
2. **Горизонтальное масштабирование** — шлюз stateless, ставьте несколько инстансов
   за балансировщик (nginx/traefik), включите sticky для SSE.
3. **Настройки Postgres под нагрузку**: `max_connections` шлюза = 20 на инстанс,
   pgbouncer при сотнях реплик.
4. **Очередь списаний** при высоком RPS — списывать асинхронно из очереди, а не в запросе.
5. Метрики/трейсинг: fastify имеет готовые плагины (prometheus, opentelemetry).

## Веб или приложение?

Оба варианта живы в этой архитектуре, у каждого своя роль:

| | Веб-чат | Приложение (текущий `naomi ui`) |
|---|---|---|
| Порог входа | ссылка в браузере, ноль установки | поставить Node-пакет |
| Где работает агент | на вашем сервере (нужна Docker-песочница!) | на ПК клиента |
| Доступ к файлам юзера | нет (или загрузка файлов) | полный: файлы, команды, проекты |
| Риски безопасности | чужой код у вас на сервере — изоляция обязательна | риск на стороне клиента |
| Для кого | массовый «чат с ИИ» | «сделай мне задачу на моём компе» |

Рекомендация: **сначала приложение** (уже готово, безопасно для вас — вы не исполняете
чужой код), а для массового рынка добавить веб-чат как отдельный фронтенд к тому же
шлюзу — там агент работает в изолированных Docker-контейнерах с ограниченными
инструментами (генерация документов, кода, картинок), без доступа к хосту.
Шлюз и биллинг общие: одни ключи, одни тарифы, оба продукта монетизируются одинаково.

## Бесплатные GLM-ключи для теста

Сами ключи бесплатно выдаются после регистрации (нужно подтвердить телефон —
за вас это сделать нельзя):

- **z.ai** (международная) — [api.z.ai](https://z.ai/model-api): Flash-модели
  (`glm-4.5-flash`, `glm-4.7-flash`) **бесплатны без лимита токенов**.
- **bigmodel.cn** (китайская) — Flash-модели тоже бесплатны, новичкам дают
  промо-токены на платные модели.

Схема бесплатного старта: в `.env` шлюза `MODELS=glm-4.5-flash,...` и ваш ключ —
клиенты тестируют на бесплатной модели, платные списания включаете, когда
переводите их на `glm-4.6`/`glm-4.5`.

## Тесты

```bash
npm run smoke                                        # на SQLite
SMOKE_DATABASE_URL=postgresql://… npm run smoke      # на PostgreSQL (таблицы чистятся)
```

14 проверок на мок-GLM: биллинг, стриминг, tool calls, подтверждения, админ-API —
реальный ключ GLM не нужен.

## Команды

| Команда | Что делает |
|---|---|
| `npm run gateway` | запустить шлюз |
| `npm run app` | веб-интерфейс клиента |
| `npm run chat` | терминальный чат |
| `npm run admin -- keys` | список ключей |
| `npm run admin -- create-key 500 "метка"` | выдать ключ |
| `npm run admin -- topup <ключ> 300` | пополнить |
| `npm run admin -- usage` | заработок за 30 дней |
| `npm run admin -- topups` | заявки на пополнение |
| `npm run admin -- topup-approve <id>` | подтвердить оплату |
| `npm run admin -- topup-reject <id>` | отклонить заявку |
| `npm run smoke` | тесты всего стека |
