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.
Files changed (2) hide show
  1. package/bin/yymaxapi.js +131 -34
  2. 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: 'yunyi-claude',
347
+ providerKey: claudeProviderKey,
307
348
  modelId: inClaude.id,
308
349
  modelName: inClaude.name,
309
- modelKey: `yunyi-claude/${inClaude.id}`
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: 'yunyi-codex',
358
+ providerKey: codexProviderKey,
318
359
  modelId: inCodex.id,
319
360
  modelName: inCodex.name,
320
- modelKey: `yunyi-codex/${inCodex.id}`
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: 'yunyi-codex',
394
+ providerKey: codexProviderKey,
354
395
  modelId: model.id,
355
396
  modelName: model.name,
356
- modelKey: `yunyi-codex/${model.id}`
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: 'yunyi-claude',
404
+ providerKey: claudeProviderKey,
364
405
  modelId: model.id,
365
406
  modelName: model.name,
366
- modelKey: `yunyi-claude/${model.id}`
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 = 'yunyi-codex';
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
- // 兼容旧版:移除历史遗留的未标记 yunyi-codex provider、旧 openclaw-relay、旧 yunyi opencode 标记块
883
- existing = existing.replace(new RegExp(`\\[model_providers\\.${escapedProviderKey}\\]\\n(?:(?!\\[)[^\\n]*\\n?)*`, 'g'), '');
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 = "云翼 Codex"`,
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 = `yunyi-claude/${getDefaultClaudeModel().id}`) {
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['yunyi-claude'] = {
954
- name: '云翼 Claude',
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['yunyi-codex'] = {
963
- name: '云翼 Codex',
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
- existing.disabled_providers = existing.disabled_providers.filter(p => p !== 'yunyi-claude' && p !== 'yunyi-codex');
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 || `yunyi-claude/${extra.claudeModelId || getDefaultClaudeModel().id}`);
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 || `yunyi-claude/${getDefaultClaudeModel().id}`,
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] || 'yunyi-codex';
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: `yunyi-claude/${modelId}` });
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 = CLAUDE_MODELS.length === 1 && CODEX_MODELS.length === 1
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 ? `yunyi-claude/${claudeModelId}` : `yunyi-codex/${codexModelId}`;
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: `yunyi-codex/${codexModelId}` }); } catch { /* ignore */ }
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
- if (CLAUDE_MODELS.length > 0) {
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.100",
3
+ "version": "1.0.101",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {