aicodeswitch 5.2.10 → 5.2.12

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.
@@ -45,6 +45,8 @@ const CODEX_CONFIG_MANAGED_FIELDS = [
45
45
  'enableRouteSelection',
46
46
  'model_providers.aicodeswitch',
47
47
  'mcp_servers',
48
+ 'features',
49
+ 'memories',
48
50
  ];
49
51
 
50
52
  /**
@@ -41,6 +41,8 @@ exports.CODEX_CONFIG_MANAGED_FIELDS = [
41
41
  { path: ['enableRouteSelection'] },
42
42
  { path: ['model_providers', 'aicodeswitch'], isSection: true },
43
43
  { path: ['mcp_servers'], isSection: true, optional: true },
44
+ { path: ['features'], isSection: true, optional: true },
45
+ { path: ['memories'], isSection: true, optional: true },
44
46
  ];
45
47
  /**
46
48
  * Codex auth.json 管理字段定义
@@ -9,6 +9,7 @@ exports.claudeToResponsesResponse = claudeToResponsesResponse;
9
9
  const id_js_1 = require("../../utils/id.js");
10
10
  const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
11
11
  const mapper_js_1 = require("../../thinking/mapper.js");
12
+ const usage_js_1 = require("../../utils/usage.js");
12
13
  /**
13
14
  * Convert a Claude Messages response to an OpenAI Responses API response.
14
15
  */
@@ -45,26 +46,9 @@ function claudeToResponsesResponse(response) {
45
46
  }
46
47
  }
47
48
  const { status, incomplete_details } = (0, stop_reasons_js_1.claudeToResponsesStatus)(response.stop_reason);
48
- const usage = claudeToResponsesUsage(response.usage);
49
+ // 上游无 usage 时省略 usage 字段(不伪造 0)
50
+ const usage = (0, usage_js_1.toResponsesUsage)(response.usage);
49
51
  const responseId = response.id || (0, id_js_1.generateResponseId)();
50
- return Object.assign(Object.assign({ id: responseId, object: 'response', status,
51
- output, model: response.model || '', created_at: Math.floor(Date.now() / 1000), usage }, (incomplete_details ? { incomplete_details } : {})), { metadata: {} });
52
- }
53
- // ---------------------------------------------------------------------------
54
- // Helpers
55
- // ---------------------------------------------------------------------------
56
- /**
57
- * Map Claude usage to Responses API usage.
58
- */
59
- function claudeToResponsesUsage(usage) {
60
- var _a, _b;
61
- if (!usage)
62
- return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
63
- const input = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : 0;
64
- const output = (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : 0;
65
- return {
66
- input_tokens: input,
67
- output_tokens: output,
68
- total_tokens: input + output,
69
- };
52
+ return Object.assign(Object.assign(Object.assign({ id: responseId, object: 'response', status,
53
+ output, model: response.model || '', created_at: Math.floor(Date.now() / 1000) }, (usage ? { usage } : {})), (incomplete_details ? { incomplete_details } : {})), { metadata: {} });
70
54
  }
@@ -10,6 +10,7 @@ exports.ClaudeToResponsesConverter = void 0;
10
10
  const id_js_1 = require("../../utils/id.js");
11
11
  const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
12
12
  const streaming_helpers_js_1 = require("../../utils/streaming-helpers.js");
13
+ const usage_js_1 = require("../../utils/usage.js");
13
14
  /**
14
15
  * ClaudeToResponsesConverter: Claude Messages SSE → Responses API SSE
15
16
  */
@@ -49,7 +50,7 @@ class ClaudeToResponsesConverter {
49
50
  enumerable: true,
50
51
  configurable: true,
51
52
  writable: true,
52
- value: { input_tokens: 0, output_tokens: 0 }
53
+ value: null
53
54
  });
54
55
  Object.defineProperty(this, "output", {
55
56
  enumerable: true,
@@ -89,7 +90,7 @@ class ClaudeToResponsesConverter {
89
90
  });
90
91
  }
91
92
  convertEvent(event) {
92
- var _a, _b, _c, _d, _e;
93
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
93
94
  if (!event.data)
94
95
  return [];
95
96
  const events = [];
@@ -99,6 +100,14 @@ class ClaudeToResponsesConverter {
99
100
  case 'message_start': {
100
101
  this.model = ((_a = data.message) === null || _a === void 0 ? void 0 : _a.model) || '';
101
102
  this.responseId = ((_b = data.message) === null || _b === void 0 ? void 0 : _b.id) || this.responseId;
103
+ // Claude 的 prompt token 数在 message_start 的 message.usage.input_tokens
104
+ const startUsage = (_c = data.message) === null || _c === void 0 ? void 0 : _c.usage;
105
+ if (startUsage) {
106
+ this.usage = {
107
+ input_tokens: (_d = startUsage.input_tokens) !== null && _d !== void 0 ? _d : 0,
108
+ output_tokens: (_e = startUsage.output_tokens) !== null && _e !== void 0 ? _e : 0,
109
+ };
110
+ }
102
111
  events.push(this.makeSSE('response.created', {
103
112
  id: this.responseId,
104
113
  object: 'response',
@@ -236,11 +245,11 @@ class ClaudeToResponsesConverter {
236
245
  break;
237
246
  }
238
247
  case 'message_delta': {
239
- this.pendingStopReason = ((_c = data.delta) === null || _c === void 0 ? void 0 : _c.stop_reason) || null;
248
+ this.pendingStopReason = ((_f = data.delta) === null || _f === void 0 ? void 0 : _f.stop_reason) || null;
240
249
  if (data.usage) {
241
250
  this.usage = {
242
- input_tokens: this.usage.input_tokens,
243
- output_tokens: (_e = (_d = data.usage.output_tokens) !== null && _d !== void 0 ? _d : data.usage.tokens) !== null && _e !== void 0 ? _e : 0,
251
+ input_tokens: (_j = (_g = data.usage.input_tokens) !== null && _g !== void 0 ? _g : (_h = this.usage) === null || _h === void 0 ? void 0 : _h.input_tokens) !== null && _j !== void 0 ? _j : 0,
252
+ output_tokens: (_o = (_l = (_k = data.usage.output_tokens) !== null && _k !== void 0 ? _k : data.usage.tokens) !== null && _l !== void 0 ? _l : (_m = this.usage) === null || _m === void 0 ? void 0 : _m.output_tokens) !== null && _o !== void 0 ? _o : 0,
244
253
  };
245
254
  }
246
255
  break;
@@ -253,7 +262,7 @@ class ClaudeToResponsesConverter {
253
262
  break;
254
263
  }
255
264
  }
256
- catch (_f) {
265
+ catch (_p) {
257
266
  // Ignore parse errors
258
267
  }
259
268
  return events;
@@ -315,20 +324,9 @@ class ClaudeToResponsesConverter {
315
324
  this.closeText(events);
316
325
  this.closeThinking(events);
317
326
  const { status, incomplete_details } = (0, stop_reasons_js_1.claudeToResponsesStatus)(this.pendingStopReason);
318
- const responseObj = {
319
- id: this.responseId,
320
- object: 'response',
321
- status,
322
- output: this.output,
323
- model: this.model,
324
- created_at: Math.floor(Date.now() / 1000),
325
- usage: {
326
- input_tokens: this.usage.input_tokens,
327
- output_tokens: this.usage.output_tokens,
328
- total_tokens: this.usage.input_tokens + this.usage.output_tokens,
329
- },
330
- metadata: {},
331
- };
327
+ // 上游无 usage 时省略 usage 字段(不伪造 0,避免 Codex missing field input_tokens)
328
+ const responsesUsage = (0, usage_js_1.toResponsesUsage)(this.usage);
329
+ const responseObj = Object.assign({ id: this.responseId, object: 'response', status, output: this.output, model: this.model, created_at: Math.floor(Date.now() / 1000), metadata: {} }, (responsesUsage ? { usage: responsesUsage } : {}));
332
330
  if (incomplete_details) {
333
331
  responseObj.incomplete_details = incomplete_details;
334
332
  }
@@ -80,15 +80,8 @@ function completionsToResponsesResponse(response) {
80
80
  const finishReason = (0, stop_reasons_js_1.completionsToResponsesFinishReason)(choice.finish_reason);
81
81
  const status = finishReason === 'incomplete' ? 'incomplete' : 'completed';
82
82
  const usage = (0, usage_js_1.completionsToResponsesUsage)(response.usage);
83
- const result = {
84
- id: ((_m = response.id) === null || _m === void 0 ? void 0 : _m.startsWith('resp_')) ? response.id : (0, id_js_1.generateResponseId)(),
85
- object: 'response',
86
- status,
87
- output,
88
- model: response.model,
89
- usage,
90
- created_at: response.created || Math.floor(Date.now() / 1000),
91
- };
83
+ const result = Object.assign({ id: ((_m = response.id) === null || _m === void 0 ? void 0 : _m.startsWith('resp_')) ? response.id : (0, id_js_1.generateResponseId)(), object: 'response', status,
84
+ output, model: response.model, created_at: response.created || Math.floor(Date.now() / 1000) }, (usage ? { usage } : {}));
92
85
  if (status === 'incomplete') {
93
86
  result.incomplete_details = { reason: 'max_output_tokens' };
94
87
  }
@@ -320,16 +320,9 @@ class CompletionsToResponsesConverter {
320
320
  }
321
321
  // Build full response object
322
322
  const status = this.finishReason === 'length' ? 'incomplete' : 'completed';
323
- const responseObj = {
324
- id: this.responseId,
325
- object: 'response',
326
- status,
327
- output: this.output,
328
- model: this.model,
329
- usage: this.usage ? (0, usage_js_1.completionsToResponsesUsage)(this.usage) : {},
330
- created_at: Math.floor(Date.now() / 1000),
331
- metadata: {},
332
- };
323
+ // 上游无 usage 时省略 usage 字段(不伪造 0,避免 Codex missing field input_tokens)
324
+ const responsesUsage = (0, usage_js_1.completionsToResponsesUsage)(this.usage);
325
+ const responseObj = Object.assign({ id: this.responseId, object: 'response', status, output: this.output, model: this.model, created_at: Math.floor(Date.now() / 1000), metadata: {} }, (responsesUsage ? { usage: responsesUsage } : {}));
333
326
  if (status === 'incomplete') {
334
327
  responseObj.incomplete_details = { reason: 'max_output_tokens' };
335
328
  }
@@ -8,6 +8,7 @@ exports.claudeToCompletionsUsage = claudeToCompletionsUsage;
8
8
  exports.geminiToClaudeUsage = geminiToClaudeUsage;
9
9
  exports.claudeToGeminiUsage = claudeToGeminiUsage;
10
10
  exports.responsesToClaudeUsage = responsesToClaudeUsage;
11
+ exports.toResponsesUsage = toResponsesUsage;
11
12
  exports.completionsToResponsesUsage = completionsToResponsesUsage;
12
13
  /** Map OpenAI Chat usage to Claude usage */
13
14
  function completionsToClaudeUsage(usage) {
@@ -69,14 +70,33 @@ function responsesToClaudeUsage(usage) {
69
70
  cache_creation_input_tokens: usage.cache_creation_input_tokens,
70
71
  };
71
72
  }
72
- /** Map OpenAI Chat usage to Responses API usage */
73
- function completionsToResponsesUsage(usage) {
74
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
75
- if (!usage)
76
- return {};
73
+ /**
74
+ * 构造标准 Responses API usage 对象(转换层兼容入口)。
75
+ *
76
+ * 真实值优先:input_tokens ?? prompt_tokens、output_tokens ?? completion_tokens、total_tokens。
77
+ * 覆盖上游 chat completions / claude / gemini 三种格式的字段命名归一。
78
+ *
79
+ * 仅当上游确实提供了任意 token 字段时返回归一化对象;否则返回 null(调用方应省略 usage 字段,
80
+ * 不伪造 0)。这是避免 Codex `ResponseCompleted: missing field input_tokens` 的关键——
81
+ * 既不吐空 `{}`,也不吐伪造的 `{0,0,0}`。
82
+ */
83
+ function toResponsesUsage(usage) {
84
+ var _a, _b;
85
+ if (!usage || typeof usage !== 'object')
86
+ return null;
87
+ const input_tokens = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : usage.prompt_tokens;
88
+ const output_tokens = (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : usage.completion_tokens;
89
+ const total_tokens = usage.total_tokens;
90
+ // 上游没返回任何 token 字段 → 不伪造,返回 null
91
+ if (input_tokens == null && output_tokens == null && total_tokens == null)
92
+ return null;
77
93
  return {
78
- input_tokens: (_b = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : usage.prompt_tokens) !== null && _b !== void 0 ? _b : 0,
79
- output_tokens: (_d = (_c = usage.output_tokens) !== null && _c !== void 0 ? _c : usage.completion_tokens) !== null && _d !== void 0 ? _d : 0,
80
- total_tokens: (_e = usage.total_tokens) !== null && _e !== void 0 ? _e : (((_g = (_f = usage.input_tokens) !== null && _f !== void 0 ? _f : usage.prompt_tokens) !== null && _g !== void 0 ? _g : 0) + ((_j = (_h = usage.output_tokens) !== null && _h !== void 0 ? _h : usage.completion_tokens) !== null && _j !== void 0 ? _j : 0)),
94
+ input_tokens: input_tokens !== null && input_tokens !== void 0 ? input_tokens : 0,
95
+ output_tokens: output_tokens !== null && output_tokens !== void 0 ? output_tokens : 0,
96
+ total_tokens: total_tokens !== null && total_tokens !== void 0 ? total_tokens : ((input_tokens !== null && input_tokens !== void 0 ? input_tokens : 0) + (output_tokens !== null && output_tokens !== void 0 ? output_tokens : 0)),
81
97
  };
82
98
  }
99
+ /** Map OpenAI Chat usage to Responses API usage(薄封装,保持现有调用方不变) */
100
+ function completionsToResponsesUsage(usage) {
101
+ return toResponsesUsage(usage);
102
+ }
@@ -2806,9 +2806,9 @@ class FileSystemDatabaseManager {
2806
2806
  serviceStats.avgResponseTime =
2807
2807
  (serviceStats.avgResponseTime * (serviceStats.totalRequests - 1) + responseTime) / serviceStats.totalRequests;
2808
2808
  }
2809
- // 更新 byModel
2810
- if (log.requestModel || log.targetModel) {
2811
- const modelName = log.requestModel || log.targetModel || 'Unknown';
2809
+ // 更新 byModel(按实际转发的模型统计,优先 targetModel 而非客户端提交的 requestModel)
2810
+ if (log.targetModel || log.requestModel) {
2811
+ const modelName = log.targetModel || log.requestModel || 'Unknown';
2812
2812
  let modelStats = this.statistics.byModel.find(s => s.modelName === modelName);
2813
2813
  if (!modelStats) {
2814
2814
  modelStats = { modelName, totalRequests: 0, totalTokens: 0, avgResponseTime: 0 };
@@ -24,6 +24,7 @@ const proxy_server_1 = require("./proxy-server");
24
24
  const index_1 = require("./access-keys/index");
25
25
  const manager_1 = require("./access-keys/manager");
26
26
  const policy_manager_1 = require("./access-keys/policy-manager");
27
+ const performance_tracker_1 = require("./performance-tracker");
27
28
  const os_1 = __importDefault(require("os"));
28
29
  const auth_1 = require("./auth");
29
30
  const version_check_1 = require("./version-check");
@@ -383,7 +384,7 @@ const DEFAULT_CODEX_REASONING_EFFORT = 'high';
383
384
  const isCodexReasoningEffort = (value) => {
384
385
  return typeof value === 'string' && VALID_CODEX_REASONING_EFFORTS.includes(value);
385
386
  };
386
- const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbManager_1, ...args_1], void 0, function* (_dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT, codexDefaultModel, options = {}) {
387
+ const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbManager_1, ...args_1], void 0, function* (_dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT, codexDefaultModel, enableMemories, options = {}) {
387
388
  var _a;
388
389
  try {
389
390
  const homeDir = os_1.default.homedir();
@@ -450,6 +451,17 @@ const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbMana
450
451
  }
451
452
  }
452
453
  };
454
+ // 记忆功能配置
455
+ if (enableMemories) {
456
+ proxyConfig.features = {
457
+ memories: true,
458
+ };
459
+ proxyConfig.memories = {
460
+ generate_memories: true,
461
+ use_memories: true,
462
+ disable_on_external_context: true,
463
+ };
464
+ }
453
465
  // 使用智能合并
454
466
  const mergedConfig = (0, config_merge_1.mergeTomlConfig)(proxyConfig, currentConfig, config_managed_fields_1.CODEX_CONFIG_MANAGED_FIELDS);
455
467
  // 原子性写入合并后的配置
@@ -681,7 +693,7 @@ const syncConfigsOnServerStartup = (dbManager) => __awaiter(void 0, void 0, void
681
693
  const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
682
694
  ? config.codexModelReasoningEffort
683
695
  : DEFAULT_CODEX_REASONING_EFFORT;
684
- const codexWritten = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel);
696
+ const codexWritten = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, config.codexEnableMemories);
685
697
  console.log(`[Startup Config Sync] Codex config ${codexWritten ? 'written' : 'skipped'}`);
686
698
  });
687
699
  const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0, void 0, function* () {
@@ -694,7 +706,7 @@ const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0,
694
706
  const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
695
707
  ? config.codexModelReasoningEffort
696
708
  : DEFAULT_CODEX_REASONING_EFFORT;
697
- const codexUpdated = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, { allowOverwriteRefresh: true });
709
+ const codexUpdated = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, config.codexEnableMemories, { allowOverwriteRefresh: true });
698
710
  console.log(`[Config Update Sync] Codex config ${codexUpdated ? 'written' : 'skipped'}`);
699
711
  });
700
712
  const getCentralSkillsDir = () => {
@@ -2038,7 +2050,11 @@ ${instruction}
2038
2050
  : isCodexReasoningEffort(appConfig.codexModelReasoningEffort)
2039
2051
  ? appConfig.codexModelReasoningEffort
2040
2052
  : DEFAULT_CODEX_REASONING_EFFORT;
2041
- const result = yield writeCodexConfig(dbManager, modelReasoningEffort, appConfig.codexDefaultModel);
2053
+ const requestedEnableMemories = req.body.enableMemories;
2054
+ const enableMemories = requestedEnableMemories !== undefined
2055
+ ? !!requestedEnableMemories
2056
+ : !!appConfig.codexEnableMemories;
2057
+ const result = yield writeCodexConfig(dbManager, modelReasoningEffort, appConfig.codexDefaultModel, enableMemories);
2042
2058
  applyWriteLocalRecords(proxyServer);
2043
2059
  res.json(result);
2044
2060
  })));
@@ -3073,6 +3089,64 @@ ${instruction}
3073
3089
  }
3074
3090
  res.json(alerts);
3075
3091
  })));
3092
+ // ============ 服务性能统计(全局,与 AUTH 无关) ============
3093
+ const requirePerfTracker = (res) => {
3094
+ const tracker = proxyServer.getPerformanceTracker();
3095
+ if (!tracker) {
3096
+ res.status(503).json({ error: 'Performance tracker not initialized' });
3097
+ }
3098
+ return tracker;
3099
+ };
3100
+ // 全部 API 服务平铺一览(含所属供应商)
3101
+ app.get('/api/performance/services-overview', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
3102
+ const tracker = requirePerfTracker(res);
3103
+ if (!tracker)
3104
+ return;
3105
+ res.json(tracker.getServicesOverview());
3106
+ })));
3107
+ // 全部供应商一览
3108
+ app.get('/api/performance/vendors', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
3109
+ const tracker = requirePerfTracker(res);
3110
+ if (!tracker)
3111
+ return;
3112
+ res.json(tracker.getVendorsOverview());
3113
+ })));
3114
+ // 某供应商详情(rollup + 其下服务)
3115
+ app.get('/api/performance/vendors/:vendorId', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
3116
+ const tracker = requirePerfTracker(res);
3117
+ if (!tracker)
3118
+ return;
3119
+ const detail = tracker.getVendorDetail(req.params.vendorId);
3120
+ if (!detail) {
3121
+ res.status(404).json({ error: 'Vendor not found' });
3122
+ return;
3123
+ }
3124
+ res.json(detail);
3125
+ })));
3126
+ // 某服务详情(rollup + 其下模型)
3127
+ app.get('/api/performance/services/:serviceId', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
3128
+ const tracker = requirePerfTracker(res);
3129
+ if (!tracker)
3130
+ return;
3131
+ const detail = tracker.getServiceDetail(req.params.serviceId);
3132
+ if (!detail) {
3133
+ res.status(404).json({ error: 'Service not found' });
3134
+ return;
3135
+ }
3136
+ res.json(detail);
3137
+ })));
3138
+ // 单模型详情(派生 + 小时走势 + 极值)
3139
+ app.get('/api/performance/services/:serviceId/models/:model', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
3140
+ const tracker = requirePerfTracker(res);
3141
+ if (!tracker)
3142
+ return;
3143
+ const detail = tracker.getModelDetail(req.params.serviceId, decodeURIComponent(req.params.model));
3144
+ if (!detail) {
3145
+ res.status(404).json({ error: 'Model not found' });
3146
+ return;
3147
+ }
3148
+ res.json(detail);
3149
+ })));
3076
3150
  // 写入MCP配置到Claude Code或Codex的全局配置文件
3077
3151
  const writeMCPConfig = (targetType) => __awaiter(void 0, void 0, void 0, function* () {
3078
3152
  try {
@@ -3320,6 +3394,16 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
3320
3394
  catch (error) {
3321
3395
  console.error('[Server] AccessKey module initialization failed:', error);
3322
3396
  }
3397
+ // Initialize Service Performance Tracker (全局统计,与 AUTH 无关)
3398
+ const performanceTracker = new performance_tracker_1.ServicePerformanceTracker(dataDir);
3399
+ try {
3400
+ yield performanceTracker.initialize();
3401
+ performanceTracker.startAutoFlush();
3402
+ proxyServer.setPerformanceTracker(performanceTracker);
3403
+ }
3404
+ catch (error) {
3405
+ console.error('[Server] Performance tracker initialization failed:', error);
3406
+ }
3323
3407
  // 恢复已写入本地的 AccessKey(在代理配置写入之后、AccessKey 模块初始化之后)
3324
3408
  try {
3325
3409
  applyWriteLocalRecords(proxyServer);
@@ -3424,6 +3508,14 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
3424
3508
  catch (error) {
3425
3509
  console.error('[Shutdown ...] AccessKey module shutdown failed:', error);
3426
3510
  }
3511
+ // Flush 服务性能统计(全局桶)后停止定时刷盘
3512
+ try {
3513
+ performanceTracker.stopAutoFlush();
3514
+ yield performanceTracker.flush();
3515
+ }
3516
+ catch (error) {
3517
+ console.error('[Shutdown ...] Performance tracker flush failed:', error);
3518
+ }
3427
3519
  dbManager.close();
3428
3520
  // 清理规则状态广播器(关闭 SSE 连接)
3429
3521
  rules_status_service_1.rulesStatusBroadcaster.destroy();