aicodeswitch 3.9.4 → 4.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/README.md +37 -36
- package/UPGRADE.md +5 -3
- package/bin/restore.js +126 -22
- package/bin/start.js +29 -41
- package/bin/stop.js +3 -3
- package/bin/utils/config-helpers.js +198 -0
- package/dist/server/config-managed-fields.js +69 -0
- package/dist/server/config-merge.js +260 -0
- package/dist/server/database-factory.js +11 -181
- package/dist/server/fs-database.js +449 -78
- package/dist/server/main.js +392 -292
- package/dist/server/original-config-reader.js +154 -88
- package/dist/server/proxy-server.js +1108 -439
- package/dist/server/rules-status-service.js +241 -127
- package/dist/server/transformers/chunk-collector.js +64 -9
- package/dist/server/transformers/streaming.js +2357 -285
- package/dist/server/transformers/transformers.js +1767 -0
- package/dist/server/version-check.js +3 -2
- package/dist/ui/assets/index-D4Fimqi6.js +514 -0
- package/dist/ui/index.html +1 -1
- package/package.json +4 -8
- package/schema/claude.schema.md +15 -14
- package/schema/deepseek-chat.schema.md +799 -0
- package/schema/{openai.schema.md → openai-chat-completions.schema.md} +9 -1083
- package/schema/openai-responses.schema.md +226196 -0
- package/schema/stream.md +2592 -0
- package/dist/server/database.js +0 -1609
- package/dist/server/migrate-to-fs.js +0 -353
- package/dist/server/transformers/claude-openai.js +0 -868
- package/dist/server/transformers/gemini.js +0 -625
- package/dist/ui/assets/index-BqSYpNgU.js +0 -511
|
@@ -29,6 +29,19 @@ const promises_1 = __importDefault(require("fs/promises"));
|
|
|
29
29
|
const crypto_1 = __importDefault(require("crypto"));
|
|
30
30
|
const crypto_js_1 = __importDefault(require("crypto-js"));
|
|
31
31
|
const type_migration_1 = require("./type-migration");
|
|
32
|
+
const VALID_CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
|
|
33
|
+
const DEFAULT_CODEX_REASONING_EFFORT = 'high';
|
|
34
|
+
const DEFAULT_FAILOVER_RECOVERY_SECONDS = 30;
|
|
35
|
+
const isCodexReasoningEffort = (value) => {
|
|
36
|
+
return typeof value === 'string' && VALID_CODEX_REASONING_EFFORTS.includes(value);
|
|
37
|
+
};
|
|
38
|
+
const normalizeFailoverRecoverySeconds = (value) => {
|
|
39
|
+
const parsed = typeof value === 'number' ? value : Number(value);
|
|
40
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
41
|
+
return DEFAULT_FAILOVER_RECOVERY_SECONDS;
|
|
42
|
+
}
|
|
43
|
+
return Math.floor(parsed);
|
|
44
|
+
};
|
|
32
45
|
/**
|
|
33
46
|
* 基于文件系统的数据库管理器
|
|
34
47
|
* 使用 JSON 文件存储数据,无需编译依赖
|
|
@@ -43,6 +56,7 @@ class FileSystemDatabaseManager {
|
|
|
43
56
|
get sessionsFile() { return path_1.default.join(this.dataPath, 'sessions.json'); }
|
|
44
57
|
get logsDir() { return path_1.default.join(this.dataPath, 'logs'); }
|
|
45
58
|
get logsIndexFile() { return path_1.default.join(this.dataPath, 'logs-index.json'); }
|
|
59
|
+
get sessionLogIndexFile() { return path_1.default.join(this.dataPath, 'session-log-index.json'); }
|
|
46
60
|
get errorLogsFile() { return path_1.default.join(this.dataPath, 'error-logs.json'); }
|
|
47
61
|
get blacklistFile() { return path_1.default.join(this.dataPath, 'blacklist.json'); }
|
|
48
62
|
get statisticsFile() { return path_1.default.join(this.dataPath, 'statistics.json'); }
|
|
@@ -120,6 +134,42 @@ class FileSystemDatabaseManager {
|
|
|
120
134
|
writable: true,
|
|
121
135
|
value: []
|
|
122
136
|
});
|
|
137
|
+
Object.defineProperty(this, "sessionLogIndex", {
|
|
138
|
+
enumerable: true,
|
|
139
|
+
configurable: true,
|
|
140
|
+
writable: true,
|
|
141
|
+
value: new Map()
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperty(this, "sessionLogIndexDirty", {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
configurable: true,
|
|
146
|
+
writable: true,
|
|
147
|
+
value: false
|
|
148
|
+
});
|
|
149
|
+
Object.defineProperty(this, "sessionLogIndexDirtyCount", {
|
|
150
|
+
enumerable: true,
|
|
151
|
+
configurable: true,
|
|
152
|
+
writable: true,
|
|
153
|
+
value: 0
|
|
154
|
+
});
|
|
155
|
+
Object.defineProperty(this, "sessionLogIndexFlushTimer", {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
configurable: true,
|
|
158
|
+
writable: true,
|
|
159
|
+
value: null
|
|
160
|
+
});
|
|
161
|
+
Object.defineProperty(this, "SESSION_LOG_INDEX_FLUSH_DELAY", {
|
|
162
|
+
enumerable: true,
|
|
163
|
+
configurable: true,
|
|
164
|
+
writable: true,
|
|
165
|
+
value: 3000
|
|
166
|
+
});
|
|
167
|
+
Object.defineProperty(this, "SESSION_LOG_INDEX_FLUSH_THRESHOLD", {
|
|
168
|
+
enumerable: true,
|
|
169
|
+
configurable: true,
|
|
170
|
+
writable: true,
|
|
171
|
+
value: 50
|
|
172
|
+
});
|
|
123
173
|
Object.defineProperty(this, "errorLogs", {
|
|
124
174
|
enumerable: true,
|
|
125
175
|
configurable: true,
|
|
@@ -209,8 +259,8 @@ class FileSystemDatabaseManager {
|
|
|
209
259
|
yield this.loadAllData();
|
|
210
260
|
// 执行数据源类型迁移(在加载数据之后)
|
|
211
261
|
yield this.migrateSourceTypes();
|
|
212
|
-
//
|
|
213
|
-
yield this.
|
|
262
|
+
// 路由级工具配置迁移到全局配置(兼容旧版本)
|
|
263
|
+
yield this.migrateRouteToolSettingsToGlobalConfig();
|
|
214
264
|
// 确保默认配置
|
|
215
265
|
yield this.ensureDefaultConfig();
|
|
216
266
|
});
|
|
@@ -229,19 +279,43 @@ class FileSystemDatabaseManager {
|
|
|
229
279
|
this.loadStatistics(),
|
|
230
280
|
this.loadMCPs(),
|
|
231
281
|
]);
|
|
282
|
+
// 会话日志索引依赖 logShardsIndex,必须在 loadLogsIndex 之后
|
|
283
|
+
yield this.loadSessionLogIndex();
|
|
232
284
|
});
|
|
233
285
|
}
|
|
234
286
|
loadVendors() {
|
|
235
287
|
return __awaiter(this, void 0, void 0, function* () {
|
|
288
|
+
let needSave = false;
|
|
236
289
|
try {
|
|
237
290
|
const data = yield promises_1.default.readFile(this.vendorsFile, 'utf-8');
|
|
238
|
-
|
|
291
|
+
const parsed = JSON.parse(data);
|
|
292
|
+
this.vendors = Array.isArray(parsed) ? parsed.map((vendor) => {
|
|
293
|
+
const normalizedServices = Array.isArray(vendor.services)
|
|
294
|
+
? vendor.services.map((service) => {
|
|
295
|
+
const normalizedService = Object.assign(Object.assign({}, service), { apiKey: typeof service.apiKey === 'string' ? service.apiKey : '', inheritVendorApiKey: service.inheritVendorApiKey === true });
|
|
296
|
+
if (normalizedService.apiKey !== service.apiKey ||
|
|
297
|
+
normalizedService.inheritVendorApiKey !== service.inheritVendorApiKey) {
|
|
298
|
+
needSave = true;
|
|
299
|
+
}
|
|
300
|
+
return normalizedService;
|
|
301
|
+
})
|
|
302
|
+
: [];
|
|
303
|
+
const normalizedVendor = Object.assign(Object.assign({}, vendor), { apiKey: typeof vendor.apiKey === 'string' ? vendor.apiKey : '', services: normalizedServices });
|
|
304
|
+
if (normalizedVendor.apiKey !== vendor.apiKey ||
|
|
305
|
+
!Array.isArray(vendor.services)) {
|
|
306
|
+
needSave = true;
|
|
307
|
+
}
|
|
308
|
+
return normalizedVendor;
|
|
309
|
+
}) : [];
|
|
239
310
|
}
|
|
240
311
|
catch (_a) {
|
|
241
312
|
this.vendors = [];
|
|
242
313
|
}
|
|
243
314
|
// 兼容性检查:如果存在旧的 services.json,自动迁移
|
|
244
315
|
yield this.migrateServicesIfNeeded();
|
|
316
|
+
if (needSave) {
|
|
317
|
+
yield this.saveVendors();
|
|
318
|
+
}
|
|
245
319
|
});
|
|
246
320
|
}
|
|
247
321
|
/**
|
|
@@ -359,41 +433,6 @@ class FileSystemDatabaseManager {
|
|
|
359
433
|
console.log(`[TypeMigration] Migration completed. Migrated ${migratedCount} services.`);
|
|
360
434
|
});
|
|
361
435
|
}
|
|
362
|
-
/**
|
|
363
|
-
* 迁移 OpenAI base URL(在初始化时执行)
|
|
364
|
-
* 仅处理 sourceType=openai 且 apiUrl 末尾为 /v1 的服务
|
|
365
|
-
*/
|
|
366
|
-
migrateOpenAIBaseUrls() {
|
|
367
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
368
|
-
console.log('[OpenAIBaseUrlMigration] Checking for OpenAI base URL migration...');
|
|
369
|
-
let migratedCount = 0;
|
|
370
|
-
for (const vendor of this.vendors) {
|
|
371
|
-
if (!vendor.services)
|
|
372
|
-
continue;
|
|
373
|
-
for (const service of vendor.services) {
|
|
374
|
-
if (service.sourceType !== 'openai' || typeof service.apiUrl !== 'string') {
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
const trimmedUrl = service.apiUrl.trim();
|
|
378
|
-
if (!/\/v1\/?$/i.test(trimmedUrl)) {
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
const migratedUrl = trimmedUrl.replace(/\/v1\/?$/i, '');
|
|
382
|
-
if (migratedUrl && migratedUrl !== service.apiUrl) {
|
|
383
|
-
console.log(`[OpenAIBaseUrlMigration] Migrated service "${service.name}": ${service.apiUrl} -> ${migratedUrl}`);
|
|
384
|
-
service.apiUrl = migratedUrl;
|
|
385
|
-
migratedCount++;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (migratedCount === 0) {
|
|
390
|
-
console.log('[OpenAIBaseUrlMigration] No migration needed');
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
yield this.saveVendors();
|
|
394
|
-
console.log(`[OpenAIBaseUrlMigration] Migration completed. Migrated ${migratedCount} services.`);
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
436
|
/**
|
|
398
437
|
* 迁移导入数据中的类型
|
|
399
438
|
* 用于导入功能,自动将旧类型转换为新类型
|
|
@@ -402,11 +441,7 @@ class FileSystemDatabaseManager {
|
|
|
402
441
|
return vendors.map(vendor => {
|
|
403
442
|
var _a;
|
|
404
443
|
return (Object.assign(Object.assign({}, vendor), { services: (_a = vendor.services) === null || _a === void 0 ? void 0 : _a.map(service => {
|
|
405
|
-
|
|
406
|
-
const normalizedApiUrl = normalizedSourceType === 'openai' && typeof service.apiUrl === 'string'
|
|
407
|
-
? service.apiUrl.trim().replace(/\/v1\/?$/i, '')
|
|
408
|
-
: service.apiUrl;
|
|
409
|
-
return Object.assign(Object.assign({}, service), { sourceType: normalizedSourceType, apiUrl: normalizedApiUrl });
|
|
444
|
+
return Object.assign(Object.assign({}, service), { sourceType: service.sourceType ? (0, type_migration_1.normalizeSourceType)(service.sourceType) : undefined });
|
|
410
445
|
}) }));
|
|
411
446
|
});
|
|
412
447
|
}
|
|
@@ -575,6 +610,96 @@ class FileSystemDatabaseManager {
|
|
|
575
610
|
yield promises_1.default.writeFile(this.logsIndexFile, JSON.stringify(this.logShardsIndex, null, 2));
|
|
576
611
|
});
|
|
577
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* 加载会话日志索引,若不存在则从现有日志全量构建
|
|
615
|
+
*/
|
|
616
|
+
loadSessionLogIndex() {
|
|
617
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
618
|
+
try {
|
|
619
|
+
const data = yield promises_1.default.readFile(this.sessionLogIndexFile, 'utf-8');
|
|
620
|
+
const parsed = JSON.parse(data);
|
|
621
|
+
this.sessionLogIndex = new Map(Object.entries(parsed));
|
|
622
|
+
console.log(`[Database] Session log index loaded: ${this.sessionLogIndex.size} sessions`);
|
|
623
|
+
}
|
|
624
|
+
catch (_a) {
|
|
625
|
+
// 索引文件不存在,从现有日志全量构建
|
|
626
|
+
console.log('[Database] Session log index not found, building from existing logs...');
|
|
627
|
+
yield this.buildSessionLogIndex();
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* 从所有现有日志分片全量构建会话日志索引(首次启动或迁移用)
|
|
633
|
+
*/
|
|
634
|
+
buildSessionLogIndex() {
|
|
635
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
636
|
+
this.sessionLogIndex.clear();
|
|
637
|
+
for (const shard of this.logShardsIndex) {
|
|
638
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
639
|
+
for (let i = 0; i < shardLogs.length; i++) {
|
|
640
|
+
const sessionId = this.extractSessionIdFromLog(shardLogs[i]);
|
|
641
|
+
if (sessionId) {
|
|
642
|
+
let refs = this.sessionLogIndex.get(sessionId);
|
|
643
|
+
if (!refs) {
|
|
644
|
+
refs = [];
|
|
645
|
+
this.sessionLogIndex.set(sessionId, refs);
|
|
646
|
+
}
|
|
647
|
+
refs.push({ filename: shard.filename, index: i, timestamp: shardLogs[i].timestamp });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (this.sessionLogIndex.size > 0) {
|
|
652
|
+
yield this.saveSessionLogIndexNow();
|
|
653
|
+
console.log(`[Database] Session log index built: ${this.sessionLogIndex.size} sessions indexed`);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* 将会话日志索引写入磁盘
|
|
659
|
+
*/
|
|
660
|
+
saveSessionLogIndexNow() {
|
|
661
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
662
|
+
const obj = {};
|
|
663
|
+
for (const [key, refs] of this.sessionLogIndex) {
|
|
664
|
+
obj[key] = refs;
|
|
665
|
+
}
|
|
666
|
+
yield promises_1.default.writeFile(this.sessionLogIndexFile, JSON.stringify(obj));
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 标记索引脏数据,触发防抖写盘
|
|
671
|
+
*/
|
|
672
|
+
scheduleSessionLogIndexFlush() {
|
|
673
|
+
this.sessionLogIndexDirty = true;
|
|
674
|
+
this.sessionLogIndexDirtyCount++;
|
|
675
|
+
// 达到阈值立即刷盘
|
|
676
|
+
if (this.sessionLogIndexDirtyCount >= this.SESSION_LOG_INDEX_FLUSH_THRESHOLD) {
|
|
677
|
+
this.flushSessionLogIndex();
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
// 防抖定时器
|
|
681
|
+
if (!this.sessionLogIndexFlushTimer) {
|
|
682
|
+
this.sessionLogIndexFlushTimer = setTimeout(() => {
|
|
683
|
+
this.flushSessionLogIndex();
|
|
684
|
+
}, this.SESSION_LOG_INDEX_FLUSH_DELAY);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* 立即将索引刷盘(关闭时调用)
|
|
689
|
+
*/
|
|
690
|
+
flushSessionLogIndex() {
|
|
691
|
+
if (this.sessionLogIndexFlushTimer) {
|
|
692
|
+
clearTimeout(this.sessionLogIndexFlushTimer);
|
|
693
|
+
this.sessionLogIndexFlushTimer = null;
|
|
694
|
+
}
|
|
695
|
+
if (this.sessionLogIndexDirty) {
|
|
696
|
+
this.sessionLogIndexDirty = false;
|
|
697
|
+
this.sessionLogIndexDirtyCount = 0;
|
|
698
|
+
this.saveSessionLogIndexNow().catch(err => {
|
|
699
|
+
console.error('[Database] Failed to flush session log index:', err);
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
578
703
|
/**
|
|
579
704
|
* 迁移旧的 logs.json 文件到新的分片格式
|
|
580
705
|
*/
|
|
@@ -687,6 +812,18 @@ class FileSystemDatabaseManager {
|
|
|
687
812
|
this.logShardsIndex = this.logShardsIndex.filter(s => !toDelete.includes(s.filename));
|
|
688
813
|
if (toDelete.length > 0) {
|
|
689
814
|
yield this.saveLogsIndex();
|
|
815
|
+
// 同步清理会话日志索引中被删除分片的条目
|
|
816
|
+
const deleteSet = new Set(toDelete);
|
|
817
|
+
for (const [sid, refs] of this.sessionLogIndex) {
|
|
818
|
+
const remaining = refs.filter(r => !deleteSet.has(r.filename));
|
|
819
|
+
if (remaining.length === 0) {
|
|
820
|
+
this.sessionLogIndex.delete(sid);
|
|
821
|
+
}
|
|
822
|
+
else if (remaining.length < refs.length) {
|
|
823
|
+
this.sessionLogIndex.set(sid, remaining);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
yield this.saveSessionLogIndexNow();
|
|
690
827
|
}
|
|
691
828
|
});
|
|
692
829
|
}
|
|
@@ -827,22 +964,89 @@ class FileSystemDatabaseManager {
|
|
|
827
964
|
}
|
|
828
965
|
ensureDefaultConfig() {
|
|
829
966
|
return __awaiter(this, void 0, void 0, function* () {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
967
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
968
|
+
const current = this.config;
|
|
969
|
+
this.config = {
|
|
970
|
+
enableLogging: (_a = current === null || current === void 0 ? void 0 : current.enableLogging) !== null && _a !== void 0 ? _a : true,
|
|
971
|
+
logRetentionDays: (_b = current === null || current === void 0 ? void 0 : current.logRetentionDays) !== null && _b !== void 0 ? _b : 30,
|
|
972
|
+
maxLogSize: (_c = current === null || current === void 0 ? void 0 : current.maxLogSize) !== null && _c !== void 0 ? _c : 100000,
|
|
973
|
+
apiKey: (_d = current === null || current === void 0 ? void 0 : current.apiKey) !== null && _d !== void 0 ? _d : '',
|
|
974
|
+
enableFailover: (_e = current === null || current === void 0 ? void 0 : current.enableFailover) !== null && _e !== void 0 ? _e : true,
|
|
975
|
+
failoverRecoverySeconds: normalizeFailoverRecoverySeconds(current === null || current === void 0 ? void 0 : current.failoverRecoverySeconds),
|
|
976
|
+
enableAgentTeams: (_f = current === null || current === void 0 ? void 0 : current.enableAgentTeams) !== null && _f !== void 0 ? _f : false,
|
|
977
|
+
enableBypassPermissionsSupport: (_g = current === null || current === void 0 ? void 0 : current.enableBypassPermissionsSupport) !== null && _g !== void 0 ? _g : false,
|
|
978
|
+
codexModelReasoningEffort: isCodexReasoningEffort(current === null || current === void 0 ? void 0 : current.codexModelReasoningEffort)
|
|
979
|
+
? current.codexModelReasoningEffort
|
|
980
|
+
: DEFAULT_CODEX_REASONING_EFFORT,
|
|
981
|
+
proxyEnabled: (_h = current === null || current === void 0 ? void 0 : current.proxyEnabled) !== null && _h !== void 0 ? _h : false,
|
|
982
|
+
proxyUrl: (_j = current === null || current === void 0 ? void 0 : current.proxyUrl) !== null && _j !== void 0 ? _j : '',
|
|
983
|
+
proxyUsername: (_k = current === null || current === void 0 ? void 0 : current.proxyUsername) !== null && _k !== void 0 ? _k : '',
|
|
984
|
+
proxyPassword: (_l = current === null || current === void 0 ? void 0 : current.proxyPassword) !== null && _l !== void 0 ? _l : '',
|
|
985
|
+
};
|
|
986
|
+
// 仅在首次创建或存在字段补齐时落盘
|
|
987
|
+
if (!current || JSON.stringify(current) !== JSON.stringify(this.config)) {
|
|
842
988
|
yield this.saveConfig();
|
|
843
989
|
}
|
|
844
990
|
});
|
|
845
991
|
}
|
|
992
|
+
migrateRouteToolSettingsToGlobalConfig() {
|
|
993
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
994
|
+
const hasGlobalToolConfig = !!this.config &&
|
|
995
|
+
(Object.prototype.hasOwnProperty.call(this.config, 'enableAgentTeams') ||
|
|
996
|
+
Object.prototype.hasOwnProperty.call(this.config, 'enableBypassPermissionsSupport') ||
|
|
997
|
+
Object.prototype.hasOwnProperty.call(this.config, 'codexModelReasoningEffort'));
|
|
998
|
+
const getPreferredRoute = (targetType) => {
|
|
999
|
+
const activeRoute = this.routes.find(route => route.targetType === targetType && route.isActive);
|
|
1000
|
+
if (activeRoute) {
|
|
1001
|
+
return activeRoute;
|
|
1002
|
+
}
|
|
1003
|
+
return this.routes.find(route => route.targetType === targetType);
|
|
1004
|
+
};
|
|
1005
|
+
let configUpdated = false;
|
|
1006
|
+
if (!hasGlobalToolConfig) {
|
|
1007
|
+
const preferredClaudeRoute = getPreferredRoute('claude-code');
|
|
1008
|
+
const preferredCodexRoute = getPreferredRoute('codex');
|
|
1009
|
+
const nextConfig = Object.assign({}, (this.config || {}));
|
|
1010
|
+
if (typeof (preferredClaudeRoute === null || preferredClaudeRoute === void 0 ? void 0 : preferredClaudeRoute.enableAgentTeams) === 'boolean') {
|
|
1011
|
+
nextConfig.enableAgentTeams = preferredClaudeRoute.enableAgentTeams;
|
|
1012
|
+
configUpdated = true;
|
|
1013
|
+
}
|
|
1014
|
+
if (typeof (preferredClaudeRoute === null || preferredClaudeRoute === void 0 ? void 0 : preferredClaudeRoute.enableBypassPermissionsSupport) === 'boolean') {
|
|
1015
|
+
nextConfig.enableBypassPermissionsSupport = preferredClaudeRoute.enableBypassPermissionsSupport;
|
|
1016
|
+
configUpdated = true;
|
|
1017
|
+
}
|
|
1018
|
+
if (isCodexReasoningEffort(preferredCodexRoute === null || preferredCodexRoute === void 0 ? void 0 : preferredCodexRoute.codexModelReasoningEffort)) {
|
|
1019
|
+
nextConfig.codexModelReasoningEffort = preferredCodexRoute.codexModelReasoningEffort;
|
|
1020
|
+
configUpdated = true;
|
|
1021
|
+
}
|
|
1022
|
+
if (configUpdated) {
|
|
1023
|
+
this.config = nextConfig;
|
|
1024
|
+
yield this.saveConfig();
|
|
1025
|
+
console.log('[ConfigMigration] Migrated route-level tool settings to global app config');
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
// 清理路由中的旧字段,避免后续重复歧义
|
|
1029
|
+
let routesUpdated = false;
|
|
1030
|
+
this.routes = this.routes.map((route) => {
|
|
1031
|
+
const hasLegacyFields = Object.prototype.hasOwnProperty.call(route, 'enableAgentTeams') ||
|
|
1032
|
+
Object.prototype.hasOwnProperty.call(route, 'enableBypassPermissionsSupport') ||
|
|
1033
|
+
Object.prototype.hasOwnProperty.call(route, 'codexModelReasoningEffort');
|
|
1034
|
+
if (!hasLegacyFields) {
|
|
1035
|
+
return route;
|
|
1036
|
+
}
|
|
1037
|
+
routesUpdated = true;
|
|
1038
|
+
const cleanedRoute = Object.assign({}, route);
|
|
1039
|
+
delete cleanedRoute.enableAgentTeams;
|
|
1040
|
+
delete cleanedRoute.enableBypassPermissionsSupport;
|
|
1041
|
+
delete cleanedRoute.codexModelReasoningEffort;
|
|
1042
|
+
return cleanedRoute;
|
|
1043
|
+
});
|
|
1044
|
+
if (routesUpdated) {
|
|
1045
|
+
yield this.saveRoutes();
|
|
1046
|
+
console.log('[ConfigMigration] Removed deprecated route-level tool settings');
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
846
1050
|
// Vendor operations
|
|
847
1051
|
getVendors() {
|
|
848
1052
|
return [...this.vendors].sort((a, b) => {
|
|
@@ -861,7 +1065,7 @@ class FileSystemDatabaseManager {
|
|
|
861
1065
|
console.log('[数据库] 创建供应商,输入数据:', JSON.stringify(vendor, null, 2));
|
|
862
1066
|
const id = crypto_1.default.randomUUID();
|
|
863
1067
|
const now = Date.now();
|
|
864
|
-
const newVendor = Object.assign(Object.assign({}, vendor), { id, services: vendor.services || [], createdAt: now, updatedAt: now });
|
|
1068
|
+
const newVendor = Object.assign(Object.assign({}, vendor), { apiKey: typeof vendor.apiKey === 'string' ? vendor.apiKey : '', id, services: vendor.services || [], createdAt: now, updatedAt: now });
|
|
865
1069
|
console.log('[数据库] 创建供应商,返回数据:', JSON.stringify(newVendor, null, 2));
|
|
866
1070
|
this.vendors.push(newVendor);
|
|
867
1071
|
yield this.saveVendors();
|
|
@@ -874,7 +1078,11 @@ class FileSystemDatabaseManager {
|
|
|
874
1078
|
if (index === -1)
|
|
875
1079
|
return false;
|
|
876
1080
|
const now = Date.now();
|
|
877
|
-
this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id,
|
|
1081
|
+
this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id, apiKey: typeof vendor.apiKey === 'string'
|
|
1082
|
+
? vendor.apiKey
|
|
1083
|
+
: (this.vendors[index].apiKey || ''),
|
|
1084
|
+
// 供应商服务应通过 create/update/deleteAPIService 单独维护,避免编辑供应商时误覆盖
|
|
1085
|
+
services: this.vendors[index].services, updatedAt: now });
|
|
878
1086
|
yield this.saveVendors();
|
|
879
1087
|
return true;
|
|
880
1088
|
});
|
|
@@ -884,12 +1092,15 @@ class FileSystemDatabaseManager {
|
|
|
884
1092
|
const index = this.vendors.findIndex(v => v.id === id);
|
|
885
1093
|
if (index === -1)
|
|
886
1094
|
return false;
|
|
887
|
-
//
|
|
1095
|
+
// 级联删除:删除该供应商下服务关联的所有规则
|
|
888
1096
|
const vendor = this.vendors[index];
|
|
889
1097
|
const serviceIds = (vendor.services || []).map(s => s.id);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1098
|
+
if (serviceIds.length > 0) {
|
|
1099
|
+
const beforeCount = this.rules.length;
|
|
1100
|
+
this.rules = this.rules.filter(r => !serviceIds.includes(r.targetServiceId));
|
|
1101
|
+
if (this.rules.length !== beforeCount) {
|
|
1102
|
+
yield this.saveRules();
|
|
1103
|
+
}
|
|
893
1104
|
}
|
|
894
1105
|
this.vendors.splice(index, 1);
|
|
895
1106
|
yield this.saveVendors();
|
|
@@ -954,7 +1165,7 @@ class FileSystemDatabaseManager {
|
|
|
954
1165
|
const _a = service, { vendorId: _ } = _a, serviceData = __rest(_a, ["vendorId"]);
|
|
955
1166
|
const id = crypto_1.default.randomUUID();
|
|
956
1167
|
const now = Date.now();
|
|
957
|
-
const newService = Object.assign(Object.assign({}, serviceData), { id, createdAt: now, updatedAt: now });
|
|
1168
|
+
const newService = Object.assign(Object.assign({}, serviceData), { apiKey: typeof serviceData.apiKey === 'string' ? serviceData.apiKey : '', inheritVendorApiKey: serviceData.inheritVendorApiKey === true, id, createdAt: now, updatedAt: now });
|
|
958
1169
|
console.log('[数据库] 创建服务,最终数据:', JSON.stringify(newService, null, 2));
|
|
959
1170
|
if (!vendor.services) {
|
|
960
1171
|
vendor.services = [];
|
|
@@ -977,7 +1188,9 @@ class FileSystemDatabaseManager {
|
|
|
977
1188
|
if (index === -1)
|
|
978
1189
|
return false;
|
|
979
1190
|
const now = Date.now();
|
|
980
|
-
vendor.services[index] = Object.assign(Object.assign(Object.assign({}, vendor.services[index]), service), { id,
|
|
1191
|
+
vendor.services[index] = Object.assign(Object.assign(Object.assign({}, vendor.services[index]), service), { id, apiKey: typeof service.apiKey === 'string' ? service.apiKey : (vendor.services[index].apiKey || ''), inheritVendorApiKey: service.inheritVendorApiKey !== undefined
|
|
1192
|
+
? service.inheritVendorApiKey === true
|
|
1193
|
+
: vendor.services[index].inheritVendorApiKey === true, updatedAt: now });
|
|
981
1194
|
// 更新供应商的 updatedAt 时间
|
|
982
1195
|
vendor.updatedAt = now;
|
|
983
1196
|
yield this.saveVendors();
|
|
@@ -995,10 +1208,11 @@ class FileSystemDatabaseManager {
|
|
|
995
1208
|
const index = vendor.services.findIndex(s => s.id === id);
|
|
996
1209
|
if (index === -1)
|
|
997
1210
|
return false;
|
|
998
|
-
//
|
|
999
|
-
const
|
|
1000
|
-
|
|
1001
|
-
|
|
1211
|
+
// 级联删除:删除使用该服务的所有规则
|
|
1212
|
+
const beforeCount = this.rules.length;
|
|
1213
|
+
this.rules = this.rules.filter(r => r.targetServiceId !== id);
|
|
1214
|
+
if (this.rules.length !== beforeCount) {
|
|
1215
|
+
yield this.saveRules();
|
|
1002
1216
|
}
|
|
1003
1217
|
vendor.services.splice(index, 1);
|
|
1004
1218
|
// 更新供应商的 updatedAt 时间
|
|
@@ -1340,6 +1554,17 @@ class FileSystemDatabaseManager {
|
|
|
1340
1554
|
yield this.updateStatistics(logWithId);
|
|
1341
1555
|
// 清除计数缓存
|
|
1342
1556
|
this.logsCountCache = null;
|
|
1557
|
+
// 更新会话日志索引
|
|
1558
|
+
const sessionId = this.extractSessionIdFromLog(logWithId);
|
|
1559
|
+
if (sessionId) {
|
|
1560
|
+
let refs = this.sessionLogIndex.get(sessionId);
|
|
1561
|
+
if (!refs) {
|
|
1562
|
+
refs = [];
|
|
1563
|
+
this.sessionLogIndex.set(sessionId, refs);
|
|
1564
|
+
}
|
|
1565
|
+
refs.push({ filename, index: shardLogs.length - 1, timestamp: logWithId.timestamp });
|
|
1566
|
+
this.scheduleSessionLogIndexFlush();
|
|
1567
|
+
}
|
|
1343
1568
|
});
|
|
1344
1569
|
}
|
|
1345
1570
|
getLogs() {
|
|
@@ -1602,7 +1827,56 @@ class FileSystemDatabaseManager {
|
|
|
1602
1827
|
}
|
|
1603
1828
|
return false;
|
|
1604
1829
|
}
|
|
1830
|
+
/**
|
|
1831
|
+
* 获取状态码为 499 的请求日志
|
|
1832
|
+
* @param limit 返回数量限制
|
|
1833
|
+
* @param offset 偏移量
|
|
1834
|
+
* @returns 匹配的请求日志列表
|
|
1835
|
+
*/
|
|
1836
|
+
getClientClosedLogs() {
|
|
1837
|
+
return __awaiter(this, arguments, void 0, function* (limit = 100, offset = 0) {
|
|
1838
|
+
const allMatches = [];
|
|
1839
|
+
const sortedShards = [...this.logShardsIndex].sort((a, b) => b.endTime - a.endTime);
|
|
1840
|
+
// 递序遍历所有分片, collect 499 logs
|
|
1841
|
+
for (const shard of sortedShards) {
|
|
1842
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
1843
|
+
for (const log of shardLogs) {
|
|
1844
|
+
if (log.statusCode === 499) {
|
|
1845
|
+
allMatches.push(log);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
// 按时间倒序排列并分页
|
|
1850
|
+
return allMatches
|
|
1851
|
+
.sort((a, b) => b.timestamp - a.timestamp)
|
|
1852
|
+
.slice(offset, offset + limit);
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* 获取状态码为 499 的请求日志数量
|
|
1857
|
+
* @returns 匹配的请求数量
|
|
1858
|
+
*/
|
|
1859
|
+
getClientClosedLogsCount() {
|
|
1860
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1861
|
+
let count = 0;
|
|
1862
|
+
for (const shard of this.logShardsIndex) {
|
|
1863
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
1864
|
+
for (const log of shardLogs) {
|
|
1865
|
+
if (log.statusCode === 499) {
|
|
1866
|
+
count++;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return count;
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
// Service blacklist operations
|
|
1605
1874
|
// Service blacklist operations
|
|
1875
|
+
getFailoverRecoveryMs() {
|
|
1876
|
+
var _a;
|
|
1877
|
+
const seconds = normalizeFailoverRecoverySeconds((_a = this.config) === null || _a === void 0 ? void 0 : _a.failoverRecoverySeconds);
|
|
1878
|
+
return seconds * 1000;
|
|
1879
|
+
}
|
|
1606
1880
|
isServiceBlacklisted(serviceId, routeId, contentType) {
|
|
1607
1881
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1608
1882
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
@@ -1621,10 +1895,11 @@ class FileSystemDatabaseManager {
|
|
|
1621
1895
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1622
1896
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
1623
1897
|
const now = Date.now();
|
|
1898
|
+
const recoveryMs = this.getFailoverRecoveryMs();
|
|
1624
1899
|
const existing = this.blacklist.get(key);
|
|
1625
1900
|
if (existing) {
|
|
1626
1901
|
existing.blacklistedAt = now;
|
|
1627
|
-
existing.expiresAt = now +
|
|
1902
|
+
existing.expiresAt = now + recoveryMs;
|
|
1628
1903
|
existing.errorCount++;
|
|
1629
1904
|
existing.lastError = errorMessage;
|
|
1630
1905
|
existing.lastStatusCode = statusCode;
|
|
@@ -1636,7 +1911,7 @@ class FileSystemDatabaseManager {
|
|
|
1636
1911
|
routeId,
|
|
1637
1912
|
contentType,
|
|
1638
1913
|
blacklistedAt: now,
|
|
1639
|
-
expiresAt: now +
|
|
1914
|
+
expiresAt: now + recoveryMs,
|
|
1640
1915
|
errorCount: 1,
|
|
1641
1916
|
lastError: errorMessage,
|
|
1642
1917
|
lastStatusCode: statusCode,
|
|
@@ -1675,7 +1950,12 @@ class FileSystemDatabaseManager {
|
|
|
1675
1950
|
}
|
|
1676
1951
|
updateConfig(config) {
|
|
1677
1952
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1678
|
-
this.config
|
|
1953
|
+
const merged = Object.assign(Object.assign({}, (this.config || {})), config);
|
|
1954
|
+
if (!isCodexReasoningEffort(merged.codexModelReasoningEffort)) {
|
|
1955
|
+
merged.codexModelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT;
|
|
1956
|
+
}
|
|
1957
|
+
merged.failoverRecoverySeconds = normalizeFailoverRecoverySeconds(merged.failoverRecoverySeconds);
|
|
1958
|
+
this.config = merged;
|
|
1679
1959
|
yield this.saveConfig();
|
|
1680
1960
|
return true;
|
|
1681
1961
|
});
|
|
@@ -1693,6 +1973,9 @@ class FileSystemDatabaseManager {
|
|
|
1693
1973
|
if (!vendor.name || typeof vendor.name !== 'string') {
|
|
1694
1974
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 缺少有效的 name 字段` };
|
|
1695
1975
|
}
|
|
1976
|
+
if (vendor.apiKey !== undefined && typeof vendor.apiKey !== 'string') {
|
|
1977
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 apiKey 必须是字符串` };
|
|
1978
|
+
}
|
|
1696
1979
|
if (!Array.isArray(vendor.services)) {
|
|
1697
1980
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 services 不是数组` };
|
|
1698
1981
|
}
|
|
@@ -1711,7 +1994,12 @@ class FileSystemDatabaseManager {
|
|
|
1711
1994
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiUrl 字段` };
|
|
1712
1995
|
}
|
|
1713
1996
|
if (!service.apiKey || typeof service.apiKey !== 'string') {
|
|
1714
|
-
|
|
1997
|
+
if (service.inheritVendorApiKey !== true) {
|
|
1998
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiKey 字段` };
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
if (service.inheritVendorApiKey !== undefined && typeof service.inheritVendorApiKey !== 'boolean') {
|
|
2002
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] inheritVendorApiKey 必须是布尔值` };
|
|
1715
2003
|
}
|
|
1716
2004
|
}
|
|
1717
2005
|
return { valid: true };
|
|
@@ -1898,6 +2186,7 @@ class FileSystemDatabaseManager {
|
|
|
1898
2186
|
}
|
|
1899
2187
|
importData(encryptedData, password) {
|
|
1900
2188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2189
|
+
var _a;
|
|
1901
2190
|
try {
|
|
1902
2191
|
// 解密
|
|
1903
2192
|
let jsonData;
|
|
@@ -1933,7 +2222,7 @@ class FileSystemDatabaseManager {
|
|
|
1933
2222
|
this.vendors = importData.vendors.map((v) => (Object.assign(Object.assign({}, v), { updatedAt: now })));
|
|
1934
2223
|
this.routes = importData.routes.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1935
2224
|
this.rules = importData.rules.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1936
|
-
this.config = Object.assign(Object.assign({}, importData.config), { updatedAt: now });
|
|
2225
|
+
this.config = Object.assign(Object.assign({}, importData.config), { failoverRecoverySeconds: normalizeFailoverRecoverySeconds((_a = importData.config) === null || _a === void 0 ? void 0 : _a.failoverRecoverySeconds), updatedAt: now });
|
|
1937
2226
|
// 保存数据
|
|
1938
2227
|
yield Promise.all([
|
|
1939
2228
|
this.saveVendors(),
|
|
@@ -2244,8 +2533,37 @@ class FileSystemDatabaseManager {
|
|
|
2244
2533
|
}
|
|
2245
2534
|
getLogsBySessionId(sessionId_1) {
|
|
2246
2535
|
return __awaiter(this, arguments, void 0, function* (sessionId, limit = 100) {
|
|
2536
|
+
const refs = this.sessionLogIndex.get(sessionId);
|
|
2537
|
+
// 有索引:仅加载相关分片,按 index 直接取值
|
|
2538
|
+
if (refs && refs.length > 0) {
|
|
2539
|
+
// 按 filename 分组,避免重复加载同一分片
|
|
2540
|
+
const shardMap = new Map();
|
|
2541
|
+
for (const ref of refs) {
|
|
2542
|
+
let indices = shardMap.get(ref.filename);
|
|
2543
|
+
if (!indices) {
|
|
2544
|
+
indices = [];
|
|
2545
|
+
shardMap.set(ref.filename, indices);
|
|
2546
|
+
}
|
|
2547
|
+
indices.push(ref.index);
|
|
2548
|
+
}
|
|
2549
|
+
const logs = [];
|
|
2550
|
+
for (const [filename, indices] of shardMap) {
|
|
2551
|
+
try {
|
|
2552
|
+
const shardLogs = yield this.loadLogShard(filename);
|
|
2553
|
+
for (const idx of indices) {
|
|
2554
|
+
if (idx >= 0 && idx < shardLogs.length) {
|
|
2555
|
+
logs.push(shardLogs[idx]);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
catch (_a) {
|
|
2560
|
+
// 分片文件可能已被清理,跳过
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return logs.sort((a, b) => b.timestamp - a.timestamp).slice(0, limit);
|
|
2564
|
+
}
|
|
2565
|
+
// 无索引(兼容旧数据):回退到全扫描
|
|
2247
2566
|
const allLogs = [];
|
|
2248
|
-
// 遍历所有分片
|
|
2249
2567
|
for (const shard of this.logShardsIndex) {
|
|
2250
2568
|
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
2251
2569
|
const filtered = shardLogs.filter(log => this.isLogBelongsToSession(log, sessionId));
|
|
@@ -2268,22 +2586,73 @@ class FileSystemDatabaseManager {
|
|
|
2268
2586
|
try {
|
|
2269
2587
|
// body 可能是对象(已解析)或字符串(未解析)
|
|
2270
2588
|
const body = typeof log.body === 'string' ? JSON.parse(log.body) : log.body;
|
|
2271
|
-
if ((
|
|
2272
|
-
|
|
2589
|
+
if ((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) {
|
|
2590
|
+
const userId = body.metadata.user_id;
|
|
2591
|
+
// 兼容新旧格式:新版本为 JSON 字符串,旧版本为纯字符串
|
|
2592
|
+
let extractedSessionId = null;
|
|
2593
|
+
try {
|
|
2594
|
+
const parsed = JSON.parse(userId);
|
|
2595
|
+
if (parsed && typeof parsed === 'object' && parsed.session_id) {
|
|
2596
|
+
extractedSessionId = parsed.session_id;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
catch (_c) {
|
|
2600
|
+
// 不是 JSON,按旧版本纯字符串处理
|
|
2601
|
+
extractedSessionId = userId;
|
|
2602
|
+
}
|
|
2603
|
+
if (extractedSessionId === sessionId) {
|
|
2604
|
+
return true;
|
|
2605
|
+
}
|
|
2273
2606
|
}
|
|
2274
2607
|
}
|
|
2275
|
-
catch (
|
|
2608
|
+
catch (_d) {
|
|
2276
2609
|
// 忽略解析错误
|
|
2277
2610
|
}
|
|
2278
2611
|
}
|
|
2279
2612
|
return false;
|
|
2280
2613
|
}
|
|
2614
|
+
/**
|
|
2615
|
+
* 从日志条目中提取 sessionId(用于索引)
|
|
2616
|
+
* Codex: headers['session_id']
|
|
2617
|
+
* Claude Code: body.metadata.user_id(兼容新旧格式)
|
|
2618
|
+
*/
|
|
2619
|
+
extractSessionIdFromLog(log) {
|
|
2620
|
+
var _a, _b;
|
|
2621
|
+
// Codex: headers 中的 session_id
|
|
2622
|
+
const headerSessionId = (_a = log.headers) === null || _a === void 0 ? void 0 : _a['session_id'];
|
|
2623
|
+
if (typeof headerSessionId === 'string')
|
|
2624
|
+
return headerSessionId;
|
|
2625
|
+
// Claude Code: body 中的 metadata.user_id
|
|
2626
|
+
if (log.body) {
|
|
2627
|
+
try {
|
|
2628
|
+
const body = typeof log.body === 'string' ? JSON.parse(log.body) : log.body;
|
|
2629
|
+
if ((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) {
|
|
2630
|
+
const userId = body.metadata.user_id;
|
|
2631
|
+
try {
|
|
2632
|
+
const parsed = JSON.parse(userId);
|
|
2633
|
+
if (parsed && typeof parsed === 'object' && parsed.session_id) {
|
|
2634
|
+
return parsed.session_id;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
catch (_c) {
|
|
2638
|
+
return userId;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
catch (_d) {
|
|
2643
|
+
// 忽略解析错误
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
return null;
|
|
2647
|
+
}
|
|
2281
2648
|
deleteSession(sessionId) {
|
|
2282
2649
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2283
2650
|
const index = this.sessions.findIndex(s => s.id === sessionId);
|
|
2284
2651
|
if (index === -1)
|
|
2285
2652
|
return false;
|
|
2286
2653
|
this.sessions.splice(index, 1);
|
|
2654
|
+
this.sessionLogIndex.delete(sessionId);
|
|
2655
|
+
this.scheduleSessionLogIndexFlush();
|
|
2287
2656
|
yield this.saveSessions();
|
|
2288
2657
|
return true;
|
|
2289
2658
|
});
|
|
@@ -2291,6 +2660,8 @@ class FileSystemDatabaseManager {
|
|
|
2291
2660
|
clearSessions() {
|
|
2292
2661
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2293
2662
|
this.sessions = [];
|
|
2663
|
+
this.sessionLogIndex.clear();
|
|
2664
|
+
this.scheduleSessionLogIndexFlush();
|
|
2294
2665
|
yield this.saveSessions();
|
|
2295
2666
|
});
|
|
2296
2667
|
}
|
|
@@ -2405,8 +2776,8 @@ class FileSystemDatabaseManager {
|
|
|
2405
2776
|
}
|
|
2406
2777
|
// Close method for compatibility (no-op for filesystem database)
|
|
2407
2778
|
close() {
|
|
2408
|
-
//
|
|
2409
|
-
|
|
2779
|
+
// 刷盘会话日志索引
|
|
2780
|
+
this.flushSessionLogIndex();
|
|
2410
2781
|
}
|
|
2411
2782
|
}
|
|
2412
2783
|
exports.FileSystemDatabaseManager = FileSystemDatabaseManager;
|