aicodeswitch 3.9.3 → 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 +211 -31
- package/dist/server/main.js +380 -241
- package/dist/server/original-config-reader.js +154 -88
- package/dist/server/proxy-server.js +993 -385
- 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-DNtgPQMm.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,6 +222,8 @@ class FileSystemDatabaseManager {
|
|
|
209
222
|
yield this.loadAllData();
|
|
210
223
|
// 执行数据源类型迁移(在加载数据之后)
|
|
211
224
|
yield this.migrateSourceTypes();
|
|
225
|
+
// 路由级工具配置迁移到全局配置(兼容旧版本)
|
|
226
|
+
yield this.migrateRouteToolSettingsToGlobalConfig();
|
|
212
227
|
// 确保默认配置
|
|
213
228
|
yield this.ensureDefaultConfig();
|
|
214
229
|
});
|
|
@@ -231,15 +246,37 @@ class FileSystemDatabaseManager {
|
|
|
231
246
|
}
|
|
232
247
|
loadVendors() {
|
|
233
248
|
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
let needSave = false;
|
|
234
250
|
try {
|
|
235
251
|
const data = yield promises_1.default.readFile(this.vendorsFile, 'utf-8');
|
|
236
|
-
|
|
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
|
+
}) : [];
|
|
237
271
|
}
|
|
238
272
|
catch (_a) {
|
|
239
273
|
this.vendors = [];
|
|
240
274
|
}
|
|
241
275
|
// 兼容性检查:如果存在旧的 services.json,自动迁移
|
|
242
276
|
yield this.migrateServicesIfNeeded();
|
|
277
|
+
if (needSave) {
|
|
278
|
+
yield this.saveVendors();
|
|
279
|
+
}
|
|
243
280
|
});
|
|
244
281
|
}
|
|
245
282
|
/**
|
|
@@ -364,7 +401,9 @@ class FileSystemDatabaseManager {
|
|
|
364
401
|
migrateVendorsOnImport(vendors) {
|
|
365
402
|
return vendors.map(vendor => {
|
|
366
403
|
var _a;
|
|
367
|
-
return (Object.assign(Object.assign({}, vendor), { services: (_a = vendor.services) === null || _a === void 0 ? void 0 : _a.map(service =>
|
|
404
|
+
return (Object.assign(Object.assign({}, vendor), { services: (_a = vendor.services) === null || _a === void 0 ? void 0 : _a.map(service => {
|
|
405
|
+
return Object.assign(Object.assign({}, service), { sourceType: service.sourceType ? (0, type_migration_1.normalizeSourceType)(service.sourceType) : undefined });
|
|
406
|
+
}) }));
|
|
368
407
|
});
|
|
369
408
|
}
|
|
370
409
|
saveVendors() {
|
|
@@ -784,22 +823,89 @@ class FileSystemDatabaseManager {
|
|
|
784
823
|
}
|
|
785
824
|
ensureDefaultConfig() {
|
|
786
825
|
return __awaiter(this, void 0, void 0, function* () {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
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)) {
|
|
799
847
|
yield this.saveConfig();
|
|
800
848
|
}
|
|
801
849
|
});
|
|
802
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
|
+
}
|
|
803
909
|
// Vendor operations
|
|
804
910
|
getVendors() {
|
|
805
911
|
return [...this.vendors].sort((a, b) => {
|
|
@@ -818,7 +924,7 @@ class FileSystemDatabaseManager {
|
|
|
818
924
|
console.log('[数据库] 创建供应商,输入数据:', JSON.stringify(vendor, null, 2));
|
|
819
925
|
const id = crypto_1.default.randomUUID();
|
|
820
926
|
const now = Date.now();
|
|
821
|
-
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 });
|
|
822
928
|
console.log('[数据库] 创建供应商,返回数据:', JSON.stringify(newVendor, null, 2));
|
|
823
929
|
this.vendors.push(newVendor);
|
|
824
930
|
yield this.saveVendors();
|
|
@@ -831,7 +937,11 @@ class FileSystemDatabaseManager {
|
|
|
831
937
|
if (index === -1)
|
|
832
938
|
return false;
|
|
833
939
|
const now = Date.now();
|
|
834
|
-
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 });
|
|
835
945
|
yield this.saveVendors();
|
|
836
946
|
return true;
|
|
837
947
|
});
|
|
@@ -841,12 +951,15 @@ class FileSystemDatabaseManager {
|
|
|
841
951
|
const index = this.vendors.findIndex(v => v.id === id);
|
|
842
952
|
if (index === -1)
|
|
843
953
|
return false;
|
|
844
|
-
//
|
|
954
|
+
// 级联删除:删除该供应商下服务关联的所有规则
|
|
845
955
|
const vendor = this.vendors[index];
|
|
846
956
|
const serviceIds = (vendor.services || []).map(s => s.id);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
+
}
|
|
850
963
|
}
|
|
851
964
|
this.vendors.splice(index, 1);
|
|
852
965
|
yield this.saveVendors();
|
|
@@ -911,7 +1024,7 @@ class FileSystemDatabaseManager {
|
|
|
911
1024
|
const _a = service, { vendorId: _ } = _a, serviceData = __rest(_a, ["vendorId"]);
|
|
912
1025
|
const id = crypto_1.default.randomUUID();
|
|
913
1026
|
const now = Date.now();
|
|
914
|
-
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 });
|
|
915
1028
|
console.log('[数据库] 创建服务,最终数据:', JSON.stringify(newService, null, 2));
|
|
916
1029
|
if (!vendor.services) {
|
|
917
1030
|
vendor.services = [];
|
|
@@ -934,7 +1047,9 @@ class FileSystemDatabaseManager {
|
|
|
934
1047
|
if (index === -1)
|
|
935
1048
|
return false;
|
|
936
1049
|
const now = Date.now();
|
|
937
|
-
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 });
|
|
938
1053
|
// 更新供应商的 updatedAt 时间
|
|
939
1054
|
vendor.updatedAt = now;
|
|
940
1055
|
yield this.saveVendors();
|
|
@@ -952,10 +1067,11 @@ class FileSystemDatabaseManager {
|
|
|
952
1067
|
const index = vendor.services.findIndex(s => s.id === id);
|
|
953
1068
|
if (index === -1)
|
|
954
1069
|
return false;
|
|
955
|
-
//
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
|
|
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();
|
|
959
1075
|
}
|
|
960
1076
|
vendor.services.splice(index, 1);
|
|
961
1077
|
// 更新供应商的 updatedAt 时间
|
|
@@ -1559,7 +1675,56 @@ class FileSystemDatabaseManager {
|
|
|
1559
1675
|
}
|
|
1560
1676
|
return false;
|
|
1561
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
|
+
}
|
|
1562
1721
|
// Service blacklist operations
|
|
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
|
+
}
|
|
1563
1728
|
isServiceBlacklisted(serviceId, routeId, contentType) {
|
|
1564
1729
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1565
1730
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
@@ -1578,10 +1743,11 @@ class FileSystemDatabaseManager {
|
|
|
1578
1743
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1579
1744
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
1580
1745
|
const now = Date.now();
|
|
1746
|
+
const recoveryMs = this.getFailoverRecoveryMs();
|
|
1581
1747
|
const existing = this.blacklist.get(key);
|
|
1582
1748
|
if (existing) {
|
|
1583
1749
|
existing.blacklistedAt = now;
|
|
1584
|
-
existing.expiresAt = now +
|
|
1750
|
+
existing.expiresAt = now + recoveryMs;
|
|
1585
1751
|
existing.errorCount++;
|
|
1586
1752
|
existing.lastError = errorMessage;
|
|
1587
1753
|
existing.lastStatusCode = statusCode;
|
|
@@ -1593,7 +1759,7 @@ class FileSystemDatabaseManager {
|
|
|
1593
1759
|
routeId,
|
|
1594
1760
|
contentType,
|
|
1595
1761
|
blacklistedAt: now,
|
|
1596
|
-
expiresAt: now +
|
|
1762
|
+
expiresAt: now + recoveryMs,
|
|
1597
1763
|
errorCount: 1,
|
|
1598
1764
|
lastError: errorMessage,
|
|
1599
1765
|
lastStatusCode: statusCode,
|
|
@@ -1632,7 +1798,12 @@ class FileSystemDatabaseManager {
|
|
|
1632
1798
|
}
|
|
1633
1799
|
updateConfig(config) {
|
|
1634
1800
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1635
|
-
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;
|
|
1636
1807
|
yield this.saveConfig();
|
|
1637
1808
|
return true;
|
|
1638
1809
|
});
|
|
@@ -1650,6 +1821,9 @@ class FileSystemDatabaseManager {
|
|
|
1650
1821
|
if (!vendor.name || typeof vendor.name !== 'string') {
|
|
1651
1822
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 缺少有效的 name 字段` };
|
|
1652
1823
|
}
|
|
1824
|
+
if (vendor.apiKey !== undefined && typeof vendor.apiKey !== 'string') {
|
|
1825
|
+
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 apiKey 必须是字符串` };
|
|
1826
|
+
}
|
|
1653
1827
|
if (!Array.isArray(vendor.services)) {
|
|
1654
1828
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的 services 不是数组` };
|
|
1655
1829
|
}
|
|
@@ -1668,7 +1842,12 @@ class FileSystemDatabaseManager {
|
|
|
1668
1842
|
return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiUrl 字段` };
|
|
1669
1843
|
}
|
|
1670
1844
|
if (!service.apiKey || typeof service.apiKey !== 'string') {
|
|
1671
|
-
|
|
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 必须是布尔值` };
|
|
1672
1851
|
}
|
|
1673
1852
|
}
|
|
1674
1853
|
return { valid: true };
|
|
@@ -1855,6 +2034,7 @@ class FileSystemDatabaseManager {
|
|
|
1855
2034
|
}
|
|
1856
2035
|
importData(encryptedData, password) {
|
|
1857
2036
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2037
|
+
var _a;
|
|
1858
2038
|
try {
|
|
1859
2039
|
// 解密
|
|
1860
2040
|
let jsonData;
|
|
@@ -1890,7 +2070,7 @@ class FileSystemDatabaseManager {
|
|
|
1890
2070
|
this.vendors = importData.vendors.map((v) => (Object.assign(Object.assign({}, v), { updatedAt: now })));
|
|
1891
2071
|
this.routes = importData.routes.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1892
2072
|
this.rules = importData.rules.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
|
|
1893
|
-
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 });
|
|
1894
2074
|
// 保存数据
|
|
1895
2075
|
yield Promise.all([
|
|
1896
2076
|
this.saveVendors(),
|