aicodeswitch 3.9.4 → 4.0.0
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 +209 -72
- package/dist/server/main.js +365 -283
- package/dist/server/original-config-reader.js +154 -88
- package/dist/server/proxy-server.js +993 -387
- package/dist/server/rules-status-service.js +285 -54
- package/dist/server/transformers/chunk-collector.js +26 -4
- package/dist/server/transformers/streaming.js +2334 -280
- package/dist/server/transformers/transformers.js +1765 -0
- package/dist/ui/assets/index-GQBwe1Rm.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
|
@@ -1,37 +1,4 @@
|
|
|
1
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
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -41,170 +8,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
9
|
});
|
|
43
10
|
};
|
|
44
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
-
};
|
|
47
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
12
|
exports.DatabaseFactory = void 0;
|
|
49
|
-
const path_1 = __importDefault(require("path"));
|
|
50
13
|
const fs_database_1 = require("./fs-database");
|
|
51
|
-
const migrateToFs = __importStar(require("./migrate-to-fs"));
|
|
52
14
|
/**
|
|
53
15
|
* 数据库工厂
|
|
54
|
-
*
|
|
16
|
+
* 创建文件系统数据库实例
|
|
55
17
|
*/
|
|
56
18
|
class DatabaseFactory {
|
|
57
19
|
/**
|
|
58
20
|
* 创建数据库实例
|
|
59
21
|
* @param dataPath 数据存储路径
|
|
60
|
-
* @param type 数据库类型,默认使用文件系统
|
|
61
22
|
*/
|
|
62
|
-
static create(
|
|
63
|
-
return __awaiter(this,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return db;
|
|
68
|
-
}
|
|
69
|
-
// 如果用户明确要求使用 SQLite,尝试加载
|
|
70
|
-
if (type === 'sqlite') {
|
|
71
|
-
try {
|
|
72
|
-
const { DatabaseManager } = yield Promise.resolve().then(() => __importStar(require('./database.js')));
|
|
73
|
-
const db = new DatabaseManager(dataPath);
|
|
74
|
-
yield db.initialize();
|
|
75
|
-
return db;
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
console.error('[Database] Failed to load SQLite database, falling back to filesystem:', error);
|
|
79
|
-
console.log('[Database] Using filesystem database instead');
|
|
80
|
-
const db = new fs_database_1.FileSystemDatabaseManager(dataPath);
|
|
81
|
-
yield db.initialize();
|
|
82
|
-
return db;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
throw new Error(`Unknown database type: ${type}`);
|
|
23
|
+
static create(dataPath) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const db = new fs_database_1.FileSystemDatabaseManager(dataPath);
|
|
26
|
+
yield db.initialize();
|
|
27
|
+
return db;
|
|
86
28
|
});
|
|
87
29
|
}
|
|
88
30
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
31
|
+
* 自动创建数据库实例(兼容旧 API)
|
|
32
|
+
* @param dataPath 数据存储路径
|
|
33
|
+
* @param _legacyDataPath 已弃用,不再使用
|
|
91
34
|
*/
|
|
92
|
-
static createAuto(dataPath,
|
|
35
|
+
static createAuto(dataPath, _legacyDataPath) {
|
|
93
36
|
return __awaiter(this, void 0, void 0, function* () {
|
|
94
|
-
|
|
95
|
-
const legacyPath = legacyDataPath && legacyDataPath !== dataPath ? legacyDataPath : null;
|
|
96
|
-
// 检查迁移完成标记文件
|
|
97
|
-
const migrationMarkerPath = path_1.default.join(dataPath, '.migration-completed');
|
|
98
|
-
const hasMigrationMarker = yield fs.access(migrationMarkerPath)
|
|
99
|
-
.then(() => true)
|
|
100
|
-
.catch(() => false);
|
|
101
|
-
// 检查是否存在文件系统数据库
|
|
102
|
-
const fsDbExists = yield fs.access(path_1.default.join(dataPath, 'config.json'))
|
|
103
|
-
.then(() => true)
|
|
104
|
-
.catch(() => false);
|
|
105
|
-
// 如果存在迁移标记且文件系统数据库存在,使用文件系统数据库
|
|
106
|
-
if (hasMigrationMarker && fsDbExists) {
|
|
107
|
-
console.log('[Database] Migration marker found, using filesystem database');
|
|
108
|
-
return this.create(dataPath, 'filesystem');
|
|
109
|
-
}
|
|
110
|
-
// 如果不存在迁移标记但存在文件系统数据库,可能是用户手动删除了标记
|
|
111
|
-
// 这种情况下仍然使用文件系统数据库(避免数据丢失)
|
|
112
|
-
if (fsDbExists) {
|
|
113
|
-
console.log('[Database] Using existing filesystem database (no migration marker)');
|
|
114
|
-
return this.create(dataPath, 'filesystem');
|
|
115
|
-
}
|
|
116
|
-
// 如果新目录为空,尝试从旧 data 目录迁移文件系统数据库
|
|
117
|
-
if (legacyPath) {
|
|
118
|
-
const legacyFsDbExists = yield fs.access(path_1.default.join(legacyPath, 'config.json'))
|
|
119
|
-
.then(() => true)
|
|
120
|
-
.catch(() => false);
|
|
121
|
-
if (legacyFsDbExists) {
|
|
122
|
-
console.log('[Database] Found legacy filesystem database, migrating to new directory...');
|
|
123
|
-
try {
|
|
124
|
-
yield migrateToFs.migrateLegacyFsData(legacyPath, dataPath);
|
|
125
|
-
yield fs.writeFile(migrationMarkerPath, new Date().toISOString(), 'utf-8');
|
|
126
|
-
console.log('[Database] Legacy filesystem database migration completed');
|
|
127
|
-
return this.create(dataPath, 'filesystem');
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
console.error('[Database] Legacy filesystem migration failed:', error);
|
|
131
|
-
console.log('[Database] Falling back to create new filesystem database');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// 如果新目录为空,检查旧 data 目录是否存在 SQLite 数据库
|
|
136
|
-
if (legacyPath) {
|
|
137
|
-
const legacySqliteExists = yield fs.access(path_1.default.join(legacyPath, 'app.db'))
|
|
138
|
-
.then(() => true)
|
|
139
|
-
.catch(() => false);
|
|
140
|
-
if (legacySqliteExists) {
|
|
141
|
-
console.log('[Database] Found legacy SQLite database, migrating to new filesystem directory...');
|
|
142
|
-
try {
|
|
143
|
-
yield migrateToFs.migrateToFileSystem(legacyPath, dataPath);
|
|
144
|
-
console.log('[Database] Verifying migration...');
|
|
145
|
-
const verification = yield migrateToFs.verifyMigration(dataPath);
|
|
146
|
-
if (verification.success) {
|
|
147
|
-
console.log('[Database] ✅ Migration verified successfully');
|
|
148
|
-
if (verification.warnings.length > 0) {
|
|
149
|
-
console.log('[Database] Warnings:', verification.warnings);
|
|
150
|
-
}
|
|
151
|
-
yield fs.writeFile(migrationMarkerPath, new Date().toISOString(), 'utf-8');
|
|
152
|
-
console.log('[Database] Migration marker file created:', migrationMarkerPath);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
console.error('[Database] ❌ Migration verification failed');
|
|
156
|
-
console.error('[Database] Errors:', verification.errors);
|
|
157
|
-
throw new Error('Migration verification failed');
|
|
158
|
-
}
|
|
159
|
-
console.log('[Database] Migration completed, using filesystem database');
|
|
160
|
-
return this.create(dataPath, 'filesystem');
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
console.error('[Database] Migration failed:', error);
|
|
164
|
-
console.log('[Database] Creating new filesystem database');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// 检查是否存在旧的 SQLite 数据库
|
|
169
|
-
const sqliteDbExists = yield fs.access(path_1.default.join(dataPath, 'app.db'))
|
|
170
|
-
.then(() => true)
|
|
171
|
-
.catch(() => false);
|
|
172
|
-
if (sqliteDbExists) {
|
|
173
|
-
console.log('[Database] Found old SQLite database, migrating to filesystem...');
|
|
174
|
-
try {
|
|
175
|
-
// 执行迁移
|
|
176
|
-
yield migrateToFs.migrateToFileSystem(dataPath);
|
|
177
|
-
// 验证迁移结果
|
|
178
|
-
console.log('[Database] Verifying migration...');
|
|
179
|
-
const verification = yield migrateToFs.verifyMigration(dataPath);
|
|
180
|
-
if (verification.success) {
|
|
181
|
-
console.log('[Database] ✅ Migration verified successfully');
|
|
182
|
-
if (verification.warnings.length > 0) {
|
|
183
|
-
console.log('[Database] Warnings:', verification.warnings);
|
|
184
|
-
}
|
|
185
|
-
// 只有在验证成功后才创建标记文件
|
|
186
|
-
const migrationMarkerPath = path_1.default.join(dataPath, '.migration-completed');
|
|
187
|
-
yield fs.writeFile(migrationMarkerPath, new Date().toISOString(), 'utf-8');
|
|
188
|
-
console.log('[Database] Migration marker file created:', migrationMarkerPath);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
console.error('[Database] ❌ Migration verification failed');
|
|
192
|
-
console.error('[Database] Errors:', verification.errors);
|
|
193
|
-
throw new Error('Migration verification failed');
|
|
194
|
-
}
|
|
195
|
-
console.log('[Database] Migration completed, using filesystem database');
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
console.error('[Database] Migration failed:', error);
|
|
199
|
-
console.log('[Database] Creating new filesystem database');
|
|
200
|
-
// 迁移失败时,原始数据库文件保持不变
|
|
201
|
-
// 用户可以使用老版本继续运行,或手动重新配置
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
console.log('[Database] No existing database found, creating new filesystem database');
|
|
206
|
-
}
|
|
207
|
-
return this.create(dataPath, 'filesystem');
|
|
37
|
+
return this.create(dataPath);
|
|
208
38
|
});
|
|
209
39
|
}
|
|
210
40
|
}
|
|
@@ -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 文件存储数据,无需编译依赖
|
|
@@ -209,8 +222,8 @@ class FileSystemDatabaseManager {
|
|
|
209
222
|
yield this.loadAllData();
|
|
210
223
|
// 执行数据源类型迁移(在加载数据之后)
|
|
211
224
|
yield this.migrateSourceTypes();
|
|
212
|
-
//
|
|
213
|
-
yield this.
|
|
225
|
+
// 路由级工具配置迁移到全局配置(兼容旧版本)
|
|
226
|
+
yield this.migrateRouteToolSettingsToGlobalConfig();
|
|
214
227
|
// 确保默认配置
|
|
215
228
|
yield this.ensureDefaultConfig();
|
|
216
229
|
});
|
|
@@ -233,15 +246,37 @@ class FileSystemDatabaseManager {
|
|
|
233
246
|
}
|
|
234
247
|
loadVendors() {
|
|
235
248
|
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
let needSave = false;
|
|
236
250
|
try {
|
|
237
251
|
const data = yield promises_1.default.readFile(this.vendorsFile, 'utf-8');
|
|
238
|
-
|
|
252
|
+
const parsed = JSON.parse(data);
|
|
253
|
+
this.vendors = Array.isArray(parsed) ? parsed.map((vendor) => {
|
|
254
|
+
const normalizedServices = Array.isArray(vendor.services)
|
|
255
|
+
? vendor.services.map((service) => {
|
|
256
|
+
const normalizedService = Object.assign(Object.assign({}, service), { apiKey: typeof service.apiKey === 'string' ? service.apiKey : '', inheritVendorApiKey: service.inheritVendorApiKey === true });
|
|
257
|
+
if (normalizedService.apiKey !== service.apiKey ||
|
|
258
|
+
normalizedService.inheritVendorApiKey !== service.inheritVendorApiKey) {
|
|
259
|
+
needSave = true;
|
|
260
|
+
}
|
|
261
|
+
return normalizedService;
|
|
262
|
+
})
|
|
263
|
+
: [];
|
|
264
|
+
const normalizedVendor = Object.assign(Object.assign({}, vendor), { apiKey: typeof vendor.apiKey === 'string' ? vendor.apiKey : '', services: normalizedServices });
|
|
265
|
+
if (normalizedVendor.apiKey !== vendor.apiKey ||
|
|
266
|
+
!Array.isArray(vendor.services)) {
|
|
267
|
+
needSave = true;
|
|
268
|
+
}
|
|
269
|
+
return normalizedVendor;
|
|
270
|
+
}) : [];
|
|
239
271
|
}
|
|
240
272
|
catch (_a) {
|
|
241
273
|
this.vendors = [];
|
|
242
274
|
}
|
|
243
275
|
// 兼容性检查:如果存在旧的 services.json,自动迁移
|
|
244
276
|
yield this.migrateServicesIfNeeded();
|
|
277
|
+
if (needSave) {
|
|
278
|
+
yield this.saveVendors();
|
|
279
|
+
}
|
|
245
280
|
});
|
|
246
281
|
}
|
|
247
282
|
/**
|
|
@@ -359,41 +394,6 @@ class FileSystemDatabaseManager {
|
|
|
359
394
|
console.log(`[TypeMigration] Migration completed. Migrated ${migratedCount} services.`);
|
|
360
395
|
});
|
|
361
396
|
}
|
|
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
397
|
/**
|
|
398
398
|
* 迁移导入数据中的类型
|
|
399
399
|
* 用于导入功能,自动将旧类型转换为新类型
|
|
@@ -402,11 +402,7 @@ class FileSystemDatabaseManager {
|
|
|
402
402
|
return vendors.map(vendor => {
|
|
403
403
|
var _a;
|
|
404
404
|
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 });
|
|
405
|
+
return Object.assign(Object.assign({}, service), { sourceType: service.sourceType ? (0, type_migration_1.normalizeSourceType)(service.sourceType) : undefined });
|
|
410
406
|
}) }));
|
|
411
407
|
});
|
|
412
408
|
}
|
|
@@ -827,22 +823,89 @@ class FileSystemDatabaseManager {
|
|
|
827
823
|
}
|
|
828
824
|
ensureDefaultConfig() {
|
|
829
825
|
return __awaiter(this, void 0, void 0, function* () {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
826
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
827
|
+
const current = this.config;
|
|
828
|
+
this.config = {
|
|
829
|
+
enableLogging: (_a = current === null || current === void 0 ? void 0 : current.enableLogging) !== null && _a !== void 0 ? _a : true,
|
|
830
|
+
logRetentionDays: (_b = current === null || current === void 0 ? void 0 : current.logRetentionDays) !== null && _b !== void 0 ? _b : 30,
|
|
831
|
+
maxLogSize: (_c = current === null || current === void 0 ? void 0 : current.maxLogSize) !== null && _c !== void 0 ? _c : 100000,
|
|
832
|
+
apiKey: (_d = current === null || current === void 0 ? void 0 : current.apiKey) !== null && _d !== void 0 ? _d : '',
|
|
833
|
+
enableFailover: (_e = current === null || current === void 0 ? void 0 : current.enableFailover) !== null && _e !== void 0 ? _e : true,
|
|
834
|
+
failoverRecoverySeconds: normalizeFailoverRecoverySeconds(current === null || current === void 0 ? void 0 : current.failoverRecoverySeconds),
|
|
835
|
+
enableAgentTeams: (_f = current === null || current === void 0 ? void 0 : current.enableAgentTeams) !== null && _f !== void 0 ? _f : false,
|
|
836
|
+
enableBypassPermissionsSupport: (_g = current === null || current === void 0 ? void 0 : current.enableBypassPermissionsSupport) !== null && _g !== void 0 ? _g : false,
|
|
837
|
+
codexModelReasoningEffort: isCodexReasoningEffort(current === null || current === void 0 ? void 0 : current.codexModelReasoningEffort)
|
|
838
|
+
? current.codexModelReasoningEffort
|
|
839
|
+
: DEFAULT_CODEX_REASONING_EFFORT,
|
|
840
|
+
proxyEnabled: (_h = current === null || current === void 0 ? void 0 : current.proxyEnabled) !== null && _h !== void 0 ? _h : false,
|
|
841
|
+
proxyUrl: (_j = current === null || current === void 0 ? void 0 : current.proxyUrl) !== null && _j !== void 0 ? _j : '',
|
|
842
|
+
proxyUsername: (_k = current === null || current === void 0 ? void 0 : current.proxyUsername) !== null && _k !== void 0 ? _k : '',
|
|
843
|
+
proxyPassword: (_l = current === null || current === void 0 ? void 0 : current.proxyPassword) !== null && _l !== void 0 ? _l : '',
|
|
844
|
+
};
|
|
845
|
+
// 仅在首次创建或存在字段补齐时落盘
|
|
846
|
+
if (!current || JSON.stringify(current) !== JSON.stringify(this.config)) {
|
|
842
847
|
yield this.saveConfig();
|
|
843
848
|
}
|
|
844
849
|
});
|
|
845
850
|
}
|
|
851
|
+
migrateRouteToolSettingsToGlobalConfig() {
|
|
852
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
853
|
+
const hasGlobalToolConfig = !!this.config &&
|
|
854
|
+
(Object.prototype.hasOwnProperty.call(this.config, 'enableAgentTeams') ||
|
|
855
|
+
Object.prototype.hasOwnProperty.call(this.config, 'enableBypassPermissionsSupport') ||
|
|
856
|
+
Object.prototype.hasOwnProperty.call(this.config, 'codexModelReasoningEffort'));
|
|
857
|
+
const getPreferredRoute = (targetType) => {
|
|
858
|
+
const activeRoute = this.routes.find(route => route.targetType === targetType && route.isActive);
|
|
859
|
+
if (activeRoute) {
|
|
860
|
+
return activeRoute;
|
|
861
|
+
}
|
|
862
|
+
return this.routes.find(route => route.targetType === targetType);
|
|
863
|
+
};
|
|
864
|
+
let configUpdated = false;
|
|
865
|
+
if (!hasGlobalToolConfig) {
|
|
866
|
+
const preferredClaudeRoute = getPreferredRoute('claude-code');
|
|
867
|
+
const preferredCodexRoute = getPreferredRoute('codex');
|
|
868
|
+
const nextConfig = Object.assign({}, (this.config || {}));
|
|
869
|
+
if (typeof (preferredClaudeRoute === null || preferredClaudeRoute === void 0 ? void 0 : preferredClaudeRoute.enableAgentTeams) === 'boolean') {
|
|
870
|
+
nextConfig.enableAgentTeams = preferredClaudeRoute.enableAgentTeams;
|
|
871
|
+
configUpdated = true;
|
|
872
|
+
}
|
|
873
|
+
if (typeof (preferredClaudeRoute === null || preferredClaudeRoute === void 0 ? void 0 : preferredClaudeRoute.enableBypassPermissionsSupport) === 'boolean') {
|
|
874
|
+
nextConfig.enableBypassPermissionsSupport = preferredClaudeRoute.enableBypassPermissionsSupport;
|
|
875
|
+
configUpdated = true;
|
|
876
|
+
}
|
|
877
|
+
if (isCodexReasoningEffort(preferredCodexRoute === null || preferredCodexRoute === void 0 ? void 0 : preferredCodexRoute.codexModelReasoningEffort)) {
|
|
878
|
+
nextConfig.codexModelReasoningEffort = preferredCodexRoute.codexModelReasoningEffort;
|
|
879
|
+
configUpdated = true;
|
|
880
|
+
}
|
|
881
|
+
if (configUpdated) {
|
|
882
|
+
this.config = nextConfig;
|
|
883
|
+
yield this.saveConfig();
|
|
884
|
+
console.log('[ConfigMigration] Migrated route-level tool settings to global app config');
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
// 清理路由中的旧字段,避免后续重复歧义
|
|
888
|
+
let routesUpdated = false;
|
|
889
|
+
this.routes = this.routes.map((route) => {
|
|
890
|
+
const hasLegacyFields = Object.prototype.hasOwnProperty.call(route, 'enableAgentTeams') ||
|
|
891
|
+
Object.prototype.hasOwnProperty.call(route, 'enableBypassPermissionsSupport') ||
|
|
892
|
+
Object.prototype.hasOwnProperty.call(route, 'codexModelReasoningEffort');
|
|
893
|
+
if (!hasLegacyFields) {
|
|
894
|
+
return route;
|
|
895
|
+
}
|
|
896
|
+
routesUpdated = true;
|
|
897
|
+
const cleanedRoute = Object.assign({}, route);
|
|
898
|
+
delete cleanedRoute.enableAgentTeams;
|
|
899
|
+
delete cleanedRoute.enableBypassPermissionsSupport;
|
|
900
|
+
delete cleanedRoute.codexModelReasoningEffort;
|
|
901
|
+
return cleanedRoute;
|
|
902
|
+
});
|
|
903
|
+
if (routesUpdated) {
|
|
904
|
+
yield this.saveRoutes();
|
|
905
|
+
console.log('[ConfigMigration] Removed deprecated route-level tool settings');
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
}
|
|
846
909
|
// Vendor operations
|
|
847
910
|
getVendors() {
|
|
848
911
|
return [...this.vendors].sort((a, b) => {
|
|
@@ -861,7 +924,7 @@ class FileSystemDatabaseManager {
|
|
|
861
924
|
console.log('[数据库] 创建供应商,输入数据:', JSON.stringify(vendor, null, 2));
|
|
862
925
|
const id = crypto_1.default.randomUUID();
|
|
863
926
|
const now = Date.now();
|
|
864
|
-
const newVendor = Object.assign(Object.assign({}, vendor), { id, services: vendor.services || [], createdAt: now, updatedAt: now });
|
|
927
|
+
const newVendor = Object.assign(Object.assign({}, vendor), { apiKey: typeof vendor.apiKey === 'string' ? vendor.apiKey : '', id, services: vendor.services || [], createdAt: now, updatedAt: now });
|
|
865
928
|
console.log('[数据库] 创建供应商,返回数据:', JSON.stringify(newVendor, null, 2));
|
|
866
929
|
this.vendors.push(newVendor);
|
|
867
930
|
yield this.saveVendors();
|
|
@@ -874,7 +937,11 @@ class FileSystemDatabaseManager {
|
|
|
874
937
|
if (index === -1)
|
|
875
938
|
return false;
|
|
876
939
|
const now = Date.now();
|
|
877
|
-
this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id,
|
|
940
|
+
this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id, apiKey: typeof vendor.apiKey === 'string'
|
|
941
|
+
? vendor.apiKey
|
|
942
|
+
: (this.vendors[index].apiKey || ''),
|
|
943
|
+
// 供应商服务应通过 create/update/deleteAPIService 单独维护,避免编辑供应商时误覆盖
|
|
944
|
+
services: this.vendors[index].services, updatedAt: now });
|
|
878
945
|
yield this.saveVendors();
|
|
879
946
|
return true;
|
|
880
947
|
});
|
|
@@ -884,12 +951,15 @@ class FileSystemDatabaseManager {
|
|
|
884
951
|
const index = this.vendors.findIndex(v => v.id === id);
|
|
885
952
|
if (index === -1)
|
|
886
953
|
return false;
|
|
887
|
-
//
|
|
954
|
+
// 级联删除:删除该供应商下服务关联的所有规则
|
|
888
955
|
const vendor = this.vendors[index];
|
|
889
956
|
const serviceIds = (vendor.services || []).map(s => s.id);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
957
|
+
if (serviceIds.length > 0) {
|
|
958
|
+
const beforeCount = this.rules.length;
|
|
959
|
+
this.rules = this.rules.filter(r => !serviceIds.includes(r.targetServiceId));
|
|
960
|
+
if (this.rules.length !== beforeCount) {
|
|
961
|
+
yield this.saveRules();
|
|
962
|
+
}
|
|
893
963
|
}
|
|
894
964
|
this.vendors.splice(index, 1);
|
|
895
965
|
yield this.saveVendors();
|
|
@@ -954,7 +1024,7 @@ class FileSystemDatabaseManager {
|
|
|
954
1024
|
const _a = service, { vendorId: _ } = _a, serviceData = __rest(_a, ["vendorId"]);
|
|
955
1025
|
const id = crypto_1.default.randomUUID();
|
|
956
1026
|
const now = Date.now();
|
|
957
|
-
const newService = Object.assign(Object.assign({}, serviceData), { id, createdAt: now, updatedAt: now });
|
|
1027
|
+
const newService = Object.assign(Object.assign({}, serviceData), { apiKey: typeof serviceData.apiKey === 'string' ? serviceData.apiKey : '', inheritVendorApiKey: serviceData.inheritVendorApiKey === true, id, createdAt: now, updatedAt: now });
|
|
958
1028
|
console.log('[数据库] 创建服务,最终数据:', JSON.stringify(newService, null, 2));
|
|
959
1029
|
if (!vendor.services) {
|
|
960
1030
|
vendor.services = [];
|
|
@@ -977,7 +1047,9 @@ class FileSystemDatabaseManager {
|
|
|
977
1047
|
if (index === -1)
|
|
978
1048
|
return false;
|
|
979
1049
|
const now = Date.now();
|
|
980
|
-
vendor.services[index] = Object.assign(Object.assign(Object.assign({}, vendor.services[index]), service), { id,
|
|
1050
|
+
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
|
|
1051
|
+
? service.inheritVendorApiKey === true
|
|
1052
|
+
: vendor.services[index].inheritVendorApiKey === true, updatedAt: now });
|
|
981
1053
|
// 更新供应商的 updatedAt 时间
|
|
982
1054
|
vendor.updatedAt = now;
|
|
983
1055
|
yield this.saveVendors();
|
|
@@ -995,10 +1067,11 @@ class FileSystemDatabaseManager {
|
|
|
995
1067
|
const index = vendor.services.findIndex(s => s.id === id);
|
|
996
1068
|
if (index === -1)
|
|
997
1069
|
return false;
|
|
998
|
-
//
|
|
999
|
-
const
|
|
1000
|
-
|
|
1001
|
-
|
|
1070
|
+
// 级联删除:删除使用该服务的所有规则
|
|
1071
|
+
const beforeCount = this.rules.length;
|
|
1072
|
+
this.rules = this.rules.filter(r => r.targetServiceId !== id);
|
|
1073
|
+
if (this.rules.length !== beforeCount) {
|
|
1074
|
+
yield this.saveRules();
|
|
1002
1075
|
}
|
|
1003
1076
|
vendor.services.splice(index, 1);
|
|
1004
1077
|
// 更新供应商的 updatedAt 时间
|
|
@@ -1602,7 +1675,56 @@ class FileSystemDatabaseManager {
|
|
|
1602
1675
|
}
|
|
1603
1676
|
return false;
|
|
1604
1677
|
}
|
|
1678
|
+
/**
|
|
1679
|
+
* 获取状态码为 499 的请求日志
|
|
1680
|
+
* @param limit 返回数量限制
|
|
1681
|
+
* @param offset 偏移量
|
|
1682
|
+
* @returns 匹配的请求日志列表
|
|
1683
|
+
*/
|
|
1684
|
+
getClientClosedLogs() {
|
|
1685
|
+
return __awaiter(this, arguments, void 0, function* (limit = 100, offset = 0) {
|
|
1686
|
+
const allMatches = [];
|
|
1687
|
+
const sortedShards = [...this.logShardsIndex].sort((a, b) => b.endTime - a.endTime);
|
|
1688
|
+
// 递序遍历所有分片, collect 499 logs
|
|
1689
|
+
for (const shard of sortedShards) {
|
|
1690
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
1691
|
+
for (const log of shardLogs) {
|
|
1692
|
+
if (log.statusCode === 499) {
|
|
1693
|
+
allMatches.push(log);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
// 按时间倒序排列并分页
|
|
1698
|
+
return allMatches
|
|
1699
|
+
.sort((a, b) => b.timestamp - a.timestamp)
|
|
1700
|
+
.slice(offset, offset + limit);
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* 获取状态码为 499 的请求日志数量
|
|
1705
|
+
* @returns 匹配的请求数量
|
|
1706
|
+
*/
|
|
1707
|
+
getClientClosedLogsCount() {
|
|
1708
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1709
|
+
let count = 0;
|
|
1710
|
+
for (const shard of this.logShardsIndex) {
|
|
1711
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
1712
|
+
for (const log of shardLogs) {
|
|
1713
|
+
if (log.statusCode === 499) {
|
|
1714
|
+
count++;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
return count;
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
// Service blacklist operations
|
|
1605
1722
|
// Service blacklist operations
|
|
1723
|
+
getFailoverRecoveryMs() {
|
|
1724
|
+
var _a;
|
|
1725
|
+
const seconds = normalizeFailoverRecoverySeconds((_a = this.config) === null || _a === void 0 ? void 0 : _a.failoverRecoverySeconds);
|
|
1726
|
+
return seconds * 1000;
|
|
1727
|
+
}
|
|
1606
1728
|
isServiceBlacklisted(serviceId, routeId, contentType) {
|
|
1607
1729
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1608
1730
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
@@ -1621,10 +1743,11 @@ class FileSystemDatabaseManager {
|
|
|
1621
1743
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1622
1744
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
1623
1745
|
const now = Date.now();
|
|
1746
|
+
const recoveryMs = this.getFailoverRecoveryMs();
|
|
1624
1747
|
const existing = this.blacklist.get(key);
|
|
1625
1748
|
if (existing) {
|
|
1626
1749
|
existing.blacklistedAt = now;
|
|
1627
|
-
existing.expiresAt = now +
|
|
1750
|
+
existing.expiresAt = now + recoveryMs;
|
|
1628
1751
|
existing.errorCount++;
|
|
1629
1752
|
existing.lastError = errorMessage;
|
|
1630
1753
|
existing.lastStatusCode = statusCode;
|
|
@@ -1636,7 +1759,7 @@ class FileSystemDatabaseManager {
|
|
|
1636
1759
|
routeId,
|
|
1637
1760
|
contentType,
|
|
1638
1761
|
blacklistedAt: now,
|
|
1639
|
-
expiresAt: now +
|
|
1762
|
+
expiresAt: now + recoveryMs,
|
|
1640
1763
|
errorCount: 1,
|
|
1641
1764
|
lastError: errorMessage,
|
|
1642
1765
|
lastStatusCode: statusCode,
|
|
@@ -1675,7 +1798,12 @@ class FileSystemDatabaseManager {
|
|
|
1675
1798
|
}
|
|
1676
1799
|
updateConfig(config) {
|
|
1677
1800
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1678
|
-
this.config
|
|
1801
|
+
const merged = Object.assign(Object.assign({}, (this.config || {})), config);
|
|
1802
|
+
if (!isCodexReasoningEffort(merged.codexModelReasoningEffort)) {
|
|
1803
|
+
merged.codexModelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT;
|
|
1804
|
+
}
|
|
1805
|
+
merged.failoverRecoverySeconds = normalizeFailoverRecoverySeconds(merged.failoverRecoverySeconds);
|
|
1806
|
+
this.config = merged;
|
|
1679
1807
|
yield this.saveConfig();
|
|
1680
1808
|
return true;
|
|
1681
1809
|
});
|
|
@@ -1693,6 +1821,9 @@ class FileSystemDatabaseManager {
|
|
|
1693
1821
|
if (!vendor.name || typeof vendor.name !== 'string') {
|
|
1694
1822
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 缺少有效的 name 字段` };
|
|
1695
1823
|
}
|
|
1824
|
+
if (vendor.apiKey !== undefined && typeof vendor.apiKey !== 'string') {
|
|
1825
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 apiKey 必须是字符串` };
|
|
1826
|
+
}
|
|
1696
1827
|
if (!Array.isArray(vendor.services)) {
|
|
1697
1828
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 services 不是数组` };
|
|
1698
1829
|
}
|
|
@@ -1711,7 +1842,12 @@ class FileSystemDatabaseManager {
|
|
|
1711
1842
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiUrl 字段` };
|
|
1712
1843
|
}
|
|
1713
1844
|
if (!service.apiKey || typeof service.apiKey !== 'string') {
|
|
1714
|
-
|
|
1845
|
+
if (service.inheritVendorApiKey !== true) {
|
|
1846
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiKey 字段` };
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (service.inheritVendorApiKey !== undefined && typeof service.inheritVendorApiKey !== 'boolean') {
|
|
1850
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] inheritVendorApiKey 必须是布尔值` };
|
|
1715
1851
|
}
|
|
1716
1852
|
}
|
|
1717
1853
|
return { valid: true };
|
|
@@ -1898,6 +2034,7 @@ class FileSystemDatabaseManager {
|
|
|
1898
2034
|
}
|
|
1899
2035
|
importData(encryptedData, password) {
|
|
1900
2036
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2037
|
+
var _a;
|
|
1901
2038
|
try {
|
|
1902
2039
|
// 解密
|
|
1903
2040
|
let jsonData;
|
|
@@ -1933,7 +2070,7 @@ class FileSystemDatabaseManager {
|
|
|
1933
2070
|
this.vendors = importData.vendors.map((v) => (Object.assign(Object.assign({}, v), { updatedAt: now })));
|
|
1934
2071
|
this.routes = importData.routes.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1935
2072
|
this.rules = importData.rules.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1936
|
-
this.config = Object.assign(Object.assign({}, importData.config), { updatedAt: now });
|
|
2073
|
+
this.config = Object.assign(Object.assign({}, importData.config), { failoverRecoverySeconds: normalizeFailoverRecoverySeconds((_a = importData.config) === null || _a === void 0 ? void 0 : _a.failoverRecoverySeconds), updatedAt: now });
|
|
1937
2074
|
// 保存数据
|
|
1938
2075
|
yield Promise.all([
|
|
1939
2076
|
this.saveVendors(),
|