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 +7 -0
- package/LICENSE +21 -0
- package/README.md +191 -0
- package/README.ru.md +191 -0
- package/assets/dashboard.png +0 -0
- package/bin/freegate.js +229 -0
- package/config.example.json +17 -0
- package/lib/cache.js +139 -0
- package/lib/clean.js +56 -0
- package/lib/dashboard.js +178 -0
- package/lib/health.js +179 -0
- package/lib/logger.js +48 -0
- package/lib/pool.js +41 -0
- package/lib/providers.js +215 -0
- package/lib/rateLimit.js +16 -0
- package/package.json +43 -0
- package/providers.json +298 -0
- package/server.js +662 -0
package/.env.example
ADDED
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
|
+
[](https://github.com/Artur21101965/davil-cod/actions)
|
|
4
|
+
[](https://hub.docker.com/r/nik951751/davil-cod)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/davil-cod)
|
|
7
|
+
[](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
|
+

|
|
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
|
+
[](https://github.com/Artur21101965/davil-cod/actions)
|
|
4
|
+
[](https://hub.docker.com/r/nik951751/davil-cod)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](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
|
+

|
|
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
|
package/bin/freegate.js
ADDED
|
@@ -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
|
+
}
|