aicodeswitch 5.1.3 → 5.2.1

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.
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QuotaChecker = void 0;
4
+ class QuotaChecker {
5
+ constructor() {
6
+ /** per keyId: 滑动窗口(60秒) */
7
+ Object.defineProperty(this, "rpmTracker", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: new Map()
12
+ });
13
+ /** per keyId: 当前并发数 */
14
+ Object.defineProperty(this, "concurrentTracker", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: new Map()
19
+ });
20
+ }
21
+ /**
22
+ * 检查所有配额限制,返回 null 表示通过,否则返回错误信息
23
+ */
24
+ checkQuota(policy, usage, keyId, requestModel) {
25
+ // 1. 模型过滤
26
+ if (requestModel) {
27
+ if (policy.allowedModels && policy.allowedModels.length > 0) {
28
+ if (!policy.allowedModels.some(m => this.matchModel(m, requestModel))) {
29
+ return { error: 'MODEL_NOT_ALLOWED', message: `模型 ${requestModel} 不在允许列表中`, httpStatus: 403 };
30
+ }
31
+ }
32
+ if (policy.blockedModels && policy.blockedModels.length > 0) {
33
+ if (policy.blockedModels.some(m => this.matchModel(m, requestModel))) {
34
+ return { error: 'MODEL_NOT_ALLOWED', message: `模型 ${requestModel} 已被禁止使用`, httpStatus: 403 };
35
+ }
36
+ }
37
+ }
38
+ if (!usage)
39
+ return null; // 无用量数据,跳过其余检查
40
+ const now = Date.now();
41
+ // 2. Token 日限额
42
+ if (policy.dailyTokenLimit) {
43
+ const limit = policy.dailyTokenLimit * 1000; // k → 实际 tokens
44
+ if (this.isPeriodExpired(usage.periods.daily.periodStart, 'daily')) {
45
+ // 周期已重置,通过
46
+ }
47
+ else if (usage.periods.daily.tokens >= limit) {
48
+ return { error: 'TOKEN_QUOTA_EXCEEDED', message: '今日 Token 配额已用尽', httpStatus: 429, dimension: 'dailyTokenLimit', usage: usage.periods.daily.tokens, limit };
49
+ }
50
+ }
51
+ // 3. Token 周限额
52
+ if (policy.weeklyTokenLimit) {
53
+ const limit = policy.weeklyTokenLimit * 1000;
54
+ if (this.isPeriodExpired(usage.periods.weekly.periodStart, 'weekly')) {
55
+ // 周期已重置,通过
56
+ }
57
+ else if (usage.periods.weekly.tokens >= limit) {
58
+ return { error: 'TOKEN_QUOTA_EXCEEDED', message: '本周 Token 配额已用尽', httpStatus: 429, dimension: 'weeklyTokenLimit', usage: usage.periods.weekly.tokens, limit };
59
+ }
60
+ }
61
+ // 4. Token 月限额
62
+ if (policy.monthlyTokenLimit) {
63
+ const limit = policy.monthlyTokenLimit * 1000;
64
+ if (this.isPeriodExpired(usage.periods.monthly.periodStart, 'monthly')) {
65
+ // 周期已重置,通过
66
+ }
67
+ else if (usage.periods.monthly.tokens >= limit) {
68
+ return { error: 'TOKEN_QUOTA_EXCEEDED', message: '本月 Token 配额已用尽', httpStatus: 429, dimension: 'monthlyTokenLimit', usage: usage.periods.monthly.tokens, limit };
69
+ }
70
+ }
71
+ // 5. Token 自定义周期
72
+ if (policy.customTokenLimit && policy.customTokenResetHours && usage.periods.custom) {
73
+ const limit = policy.customTokenLimit * 1000;
74
+ const resetMs = policy.customTokenResetHours * 3600 * 1000;
75
+ if (now - usage.periods.custom.periodStart >= resetMs) {
76
+ // 周期已重置,通过
77
+ }
78
+ else if (usage.periods.custom.tokens >= limit) {
79
+ return { error: 'TOKEN_QUOTA_EXCEEDED', message: 'Token 配额已用尽', httpStatus: 429, dimension: 'customTokenLimit', usage: usage.periods.custom.tokens, limit };
80
+ }
81
+ }
82
+ // 6. 请求日限额
83
+ if (policy.dailyRequestLimit) {
84
+ if (!this.isPeriodExpired(usage.periods.daily.periodStart, 'daily') && usage.periods.daily.requests >= policy.dailyRequestLimit) {
85
+ return { error: 'REQUEST_QUOTA_EXCEEDED', message: '今日请求次数已用尽', httpStatus: 429, dimension: 'dailyRequestLimit', usage: usage.periods.daily.requests, limit: policy.dailyRequestLimit };
86
+ }
87
+ }
88
+ // 7. 请求周限额
89
+ if (policy.weeklyRequestLimit) {
90
+ if (!this.isPeriodExpired(usage.periods.weekly.periodStart, 'weekly') && usage.periods.weekly.requests >= policy.weeklyRequestLimit) {
91
+ return { error: 'REQUEST_QUOTA_EXCEEDED', message: '本周请求次数已用尽', httpStatus: 429, dimension: 'weeklyRequestLimit', usage: usage.periods.weekly.requests, limit: policy.weeklyRequestLimit };
92
+ }
93
+ }
94
+ // 8. 请求月限额
95
+ if (policy.monthlyRequestLimit) {
96
+ if (!this.isPeriodExpired(usage.periods.monthly.periodStart, 'monthly') && usage.periods.monthly.requests >= policy.monthlyRequestLimit) {
97
+ return { error: 'REQUEST_QUOTA_EXCEEDED', message: '本月请求次数已用尽', httpStatus: 429, dimension: 'monthlyRequestLimit', usage: usage.periods.monthly.requests, limit: policy.monthlyRequestLimit };
98
+ }
99
+ }
100
+ // 9. 请求自定义周期
101
+ if (policy.customRequestLimit && policy.customRequestResetHours && usage.periods.custom) {
102
+ const resetMs = policy.customRequestResetHours * 3600 * 1000;
103
+ if (now - usage.periods.custom.periodStart < resetMs && usage.periods.custom.requests >= policy.customRequestLimit) {
104
+ return { error: 'REQUEST_QUOTA_EXCEEDED', message: '请求次数配额已用尽', httpStatus: 429, dimension: 'customRequestLimit', usage: usage.periods.custom.requests, limit: policy.customRequestLimit };
105
+ }
106
+ }
107
+ // 10. RPM 检查
108
+ if (policy.rpmLimit) {
109
+ const counter = this.getRpmCounter(keyId);
110
+ this.cleanExpiredTimestamps(counter, now);
111
+ if (counter.timestamps.length >= policy.rpmLimit) {
112
+ return { error: 'RPM_LIMIT_EXCEEDED', message: `每分钟请求数超过限制 (${policy.rpmLimit})`, httpStatus: 429, dimension: 'rpmLimit', usage: counter.timestamps.length, limit: policy.rpmLimit };
113
+ }
114
+ }
115
+ // 11. 并发检查
116
+ if (policy.concurrentLimit) {
117
+ const current = this.concurrentTracker.get(keyId) || 0;
118
+ if (current >= policy.concurrentLimit) {
119
+ return { error: 'CONCURRENT_LIMIT_EXCEEDED', message: `并发请求数超过限制 (${policy.concurrentLimit})`, httpStatus: 429, dimension: 'concurrentLimit', usage: current, limit: policy.concurrentLimit };
120
+ }
121
+ }
122
+ return null; // 所有检查通过
123
+ }
124
+ /** 请求开始:增加并发计数 + RPM 计数 */
125
+ onRequestStart(keyId, policy) {
126
+ // 并发 +1
127
+ const current = this.concurrentTracker.get(keyId) || 0;
128
+ this.concurrentTracker.set(keyId, current + 1);
129
+ // RPM +1
130
+ if (policy.rpmLimit) {
131
+ const counter = this.getRpmCounter(keyId);
132
+ counter.timestamps.push(Date.now());
133
+ }
134
+ }
135
+ /** 请求结束:减少并发计数 */
136
+ onRequestEnd(keyId) {
137
+ const current = this.concurrentTracker.get(keyId) || 0;
138
+ this.concurrentTracker.set(keyId, Math.max(0, current - 1));
139
+ }
140
+ /** 获取当前并发数 */
141
+ getConcurrentCount(keyId) {
142
+ return this.concurrentTracker.get(keyId) || 0;
143
+ }
144
+ /** 获取当前 RPM */
145
+ getCurrentRpm(keyId) {
146
+ const counter = this.rpmTracker.get(keyId);
147
+ if (!counter)
148
+ return 0;
149
+ this.cleanExpiredTimestamps(counter, Date.now());
150
+ return counter.timestamps.length;
151
+ }
152
+ // ---- helpers ----
153
+ getRpmCounter(keyId) {
154
+ let counter = this.rpmTracker.get(keyId);
155
+ if (!counter) {
156
+ counter = { timestamps: [] };
157
+ this.rpmTracker.set(keyId, counter);
158
+ }
159
+ return counter;
160
+ }
161
+ cleanExpiredTimestamps(counter, now) {
162
+ const windowMs = 60000; // 60 seconds
163
+ counter.timestamps = counter.timestamps.filter(t => now - t < windowMs);
164
+ }
165
+ isPeriodExpired(periodStart, type) {
166
+ const now = new Date();
167
+ switch (type) {
168
+ case 'daily': {
169
+ // UTC 00:00 重置
170
+ const todayStart = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
171
+ return periodStart < todayStart;
172
+ }
173
+ case 'weekly': {
174
+ // 周一 UTC 00:00 重置
175
+ const day = now.getUTCDay();
176
+ const mondayOffset = day === 0 ? 6 : day - 1;
177
+ const mondayStart = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - mondayOffset);
178
+ return periodStart < mondayStart;
179
+ }
180
+ case 'monthly': {
181
+ // 每月 1 日 UTC 00:00 重置
182
+ const monthStart = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1);
183
+ return periodStart < monthStart;
184
+ }
185
+ }
186
+ }
187
+ /** 模型名匹配(支持前缀匹配) */
188
+ matchModel(pattern, model) {
189
+ if (pattern === model)
190
+ return true;
191
+ // 支持前缀匹配,如 "claude-sonnet" 匹配 "claude-sonnet-4-20250514"
192
+ if (model.startsWith(pattern))
193
+ return true;
194
+ return false;
195
+ }
196
+ }
197
+ exports.QuotaChecker = QuotaChecker;
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.UsageTracker = void 0;
16
+ /**
17
+ * Key 级用量追踪器
18
+ * 负责用量的持久化、周期重置、历史记录
19
+ */
20
+ const path_1 = __importDefault(require("path"));
21
+ const promises_1 = __importDefault(require("fs/promises"));
22
+ class UsageTracker {
23
+ constructor(dataPath) {
24
+ Object.defineProperty(this, "dataPath", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: void 0
29
+ });
30
+ /** 内存缓存 keyId → KeyUsage */
31
+ Object.defineProperty(this, "cache", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: new Map()
36
+ });
37
+ /** 脏数据标记 */
38
+ Object.defineProperty(this, "dirty", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: new Set()
43
+ });
44
+ /** debounce 写入定时器 */
45
+ Object.defineProperty(this, "flushTimer", {
46
+ enumerable: true,
47
+ configurable: true,
48
+ writable: true,
49
+ value: null
50
+ });
51
+ Object.defineProperty(this, "FLUSH_INTERVAL", {
52
+ enumerable: true,
53
+ configurable: true,
54
+ writable: true,
55
+ value: 5000
56
+ }); // 5 秒
57
+ this.dataPath = dataPath;
58
+ }
59
+ /** 初始化,确保目录存在 */
60
+ initialize() {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const usageDir = path_1.default.join(this.dataPath, 'key-usage');
63
+ yield promises_1.default.mkdir(usageDir, { recursive: true });
64
+ });
65
+ }
66
+ /** 获取 KeyUsage(优先从缓存读取) */
67
+ getUsage(keyId) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ if (this.cache.has(keyId)) {
70
+ return this.cache.get(keyId);
71
+ }
72
+ const usage = yield this.loadUsageFile(keyId);
73
+ this.cache.set(keyId, usage);
74
+ return usage;
75
+ });
76
+ }
77
+ /** 记录一次请求的 Token 消耗 */
78
+ recordTokenUsage(keyId, tokenUsage) {
79
+ return __awaiter(this, void 0, void 0, function* () {
80
+ const usage = yield this.getUsage(keyId);
81
+ const now = Date.now();
82
+ const totalTokens = tokenUsage.totalTokens || (tokenUsage.inputTokens + tokenUsage.outputTokens);
83
+ // 更新 lifetime
84
+ usage.lifetime.totalTokens += totalTokens;
85
+ usage.lifetime.inputTokens += tokenUsage.inputTokens;
86
+ usage.lifetime.outputTokens += tokenUsage.outputTokens;
87
+ usage.lifetime.totalRequests += 1;
88
+ // 更新周期用量(自动检测重置)
89
+ this.updatePeriodTokens(usage.periods.daily, now, 'daily', totalTokens);
90
+ this.updatePeriodTokens(usage.periods.weekly, now, 'weekly', totalTokens);
91
+ this.updatePeriodTokens(usage.periods.monthly, now, 'monthly', totalTokens);
92
+ if (usage.periods.custom) {
93
+ this.updatePeriodTokens(usage.periods.custom, now, 'custom', totalTokens);
94
+ }
95
+ // 更新日历史
96
+ this.updateDailyHistory(usage, now, totalTokens, 0);
97
+ // 标记脏数据
98
+ this.markDirty(keyId);
99
+ });
100
+ }
101
+ /** 记录一次请求(仅计数,无 Token) */
102
+ recordRequest(keyId) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ const usage = yield this.getUsage(keyId);
105
+ const now = Date.now();
106
+ // 更新周期请求计数
107
+ this.updatePeriodRequests(usage.periods.daily, now, 'daily');
108
+ this.updatePeriodRequests(usage.periods.weekly, now, 'weekly');
109
+ this.updatePeriodRequests(usage.periods.monthly, now, 'monthly');
110
+ if (usage.periods.custom) {
111
+ this.updatePeriodRequests(usage.periods.custom, now, 'custom');
112
+ }
113
+ this.markDirty(keyId);
114
+ });
115
+ }
116
+ /** 记录错误 */
117
+ recordError(keyId) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ const usage = yield this.getUsage(keyId);
120
+ usage.lifetime.errorCount += 1;
121
+ const now = Date.now();
122
+ const today = this.formatDate(now);
123
+ const record = usage.dailyHistory.find(h => h.date === today);
124
+ if (record) {
125
+ record.errors += 1;
126
+ }
127
+ this.markDirty(keyId);
128
+ });
129
+ }
130
+ /** 获取用量趋势 */
131
+ getTrend(keyId_1) {
132
+ return __awaiter(this, arguments, void 0, function* (keyId, days = 30) {
133
+ const usage = yield this.getUsage(keyId);
134
+ return usage.dailyHistory.slice(-days);
135
+ });
136
+ }
137
+ /** 刷新脏数据到磁盘 */
138
+ flush() {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ if (this.dirty.size === 0)
141
+ return;
142
+ const keys = Array.from(this.dirty);
143
+ this.dirty.clear();
144
+ yield Promise.all(keys.map(keyId => this.saveUsageFile(keyId)));
145
+ });
146
+ }
147
+ /** 定时刷新 */
148
+ startAutoFlush() {
149
+ this.flushTimer = setInterval(() => {
150
+ this.flush().catch(err => console.error('[UsageTracker] Auto flush error:', err));
151
+ }, this.FLUSH_INTERVAL);
152
+ }
153
+ /** 停止自动刷新 */
154
+ stopAutoFlush() {
155
+ if (this.flushTimer) {
156
+ clearInterval(this.flushTimer);
157
+ this.flushTimer = null;
158
+ }
159
+ }
160
+ // ---- helpers ----
161
+ loadUsageFile(keyId) {
162
+ return __awaiter(this, void 0, void 0, function* () {
163
+ const filePath = this.getUsageFilePath(keyId);
164
+ try {
165
+ const data = yield promises_1.default.readFile(filePath, 'utf-8');
166
+ return JSON.parse(data);
167
+ }
168
+ catch (_a) {
169
+ return this.createEmptyUsage(keyId);
170
+ }
171
+ });
172
+ }
173
+ saveUsageFile(keyId) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ const usage = this.cache.get(keyId);
176
+ if (!usage)
177
+ return;
178
+ const filePath = this.getUsageFilePath(keyId);
179
+ const dir = path_1.default.dirname(filePath);
180
+ yield promises_1.default.mkdir(dir, { recursive: true });
181
+ // 原子写入
182
+ const tmpPath = filePath + '.tmp';
183
+ yield promises_1.default.writeFile(tmpPath, JSON.stringify(usage, null, 2), 'utf-8');
184
+ yield promises_1.default.rename(tmpPath, filePath);
185
+ });
186
+ }
187
+ getUsageFilePath(keyId) {
188
+ return path_1.default.join(this.dataPath, 'key-usage', `${keyId}.json`);
189
+ }
190
+ createEmptyUsage(keyId) {
191
+ const now = Date.now();
192
+ return {
193
+ keyId,
194
+ lifetime: { totalTokens: 0, inputTokens: 0, outputTokens: 0, totalRequests: 0, errorCount: 0 },
195
+ periods: {
196
+ daily: { tokens: 0, requests: 0, periodStart: now },
197
+ weekly: { tokens: 0, requests: 0, periodStart: now },
198
+ monthly: { tokens: 0, requests: 0, periodStart: now },
199
+ },
200
+ dailyHistory: [],
201
+ };
202
+ }
203
+ /** 更新周期 Token(自动检测重置) */
204
+ updatePeriodTokens(period, now, type, tokens) {
205
+ if (this.isPeriodExpired(period.periodStart, now, type)) {
206
+ period.tokens = tokens;
207
+ period.requests = 0;
208
+ period.periodStart = now;
209
+ }
210
+ else {
211
+ period.tokens += tokens;
212
+ }
213
+ }
214
+ /** 更新周期请求计数(自动检测重置) */
215
+ updatePeriodRequests(period, now, type) {
216
+ if (this.isPeriodExpired(period.periodStart, now, type)) {
217
+ period.requests = 1;
218
+ period.tokens = 0;
219
+ period.periodStart = now;
220
+ }
221
+ else {
222
+ period.requests += 1;
223
+ }
224
+ }
225
+ /** 检查周期是否已过期(需要重置) */
226
+ isPeriodExpired(periodStart, now, type) {
227
+ switch (type) {
228
+ case 'daily': {
229
+ const d1 = new Date(periodStart);
230
+ const d2 = new Date(now);
231
+ return d1.getUTCFullYear() !== d2.getUTCFullYear() ||
232
+ d1.getUTCMonth() !== d2.getUTCMonth() ||
233
+ d1.getUTCDate() !== d2.getUTCDate();
234
+ }
235
+ case 'weekly': {
236
+ // 简单判断:如果超过 7 天
237
+ return (now - periodStart) > 7 * 24 * 3600 * 1000;
238
+ }
239
+ case 'monthly': {
240
+ const d1 = new Date(periodStart);
241
+ const d2 = new Date(now);
242
+ return d1.getUTCFullYear() !== d2.getUTCFullYear() || d1.getUTCMonth() !== d2.getUTCMonth();
243
+ }
244
+ case 'custom':
245
+ // 自定义周期由外部处理
246
+ return false;
247
+ }
248
+ }
249
+ /** 更新日历史记录 */
250
+ updateDailyHistory(usage, now, tokens, errors) {
251
+ const today = this.formatDate(now);
252
+ const existing = usage.dailyHistory.find(h => h.date === today);
253
+ if (existing) {
254
+ existing.tokens += tokens;
255
+ existing.requests += 1;
256
+ existing.errors += errors;
257
+ }
258
+ else {
259
+ usage.dailyHistory.push({
260
+ date: today,
261
+ tokens,
262
+ requests: 1,
263
+ errors,
264
+ });
265
+ // 保留最近 90 天
266
+ if (usage.dailyHistory.length > 90) {
267
+ usage.dailyHistory = usage.dailyHistory.slice(-90);
268
+ }
269
+ }
270
+ }
271
+ formatDate(ts) {
272
+ const d = new Date(ts);
273
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`;
274
+ }
275
+ markDirty(keyId) {
276
+ this.dirty.add(keyId);
277
+ }
278
+ }
279
+ exports.UsageTracker = UsageTracker;
@@ -60,6 +60,10 @@ function verifyToken(token) {
60
60
  *
61
61
  * 如果未启用鉴权,直接放行
62
62
  * 如果启用鉴权但 token 无效,返回 401
63
+ *
64
+ * 认证 Header 策略:
65
+ * - `Access-Token: <jwt_token>` — 管理面板 JWT 认证
66
+ * - `Authorization: Bearer ...` — 可能是 AccessKey(sk_ 前缀),跳过此中间件,由代理引擎处理
63
67
  */
64
68
  function authMiddleware(req, res, next) {
65
69
  // 如果未启用鉴权,直接放行
@@ -67,14 +71,22 @@ function authMiddleware(req, res, next) {
67
71
  next();
68
72
  return;
69
73
  }
70
- // Authorization header 中提取 token
74
+ // 如果请求带有 Authorization header,视为 AccessKey 请求,跳过管理面板认证
75
+ // 由代理引擎在业务前置对 AccessKey 进行鉴权
71
76
  const authHeader = req.headers.authorization;
72
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
77
+ if (authHeader) {
78
+ next();
79
+ return;
80
+ }
81
+ // 从 Access-Token header 或查询参数中提取 JWT token
82
+ // 查询参数支持用于 EventSource 等 API(无法设置自定义 Header)
83
+ const accessToken = req.headers['access-token']
84
+ || req.query.token;
85
+ if (!accessToken) {
73
86
  res.status(401).json({ error: 'Unauthorized: Missing or invalid token' });
74
87
  return;
75
88
  }
76
- const token = authHeader.substring(7); // 移除 "Bearer " 前缀
77
- if (verifyToken(token)) {
89
+ if (verifyToken(accessToken)) {
78
90
  next();
79
91
  }
80
92
  else {
@@ -6,9 +6,11 @@ exports.getManagedFields = exports.CODEX_AUTH_MANAGED_FIELDS = exports.CODEX_CON
6
6
  */
7
7
  exports.CLAUDE_SETTINGS_MANAGED_FIELDS = [
8
8
  { path: ['env', 'ANTHROPIC_AUTH_TOKEN'] },
9
+ { path: ['env', 'ANTHROPIC_API_KEY'], optional: true },
9
10
  { path: ['env', 'ANTHROPIC_BASE_URL'] },
10
11
  { path: ['env', 'API_TIMEOUT_MS'] },
11
12
  { path: ['env', 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'] },
13
+ { path: ['env', 'CLAUDE_CODE_MAX_RETRIES'] },
12
14
  { path: ['env', 'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'], optional: true },
13
15
  { path: ['env', 'CLAUDE_AUTOCOMPACT_PCT_OVERRIDE'], optional: true },
14
16
  { path: ['env', 'ANTHROPIC_DEFAULT_HAIKU_MODEL'], optional: true },
@@ -1203,7 +1203,6 @@ class FileSystemDatabaseManager {
1203
1203
  enableLogging: true,
1204
1204
  logRetentionDays: 30,
1205
1205
  maxLogSize: 100000,
1206
- apiKey: '',
1207
1206
  enableFailover: true,
1208
1207
  failoverRecoverySeconds: DEFAULT_FAILOVER_RECOVERY_SECONDS,
1209
1208
  ruleGlobalTimeout: undefined,
@@ -1218,6 +1217,7 @@ class FileSystemDatabaseManager {
1218
1217
  proxyUrl: '',
1219
1218
  proxyUsername: '',
1220
1219
  proxyPassword: '',
1220
+ enableLanDiscovery: false,
1221
1221
  };
1222
1222
  // spread: current 覆盖 defaults,未来新增字段自动保留
1223
1223
  this.config = Object.assign(Object.assign({}, defaults), current);
@@ -2376,6 +2376,9 @@ class FileSystemDatabaseManager {
2376
2376
  if (service.inheritVendorApiBaseUrl !== undefined && typeof service.inheritVendorApiBaseUrl !== 'boolean') {
2377
2377
  return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] inheritVendorApiBaseUrl 必须是布尔值` };
2378
2378
  }
2379
+ if (service.inheritVendorAuthType !== undefined && typeof service.inheritVendorAuthType !== 'boolean') {
2380
+ return { valid: false, error: `供应商[${index}](${vendor.id}) 的服务[${i}] inheritVendorAuthType 必须是布尔值` };
2381
+ }
2379
2382
  }
2380
2383
  return { valid: true };
2381
2384
  }
@@ -2690,6 +2693,16 @@ class FileSystemDatabaseManager {
2690
2693
  });
2691
2694
  }
2692
2695
  // Statistics operations
2696
+ /**
2697
+ * 从 AccessKey 请求同步全局统计数据(不写入日志,仅更新统计)
2698
+ */
2699
+ syncStatisticsFromAccessKey(logData) {
2700
+ return __awaiter(this, void 0, void 0, function* () {
2701
+ // 构造一个带有 id 的 RequestLog 以复用 updateStatistics
2702
+ const log = Object.assign(Object.assign({}, logData), { id: `ak-sync-${Date.now()}` });
2703
+ yield this.updateStatistics(log);
2704
+ });
2705
+ }
2693
2706
  /**
2694
2707
  * 更新统计数据 - 在每次添加日志时调用
2695
2708
  */