coding-tool-x 3.9.6-261014 → 3.9.6-261016
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/dist/web/assets/{AgentsDrawer-D-1A92Ig.js → AgentsDrawer-D2oYKcOj.js} +1 -1
- package/dist/web/assets/{Analytics-B2O5txNK.js → Analytics-Cy3QSSrn.js} +1 -1
- package/dist/web/assets/{AssetPathField-DuohRI-K.js → AssetPathField-BGuw5ZR7.js} +1 -1
- package/dist/web/assets/{ChatHistoryDrawer-CjyehFJ5.js → ChatHistoryDrawer-BZ8FY_-9.js} +1 -1
- package/dist/web/assets/{CollapseItem--khwfagQ.js → CollapseItem-DOoxtmtj.js} +1 -1
- package/dist/web/assets/{CommandsDrawer-gZsBcvyW.js → CommandsDrawer-YCgZAeNx.js} +1 -1
- package/dist/web/assets/{ConfigExportDrawer-CJnljMGE.js → ConfigExportDrawer-BlAHQidK.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-wkwjB2C5.js → ConfigTemplates-C6qSF4Ht.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesDrawer-hAGvUnSt.js → ConfigTemplatesDrawer-lRT_6-jD.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesPanel-BfF8qmL_.js → ConfigTemplatesPanel-DrS57Y8V.js} +1 -1
- package/dist/web/assets/{FavoritesDrawer-YeAnYTuA.js → FavoritesDrawer-CuOcHtqM.js} +1 -1
- package/dist/web/assets/{GatewayConvertDrawer-D1znBVev.js → GatewayConvertDrawer-b0pIvH02.js} +1 -1
- package/dist/web/assets/{Home-CSsVyr4n.js → Home-DvfQcP4b.js} +1 -1
- package/dist/web/assets/{MarkdownEditor-CeLdWA-c.js → MarkdownEditor-BLS6HmBb.js} +1 -1
- package/dist/web/assets/{McpDrawer-z-veNi2j.js → McpDrawer-BHt7zFf0.js} +1 -1
- package/dist/web/assets/{McpFormDrawer-DgDBnMOk.js → McpFormDrawer-DpTMHc-e.js} +1 -1
- package/dist/web/assets/{PluginManager-DFYt_Xmd.js → PluginManager-CYAuy5dm.js} +1 -1
- package/dist/web/assets/{PluginsDrawer-Dqpb2UsR.js → PluginsDrawer-CoyprWNl.js} +1 -1
- package/dist/web/assets/{PluginsPanel-CvcT0Up3.js → PluginsPanel-C3AzXlkH.js} +1 -1
- package/dist/web/assets/{ProjectList-DjwDDvVo.js → ProjectList-pJo0xp0Q.js} +1 -1
- package/dist/web/assets/{PromptsDrawer-B2vI4UfM.js → PromptsDrawer-CM-irN84.js} +1 -1
- package/dist/web/assets/{RecentSessionsDrawer-DTbikshr.js → RecentSessionsDrawer-l5nO_5bS.js} +1 -1
- package/dist/web/assets/{SessionCard-AKJF0KLk.js → SessionCard-BqTFOt05.js} +1 -1
- package/dist/web/assets/{SessionList-C2kxSHyw.js → SessionList-Bs_YFKW5.js} +1 -1
- package/dist/web/assets/{SettingsDrawer-CYNU3Scm.js → SettingsDrawer-COszrj_Y.js} +1 -1
- package/dist/web/assets/{SkillManager-MMDMnZuA.js → SkillManager-DgLhL2Qv.js} +1 -1
- package/dist/web/assets/{SkillsDrawer-Bg6QLvgm.js → SkillsDrawer-CT69YSEE.js} +1 -1
- package/dist/web/assets/{SkillsPanel-CDUSYnfc.js → SkillsPanel-Dm8FkYD6.js} +1 -1
- package/dist/web/assets/{Upload-DMr2S_D5.js → Upload-BHTymsxe.js} +1 -1
- package/dist/web/assets/{WorkspaceDrawer-VrUPX-Pa.js → WorkspaceDrawer-DLXh_c7u.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-q76RE4Ss.js → WorkspaceManager-E_IoRTlC.js} +1 -1
- package/dist/web/assets/{WorkspacePanel-CSDGZMrU.js → WorkspacePanel-DLlTIU8Y.js} +1 -1
- package/dist/web/assets/index-DP0Ggf-8.js +2 -0
- package/dist/web/assets/index-DjIuAtaS.css +1 -0
- package/dist/web/assets/{platforms-JV36QBCw.js → platforms-DNVEAtqx.js} +1 -1
- package/dist/web/assets/platforms-p9AiybLU.js +1 -0
- package/dist/web/assets/{sessions-wvj5oaYr.js → sessions-DJiJJgvg.js} +1 -1
- package/dist/web/assets/sessions-DSLpMXCq.js +1 -0
- package/dist/web/assets/{useResponsiveDrawer-DXmsFFe1.js → useResponsiveDrawer-D9ti7g-U.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +1 -1
- package/src/commands/channels.js +88 -95
- package/src/commands/toggle-proxy.js +26 -19
- package/src/platforms/drivers/claude/channels.js +11 -1
- package/src/platforms/drivers/claude/proxy.js +1 -2
- package/src/platforms/drivers/codex/channels.js +14 -2
- package/src/platforms/drivers/codex/proxy.js +1 -2
- package/src/platforms/drivers/dsh/api-operations.js +1 -1
- package/src/platforms/drivers/dsh/channels.js +3 -7
- package/src/platforms/drivers/dsh/proxy.js +0 -4
- package/src/platforms/drivers/gemini/channels.js +10 -12
- package/src/platforms/drivers/gemini/proxy.js +1 -2
- package/src/platforms/drivers/omp/channels.js +53 -40
- package/src/platforms/drivers/omp/gateway-routing.js +0 -54
- package/src/platforms/drivers/omp/proxy.js +1 -2
- package/src/platforms/drivers/opencode/channels.js +53 -24
- package/src/platforms/drivers/opencode/proxy.js +1 -2
- package/src/platforms/manifest-schema.js +53 -1
- package/src/platforms/manifests/claude.json +11 -0
- package/src/platforms/manifests/codex.json +11 -16
- package/src/platforms/manifests/dsh.json +13 -0
- package/src/platforms/manifests/gemini.json +17 -16
- package/src/platforms/manifests/omp.json +6 -16
- package/src/platforms/manifests/opencode.json +21 -16
- package/src/server/api/platforms.js +29 -0
- package/src/shared/channel-driver.js +12 -14
- package/dist/web/assets/index-BXFl1mzC.js +0 -2
- package/dist/web/assets/index-CcIxENSq.css +0 -1
- package/dist/web/assets/platforms-DILqyP72.js +0 -1
- package/dist/web/assets/sessions-WXODMvLY.js +0 -1
|
@@ -188,13 +188,17 @@ async function handleStartProxy(cliType, services) {
|
|
|
188
188
|
console.log(chalk.bold.cyan('\n╔=======================================╗'));
|
|
189
189
|
const toolName = getPlatformLabel(cliType);
|
|
190
190
|
const defaultPort = services.defaultPort;
|
|
191
|
+
const labels = services.proxyLabels || {};
|
|
192
|
+
const serviceLabel = labels.serviceLabel || `${toolName} 代理服务`;
|
|
193
|
+
const proxyLabel = labels.proxyLabel || '动态切换';
|
|
194
|
+
const addressLabel = labels.addressLabel || (services.managedProviderConfig ? '网关地址' : '代理地址');
|
|
191
195
|
|
|
192
196
|
console.log(chalk.cyan('动态切换功能说明:'));
|
|
193
197
|
if (services.managedProviderConfig) {
|
|
194
|
-
console.log(chalk.gray(
|
|
195
|
-
console.log(chalk.gray(`• 可以通过 Web UI 或"渠道管理"功能调整启用的 ${toolName}
|
|
196
|
-
console.log(chalk.gray('•
|
|
197
|
-
console.log(chalk.gray(`•
|
|
198
|
+
console.log(chalk.gray(`• 开启后会启动${serviceLabel},并将平台原生 provider 指向本地网关`));
|
|
199
|
+
console.log(chalk.gray(`• 可以通过 Web UI 或"渠道管理"功能调整启用的 ${toolName} 渠道`));
|
|
200
|
+
console.log(chalk.gray('• 网关按平台路由设置分发请求,模型 fallback 仍由平台处理'));
|
|
201
|
+
console.log(chalk.gray(`• ${addressLabel}: http://127.0.0.1:${defaultPort}\n`));
|
|
198
202
|
} else {
|
|
199
203
|
console.log(chalk.gray('• 开启后会在本地启动一个代理服务'));
|
|
200
204
|
console.log(chalk.gray(`• 可以在不重启 ${toolName} 的情况下动态管理渠道`));
|
|
@@ -204,8 +208,8 @@ async function handleStartProxy(cliType, services) {
|
|
|
204
208
|
|
|
205
209
|
console.log(chalk.yellow('[WARN] 重要提示:'));
|
|
206
210
|
if (services.managedProviderConfig) {
|
|
207
|
-
console.log(chalk.yellow('•
|
|
208
|
-
console.log(chalk.yellow('•
|
|
211
|
+
console.log(chalk.yellow('• 启用后会写入指向本地网关的受管 provider 配置'));
|
|
212
|
+
console.log(chalk.yellow('• 关闭时会由平台 Driver 按原生配置策略完成交接\n'));
|
|
209
213
|
} else {
|
|
210
214
|
console.log(chalk.yellow('• 开启期间请勿关闭 CLI 终端窗口'));
|
|
211
215
|
console.log(chalk.yellow('• 如果异常关闭导致代理失效,请运行: ctx reset'));
|
|
@@ -228,18 +232,18 @@ async function handleStartProxy(cliType, services) {
|
|
|
228
232
|
|
|
229
233
|
try {
|
|
230
234
|
console.log(chalk.cyan(services.managedProviderConfig
|
|
231
|
-
?
|
|
235
|
+
? `\n[START] 正在启用${serviceLabel}...\n`
|
|
232
236
|
: '\n[START] 正在启动代理服务...\n'));
|
|
233
237
|
|
|
234
238
|
// 启动代理服务器
|
|
235
239
|
const proxyResult = await services.startProxyServer();
|
|
236
240
|
|
|
237
241
|
if (!proxyResult.success) {
|
|
238
|
-
throw new Error(services.managedProviderConfig ?
|
|
242
|
+
throw new Error(services.managedProviderConfig ? `${serviceLabel}启用失败` : '代理服务器启动失败');
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
if (services.managedProviderConfig) {
|
|
242
|
-
console.log(chalk.green(
|
|
246
|
+
console.log(chalk.green(`[OK] ${labels.startedMessage || `${serviceLabel}已启用`}`));
|
|
243
247
|
(proxyResult.warnings || []).forEach((warning) => {
|
|
244
248
|
console.log(chalk.yellow(`[WARN] ${warning}`));
|
|
245
249
|
});
|
|
@@ -259,9 +263,9 @@ async function handleStartProxy(cliType, services) {
|
|
|
259
263
|
}
|
|
260
264
|
}
|
|
261
265
|
|
|
262
|
-
console.log(chalk.cyan(
|
|
266
|
+
console.log(chalk.cyan(`\n[TIP] ${labels.startedMessage || `${proxyLabel}已启用`}!`));
|
|
263
267
|
if (services.managedProviderConfig) {
|
|
264
|
-
console.log(chalk.gray(
|
|
268
|
+
console.log(chalk.gray(` 现在可以通过"渠道管理"功能调整已启用的 ${toolName} 渠道\n`));
|
|
265
269
|
} else {
|
|
266
270
|
console.log(chalk.gray(` 现在可以通过"渠道管理"功能快速调整,无需重启 ${toolName}\n`));
|
|
267
271
|
}
|
|
@@ -297,11 +301,14 @@ async function handleStopProxy(cliType, services) {
|
|
|
297
301
|
|
|
298
302
|
const toolName = getPlatformLabel(cliType);
|
|
299
303
|
const proxyStatus = services.getProxyStatus();
|
|
304
|
+
const labels = services.proxyLabels || {};
|
|
305
|
+
const serviceLabel = labels.serviceLabel || `${toolName} 代理服务`;
|
|
306
|
+
const proxyLabel = labels.proxyLabel || '动态切换';
|
|
300
307
|
|
|
301
308
|
console.log(chalk.cyan('当前状态:'));
|
|
302
309
|
if (services.managedProviderConfig) {
|
|
303
|
-
console.log(chalk.gray(`•
|
|
304
|
-
console.log(chalk.gray(`• 配置模式: ${proxyStatus.mode || '
|
|
310
|
+
console.log(chalk.gray(`• ${proxyLabel}: ${chalk.green(labels.runningText || '已启用')}`));
|
|
311
|
+
console.log(chalk.gray(`• 配置模式: ${proxyStatus.mode || '平台受管 provider 配置'}`));
|
|
305
312
|
} else {
|
|
306
313
|
console.log(chalk.gray(`• 代理服务: ${chalk.green('运行中')}`));
|
|
307
314
|
console.log(chalk.gray(`• 代理端口: ${proxyStatus.port}`));
|
|
@@ -310,8 +317,8 @@ async function handleStopProxy(cliType, services) {
|
|
|
310
317
|
|
|
311
318
|
console.log(chalk.yellow('关闭后:'));
|
|
312
319
|
if (services.managedProviderConfig) {
|
|
313
|
-
console.log(chalk.gray(
|
|
314
|
-
console.log(chalk.gray(`• 之后调整 ${toolName}
|
|
320
|
+
console.log(chalk.gray(`• ${serviceLabel}会按平台 Driver 策略交接原生 provider 配置`));
|
|
321
|
+
console.log(chalk.gray(`• 之后调整 ${toolName} 渠道或模型将按平台配置策略生效\n`));
|
|
315
322
|
} else {
|
|
316
323
|
console.log(chalk.gray('• 代理服务将被停止'));
|
|
317
324
|
console.log(chalk.gray('• 配置将恢复为当前激活渠道的单渠道模式'));
|
|
@@ -334,13 +341,13 @@ async function handleStopProxy(cliType, services) {
|
|
|
334
341
|
|
|
335
342
|
try {
|
|
336
343
|
console.log(chalk.cyan(services.managedProviderConfig
|
|
337
|
-
?
|
|
344
|
+
? `\n[STOP] 正在停止${serviceLabel}...\n`
|
|
338
345
|
: '\n[STOP] 正在停止代理服务...\n'));
|
|
339
346
|
|
|
340
347
|
// 停止代理服务器
|
|
341
348
|
await services.stopProxyServer();
|
|
342
349
|
console.log(chalk.green(services.managedProviderConfig
|
|
343
|
-
?
|
|
350
|
+
? (labels.stoppedMessage || `${serviceLabel}已停止`)
|
|
344
351
|
: '[OK] 代理服务已停止'));
|
|
345
352
|
|
|
346
353
|
// 恢复配置文件
|
|
@@ -364,9 +371,9 @@ async function handleStopProxy(cliType, services) {
|
|
|
364
371
|
console.log(chalk.green('[OK] 已清理代理接管状态'));
|
|
365
372
|
}
|
|
366
373
|
|
|
367
|
-
console.log(chalk.cyan(
|
|
374
|
+
console.log(chalk.cyan(`\n[TIP] ${labels.stoppedMessage || `${proxyLabel}已关闭`}`));
|
|
368
375
|
if (services.managedProviderConfig) {
|
|
369
|
-
console.log(chalk.gray(
|
|
376
|
+
console.log(chalk.gray(` ${toolName} 渠道配置已交由平台 Driver 处理\n`));
|
|
370
377
|
} else {
|
|
371
378
|
console.log(chalk.gray(` 现在调整渠道需要重启 ${toolName} 才能生效\n`));
|
|
372
379
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
|
|
5
5
|
function createDriver(context = {}) {
|
|
6
|
+
const { resetChannelHealth } = require('../../../server/services/channel-health');
|
|
6
7
|
const driver = createChannelDriver({
|
|
7
8
|
...context,
|
|
8
9
|
platform: 'claude',
|
|
@@ -10,16 +11,25 @@ function createDriver(context = {}) {
|
|
|
10
11
|
localServicePath: '../platforms/drivers/claude/channels-implementation',
|
|
11
12
|
listMethod: 'getAllChannels',
|
|
12
13
|
syncMethod: 'syncCurrentClaudeChannel',
|
|
14
|
+
modelListType: context.manifest?.channelModelType || 'claude',
|
|
13
15
|
createArgs: (input, rest) => typeof input === 'object'
|
|
14
16
|
? (() => {
|
|
15
17
|
const { name, baseUrl, apiKey, websiteUrl, extra, ...channelExtra } = input;
|
|
16
18
|
return [name, baseUrl, apiKey, websiteUrl, { ...channelExtra, ...extra }];
|
|
17
19
|
})()
|
|
18
20
|
: [input, ...rest],
|
|
19
|
-
cliMetadata: { supportsCliCreate: true, supportsCliToggle: true, defaultPort: 20088 },
|
|
20
21
|
dashboardChannelShape: 'array'
|
|
21
22
|
});
|
|
22
23
|
const callLegacy = (name, args) => driver._service()[name](...args);
|
|
24
|
+
const saveOrder = driver.saveOrder;
|
|
25
|
+
driver.saveOrder = input => saveOrder(input?.order || input);
|
|
26
|
+
driver.getCurrentChannel = () => callLegacy('getCurrentChannel', []);
|
|
27
|
+
driver.resetHealth = input => {
|
|
28
|
+
const channelId = typeof input === 'string' ? input : input?.channelId || input?.body?.channelId;
|
|
29
|
+
if (!channelId) throw new Error('Channel id is required');
|
|
30
|
+
resetChannelHealth(channelId, 'claude');
|
|
31
|
+
return { channelId, reset: true };
|
|
32
|
+
};
|
|
23
33
|
const auth = require('../../channel-auth-service');
|
|
24
34
|
driver.getAuth = context => auth.getChannelAuth('claude', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
|
|
25
35
|
driver.syncLocalAuth = context => auth.syncLocalChannelAuth('claude', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
|
|
@@ -8,8 +8,7 @@ function createDriver(context = {}) {
|
|
|
8
8
|
platform: 'claude',
|
|
9
9
|
servicePath: './claude/proxy-implementation',
|
|
10
10
|
localServicePath: '../platforms/drivers/claude/proxy-implementation',
|
|
11
|
-
exports: { status: 'getProxyStatus', start: 'startProxyServer', stop: 'stopProxyServer' }
|
|
12
|
-
cliMetadata: { defaultPort: 20088 }
|
|
11
|
+
exports: { status: 'getProxyStatus', start: 'startProxyServer', stop: 'stopProxyServer' }
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
|
|
5
5
|
function createDriver(context = {}) {
|
|
6
|
+
const { resetChannelHealth } = require('../../../server/services/channel-health');
|
|
6
7
|
const driver = createChannelDriver({
|
|
7
8
|
...context,
|
|
8
9
|
platform: 'codex',
|
|
@@ -15,12 +16,23 @@ function createDriver(context = {}) {
|
|
|
15
16
|
return [name, providerKey, baseUrl, apiKey, wireApi, { ...channelExtra, ...extra }];
|
|
16
17
|
})()
|
|
17
18
|
: [input, ...rest],
|
|
18
|
-
|
|
19
|
-
modelListType: 'openai_compatible',
|
|
19
|
+
modelListType: context.manifest?.channelModelType || 'openai_compatible',
|
|
20
20
|
formatCliChannelDetails: channel => channel.providerKey
|
|
21
21
|
? [`provider ${channel.providerKey}`]
|
|
22
22
|
: []
|
|
23
23
|
});
|
|
24
|
+
const saveOrder = driver.saveOrder;
|
|
25
|
+
driver.saveOrder = input => saveOrder(input?.order || input);
|
|
26
|
+
driver.resetHealth = input => {
|
|
27
|
+
const channelId = typeof input === 'string' ? input : input?.channelId || input?.body?.channelId;
|
|
28
|
+
if (!channelId) throw new Error('Channel id is required');
|
|
29
|
+
resetChannelHealth(channelId, 'codex');
|
|
30
|
+
return { channelId, reset: true };
|
|
31
|
+
};
|
|
32
|
+
driver.createFromCli = input => driver.create({
|
|
33
|
+
...input,
|
|
34
|
+
providerKey: String(input?.providerKey || input?.name || '').trim().toLowerCase().replace(/\s+/g, '-')
|
|
35
|
+
});
|
|
24
36
|
const auth = require('../../channel-auth-service');
|
|
25
37
|
driver.getAuth = context => auth.getChannelAuth('codex', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
|
|
26
38
|
driver.syncLocalAuth = context => auth.syncLocalChannelAuth('codex', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
|
|
@@ -8,8 +8,7 @@ function createDriver(context = {}) {
|
|
|
8
8
|
platform: 'codex',
|
|
9
9
|
servicePath: './codex/proxy-implementation',
|
|
10
10
|
localServicePath: '../platforms/drivers/codex/proxy-implementation',
|
|
11
|
-
exports: { status: 'getCodexProxyStatus', start: 'startCodexProxyServer', stop: 'stopCodexProxyServer' }
|
|
12
|
-
cliMetadata: { defaultPort: 20089 }
|
|
11
|
+
exports: { status: 'getCodexProxyStatus', start: 'startCodexProxyServer', stop: 'stopCodexProxyServer' }
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -222,7 +222,7 @@ function createDriver(context = {}) {
|
|
|
222
222
|
|
|
223
223
|
// Keep the aggregate DSH API driver compatible with the manifest consistency
|
|
224
224
|
// contract. Actual descriptor requests are still dispatched by capability.
|
|
225
|
-
for (const operation of ['list', 'current', 'enabled', 'create', 'update', 'remove', 'applyToSettings', 'sync', 'order', 'models', 'probeModels', 'speedTest']) {
|
|
225
|
+
for (const operation of ['list', 'current', 'enabled', 'poolStatus', 'create', 'update', 'remove', 'applyToSettings', 'sync', 'order', 'models', 'probeModels', 'speedTest']) {
|
|
226
226
|
driver[operation] = (...args) => channels[operation](...args);
|
|
227
227
|
}
|
|
228
228
|
for (const operation of ['status', 'start', 'stop']) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const implementation = require('./channels-implementation');
|
|
4
|
+
const { getSchedulerState } = require('../../../server/services/channel-scheduler');
|
|
4
5
|
|
|
5
6
|
function resultFor(request, status, data, error) {
|
|
6
7
|
const route = request?.route || {};
|
|
@@ -69,17 +70,12 @@ function createDriver(context = {}) {
|
|
|
69
70
|
const driver = {
|
|
70
71
|
platform: 'dsh',
|
|
71
72
|
capability: 'channels',
|
|
72
|
-
getCliMetadata: () => ({
|
|
73
|
-
supportsCliCreate: false,
|
|
74
|
-
supportsCliToggle: true,
|
|
75
|
-
managedProviderConfig: true,
|
|
76
|
-
defaultPort: 20093,
|
|
77
|
-
createUnavailableMessage: '提示: DSH 渠道请通过 Web UI 或 API 添加。'
|
|
78
|
-
}),
|
|
73
|
+
getCliMetadata: () => ({ ...(context.manifest?.channelCli || {}) }),
|
|
79
74
|
list: request => withResult(request, 'list', () => implementation.getChannels()),
|
|
80
75
|
getChannels: () => implementation.getChannels().channels,
|
|
81
76
|
getEnabled: request => withResult(request, 'enabled', () => ({ channels: implementation.getEnabledChannels() })),
|
|
82
77
|
getEnabledChannels: () => implementation.getEnabledChannels(),
|
|
78
|
+
poolStatus: request => withResult(request, 'poolStatus', () => ({ scheduler: getSchedulerState('dsh') })),
|
|
83
79
|
current: request => withResult(request, 'current', () => implementation.getCurrentChannel()),
|
|
84
80
|
create: request => withResult(request, 'create', () => {
|
|
85
81
|
const channel = implementation.createChannel(bodyOf(request));
|
|
@@ -3,32 +3,30 @@
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
|
|
5
5
|
function createDriver(context = {}) {
|
|
6
|
+
const { resetChannelHealth } = require('../../../server/services/channel-health');
|
|
6
7
|
const driver = createChannelDriver({
|
|
7
8
|
...context,
|
|
8
9
|
platform: 'gemini',
|
|
9
10
|
servicePath: './gemini/channels-implementation',
|
|
10
11
|
localServicePath: '../platforms/drivers/gemini/channels-implementation',
|
|
11
12
|
syncMethod: 'syncCurrentGeminiChannel',
|
|
13
|
+
modelListType: context.manifest?.channelModelType || 'gemini',
|
|
12
14
|
createArgs: (input, rest) => typeof input === 'object'
|
|
13
15
|
? (() => {
|
|
14
16
|
const { name, baseUrl, apiKey, model, extra, ...channelExtra } = input;
|
|
15
17
|
return [name, baseUrl, apiKey, model, { ...channelExtra, ...extra }];
|
|
16
18
|
})()
|
|
17
19
|
: [input, ...rest],
|
|
18
|
-
cliMetadata: {
|
|
19
|
-
supportsCliCreate: true,
|
|
20
|
-
supportsCliToggle: true,
|
|
21
|
-
defaultPort: 20090,
|
|
22
|
-
createDefaults: { model: 'gemini-2.5-pro' },
|
|
23
|
-
createQuestions: [{
|
|
24
|
-
type: 'input',
|
|
25
|
-
name: 'model',
|
|
26
|
-
message: '模型名称 (默认: gemini-2.5-pro):',
|
|
27
|
-
default: 'gemini-2.5-pro'
|
|
28
|
-
}]
|
|
29
|
-
},
|
|
30
20
|
formatCliChannelDetails: channel => channel.model ? [`model ${channel.model}`] : []
|
|
31
21
|
});
|
|
22
|
+
const saveOrder = driver.saveOrder;
|
|
23
|
+
driver.saveOrder = input => saveOrder(input?.order || input);
|
|
24
|
+
driver.resetHealth = input => {
|
|
25
|
+
const channelId = typeof input === 'string' ? input : input?.channelId || input?.body?.channelId;
|
|
26
|
+
if (!channelId) throw new Error('Channel id is required');
|
|
27
|
+
resetChannelHealth(channelId, 'gemini');
|
|
28
|
+
return { channelId, reset: true };
|
|
29
|
+
};
|
|
32
30
|
const auth = require('../../channel-auth-service');
|
|
33
31
|
driver.getAuth = context => auth.getChannelAuth('gemini', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
|
|
34
32
|
driver.syncLocalAuth = context => auth.syncLocalChannelAuth('gemini', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
|
|
@@ -8,8 +8,7 @@ function createDriver(context = {}) {
|
|
|
8
8
|
platform: 'gemini',
|
|
9
9
|
servicePath: './gemini/proxy-implementation',
|
|
10
10
|
localServicePath: '../platforms/drivers/gemini/proxy-implementation',
|
|
11
|
-
exports: { status: 'getGeminiProxyStatus', start: 'startGeminiProxyServer', stop: 'stopGeminiProxyServer' }
|
|
12
|
-
cliMetadata: { defaultPort: 20090 }
|
|
11
|
+
exports: { status: 'getGeminiProxyStatus', start: 'startGeminiProxyServer', stop: 'stopGeminiProxyServer' }
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -2,59 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
const { ok, failed } = require('../../../shared/driver-result');
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
function providerApiForChannel(channel = {}, input = {}) {
|
|
7
|
+
if (channel.providerApi) return channel.providerApi;
|
|
8
|
+
if (channel.gatewaySourceType === 'claude') return 'anthropic-messages';
|
|
9
|
+
if (channel.gatewaySourceType === 'gemini') return 'google-generative-ai';
|
|
10
|
+
if (channel.gatewaySourceType === 'codex') return 'responses';
|
|
11
|
+
return input.wireApi || 'openai-completions';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createChannelArgs(input, rest) {
|
|
15
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return [input, ...rest];
|
|
16
|
+
const { name, baseUrl, apiKey, extra, ...fields } = input;
|
|
17
|
+
const inputFields = { ...fields, ...(extra || {}) };
|
|
18
|
+
const channel = {
|
|
19
|
+
wireApi: 'openai',
|
|
20
|
+
providerKey: '',
|
|
21
|
+
authMode: 'api_key',
|
|
22
|
+
oauthProviderId: '',
|
|
23
|
+
transport: '',
|
|
24
|
+
routingGroup: '',
|
|
25
|
+
gatewaySourceType: 'openai_compatible',
|
|
26
|
+
enabled: true,
|
|
27
|
+
model: null,
|
|
28
|
+
allowedModels: [],
|
|
29
|
+
speedTestModel: null,
|
|
30
|
+
presetId: null,
|
|
31
|
+
websiteUrl: '',
|
|
32
|
+
balanceToken: '',
|
|
33
|
+
balanceUserId: null,
|
|
34
|
+
models: [],
|
|
35
|
+
modelMetadataMode: 'auto',
|
|
36
|
+
modelBindings: [],
|
|
37
|
+
providerConfig: {},
|
|
38
|
+
...inputFields
|
|
39
|
+
};
|
|
40
|
+
channel.enabled = channel.enabled !== false;
|
|
41
|
+
channel.allowedModels = Array.isArray(channel.allowedModels) ? channel.allowedModels : [];
|
|
42
|
+
channel.models = Array.isArray(channel.models) ? channel.models : [];
|
|
43
|
+
channel.modelBindings = Array.isArray(channel.modelBindings) ? channel.modelBindings : [];
|
|
44
|
+
channel.providerApi = providerApiForChannel(channel, inputFields);
|
|
45
|
+
return [name, baseUrl, apiKey, channel];
|
|
46
|
+
}
|
|
6
47
|
|
|
7
48
|
function createDriver(context = {}) {
|
|
49
|
+
const { resetChannelHealth } = require('../../../server/services/channel-health');
|
|
8
50
|
const driver = createChannelDriver({
|
|
9
51
|
...context,
|
|
10
52
|
platform: 'omp',
|
|
11
53
|
servicePath: './omp/channels-implementation',
|
|
12
54
|
localServicePath: '../platforms/drivers/omp/channels-implementation',
|
|
13
55
|
syncMethod: 'syncCurrentOmpChannel',
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const { name, baseUrl, apiKey, extra, ...channelExtra } = input;
|
|
17
|
-
return [name, baseUrl, apiKey, { ...channelExtra, ...extra }];
|
|
18
|
-
})()
|
|
19
|
-
: [input, ...rest],
|
|
20
|
-
cliMetadata: {
|
|
21
|
-
supportsCliCreate: false,
|
|
22
|
-
supportsCliToggle: true,
|
|
23
|
-
managedProviderConfig: true,
|
|
24
|
-
defaultPort: 20092,
|
|
25
|
-
createUnavailableMessage: '提示: OMP 渠道请通过 Web UI 或 API 添加后,再在这里启停调度。'
|
|
26
|
-
},
|
|
56
|
+
modelListType: context.manifest?.channelModelType || 'openai_compatible',
|
|
57
|
+
createArgs: createChannelArgs,
|
|
27
58
|
dashboardChannelShape: 'array'
|
|
28
59
|
});
|
|
60
|
+
const saveOrder = driver.saveOrder;
|
|
61
|
+
driver.saveOrder = input => saveOrder(input?.order || input);
|
|
62
|
+
driver.resetHealth = input => {
|
|
63
|
+
const channelId = typeof input === 'string' ? input : input?.channelId || input?.body?.channelId;
|
|
64
|
+
if (!channelId) throw new Error('Channel id is required');
|
|
65
|
+
resetChannelHealth(channelId, 'omp');
|
|
66
|
+
return { channelId, reset: true };
|
|
67
|
+
};
|
|
29
68
|
driver.syncManagedProviderExtension = (...args) => (
|
|
30
69
|
driver._service().syncManagedProviderExtension(...args)
|
|
31
70
|
);
|
|
32
|
-
const getPoolStatus = driver.poolStatus;
|
|
33
|
-
driver.poolStatus = () => {
|
|
34
|
-
try {
|
|
35
|
-
const service = driver._service();
|
|
36
|
-
const modeState = service.loadManagedOmpModeState?.();
|
|
37
|
-
const routingSummary = channels => buildOmpRoutingSummary(
|
|
38
|
-
Array.isArray(channels) ? channels : channels?.channels || [],
|
|
39
|
-
modeState?.gateway || {}
|
|
40
|
-
);
|
|
41
|
-
const result = getPoolStatus();
|
|
42
|
-
if (result?.status !== 'ok') return result;
|
|
43
|
-
const channels = service.getChannels();
|
|
44
|
-
if (channels && typeof channels.then === 'function') {
|
|
45
|
-
return channels.then(value => ok('omp', 'channels', 'poolStatus', {
|
|
46
|
-
...result.data,
|
|
47
|
-
routingSummary: routingSummary(value)
|
|
48
|
-
})).catch(error => failed('omp', 'channels', 'poolStatus', error));
|
|
49
|
-
}
|
|
50
|
-
return ok('omp', 'channels', 'poolStatus', {
|
|
51
|
-
...result.data,
|
|
52
|
-
routingSummary: routingSummary(channels)
|
|
53
|
-
});
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return failed('omp', 'channels', 'poolStatus', error);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
71
|
driver.catalogMetadata = (input = {}) => {
|
|
59
72
|
const operation = 'catalogMetadata';
|
|
60
73
|
const body = input && typeof input === 'object' && Object.prototype.hasOwnProperty.call(input, 'body')
|
|
@@ -228,59 +228,6 @@ function channelSupportsOmpModel(channel = {}, modelId = '') {
|
|
|
228
228
|
return declared.length === 0 || declared.includes(requested);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
function buildOmpRoutingSummary(channels = [], gateway = {}) {
|
|
232
|
-
const groups = [];
|
|
233
|
-
const unsupportedChannels = [];
|
|
234
|
-
collectRoutingGroups(channels).forEach((groupChannels) => {
|
|
235
|
-
try {
|
|
236
|
-
assertCompatibleGroup(groupChannels);
|
|
237
|
-
} catch {
|
|
238
|
-
groupChannels.forEach(channel => unsupportedChannels.push({
|
|
239
|
-
id: channel.id,
|
|
240
|
-
name: channel.name,
|
|
241
|
-
reason: 'incompatible-routing-group'
|
|
242
|
-
}));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const reason = getGroupUnsupportedReason(groupChannels, gateway);
|
|
246
|
-
if (reason) {
|
|
247
|
-
groupChannels.forEach(channel => unsupportedChannels.push({
|
|
248
|
-
id: channel.id,
|
|
249
|
-
name: channel.name,
|
|
250
|
-
reason
|
|
251
|
-
}));
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const modelChannels = new Map();
|
|
256
|
-
const unrestrictedChannelIds = [];
|
|
257
|
-
groupChannels.forEach((channel) => {
|
|
258
|
-
const modelIds = getDeclaredModelIds(channel);
|
|
259
|
-
if (modelIds.length === 0) {
|
|
260
|
-
unrestrictedChannelIds.push(channel.id);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
modelIds.forEach((modelId) => {
|
|
264
|
-
const key = normalizeOmpModelId(modelId).toLowerCase();
|
|
265
|
-
if (!key) return;
|
|
266
|
-
if (!modelChannels.has(key)) modelChannels.set(key, { id: modelId, channelIds: [] });
|
|
267
|
-
modelChannels.get(key).channelIds.push(channel.id);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
groups.push({
|
|
272
|
-
routingGroup: getRoutingGroup(groupChannels[0]),
|
|
273
|
-
providerKey: getProviderKey(groupChannels[0]),
|
|
274
|
-
providerApi: getProviderApi(groupChannels[0]),
|
|
275
|
-
channelIds: groupChannels.map(channel => channel.id),
|
|
276
|
-
channels: groupChannels.map(channel => ({ id: channel.id, name: channel.name || channel.id })),
|
|
277
|
-
unrestrictedChannelIds,
|
|
278
|
-
models: [...modelChannels.values()]
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
return { groups, unsupportedChannels };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
231
|
function buildManagedBaseUrl(host, port, routeToken) {
|
|
285
232
|
return `http://${host}:${port}/omp/${routeToken}`;
|
|
286
233
|
}
|
|
@@ -381,7 +328,6 @@ function resolveOmpGatewayRoute(pathname, channels, gateway) {
|
|
|
381
328
|
}
|
|
382
329
|
|
|
383
330
|
module.exports = {
|
|
384
|
-
buildOmpRoutingSummary,
|
|
385
331
|
channelSupportsOmpModel,
|
|
386
332
|
normalizeOmpModelId,
|
|
387
333
|
prepareManagedOmpChannels,
|
|
@@ -8,8 +8,7 @@ function createDriver(context = {}) {
|
|
|
8
8
|
platform: 'omp',
|
|
9
9
|
servicePath: './omp/proxy-implementation',
|
|
10
10
|
localServicePath: '../platforms/drivers/omp/proxy-implementation',
|
|
11
|
-
exports: { status: 'getOmpProxyStatus', start: 'startOmpProxyServer', stop: 'stopOmpProxyServer' }
|
|
12
|
-
cliMetadata: { managedProviderConfig: true, defaultPort: 20092 }
|
|
11
|
+
exports: { status: 'getOmpProxyStatus', start: 'startOmpProxyServer', stop: 'stopOmpProxyServer' }
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -2,39 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
const { createChannelDriver } = require('../../../shared/channel-driver');
|
|
4
4
|
|
|
5
|
+
function createChannelArgs(input, rest) {
|
|
6
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return [input, ...rest];
|
|
7
|
+
const { name, baseUrl, apiKey, extra, ...fields } = input;
|
|
8
|
+
const inputFields = { ...fields, ...(extra || {}) };
|
|
9
|
+
const channel = {
|
|
10
|
+
wireApi: 'openai',
|
|
11
|
+
gatewaySourceType: 'codex',
|
|
12
|
+
enabled: true,
|
|
13
|
+
weight: 1,
|
|
14
|
+
maxConcurrency: null,
|
|
15
|
+
model: null,
|
|
16
|
+
modelRedirects: [],
|
|
17
|
+
speedTestModel: null,
|
|
18
|
+
presetId: null,
|
|
19
|
+
providerKey: '',
|
|
20
|
+
websiteUrl: '',
|
|
21
|
+
allowedModels: [],
|
|
22
|
+
authMode: 'api_key',
|
|
23
|
+
oauthProviderId: '',
|
|
24
|
+
balanceToken: '',
|
|
25
|
+
balanceUserId: null,
|
|
26
|
+
...inputFields
|
|
27
|
+
};
|
|
28
|
+
channel.wireApi = channel.wireApi || 'openai';
|
|
29
|
+
channel.gatewaySourceType = channel.gatewaySourceType || 'codex';
|
|
30
|
+
channel.enabled = channel.enabled !== false;
|
|
31
|
+
channel.weight = channel.weight || 1;
|
|
32
|
+
channel.maxConcurrency = channel.maxConcurrency || null;
|
|
33
|
+
channel.model = channel.model || null;
|
|
34
|
+
channel.modelRedirects = channel.modelRedirects || [];
|
|
35
|
+
channel.speedTestModel = channel.speedTestModel || null;
|
|
36
|
+
channel.presetId = channel.presetId || null;
|
|
37
|
+
channel.providerKey = channel.providerKey || '';
|
|
38
|
+
channel.websiteUrl = channel.websiteUrl || '';
|
|
39
|
+
channel.allowedModels = channel.allowedModels || [];
|
|
40
|
+
channel.authMode = channel.authMode || 'api_key';
|
|
41
|
+
channel.oauthProviderId = channel.oauthProviderId || '';
|
|
42
|
+
channel.balanceToken = channel.balanceToken || '';
|
|
43
|
+
channel.balanceUserId = channel.balanceUserId || null;
|
|
44
|
+
return [name, baseUrl, apiKey, channel];
|
|
45
|
+
}
|
|
46
|
+
|
|
5
47
|
function createDriver(context = {}) {
|
|
48
|
+
const { resetChannelHealth } = require('../../../server/services/channel-health');
|
|
6
49
|
const driver = createChannelDriver({
|
|
7
50
|
...context,
|
|
8
51
|
platform: 'opencode',
|
|
9
52
|
servicePath: './opencode/channels-implementation',
|
|
10
53
|
localServicePath: '../opencode/channels-implementation',
|
|
11
54
|
syncMethod: 'syncCurrentOpenCodeChannel',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const { name, baseUrl, apiKey, extra, ...channelExtra } = input;
|
|
15
|
-
return [name, baseUrl, apiKey, { ...channelExtra, ...extra }];
|
|
16
|
-
})()
|
|
17
|
-
: [input, ...rest],
|
|
18
|
-
cliMetadata: {
|
|
19
|
-
supportsCliCreate: true,
|
|
20
|
-
supportsCliToggle: true,
|
|
21
|
-
defaultPort: 20091,
|
|
22
|
-
createQuestions: [
|
|
23
|
-
{
|
|
24
|
-
type: 'input',
|
|
25
|
-
name: 'wireApi',
|
|
26
|
-
message: 'Wire API (默认: openai):',
|
|
27
|
-
default: 'openai'
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
type: 'input',
|
|
31
|
-
name: 'model',
|
|
32
|
-
message: '默认模型(可选,直接回车跳过):'
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
},
|
|
55
|
+
modelListType: context.manifest?.channelModelType || 'openai_compatible',
|
|
56
|
+
createArgs: createChannelArgs,
|
|
36
57
|
dashboardChannelShape: 'array'
|
|
37
58
|
});
|
|
59
|
+
const saveOrder = driver.saveOrder;
|
|
60
|
+
driver.saveOrder = input => saveOrder(input?.order || input);
|
|
61
|
+
driver.resetHealth = input => {
|
|
62
|
+
const channelId = typeof input === 'string' ? input : input?.channelId || input?.body?.channelId;
|
|
63
|
+
if (!channelId) throw new Error('Channel id is required');
|
|
64
|
+
resetChannelHealth(channelId, 'opencode');
|
|
65
|
+
return { channelId, reset: true };
|
|
66
|
+
};
|
|
38
67
|
const auth = require('../../channel-auth-service');
|
|
39
68
|
driver.getAuth = context => auth.getChannelAuth('opencode', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
|
|
40
69
|
driver.syncLocalAuth = context => auth.syncLocalChannelAuth('opencode', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
|
|
@@ -8,8 +8,7 @@ function createDriver(context = {}) {
|
|
|
8
8
|
platform: 'opencode',
|
|
9
9
|
servicePath: './opencode/proxy-implementation',
|
|
10
10
|
localServicePath: '../opencode/proxy-implementation',
|
|
11
|
-
exports: { status: 'getOpenCodeProxyStatus', start: 'startOpenCodeProxyServer', stop: 'stopOpenCodeProxyServer' }
|
|
12
|
-
cliMetadata: { defaultPort: 20091 }
|
|
11
|
+
exports: { status: 'getOpenCodeProxyStatus', start: 'startOpenCodeProxyServer', stop: 'stopOpenCodeProxyServer' }
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
|