aicodeswitch 4.0.4 → 5.1.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.
Files changed (77) hide show
  1. package/README.md +6 -5
  2. package/UPGRADE.md +5 -6
  3. package/dist/server/coding-plan.js +94 -0
  4. package/dist/server/config-managed-fields.js +1 -0
  5. package/dist/server/conversions/compact.js +613 -0
  6. package/dist/server/conversions/detector.js +70 -0
  7. package/dist/server/conversions/index.js +290 -0
  8. package/dist/server/conversions/pairs/claude-completions/request.js +167 -0
  9. package/dist/server/conversions/pairs/claude-completions/response.js +56 -0
  10. package/dist/server/conversions/pairs/claude-completions/streaming.js +259 -0
  11. package/dist/server/conversions/pairs/claude-gemini/request.js +130 -0
  12. package/dist/server/conversions/pairs/claude-gemini/response.js +65 -0
  13. package/dist/server/conversions/pairs/claude-gemini/streaming.js +199 -0
  14. package/dist/server/conversions/pairs/claude-responses/request.js +190 -0
  15. package/dist/server/conversions/pairs/claude-responses/response.js +89 -0
  16. package/dist/server/conversions/pairs/claude-responses/streaming.js +266 -0
  17. package/dist/server/conversions/pairs/completions-claude/request.js +111 -0
  18. package/dist/server/conversions/pairs/completions-claude/response.js +67 -0
  19. package/dist/server/conversions/pairs/completions-claude/streaming.js +165 -0
  20. package/dist/server/conversions/pairs/completions-gemini/request.js +169 -0
  21. package/dist/server/conversions/pairs/completions-gemini/response.js +70 -0
  22. package/dist/server/conversions/pairs/completions-gemini/streaming.js +132 -0
  23. package/dist/server/conversions/pairs/completions-responses/request.js +149 -0
  24. package/dist/server/conversions/pairs/completions-responses/response.js +74 -0
  25. package/dist/server/conversions/pairs/completions-responses/streaming.js +189 -0
  26. package/dist/server/conversions/pairs/gemini-claude/request.js +118 -0
  27. package/dist/server/conversions/pairs/gemini-claude/response.js +45 -0
  28. package/dist/server/conversions/pairs/gemini-claude/streaming.js +146 -0
  29. package/dist/server/conversions/pairs/gemini-completions/request.js +151 -0
  30. package/dist/server/conversions/pairs/gemini-completions/response.js +54 -0
  31. package/dist/server/conversions/pairs/gemini-completions/streaming.js +108 -0
  32. package/dist/server/conversions/pairs/gemini-responses/request.js +18 -0
  33. package/dist/server/conversions/pairs/gemini-responses/response.js +18 -0
  34. package/dist/server/conversions/pairs/gemini-responses/streaming.js +43 -0
  35. package/dist/server/conversions/pairs/responses-claude/request.js +180 -0
  36. package/dist/server/conversions/pairs/responses-claude/response.js +70 -0
  37. package/dist/server/conversions/pairs/responses-claude/streaming.js +345 -0
  38. package/dist/server/conversions/pairs/responses-completions/request.js +207 -0
  39. package/dist/server/conversions/pairs/responses-completions/response.js +96 -0
  40. package/dist/server/conversions/pairs/responses-completions/streaming.js +344 -0
  41. package/dist/server/conversions/pairs/responses-gemini/request.js +18 -0
  42. package/dist/server/conversions/pairs/responses-gemini/response.js +18 -0
  43. package/dist/server/conversions/pairs/responses-gemini/streaming.js +43 -0
  44. package/dist/server/conversions/pairs/responses-responses/request.js +115 -0
  45. package/dist/server/conversions/pipeline.js +296 -0
  46. package/dist/server/conversions/stream-converter-adapter.js +49 -0
  47. package/dist/server/conversions/thinking/effort.js +61 -0
  48. package/dist/server/conversions/thinking/mapper.js +59 -0
  49. package/dist/server/conversions/thinking/providers.js +80 -0
  50. package/dist/server/conversions/types.js +5 -0
  51. package/dist/server/conversions/url-normalizer.js +58 -0
  52. package/dist/server/conversions/utils/format-mappers.js +57 -0
  53. package/dist/server/conversions/utils/id.js +33 -0
  54. package/dist/server/conversions/utils/stop-reasons.js +95 -0
  55. package/dist/server/conversions/utils/streaming-helpers.js +59 -0
  56. package/dist/server/conversions/utils/tool-schema.js +169 -0
  57. package/dist/server/conversions/utils/usage.js +82 -0
  58. package/dist/server/fs-database.js +465 -135
  59. package/dist/server/main.js +93 -33
  60. package/dist/server/original-config-reader.js +1 -1
  61. package/dist/server/proxy-server.js +887 -633
  62. package/dist/server/transformers/chunk-collector.js +5 -1
  63. package/dist/server/transformers/streaming.js +6 -3235
  64. package/dist/server/type-migration.js +2 -3
  65. package/dist/server/utils.js +5 -0
  66. package/dist/ui/assets/{index-C7G0whng.css → index-BHR12ImE.css} +1 -1
  67. package/dist/ui/assets/index-Rwiqttz-.js +517 -0
  68. package/dist/ui/index.html +2 -2
  69. package/package.json +1 -1
  70. package/dist/server/transformers/transformers.js +0 -1767
  71. package/dist/ui/assets/index-Dl-B9pXM.js +0 -514
  72. package/schema/claude.schema.md +0 -946
  73. package/schema/deepseek-chat.schema.md +0 -799
  74. package/schema/gemini.schema.md +0 -1408
  75. package/schema/openai-chat-completions.schema.md +0 -1088
  76. package/schema/openai-responses.schema.md +0 -226196
  77. package/schema/stream.md +0 -2592
@@ -111,7 +111,10 @@ const DEFAULT_CLAUDE_EFFORT_LEVEL = 'medium';
111
111
  const isClaudeEffortLevel = (value) => {
112
112
  return typeof value === 'string' && VALID_CLAUDE_EFFORT_LEVELS.includes(value);
113
113
  };
114
- const writeClaudeConfig = (dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, ...args_1) => __awaiter(void 0, [dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, ...args_1], void 0, function* (dbManager, enableAgentTeams, enableBypassPermissionsSupport, effortLevel, defaultModel, options = {}) {
114
+ const isValidAutocompactPct = (v) => {
115
+ return typeof v === 'number' && Number.isInteger(v) && v >= 1 && v <= 100;
116
+ };
117
+ const writeClaudeConfig = (dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, autocompactPctOverride_1, ...args_1) => __awaiter(void 0, [dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, autocompactPctOverride_1, ...args_1], void 0, function* (dbManager, enableAgentTeams, enableBypassPermissionsSupport, effortLevel, defaultModel, autocompactPctOverride, options = {}) {
115
118
  var _a;
116
119
  try {
117
120
  const homeDir = os_1.default.homedir();
@@ -191,6 +194,10 @@ const writeClaudeConfig = (dbManager_1, enableAgentTeams_1, enableBypassPermissi
191
194
  if (defaultModel && typeof defaultModel === 'string' && defaultModel.trim()) {
192
195
  proxySettings.model = defaultModel.trim();
193
196
  }
197
+ // 如果设置了自动压缩百分比阈值,添加对应的配置项
198
+ if (isValidAutocompactPct(autocompactPctOverride)) {
199
+ claudeSettingsEnv.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = String(autocompactPctOverride);
200
+ }
194
201
  // 使用智能合并:将代理配置的管理字段写入,保留当前配置的非管理字段
195
202
  const mergedSettings = (0, config_merge_1.mergeJsonConfig)(proxySettings, currentSettings, config_managed_fields_1.CLAUDE_SETTINGS_MANAGED_FIELDS);
196
203
  // 原子性写入合并后的配置
@@ -538,7 +545,7 @@ const syncConfigsOnServerStartup = (dbManager) => __awaiter(void 0, void 0, void
538
545
  const claudeEffortLevel = isClaudeEffortLevel(config.claudeEffortLevel)
539
546
  ? config.claudeEffortLevel
540
547
  : DEFAULT_CLAUDE_EFFORT_LEVEL;
541
- const claudeWritten = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel);
548
+ const claudeWritten = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel, config.autocompactPctOverride);
542
549
  console.log(`[Startup Config Sync] Claude Code config ${claudeWritten ? 'written' : 'skipped'}`);
543
550
  const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
544
551
  ? config.codexModelReasoningEffort
@@ -551,7 +558,7 @@ const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0,
551
558
  const claudeEffortLevel = isClaudeEffortLevel(config.claudeEffortLevel)
552
559
  ? config.claudeEffortLevel
553
560
  : DEFAULT_CLAUDE_EFFORT_LEVEL;
554
- const claudeUpdated = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel, { allowOverwriteRefresh: true });
561
+ const claudeUpdated = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel, config.autocompactPctOverride, { allowOverwriteRefresh: true });
555
562
  console.log(`[Config Update Sync] Claude Code config ${claudeUpdated ? 'written' : 'skipped'}`);
556
563
  const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
557
564
  ? config.codexModelReasoningEffort
@@ -1000,46 +1007,66 @@ const registerRoutes = (dbManager, proxyServer) => {
1000
1007
  app.get('/api/routes', (_req, res) => res.json(dbManager.getRoutes()));
1001
1008
  app.post('/api/routes', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () { return res.json(yield dbManager.createRoute(req.body)); })));
1002
1009
  app.put('/api/routes/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () { return res.json(yield dbManager.updateRoute(req.params.id, req.body)); })));
1003
- app.delete('/api/routes/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () { return res.json(yield dbManager.deleteRoute(req.params.id)); })));
1004
- app.post('/api/routes/:id/activate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1005
- const result = yield dbManager.activateRoute(req.params.id);
1010
+ app.delete('/api/routes/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1011
+ // Check if route is bound to any tool
1012
+ if (dbManager.isRouteBound(req.params.id)) {
1013
+ return res.status(400).json({ error: '该路由当前被工具使用中,请先停用后再删除' });
1014
+ }
1015
+ const result = yield dbManager.deleteRoute(req.params.id);
1016
+ res.json(result);
1017
+ })));
1018
+ // Tool Bindings API
1019
+ app.get('/api/tool-bindings', (_req, res) => {
1020
+ res.json(dbManager.getToolBindings());
1021
+ });
1022
+ app.post('/api/tool-bindings/activate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1023
+ const { tool, routeId } = req.body;
1024
+ if (!tool || !routeId) {
1025
+ return res.status(400).json({ error: 'tool and routeId are required' });
1026
+ }
1027
+ if (tool !== 'claude-code' && tool !== 'codex') {
1028
+ return res.status(400).json({ error: 'Invalid tool name' });
1029
+ }
1030
+ const route = dbManager.getRoute(routeId);
1031
+ if (!route) {
1032
+ return res.status(404).json({ error: 'Route not found' });
1033
+ }
1034
+ const result = yield dbManager.activateToolRoute(tool, routeId);
1006
1035
  if (result) {
1007
1036
  yield proxyServer.reloadRoutes();
1008
- // 激活路由后,同步MCP配置
1009
- const routes = dbManager.getRoutes();
1010
- const route = routes.find(r => r.id === req.params.id);
1011
- if (route) {
1012
- const mcps = dbManager.getMCPs();
1013
- const hasMCPForTarget = mcps.some(m => { var _a; return (_a = m.targets) === null || _a === void 0 ? void 0 : _a.includes(route.targetType); });
1014
- if (hasMCPForTarget) {
1015
- yield writeMCPConfig(route.targetType);
1016
- }
1037
+ // Sync MCP config for this tool
1038
+ const mcps = dbManager.getMCPs();
1039
+ const hasMCPForTarget = mcps.some(m => { var _a; return (_a = m.targets) === null || _a === void 0 ? void 0 : _a.includes(tool); });
1040
+ if (hasMCPForTarget) {
1041
+ yield writeMCPConfig(tool);
1017
1042
  }
1018
1043
  }
1019
- res.json(result);
1044
+ res.json({ success: result });
1020
1045
  })));
1021
- app.post('/api/routes/:id/deactivate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1022
- const result = yield dbManager.deactivateRoute(req.params.id);
1046
+ app.post('/api/tool-bindings/deactivate', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1047
+ const { tool } = req.body;
1048
+ if (!tool || (tool !== 'claude-code' && tool !== 'codex')) {
1049
+ return res.status(400).json({ error: 'Invalid tool name' });
1050
+ }
1051
+ const result = yield dbManager.deactivateToolRoute(tool);
1023
1052
  if (result) {
1024
1053
  yield proxyServer.reloadRoutes();
1025
1054
  }
1026
- res.json(result);
1055
+ res.json({ success: result });
1027
1056
  })));
1028
- // 批量停用所有激活的路由(用于应用关闭时清理)
1057
+ // 批量停用所有工具绑定(用于应用关闭时清理)
1029
1058
  app.post('/api/routes/deactivate-all', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
1030
- console.log('[Deactivate All Routes] Starting route deactivation...');
1031
- // 仅停用路由,不再在路由接口内处理配置文件恢复
1032
- console.log('[Deactivate All Routes] Deactivating all active routes...');
1033
- const deactivatedCount = yield dbManager.deactivateAllRoutes();
1059
+ console.log('[Deactivate All] Starting tool-bindings deactivation...');
1060
+ const deactivatedCount = yield dbManager.deactivateAllToolRoutes();
1034
1061
  if (deactivatedCount > 0) {
1035
- console.log(`[Deactivate All Routes] Deactivated ${deactivatedCount} route(s), reloading routes...`);
1062
+ console.log(`[Deactivate All] Deactivated ${deactivatedCount} tool binding(s), reloading routes...`);
1036
1063
  yield proxyServer.reloadRoutes();
1037
- console.log('[Deactivate All Routes] Routes reloaded successfully');
1064
+ console.log('[Deactivate All] Routes reloaded successfully');
1038
1065
  }
1039
1066
  else {
1040
- console.log('[Deactivate All Routes] No active routes to deactivate');
1067
+ console.log('[Deactivate All] No active tool bindings to deactivate');
1041
1068
  }
1042
- console.log('[Deactivate All Routes] Route deactivation completed');
1069
+ console.log('[Deactivate All] Deactivation completed');
1043
1070
  res.json({
1044
1071
  success: true,
1045
1072
  deactivatedCount
@@ -1572,7 +1599,7 @@ ${instruction}
1572
1599
  const enableBypassPermissionsSupport = typeof requestedBypass === 'boolean'
1573
1600
  ? requestedBypass
1574
1601
  : appConfig.enableBypassPermissionsSupport;
1575
- const result = yield writeClaudeConfig(dbManager, enableAgentTeams, enableBypassPermissionsSupport, undefined, appConfig.claudeDefaultModel);
1602
+ const result = yield writeClaudeConfig(dbManager, enableAgentTeams, enableBypassPermissionsSupport, undefined, appConfig.claudeDefaultModel, appConfig.autocompactPctOverride);
1576
1603
  res.json(result);
1577
1604
  })));
1578
1605
  app.post('/api/write-config/codex', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
@@ -1651,6 +1678,35 @@ ${instruction}
1651
1678
  const status = (0, config_metadata_1.checkCodexConfigStatus)();
1652
1679
  res.json(status);
1653
1680
  });
1681
+ // API 路径路由映射
1682
+ app.get('/api/api-path-bindings', (_req, res) => {
1683
+ res.json({ bindings: dbManager.getApiPathBindings(), models: dbManager.getApiPathModels() });
1684
+ });
1685
+ app.put('/api/api-path-bindings', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1686
+ const { bindings } = req.body;
1687
+ const VALID_API_PATHS = ['/v1/messages', '/v1/responses', '/v1/chat/completions', '/v1beta/models', '/v1/models'];
1688
+ const routes = dbManager.getRoutes();
1689
+ const routeIds = new Set(routes.map((r) => r.id));
1690
+ // Validate
1691
+ for (const b of bindings) {
1692
+ if (!VALID_API_PATHS.includes(b.apiPath)) {
1693
+ res.status(400).json({ error: `Invalid apiPath: ${b.apiPath}` });
1694
+ return;
1695
+ }
1696
+ if (b.routeId !== null && !routeIds.has(b.routeId)) {
1697
+ res.status(400).json({ error: `Route not found: ${b.routeId}` });
1698
+ return;
1699
+ }
1700
+ // /v1/models does not accept route binding
1701
+ if (b.apiPath === '/v1/models' && b.routeId !== null) {
1702
+ res.status(400).json({ error: '/v1/models does not accept route binding' });
1703
+ return;
1704
+ }
1705
+ }
1706
+ const { models } = req.body;
1707
+ yield dbManager.updateApiPathBindings(bindings.map(b => ({ apiPath: b.apiPath, routeId: b.routeId })), models);
1708
+ res.json({ success: true, bindings: dbManager.getApiPathBindings(), models: dbManager.getApiPathModels() });
1709
+ })));
1654
1710
  app.post('/api/export', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1655
1711
  const { password } = req.body;
1656
1712
  const data = yield dbManager.exportData(password);
@@ -1817,12 +1873,11 @@ ${instruction}
1817
1873
  app.post('/api/mcps', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
1818
1874
  const mcpData = req.body;
1819
1875
  const result = yield dbManager.createMCP(Object.assign(Object.assign({}, mcpData), { targets: mcpData.targets || [] }));
1820
- // 如果有激活的路由,立即写入MCP配置
1876
+ // 如果有工具绑定的路由,立即写入MCP配置
1821
1877
  if (mcpData.targets) {
1822
- const routes = dbManager.getRoutes();
1823
1878
  for (const target of mcpData.targets) {
1824
- const activeRoute = routes.find(r => r.targetType === target && r.isActive);
1825
- if (activeRoute) {
1879
+ const activeRouteId = dbManager.getActiveRouteIdForTool(target);
1880
+ if (activeRouteId) {
1826
1881
  yield writeMCPConfig(target);
1827
1882
  }
1828
1883
  }
@@ -1969,6 +2024,11 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
1969
2024
  }
1970
2025
  const server = app.listen(port, host, () => {
1971
2026
  console.log(`Admin server running on http://${host}:${port}`);
2027
+ // 启动后异步执行延迟维护任务(分片校验/修复、日志清理、会话索引构建)
2028
+ // 不阻塞服务启动,后台静默执行
2029
+ dbManager.deferredMaintenance().catch(err => {
2030
+ console.error('[Server] Deferred maintenance error:', err);
2031
+ });
1972
2032
  });
1973
2033
  // 创建 WebSocket 服务器用于工具安装
1974
2034
  const toolInstallWss = (0, websocket_service_1.createToolInstallationWSServer)();
@@ -36,7 +36,7 @@ const inferSourceTypeFromBaseUrlAndWireApi = (baseUrl, wireApi) => {
36
36
  return normalizedWireApi === 'chat' ? 'gemini-chat' : 'gemini';
37
37
  }
38
38
  if (lowerBaseUrl.includes('deepseek')) {
39
- return 'deepseek-reasoning-chat';
39
+ return 'openai-chat';
40
40
  }
41
41
  if (normalizedWireApi === 'chat') {
42
42
  return 'openai-chat';