@adversity/coding-tool-x 3.0.5 → 3.0.6
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/CHANGELOG.md +19 -0
- package/dist/web/assets/{icons-BlzwYoRU.js → icons-BxudHPiX.js} +1 -1
- package/dist/web/assets/index-D2VfwJBa.js +14 -0
- package/dist/web/assets/index-oXBzu0bd.css +41 -0
- package/dist/web/assets/{naive-ui-B1TP-0TP.js → naive-ui-DT-Uur8K.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +1 -1
- package/src/server/api/channels.js +3 -0
- package/src/server/api/codex-channels.js +40 -0
- package/src/server/api/config-registry.js +341 -0
- package/src/server/api/gemini-channels.js +40 -0
- package/src/server/api/permissions.js +30 -15
- package/src/server/codex-proxy-server.js +27 -2
- package/src/server/gemini-proxy-server.js +61 -1
- package/src/server/index.js +3 -0
- package/src/server/proxy-server.js +27 -2
- package/src/server/services/codex-channels.js +9 -3
- package/src/server/services/config-registry-service.js +762 -0
- package/src/server/services/config-sync-manager.js +456 -0
- package/src/server/services/config-templates-service.js +38 -3
- package/src/server/services/gemini-channels.js +7 -1
- package/src/server/services/model-detector.js +116 -23
- package/src/server/services/permission-templates-service.js +0 -31
- package/dist/web/assets/index-19ZPjh5b.css +0 -41
- package/dist/web/assets/index-B4w1yh7H.js +0 -14
|
@@ -18,6 +18,10 @@ let currentPort = null;
|
|
|
18
18
|
// 用于存储每个请求的元数据
|
|
19
19
|
const requestMetadata = new Map();
|
|
20
20
|
|
|
21
|
+
// 用于缓存已打印过的模型重定向规则,避免重复打印
|
|
22
|
+
// 格式: { channelId: { "originalModel": "redirectedModel", ... } }
|
|
23
|
+
const printedGeminiRedirectCache = new Map();
|
|
24
|
+
|
|
21
25
|
// Gemini 模型定价(每百万 tokens 的价格,单位:美元)
|
|
22
26
|
const PRICING = {
|
|
23
27
|
'gemini-2.5-pro': { input: 1.25, output: 5 },
|
|
@@ -47,6 +51,27 @@ function resolveGeminiTarget(baseUrl = '', requestPath = '') {
|
|
|
47
51
|
return target;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* 应用模型重定向(精确匹配)
|
|
56
|
+
* @param {string} originalModel - 原始模型名称
|
|
57
|
+
* @param {object} channel - 渠道对象,包含 modelRedirects 数组
|
|
58
|
+
* @returns {string} 重定向后的模型名称
|
|
59
|
+
*/
|
|
60
|
+
function redirectModel(originalModel, channel) {
|
|
61
|
+
if (!originalModel) return originalModel;
|
|
62
|
+
|
|
63
|
+
const modelRedirects = channel?.modelRedirects;
|
|
64
|
+
if (Array.isArray(modelRedirects) && modelRedirects.length > 0) {
|
|
65
|
+
for (const rule of modelRedirects) {
|
|
66
|
+
if (rule.from && rule.to && rule.from === originalModel) {
|
|
67
|
+
return rule.to;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return originalModel;
|
|
73
|
+
}
|
|
74
|
+
|
|
50
75
|
/**
|
|
51
76
|
* 计算请求成本
|
|
52
77
|
*/
|
|
@@ -153,6 +178,27 @@ async function startGeminiProxyServer(options = {}) {
|
|
|
153
178
|
|
|
154
179
|
broadcastSchedulerState('gemini', getSchedulerState('gemini'));
|
|
155
180
|
|
|
181
|
+
// 从 URL 中提取模型名称并应用重定向
|
|
182
|
+
// URL 格式: /models/gemini-2.5-pro:generateContent 或 /v1/models/gemini-2.5-pro:generateContent
|
|
183
|
+
const urlMatch = req.url.match(/\/models\/([\w.-]+)(:[^?]*)?/);
|
|
184
|
+
if (urlMatch) {
|
|
185
|
+
const originalModel = urlMatch[1];
|
|
186
|
+
const redirectedModel = redirectModel(originalModel, channel);
|
|
187
|
+
|
|
188
|
+
if (redirectedModel !== originalModel) {
|
|
189
|
+
// 替换 URL 中的模型名称
|
|
190
|
+
req.url = req.url.replace(`/models/${originalModel}`, `/models/${redirectedModel}`);
|
|
191
|
+
|
|
192
|
+
// 只在重定向规则变化时打印日志(避免每次请求都打印)
|
|
193
|
+
const cachedRedirects = printedGeminiRedirectCache.get(channel.id) || {};
|
|
194
|
+
if (cachedRedirects[originalModel] !== redirectedModel) {
|
|
195
|
+
cachedRedirects[originalModel] = redirectedModel;
|
|
196
|
+
printedGeminiRedirectCache.set(channel.id, cachedRedirects);
|
|
197
|
+
console.log(`[Gemini Model Redirect] ${originalModel} → ${redirectedModel} (channel: ${channel.name})`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
156
202
|
const target = resolveGeminiTarget(channel.baseUrl, req.url);
|
|
157
203
|
|
|
158
204
|
proxy.web(req, res, {
|
|
@@ -511,8 +557,22 @@ function getGeminiProxyStatus() {
|
|
|
511
557
|
};
|
|
512
558
|
}
|
|
513
559
|
|
|
560
|
+
/**
|
|
561
|
+
* 清除指定渠道的模型重定向日志缓存
|
|
562
|
+
* 用于在渠道配置更新后触发重新打印日志
|
|
563
|
+
* @param {string} channelId - 渠道 ID
|
|
564
|
+
*/
|
|
565
|
+
function clearGeminiRedirectCache(channelId) {
|
|
566
|
+
if (channelId) {
|
|
567
|
+
printedGeminiRedirectCache.delete(channelId);
|
|
568
|
+
} else {
|
|
569
|
+
printedGeminiRedirectCache.clear();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
514
573
|
module.exports = {
|
|
515
574
|
startGeminiProxyServer,
|
|
516
575
|
stopGeminiProxyServer,
|
|
517
|
-
getGeminiProxyStatus
|
|
576
|
+
getGeminiProxyStatus,
|
|
577
|
+
clearGeminiRedirectCache
|
|
518
578
|
};
|
package/src/server/index.js
CHANGED
|
@@ -154,6 +154,9 @@ async function startServer(port) {
|
|
|
154
154
|
// 配置同步 API
|
|
155
155
|
app.use('/api/config-sync', require('./api/config-sync'));
|
|
156
156
|
|
|
157
|
+
// 配置注册表 API (集中管理 skills/commands/agents/rules 的启用/禁用)
|
|
158
|
+
app.use('/api/config-registry', require('./api/config-registry'));
|
|
159
|
+
|
|
157
160
|
// 健康检查 API
|
|
158
161
|
app.use('/api/health-check', require('./api/health-check')(config));
|
|
159
162
|
|
|
@@ -21,6 +21,10 @@ let currentPort = null;
|
|
|
21
21
|
// 用于存储每个请求的元数据(用于 WebSocket 日志)
|
|
22
22
|
const requestMetadata = new Map();
|
|
23
23
|
|
|
24
|
+
// 用于缓存已打印过的模型重定向规则,避免重复打印
|
|
25
|
+
// 格式: { channelId: { "originalModel": "redirectedModel", ... } }
|
|
26
|
+
const printedRedirectCache = new Map();
|
|
27
|
+
|
|
24
28
|
const CLAUDE_BASE_PRICING = DEFAULT_CONFIG.pricing.claude;
|
|
25
29
|
const ONE_MILLION = 1000000;
|
|
26
30
|
|
|
@@ -225,7 +229,14 @@ async function startProxyServer(options = {}) {
|
|
|
225
229
|
req.body.model = redirectedModel;
|
|
226
230
|
// 更新 rawBody 以匹配修改后的 body
|
|
227
231
|
req.rawBody = Buffer.from(JSON.stringify(req.body));
|
|
228
|
-
|
|
232
|
+
|
|
233
|
+
// 只在重定向规则变化时打印日志(避免每次请求都打印)
|
|
234
|
+
const cachedRedirects = printedRedirectCache.get(channel.id) || {};
|
|
235
|
+
if (cachedRedirects[originalModel] !== redirectedModel) {
|
|
236
|
+
cachedRedirects[originalModel] = redirectedModel;
|
|
237
|
+
printedRedirectCache.set(channel.id, cachedRedirects);
|
|
238
|
+
console.log(`[Model Redirect] ${originalModel} → ${redirectedModel} (channel: ${channel.name})`);
|
|
239
|
+
}
|
|
229
240
|
}
|
|
230
241
|
}
|
|
231
242
|
|
|
@@ -526,8 +537,22 @@ function getProxyStatus() {
|
|
|
526
537
|
};
|
|
527
538
|
}
|
|
528
539
|
|
|
540
|
+
/**
|
|
541
|
+
* 清除指定渠道的模型重定向日志缓存
|
|
542
|
+
* 用于在渠道配置更新后触发重新打印日志
|
|
543
|
+
* @param {string} channelId - 渠道 ID
|
|
544
|
+
*/
|
|
545
|
+
function clearRedirectCache(channelId) {
|
|
546
|
+
if (channelId) {
|
|
547
|
+
printedRedirectCache.delete(channelId);
|
|
548
|
+
} else {
|
|
549
|
+
printedRedirectCache.clear();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
529
553
|
module.exports = {
|
|
530
554
|
startProxyServer,
|
|
531
555
|
stopProxyServer,
|
|
532
|
-
getProxyStatus
|
|
556
|
+
getProxyStatus,
|
|
557
|
+
clearRedirectCache
|
|
533
558
|
};
|
|
@@ -47,7 +47,9 @@ function loadChannels() {
|
|
|
47
47
|
...ch,
|
|
48
48
|
enabled: ch.enabled !== false, // 默认启用
|
|
49
49
|
weight: ch.weight || 1,
|
|
50
|
-
maxConcurrency: ch.maxConcurrency || null
|
|
50
|
+
maxConcurrency: ch.maxConcurrency || null,
|
|
51
|
+
modelRedirects: ch.modelRedirects || [],
|
|
52
|
+
speedTestModel: ch.speedTestModel || null
|
|
51
53
|
}));
|
|
52
54
|
}
|
|
53
55
|
return data;
|
|
@@ -175,6 +177,8 @@ function createChannel(name, providerKey, baseUrl, apiKey, wireApi = 'responses'
|
|
|
175
177
|
enabled: extraConfig.enabled !== false, // 默认启用
|
|
176
178
|
weight: extraConfig.weight || 1,
|
|
177
179
|
maxConcurrency: extraConfig.maxConcurrency || null,
|
|
180
|
+
modelRedirects: extraConfig.modelRedirects || [],
|
|
181
|
+
speedTestModel: extraConfig.speedTestModel || null,
|
|
178
182
|
createdAt: Date.now(),
|
|
179
183
|
updatedAt: Date.now()
|
|
180
184
|
};
|
|
@@ -217,11 +221,13 @@ function updateChannel(channelId, updates) {
|
|
|
217
221
|
}
|
|
218
222
|
}
|
|
219
223
|
|
|
224
|
+
const merged = { ...oldChannel, ...updates };
|
|
220
225
|
const newChannel = {
|
|
221
|
-
...
|
|
222
|
-
...updates,
|
|
226
|
+
...merged,
|
|
223
227
|
id: channelId, // 保持 ID 不变
|
|
224
228
|
createdAt: oldChannel.createdAt, // 保持创建时间
|
|
229
|
+
modelRedirects: merged.modelRedirects || [],
|
|
230
|
+
speedTestModel: merged.speedTestModel !== undefined ? merged.speedTestModel : (oldChannel.speedTestModel || null),
|
|
225
231
|
updatedAt: Date.now()
|
|
226
232
|
};
|
|
227
233
|
|