aicodeswitch 2.1.5 → 3.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.
@@ -19,7 +19,7 @@ const fs_1 = __importDefault(require("fs"));
19
19
  const path_1 = __importDefault(require("path"));
20
20
  const crypto_1 = require("crypto");
21
21
  const https_proxy_agent_1 = require("https-proxy-agent");
22
- const database_1 = require("./database");
22
+ const database_factory_1 = require("./database-factory");
23
23
  const proxy_server_1 = require("./proxy-server");
24
24
  const os_1 = __importDefault(require("os"));
25
25
  const auth_1 = require("./auth");
@@ -33,7 +33,7 @@ const config_1 = require("./config");
33
33
  const appDir = path_1.default.join(os_1.default.homedir(), '.aicodeswitch');
34
34
  const dataDir = path_1.default.join(appDir, 'data');
35
35
  const dotenvPath = path_1.default.resolve(appDir, 'aicodeswitch.conf');
36
- const migrationHashFilePath = path_1.default.join(appDir, 'migration-hash');
36
+ const upgradeHashFilePath = path_1.default.join(appDir, 'upgrade-hash');
37
37
  if (fs_1.default.existsSync(dotenvPath)) {
38
38
  dotenv_1.default.config({ path: dotenvPath });
39
39
  }
@@ -742,14 +742,14 @@ const registerRoutes = (dbManager, proxyServer) => {
742
742
  app.put('/api/routes/:id', (req, res) => res.json(dbManager.updateRoute(req.params.id, req.body)));
743
743
  app.delete('/api/routes/:id', (req, res) => res.json(dbManager.deleteRoute(req.params.id)));
744
744
  app.post('/api/routes/:id/activate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
745
- const result = dbManager.activateRoute(req.params.id);
745
+ const result = yield dbManager.activateRoute(req.params.id);
746
746
  if (result) {
747
747
  yield proxyServer.reloadRoutes();
748
748
  }
749
749
  res.json(result);
750
750
  })));
751
751
  app.post('/api/routes/:id/deactivate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
752
- const result = dbManager.deactivateRoute(req.params.id);
752
+ const result = yield dbManager.deactivateRoute(req.params.id);
753
753
  if (result) {
754
754
  yield proxyServer.reloadRoutes();
755
755
  }
@@ -778,7 +778,7 @@ const registerRoutes = (dbManager, proxyServer) => {
778
778
  }
779
779
  // 步骤3:停用所有激活的路由
780
780
  console.log('[Deactivate All Routes] Deactivating all active routes...');
781
- const deactivatedCount = dbManager.deactivateAllRoutes();
781
+ const deactivatedCount = yield dbManager.deactivateAllRoutes();
782
782
  if (deactivatedCount > 0) {
783
783
  console.log(`[Deactivate All Routes] Deactivated ${deactivatedCount} route(s), reloading routes...`);
784
784
  yield proxyServer.reloadRoutes();
@@ -874,6 +874,10 @@ const registerRoutes = (dbManager, proxyServer) => {
874
874
  yield dbManager.clearErrorLogs();
875
875
  res.json(true);
876
876
  })));
877
+ app.delete('/api/statistics', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
878
+ yield dbManager.resetStatistics();
879
+ res.json(true);
880
+ })));
877
881
  app.get('/api/logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
878
882
  const count = yield dbManager.getLogsCount();
879
883
  res.json({ count });
@@ -885,7 +889,7 @@ const registerRoutes = (dbManager, proxyServer) => {
885
889
  app.get('/api/config', (_req, res) => res.json(dbManager.getConfig()));
886
890
  app.put('/api/config', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
887
891
  const config = req.body;
888
- const result = dbManager.updateConfig(config);
892
+ const result = yield dbManager.updateConfig(config);
889
893
  if (result) {
890
894
  yield proxyServer.updateConfig(config);
891
895
  updateProxyConfig(config);
@@ -1287,11 +1291,11 @@ ${instruction}
1287
1291
  const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
1288
1292
  const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
1289
1293
  const offset = Number.isFinite(rawOffset) ? rawOffset : 0;
1290
- const sessions = dbManager.getSessions(limit, offset);
1294
+ const sessions = yield dbManager.getSessions(undefined, limit, offset);
1291
1295
  res.json(sessions);
1292
1296
  })));
1293
1297
  app.get('/api/sessions/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1294
- const count = dbManager.getSessionsCount();
1298
+ const count = yield dbManager.getSessionsCount();
1295
1299
  res.json({ count });
1296
1300
  })));
1297
1301
  app.get('/api/sessions/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
@@ -1334,67 +1338,60 @@ ${instruction}
1334
1338
  const text = yield resp.text();
1335
1339
  res.type('text/plain').send(text);
1336
1340
  })));
1337
- // 查找 migration.md 文件的路径
1338
- const findMigrationPath = () => {
1339
- // 可能的路径列表
1340
- const possiblePaths = [
1341
- // 开发环境:src/server/main.ts -> public/migration.md
1342
- path_1.default.resolve(__dirname, '../../public/migration.md'),
1343
- // 生产环境:dist/server/main.js -> dist/ui/migration.md
1344
- path_1.default.resolve(__dirname, '../ui/migration.md'),
1345
- ];
1346
- for (const possiblePath of possiblePaths) {
1347
- if (fs_1.default.existsSync(possiblePath)) {
1348
- return possiblePath;
1349
- }
1341
+ app.get('/api/docs/upgrade', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1342
+ const resp = yield fetch('https://unpkg.com/aicodeswitch/docs/upgrade.md');
1343
+ if (!resp.ok) {
1344
+ res.status(500).send('');
1345
+ return;
1350
1346
  }
1351
- return null;
1352
- };
1353
- app.get('/api/migration', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1347
+ const text = yield resp.text();
1348
+ res.type('text/plain').send(text);
1349
+ })));
1350
+ app.get('/api/upgrade', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1354
1351
  try {
1355
- // 读取 migration.md 文件
1356
- const migrationPath = findMigrationPath();
1357
- if (!migrationPath) {
1352
+ // 读取 upgrade.md 文件
1353
+ const upgradePath = path_1.default.resolve(__dirname, '../../UPGRADE.md');
1354
+ if (!upgradePath) {
1358
1355
  res.json({ shouldShow: false, content: '' });
1359
1356
  return;
1360
1357
  }
1361
- const content = fs_1.default.readFileSync(migrationPath, 'utf-8').trim();
1358
+ const content = fs_1.default.readFileSync(upgradePath, 'utf-8').trim();
1362
1359
  // 计算当前内容的 hash
1363
1360
  const currentHash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
1364
1361
  // 如果 hash 文件不存在,说明是第一次安装
1365
- if (!fs_1.default.existsSync(migrationHashFilePath)) {
1362
+ if (!fs_1.default.existsSync(upgradeHashFilePath)) {
1366
1363
  // 第一次安装,直接保存当前 hash,不显示弹窗
1367
- fs_1.default.writeFileSync(migrationHashFilePath, currentHash, 'utf-8');
1364
+ fs_1.default.writeFileSync(upgradeHashFilePath, currentHash, 'utf-8');
1368
1365
  res.json({ shouldShow: false, content: '' });
1369
1366
  return;
1370
1367
  }
1371
1368
  // 读取已保存的 hash
1372
- const savedHash = fs_1.default.readFileSync(migrationHashFilePath, 'utf-8').trim();
1369
+ const savedHash = fs_1.default.readFileSync(upgradeHashFilePath, 'utf-8').trim();
1373
1370
  // 如果 hash 不同,需要显示弹窗
1374
1371
  const shouldShow = savedHash !== currentHash;
1375
1372
  res.json({ shouldShow, content: shouldShow ? content : '' });
1376
1373
  }
1377
1374
  catch (error) {
1378
- console.error('Failed to read migration file:', error);
1375
+ console.error('Failed to read upgrade file:', error);
1379
1376
  res.json({ shouldShow: false, content: '' });
1380
1377
  }
1381
1378
  })));
1382
- app.post('/api/migration/ack', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1379
+ app.post('/api/upgrade/ack', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1383
1380
  try {
1384
- // 读取 migration.md 文件并计算 hash
1385
- const migrationPath = findMigrationPath();
1386
- if (!migrationPath) {
1381
+ // 读取 upgrade.md 文件并计算 hash
1382
+ const upgradePath = path_1.default.resolve(__dirname, '../../UPGRADE.md');
1383
+ if (!upgradePath) {
1387
1384
  res.json({ success: false });
1388
1385
  return;
1389
1386
  }
1390
- const content = fs_1.default.readFileSync(migrationPath, 'utf-8').trim();
1387
+ const content = fs_1.default.readFileSync(upgradePath, 'utf-8').trim();
1391
1388
  const hash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
1392
1389
  // 保存 hash 到文件
1393
- fs_1.default.writeFileSync(migrationHashFilePath, hash, 'utf-8');
1390
+ fs_1.default.writeFileSync(upgradeHashFilePath, hash, 'utf-8');
1394
1391
  res.json({ success: true });
1395
1392
  }
1396
1393
  catch (error) {
1397
- console.error('Failed to acknowledge migration:', error);
1394
+ console.error('Failed to acknowledge upgrade:', error);
1398
1395
  res.json({ success: false });
1399
1396
  }
1400
1397
  })));
@@ -1414,9 +1411,10 @@ ${instruction}
1414
1411
  };
1415
1412
  const start = () => __awaiter(void 0, void 0, void 0, function* () {
1416
1413
  fs_1.default.mkdirSync(dataDir, { recursive: true });
1417
- const dbManager = new database_1.DatabaseManager(dataDir);
1418
- // 必须先初始化数据库,否则会报错
1419
- yield dbManager.initialize();
1414
+ // 自动检测数据库类型并执行迁移(如果需要)
1415
+ console.log('[Server] Initializing database...');
1416
+ const dbManager = yield database_factory_1.DatabaseFactory.createAuto(dataDir);
1417
+ console.log('[Server] Database initialized successfully');
1420
1418
  const proxyServer = new proxy_server_1.ProxyServer(dbManager, app);
1421
1419
  // Initialize proxy server and register proxy routes last
1422
1420
  proxyServer.initialize();
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.migrateToFileSystem = migrateToFileSystem;
49
+ exports.needsMigration = needsMigration;
50
+ exports.verifyMigration = verifyMigration;
51
+ const path_1 = __importDefault(require("path"));
52
+ const promises_1 = __importDefault(require("fs/promises"));
53
+ /**
54
+ * 数据库迁移工具
55
+ * 从 better-sqlite3 和 leveldb 迁移到文件系统数据库
56
+ */
57
+ function migrateToFileSystem(dataPath) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ console.log('[Migration] Starting migration to file system database...');
60
+ try {
61
+ // 动态导入旧数据库(如果存在)
62
+ const oldDbPath = path_1.default.join(dataPath, 'app.db');
63
+ const oldDbExists = yield promises_1.default.access(oldDbPath).then(() => true).catch(() => false);
64
+ if (!oldDbExists) {
65
+ console.log('[Migration] No old database found, skipping migration');
66
+ return;
67
+ }
68
+ // 尝试导入旧数据库模块(仅在有 SQLite 依赖时可用)
69
+ let DatabaseManager;
70
+ try {
71
+ const dbModule = yield Promise.resolve().then(() => __importStar(require('./database.js')));
72
+ DatabaseManager = dbModule.DatabaseManager;
73
+ }
74
+ catch (error) {
75
+ console.log('[Migration] Old database module not available');
76
+ console.log('[Migration] This is expected if better-sqlite3 is not installed');
77
+ console.log('[Migration] To migrate old data, please run: yarn add better-sqlite3 level');
78
+ console.log('[Migration] Then run the migration again');
79
+ return;
80
+ }
81
+ // 创建旧数据库实例
82
+ console.log('[Migration] Initializing old database...');
83
+ const oldDb = new DatabaseManager(dataPath);
84
+ yield oldDb.initialize();
85
+ // 导出核心数据
86
+ console.log('[Migration] Exporting core data from old database...');
87
+ const vendors = oldDb.getVendors();
88
+ const services = oldDb.getAPIServices();
89
+ const routes = oldDb.getRoutes();
90
+ const rules = oldDb.getRules();
91
+ const config = oldDb.getConfig();
92
+ console.log(`[Migration] Found ${vendors.length} vendors`);
93
+ console.log(`[Migration] Found ${services.length} services`);
94
+ console.log(`[Migration] Found ${routes.length} routes`);
95
+ console.log(`[Migration] Found ${rules.length} rules`);
96
+ // 保存核心数据到新的文件系统格式
97
+ console.log('[Migration] Saving core data to file system...');
98
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'vendors.json'), JSON.stringify(vendors, null, 2));
99
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'services.json'), JSON.stringify(services, null, 2));
100
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'routes.json'), JSON.stringify(routes, null, 2));
101
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'rules.json'), JSON.stringify(rules, null, 2));
102
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'config.json'), JSON.stringify(config, null, 2));
103
+ // 迁移 sessions(如果存在)
104
+ try {
105
+ console.log('[Migration] Migrating sessions...');
106
+ const sessions = oldDb.getSessions(10000, 0);
107
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'sessions.json'), JSON.stringify(sessions, null, 2));
108
+ console.log(`[Migration] Migrated ${sessions.length} sessions`);
109
+ }
110
+ catch (error) {
111
+ console.log('[Migration] Could not migrate sessions:', error);
112
+ // 创建空的 sessions 文件
113
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'sessions.json'), JSON.stringify([], null, 2));
114
+ }
115
+ // 迁移请求日志(限制数量以避免文件过大)
116
+ try {
117
+ console.log('[Migration] Migrating request logs (last 5000)...');
118
+ const logs = yield oldDb.getLogs(5000, 0);
119
+ // 完整迁移所有日志,不进行任何截断
120
+ if (logs.length > 0) {
121
+ // 修复字段名:将 response 改为 responseBody
122
+ const cleanedLogs = logs.map((log) => (Object.assign(Object.assign({}, log), {
123
+ // 兼容旧的 response 字段名,重命名为 responseBody
124
+ responseBody: log.responseBody || log.response,
125
+ // 移除旧的 response 字段(如果存在)
126
+ response: undefined })));
127
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'logs.json'), JSON.stringify(cleanedLogs, null, 2));
128
+ console.log(`[Migration] Migrated ${logs.length} request logs`);
129
+ }
130
+ else {
131
+ // 创建空的日志文件
132
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'logs.json'), JSON.stringify([], null, 2));
133
+ console.log('[Migration] No request logs to migrate');
134
+ }
135
+ }
136
+ catch (error) {
137
+ console.log('[Migration] Could not migrate logs:', error instanceof Error ? error.message : error);
138
+ // 创建空的日志文件
139
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'logs.json'), JSON.stringify([], null, 2));
140
+ }
141
+ // 迁移错误日志
142
+ try {
143
+ console.log('[Migration] Migrating error logs (last 1000)...');
144
+ const errorLogs = yield oldDb.getErrorLogs(1000, 0);
145
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'error-logs.json'), JSON.stringify(errorLogs, null, 2));
146
+ console.log(`[Migration] Migrated ${errorLogs.length} error logs`);
147
+ }
148
+ catch (error) {
149
+ console.log('[Migration] Could not migrate error logs:', error);
150
+ // 创建空的错误日志文件
151
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'error-logs.json'), JSON.stringify([], null, 2));
152
+ }
153
+ // 创建空的黑名单文件
154
+ yield promises_1.default.writeFile(path_1.default.join(dataPath, 'blacklist.json'), JSON.stringify([], null, 2));
155
+ // 关闭旧数据库连接
156
+ try {
157
+ console.log('[Migration] Closing old database connections...');
158
+ oldDb.close();
159
+ // 等待一下确保文件句柄被释放
160
+ yield new Promise(resolve => setTimeout(resolve, 1000));
161
+ console.log('[Migration] Old database connections closed');
162
+ }
163
+ catch (error) {
164
+ console.log('[Migration] Warning: Could not close old database:', error instanceof Error ? error.message : error);
165
+ }
166
+ // 保留原始数据库文件,不进行备份或重命名
167
+ // 这样如果迁移失败,用户可以使用老版本继续运行
168
+ console.log('[Migration] ✅ Migration data export completed successfully!');
169
+ console.log('[Migration] Original database files preserved for rollback');
170
+ }
171
+ catch (error) {
172
+ console.error('[Migration] ❌ Migration failed:', error);
173
+ console.error('[Migration] Stack trace:', error.stack);
174
+ throw error;
175
+ }
176
+ });
177
+ }
178
+ /**
179
+ * 检查是否需要迁移
180
+ * @param dataPath 数据目录路径
181
+ * @returns 是否需要迁移
182
+ */
183
+ function needsMigration(dataPath) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ try {
186
+ // 检查是否存在旧的 SQLite 数据库
187
+ const oldDbPath = path_1.default.join(dataPath, 'app.db');
188
+ const oldDbExists = yield promises_1.default.access(oldDbPath).then(() => true).catch(() => false);
189
+ if (!oldDbExists) {
190
+ return false;
191
+ }
192
+ // 检查是否已经存在新的文件系统数据库
193
+ const newDbPath = path_1.default.join(dataPath, 'config.json');
194
+ const newDbExists = yield promises_1.default.access(newDbPath).then(() => true).catch(() => false);
195
+ // 如果旧数据库存在且新数据库不存在,则需要迁移
196
+ return oldDbExists && !newDbExists;
197
+ }
198
+ catch (error) {
199
+ console.error('[Migration] Error checking migration status:', error);
200
+ return false;
201
+ }
202
+ });
203
+ }
204
+ /**
205
+ * 验证迁移结果
206
+ * @param dataPath 数据目录路径
207
+ * @returns 验证结果
208
+ */
209
+ function verifyMigration(dataPath) {
210
+ return __awaiter(this, void 0, void 0, function* () {
211
+ const errors = [];
212
+ const warnings = [];
213
+ try {
214
+ // 检查必需的文件
215
+ const requiredFiles = [
216
+ 'vendors.json',
217
+ 'services.json',
218
+ 'routes.json',
219
+ 'rules.json',
220
+ 'config.json',
221
+ 'sessions.json',
222
+ 'logs.json',
223
+ 'error-logs.json',
224
+ 'blacklist.json',
225
+ ];
226
+ for (const file of requiredFiles) {
227
+ const filePath = path_1.default.join(dataPath, file);
228
+ try {
229
+ yield promises_1.default.access(filePath);
230
+ // 尝试解析 JSON
231
+ const content = yield promises_1.default.readFile(filePath, 'utf-8');
232
+ JSON.parse(content);
233
+ }
234
+ catch (error) {
235
+ errors.push(`File ${file} is missing or invalid`);
236
+ }
237
+ }
238
+ return {
239
+ success: errors.length === 0,
240
+ errors,
241
+ warnings,
242
+ };
243
+ }
244
+ catch (error) {
245
+ errors.push(`Verification failed: ${error.message}`);
246
+ return {
247
+ success: false,
248
+ errors,
249
+ warnings,
250
+ };
251
+ }
252
+ });
253
+ }
@@ -198,7 +198,7 @@ class ProxyServer {
198
198
  method: req.method,
199
199
  path: req.path,
200
200
  headers: this.normalizeHeaders(req.headers),
201
- body: req.body ? JSON.stringify(req.body) : undefined,
201
+ body: req.body,
202
202
  error: (lastError === null || lastError === void 0 ? void 0 : lastError.message) || 'All services failed',
203
203
  });
204
204
  }
@@ -246,7 +246,7 @@ class ProxyServer {
246
246
  method: req.method,
247
247
  path: req.path,
248
248
  headers: this.normalizeHeaders(req.headers),
249
- body: req.body ? JSON.stringify(req.body) : undefined,
249
+ body: req.body,
250
250
  error: error.message,
251
251
  });
252
252
  }
@@ -379,7 +379,7 @@ class ProxyServer {
379
379
  method: req.method,
380
380
  path: req.path,
381
381
  headers: this.normalizeHeaders(req.headers),
382
- body: req.body ? JSON.stringify(req.body) : undefined,
382
+ body: req.body,
383
383
  error: (lastError === null || lastError === void 0 ? void 0 : lastError.message) || 'All services failed',
384
384
  });
385
385
  }
@@ -425,7 +425,7 @@ class ProxyServer {
425
425
  method: req.method,
426
426
  path: req.path,
427
427
  headers: this.normalizeHeaders(req.headers),
428
- body: req.body ? JSON.stringify(req.body) : undefined,
428
+ body: req.body,
429
429
  error: error.message,
430
430
  });
431
431
  }
@@ -1285,6 +1285,7 @@ class ProxyServer {
1285
1285
  * 提取会话标题(默认方法)
1286
1286
  * 对于新会话,尝试从第一条消息的内容中提取标题
1287
1287
  * 优化:使用第一条用户消息的完整内容,并智能截取
1288
+ * 对于结构化内容(数组),从最后一个元素取值
1288
1289
  */
1289
1290
  defaultExtractSessionTitle(request, sessionId) {
1290
1291
  var _a;
@@ -1304,11 +1305,19 @@ class ProxyServer {
1304
1305
  if (typeof content === 'string') {
1305
1306
  rawText = content;
1306
1307
  }
1307
- else if (Array.isArray(content)) {
1308
+ else if (Array.isArray(content) && content.length > 0) {
1308
1309
  // 处理结构化内容(如图片+文本)
1309
- const textBlock = content.find((block) => (block === null || block === void 0 ? void 0 : block.type) === 'text');
1310
- if (textBlock === null || textBlock === void 0 ? void 0 : textBlock.text) {
1311
- rawText = textBlock.text;
1310
+ // 从最后一个元素取值,通常最后的文本才是真正的用户输入
1311
+ const lastBlock = content[content.length - 1];
1312
+ if ((lastBlock === null || lastBlock === void 0 ? void 0 : lastBlock.type) === 'text' && (lastBlock === null || lastBlock === void 0 ? void 0 : lastBlock.text)) {
1313
+ rawText = lastBlock.text;
1314
+ }
1315
+ else {
1316
+ // 如果最后一个不是 text 类型,尝试找到第一个 text 类型作为备用
1317
+ const textBlock = content.find((block) => (block === null || block === void 0 ? void 0 : block.type) === 'text');
1318
+ if (textBlock === null || textBlock === void 0 ? void 0 : textBlock.text) {
1319
+ rawText = textBlock.text;
1320
+ }
1312
1321
  }
1313
1322
  }
1314
1323
  if (rawText) {
@@ -1422,7 +1431,7 @@ class ProxyServer {
1422
1431
  method: req.method,
1423
1432
  path: req.path,
1424
1433
  headers: this.normalizeHeaders(req.headers),
1425
- body: req.body ? JSON.stringify(req.body) : undefined,
1434
+ body: req.body,
1426
1435
  statusCode,
1427
1436
  responseTime: Date.now() - startTime,
1428
1437
  targetProvider: service.name,
@@ -1607,7 +1616,8 @@ class ProxyServer {
1607
1616
  const serializer = new streaming_1.SSESerializerTransform();
1608
1617
  // 收集响应头
1609
1618
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
1610
- res.on('finish', () => {
1619
+ // 监听事件收集器的完成事件,确保所有chunks都被收集
1620
+ const finalizeChunks = () => {
1611
1621
  const usage = converter.getUsage();
1612
1622
  if (usage) {
1613
1623
  usageForLog = (0, claude_openai_1.extractTokenUsageFromClaudeUsage)(usage);
@@ -1621,8 +1631,24 @@ class ProxyServer {
1621
1631
  }
1622
1632
  // 收集stream chunks(每个chunk是一个完整的SSE事件)
1623
1633
  streamChunksForLog = eventCollector.getChunks();
1634
+ // 将所有 chunks 合并成完整的响应体用于日志记录
1635
+ responseBodyForLog = streamChunksForLog.join('\n');
1624
1636
  console.log('[Proxy] Stream request finished, collected chunks:', (streamChunksForLog === null || streamChunksForLog === void 0 ? void 0 : streamChunksForLog.length) || 0);
1637
+ console.log('[Proxy] Response body length:', (responseBodyForLog === null || responseBodyForLog === void 0 ? void 0 : responseBodyForLog.length) || 0);
1625
1638
  void finalizeLog(res.statusCode);
1639
+ };
1640
+ // 在pipeline完成且eventCollector flush后执行
1641
+ eventCollector.on('finish', () => {
1642
+ console.log('[Proxy] EventCollector finished, collecting chunks...');
1643
+ finalizeChunks();
1644
+ });
1645
+ // 备用:如果eventCollector的finish没有触发,监听res的finish
1646
+ res.on('finish', () => {
1647
+ console.log('[Proxy] Response finished');
1648
+ if (!streamChunksForLog) {
1649
+ console.log('[Proxy] Chunks not collected yet, forcing collection...');
1650
+ finalizeChunks();
1651
+ }
1626
1652
  });
1627
1653
  // 监听 res 的错误事件
1628
1654
  res.on('error', (err) => {
@@ -1693,7 +1719,8 @@ class ProxyServer {
1693
1719
  const converter = new streaming_1.ClaudeToOpenAIChatEventTransform({ model: requestBody === null || requestBody === void 0 ? void 0 : requestBody.model });
1694
1720
  const serializer = new streaming_1.SSESerializerTransform();
1695
1721
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
1696
- res.on('finish', () => {
1722
+ // 监听事件收集器的完成事件,确保所有chunks都被收集
1723
+ const finalizeChunks = () => {
1697
1724
  const usage = converter.getUsage();
1698
1725
  if (usage) {
1699
1726
  usageForLog = (0, claude_openai_1.extractTokenUsageFromOpenAIUsage)(usage);
@@ -1706,8 +1733,24 @@ class ProxyServer {
1706
1733
  }
1707
1734
  }
1708
1735
  streamChunksForLog = eventCollector.getChunks();
1736
+ // 将所有 chunks 合并成完整的响应体用于日志记录
1737
+ responseBodyForLog = streamChunksForLog.join('\n');
1709
1738
  console.log('[Proxy] Codex stream request finished, collected chunks:', (streamChunksForLog === null || streamChunksForLog === void 0 ? void 0 : streamChunksForLog.length) || 0);
1739
+ console.log('[Proxy] Response body length:', (responseBodyForLog === null || responseBodyForLog === void 0 ? void 0 : responseBodyForLog.length) || 0);
1710
1740
  void finalizeLog(res.statusCode);
1741
+ };
1742
+ // 在pipeline完成且eventCollector flush后执行
1743
+ eventCollector.on('finish', () => {
1744
+ console.log('[Proxy] EventCollector finished (codex), collecting chunks...');
1745
+ finalizeChunks();
1746
+ });
1747
+ // 备用:如果eventCollector的finish没有触发,监听res的finish
1748
+ res.on('finish', () => {
1749
+ console.log('[Proxy] Response finished (codex)');
1750
+ if (!streamChunksForLog) {
1751
+ console.log('[Proxy] Chunks not collected yet, forcing collection...');
1752
+ finalizeChunks();
1753
+ }
1711
1754
  });
1712
1755
  // 监听 res 的错误事件
1713
1756
  res.on('error', (err) => {
@@ -1766,23 +1809,43 @@ class ProxyServer {
1766
1809
  return;
1767
1810
  }
1768
1811
  // 默认stream处理(无转换)
1812
+ const parser = new streaming_1.SSEParserTransform();
1769
1813
  const eventCollector = new chunk_collector_1.SSEEventCollectorTransform();
1814
+ const serializer = new streaming_1.SSESerializerTransform();
1770
1815
  responseHeadersForLog = this.normalizeResponseHeaders(responseHeaders);
1771
1816
  this.copyResponseHeaders(responseHeaders, res);
1772
- // 监听 res 的错误事件
1773
- res.on('error', (err) => {
1774
- console.error('[Proxy] Response stream error:', err);
1775
- });
1776
- res.on('finish', () => {
1817
+ // 监听事件收集器的完成事件,确保所有chunks都被收集
1818
+ const finalizeChunks = () => {
1777
1819
  streamChunksForLog = eventCollector.getChunks();
1820
+ // 将所有 chunks 合并成完整的响应体用于日志记录
1821
+ responseBodyForLog = streamChunksForLog.join('\n');
1778
1822
  // 尝试从event collector中提取usage信息
1779
1823
  const extractedUsage = eventCollector.extractUsage();
1780
1824
  if (extractedUsage) {
1781
1825
  usageForLog = this.extractTokenUsage(extractedUsage);
1782
1826
  }
1827
+ console.log('[Proxy] Default stream request finished, collected chunks:', (streamChunksForLog === null || streamChunksForLog === void 0 ? void 0 : streamChunksForLog.length) || 0);
1828
+ console.log('[Proxy] Response body length:', (responseBodyForLog === null || responseBodyForLog === void 0 ? void 0 : responseBodyForLog.length) || 0);
1783
1829
  void finalizeLog(res.statusCode);
1830
+ };
1831
+ // 在pipeline完成且eventCollector flush后执行
1832
+ eventCollector.on('finish', () => {
1833
+ console.log('[Proxy] EventCollector finished (default stream), collecting chunks...');
1834
+ finalizeChunks();
1835
+ });
1836
+ // 备用:如果eventCollector的finish没有触发,监听res的finish
1837
+ res.on('finish', () => {
1838
+ console.log('[Proxy] Response finished (default stream)');
1839
+ if (!streamChunksForLog) {
1840
+ console.log('[Proxy] Chunks not collected yet, forcing collection...');
1841
+ finalizeChunks();
1842
+ }
1843
+ });
1844
+ // 监听 res 的错误事件
1845
+ res.on('error', (err) => {
1846
+ console.error('[Proxy] Response stream error:', err);
1784
1847
  });
1785
- (0, stream_1.pipeline)(response.data, eventCollector, res, (error) => __awaiter(this, void 0, void 0, function* () {
1848
+ (0, stream_1.pipeline)(response.data, parser, eventCollector, serializer, res, (error) => __awaiter(this, void 0, void 0, function* () {
1786
1849
  if (error) {
1787
1850
  console.error('[Proxy] Pipeline error (default stream):', error);
1788
1851
  // 记录到错误日志
@@ -203,7 +203,8 @@ class SSEEventCollectorTransform extends stream_1.Transform {
203
203
  this.currentEvent = { dataLines: [], rawLines: [] };
204
204
  return;
205
205
  }
206
- const raw = this.currentEvent.rawLines.join('\n');
206
+ // SSE格式要求事件以空行结束,所以添加一个空行
207
+ const raw = this.currentEvent.rawLines.join('\n') + '\n';
207
208
  const event = {
208
209
  event: this.currentEvent.event,
209
210
  id: this.currentEvent.id,