yymaxapi 1.0.100 → 1.0.101
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/bin/yymaxapi.js +131 -34
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -254,6 +254,34 @@ function getDefaultCodexModel() {
|
|
|
254
254
|
return CODEX_MODELS[0] || { id: 'gpt-5.4', name: 'GPT 5.4' };
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
function getExternalClaudeProviderKey() {
|
|
258
|
+
return String(API_CONFIG?.claude?.providerName || 'yunyi-claude').trim() || 'yunyi-claude';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getExternalCodexProviderKey() {
|
|
262
|
+
const providerKey = String(API_CONFIG?.codex?.providerName || '').trim();
|
|
263
|
+
if (!providerKey) return 'yunyi-codex';
|
|
264
|
+
return providerKey === 'yunyi' ? 'yunyi-codex' : providerKey;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getExternalModelKey(type, modelId) {
|
|
268
|
+
const providerKey = type === 'codex' ? getExternalCodexProviderKey() : getExternalClaudeProviderKey();
|
|
269
|
+
return `${providerKey}/${modelId}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getProviderBrandPrefix() {
|
|
273
|
+
const providerKeys = [getExternalClaudeProviderKey(), getExternalCodexProviderKey()];
|
|
274
|
+
if (providerKeys.some(key => key.startsWith('maxapi'))) return 'MAXAPI';
|
|
275
|
+
if (providerKeys.some(key => key.startsWith('yunyi'))) return '云翼';
|
|
276
|
+
return 'OpenClaw';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isUnifiedSingleModelMode() {
|
|
280
|
+
return CLAUDE_MODELS.length === 1
|
|
281
|
+
&& CODEX_MODELS.length === 1
|
|
282
|
+
&& CLAUDE_MODELS[0].id === CODEX_MODELS[0].id;
|
|
283
|
+
}
|
|
284
|
+
|
|
257
285
|
function buildProviderModelMap(models) {
|
|
258
286
|
const mapped = {};
|
|
259
287
|
for (const model of models || []) {
|
|
@@ -268,7 +296,7 @@ function getClaudeSwitchHint() {
|
|
|
268
296
|
}
|
|
269
297
|
|
|
270
298
|
function getOpencodeSwitchHint() {
|
|
271
|
-
return [...CLAUDE_MODELS, ...CODEX_MODELS].map(model => model.name).join(' / ');
|
|
299
|
+
return [...new Set([...CLAUDE_MODELS, ...CODEX_MODELS].map(model => model.name))].join(' / ');
|
|
272
300
|
}
|
|
273
301
|
|
|
274
302
|
async function promptClaudeModelSelection(args = {}, message = '选择 Claude 模型:') {
|
|
@@ -297,16 +325,29 @@ async function promptOpencodeDefaultModelSelection(args = {}, message = '选择
|
|
|
297
325
|
const fallbackClaude = getDefaultClaudeModel();
|
|
298
326
|
const fallbackCodex = getDefaultCodexModel();
|
|
299
327
|
const requested = (args['default-model'] || args.model || args['claude-model'] || args['codex-model'] || '').toString().trim();
|
|
328
|
+
const claudeProviderKey = getExternalClaudeProviderKey();
|
|
329
|
+
const codexProviderKey = getExternalCodexProviderKey();
|
|
330
|
+
|
|
331
|
+
if (isUnifiedSingleModelMode()) {
|
|
332
|
+
const unifiedModel = CLAUDE_MODELS[0] || CODEX_MODELS[0] || fallbackClaude;
|
|
333
|
+
return {
|
|
334
|
+
type: 'claude',
|
|
335
|
+
providerKey: claudeProviderKey,
|
|
336
|
+
modelId: unifiedModel.id,
|
|
337
|
+
modelName: unifiedModel.name,
|
|
338
|
+
modelKey: getExternalModelKey('claude', unifiedModel.id)
|
|
339
|
+
};
|
|
340
|
+
}
|
|
300
341
|
|
|
301
342
|
if (requested) {
|
|
302
343
|
const inClaude = CLAUDE_MODELS.find(model => model.id === requested);
|
|
303
344
|
if (inClaude) {
|
|
304
345
|
return {
|
|
305
346
|
type: 'claude',
|
|
306
|
-
providerKey:
|
|
347
|
+
providerKey: claudeProviderKey,
|
|
307
348
|
modelId: inClaude.id,
|
|
308
349
|
modelName: inClaude.name,
|
|
309
|
-
modelKey:
|
|
350
|
+
modelKey: getExternalModelKey('claude', inClaude.id)
|
|
310
351
|
};
|
|
311
352
|
}
|
|
312
353
|
|
|
@@ -314,10 +355,10 @@ async function promptOpencodeDefaultModelSelection(args = {}, message = '选择
|
|
|
314
355
|
if (inCodex) {
|
|
315
356
|
return {
|
|
316
357
|
type: 'codex',
|
|
317
|
-
providerKey:
|
|
358
|
+
providerKey: codexProviderKey,
|
|
318
359
|
modelId: inCodex.id,
|
|
319
360
|
modelName: inCodex.name,
|
|
320
|
-
modelKey:
|
|
361
|
+
modelKey: getExternalModelKey('codex', inCodex.id)
|
|
321
362
|
};
|
|
322
363
|
}
|
|
323
364
|
}
|
|
@@ -350,20 +391,20 @@ async function promptOpencodeDefaultModelSelection(args = {}, message = '选择
|
|
|
350
391
|
const model = CODEX_MODELS.find(item => item.id === pickedId) || fallbackCodex;
|
|
351
392
|
return {
|
|
352
393
|
type: 'codex',
|
|
353
|
-
providerKey:
|
|
394
|
+
providerKey: codexProviderKey,
|
|
354
395
|
modelId: model.id,
|
|
355
396
|
modelName: model.name,
|
|
356
|
-
modelKey:
|
|
397
|
+
modelKey: getExternalModelKey('codex', model.id)
|
|
357
398
|
};
|
|
358
399
|
}
|
|
359
400
|
|
|
360
401
|
const model = CLAUDE_MODELS.find(item => item.id === pickedId) || fallbackClaude;
|
|
361
402
|
return {
|
|
362
403
|
type: 'claude',
|
|
363
|
-
providerKey:
|
|
404
|
+
providerKey: claudeProviderKey,
|
|
364
405
|
modelId: model.id,
|
|
365
406
|
modelName: model.name,
|
|
366
|
-
modelKey:
|
|
407
|
+
modelKey: getExternalModelKey('claude', model.id)
|
|
367
408
|
};
|
|
368
409
|
}
|
|
369
410
|
|
|
@@ -864,13 +905,14 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
|
|
|
864
905
|
const markerEnd = '# <<< maxapi codex <<<';
|
|
865
906
|
const topMarker = '# >>> maxapi codex top >>>';
|
|
866
907
|
const topMarkerEnd = '# <<< maxapi codex top <<<';
|
|
867
|
-
const providerKey =
|
|
908
|
+
const providerKey = getExternalCodexProviderKey();
|
|
909
|
+
const providerLabel = `${getProviderBrandPrefix()} Codex`;
|
|
910
|
+
const removableProviderKeys = [...new Set([providerKey, 'yunyi-codex', 'maxapi-codex'])];
|
|
868
911
|
// 确保 base_url 以 /v1 结尾(Codex CLI 要求)
|
|
869
912
|
let normalizedUrl = baseUrl.replace(/\/+$/, '');
|
|
870
913
|
if (!normalizedUrl.endsWith('/v1')) normalizedUrl += '/v1';
|
|
871
914
|
try {
|
|
872
915
|
let existing = '';
|
|
873
|
-
const escapedProviderKey = providerKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
874
916
|
if (fs.existsSync(configPath)) {
|
|
875
917
|
existing = fs.readFileSync(configPath, 'utf8');
|
|
876
918
|
// 移除旧的 maxapi section(provider block)
|
|
@@ -879,8 +921,11 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
|
|
|
879
921
|
// 移除旧的 maxapi top-level block
|
|
880
922
|
const topRe = new RegExp(`${topMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${topMarkerEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`, 'g');
|
|
881
923
|
existing = existing.replace(topRe, '');
|
|
882
|
-
//
|
|
883
|
-
|
|
924
|
+
// 兼容旧版:移除历史遗留的 provider block、旧 openclaw-relay、旧 yunyi opencode 标记块
|
|
925
|
+
for (const removableProviderKey of removableProviderKeys) {
|
|
926
|
+
const escapedProviderKey = removableProviderKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
927
|
+
existing = existing.replace(new RegExp(`\\[model_providers\\.${escapedProviderKey}\\]\\n(?:(?!\\[)[^\\n]*\\n?)*`, 'g'), '');
|
|
928
|
+
}
|
|
884
929
|
existing = existing.replace(/\[model_providers\.openclaw-relay\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
|
|
885
930
|
existing = existing.replace(/# >>> yunyi opencode >>>[\s\S]*?# <<< yunyi opencode <<<\n?/g, '');
|
|
886
931
|
existing = existing.replace(/\n{3,}/g, '\n\n').trim();
|
|
@@ -900,7 +945,7 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
|
|
|
900
945
|
const providerBlock = [
|
|
901
946
|
marker,
|
|
902
947
|
`[model_providers.${providerKey}]`,
|
|
903
|
-
`name = "
|
|
948
|
+
`name = "${providerLabel}"`,
|
|
904
949
|
`base_url = "${normalizedUrl}"`,
|
|
905
950
|
`wire_api = "responses"`,
|
|
906
951
|
`experimental_bearer_token = "${apiKey}"`,
|
|
@@ -931,10 +976,13 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
|
|
|
931
976
|
} catch { /* 非关键,静默失败 */ }
|
|
932
977
|
}
|
|
933
978
|
|
|
934
|
-
function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKey =
|
|
979
|
+
function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKey = getExternalModelKey('claude', getDefaultClaudeModel().id)) {
|
|
935
980
|
const home = os.homedir();
|
|
936
981
|
const claudeUrl = claudeBaseUrl.replace(/\/+$/, '');
|
|
937
982
|
const codexUrl = (codexBaseUrl || '').replace(/\/+$/, '');
|
|
983
|
+
const claudeProviderKey = getExternalClaudeProviderKey();
|
|
984
|
+
const codexProviderKey = getExternalCodexProviderKey();
|
|
985
|
+
const brandPrefix = getProviderBrandPrefix();
|
|
938
986
|
|
|
939
987
|
// ---- 1. opencode.json (CLI + 桌面版) ----
|
|
940
988
|
const configDir = process.platform === 'win32'
|
|
@@ -950,8 +998,8 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKe
|
|
|
950
998
|
if (!existing.provider) existing.provider = {};
|
|
951
999
|
|
|
952
1000
|
// Claude provider (@ai-sdk/anthropic)
|
|
953
|
-
existing.provider[
|
|
954
|
-
name:
|
|
1001
|
+
existing.provider[claudeProviderKey] = {
|
|
1002
|
+
name: `${brandPrefix} Claude`,
|
|
955
1003
|
npm: '@ai-sdk/anthropic',
|
|
956
1004
|
models: buildProviderModelMap(CLAUDE_MODELS),
|
|
957
1005
|
options: { apiKey, baseURL: `${claudeUrl}/v1` }
|
|
@@ -959,8 +1007,8 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKe
|
|
|
959
1007
|
|
|
960
1008
|
// Codex provider (@ai-sdk/openai)
|
|
961
1009
|
if (codexUrl) {
|
|
962
|
-
existing.provider[
|
|
963
|
-
name:
|
|
1010
|
+
existing.provider[codexProviderKey] = {
|
|
1011
|
+
name: `${brandPrefix} Codex`,
|
|
964
1012
|
npm: '@ai-sdk/openai',
|
|
965
1013
|
models: buildProviderModelMap(CODEX_MODELS),
|
|
966
1014
|
options: { apiKey, baseURL: codexUrl }
|
|
@@ -978,7 +1026,8 @@ function writeOpencodeConfig(claudeBaseUrl, codexBaseUrl, apiKey, defaultModelKe
|
|
|
978
1026
|
|
|
979
1027
|
// 从 disabled_providers 中移除我们的 provider
|
|
980
1028
|
if (Array.isArray(existing.disabled_providers)) {
|
|
981
|
-
|
|
1029
|
+
const enabledProviders = new Set([claudeProviderKey, codexProviderKey, 'yunyi-claude', 'yunyi-codex', 'maxapi', 'maxapi-codex']);
|
|
1030
|
+
existing.disabled_providers = existing.disabled_providers.filter(p => !enabledProviders.has(p));
|
|
982
1031
|
if (existing.disabled_providers.length === 0) delete existing.disabled_providers;
|
|
983
1032
|
}
|
|
984
1033
|
|
|
@@ -1036,7 +1085,7 @@ function syncExternalTools(type, baseUrl, apiKey, extra = {}) {
|
|
|
1036
1085
|
synced.push('Codex CLI config');
|
|
1037
1086
|
}
|
|
1038
1087
|
if (type === 'claude' && extra.codexBaseUrl) {
|
|
1039
|
-
writeOpencodeConfig(baseUrl, extra.codexBaseUrl, apiKey, extra.opencodeDefaultModelKey ||
|
|
1088
|
+
writeOpencodeConfig(baseUrl, extra.codexBaseUrl, apiKey, extra.opencodeDefaultModelKey || getExternalModelKey('claude', extra.claudeModelId || getDefaultClaudeModel().id));
|
|
1040
1089
|
synced.push('Opencode config');
|
|
1041
1090
|
}
|
|
1042
1091
|
} catch { /* ignore */ }
|
|
@@ -3399,7 +3448,7 @@ function readOpencodeCliConfig() {
|
|
|
3399
3448
|
const config = readJsonIfExists(configPath) || {};
|
|
3400
3449
|
return {
|
|
3401
3450
|
configPath,
|
|
3402
|
-
modelKey: config.model ||
|
|
3451
|
+
modelKey: config.model || getExternalModelKey('claude', getDefaultClaudeModel().id),
|
|
3403
3452
|
configured: fs.existsSync(configPath),
|
|
3404
3453
|
config
|
|
3405
3454
|
};
|
|
@@ -3410,7 +3459,7 @@ function readCodexCliConfig() {
|
|
|
3410
3459
|
const configRaw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
|
|
3411
3460
|
const auth = readJsonIfExists(authPath) || {};
|
|
3412
3461
|
const model = (configRaw.match(/^model\s*=\s*"([^"]+)"\s*$/m) || [])[1] || getDefaultCodexModel().id;
|
|
3413
|
-
const provider = (configRaw.match(/^model_provider\s*=\s*"([^"]+)"\s*$/m) || [])[1] ||
|
|
3462
|
+
const provider = (configRaw.match(/^model_provider\s*=\s*"([^"]+)"\s*$/m) || [])[1] || getExternalCodexProviderKey();
|
|
3414
3463
|
const providerBlockRegex = new RegExp(`\\[model_providers\\.${escapeRegExp(provider)}\\]([\\s\\S]*?)(?=\\n\\[|$)`, 'm');
|
|
3415
3464
|
const providerBlock = (configRaw.match(providerBlockRegex) || [])[1] || '';
|
|
3416
3465
|
const baseUrl = (providerBlock.match(/base_url\s*=\s*"([^"]+)"/) || [])[1] || '';
|
|
@@ -3920,6 +3969,22 @@ async function tryAutoStartGateway(port, allowAutoDaemon) {
|
|
|
3920
3969
|
}
|
|
3921
3970
|
}
|
|
3922
3971
|
|
|
3972
|
+
if (process.platform !== 'win32') {
|
|
3973
|
+
for (const sh of ['/bin/zsh', '/bin/bash']) {
|
|
3974
|
+
if (!fs.existsSync(sh)) continue;
|
|
3975
|
+
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
3976
|
+
const loginShellCmd = `${sh} -lc '${name} gateway'`;
|
|
3977
|
+
console.log(chalk.yellow(`⚠️ 尝试启动 Gateway: ${loginShellCmd}`));
|
|
3978
|
+
if (spawnDetached(loginShellCmd, env)) {
|
|
3979
|
+
if (await waitForGateway(port, '127.0.0.1', 15000)) {
|
|
3980
|
+
console.log(chalk.green('✅ Gateway 已通过 login shell 启动'));
|
|
3981
|
+
return { started: true, method: 'cli-login-shell', cmd: loginShellCmd };
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3923
3988
|
return { started: false };
|
|
3924
3989
|
}
|
|
3925
3990
|
|
|
@@ -4369,7 +4434,7 @@ async function presetClaude(paths, args = {}) {
|
|
|
4369
4434
|
if (yunyiLayoutResult.applied) {
|
|
4370
4435
|
syncManagedYunyiAuthProfiles(paths, config);
|
|
4371
4436
|
}
|
|
4372
|
-
syncExternalTools('claude', baseUrl, apiKey, { claudeModelId: modelId, opencodeDefaultModelKey:
|
|
4437
|
+
syncExternalTools('claude', baseUrl, apiKey, { claudeModelId: modelId, opencodeDefaultModelKey: getExternalModelKey('claude', modelId) });
|
|
4373
4438
|
writeSpinner.succeed('配置写入完成');
|
|
4374
4439
|
|
|
4375
4440
|
console.log(chalk.green('\n✅ Claude 节点配置完成!'));
|
|
@@ -4621,15 +4686,14 @@ async function autoActivate(paths, args = {}) {
|
|
|
4621
4686
|
}
|
|
4622
4687
|
|
|
4623
4688
|
// ---- 选模型(Claude + GPT 合并展示) ----
|
|
4624
|
-
const isSingleMode =
|
|
4625
|
-
&& CLAUDE_MODELS[0].id === CODEX_MODELS[0].id;
|
|
4689
|
+
const isSingleMode = isUnifiedSingleModelMode();
|
|
4626
4690
|
|
|
4627
4691
|
let selectedModelId;
|
|
4628
4692
|
let selectedType; // 'claude' or 'codex'
|
|
4629
4693
|
|
|
4630
4694
|
if (isSingleMode) {
|
|
4631
4695
|
selectedModelId = CLAUDE_MODELS[0].id;
|
|
4632
|
-
selectedType = 'claude';
|
|
4696
|
+
selectedType = String(args.primary || '').trim() === 'codex' ? 'codex' : 'claude';
|
|
4633
4697
|
} else {
|
|
4634
4698
|
const modelArg = (args.model || args['claude-model'] || args['codex-model'] || '').toString().trim();
|
|
4635
4699
|
if (modelArg) {
|
|
@@ -4725,9 +4789,9 @@ async function autoActivate(paths, args = {}) {
|
|
|
4725
4789
|
|
|
4726
4790
|
// 主模型 = 用户选的,备选 = 另一个
|
|
4727
4791
|
const primaryModelKey = isClaudePrimary ? claudeModelKey : codexModelKey;
|
|
4728
|
-
const fallbackModelKey = isClaudePrimary ? codexModelKey : claudeModelKey;
|
|
4792
|
+
const fallbackModelKey = isSingleMode ? '' : (isClaudePrimary ? codexModelKey : claudeModelKey);
|
|
4729
4793
|
config.agents.defaults.model.primary = primaryModelKey;
|
|
4730
|
-
config.agents.defaults.model.fallbacks = [fallbackModelKey];
|
|
4794
|
+
config.agents.defaults.model.fallbacks = fallbackModelKey ? [fallbackModelKey] : [];
|
|
4731
4795
|
const yunyiLayoutResult = applyManagedYunyiOpenClawLayout(config, {
|
|
4732
4796
|
force: true,
|
|
4733
4797
|
endpointUrl: selectedEndpoint.url,
|
|
@@ -4755,7 +4819,7 @@ async function autoActivate(paths, args = {}) {
|
|
|
4755
4819
|
console.log(chalk.gray(` 已重置 ${selectionResult.agentId} 的活动会话映射`));
|
|
4756
4820
|
}
|
|
4757
4821
|
}
|
|
4758
|
-
const opencodeDefaultModelKey = isClaudePrimary ?
|
|
4822
|
+
const opencodeDefaultModelKey = isClaudePrimary ? getExternalModelKey('claude', claudeModelId) : getExternalModelKey('codex', codexModelId);
|
|
4759
4823
|
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey }); } catch { /* ignore */ }
|
|
4760
4824
|
try { syncExternalTools('codex', codexBaseUrl, apiKey, { modelId: codexModelId }); } catch { /* ignore */ }
|
|
4761
4825
|
writeSpinner.succeed('配置写入完成');
|
|
@@ -5205,7 +5269,7 @@ async function yycodeQuickSetup(paths) {
|
|
|
5205
5269
|
if (yunyiLayoutResult.applied) {
|
|
5206
5270
|
syncManagedYunyiAuthProfiles(paths, config);
|
|
5207
5271
|
}
|
|
5208
|
-
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey:
|
|
5272
|
+
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey: getExternalModelKey('codex', codexModelId) }); } catch { /* ignore */ }
|
|
5209
5273
|
try { syncExternalTools('codex', codexBaseUrl, apiKey, { modelId: codexModelId }); } catch { /* ignore */ }
|
|
5210
5274
|
writeSpinner.succeed('配置写入完成');
|
|
5211
5275
|
|
|
@@ -5359,6 +5423,8 @@ function getConfigStatusLine(paths) {
|
|
|
5359
5423
|
'claude-yunyi': 'Claude(包月)',
|
|
5360
5424
|
'yunyi': 'Codex(包月)',
|
|
5361
5425
|
'heibai': 'MAXAPI(按量)',
|
|
5426
|
+
'maxapi': 'MAXAPI(按量)',
|
|
5427
|
+
'maxapi-codex': 'MAXAPI(按量)',
|
|
5362
5428
|
};
|
|
5363
5429
|
|
|
5364
5430
|
const parts = [];
|
|
@@ -5569,6 +5635,8 @@ async function switchModel(paths) {
|
|
|
5569
5635
|
'claude-yunyi': '云翼 Claude (包月)',
|
|
5570
5636
|
'yunyi': '云翼 Codex (包月)',
|
|
5571
5637
|
'heibai': 'MAXAPI (按量)',
|
|
5638
|
+
'maxapi': 'MAXAPI (按量)',
|
|
5639
|
+
'maxapi-codex': 'MAXAPI (按量)',
|
|
5572
5640
|
};
|
|
5573
5641
|
|
|
5574
5642
|
const claudeProviderName = API_CONFIG.claude.providerName;
|
|
@@ -5577,8 +5645,25 @@ async function switchModel(paths) {
|
|
|
5577
5645
|
// 预设模型列表
|
|
5578
5646
|
const choices = [];
|
|
5579
5647
|
const presetKeys = new Set();
|
|
5580
|
-
|
|
5581
|
-
|
|
5648
|
+
const unifiedSingleMode = isUnifiedSingleModelMode();
|
|
5649
|
+
const unifiedModel = unifiedSingleMode ? (CLAUDE_MODELS[0] || CODEX_MODELS[0] || null) : null;
|
|
5650
|
+
|
|
5651
|
+
if (unifiedSingleMode && unifiedModel) {
|
|
5652
|
+
const preferredProviders = [
|
|
5653
|
+
primary.split('/')[0],
|
|
5654
|
+
providers[claudeProviderName] ? claudeProviderName : '',
|
|
5655
|
+
providers[codexProviderName] ? codexProviderName : '',
|
|
5656
|
+
Object.keys(providers)[0] || ''
|
|
5657
|
+
].filter(Boolean);
|
|
5658
|
+
const unifiedProviderName = preferredProviders[0];
|
|
5659
|
+
const modelKey = `${unifiedProviderName}/${unifiedModel.id}`;
|
|
5660
|
+
const isCurrent = modelKey === primary;
|
|
5661
|
+
choices.push({
|
|
5662
|
+
name: isCurrent ? `${unifiedModel.name} (当前)` : unifiedModel.name,
|
|
5663
|
+
value: modelKey,
|
|
5664
|
+
});
|
|
5665
|
+
presetKeys.add(modelKey);
|
|
5666
|
+
} else if (CLAUDE_MODELS.length > 0) {
|
|
5582
5667
|
choices.push(new inquirer.Separator(' -- Claude --'));
|
|
5583
5668
|
for (const m of CLAUDE_MODELS) {
|
|
5584
5669
|
const pName = providers[claudeProviderName] ? claudeProviderName : Object.keys(providers)[0];
|
|
@@ -5592,7 +5677,7 @@ async function switchModel(paths) {
|
|
|
5592
5677
|
}
|
|
5593
5678
|
}
|
|
5594
5679
|
|
|
5595
|
-
if (CODEX_MODELS.length > 0) {
|
|
5680
|
+
if (!unifiedSingleMode && CODEX_MODELS.length > 0) {
|
|
5596
5681
|
choices.push(new inquirer.Separator(' -- GPT --'));
|
|
5597
5682
|
for (const m of CODEX_MODELS) {
|
|
5598
5683
|
const pName = providers[codexProviderName] ? codexProviderName : Object.keys(providers)[0];
|
|
@@ -5610,6 +5695,12 @@ async function switchModel(paths) {
|
|
|
5610
5695
|
const otherModels = [];
|
|
5611
5696
|
for (const [providerName, providerConfig] of Object.entries(providers)) {
|
|
5612
5697
|
for (const m of (providerConfig.models || [])) {
|
|
5698
|
+
if (unifiedSingleMode
|
|
5699
|
+
&& unifiedModel
|
|
5700
|
+
&& m.id === unifiedModel.id
|
|
5701
|
+
&& (providerName === claudeProviderName || providerName === codexProviderName)) {
|
|
5702
|
+
continue;
|
|
5703
|
+
}
|
|
5613
5704
|
const modelKey = `${providerName}/${m.id}`;
|
|
5614
5705
|
if (!presetKeys.has(modelKey)) {
|
|
5615
5706
|
otherModels.push({ modelKey, name: m.name || m.id, providerName });
|
|
@@ -5618,6 +5709,12 @@ async function switchModel(paths) {
|
|
|
5618
5709
|
}
|
|
5619
5710
|
const registeredKeys = Object.keys(config.agents?.defaults?.models || {});
|
|
5620
5711
|
for (const modelKey of registeredKeys) {
|
|
5712
|
+
if (unifiedSingleMode && unifiedModel) {
|
|
5713
|
+
const [providerName, modelId] = modelKey.split('/');
|
|
5714
|
+
if (modelId === unifiedModel.id && (providerName === claudeProviderName || providerName === codexProviderName)) {
|
|
5715
|
+
continue;
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5621
5718
|
if (presetKeys.has(modelKey) || otherModels.some(o => o.modelKey === modelKey)) continue;
|
|
5622
5719
|
const [pName, mId] = modelKey.split('/');
|
|
5623
5720
|
if (!pName || !mId) continue;
|