freegate 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,7 @@
1
+ # Copy to .env and fill in your keys. Only keys marked free:true are required.
2
+ PROVIDER_GROQ_APIKEY=
3
+ PROVIDER_MISTRAL_APIKEY=
4
+ PROVIDER_GEMINI_APIKEY=
5
+ PROVIDER_NIM_APIKEY=
6
+ PROVIDER_ZAI_APIKEY=
7
+ PROVIDER_OPENROUTER_APIKEY=
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 sid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # Freegate
2
+
3
+ [![CI](https://github.com/Artur21101965/davil-cod/actions/workflows/ci.yml/badge.svg)](https://github.com/Artur21101965/davil-cod/actions)
4
+ [![Docker Hub](https://img.shields.io/docker/pulls/nik951751/davil-cod.svg)](https://hub.docker.com/r/nik951751/davil-cod)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![npm](https://img.shields.io/npm/v/davil-cod.svg)](https://www.npmjs.com/package/davil-cod)
7
+ [![GitHub stars](https://img.shields.io/github/stars/Artur21101965/davil-cod?style=social)](https://github.com/Artur21101965/davil-cod)
8
+
9
+ **[Русская версия](README.ru.md) · Russian version**
10
+
11
+ ## Why pay for LLMs when free ones exist?
12
+
13
+ Your AI agent, bot, or script talks to a single OpenAI-compatible endpoint.
14
+ Behind it, Freegate automatically routes requests across **25 free models**
15
+ from Groq, Mistral, Gemini, NVIDIA NIM, OpenRouter, ZAI, Cerebras, DeepSeek
16
+ and local models. If one provider goes down, gets overloaded, or burns its
17
+ daily limit — the request **instantly falls through to the next one**. You
18
+ never see "rate limit", and you never pay.
19
+
20
+ **Result:** full LLM access for everyday work at the price of **$0**.
21
+
22
+ ![Freegate Dashboard](assets/dashboard.png)
23
+
24
+ ## Features
25
+
26
+ | | |
27
+ |---|---|
28
+ | 🔀 **Auto-failover** | 25 providers in one chain. Provider down? The next one answers. |
29
+ | 🤖 **Self-managing models** | Auto-discovers new free models, tests them, adds working ones, disables dead ones — every 6h. |
30
+ | 🏷️ **Model categories** | reasoning / coding / general / vision / local — the right model for the right job. |
31
+ | 🖼️ **Two-stage vision** | Screenshot → vision model reads it → coding model answers the fix. |
32
+ | 💰 **Free** | Free models only. The dashboard shows each provider's remaining limit. |
33
+ | ⚡ **Smart routing** | Picks the fastest, most stable provider for every request. |
34
+ | 🛡️ **Reliability** | Circuit breaker, request queue, auto-disable of dead providers, watchdog. |
35
+ | 📊 **Dashboard** | Status, speed, limits, history, tokens, RPM chart. |
36
+ | 💾 **Disk cache** | Repeat prompts don't consume limits at all. |
37
+ | 🔌 **Compatible** | Any OpenAI client: opencode, Cursor, your scripts. |
38
+
39
+ ## Quick start — 30 seconds
40
+
41
+ ```bash
42
+ npx davil-cod init -i # wizard: password + each provider's key
43
+ npx davil-cod start # proxy on http://localhost:4000
44
+ npx davil-cod test # verify everything works
45
+ ```
46
+
47
+ Dashboard: `http://localhost:4000/?key=your_password`
48
+
49
+ Or via Docker:
50
+
51
+ ```bash
52
+ docker run -d --name davil-cod -p 4000:4000 \
53
+ -e PROVIDER_GROQ_APIKEY=... \
54
+ -e PROVIDER_MISTRAL_APIKEY=... \
55
+ -e AUTH=your-secret-key \
56
+ nik951751/davil-cod
57
+ ```
58
+
59
+ ### Connect any OpenAI client
60
+
61
+ | Field | Value |
62
+ |-------|-------|
63
+ | Base URL | `http://localhost:4000/v1` |
64
+ | API Key | your password from `init` |
65
+ | Model | `tier-s` (fast) / `tier-splus` (powerful) |
66
+
67
+ Example for opencode (`~/.config/opencode/opencode.jsonc`):
68
+
69
+ ```jsonc
70
+ {
71
+ "provider": {
72
+ "free-proxy": {
73
+ "npm": "@ai-sdk/openai-compatible",
74
+ "name": "Freegate",
75
+ "options": {
76
+ "baseURL": "http://localhost:4000/v1",
77
+ "apiKey": "your-secret-key-here"
78
+ },
79
+ "models": {
80
+ "tier-splus": { "name": "Freegate (Best)", "input": ["text"] },
81
+ "tier-s": { "name": "Freegate (Fast)", "input": ["text"] }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Supported providers
89
+
90
+ | Provider | Models | Where to get key | Limit/day |
91
+ |----------|--------|------------------|-----------|
92
+ | Groq | gpt-oss-120b, qwen-27b, allam-2-7b, compound | console.groq.com | 1000 |
93
+ | Mistral | codestral, small | console.mistral.ai | 500K tok |
94
+ | NVIDIA NIM | llama, vision | build.nvidia.com | 40 |
95
+ | Gemini | gemini-3.6-flash, vision | aistudio.google.com | 1500 |
96
+ | OpenRouter | cohere-north, glm, nemotron, ox-alpha, dots-3, lfm, laguna | openrouter.ai | 50-100 |
97
+ | ZAI | glm-4.7-flash | open.bigmodel.cn | 1000 |
98
+ | Cerebras | gpt-oss-120b, gemma-4-31b | cloud.cerebras.ai | 1000 |
99
+ | DeepSeek | deepseek-v4-flash, vision | platform.deepseek.com | 1000 |
100
+ | Local | Ollama, LM Studio | — | unlimited |
101
+
102
+ **New free models are discovered, tested, and added automatically** — no need
103
+ to watch for new releases. The model manager runs every 6 hours.
104
+
105
+ > The catalog is extensible: add a model to `providers.json` and it joins the pool.
106
+ > Providers unavailable to your key (404) are auto-disabled.
107
+
108
+ ## How it works
109
+
110
+ 1. A request arrives at `/v1/chat/completions` (OpenAI format).
111
+ 2. Freegate picks the best provider: healthy, under limit, fastest today.
112
+ 3. If it fails — instantly tries the next one in the chain.
113
+ 4. The response returns in the same format — the client never notices.
114
+
115
+ ## Development
116
+
117
+ ```bash
118
+ npm test # unit tests (17)
119
+ node server.js # run from source
120
+ ```
121
+
122
+ ## Configuration
123
+
124
+ **Keys** — only in `.env` (never committed):
125
+
126
+ ```bash
127
+ PROVIDER_GROQ_APIKEY=... # Groq
128
+ PROVIDER_MISTRAL_APIKEY=... # Mistral
129
+ PROVIDER_GEMINI_APIKEY=... # Gemini
130
+ PROVIDER_NIM_APIKEY=... # NVIDIA NIM
131
+ PROVIDER_OPENROUTER_APIKEY=... # OpenRouter
132
+ PROVIDER_ZAI_APIKEY=... # ZAI
133
+ ```
134
+
135
+ **Server settings** — in `config.json` or env:
136
+
137
+ | Setting | Env | Default |
138
+ |---------|-----|---------|
139
+ | Port | `PORT` | `4000` |
140
+ | Password | `AUTH` | empty (no auth) |
141
+ | Rate limit/min | `config.json → rateLimit` | 100/min |
142
+
143
+ **Providers** — catalog in `providers.json` (18 models). Add your own:
144
+ put it in `config.json` → `providers` (same format as `providers.json`).
145
+
146
+ ## CLI commands
147
+
148
+ ```bash
149
+ npx davil-cod init # create config
150
+ npx davil-cod init -i # interactive wizard (keys, password)
151
+ npx davil-cod start # start proxy
152
+ npx davil-cod status # diagnostics: providers, limits, errors
153
+ npx davil-cod test # verify it works
154
+ npx davil-cod install-service # autostart at boot
155
+ npx davil-cod dashboard # open dashboard
156
+ ```
157
+
158
+ ## FAQ
159
+
160
+ **Is this legal?** Yes. You connect **your own** free provider keys — you just
161
+ get a single reliable gateway to all of them.
162
+
163
+ **How much does it cost?** $0. Only the free-tier limits of the providers.
164
+
165
+ **Which models are fastest?** The proxy measures and picks. Currently leading:
166
+ Qwen (Groq, ~300ms) and cohere-north (OpenRouter).
167
+
168
+ **Can I add my own provider?** Yes — add it to `config.json` or `providers.json`.
169
+
170
+ **Is it only for opencode?** No. Any OpenAI-compatible client
171
+ (see `examples/` — Cursor, Claude Code, scripts).
172
+
173
+ ## Tools
174
+
175
+ ### Shorts generator — `tools/generate_shorts.py`
176
+ Free vertical video (9:16) generation via **MiniMax H3** (video + sound from
177
+ one prompt) or **Wan 2.1**. Runs through Hugging Face online demos — GPU in
178
+ the cloud, no install.
179
+
180
+ ```bash
181
+ cd tools
182
+ uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt
183
+ export HF_TOKEN=hf_xxx # free: huggingface.co → settings/tokens
184
+ ./.venv/bin/python generate_shorts.py "Cozy morning scene, warm light" --format 9:16 --duration 5
185
+ ```
186
+
187
+ Prompt catalog: `tools/prompts.md`.
188
+
189
+ ## License
190
+
191
+ MIT
package/README.ru.md ADDED
@@ -0,0 +1,191 @@
1
+ # Freegate
2
+
3
+ [![CI](https://github.com/Artur21101965/davil-cod/actions/workflows/ci.yml/badge.svg)](https://github.com/Artur21101965/davil-cod/actions)
4
+ [![Docker Hub](https://img.shields.io/docker/pulls/nik951751/davil-cod.svg)](https://hub.docker.com/r/nik951751/davil-cod)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![npm](https://img.shields.io/npm/v/davil-cod.svg)](https://www.npmjs.com/package/davil-cod)
7
+
8
+ **English version: [README.md](README.md)**
9
+
10
+ ## Зачем платить за LLM, когда есть бесплатные?
11
+
12
+ Твой AI-агент, бот или скрипт использует один OpenAI-совместимый endpoint.
13
+ За ним Freegate автоматически распределяет запросы между **25 бесплатными
14
+ моделями** — Groq, Mistral, Gemini, NVIDIA NIM, OpenRouter, ZAI, Cerebras,
15
+ DeepSeek и локальные модели. Если один провайдер упал, перегружен или сжёг
16
+ дневной лимит — запрос **мгновенно уходит на следующий**. Ты никогда не
17
+ видишь «rate limit», и никогда не платишь.
18
+
19
+ **Результат:** полноценный LLM-доступ для повседневной работы по цене $0.
20
+
21
+ ![Дашборд Freegate](assets/dashboard.png)
22
+
23
+ ## Возможности
24
+
25
+ | | |
26
+ |---|---|
27
+ | 🔀 **Автопереключение** | 25 провайдеров в одной цепочке. Провайдер упал? Следующий уже отвечает. |
28
+ | 🤖 **Автоуправление моделями** | Сам находит новые бесплатные модели, тестирует, добавляет рабочие, отключает мёртвые — каждые 6 часов. |
29
+ | 🏷️ **Категории моделей** | reasoning / coding / general / vision / local — правильная модель для каждой задачи. |
30
+ | 🖼️ **Vision-конвейер** | Скриншот → vision-модель читает → кодинг-модель отвечает на вопрос. |
31
+ | 💰 **Бесплатно** | Только free-модели. Дашборд показывает остаток лимита каждого провайдера. |
32
+ | ⚡ **Умный выбор** | Прокси сам находит самый быстрый и стабильный провайдер для каждого запроса. |
33
+ | 🛡️ **Надёжность** | Circuit breaker, очередь запросов, автоотключение мёртвых провайдеров, watchdog. |
34
+ | 📊 **Дашборд** | Статус, скорость, лимиты, история, токены, RPM-график. |
35
+ | 💾 **Кэш на диске** | Повторные промпты не тратят лимиты вообще. |
36
+ | 🔌 **Совместимость** | Любой OpenAI-клиент: opencode, Cursor, ChatGPT-аналоги, твои скрипты. |
37
+
38
+ ## Быстрый старт — 30 секунд
39
+
40
+ ```bash
41
+ npx davil-cod init -i # мастер спросит: пароль + ключи каждого провайдера
42
+ npx davil-cod start # прокси на http://localhost:4000
43
+ npx davil-cod test # проверить, что всё работает
44
+ ```
45
+
46
+ Дашборд: `http://localhost:4000/?key=твой_пароль`
47
+
48
+ Или через Docker:
49
+
50
+ ```bash
51
+ docker run -d --name davil-cod -p 4000:4000 \
52
+ -e PROVIDER_GROQ_APIKEY=... \
53
+ -e PROVIDER_MISTRAL_APIKEY=... \
54
+ -e AUTH=your-secret-key \
55
+ nik951751/davil-cod
56
+ ```
57
+
58
+ ### Подключение к любому OpenAI-клиенту
59
+
60
+ | Поле | Значение |
61
+ |------|----------|
62
+ | Base URL | `http://localhost:4000/v1` |
63
+ | API Key | твой пароль из `init` |
64
+ | Модель | `tier-s` (быстрая) / `tier-splus` (мощная) |
65
+
66
+ Пример для opencode (`~/.config/opencode/opencode.jsonc`):
67
+
68
+ ```jsonc
69
+ {
70
+ "provider": {
71
+ "free-proxy": {
72
+ "npm": "@ai-sdk/openai-compatible",
73
+ "name": "Freegate",
74
+ "options": {
75
+ "baseURL": "http://localhost:4000/v1",
76
+ "apiKey": "your-secret-key-here"
77
+ },
78
+ "models": {
79
+ "tier-splus": { "name": "Freegate (Best)", "input": ["text"] },
80
+ "tier-s": { "name": "Freegate (Fast)", "input": ["text"] }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Поддерживаемые провайдеры
88
+
89
+ | Провайдер | Модели | Где ключ | Лимит/день |
90
+ |-----------|--------|----------|------------|
91
+ | Groq | gpt-oss-120b, qwen-27b, allam-2-7b, compound | console.groq.com | 1000 |
92
+ | Mistral | codestral, small | console.mistral.ai | 500K ток |
93
+ | NVIDIA NIM | llama, vision | build.nvidia.com | 40 |
94
+ | Gemini | gemini-3.6-flash, vision | aistudio.google.com | 1500 |
95
+ | OpenRouter | cohere-north, glm, nemotron, ox-alpha, dots-3, lfm, laguna | openrouter.ai | 50-100 |
96
+ | ZAI | glm-4.7-flash | open.bigmodel.cn | 1000 |
97
+ | Cerebras | gpt-oss-120b, gemma-4-31b | cloud.cerebras.ai | 1000 |
98
+ | DeepSeek | deepseek-v4-flash, vision | platform.deepseek.com | 1000 |
99
+ | Локальные | Ollama, LM Studio | — | безлимит |
100
+
101
+ **Новые бесплатные модели находятся, тестируются и добавляются автоматически**
102
+ — не нужно следить за релизами. Менеджер моделей работает каждые 6 часов.
103
+
104
+ > Каталог расширяемый: добавь модель в `providers.json` — и она попадёт в пул.
105
+ > Провайдеры, недоступные твоему ключу (404), отключаются автоматически.
106
+
107
+ ## Как это работает
108
+
109
+ 1. Приходит запрос на `/v1/chat/completions` (формат OpenAI).
110
+ 2. Freegate выбирает лучший провайдер: здоровый, под лимитом, самый быстрый сегодня.
111
+ 3. Если запрос не прошёл — мгновенно пробует следующий из цепочки.
112
+ 4. Ответ возвращается клиенту в том же формате — клиент ничего не замечает.
113
+
114
+ ## Разработка
115
+
116
+ ```bash
117
+ npm test # unit-тесты (17 шт.)
118
+ node server.js # запуск из исходников
119
+ ```
120
+
121
+ ## Конфигурация
122
+
123
+ **Ключи** — только в `.env` (не в git):
124
+
125
+ ```bash
126
+ PROVIDER_GROQ_APIKEY=... # Groq
127
+ PROVIDER_MISTRAL_APIKEY=... # Mistral
128
+ PROVIDER_GEMINI_APIKEY=... # Gemini
129
+ PROVIDER_NIM_APIKEY=... # NVIDIA NIM
130
+ PROVIDER_OPENROUTER_APIKEY=... # OpenRouter
131
+ PROVIDER_ZAI_APIKEY=... # ZAI
132
+ ```
133
+
134
+ **Параметры сервера** — в `config.json` или env:
135
+
136
+ | Параметр | Env | По умолчанию |
137
+ |----------|-----|--------------|
138
+ | Порт | `PORT` | `4000` |
139
+ | Пароль | `AUTH` | пусто (нет auth) |
140
+ | Лимит запросов/мин | `config.json → rateLimit` | 100/мин |
141
+
142
+ **Провайдеры** — каталог в `providers.json` (18 моделей). Добавить свой:
143
+ впиши его в `config.json` → `providers` (формат как в `providers.json`).
144
+
145
+ ## Команды CLI
146
+
147
+ ```bash
148
+ npx davil-cod init # создать конфиг
149
+ npx davil-cod init -i # интерактивный мастер (ключи, пароль)
150
+ npx davil-cod start # запустить прокси
151
+ npx davil-cod status # диагностика: провайдеры, лимиты, ошибки
152
+ npx davil-cod test # проверить, что работает
153
+ npx davil-cod install-service # автозапуск при старте системы
154
+ npx davil-cod dashboard # открыть дашборд
155
+ ```
156
+
157
+ ## FAQ
158
+
159
+ **Это законно?** Да. Ты подключаешь **свои** бесплатные ключи провайдеров —
160
+ просто получаешь единый надёжный доступ к ним всем.
161
+
162
+ **Сколько это стоит?** $0. Только лимиты бесплатных тарифов провайдеров.
163
+
164
+ **Какие модели самые быстрые?** Прокси сам измеряет и выбирает. Сейчас
165
+ лидируют Qwen (Groq, ~300ms) и cohere-north (OpenRouter).
166
+
167
+ **Могу добавить свой провайдер?** Да — впиши его в `config.json` или
168
+ `providers.json`.
169
+
170
+ **Это только для opencode?** Нет. Любой OpenAI-совместимый клиент
171
+ (см. `examples/` — Cursor, Claude Code, скрипты).
172
+
173
+ ## Инструменты (tools/)
174
+
175
+ ### Генератор шортс — `tools/generate_shorts.py`
176
+ Бесплатная генерация вертикальных видео (9:16) через **MiniMax H3** (видео + звук
177
+ из одного промпта) или **Wan 2.1**. Работает через онлайн-демо Hugging Face —
178
+ GPU в облаке, без установки.
179
+
180
+ ```bash
181
+ cd tools
182
+ uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt
183
+ export HF_TOKEN=hf_xxx # бесплатно: huggingface.co → settings/tokens
184
+ ./.venv/bin/python generate_shorts.py "Cozy morning scene, warm light" --format 9:16 --duration 5
185
+ ```
186
+
187
+ Каталог готовых промптов: `tools/prompts.md`.
188
+
189
+ ## Лицензия
190
+
191
+ MIT
Binary file
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ // bin/davil-cod.js — CLI wrapper + interactive setup wizard
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const { spawn } = require('child_process');
6
+ const readline = require('readline');
7
+
8
+ const ROOT = path.join(__dirname, '..');
9
+ const SERVER = path.join(ROOT, 'server.js');
10
+ const CATALOG = JSON.parse(fs.readFileSync(path.join(ROOT, 'providers.json'), 'utf8'));
11
+
12
+ // Sequential stdin reader (works with both TTY and piped input)
13
+ function makeReader() {
14
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
15
+ const queue = [];
16
+ let buffer = '';
17
+ rl.on('line', (line) => {
18
+ const q = queue.shift();
19
+ if (q) q(line);
20
+ else buffer += line + '\n';
21
+ });
22
+ return function ask(question) {
23
+ process.stdout.write(question);
24
+ if (buffer.length) {
25
+ const idx = buffer.indexOf('\n');
26
+ const line = idx >= 0 ? buffer.slice(0, idx) : buffer;
27
+ buffer = idx >= 0 ? buffer.slice(idx + 1) : '';
28
+ return Promise.resolve(line);
29
+ }
30
+ return new Promise((resolve) => queue.push(resolve));
31
+ };
32
+ }
33
+
34
+ async function initWizard() {
35
+ const ask = makeReader();
36
+
37
+ // 1. Auth key for the proxy
38
+ const auth = await ask('Пароль для доступа к прокси (Enter = сгенерировать): ');
39
+ const authKey = auth.trim() || 'dc_' + Math.random().toString(36).slice(2, 14);
40
+
41
+ // 2. Collect provider keys from the catalog (unique env vars)
42
+ const envVars = new Map(); // envVar -> [providerNames]
43
+ for (const [name, p] of Object.entries(CATALOG)) {
44
+ if (!p.envVar) continue;
45
+ if (!envVars.has(p.envVar)) envVars.set(p.envVar, []);
46
+ envVars.get(p.envVar).push(name);
47
+ }
48
+
49
+ const keys = {};
50
+ for (const [envVar, providers] of envVars) {
51
+ const sample = providers[0];
52
+ const hint = CATALOG[sample].keyHint || '';
53
+ const answer = await ask(`\n${providers.join(', ')}\n Ключ (${hint}) — пусто = пропустить: `);
54
+ if (answer.trim()) keys[envVar] = answer.trim();
55
+ }
56
+
57
+ // 3. Write .env
58
+ const envLines = ['# Freegate — API ключи. Сгенерировано ' + new Date().toISOString().slice(0, 10)];
59
+ for (const envVar of envVars.keys()) {
60
+ envLines.push(`${envVar}=${keys[envVar] || ''}`);
61
+ }
62
+ fs.writeFileSync(path.join(process.cwd(), '.env'), envLines.join('\n') + '\n');
63
+ console.log('✓ .env создан');
64
+
65
+ // 4. Write config.json
66
+ const config = {
67
+ port: 4000,
68
+ auth: authKey,
69
+ rateLimit: { maxRequests: 100, windowMs: 60000 },
70
+ providers: {},
71
+ };
72
+ fs.writeFileSync(path.join(process.cwd(), 'config.json'), JSON.stringify(config, null, 2));
73
+ console.log('✓ config.json создан');
74
+
75
+ const filled = Object.values(keys).filter(Boolean).length;
76
+ console.log(`\nГотово! Провайдеров с ключами: ${filled} из ${envVars.size}`);
77
+ console.log(`Запуск: npx davil-cod start (пароль: ${authKey})`);
78
+ }
79
+
80
+ function copyExample(name, example) {
81
+ const src = path.join(ROOT, example || name + '.example');
82
+ const dst = path.join(process.cwd(), name);
83
+ if (fs.existsSync(dst)) { console.log('✓ ' + name + ' уже есть'); return; }
84
+ if (!fs.existsSync(src)) { console.log('✗ ' + example + ' не найден в пакете'); return; }
85
+ fs.copyFileSync(src, dst);
86
+ console.log('✓ создан ' + name + ' — заполни ключи');
87
+ }
88
+
89
+ const cmd = process.argv[2];
90
+
91
+ if (cmd === 'init') {
92
+ if (process.argv.includes('--interactive') || process.argv.includes('-i')) {
93
+ initWizard().catch((e) => { console.error('Ошибка:', e.message); process.exit(1); });
94
+ } else {
95
+ // Non-interactive fallback: plain copy (safe — never overwrites)
96
+ copyExample('config.json', 'config.example.json');
97
+ copyExample('.env', '.env.example');
98
+ console.log('\nГотово! Заполни .env ключами, затем: npx davil-cod start');
99
+ console.log('Совет: npx davil-cod init -i — интерактивный мастер с вопросами.');
100
+ }
101
+ } else if (cmd === 'start') {
102
+ // Spawn from the user's cwd (not package dir) so server.js picks up their
103
+ // config.json / .env created by `init`.
104
+ const child = spawn(process.execPath, [SERVER], { stdio: 'inherit' });
105
+ child.on('close', (c) => process.exit(c || 0));
106
+ } else if (cmd === 'install-service') {
107
+ const os = require('os');
108
+ const { execSync } = require('child_process');
109
+ const isMac = process.platform === 'darwin';
110
+ const launchDir = process.cwd();
111
+ const label = 'com.davilcod.proxy';
112
+
113
+ if (isMac) {
114
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
115
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
116
+ <plist version="1.0">
117
+ <dict>
118
+ <key>Label</key>
119
+ <string>${label}</string>
120
+ <key>ProgramArguments</key>
121
+ <array>
122
+ <string>${process.execPath}</string>
123
+ <string>${SERVER}</string>
124
+ </array>
125
+ <key>WorkingDirectory</key>
126
+ <string>${launchDir}</string>
127
+ <key>RunAtLoad</key>
128
+ <true/>
129
+ <key>KeepAlive</key>
130
+ <true/>
131
+ </dict>
132
+ </plist>`;
133
+ const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', label + '.plist');
134
+ fs.writeFileSync(plistPath, plist);
135
+ execSync(`launchctl unload ${plistPath} 2>/dev/null; launchctl load ${plistPath}`);
136
+ console.log('✅ Служба автозапуска установлена (launchd)');
137
+ console.log(' Прокси будет запускаться при включении компьютера');
138
+ } else {
139
+ // Linux: systemd user service
140
+ const unit = `[Unit]
141
+ Description=Freegate LLM proxy
142
+ After=network.target
143
+
144
+ [Service]
145
+ WorkingDirectory=${launchDir}
146
+ ExecStart=${process.execPath} ${SERVER}
147
+ Restart=always
148
+ RestartSec=5
149
+
150
+ [Install]
151
+ WantedBy=default.target
152
+ `;
153
+ const unitPath = path.join(os.homedir(), '.config', 'systemd', 'user', 'davil-cod.service');
154
+ fs.mkdirSync(path.dirname(unitPath), { recursive: true });
155
+ fs.writeFileSync(unitPath, unit);
156
+ execSync(`systemctl --user daemon-reload && systemctl --user enable davil-cod && systemctl --user start davil-cod`);
157
+ console.log('✅ Служба автозапуска установлена (systemd)');
158
+ }
159
+ } else if (cmd === 'dashboard') {
160
+ console.log('Открой http://localhost:4000/ (запусти start сначала)');
161
+ } else if (cmd === 'test') {
162
+ const http = require('http');
163
+ const base = `http://127.0.0.1:${process.env.PORT || 4000}`;
164
+ http.get(base + '/health', (res) => {
165
+ let data = '';
166
+ res.on('data', (c) => data += c);
167
+ res.on('end', () => {
168
+ if (res.statusCode === 200) {
169
+ console.log('✅ Прокси работает: ' + data);
170
+ } else {
171
+ console.log('❌ Прокси ответил ' + res.statusCode + ': ' + data);
172
+ process.exit(1);
173
+ }
174
+ });
175
+ }).on('error', (err) => {
176
+ console.log('❌ Прокси не запущен на ' + base);
177
+ console.log(' Запусти: npx davil-cod start');
178
+ process.exit(1);
179
+ });
180
+ } else if (cmd === 'status') {
181
+ const http = require('http');
182
+ const base = `http://127.0.0.1:${process.env.PORT || 4000}`;
183
+ const pkg = require(path.join(ROOT, 'package.json'));
184
+ console.log('Freegate v' + pkg.version);
185
+ console.log('-----------------------------');
186
+ http.get(base + '/v1/stats', (res) => {
187
+ let data = '';
188
+ res.on('data', (c) => data += c);
189
+ res.on('end', () => {
190
+ if (res.statusCode !== 200) {
191
+ console.log('❌ Прокси не отвечает (HTTP ' + res.statusCode + ')');
192
+ console.log(' Запусти: npx davil-cod start');
193
+ process.exit(1);
194
+ }
195
+ try {
196
+ const s = JSON.parse(data);
197
+ console.log(`Запросов: ${s.total_requests} (успех ${s.successful_requests}, ошибок ${s.failed_requests})`);
198
+ console.log(`Кэш: ${s.cache.size}/${s.cache.maxSize}, точность ${s.cache.hitRate}%`);
199
+ const up = Object.values(s.health).filter(h => h.status === 'up').length;
200
+ console.log(`Провайдеры: ${up}/${Object.keys(s.health).length} в строю`);
201
+ for (const [k, h] of Object.entries(s.health)) {
202
+ const icon = h.status === 'up' ? '✅' : '❌';
203
+ const limit = s.limits?.[k];
204
+ const limitStr = limit ? ` · лимит ${limit.used}/${limit.limit}` : '';
205
+ console.log(` ${icon} ${k} (${h.latency_ms}ms)${limitStr}${h.reason ? ' · ' + h.reason : ''}`);
206
+ }
207
+ } catch (e) {
208
+ console.log('❌ Не удалось разобрать ответ: ' + e.message);
209
+ process.exit(1);
210
+ }
211
+ });
212
+ }).on('error', () => {
213
+ console.log('❌ Прокси не запущен на ' + base);
214
+ console.log(' Запусти: npx davil-cod start');
215
+ process.exit(1);
216
+ });
217
+ } else if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
218
+ const pkg = require(path.join(ROOT, 'package.json'));
219
+ console.log(pkg.version);
220
+ } else {
221
+ console.log('Freegate — бесплатный LLM-прокси с failover');
222
+ console.log('Команды:');
223
+ console.log(' npx davil-cod init интерактивная настройка (ключи, пароль)');
224
+ console.log(' npx davil-cod start запустить прокси');
225
+ console.log(' npx davil-cod status диагностика: провайдеры, лимиты, ошибки');
226
+ console.log(' npx davil-cod test проверить, что прокси работает');
227
+ console.log(' npx davil-cod dashboard открыть дашборд');
228
+ console.log(' npx davil-cod install-service автозапуск при старте системы');
229
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$comment": "Copy to config.json. Keys go in .env, not here. Custom providers beyond the catalog go here.",
3
+ "port": 4000,
4
+ "auth": "your-secret-key-here",
5
+ "rateLimit": { "maxRequests": 100, "windowMs": 60000 },
6
+ "providers": {
7
+ "or-nemotron-550b": {
8
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
9
+ "model": "nvidia/nemotron-3-ultra-550b-a55b:free",
10
+ "priority": 11,
11
+ "dailyLimit": 50,
12
+ "keyHint": "openrouter.ai → Keys",
13
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
14
+ "free": true
15
+ }
16
+ }
17
+ }