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/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +2054 -0
- package/bin/qwen-api-proxy.js +414 -0
- package/index.js +444 -0
- package/package.json +85 -0
- package/src/Authorization.txt +17 -0
- package/src/AvailableModels.txt +26 -0
- package/src/api/chat.js +1392 -0
- package/src/api/chatHistory.js +344 -0
- package/src/api/fileUpload.js +182 -0
- package/src/api/imageGeneration.js +459 -0
- package/src/api/modelMapping.js +274 -0
- package/src/api/routes.js +2160 -0
- package/src/api/tokenManager.js +382 -0
- package/src/browser/auth.js +171 -0
- package/src/browser/browser.js +233 -0
- package/src/browser/session.js +134 -0
- package/src/config.js +116 -0
- package/src/logger/index.js +89 -0
- package/src/utils/accountSetup.js +153 -0
- package/src/utils/botSettings.js +231 -0
- package/src/utils/permissionChecker.js +205 -0
- package/src/utils/prompt.js +11 -0
- package/src/utils/proxy.js +255 -0
- package/src/utils/telegramBot.js +2977 -0
- package/src/utils/telegramNotifier.js +94 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* qwen-api-proxy - CLI entry point for global installation
|
|
5
|
+
*
|
|
6
|
+
* This script:
|
|
7
|
+
* 1. Sets up the working directory structure in the current location
|
|
8
|
+
* 2. Creates necessary directories (session, logs, uploads, temp)
|
|
9
|
+
* 3. Creates .env.example if not exists
|
|
10
|
+
* 4. Passes control to the main index.js
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import { execSync } from 'child_process';
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
20
|
+
const WORKING_DIR = process.cwd();
|
|
21
|
+
|
|
22
|
+
// Parse command-line arguments early to get custom directory
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const dirFlag = args.find(arg => arg.startsWith('--dir='));
|
|
25
|
+
const customDir = dirFlag ? dirFlag.split('=')[1] : null;
|
|
26
|
+
|
|
27
|
+
// Use custom directory if specified
|
|
28
|
+
const EFFECTIVE_DIR = customDir ? path.resolve(customDir) : WORKING_DIR;
|
|
29
|
+
if (customDir) {
|
|
30
|
+
console.log(`š Using custom directory: ${EFFECTIVE_DIR}`);
|
|
31
|
+
if (!fs.existsSync(EFFECTIVE_DIR)) {
|
|
32
|
+
fs.mkdirSync(EFFECTIVE_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates required directories in the working directory
|
|
38
|
+
*/
|
|
39
|
+
function setupWorkingDirectory(targetDir = EFFECTIVE_DIR) {
|
|
40
|
+
const dirs = [
|
|
41
|
+
'session',
|
|
42
|
+
'session/accounts',
|
|
43
|
+
'session/history',
|
|
44
|
+
'logs',
|
|
45
|
+
'uploads',
|
|
46
|
+
'temp'
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
console.log('š Setting up working directory...');
|
|
50
|
+
|
|
51
|
+
dirs.forEach(dir => {
|
|
52
|
+
const dirPath = path.join(targetDir, dir);
|
|
53
|
+
if (!fs.existsSync(dirPath)) {
|
|
54
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
55
|
+
console.log(` ā Created ${dir}/`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(` ā ${dir}/ already exists`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Create .gitkeep for history
|
|
62
|
+
const gitkeepPath = path.join(targetDir, 'session', 'history', '.gitkeep');
|
|
63
|
+
if (!fs.existsSync(gitkeepPath)) {
|
|
64
|
+
fs.writeFileSync(gitkeepPath, '');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Creates .env file from example if it doesn't exist
|
|
70
|
+
*/
|
|
71
|
+
function setupEnvFile(targetDir = EFFECTIVE_DIR) {
|
|
72
|
+
const envPath = path.join(targetDir, '.env');
|
|
73
|
+
const envExamplePath = path.join(targetDir, '.env.example');
|
|
74
|
+
|
|
75
|
+
if (!fs.existsSync(envPath)) {
|
|
76
|
+
// Copy .env.example from package if it exists in working dir, otherwise create basic
|
|
77
|
+
const packageEnvExample = path.join(PACKAGE_ROOT, '.env.example');
|
|
78
|
+
|
|
79
|
+
if (fs.existsSync(packageEnvExample)) {
|
|
80
|
+
fs.copyFileSync(packageEnvExample, envExamplePath);
|
|
81
|
+
console.log(' ā Created .env.example');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Create empty .env if not exists
|
|
85
|
+
if (!fs.existsSync(envPath)) {
|
|
86
|
+
fs.writeFileSync(envPath, '# Qwen API Proxy Configuration\n# See .env.example for available options\n\n');
|
|
87
|
+
console.log(' ā Created .env (empty)');
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
console.log(' ā .env already exists');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates or validates root .gitignore file
|
|
96
|
+
*/
|
|
97
|
+
function setupGitignore(targetDir = EFFECTIVE_DIR) {
|
|
98
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
99
|
+
const requiredEntries = [
|
|
100
|
+
'session',
|
|
101
|
+
'logs',
|
|
102
|
+
'uploads',
|
|
103
|
+
'temp',
|
|
104
|
+
'session_backup',
|
|
105
|
+
'session_backup_*'
|
|
106
|
+
];
|
|
107
|
+
const missingEntries = [];
|
|
108
|
+
|
|
109
|
+
// Check which entries are missing from .gitignore
|
|
110
|
+
if (fs.existsSync(gitignorePath)) {
|
|
111
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
112
|
+
const lines = content.split('\n').map(line => line.trim());
|
|
113
|
+
requiredEntries.forEach(entry => {
|
|
114
|
+
if (!lines.includes(entry)) {
|
|
115
|
+
missingEntries.push(entry);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
// .gitignore doesn't exist - create it
|
|
120
|
+
const gitignoreContent = `# Qwen API Proxy - Auto-generated
|
|
121
|
+
# Sensitive data and runtime files
|
|
122
|
+
session
|
|
123
|
+
logs
|
|
124
|
+
uploads
|
|
125
|
+
temp
|
|
126
|
+
session_backup
|
|
127
|
+
|
|
128
|
+
# Backup archives
|
|
129
|
+
session_backup_*
|
|
130
|
+
*.zip
|
|
131
|
+
|
|
132
|
+
# Environment files
|
|
133
|
+
.env
|
|
134
|
+
.env.local
|
|
135
|
+
.env.*.local
|
|
136
|
+
|
|
137
|
+
# Logs
|
|
138
|
+
*.log
|
|
139
|
+
npm-debug.log*
|
|
140
|
+
|
|
141
|
+
# Runtime files
|
|
142
|
+
.restart_flag
|
|
143
|
+
.pending_archive
|
|
144
|
+
.last_telegram_update
|
|
145
|
+
`;
|
|
146
|
+
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
147
|
+
console.log(' ā Created .gitignore');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return missingEntries;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if zip is available
|
|
155
|
+
*/
|
|
156
|
+
function checkDependencies() {
|
|
157
|
+
try {
|
|
158
|
+
execSync('zip --version', { stdio: 'ignore' });
|
|
159
|
+
console.log(' ā zip command available');
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.warn(' ā ļø zip command not found (required for archive command)');
|
|
162
|
+
console.warn(' Install with: sudo apt install zip (Ubuntu) or brew install zip (macOS)');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Main setup function
|
|
168
|
+
*/
|
|
169
|
+
function setup() {
|
|
170
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
171
|
+
console.log('ā Qwen API Proxy - Working Directory Setup ā');
|
|
172
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
173
|
+
|
|
174
|
+
console.log(`š Working directory: ${EFFECTIVE_DIR}\n`);
|
|
175
|
+
|
|
176
|
+
setupWorkingDirectory();
|
|
177
|
+
console.log();
|
|
178
|
+
setupEnvFile();
|
|
179
|
+
console.log();
|
|
180
|
+
|
|
181
|
+
const missingGitignoreDirs = setupGitignore();
|
|
182
|
+
console.log();
|
|
183
|
+
|
|
184
|
+
checkDependencies();
|
|
185
|
+
|
|
186
|
+
console.log('\nā
Working directory setup complete!\n');
|
|
187
|
+
|
|
188
|
+
// Show warnings for missing .gitignore entries
|
|
189
|
+
if (missingGitignoreDirs.length > 0) {
|
|
190
|
+
console.warn('ā ļø WARNING: The following entries are not in .gitignore:');
|
|
191
|
+
missingGitignoreDirs.forEach(entry => {
|
|
192
|
+
console.warn(` - ${entry}`);
|
|
193
|
+
});
|
|
194
|
+
console.warn('\n Add them to .gitignore to prevent committing sensitive data!\n');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Run setup only on first run or when --setup flag is used
|
|
199
|
+
const needsSetup = args.includes('--setup') || !fs.existsSync(path.join(EFFECTIVE_DIR, 'session'));
|
|
200
|
+
|
|
201
|
+
// Parse command from arguments (already defined args above)
|
|
202
|
+
const command = args.find(arg => !arg.startsWith('-'));
|
|
203
|
+
|
|
204
|
+
// Handle 'init' command - manual setup only
|
|
205
|
+
if (command === 'init') {
|
|
206
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
207
|
+
console.log('ā Qwen API Proxy - Initialize Working Directory ā');
|
|
208
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
209
|
+
|
|
210
|
+
console.log(`š Working directory: ${EFFECTIVE_DIR}\n`);
|
|
211
|
+
|
|
212
|
+
setupWorkingDirectory(EFFECTIVE_DIR);
|
|
213
|
+
console.log();
|
|
214
|
+
setupEnvFile(EFFECTIVE_DIR);
|
|
215
|
+
console.log();
|
|
216
|
+
|
|
217
|
+
const missingGitignoreDirs = setupGitignore(EFFECTIVE_DIR);
|
|
218
|
+
console.log();
|
|
219
|
+
|
|
220
|
+
checkDependencies();
|
|
221
|
+
|
|
222
|
+
console.log('\nā
Working directory initialized successfully!\n');
|
|
223
|
+
console.log('š Next steps:');
|
|
224
|
+
console.log(' 1. Edit .env file with your configuration');
|
|
225
|
+
console.log(' 2. Run: qwen-api-proxy');
|
|
226
|
+
console.log('\n');
|
|
227
|
+
|
|
228
|
+
// Show warnings for missing .gitignore entries
|
|
229
|
+
if (missingGitignoreDirs.length > 0) {
|
|
230
|
+
console.warn('ā ļø WARNING: The following entries are not in .gitignore:');
|
|
231
|
+
missingGitignoreDirs.forEach(entry => {
|
|
232
|
+
console.warn(` - ${entry}`);
|
|
233
|
+
});
|
|
234
|
+
console.warn('\n Add them to .gitignore to prevent committing sensitive data!\n');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
process.exit(0);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Handle 'doctor' command - system health check
|
|
241
|
+
if (command === 'doctor') {
|
|
242
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
243
|
+
console.log('ā Qwen API Proxy - System Health Check ā');
|
|
244
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
245
|
+
|
|
246
|
+
const issues = [];
|
|
247
|
+
const warnings = [];
|
|
248
|
+
const ok = [];
|
|
249
|
+
|
|
250
|
+
// Check Node.js version
|
|
251
|
+
const nodeVersion = process.version;
|
|
252
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
253
|
+
if (majorVersion >= 18) {
|
|
254
|
+
ok.push(`Node.js ${nodeVersion} (ā version >= 18)`);
|
|
255
|
+
} else {
|
|
256
|
+
issues.push(`Node.js ${nodeVersion} (ā requires >= 18)`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check zip command
|
|
260
|
+
try {
|
|
261
|
+
execSync('zip --version', { stdio: 'ignore' });
|
|
262
|
+
ok.push('zip command available');
|
|
263
|
+
} catch (error) {
|
|
264
|
+
warnings.push('zip command not found (required for archive command)');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check working directory structure
|
|
268
|
+
const requiredDirs = ['session', 'session/accounts', 'session/history', 'logs', 'uploads', 'temp'];
|
|
269
|
+
requiredDirs.forEach(dir => {
|
|
270
|
+
const dirPath = path.join(EFFECTIVE_DIR, dir);
|
|
271
|
+
if (fs.existsSync(dirPath)) {
|
|
272
|
+
ok.push(`${dir}/ directory exists`);
|
|
273
|
+
} else {
|
|
274
|
+
warnings.push(`${dir}/ directory missing`);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Check .env file
|
|
279
|
+
const envPath = path.join(EFFECTIVE_DIR, '.env');
|
|
280
|
+
if (fs.existsSync(envPath)) {
|
|
281
|
+
ok.push('.env file exists');
|
|
282
|
+
|
|
283
|
+
// Check for common configurations
|
|
284
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
285
|
+
if (envContent.includes('TELEGRAM_BOT_TOKEN=') && !envContent.includes('TELEGRAM_BOT_TOKEN=your_bot_token_here')) {
|
|
286
|
+
ok.push('Telegram bot token configured');
|
|
287
|
+
} else {
|
|
288
|
+
warnings.push('Telegram bot token not configured (optional)');
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
warnings.push('.env file not found (run "qwen-api-proxy init" to create)');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check .gitignore file
|
|
295
|
+
const gitignorePath = path.join(EFFECTIVE_DIR, '.gitignore');
|
|
296
|
+
const requiredGitignoreEntries = [
|
|
297
|
+
'session',
|
|
298
|
+
'logs',
|
|
299
|
+
'uploads',
|
|
300
|
+
'temp',
|
|
301
|
+
'session_backup',
|
|
302
|
+
'session_backup_*'
|
|
303
|
+
];
|
|
304
|
+
const missingGitignoreEntries = [];
|
|
305
|
+
|
|
306
|
+
if (fs.existsSync(gitignorePath)) {
|
|
307
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
308
|
+
const lines = content.split('\n').map(line => line.trim());
|
|
309
|
+
requiredGitignoreEntries.forEach(entry => {
|
|
310
|
+
if (!lines.includes(entry)) {
|
|
311
|
+
missingGitignoreEntries.push(entry);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
if (missingGitignoreEntries.length === 0) {
|
|
316
|
+
ok.push('.gitignore properly configured');
|
|
317
|
+
} else {
|
|
318
|
+
warnings.push(`.gitignore missing: ${missingGitignoreEntries.join(', ')}`);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
warnings.push('.gitignore not found (run "qwen-api-proxy init" to create)');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Check permissions
|
|
325
|
+
const testDirs = ['session', 'logs', 'uploads'];
|
|
326
|
+
let permissionsOk = true;
|
|
327
|
+
testDirs.forEach(dir => {
|
|
328
|
+
const dirPath = path.join(EFFECTIVE_DIR, dir);
|
|
329
|
+
if (fs.existsSync(dirPath)) {
|
|
330
|
+
try {
|
|
331
|
+
const testFile = path.join(dirPath, '.write_test');
|
|
332
|
+
fs.writeFileSync(testFile, 'test');
|
|
333
|
+
fs.unlinkSync(testFile);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
issues.push(`${dir}/ - no write permission`);
|
|
336
|
+
permissionsOk = false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
if (permissionsOk) {
|
|
341
|
+
ok.push('Directory permissions OK');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Check session data
|
|
345
|
+
const tokensPath = path.join(EFFECTIVE_DIR, 'session', 'tokens.json');
|
|
346
|
+
if (fs.existsSync(tokensPath)) {
|
|
347
|
+
try {
|
|
348
|
+
const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf8'));
|
|
349
|
+
if (Array.isArray(tokens) && tokens.length > 0) {
|
|
350
|
+
ok.push(`${tokens.length} account(s) configured`);
|
|
351
|
+
} else {
|
|
352
|
+
warnings.push('No accounts configured (run server and add account)');
|
|
353
|
+
}
|
|
354
|
+
} catch (error) {
|
|
355
|
+
issues.push('tokens.json is corrupted');
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
warnings.push('No accounts configured yet');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Check disk space
|
|
362
|
+
try {
|
|
363
|
+
const dfOutput = execSync('df -k .', { encoding: 'utf8' });
|
|
364
|
+
const lines = dfOutput.split('\n');
|
|
365
|
+
if (lines.length >= 2) {
|
|
366
|
+
const parts = lines[1].split(/\s+/);
|
|
367
|
+
const availableKB = parseInt(parts[3]);
|
|
368
|
+
const availableGB = (availableKB / 1024 / 1024).toFixed(2);
|
|
369
|
+
if (availableGB > 1) {
|
|
370
|
+
ok.push(`Disk space: ${availableGB} GB available`);
|
|
371
|
+
} else {
|
|
372
|
+
warnings.push(`Low disk space: ${availableGB} GB available`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
} catch (error) {
|
|
376
|
+
// df not available, skip
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Print results
|
|
380
|
+
console.log('ā
OK:');
|
|
381
|
+
ok.forEach(msg => console.log(` ā ${msg}`));
|
|
382
|
+
|
|
383
|
+
if (warnings.length > 0) {
|
|
384
|
+
console.log('\nā ļø Warnings:');
|
|
385
|
+
warnings.forEach(msg => console.log(` ā ${msg}`));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (issues.length > 0) {
|
|
389
|
+
console.log('\nā Issues:');
|
|
390
|
+
issues.forEach(msg => console.log(` ā ${msg}`));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
console.log('\n' + '='.repeat(60));
|
|
394
|
+
if (issues.length === 0 && warnings.length === 0) {
|
|
395
|
+
console.log('š All checks passed! System is healthy.');
|
|
396
|
+
} else if (issues.length === 0) {
|
|
397
|
+
console.log(`ā System operational (${warnings.length} warning(s))`);
|
|
398
|
+
} else {
|
|
399
|
+
console.log(`ā ${issues.length} issue(s) found - please fix before running`);
|
|
400
|
+
}
|
|
401
|
+
console.log('='.repeat(60) + '\n');
|
|
402
|
+
|
|
403
|
+
process.exit(issues.length > 0 ? 1 : 0);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (needsSetup) {
|
|
407
|
+
setup();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Set environment variable to indicate we're running from global install
|
|
411
|
+
process.env.QWEN_API_PROXY_GLOBAL = 'true';
|
|
412
|
+
|
|
413
|
+
// Import and run the main application
|
|
414
|
+
await import(path.join(PACKAGE_ROOT, 'index.js'));
|