aicodeswitch 3.0.3 → 3.0.4

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 CHANGED
@@ -105,6 +105,7 @@ Codex的配置覆盖逻辑一模一样。
105
105
  * Claude Chat
106
106
  * Claude Code
107
107
  * DeepSeek Chat
108
+ * Gemini
108
109
 
109
110
  **有什么用?**
110
111
 
@@ -181,6 +181,16 @@ class FileSystemDatabaseManager {
181
181
  writable: true,
182
182
  value: 30
183
183
  });
184
+ // Export/Import operations
185
+ /**
186
+ * 当前支持的导出数据版本
187
+ */
188
+ Object.defineProperty(this, "CURRENT_EXPORT_VERSION", {
189
+ enumerable: true,
190
+ configurable: true,
191
+ writable: true,
192
+ value: '3.0.0'
193
+ });
184
194
  this.dataPath = dataPath;
185
195
  }
186
196
  initialize() {
@@ -1362,23 +1372,153 @@ class FileSystemDatabaseManager {
1362
1372
  return true;
1363
1373
  });
1364
1374
  }
1365
- // Export/Import operations
1375
+ /**
1376
+ * 验证供应商数据格式
1377
+ */
1378
+ validateVendor(vendor, index) {
1379
+ if (!vendor || typeof vendor !== 'object') {
1380
+ return { valid: false, error: `供应商[${index}] 不是有效的对象` };
1381
+ }
1382
+ if (!vendor.id || typeof vendor.id !== 'string') {
1383
+ return { valid: false, error: `供应商[${index}] 缺少有效的 id 字段` };
1384
+ }
1385
+ if (!vendor.name || typeof vendor.name !== 'string') {
1386
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 缺少有效的 name 字段` };
1387
+ }
1388
+ if (!Array.isArray(vendor.services)) {
1389
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的 services 不是数组` };
1390
+ }
1391
+ for (let i = 0; i < vendor.services.length; i++) {
1392
+ const service = vendor.services[i];
1393
+ if (!service || typeof service !== 'object') {
1394
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 不是有效的对象` };
1395
+ }
1396
+ if (!service.id || typeof service.id !== 'string') {
1397
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 id 字段` };
1398
+ }
1399
+ if (!service.name || typeof service.name !== 'string') {
1400
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 name 字段` };
1401
+ }
1402
+ if (!service.apiUrl || typeof service.apiUrl !== 'string') {
1403
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiUrl 字段` };
1404
+ }
1405
+ if (!service.apiKey || typeof service.apiKey !== 'string') {
1406
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] 缺少有效的 apiKey 字段` };
1407
+ }
1408
+ }
1409
+ return { valid: true };
1410
+ }
1411
+ /**
1412
+ * 验证路由数据格式
1413
+ */
1414
+ validateRoute(route, index) {
1415
+ if (!route || typeof route !== 'object') {
1416
+ return { valid: false, error: `路由[${index}] 不是有效的对象` };
1417
+ }
1418
+ if (!route.id || typeof route.id !== 'string') {
1419
+ return { valid: false, error: `路由[${index}] 缺少有效的 id 字段` };
1420
+ }
1421
+ if (!route.name || typeof route.name !== 'string') {
1422
+ return { valid: false, error: `路由[${index}](${route.id}) 缺少有效的 name 字段` };
1423
+ }
1424
+ if (!route.targetType || !['claude-code', 'codex'].includes(route.targetType)) {
1425
+ return { valid: false, error: `路由[${index}](${route.id}) 的 targetType 必须是 'claude-code' 或 'codex'` };
1426
+ }
1427
+ if (typeof route.isActive !== 'boolean') {
1428
+ return { valid: false, error: `路由[${index}](${route.id}) 的 isActive 必须是布尔值` };
1429
+ }
1430
+ return { valid: true };
1431
+ }
1432
+ /**
1433
+ * 验证规则数据格式
1434
+ */
1435
+ validateRule(rule, index) {
1436
+ if (!rule || typeof rule !== 'object') {
1437
+ return { valid: false, error: `规则[${index}] 不是有效的对象` };
1438
+ }
1439
+ if (!rule.id || typeof rule.id !== 'string') {
1440
+ return { valid: false, error: `规则[${index}] 缺少有效的 id 字段` };
1441
+ }
1442
+ if (!rule.routeId || typeof rule.routeId !== 'string') {
1443
+ return { valid: false, error: `规则[${index}](${rule.id}) 缺少有效的 routeId 字段` };
1444
+ }
1445
+ if (!rule.targetServiceId || typeof rule.targetServiceId !== 'string') {
1446
+ return { valid: false, error: `规则[${index}](${rule.id}) 缺少有效的 targetServiceId 字段` };
1447
+ }
1448
+ const validContentTypes = ['default', 'background', 'thinking', 'long-context', 'image-understanding', 'model-mapping'];
1449
+ if (!rule.contentType || !validContentTypes.includes(rule.contentType)) {
1450
+ return { valid: false, error: `规则[${index}](${rule.id}) 的 contentType 无效` };
1451
+ }
1452
+ return { valid: true };
1453
+ }
1454
+ /**
1455
+ * 验证配置数据格式
1456
+ */
1457
+ validateConfig(config) {
1458
+ if (!config || typeof config !== 'object') {
1459
+ return { valid: false, error: 'config 不是有效的对象' };
1460
+ }
1461
+ return { valid: true };
1462
+ }
1463
+ /**
1464
+ * 验证导出数据格式(严格校验)
1465
+ */
1466
+ validateExportData(data) {
1467
+ if (!data || typeof data !== 'object') {
1468
+ return { valid: false, error: '数据不是有效的对象' };
1469
+ }
1470
+ // 检查必需字段是否存在
1471
+ if (!data.version || typeof data.version !== 'string') {
1472
+ return { valid: false, error: '缺少有效的 version 字段' };
1473
+ }
1474
+ // 检查版本是否匹配当前版本
1475
+ if (data.version !== this.CURRENT_EXPORT_VERSION) {
1476
+ return { valid: false, error: `数据版本 ${data.version} 与当前支持的版本 ${this.CURRENT_EXPORT_VERSION} 不匹配。请使用相同版本的系统导出数据。` };
1477
+ }
1478
+ if (!data.exportDate || typeof data.exportDate !== 'number') {
1479
+ return { valid: false, error: '缺少有效的 exportDate 字段' };
1480
+ }
1481
+ // 检查 vendors
1482
+ if (!Array.isArray(data.vendors)) {
1483
+ return { valid: false, error: 'vendors 不是数组' };
1484
+ }
1485
+ for (let i = 0; i < data.vendors.length; i++) {
1486
+ const result = this.validateVendor(data.vendors[i], i);
1487
+ if (!result.valid)
1488
+ return result;
1489
+ }
1490
+ // 检查 routes
1491
+ if (!Array.isArray(data.routes)) {
1492
+ return { valid: false, error: 'routes 不是数组' };
1493
+ }
1494
+ for (let i = 0; i < data.routes.length; i++) {
1495
+ const result = this.validateRoute(data.routes[i], i);
1496
+ if (!result.valid)
1497
+ return result;
1498
+ }
1499
+ // 检查 rules
1500
+ if (!Array.isArray(data.rules)) {
1501
+ return { valid: false, error: 'rules 不是数组' };
1502
+ }
1503
+ for (let i = 0; i < data.rules.length; i++) {
1504
+ const result = this.validateRule(data.rules[i], i);
1505
+ if (!result.valid)
1506
+ return result;
1507
+ }
1508
+ // 检查 config
1509
+ const configResult = this.validateConfig(data.config);
1510
+ if (!configResult.valid)
1511
+ return configResult;
1512
+ return { valid: true };
1513
+ }
1366
1514
  exportData(password) {
1367
1515
  return __awaiter(this, void 0, void 0, function* () {
1368
- // 扁平化所有服务(兼容旧格式)
1369
- const allServices = [];
1370
- for (const vendor of this.vendors) {
1371
- if (vendor.services) {
1372
- // 为每个服务添加 vendorId(兼容旧格式)
1373
- const servicesWithVendorId = vendor.services.map(s => (Object.assign(Object.assign({}, s), { vendorId: vendor.id })));
1374
- allServices.push(...servicesWithVendorId);
1375
- }
1376
- }
1516
+ // 只导出当前格式,不再兼容旧格式
1377
1517
  const exportData = {
1378
- version: '2.0.0', // 更新版本号
1518
+ version: this.CURRENT_EXPORT_VERSION,
1379
1519
  exportDate: Date.now(),
1380
1520
  vendors: this.vendors,
1381
- apiServices: allServices, // 兼容旧格式
1521
+ apiServices: [], // 保留字段以兼容类型定义,但内容为空
1382
1522
  routes: this.routes,
1383
1523
  rules: this.rules,
1384
1524
  config: this.config,
@@ -1388,49 +1528,111 @@ class FileSystemDatabaseManager {
1388
1528
  return encrypted;
1389
1529
  });
1390
1530
  }
1391
- importData(encryptedData, password) {
1531
+ /**
1532
+ * 预览导入数据
1533
+ */
1534
+ previewImportData(encryptedData, password) {
1392
1535
  return __awaiter(this, void 0, void 0, function* () {
1393
1536
  try {
1394
- const decrypted = crypto_js_1.default.AES.decrypt(encryptedData, password);
1395
- const jsonData = decrypted.toString(crypto_js_1.default.enc.Utf8);
1396
- const importData = JSON.parse(jsonData);
1397
- // 检测数据版本
1398
- const isNewFormat = importData.vendors.some((v) => v.services);
1399
- if (isNewFormat || importData.version >= '2.0.0') {
1400
- // 新格式:直接使用 vendors(已包含 services)
1401
- this.vendors = importData.vendors;
1537
+ // 解密
1538
+ let jsonData;
1539
+ try {
1540
+ const decrypted = crypto_js_1.default.AES.decrypt(encryptedData, password);
1541
+ jsonData = decrypted.toString(crypto_js_1.default.enc.Utf8);
1542
+ if (!jsonData) {
1543
+ return { success: false, message: '解密失败:密码错误或数据损坏' };
1544
+ }
1402
1545
  }
1403
- else {
1404
- // 旧格式:需要重建 services 关系
1405
- const { vendors, apiServices } = importData;
1406
- // 构建 vendorId -> services 映射
1407
- const servicesByVendor = new Map();
1408
- for (const service of apiServices) {
1409
- if (!service.vendorId)
1410
- continue;
1411
- if (!servicesByVendor.has(service.vendorId)) {
1412
- servicesByVendor.set(service.vendorId, []);
1413
- }
1414
- const { vendorId } = service, serviceWithoutVendorId = __rest(service, ["vendorId"]);
1415
- servicesByVendor.get(service.vendorId).push(serviceWithoutVendorId);
1546
+ catch (error) {
1547
+ return { success: false, message: '解密失败:密码错误或数据格式错误' };
1548
+ }
1549
+ // 解析 JSON
1550
+ let importData;
1551
+ try {
1552
+ importData = JSON.parse(jsonData);
1553
+ }
1554
+ catch (error) {
1555
+ return { success: false, message: '数据解析失败:不是有效的 JSON 格式' };
1556
+ }
1557
+ // 验证数据格式
1558
+ const validation = this.validateExportData(importData);
1559
+ if (!validation.valid) {
1560
+ return { success: false, message: `数据验证失败:${validation.error}` };
1561
+ }
1562
+ // 计算服务数量
1563
+ const servicesCount = importData.vendors.reduce((sum, v) => { var _a; return sum + (((_a = v.services) === null || _a === void 0 ? void 0 : _a.length) || 0); }, 0);
1564
+ return {
1565
+ success: true,
1566
+ data: {
1567
+ vendors: importData.vendors.length,
1568
+ services: servicesCount,
1569
+ routes: importData.routes.length,
1570
+ rules: importData.rules.length,
1571
+ exportDate: importData.exportDate,
1572
+ version: importData.version,
1573
+ }
1574
+ };
1575
+ }
1576
+ catch (error) {
1577
+ console.error('Preview import error:', error);
1578
+ return { success: false, message: `预览失败:${error instanceof Error ? error.message : '未知错误'}` };
1579
+ }
1580
+ });
1581
+ }
1582
+ importData(encryptedData, password) {
1583
+ return __awaiter(this, void 0, void 0, function* () {
1584
+ try {
1585
+ // 解密
1586
+ let jsonData;
1587
+ try {
1588
+ const decrypted = crypto_js_1.default.AES.decrypt(encryptedData, password);
1589
+ jsonData = decrypted.toString(crypto_js_1.default.enc.Utf8);
1590
+ if (!jsonData) {
1591
+ return { success: false, message: '导入失败', details: '解密失败:密码错误或数据损坏' };
1416
1592
  }
1417
- // 合并到 vendors
1418
- this.vendors = vendors.map((vendor) => (Object.assign(Object.assign({}, vendor), { services: servicesByVendor.get(vendor.id) || [] })));
1419
1593
  }
1420
- this.routes = importData.routes;
1421
- this.rules = importData.rules;
1422
- this.config = importData.config;
1594
+ catch (error) {
1595
+ return { success: false, message: '导入失败', details: '解密失败:密码错误或数据格式错误' };
1596
+ }
1597
+ // 解析 JSON
1598
+ let importData;
1599
+ try {
1600
+ importData = JSON.parse(jsonData);
1601
+ }
1602
+ catch (error) {
1603
+ return { success: false, message: '导入失败', details: '数据解析失败:不是有效的 JSON 格式' };
1604
+ }
1605
+ // 验证数据格式
1606
+ const validation = this.validateExportData(importData);
1607
+ if (!validation.valid) {
1608
+ return { success: false, message: '导入失败', details: `数据验证失败:${validation.error}` };
1609
+ }
1610
+ // 导入数据(更新 updatedAt)
1611
+ const now = Date.now();
1612
+ this.vendors = importData.vendors.map((v) => (Object.assign(Object.assign({}, v), { updatedAt: now })));
1613
+ this.routes = importData.routes.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
1614
+ this.rules = importData.rules.map((r) => (Object.assign(Object.assign({}, r), { updatedAt: now })));
1615
+ this.config = Object.assign(Object.assign({}, importData.config), { updatedAt: now });
1616
+ // 保存数据
1423
1617
  yield Promise.all([
1424
1618
  this.saveVendors(),
1425
- // 删除: this.saveServices(),
1426
1619
  this.saveRoutes(),
1427
1620
  this.saveConfig(),
1428
1621
  ]);
1429
- return true;
1622
+ const servicesCount = this.vendors.reduce((sum, v) => { var _a; return sum + (((_a = v.services) === null || _a === void 0 ? void 0 : _a.length) || 0); }, 0);
1623
+ return {
1624
+ success: true,
1625
+ message: '导入成功',
1626
+ details: `已导入 ${this.vendors.length} 个供应商、${servicesCount} 个服务、${this.routes.length} 个路由、${this.rules.length} 个规则`
1627
+ };
1430
1628
  }
1431
1629
  catch (error) {
1432
1630
  console.error('Import error:', error);
1433
- return false;
1631
+ return {
1632
+ success: false,
1633
+ message: '导入失败',
1634
+ details: error instanceof Error ? error.message : '未知错误'
1635
+ };
1434
1636
  }
1435
1637
  });
1436
1638
  }
@@ -1274,10 +1274,16 @@ ${instruction}
1274
1274
  const data = yield dbManager.exportData(password);
1275
1275
  res.json({ data });
1276
1276
  })));
1277
+ // 导入数据预览
1278
+ app.post('/api/import/preview', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1279
+ const { encryptedData, password } = req.body;
1280
+ const result = yield dbManager.previewImportData(encryptedData, password);
1281
+ res.json(result);
1282
+ })));
1277
1283
  app.post('/api/import', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1278
1284
  const { encryptedData, password } = req.body;
1279
1285
  const result = yield dbManager.importData(encryptedData, password);
1280
- if (result) {
1286
+ if (result.success) {
1281
1287
  yield proxyServer.reloadRoutes();
1282
1288
  }
1283
1289
  res.json(result);