yymaxapi 1.0.45 → 1.0.47

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 +108 -68
  2. package/package.json +1 -1
package/bin/yymaxapi.js CHANGED
@@ -2660,22 +2660,7 @@ async function autoActivate(paths, args = {}) {
2660
2660
  // ---- 构建配置 ----
2661
2661
  const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
2662
2662
 
2663
- const existingProviders = Object.keys(config.models.providers || {});
2664
- const toRemove = existingProviders.filter(n => n !== claudeProviderName && n !== codexProviderName);
2665
- if (toRemove.length > 0 && !args.force) {
2666
- const { overwrite } = await inquirer.prompt([{
2667
- type: 'confirm',
2668
- name: 'overwrite',
2669
- message: `检测到已有中转配置: ${existingProviders.join(', ')},将替换为 ${claudeProviderName} + ${codexProviderName}。是否继续?`,
2670
- default: false
2671
- }]);
2672
- if (!overwrite) { console.log(chalk.gray('已取消')); return; }
2673
- }
2674
-
2675
- if (toRemove.length > 0) {
2676
- pruneProvidersExcept(config, [claudeProviderName, codexProviderName]);
2677
- pruneAuthProfilesExcept(paths.authProfiles, [claudeProviderName, codexProviderName]);
2678
- }
2663
+ // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
2679
2664
 
2680
2665
  // Claude 侧
2681
2666
  const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
@@ -3042,13 +3027,7 @@ async function yycodeQuickSetup(paths) {
3042
3027
  // ---- 写入两套配置 ----
3043
3028
  const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
3044
3029
 
3045
- // 清理旧 provider
3046
- const existingProviders = Object.keys(config.models.providers || {});
3047
- const toRemove = existingProviders.filter(n => n !== claudeProviderName && n !== codexProviderName);
3048
- if (toRemove.length > 0) {
3049
- pruneProvidersExcept(config, [claudeProviderName, codexProviderName]);
3050
- pruneAuthProfilesExcept(paths.authProfiles, [claudeProviderName, codexProviderName]);
3051
- }
3030
+ // 增量写入: 只添加/更新云翼 provider,保留其他已有配置
3052
3031
 
3053
3032
  // Claude 侧
3054
3033
  const claudeBaseUrl = buildFullUrl(selectedEndpoint.url, 'claude');
@@ -3238,30 +3217,31 @@ function getConfigStatusLine(paths) {
3238
3217
 
3239
3218
  const providers = Object.keys(config.models.providers);
3240
3219
  const primary = config?.agents?.defaults?.model?.primary || '';
3220
+ const primaryProvider = primary.split('/')[0] || '';
3241
3221
 
3242
- const parts = [];
3243
-
3244
- // 检查 Claude
3245
- const hasClaude = providers.some(p => p.includes('claude') || p.includes('yunyi-claude'));
3246
- if (hasClaude) {
3247
- const isActive = primary.includes('claude');
3248
- parts.push(isActive ? chalk.green('Claude ✓') : chalk.yellow('Claude ○'));
3249
- }
3222
+ const PROVIDER_LABELS = {
3223
+ 'claude-yunyi': 'Claude(包月)',
3224
+ 'yunyi': 'Codex(包月)',
3225
+ 'heibai': 'MAXAPI(按量)',
3226
+ };
3250
3227
 
3251
- // 检查 Codex/GPT
3252
- const hasCodex = providers.some(p => p.includes('codex') || p.includes('gpt') || p.includes('yunyi-codex'));
3253
- if (hasCodex) {
3254
- const isActive = primary.includes('codex') || primary.includes('gpt');
3255
- parts.push(isActive ? chalk.green('Codex ✓') : chalk.yellow('Codex ○'));
3228
+ const parts = [];
3229
+ for (const p of providers) {
3230
+ const label = PROVIDER_LABELS[p] || p;
3231
+ const isActive = p === primaryProvider;
3232
+ parts.push(isActive ? chalk.green(`${label} ✓`) : chalk.yellow(`${label} ○`));
3256
3233
  }
3257
3234
 
3258
3235
  if (parts.length === 0) {
3259
3236
  return chalk.gray('当前状态: 未配置任何模型');
3260
3237
  }
3261
3238
 
3239
+ const primaryModelId = primary.split('/')[1] || '';
3240
+ const modelTag = primaryModelId ? chalk.cyan(` [模型: ${primaryModelId}]`) : '';
3241
+
3262
3242
  const gwEnv = detectGatewayEnv();
3263
3243
  const envTag = gwEnv === 'wsl' ? chalk.cyan(' [WSL]') : '';
3264
- return chalk.gray('当前状态: ') + parts.join(' ') + chalk.gray(' (✓ 主模型 ○ 已配置)') + envTag;
3244
+ return chalk.gray('当前状态: ') + parts.join(' ') + chalk.gray(' (✓ 主模型 ○ 已配置)') + modelTag + envTag;
3265
3245
  } catch {
3266
3246
  return null;
3267
3247
  }
@@ -3404,59 +3384,103 @@ async function activate(paths, type) {
3404
3384
 
3405
3385
  // ============ 切换模型 ============
3406
3386
  async function switchModel(paths) {
3407
- console.log(chalk.cyan('🔄 切换 Claude 模型\n'));
3387
+ console.log(chalk.cyan('🔄 切换模型\n'));
3408
3388
 
3409
3389
  const config = ensureConfigStructure(readConfig(paths.openclawConfig) || {});
3410
3390
  const primary = config.agents?.defaults?.model?.primary || '';
3391
+ const providers = config.models?.providers || {};
3411
3392
 
3412
- if (!primary.includes('/')) {
3413
- console.log(chalk.yellow('⚠️ 请先通过「一键激活」设置主模型'));
3393
+ if (Object.keys(providers).length === 0) {
3394
+ console.log(chalk.yellow('⚠️ 未找到任何中转配置,请先通过「一键配置」设置'));
3414
3395
  return;
3415
3396
  }
3416
3397
 
3417
- const providerName = primary.split('/')[0];
3418
- const currentModelId = primary.split('/')[1] || '';
3419
- const provider = config.models?.providers?.[providerName];
3398
+ // 已知 provider 别名映射
3399
+ const PROVIDER_LABELS = {
3400
+ 'claude-yunyi': '云翼 Claude (包月)',
3401
+ 'yunyi': '云翼 Codex (包月)',
3402
+ 'heibai': 'MAXAPI (按量)',
3403
+ };
3420
3404
 
3421
- if (!provider) {
3422
- console.log(chalk.yellow(`⚠️ 主模型对应的中转站不存在: ${providerName}`));
3423
- return;
3405
+ // 收集所有 provider 的所有模型
3406
+ const choices = [];
3407
+ for (const [providerName, providerConfig] of Object.entries(providers)) {
3408
+ const label = PROVIDER_LABELS[providerName] || providerName;
3409
+ const models = providerConfig.models || [];
3410
+ if (models.length === 0) continue;
3411
+
3412
+ choices.push(new inquirer.Separator(` -- ${label} --`));
3413
+ for (const m of models) {
3414
+ const modelKey = `${providerName}/${m.id}`;
3415
+ const isCurrent = modelKey === primary;
3416
+ choices.push({
3417
+ name: isCurrent ? `${m.name || m.id} (当前)` : (m.name || m.id),
3418
+ value: modelKey,
3419
+ });
3420
+ }
3424
3421
  }
3425
3422
 
3426
- const models = [
3427
- { id: 'claude-opus-4-6', name: 'Claude Opus 4.6' },
3428
- { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6' },
3429
- ];
3423
+ // 也把 agents.defaults.models 里注册过但 provider.models 里没列出的补上
3424
+ const registeredKeys = Object.keys(config.agents?.defaults?.models || {});
3425
+ const listedKeys = new Set(choices.filter(c => c.value).map(c => c.value));
3426
+ for (const modelKey of registeredKeys) {
3427
+ if (listedKeys.has(modelKey)) continue;
3428
+ const [pName, mId] = modelKey.split('/');
3429
+ if (!pName || !mId) continue;
3430
+ const label = PROVIDER_LABELS[pName] || pName;
3431
+ const isCurrent = modelKey === primary;
3432
+ choices.push({
3433
+ name: isCurrent ? `${label} / ${mId} (当前)` : `${label} / ${mId}`,
3434
+ value: modelKey,
3435
+ });
3436
+ }
3437
+
3438
+ if (choices.filter(c => c.value).length === 0) {
3439
+ console.log(chalk.yellow('⚠️ 未找到任何可用模型'));
3440
+ return;
3441
+ }
3430
3442
 
3431
- console.log(chalk.gray(`当前模型: ${currentModelId}`));
3432
- console.log(chalk.gray(`中转节点: ${provider.baseUrl}\n`));
3443
+ const currentProvider = primary.split('/')[0] || '';
3444
+ const currentProviderConfig = providers[currentProvider];
3445
+ console.log(chalk.gray(`当前模型: ${primary || '未设置'}`));
3446
+ if (currentProviderConfig) {
3447
+ console.log(chalk.gray(`中转节点: ${currentProviderConfig.baseUrl}`));
3448
+ }
3449
+ console.log('');
3433
3450
 
3434
3451
  const { selected } = await inquirer.prompt([{
3435
3452
  type: 'list',
3436
3453
  name: 'selected',
3437
3454
  message: '选择模型:',
3438
- default: currentModelId,
3439
- choices: models.map(m => ({
3440
- name: m.id === currentModelId ? `${m.name} (当前)` : m.name,
3441
- value: m.id,
3442
- })),
3455
+ default: primary,
3456
+ pageSize: 15,
3457
+ choices,
3443
3458
  }]);
3444
3459
 
3445
- if (selected === currentModelId) {
3460
+ if (selected === primary) {
3446
3461
  console.log(chalk.gray('\n模型未变更'));
3447
3462
  return;
3448
3463
  }
3449
3464
 
3450
- const newPrimary = `${providerName}/${selected}`;
3451
- config.agents.defaults.model.primary = newPrimary;
3452
- if (!config.agents.defaults.models[newPrimary]) {
3453
- config.agents.defaults.models[newPrimary] = { alias: providerName };
3465
+ const selectedProvider = selected.split('/')[0];
3466
+ config.agents.defaults.model.primary = selected;
3467
+ if (!config.agents.defaults.models[selected]) {
3468
+ config.agents.defaults.models[selected] = { alias: selectedProvider };
3469
+ }
3470
+ config.agents.defaults.model.fallbacks = (config.agents.defaults.model.fallbacks || []).filter(k => k !== selected);
3471
+ // 把旧的 primary 加入 fallbacks (如果存在且不同)
3472
+ if (primary && primary !== selected && !config.agents.defaults.model.fallbacks.includes(primary)) {
3473
+ config.agents.defaults.model.fallbacks.push(primary);
3454
3474
  }
3455
3475
  writeConfigWithSync(paths, config);
3456
3476
 
3457
- const selectedName = models.find(m => m.id === selected).name;
3458
- console.log(chalk.green(`\n✅ 已切换到 ${selectedName}`));
3459
- console.log(chalk.gray(` ${newPrimary}`));
3477
+ const selectedProviderConfig = providers[selectedProvider];
3478
+ const selectedLabel = PROVIDER_LABELS[selectedProvider] || selectedProvider;
3479
+ console.log(chalk.green(`\n✅ 已切换到 ${selected}`));
3480
+ console.log(chalk.gray(` 来源: ${selectedLabel}`));
3481
+ if (selectedProviderConfig) {
3482
+ console.log(chalk.gray(` 节点: ${selectedProviderConfig.baseUrl}`));
3483
+ }
3460
3484
  console.log(chalk.yellow('\n💡 切换后建议重启 Gateway: openclaw gateway restart'));
3461
3485
 
3462
3486
  const gwPort = config.gateway?.port || 18789;
@@ -3478,10 +3502,26 @@ async function testConnection(paths, args = {}) {
3478
3502
  }
3479
3503
 
3480
3504
  // 检查当前激活的是哪个
3481
- const primary = config.agents?.defaults?.model?.primary || '';
3505
+ let primary = config.agents?.defaults?.model?.primary || '';
3482
3506
  if (!primary.includes('/')) {
3483
- console.log(chalk.yellow('⚠️ 请先设置主模型'));
3484
- return;
3507
+ // primary 未设置,尝试从已有 provider 自动选择
3508
+ const providers = config.models?.providers || {};
3509
+ const providerNames = Object.keys(providers);
3510
+ if (providerNames.length === 0) {
3511
+ console.log(chalk.yellow('⚠️ 请先设置主模型'));
3512
+ return;
3513
+ }
3514
+ // 优先选 claude provider
3515
+ const claudeP = providerNames.find(n => n.includes('claude'));
3516
+ const firstP = claudeP || providerNames[0];
3517
+ const firstModels = providers[firstP]?.models || [];
3518
+ if (firstModels.length > 0) {
3519
+ primary = `${firstP}/${firstModels[0].id}`;
3520
+ console.log(chalk.yellow(`⚠️ 主模型未设置,自动使用: ${primary}`));
3521
+ } else {
3522
+ console.log(chalk.yellow('⚠️ 请先设置主模型'));
3523
+ return;
3524
+ }
3485
3525
  }
3486
3526
 
3487
3527
  const providerName = primary.split('/')[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {