@kikkimo/claude-launcher 2.2.0 → 2.4.0

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/claude-launcher CHANGED
@@ -263,9 +263,52 @@ async function addNewThirdPartyApi() {
263
263
  }
264
264
 
265
265
  /**
266
- * Remove third-party API
266
+ * Remove third-party API menu with submenu
267
267
  */
268
268
  async function removeThirdPartyApi() {
269
+ console.clear();
270
+ console.log('');
271
+ console.log(colors.bright + colors.orange + '🗑️ ' + await i18n.t('menu.remove_api.title') + colors.reset);
272
+ console.log('');
273
+
274
+ const apis = apiManager.getApis();
275
+
276
+ // Show current API count
277
+ if (apis.length > 0) {
278
+ console.log(colors.cyan + ' ' + await i18n.t('messages.info.current_api_count', apis.length) + colors.reset);
279
+ console.log('');
280
+ }
281
+
282
+ const menuOptions = [
283
+ await i18n.t('menu.remove_api.delete_single'),
284
+ await i18n.t('menu.remove_api.clear_all'),
285
+ await i18n.t('menu.remove_api.back')
286
+ ];
287
+
288
+ initializeGlobalMenus();
289
+ globalApiManagementMenu.setOptions(menuOptions);
290
+ const choice = await globalApiManagementMenu.navigate();
291
+
292
+ switch (choice) {
293
+ case 0: // Delete Single API
294
+ await deleteSingleApi();
295
+ return;
296
+
297
+ case 1: // Clear All APIs
298
+ await clearAllApis();
299
+ return;
300
+
301
+ case 2: // Back
302
+ case -1:
303
+ default:
304
+ return;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Delete a single API
310
+ */
311
+ async function deleteSingleApi() {
269
312
  try {
270
313
  // Get API list
271
314
  const apis = apiManager.getApis();
@@ -281,7 +324,7 @@ async function removeThirdPartyApi() {
281
324
  );
282
325
 
283
326
  if (!selectedApi) {
284
- return showMenu();
327
+ return;
285
328
  }
286
329
 
287
330
  // Show confirmation dialog
@@ -299,23 +342,20 @@ async function removeThirdPartyApi() {
299
342
  `${await i18n.t('api.details.provider')}: ${selectedApi.provider}`
300
343
  ]);
301
344
 
302
- // Show success message and return to main menu
345
+ // Show success message
303
346
  const remainingApis = apiManager.getApis();
304
347
  if (remainingApis.length === 0) {
305
348
  showInfo(await i18n.t('messages.info.all_apis_removed'));
306
349
  }
307
350
  await waitForKey(await i18n.t('messages.prompts.press_any_key'));
308
- return showMenu();
309
351
 
310
352
  } catch (removeError) {
311
353
  showError(await i18n.t('errors.api.failed_remove', removeError.message));
312
354
  await waitForKey(await i18n.t('messages.prompts.press_any_key'));
313
- return showMenu();
314
355
  }
315
356
  } else {
316
357
  showInfo(await i18n.t('messages.info.removal_cancelled'));
317
358
  await waitForKey(await i18n.t('messages.prompts.press_any_key'));
318
- return showMenu();
319
359
  }
320
360
 
321
361
  } catch (error) {
@@ -325,6 +365,42 @@ async function removeThirdPartyApi() {
325
365
  }
326
366
  }
327
367
 
368
+ /**
369
+ * Clear all APIs with confirmation
370
+ */
371
+ async function clearAllApis() {
372
+ const { simpleInput } = require('./lib/ui/prompts');
373
+
374
+ const apis = apiManager.getApis();
375
+ const count = apis.length;
376
+
377
+ if (count === 0) {
378
+ console.clear();
379
+ showInfo(await i18n.t('messages.info.no_apis'));
380
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
381
+ return;
382
+ }
383
+
384
+ console.clear();
385
+ console.log('');
386
+ console.log(colors.bright + colors.red + '⚠️ ' + await i18n.t('menu.remove_api.clear_all') + colors.reset);
387
+ console.log('');
388
+ console.log(colors.yellow + ' ' + await i18n.t('messages.prompts.confirm_clear_all', count) + colors.reset);
389
+ console.log('');
390
+
391
+ const input = await simpleInput(colors.cyan + ' ' + await i18n.t('messages.prompts.confirm_clear_all_input') + colors.reset);
392
+
393
+ if (input === 'CLEAR') {
394
+ const clearedCount = apiManager.clearAllApis();
395
+ console.clear();
396
+ showSuccess(await i18n.t('messages.info.all_apis_cleared', clearedCount));
397
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
398
+ } else {
399
+ showInfo(await i18n.t('messages.info.clear_cancelled'));
400
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
401
+ }
402
+ }
403
+
328
404
  /**
329
405
  * Switch active third-party API
330
406
  */
@@ -357,40 +433,110 @@ async function switchThirdPartyApi() {
357
433
  /**
358
434
  * View API statistics
359
435
  */
436
+ /**
437
+ * Format timestamp to relative time
438
+ * @param {string|null} timestamp - ISO timestamp or null
439
+ * @returns {string} Relative time string
440
+ */
441
+ function formatRelativeTime(timestamp) {
442
+ if (!timestamp) return i18n.tSync('statistics.time_never');
443
+
444
+ const now = Date.now();
445
+ const diff = now - new Date(timestamp).getTime();
446
+ const minutes = Math.floor(diff / 60000);
447
+ const hours = Math.floor(diff / 3600000);
448
+ const days = Math.floor(diff / 86400000);
449
+
450
+ if (minutes < 1) return i18n.tSync('statistics.time_just_now');
451
+ if (minutes < 60) return i18n.tSync('statistics.time_minutes_ago', minutes);
452
+ if (hours < 24) return i18n.tSync('statistics.time_hours_ago', hours);
453
+ return i18n.tSync('statistics.time_days_ago', days);
454
+ }
455
+
360
456
  async function viewStatistics() {
361
457
  console.clear();
362
458
  console.log('');
363
459
  console.log(colors.bright + colors.orange + '📊 ' + await i18n.t('statistics.title') + colors.reset);
364
460
  console.log('');
365
461
 
366
- const stats = apiManager.getStatistics();
367
- const apis = apiManager.getApis();
462
+ const menuOptions = [
463
+ await i18n.t('statistics.menu_view'),
464
+ await i18n.t('statistics.menu_reset'),
465
+ await i18n.t('statistics.menu_back')
466
+ ];
467
+
468
+ initializeGlobalMenus();
469
+ globalApiManagementMenu.setOptions(menuOptions);
470
+ const choice = await globalApiManagementMenu.navigate();
471
+
472
+ switch (choice) {
473
+ case 0: // View Statistics Details
474
+ await showStatisticsDetails();
475
+ return viewStatistics();
476
+
477
+ case 1: // Reset Statistics
478
+ const { simpleInput } = require('./lib/ui/prompts');
479
+ const confirm = await simpleInput(colors.yellow + ' ' + await i18n.t('statistics.reset_confirm') + ' ' + colors.reset);
480
+ if (confirm.toLowerCase() === 'y') {
481
+ apiManager.resetStatistics();
482
+ console.log(colors.green + ' ✓ ' + await i18n.t('statistics.reset_success') + colors.reset);
483
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
484
+ }
485
+ return viewStatistics();
486
+
487
+ case 2: // Back
488
+ case -1:
489
+ default:
490
+ return;
491
+ }
492
+ }
493
+
494
+ /**
495
+ * Show detailed statistics
496
+ */
497
+ async function showStatisticsDetails() {
498
+ const { padStringToWidth } = require('./lib/utils/string-width');
368
499
 
369
- console.log(colors.cyan + ' ' + i18n.tSync('ui.general.summary') + colors.reset);
500
+ console.clear();
501
+ console.log('');
502
+ console.log(colors.bright + colors.orange + '📊 ' + await i18n.t('statistics.title') + colors.reset);
503
+ console.log('');
504
+
505
+ const stats = apiManager.getEnhancedStatistics();
506
+
507
+ // Summary section
508
+ console.log(colors.cyan + ' ' + i18n.tSync('ui.general.summary') + ':' + colors.reset);
370
509
  console.log(colors.gray + ` ${await i18n.t('statistics.total_apis', stats.totalApis)}` + colors.reset);
371
510
  console.log(colors.gray + ` ${await i18n.t('statistics.active_api', stats.activeApiName)}` + colors.reset);
372
511
  console.log(colors.gray + ` ${await i18n.t('statistics.most_used', stats.mostUsedApi)}` + colors.reset);
373
512
  console.log(colors.gray + ` ${await i18n.t('statistics.total_usage', stats.totalUsage)}` + colors.reset);
513
+ console.log(colors.gray + ` ${await i18n.t('statistics.success_rate', stats.successRate)}` + colors.reset);
374
514
  console.log('');
375
515
 
376
- if (apis.length > 0) {
377
- console.log(colors.cyan + ' ' + i18n.tSync('ui.general.configured_apis') + colors.reset);
378
-
379
- // Pre-fetch translations to avoid await in forEach
380
- const currentlyActiveText = await i18n.t('api.details.currently_active');
381
- const providerText = await i18n.t('api.details.provider');
382
- const usageText = await i18n.t('api.details.usage');
383
- const timesSuffixText = await i18n.t('api.details.times_suffix');
384
- const createdAtText = await i18n.t('api.details.created_at');
385
-
386
- apis.forEach((api, index) => {
387
- const isActive = apiManager.getActiveApi()?.id === api.id;
388
- const activeText = isActive ? ` (${currentlyActiveText})` : '';
389
- console.log(colors.gray + ` ${index + 1}. ${api.name}${activeText}` + colors.reset);
390
- console.log(colors.dim + ` ${providerText}: ${api.provider}` + colors.reset);
391
- console.log(colors.dim + ` ${usageText}: ${api.usageCount || 0} ${timesSuffixText}` + colors.reset);
392
- console.log(colors.dim + ` ${createdAtText}: ${api.createdAt}` + colors.reset);
393
- });
516
+ if (stats.apiStats.length > 0) {
517
+ console.log(colors.cyan + ' ' + i18n.tSync('ui.general.configured_apis') + ':' + colors.reset);
518
+ console.log('');
519
+
520
+ // Table header
521
+ console.log(colors.dim + ' ' +
522
+ padStringToWidth(await i18n.t('statistics.header_name'), 20) +
523
+ padStringToWidth(await i18n.t('statistics.header_usage'), 10) +
524
+ padStringToWidth(await i18n.t('statistics.header_success'), 10) +
525
+ await i18n.t('statistics.header_last_used') +
526
+ colors.reset);
527
+ console.log(colors.dim + ' ' + '─'.repeat(60) + colors.reset);
528
+
529
+ for (const api of stats.apiStats) {
530
+ const lastUsedText = formatRelativeTime(api.lastUsed);
531
+ console.log(colors.gray + ' ' +
532
+ padStringToWidth(api.name, 20) +
533
+ padStringToWidth(String(api.usageCount), 10) +
534
+ padStringToWidth(api.successRate, 10) +
535
+ lastUsedText +
536
+ colors.reset);
537
+ }
538
+ } else {
539
+ console.log(colors.gray + ' ' + await i18n.t('statistics.no_usage') + colors.reset);
394
540
  }
395
541
 
396
542
  console.log('');
@@ -568,20 +714,23 @@ async function showApiManagementMenu() {
568
714
 
569
715
  // Build menu options based on password setup status
570
716
  const menuOptions = [
571
- await i18n.t('menu.api_management.add_new'),
572
- await i18n.t('menu.api_management.remove'),
573
- await i18n.t('menu.api_management.switch'),
574
- await i18n.t('menu.api_management.statistics')
717
+ await i18n.t('menu.api_management.add_new'), // 0
718
+ await i18n.t('menu.api_management.remove'), // 1
719
+ await i18n.t('menu.api_management.switch'), // 2
720
+ await i18n.t('menu.api_management.statistics') // 3
575
721
  ];
576
722
 
577
723
  // Add import/export options only if password is set
578
724
  if (apiManager.canUseImportExport()) {
579
- menuOptions.push(await i18n.t('menu.api_management.export'));
580
- menuOptions.push(await i18n.t('menu.api_management.import'));
581
- menuOptions.push(await i18n.t('menu.api_management.change_password'));
725
+ menuOptions.push(await i18n.t('menu.api_management.export')); // 4
726
+ menuOptions.push(await i18n.t('menu.api_management.import')); // 5
727
+ menuOptions.push(await i18n.t('menu.api_management.change_password')); // 6
582
728
  }
583
729
 
584
- menuOptions.push(await i18n.t('menu.api_management.back'));
730
+ // Add model upgrade settings (always available)
731
+ menuOptions.push(await i18n.t('model_upgrade.settings_title')); // 4 or 7 (depending on import/export)
732
+
733
+ menuOptions.push(await i18n.t('menu.api_management.back')); // 5 or 8
585
734
 
586
735
  // Ensure global menus are initialized
587
736
  initializeGlobalMenus();
@@ -604,7 +753,7 @@ async function showApiManagementMenu() {
604
753
  await viewStatistics();
605
754
  return showMenu();
606
755
  } else if (apiManager.canUseImportExport()) {
607
- // If import/export is available, handle those options
756
+ // With import/export enabled: indices 4-8
608
757
  if (choice === 4) { // Export Configuration
609
758
  await exportConfiguration();
610
759
  return showMenu();
@@ -614,12 +763,16 @@ async function showApiManagementMenu() {
614
763
  } else if (choice === 6) { // Change Password
615
764
  await changePassword();
616
765
  return showMenu();
617
- } else if (choice === 7) { // Back to Main Menu
766
+ } else if (choice === 7) { // Model Upgrade Settings (NEW)
767
+ return await showModelUpgradeSettings();
768
+ } else if (choice === 8) { // Back to Main Menu
618
769
  return showMenu();
619
770
  }
620
771
  } else {
621
- // If import/export is not available, only Back to Main Menu
622
- if (choice === 4) { // Back to Main Menu
772
+ // Without import/export: indices 4-5
773
+ if (choice === 4) { // Model Upgrade Settings (NEW)
774
+ return await showModelUpgradeSettings();
775
+ } else if (choice === 5) { // Back to Main Menu
623
776
  return showMenu();
624
777
  }
625
778
  }
@@ -628,6 +781,144 @@ async function showApiManagementMenu() {
628
781
  return showMenu();
629
782
  }
630
783
 
784
+ /**
785
+ * Show model upgrade settings menu
786
+ */
787
+ async function showModelUpgradeSettings() {
788
+ const versionChecker = require('./lib/utils/version-checker');
789
+ const upgradeChecker = require('./lib/utils/model-upgrade-checker');
790
+
791
+ console.clear();
792
+ console.log('');
793
+ console.log(colors.bright + colors.orange + '⚙️ ' + await i18n.t('model_upgrade.settings_title') + colors.reset);
794
+ console.log('');
795
+
796
+ const config = await versionChecker.loadConfig();
797
+ const isAutoOn = config.autoModelUpgrade === true;
798
+
799
+ console.log(colors.cyan + ' ' + await i18n.t('model_upgrade.current_config') + ':' + colors.reset);
800
+ console.log(colors.gray + ' ' + await i18n.t('model_upgrade.auto_upgrade_label') + ': ' +
801
+ (isAutoOn
802
+ ? colors.green + await i18n.t('model_upgrade.auto_upgrade_on')
803
+ : colors.dim + await i18n.t('model_upgrade.auto_upgrade_off')
804
+ ) + colors.reset);
805
+ console.log('');
806
+
807
+ const menuOptions = [
808
+ isAutoOn
809
+ ? await i18n.t('model_upgrade.menu_toggle_auto_on')
810
+ : await i18n.t('model_upgrade.menu_toggle_auto_off'),
811
+ await i18n.t('model_upgrade.menu_manual_upgrade'),
812
+ await i18n.t('model_upgrade.menu_back')
813
+ ];
814
+
815
+ initializeGlobalMenus();
816
+ globalApiManagementMenu.setOptions(menuOptions);
817
+ const choice = await globalApiManagementMenu.navigate();
818
+
819
+ switch (choice) {
820
+ case 0: // Toggle auto upgrade
821
+ await versionChecker.setAutoModelUpgrade(!isAutoOn);
822
+ console.log('');
823
+ console.log(colors.green + '✓ ' + await i18n.t('model_upgrade.auto_upgrade_label') + ': ' +
824
+ (!isAutoOn
825
+ ? await i18n.t('model_upgrade.auto_upgrade_on')
826
+ : await i18n.t('model_upgrade.auto_upgrade_off')
827
+ ) + colors.reset);
828
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
829
+ return showModelUpgradeSettings();
830
+
831
+ case 1: // Manual upgrade
832
+ await performManualUpgrade();
833
+ return showModelUpgradeSettings();
834
+
835
+ case 2: // Back
836
+ case -1:
837
+ default:
838
+ return showApiManagementMenu();
839
+ }
840
+ }
841
+
842
+ /**
843
+ * Perform manual upgrade for all APIs with interactive confirmation
844
+ */
845
+ async function performManualUpgrade() {
846
+ const { getLatestModel, getProvider } = require('./lib/presets/providers');
847
+ const { simpleInput } = require('./lib/ui/prompts');
848
+
849
+ console.clear();
850
+ console.log('');
851
+ console.log(colors.bright + colors.orange + '🔄 ' + await i18n.t('model_upgrade.manual_title') + colors.reset);
852
+ console.log('');
853
+
854
+ const apis = apiManager.getApis();
855
+
856
+ if (apis.length === 0) {
857
+ console.log(colors.yellow + ' ' + await i18n.t('messages.info.no_apis') + colors.reset);
858
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
859
+ return;
860
+ }
861
+
862
+ console.log(colors.gray + ' ' + await i18n.t('model_upgrade.manual_checking', apis.length) + colors.reset);
863
+ console.log('');
864
+
865
+ let upgradedCount = 0;
866
+ let skippedUpToDate = 0;
867
+ let skippedNoInfo = 0;
868
+ let skippedByUser = 0;
869
+
870
+ for (let i = 0; i < apis.length; i++) {
871
+ const api = apis[i];
872
+ const latestModel = getLatestModel(api.model, api.provider);
873
+
874
+ console.log(colors.cyan + ' ─────────────────────────────────────────────────' + colors.reset);
875
+ console.log(colors.bright + ` ${i + 1}/${apis.length} ${api.name}` + colors.reset);
876
+ console.log(colors.gray + ' ' + await i18n.t('model_upgrade.manual_api_current', api.model) + colors.reset);
877
+
878
+ if (latestModel) {
879
+ console.log(colors.green + ' ' + await i18n.t('model_upgrade.manual_api_latest', latestModel) + colors.reset);
880
+ console.log('');
881
+
882
+ // Ask for confirmation
883
+ const answer = await simpleInput(colors.yellow + ' ' + await i18n.t('model_upgrade.manual_confirm') + ' ' + colors.reset);
884
+
885
+ if (answer.toLowerCase() === 'y') {
886
+ apiManager.updateApiModel(api.id, latestModel);
887
+ console.log(colors.green + ' ✓ ' + await i18n.t('model_upgrade.manual_upgraded', api.model, latestModel) + colors.reset);
888
+ upgradedCount++;
889
+ } else {
890
+ console.log(colors.dim + ' ' + await i18n.t('model_upgrade.manual_skipped') + colors.reset);
891
+ skippedByUser++;
892
+ }
893
+ } else {
894
+ // No upgrade info available - check if model exists in provider
895
+ const provider = getProvider(api.provider);
896
+ if (provider && provider.models && provider.models.includes(api.model)) {
897
+ // Model exists in provider, likely already latest or no alias defined
898
+ console.log(colors.dim + ' ' + await i18n.t('model_upgrade.manual_api_uptodate') + colors.reset);
899
+ skippedUpToDate++;
900
+ } else {
901
+ console.log(colors.dim + ' ' + await i18n.t('model_upgrade.manual_api_no_info') + colors.reset);
902
+ skippedNoInfo++;
903
+ }
904
+ }
905
+
906
+ console.log('');
907
+ }
908
+
909
+ console.log(colors.cyan + ' ─────────────────────────────────────────────────' + colors.reset);
910
+ console.log('');
911
+ console.log(colors.green + ' ' + await i18n.t('model_upgrade.manual_complete') + colors.reset);
912
+ console.log(colors.gray + ' ' + await i18n.t('model_upgrade.manual_stats_upgraded', upgradedCount) + colors.reset);
913
+ console.log(colors.gray + ' ' + await i18n.t('model_upgrade.manual_stats_skipped',
914
+ skippedUpToDate + skippedNoInfo + skippedByUser,
915
+ skippedUpToDate,
916
+ skippedNoInfo) + colors.reset);
917
+ console.log('');
918
+
919
+ await waitForKey(await i18n.t('messages.prompts.press_any_key'));
920
+ }
921
+
631
922
  /**
632
923
  * Handle third-party API launch
633
924
  */
@@ -646,12 +937,15 @@ async function handleThirdPartyApiLaunch(skipPermissions = false) {
646
937
  return showMenu();
647
938
  }
648
939
 
649
- // Increment usage count for the active API since we're actually using it
650
- apiManager.incrementActiveApiUsage();
940
+ // Record successful launch BEFORE launching (since process exits after)
941
+ apiManager.recordSuccessfulLaunch();
651
942
 
652
943
  launchClaudeWithApi(activeApi, skipPermissions);
653
944
 
654
945
  } catch (error) {
946
+ // Record failed launch
947
+ apiManager.recordFailedLaunch(error.message);
948
+
655
949
  showError('Failed to launch with third-party API', [error.message]);
656
950
 
657
951
  setTimeout(() => {
@@ -741,6 +1035,60 @@ async function showMenu() {
741
1035
  // Silently ignore update check errors
742
1036
  }
743
1037
 
1038
+ // ========================================
1039
+ // Model upgrade check (new feature)
1040
+ // ========================================
1041
+ let modelUpgradeInfo = null;
1042
+ try {
1043
+ const upgradeChecker = require('./lib/utils/model-upgrade-checker');
1044
+ const autoUpgrade = await upgradeChecker.isAutoUpgradeEnabled();
1045
+
1046
+ if (autoUpgrade) {
1047
+ // Auto upgrade enabled: always check and upgrade (bypass cache)
1048
+ const upgrades = upgradeChecker.checkAllApiUpgrades(apiManager);
1049
+ if (upgrades.length > 0) {
1050
+ const upgraded = upgradeChecker.performAutoUpgrade(apiManager, upgrades);
1051
+ if (upgraded.length > 0) {
1052
+ modelUpgradeInfo = colors.green + ' ✓ ' +
1053
+ i18n.tSync('model_upgrade.auto_upgraded', upgraded[0].from, upgraded[0].to) + colors.reset;
1054
+ if (upgraded.length > 1) {
1055
+ modelUpgradeInfo += '\n' + colors.green + ' ' +
1056
+ `(+${upgraded.length - 1} more)` + colors.reset;
1057
+ }
1058
+ }
1059
+ }
1060
+ } else {
1061
+ // Auto upgrade disabled: use cache for notification
1062
+ const result = await upgradeChecker.checkForModelUpgrades(apiManager);
1063
+ if (result.needsCheck && result.upgrades.length > 0) {
1064
+ const first = result.upgrades[0];
1065
+ modelUpgradeInfo = colors.yellow + ' ⚠️ ' +
1066
+ i18n.tSync('model_upgrade.notification', first.currentModel, first.latestModel) +
1067
+ colors.reset + '\n' +
1068
+ colors.yellow + ' ' +
1069
+ i18n.tSync('model_upgrade.notification_api', first.apiName) +
1070
+ colors.reset + '\n' +
1071
+ colors.gray + ' ' +
1072
+ i18n.tSync('model_upgrade.notification_hint') +
1073
+ colors.reset;
1074
+ }
1075
+ }
1076
+ } catch (error) {
1077
+ // Silently ignore model upgrade check errors
1078
+ }
1079
+
1080
+ // Combine version info and model upgrade info
1081
+ let displayInfo = '';
1082
+ if (versionInfo) {
1083
+ displayInfo += versionInfo;
1084
+ }
1085
+ if (modelUpgradeInfo) {
1086
+ if (displayInfo) {
1087
+ displayInfo += '\n';
1088
+ }
1089
+ displayInfo += modelUpgradeInfo;
1090
+ }
1091
+
744
1092
  // Populate menu options dynamically with i18n translations
745
1093
  menuOptions = [
746
1094
  await i18n.t('menu.main.launch_default'),
@@ -754,7 +1102,7 @@ async function showMenu() {
754
1102
  ];
755
1103
 
756
1104
  globalMainMenu.setOptions(menuOptions);
757
- const selection = await globalMainMenu.navigate(false, versionInfo); // Pass version info to display between banner and nav
1105
+ const selection = await globalMainMenu.navigate(false, displayInfo || null); // Pass combined info to display between banner and nav
758
1106
 
759
1107
  if (selection === -1) {
760
1108
  console.log('');
package/docs/README-zh.md CHANGED
@@ -25,11 +25,12 @@
25
25
  - 强密码要求和验证
26
26
 
27
27
  ### 🚀 **第三方 API 管理**
28
- - 全面支持多个第三方 API 提供商(OpenAI、Anthropic、DeepSeek、Kimi、GLM/智谱AI 和自定义 API)
28
+ - 全面支持多个第三方 API 提供商(Anthropic、OpenAI、DeepSeek、Moonshot/Kimi、MiniMax、GLM/智谱AI 和自定义 API)
29
29
  - 带验证的交互式 API 配置
30
- - API 使用统计和跟踪
30
+ - API 使用统计,支持成功/失败率追踪
31
+ - 模型升级通知和自动升级支持
31
32
  - 安全的配置备份和恢复
32
- - 简单的 API 切换和删除
33
+ - 简单的 API 切换、删除和批量清空
33
34
 
34
35
  ### 🌍 **企业级功能**
35
36
  - 全局安装 - 在任何地方都可以使用 `claude-launcher`
@@ -85,7 +86,11 @@ node claude-launcher
85
86
  2. **启动 Claude Code(跳过权限)** - 使用 `--dangerously-skip-permissions` 启动
86
87
  3. **使用第三方 API 启动 Claude Code** - 使用配置的第三方 API
87
88
  4. **使用第三方 API 启动 Claude Code(跳过权限)** - 结合第三方 API 和跳过权限
88
- 5. **第三方 API 管理** - 配置、切换、删除 API,查看统计信息
89
+ 5. **第三方 API 管理** - 完整的 API 生命周期管理:
90
+ - 添加、切换和删除 API
91
+ - 查看使用统计(含成功/失败率)
92
+ - 模型升级设置(自动/手动升级)
93
+ - 导入/导出配置
89
94
  6. **语言设置** - 在11种支持的语言之间切换
90
95
  7. **版本更新检查** - 检查启动器更新
91
96
  8. **退出** - 关闭启动器
@@ -126,15 +131,23 @@ $ claude-launcher
126
131
  📋 第三方 API 管理
127
132
 
128
133
  → 添加新 API
129
- 删除 API
134
+ 删除 API → 删除单个 API / 清空所有 API
130
135
  切换活动 API
131
- 查看统计信息
136
+ 查看统计信息 → 查看详情 / 重置统计
137
+ 模型升级设置 → 自动升级 [开/关] / 手动升级
132
138
  导出配置
133
139
  导入配置
134
140
  更改密码
135
141
  返回主菜单
136
142
  ```
137
143
 
144
+ ### 模型升级功能
145
+
146
+ 启动器会在启动时自动检查模型升级:
147
+ - **自动升级**:自动使用最新的模型版本
148
+ - **手动升级**:逐个审核并确认模型升级
149
+ - **启动通知**:当有新模型版本可用时收到通知
150
+
138
151
  ## ⚙️ 配置
139
152
 
140
153
  ### 现代配置系统
@@ -158,10 +171,11 @@ Claude Launcher 2.0 使用先进的配置系统:
158
171
 
159
172
  通过交互界面配置任何第三方 API 提供商:
160
173
 
161
- - **支持的提供商**:OpenAI、Anthropic、DeepSeek、Kimi、GLM/智谱AI 和自定义 API
174
+ - **支持的提供商**:Anthropic、OpenAI、DeepSeek、Moonshot/Kimi、MiniMax(国内版/国际版)、GLM/智谱AI(GLM-4、GLM-5)和自定义 Anthropic 兼容 API
162
175
  - **安全存储**:所有 API 令牌在存储前加密
163
176
  - **验证**:URL、令牌和模型的实时验证
164
- - **使用跟踪**:监控 API 使用统计
177
+ - **使用跟踪**:监控 API 使用统计,支持成功/失败率追踪
178
+ - **模型升级**:自动检测并升级到最新模型版本
165
179
  - **提供商特定功能**:为每个提供商优化配置,提供有用的注释和建议
166
180
 
167
181
  ### 配置导入/导出