aicodeswitch 1.10.2 → 2.0.1
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/CHANGELOG.md +2 -0
- package/bin/cli.js +7 -9
- package/bin/restart.js +7 -229
- package/bin/restore.js +1 -1
- package/bin/start.js +75 -87
- package/bin/stop.js +77 -14
- package/bin/ui.js +19 -134
- package/bin/update.js +1 -1
- package/bin/utils/get-server.js +58 -0
- package/bin/utils/port-utils.js +118 -0
- package/bin/version.js +1 -1
- package/dist/server/database.js +196 -116
- package/dist/server/main.js +116 -22
- package/dist/server/proxy-server.js +334 -158
- package/dist/server/transformers/claude-openai.js +86 -3
- package/dist/server/transformers/streaming.js +4 -1
- package/dist/server/utils.js +16 -0
- package/dist/ui/assets/index-BLqGemLn.js +423 -0
- package/dist/ui/assets/index-IVPeH7yC.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/migration.md +7 -0
- package/package.json +3 -2
- package/public/migration.md +7 -0
- package/dist/ui/assets/index-D6RrKKB5.js +0 -391
- package/dist/ui/assets/index-DU8EG0kT.css +0 -1
package/dist/server/main.js
CHANGED
|
@@ -17,11 +17,13 @@ const cors_1 = __importDefault(require("cors"));
|
|
|
17
17
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
18
18
|
const fs_1 = __importDefault(require("fs"));
|
|
19
19
|
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
20
21
|
const database_1 = require("./database");
|
|
21
22
|
const proxy_server_1 = require("./proxy-server");
|
|
22
23
|
const os_1 = __importDefault(require("os"));
|
|
23
24
|
const auth_1 = require("./auth");
|
|
24
25
|
const version_check_1 = require("./version-check");
|
|
26
|
+
const utils_1 = require("./utils");
|
|
25
27
|
const dotenvPath = path_1.default.resolve(os_1.default.homedir(), '.aicodeswitch/aicodeswitch.conf');
|
|
26
28
|
if (fs_1.default.existsSync(dotenvPath)) {
|
|
27
29
|
dotenv_1.default.config({ path: dotenvPath });
|
|
@@ -343,18 +345,6 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
343
345
|
yield dbManager.clearLogs();
|
|
344
346
|
res.json(true);
|
|
345
347
|
})));
|
|
346
|
-
app.get('/api/access-logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
347
|
-
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
348
|
-
const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
|
|
349
|
-
const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
|
|
350
|
-
const offset = Number.isFinite(rawOffset) ? rawOffset : 0;
|
|
351
|
-
const logs = yield dbManager.getAccessLogs(limit, offset);
|
|
352
|
-
res.json(logs);
|
|
353
|
-
})));
|
|
354
|
-
app.delete('/api/access-logs', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
355
|
-
yield dbManager.clearAccessLogs();
|
|
356
|
-
res.json(true);
|
|
357
|
-
})));
|
|
358
348
|
app.get('/api/error-logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
359
349
|
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
360
350
|
const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
|
|
@@ -371,10 +361,6 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
371
361
|
const count = yield dbManager.getLogsCount();
|
|
372
362
|
res.json({ count });
|
|
373
363
|
})));
|
|
374
|
-
app.get('/api/access-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
375
|
-
const count = yield dbManager.getAccessLogsCount();
|
|
376
|
-
res.json({ count });
|
|
377
|
-
})));
|
|
378
364
|
app.get('/api/error-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
379
365
|
const count = yield dbManager.getErrorLogsCount();
|
|
380
366
|
res.json({ count });
|
|
@@ -432,6 +418,41 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
432
418
|
const stats = yield dbManager.getStatistics(days);
|
|
433
419
|
res.json(stats);
|
|
434
420
|
})));
|
|
421
|
+
// Sessions 相关端点
|
|
422
|
+
app.get('/api/sessions', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
423
|
+
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
424
|
+
const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
|
|
425
|
+
const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
|
|
426
|
+
const offset = Number.isFinite(rawOffset) ? rawOffset : 0;
|
|
427
|
+
const sessions = dbManager.getSessions(limit, offset);
|
|
428
|
+
res.json(sessions);
|
|
429
|
+
})));
|
|
430
|
+
app.get('/api/sessions/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
431
|
+
const count = dbManager.getSessionsCount();
|
|
432
|
+
res.json({ count });
|
|
433
|
+
})));
|
|
434
|
+
app.get('/api/sessions/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
435
|
+
const session = dbManager.getSession(req.params.id);
|
|
436
|
+
if (!session) {
|
|
437
|
+
res.status(404).json({ error: 'Session not found' });
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
res.json(session);
|
|
441
|
+
})));
|
|
442
|
+
app.get('/api/sessions/:id/logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
443
|
+
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
444
|
+
const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
|
|
445
|
+
const logs = yield dbManager.getLogsBySessionId(req.params.id, limit);
|
|
446
|
+
res.json(logs);
|
|
447
|
+
})));
|
|
448
|
+
app.delete('/api/sessions/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
449
|
+
const result = dbManager.deleteSession(req.params.id);
|
|
450
|
+
res.json(result);
|
|
451
|
+
})));
|
|
452
|
+
app.delete('/api/sessions', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
453
|
+
dbManager.clearSessions();
|
|
454
|
+
res.json(true);
|
|
455
|
+
})));
|
|
435
456
|
app.get('/api/docs/recommend-vendors', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
436
457
|
const resp = yield fetch('https://unpkg.com/aicodeswitch/docs/vendors-recommand.md');
|
|
437
458
|
if (!resp.ok) {
|
|
@@ -450,24 +471,97 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
450
471
|
const text = yield resp.text();
|
|
451
472
|
res.type('text/plain').send(text);
|
|
452
473
|
})));
|
|
453
|
-
|
|
474
|
+
// Migration 相关端点
|
|
475
|
+
const getMigrationHashPath = () => path_1.default.join(dataDir, '.migration-hash');
|
|
476
|
+
// 查找 migration.md 文件的路径
|
|
477
|
+
const findMigrationPath = () => {
|
|
478
|
+
// 可能的路径列表
|
|
479
|
+
const possiblePaths = [
|
|
480
|
+
// 开发环境:src/server/main.ts -> public/migration.md
|
|
481
|
+
path_1.default.resolve(__dirname, '../../public/migration.md'),
|
|
482
|
+
// 生产环境:dist/server/main.js -> dist/ui/migration.md
|
|
483
|
+
path_1.default.resolve(__dirname, '../ui/migration.md'),
|
|
484
|
+
];
|
|
485
|
+
for (const possiblePath of possiblePaths) {
|
|
486
|
+
if (fs_1.default.existsSync(possiblePath)) {
|
|
487
|
+
return possiblePath;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
};
|
|
492
|
+
app.get('/api/migration', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
493
|
+
try {
|
|
494
|
+
// 读取 migration.md 文件
|
|
495
|
+
const migrationPath = findMigrationPath();
|
|
496
|
+
if (!migrationPath) {
|
|
497
|
+
res.json({ shouldShow: false, content: '' });
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const content = fs_1.default.readFileSync(migrationPath, 'utf-8');
|
|
501
|
+
// 计算当前内容的 hash
|
|
502
|
+
const currentHash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
503
|
+
// 读取之前保存的 hash
|
|
504
|
+
const hashPath = getMigrationHashPath();
|
|
505
|
+
let savedHash = '';
|
|
506
|
+
if (fs_1.default.existsSync(hashPath)) {
|
|
507
|
+
savedHash = fs_1.default.readFileSync(hashPath, 'utf-8').trim();
|
|
508
|
+
}
|
|
509
|
+
// 如果 hash 不同,需要显示弹窗
|
|
510
|
+
const shouldShow = savedHash !== currentHash;
|
|
511
|
+
res.json({ shouldShow, content: shouldShow ? content : '' });
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
console.error('Failed to read migration file:', error);
|
|
515
|
+
res.json({ shouldShow: false, content: '' });
|
|
516
|
+
}
|
|
517
|
+
})));
|
|
518
|
+
app.post('/api/migration/ack', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
519
|
+
try {
|
|
520
|
+
// 读取 migration.md 文件并计算 hash
|
|
521
|
+
const migrationPath = findMigrationPath();
|
|
522
|
+
if (!migrationPath) {
|
|
523
|
+
res.json({ success: false });
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const content = fs_1.default.readFileSync(migrationPath, 'utf-8');
|
|
527
|
+
const hash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
528
|
+
// 保存 hash 到文件
|
|
529
|
+
const hashPath = getMigrationHashPath();
|
|
530
|
+
fs_1.default.writeFileSync(hashPath, hash, 'utf-8');
|
|
531
|
+
res.json({ success: true });
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
console.error('Failed to acknowledge migration:', error);
|
|
535
|
+
res.json({ success: false });
|
|
536
|
+
}
|
|
537
|
+
})));
|
|
454
538
|
};
|
|
455
539
|
const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
456
540
|
fs_1.default.mkdirSync(dataDir, { recursive: true });
|
|
457
541
|
const dbManager = new database_1.DatabaseManager(dataDir);
|
|
458
|
-
yield dbManager.initialize();
|
|
459
542
|
const proxyServer = new proxy_server_1.ProxyServer(dbManager, app);
|
|
543
|
+
yield dbManager.initialize();
|
|
544
|
+
// Initialize proxy server and register proxy routes last
|
|
545
|
+
proxyServer.initialize();
|
|
460
546
|
// Register admin routes first
|
|
461
547
|
registerRoutes(dbManager, proxyServer);
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const
|
|
548
|
+
yield proxyServer.registerProxyRoutes();
|
|
549
|
+
app.use(express_1.default.static(path_1.default.resolve(__dirname, '../ui')));
|
|
550
|
+
const isPortUsable = yield (0, utils_1.checkPortUsable)(port);
|
|
551
|
+
if (!isPortUsable) {
|
|
552
|
+
console.error(`端口 ${port} 已被占用,无法启动服务。请执行 aicos stop 后重启。`);
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
const server = app.listen(port, host, () => {
|
|
465
556
|
console.log(`Admin server running on http://${host}:${port}`);
|
|
466
557
|
});
|
|
467
558
|
const shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
468
559
|
console.log('Shutting down server...');
|
|
469
560
|
dbManager.close();
|
|
470
|
-
|
|
561
|
+
server.close(() => {
|
|
562
|
+
console.log('Server stopped.');
|
|
563
|
+
process.exit(0);
|
|
564
|
+
});
|
|
471
565
|
});
|
|
472
566
|
process.on('SIGINT', shutdown);
|
|
473
567
|
process.on('SIGTERM', shutdown);
|