qwen-api-proxy 1.0.10

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/index.js ADDED
@@ -0,0 +1,444 @@
1
+ import express from 'express';
2
+ import bodyParser from 'body-parser';
3
+
4
+ import { initBrowser, shutdownBrowser } from './src/browser/browser.js';
5
+ import apiRoutes from './src/api/routes.js';
6
+ import { getAvailableModelsFromFile, fetchModelsFromAPI, getDefaultModel, getApiKeys } from './src/api/chat.js';
7
+ import { loadTokens } from './src/api/tokenManager.js';
8
+ import { addAccountInteractive } from './src/utils/accountSetup.js';
9
+ import { logHttpRequest, logInfo, logError, logWarn, logDebug } from './src/logger/index.js';
10
+ import { prompt } from './src/utils/prompt.js';
11
+ import { PORT, HOST } from './src/config.js';
12
+ import { startTelegramBot, stopTelegramBot, notifyAllUsers, configureProxy, processPendingArchive, checkAllSubsystems, startPeriodicHealthCheck } from './src/utils/telegramBot.js';
13
+ import { getProxyInfo } from './src/utils/proxy.js';
14
+ import { checkPermissions } from './src/utils/permissionChecker.js';
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import { execSync } from 'child_process';
18
+ import { SESSION_DIR } from './src/config.js';
19
+
20
+ const app = express();
21
+
22
+ const port = Number.parseInt(process.env.PORT ?? PORT, 10);
23
+ const host = process.env.HOST || HOST;
24
+
25
+ if (Number.isNaN(port) || port <= 0 || port > 65535) {
26
+ throw new Error(`Некорректное значение переменной PORT: ${process.env.PORT}`);
27
+ }
28
+
29
+ function toBoolean(value) {
30
+ if (typeof value !== 'string') return false;
31
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
32
+ }
33
+
34
+ const skipAccountMenu = toBoolean(process.env.SKIP_ACCOUNT_MENU) || toBoolean(process.env.NON_INTERACTIVE);
35
+
36
+ function ensureNonInteractiveTokens() {
37
+ const tokens = loadTokens();
38
+ if (!tokens.length) {
39
+ logWarn('⚠️ Не найдено ни одного аккаунта. Сервер работает в режиме Telegram бота.');
40
+ logWarn('📦 Отправьте архив с сессиями через Telegram бот для добавления аккаунтов.');
41
+ // Не выходим - позволяем работать как бот для получения архивов
42
+ return false;
43
+ }
44
+ const now = Date.now();
45
+ const validTokens = tokens.filter(t => (!t.resetAt || new Date(t.resetAt).getTime() <= now) && !t.invalid);
46
+ if (!validTokens.length) {
47
+ logWarn('⚠️ Все аккаунты недоступны. Сервер работает в режиме Telegram бота.');
48
+ logWarn('📦 Отправьте архив с сессиями через Telegram бот для обновления аккаунтов.');
49
+ // Не выходим - позволяем работать как бот
50
+ return false;
51
+ }
52
+ logInfo(`Автоматический запуск: обнаружено ${tokens.length} аккаунтов, из них ${validTokens.length} активны.`);
53
+ return true;
54
+ }
55
+
56
+ app.use(logHttpRequest);
57
+ app.use(bodyParser.json({ limit: '150mb' }));
58
+ app.use(bodyParser.urlencoded({ limit: '150mb', extended: true }));
59
+
60
+ app.use((err, req, res, next) => {
61
+ const isJsonSyntaxError = err instanceof SyntaxError && err.status === 400 && Object.prototype.hasOwnProperty.call(err, 'body');
62
+
63
+ if (isJsonSyntaxError) {
64
+ logWarn(`Некорректный JSON в запросе: ${err.message}`);
65
+ return res.status(400).json({
66
+ error: 'Некорректный JSON',
67
+ message: 'Проверьте тело запроса: используйте валидный JSON с двойными кавычками.'
68
+ });
69
+ }
70
+
71
+ return next(err);
72
+ });
73
+
74
+ app.use((req, res, next) => {
75
+ res.header('Access-Control-Allow-Origin', '*');
76
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
77
+ res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
78
+ if (req.method === 'OPTIONS') return res.sendStatus(200);
79
+ next();
80
+ });
81
+
82
+ app.use('/api', apiRoutes);
83
+
84
+ app.use((req, res) => {
85
+ logWarn(`404 Not Found: ${req.method} ${req.originalUrl}`);
86
+ res.status(404).json({ error: 'Эндпоинт не найден' });
87
+ });
88
+
89
+ app.use((err, req, res, next) => {
90
+ logError('Внутренняя ошибка сервера', err);
91
+ res.status(500).json({ error: 'Внутренняя ошибка сервера' });
92
+ });
93
+
94
+ process.on('SIGINT', handleShutdown);
95
+ process.on('SIGTERM', handleShutdown);
96
+ process.on('SIGHUP', handleShutdown);
97
+ process.on('uncaughtException', async (error) => {
98
+ logError('Необработанное исключение', error);
99
+ await handleShutdown();
100
+ });
101
+
102
+ async function handleShutdown() {
103
+ logInfo('\nПолучен сигнал завершения. Закрываем браузер...');
104
+ stopTelegramBot();
105
+ await shutdownBrowser();
106
+ logInfo('Завершение работы.');
107
+ process.exit(0);
108
+ }
109
+
110
+ /**
111
+ * Создает ZIP архив папки session/
112
+ */
113
+ function createSessionArchive() {
114
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
115
+ const archiveName = `session_backup_${timestamp}.zip`;
116
+ const archivePath = path.resolve(process.cwd(), archiveName);
117
+ const sessionPath = path.resolve(process.cwd(), SESSION_DIR);
118
+
119
+ console.log('\n📦 Создание архива сессии...');
120
+ console.log(`📂 Путь к сессии: ${sessionPath}`);
121
+ console.log(`📄 Имя архива: ${archiveName}`);
122
+
123
+ try {
124
+ // Проверяем существование папки сессии
125
+ if (!fs.existsSync(sessionPath)) {
126
+ throw new Error(`Папка сессии не найдена: ${sessionPath}`);
127
+ }
128
+
129
+ // Проверяем содержимое
130
+ const files = fs.readdirSync(sessionPath);
131
+ if (files.length === 0) {
132
+ throw new Error('Папка сессии пуста. Сначала выполните авторизацию.');
133
+ }
134
+
135
+ // Создаем ZIP архив
136
+ const command = `cd "${process.cwd()}" && zip -r "${archiveName}" "${SESSION_DIR}/"`;
137
+ logInfo(`Выполнение команды: ${command}`);
138
+ execSync(command, { stdio: 'inherit' });
139
+
140
+ // Проверяем создание архива
141
+ if (!fs.existsSync(archivePath)) {
142
+ throw new Error('Архив не был создан');
143
+ }
144
+
145
+ const stats = fs.statSync(archivePath);
146
+ const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
147
+
148
+ console.log('\n✅ Архив успешно создан!');
149
+ console.log(`📍 Путь: ${archivePath}`);
150
+ console.log(`📏 Размер: ${sizeMB} MB`);
151
+
152
+ return archivePath;
153
+ } catch (error) {
154
+ logError('Ошибка при создании архива', error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Проверяет наличие и содержимое .gitignore в корневой директории
161
+ */
162
+ function validateGitignore() {
163
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
164
+ const requiredEntries = [
165
+ 'session',
166
+ 'logs',
167
+ 'uploads',
168
+ 'temp',
169
+ 'session_backup',
170
+ 'session_backup_*'
171
+ ];
172
+
173
+ if (!fs.existsSync(gitignorePath)) {
174
+ logWarn('⚠️ Файл .gitignore не найден в корневой директории');
175
+ logWarn(' Рекомендуется создать .gitignore для защиты чувствительных данных');
176
+ return false;
177
+ }
178
+
179
+ const content = fs.readFileSync(gitignorePath, 'utf8');
180
+ const lines = content.split('\n').map(line => line.trim());
181
+ const missingEntries = [];
182
+
183
+ requiredEntries.forEach(entry => {
184
+ if (!lines.includes(entry)) {
185
+ missingEntries.push(entry);
186
+ }
187
+ });
188
+
189
+ if (missingEntries.length > 0) {
190
+ logWarn(`⚠️ .gitignore отсутствует: ${missingEntries.join(', ')}`);
191
+ logWarn(' Добавьте эти записи в .gitignore для защиты данных');
192
+ return false;
193
+ }
194
+
195
+ logInfo('✅ .gitignore проверен - все записи на месте');
196
+ return true;
197
+ }
198
+
199
+ /**
200
+ * Обрабатывает CLI команды
201
+ */
202
+ async function handleCLICommand() {
203
+ const args = process.argv.slice(2);
204
+ const command = args[0];
205
+
206
+ if (!command) {
207
+ return false; // Нет команды, продолжаем обычный запуск
208
+ }
209
+
210
+ if (command === 'archive' || command === '--archive') {
211
+ console.log('\n╔══════════════════════════════════════════════════════════╗');
212
+ console.log('║ Session Archive Creator ║');
213
+ console.log('║ ║');
214
+ console.log('║ Создание ZIP архива папки session/ ║');
215
+ console.log('╚══════════════════════════════════════════════════════════╝\n');
216
+
217
+ try {
218
+ const archivePath = createSessionArchive();
219
+ console.log('\n🎉 ГОТОВО!');
220
+ console.log(`📄 Архив: ${archivePath}`);
221
+ console.log('\n📱 Следующие шаги:');
222
+ console.log(' 1. Откройте Telegram бота');
223
+ console.log(' 2. Нажмите 📎 (скрепка)');
224
+ console.log(' 3. Выберите "Файл" (НЕ "Фото"!)');
225
+ console.log(' 4. Отправьте архив боту');
226
+ console.log(' 5. Дождитесь подтверждения\n');
227
+ process.exit(0);
228
+ } catch (error) {
229
+ logError('Ошибка при создании архива', error);
230
+ process.exit(1);
231
+ }
232
+ }
233
+
234
+ return false;
235
+ }
236
+
237
+ async function startServer() {
238
+ console.log(`
239
+ ███████ ██████ ███████ ███████ ██████ ██ ██ ███████ ███ ██ █████ ██████ ██
240
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██
241
+ █████ ██████ █████ █████ ██ ██ ██ █ ██ █████ ██ ██ ██ ███████ ██████ ██
242
+ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██
243
+ ██ ██ ██ ███████ ███████ ██████ ███ ███ ███████ ██ ████ ██ ██ ██ ██
244
+ ▀▀
245
+ API-прокси для Qwen
246
+ `);
247
+
248
+ logInfo('Запуск сервера...');
249
+
250
+ // ПЕРВЫМ ДЕЛОМ: Проверяем права доступа ко всем директориям и файлам
251
+ const permissionsOk = await checkPermissions();
252
+ if (!permissionsOk) {
253
+ logError('⛔ НЕВОЗМОЖНО ЗАПУСТИТЬСЯ: Исправьте права доступа и перезапустите сервер');
254
+ process.exit(1);
255
+ }
256
+
257
+ await configureProxy();
258
+
259
+
260
+ // ПЕРВЫМ ДЕЛОМ: Проверяем и обрабатываем ожидающий архив
261
+ const archiveProcessed = await processPendingArchive();
262
+ if (archiveProcessed) {
263
+ logInfo('✅ Ожидющий архив успешно распакован при запуске');
264
+ }
265
+
266
+ // Проверяем .gitignore
267
+ validateGitignore();
268
+
269
+ // Проверяем флаг перезапуска
270
+ const restartFlagPath = path.join(process.cwd(), '.restart_flag');
271
+ if (fs.existsSync(restartFlagPath)) {
272
+ try {
273
+ const restartInfo = JSON.parse(fs.readFileSync(restartFlagPath, 'utf8'));
274
+ logInfo(`🔄 Обнаружен флаг перезапуска: ${restartInfo.reason}`);
275
+ fs.unlinkSync(restartFlagPath);
276
+ } catch (e) {
277
+ logWarn('Ошибка чтения флага перезапуска', e);
278
+ }
279
+ }
280
+
281
+ if (!skipAccountMenu) {
282
+ while (true) {
283
+ const tokens = loadTokens();
284
+ console.log('\nСписок аккаунтов:');
285
+ if (!tokens.length) {
286
+ console.log(' (пусто)');
287
+ } else {
288
+ tokens.forEach((token, i) => {
289
+ const now = Date.now();
290
+ const isInvalid = token.invalid === true;
291
+ const isWaiting = Boolean(token.resetAt && new Date(token.resetAt).getTime() > now);
292
+ const statusLabel = isInvalid ? '❌ Недействителен' : isWaiting ? '⏳ Ожидание сброса' : '✅ OK';
293
+ const statusCode = isInvalid ? 0 : isWaiting ? 1 : 2;
294
+ console.log(`${String(i + 1).padStart(2, ' ')} | ${token.id} | ${statusLabel} (${statusCode})`);
295
+ });
296
+ }
297
+ console.log('\n=== Меню ===');
298
+ console.log('1 - Добавить новый аккаунт');
299
+ console.log('2 - Перелогинить аккаунт с истекшим токеном');
300
+ console.log('3 - Запустить прокси (по умолчанию)');
301
+ console.log('4 - Удалить аккаунт');
302
+
303
+ let choice = await prompt('Ваш выбор (Enter = 3): ');
304
+ if (!choice) choice = '3';
305
+
306
+ if (choice === '1') {
307
+ await addAccountInteractive();
308
+ } else if (choice === '2') {
309
+ const { reloginAccountInteractive } = await import('./src/utils/accountSetup.js');
310
+ await reloginAccountInteractive();
311
+ } else if (choice === '3') {
312
+ const hasValidToken = tokens.some(t => {
313
+ if (t.invalid) return false;
314
+ if (!t.resetAt) return true;
315
+ return new Date(t.resetAt).getTime() <= Date.now();
316
+ });
317
+ if (!tokens.length || !hasValidToken) {
318
+ console.log('Нужен хотя бы один валидный аккаунт для запуска.');
319
+ continue;
320
+ }
321
+ break;
322
+ } else if (choice === '4') {
323
+ const { removeAccountInteractive } = await import('./src/utils/accountSetup.js');
324
+ await removeAccountInteractive();
325
+ }
326
+ }
327
+ } else {
328
+ const hasValidTokens = ensureNonInteractiveTokens();
329
+ // Если нет токенов, браузер не нужен - работаем только как бот
330
+ if (!hasValidTokens) {
331
+ logInfo('🤖 Режим Telegram бота: ожидание архива с сессиями...');
332
+ }
333
+ }
334
+
335
+ const browserInitialized = await initBrowser(false);
336
+ if (!browserInitialized) {
337
+ const tokens = loadTokens();
338
+ if (tokens.length > 0) {
339
+ logError('Не удалось инициализировать браузер. Завершение работы.');
340
+ process.exit(1);
341
+ } else {
342
+ logWarn('⚠️ Браузер не инициализирован, но бот продолжит работу для получения сессий');
343
+ }
344
+ }
345
+
346
+ // Запускаем Telegram бота
347
+ const telegramBotStarted = await startTelegramBot();
348
+ if (telegramBotStarted) {
349
+ logInfo('🤖 Telegram бот запущен и готов принимать команды');
350
+ }
351
+
352
+ // Проверяем все подсистемы
353
+ await checkAllSubsystems(telegramBotStarted);
354
+
355
+ // Логируем информацию о прокси
356
+ const proxyInfo = getProxyInfo();
357
+ if (proxyInfo.telegram.configured) {
358
+ logInfo(`📱 Telegram Прокси: ${proxyInfo.telegram.url} (${proxyInfo.telegram.active ? '✅ активен' : '❌ неактивен'})`);
359
+ } else {
360
+ logInfo('📱 Telegram Прокси: не настроен');
361
+ }
362
+ if (proxyInfo.qwen.configured) {
363
+ logInfo(`🧠 Qwen LLM Прокси: ${proxyInfo.qwen.url} (${proxyInfo.qwen.active ? '✅ активен' : '❌ неактивен'})`);
364
+ } else {
365
+ logInfo('🧠 Qwen LLM Прокси: не настроен');
366
+ }
367
+
368
+ // Запускаем периодическую проверку здоровья каждые 4 часа
369
+ if (telegramBotStarted) {
370
+ startPeriodicHealthCheck();
371
+ }
372
+
373
+ try {
374
+ app.listen(port, host, async () => {
375
+ const displayHost = host === '0.0.0.0' ? 'localhost' : host;
376
+ logInfo(`Сервер запущен на ${host}:${port}`);
377
+ logInfo(`API доступен по адресу: http://${displayHost}:${port}/api`);
378
+ logInfo('Для проверки статуса авторизации: GET /api/status');
379
+ logInfo('Для отправки сообщения: POST /api/chat');
380
+ logInfo('Для получения списка моделей: GET /api/models');
381
+ logInfo('======================================================');
382
+ logInfo('API v2 - История чатов хранится на серверах Qwen');
383
+ logInfo('Создать новый чат: POST /api/chats');
384
+ logInfo('Отправить сообщение: POST /api/chat (с chatId и parentId)');
385
+ logInfo('======================================================');
386
+ logInfo('Доступно 25 моделей Qwen (через систему маппинга):');
387
+ logInfo('- Стандартные: qwen-max, qwen-plus, qwen-turbo и их latest-версии');
388
+ logInfo('- Coder: qwen3-coder-plus, qwen2.5-coder-*b-instruct (0.5b - 32b)');
389
+ logInfo('- Визуальные: qwen-vl-max, qwen-vl-plus и их latest-версии');
390
+ logInfo('- Qwen 3: qwen3, qwen3-max, qwen3-plus, qwen3-omni-flash');
391
+ logInfo('- Qwen 3.5: qwen3.5-plus, qwen3.5-flash, qwen3.5-397b-a17b, qwen3.5-122b-a10b, qwen3.5-27b, qwen3.5-35b-a3b');
392
+ logInfo('======================================================');
393
+ logInfo('Формат JSON запроса на чат:');
394
+ logInfo('{ "message": "текст сообщения", "model": "название модели (опционально)", "chatId": "ID чата (опционально)", "parentId": "ID родительского сообщения (опционально)" }');
395
+ logInfo('Пример первого запроса: { "message": "Привет, как дела?" }');
396
+ logInfo('Пример второго запроса: { "message": "А что ты умеешь?", "chatId": "полученный_id_чата", "parentId": "полученный_parentId" }');
397
+ logInfo('======================================================');
398
+ logInfo('Поддержка OpenAI совместимого API: POST /api/chat/completions');
399
+ logInfo('В ответе возвращаются chatId и parentId для продолжения диалога');
400
+ logInfo('======================================================');
401
+
402
+ // Загружаем список моделей (сначала из API, потом из файла как fallback)
403
+ logInfo('🔍 Начинаем загрузку списка моделей...');
404
+ const apiModels = await fetchModelsFromAPI();
405
+ logDebug(`fetchModelsFromAPI вернул: ${apiModels ? apiModels.length + ' моделей' : 'null'}`);
406
+
407
+ if (apiModels && apiModels.length > 0) {
408
+ logInfo(`✅ Загружено ${apiModels.length} моделей с Qwen API`);
409
+ logDebug(`Первые 5 моделей: ${apiModels.slice(0, 5).join(', ')}`);
410
+ // Можно сохранить в файл при необходимости
411
+ } else {
412
+ logWarn('⚠️ Используем модели из локального файла');
413
+ logDebug('Вызываем getAvailableModelsFromFile()...');
414
+ const fileModels = getAvailableModelsFromFile();
415
+ logDebug(`getAvailableModelsFromFile вернул: ${fileModels ? fileModels.length + ' моделей' : 'null'}`);
416
+ }
417
+
418
+ const defaultModel = getDefaultModel();
419
+ logInfo(`🎯 Модель по умолчанию: ${defaultModel}`);
420
+ logDebug(`getDefaultModel() вернул: ${defaultModel}`);
421
+
422
+ getApiKeys();
423
+ });
424
+ } catch (err) {
425
+ if (err.code === 'EADDRINUSE') {
426
+ logError(`Порт ${port} уже используется. Возможно, сервер уже запущен.`);
427
+ await shutdownBrowser();
428
+ process.exit(1);
429
+ }
430
+ throw err;
431
+ }
432
+ }
433
+
434
+ // Проверяем CLI команды перед запуском сервера
435
+ handleCLICommand().then(hasCommand => {
436
+ if (!hasCommand) {
437
+ // Нет команды CLI, запускаем сервер как обычно
438
+ startServer().catch(async error => {
439
+ logError('Ошибка при запуске сервера', error);
440
+ await shutdownBrowser();
441
+ process.exit(1);
442
+ });
443
+ }
444
+ });
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "qwen-api-proxy",
3
+ "version": "1.0.10",
4
+ "description": "Proxy server for accessing Qwen API through browser emulation with OpenAI-compatible API and Telegram bot",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "EndyKaufman",
8
+ "email": "admin@site15.ru"
9
+ },
10
+ "engines": {
11
+ "node": ">=18.0.0"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/EndyKaufman/FreeQwenApi.git"
16
+ },
17
+ "homepage": "https://github.com/EndyKaufman/FreeQwenApi#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/EndyKaufman/FreeQwenApi/issues"
20
+ },
21
+ "keywords": [
22
+ "qwen",
23
+ "api",
24
+ "proxy",
25
+ "openai",
26
+ "chat",
27
+ "ai",
28
+ "llm",
29
+ "telegram-bot",
30
+ "browser-automation"
31
+ ],
32
+ "bin": {
33
+ "qwen-api-proxy": "./bin/qwen-api-proxy.js"
34
+ },
35
+ "main": "index.js",
36
+ "type": "module",
37
+ "files": [
38
+ "bin/",
39
+ "src/",
40
+ "index.js",
41
+ ".env.example",
42
+ "README.md"
43
+ ],
44
+ "scripts": {
45
+ "start": "node index.js",
46
+ "archive": "node index.js archive",
47
+ "test": "echo \"Error: no test specified\" && exit 1",
48
+ "check-permissions": "node scripts/checkPermissions.js",
49
+ "fix-permissions": "bash fix-permissions.sh",
50
+ "rebuild-tokens": "node scripts/rebuildTokens.js",
51
+ "example:stream": "node examples/openai-sdk/streaming.js",
52
+ "example:simple": "node examples/openai-sdk/simple.js",
53
+ "example:system": "node examples/openai-sdk/system-message.js",
54
+ "example:image": "node examples/openai-sdk/image-analysis.js",
55
+ "example:conversation": "node examples/openai-sdk/conversation.js",
56
+ "example:compatibility": "node examples/openai-sdk/openai-compatibility.js",
57
+ "example:direct": "node examples/direct-api/fetch-example.js",
58
+ "example:axios": "node examples/direct-api/axios-example.js",
59
+ "example:file-upload": "node examples/file-upload/upload-example.js",
60
+ "example:streaming-test": "node examples/streaming-test.js",
61
+ "auth": "node scripts/auth.js",
62
+ "create-session-archive": "node scripts/createSessionArchive.js",
63
+ "extend-session": "node scripts/extendSession.js",
64
+ "check-sessions": "node scripts/checkSessions.js"
65
+ },
66
+ "dependencies": {
67
+ "adm-zip": "^0.5.10",
68
+ "ali-oss": "^6.18.0",
69
+ "axios": "^1.17.0",
70
+ "body-parser": "^1.20.2",
71
+ "dotenv": "^16.6.1",
72
+ "express": "^4.18.2",
73
+ "form-data": "^4.0.2",
74
+ "morgan": "^1.10.0",
75
+ "multer": "^2.0.0",
76
+ "node-fetch": "^3.3.2",
77
+ "openai": "^4.104.0",
78
+ "proxy-agent": "^8.0.1",
79
+ "puppeteer": "^24.31.0",
80
+ "puppeteer-extra": "^3.3.6",
81
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
82
+ "undici": "^6.21.1",
83
+ "winston": "^3.17.0"
84
+ }
85
+ }
@@ -0,0 +1,17 @@
1
+ # Файл API-ключей для прокси
2
+ # --------------------------------------------
3
+ # В этом файле перечислены токены, которые
4
+ # прокси будет считать «действительными».
5
+ # Один ключ — одна строка без пробелов.
6
+ #
7
+ # 1) Хотите ОТКЛЮЧИТЬ авторизацию целиком?
8
+ # Оставьте файл пустым — сервер перестанет
9
+ # проверять заголовок Authorization.
10
+ #
11
+ # 2) Хотите разрешить доступ нескольким людям?
12
+ # Впишите каждый ключ в отдельной строке:
13
+ # d35ab3e1-a6f9-4d...
14
+ # f2b1cd9c-1b2e-4a...
15
+ #
16
+ # Пустые строки и строки, начинающиеся с «#»,
17
+ # игнорируются.
@@ -0,0 +1,26 @@
1
+ # Qwen Models - Updated from API
2
+ # Last updated: 2026-05-31
3
+ # Total: 22 models from Qwen API v2
4
+
5
+ qwen3.6-plus
6
+ qwen3.7-max
7
+ qwen3.6-max-preview
8
+ qwen3.6-27b
9
+ qwen-latest-series-invite-beta-v24
10
+ qwen-latest-series-invite-beta-v16
11
+ qwen3.5-plus
12
+ qwen3.5-omni-plus
13
+ qwen3.6-35b-a3b
14
+ qwen3.5-flash
15
+ qwen3.5-max-2026-03-08
16
+ qwen3.6-plus-preview
17
+ qwen3.5-397b-a17b
18
+ qwen3.5-122b-a10b
19
+ qwen3.5-omni-flash
20
+ qwen3.5-27b
21
+ qwen3.5-35b-a3b
22
+ qwen3-max-2026-01-23
23
+ qwen-plus-2025-07-28
24
+ qwen3-coder-plus
25
+ qwen3-vl-plus
26
+ qwen3-omni-flash-2025-12-01