@kikkimo/claude-launcher 2.4.0 → 3.0.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.
@@ -6,7 +6,9 @@
6
6
  const { getPasswordInput } = require('./password-input');
7
7
  const colors = require('../ui/colors');
8
8
  const { validatePasswordStrength, getPasswordRequirements, generatePasswordExample } = require('./password-strength');
9
+ const { waitForKey } = require('../ui/prompts');
9
10
  const i18n = require('../i18n');
11
+ const screen = require('../ui/screen');
10
12
 
11
13
  /**
12
14
  * Force cleanup stdin state to prevent navigation issues
@@ -43,36 +45,70 @@ function getTranslatedStrength(strength) {
43
45
  }
44
46
 
45
47
  /**
46
- * Verify user password for export/import operations
47
- * @param {Object} apiManager - The API manager instance
48
- * @param {string} operation - The operation being performed (for error messages)
49
- * @returns {Promise<boolean>} - True if password is verified, false otherwise
48
+ * Unified password guard for protected operations
49
+ * Mode A (delete/edit): clears screen, shows header, prompts password
50
+ * Mode B (export/import): no clear, no header, just prompts password
51
+ * @param {Object} apiManager - ApiManager instance
52
+ * @param {string} operation - 'delete' | 'edit' | 'export' | 'import'
53
+ * @returns {Promise<boolean>} true if authorized, false if denied/cancelled
50
54
  */
51
- async function verifyExportPassword(apiManager, operation = 'operation') {
55
+ async function passwordGuard(apiManager, operation) {
56
+ const hasPassword = apiManager.hasExportPassword();
57
+
58
+ // Mode A: delete/edit — no password means allow freely
59
+ if ((operation === 'delete' || operation === 'edit') && !hasPassword) {
60
+ return true;
61
+ }
62
+
63
+ // Mode B: export/import — no password means block (defense-in-depth)
64
+ if ((operation === 'export' || operation === 'import') && !hasPassword) {
65
+ return false;
66
+ }
67
+
68
+ // Mode A: clear screen and show header
69
+ if (operation === 'delete' || operation === 'edit') {
70
+ const headerKey = `password.guard.${operation}.header`;
71
+ screen.render([
72
+ '',
73
+ colors.bright + colors.orange + i18n.tSync(headerKey) + colors.reset,
74
+ '',
75
+ ]);
76
+ }
77
+
52
78
  try {
53
79
  const password = await getPasswordInput(i18n.tSync('messages.prompts.enter_password'));
54
80
 
81
+ // Empty password check
55
82
  if (!password) {
56
83
  forceStdinCleanup();
57
- console.log(colors.red + i18n.tSync('errors.password.empty') + colors.reset);
84
+ screen.write(colors.red + i18n.tSync('errors.password.empty') + colors.reset + '\n');
85
+ await waitForKey(i18n.tSync('ui.general.press_any_key_continue'));
58
86
  return false;
59
87
  }
60
88
 
89
+ // Verify password
61
90
  if (!apiManager.verifyExportPassword(password)) {
62
91
  forceStdinCleanup();
63
- console.log(colors.red + '❌ ' + i18n.tSync('errors.password.verification_failed') + colors.reset);
64
- console.log(colors.gray + i18n.tSync('errors.general.operation_failed', operation) + colors.reset);
92
+ screen.write(colors.red + '❌ ' + i18n.tSync('errors.password.verification_failed') + colors.reset + '\n');
93
+ await waitForKey(i18n.tSync('ui.general.press_any_key_continue'));
65
94
  return false;
66
95
  }
67
96
 
68
97
  return true;
69
98
  } catch (error) {
70
99
  forceStdinCleanup();
71
- if (error.message.includes('cancelled')) {
72
- console.log(colors.yellow + '\n' + i18n.tSync('errors.general.cancelled_by_user').replace('{0}', operation) + colors.reset);
73
- } else {
74
- console.log(colors.red + `❌ Password verification error: ${error.message}` + colors.reset);
100
+ if (error.message === 'Password input cancelled') {
101
+ // Esc silent return
102
+ return false;
103
+ }
104
+ if (error.message.includes('Ctrl+C')) {
105
+ // Delegate to stdinManager double-tap exit
106
+ const stdinManager = require('../utils/stdin-manager');
107
+ stdinManager.handleCtrlC();
108
+ return false;
75
109
  }
110
+ // Unexpected error — treat as failure
111
+ screen.write(colors.red + `❌ ${error.message}` + colors.reset + '\n');
76
112
  return false;
77
113
  }
78
114
  }
@@ -88,13 +124,13 @@ async function verifyCurrentPassword(apiManager) {
88
124
 
89
125
  if (!currentPassword) {
90
126
  forceStdinCleanup();
91
- console.log(colors.red + i18n.tSync('errors.password.empty') + colors.reset);
127
+ screen.write(colors.red + i18n.tSync('errors.password.empty') + colors.reset + '\n');
92
128
  return false;
93
129
  }
94
130
 
95
131
  if (!apiManager.verifyExportPassword(currentPassword)) {
96
132
  forceStdinCleanup();
97
- console.log(colors.red + '❌ ' + i18n.tSync('errors.password.current_incorrect') + colors.reset);
133
+ screen.write(colors.red + '❌ ' + i18n.tSync('errors.password.current_incorrect') + colors.reset + '\n');
98
134
  return false;
99
135
  }
100
136
 
@@ -102,9 +138,9 @@ async function verifyCurrentPassword(apiManager) {
102
138
  } catch (error) {
103
139
  forceStdinCleanup();
104
140
  if (error.message.includes('cancelled')) {
105
- console.log(colors.yellow + '\n' + i18n.tSync('errors.password.verification_cancelled') + colors.reset);
141
+ screen.write(colors.yellow + '\n' + i18n.tSync('errors.password.verification_cancelled') + colors.reset + '\n');
106
142
  } else {
107
- console.log(colors.red + `❌ Password verification error: ${error.message}` + colors.reset);
143
+ screen.write(colors.red + `❌ Password verification error: ${error.message}` + colors.reset + '\n');
108
144
  }
109
145
  return false;
110
146
  }
@@ -118,24 +154,23 @@ async function verifyCurrentPassword(apiManager) {
118
154
  */
119
155
  async function setupNewPassword(apiManager, isFirstTime = false) {
120
156
  try {
121
- console.log('');
122
- console.log(colors.cyan + (isFirstTime ? i18n.tSync('password.setup.title') : i18n.tSync('password.setup.change_title')) + colors.reset);
123
- console.log('');
157
+ const titleLine = colors.cyan + (isFirstTime ? i18n.tSync('password.setup.title') : i18n.tSync('password.setup.change_title')) + colors.reset;
158
+ const requirements = getPasswordRequirements();
124
159
 
160
+ const headerLines = ['', titleLine, ''];
125
161
  if (!isFirstTime) {
126
- console.log(colors.yellow + '⚠️ ' + i18n.tSync('password.setup.warning') + colors.reset);
127
- console.log('');
162
+ headerLines.push(colors.yellow + '⚠️ ' + i18n.tSync('password.setup.warning') + colors.reset);
163
+ headerLines.push('');
128
164
  }
129
-
130
- // Display password requirements
131
- console.log(colors.cyan + i18n.tSync('ui.general.password_requirements_title') + colors.reset);
132
- const requirements = getPasswordRequirements();
165
+ headerLines.push(colors.cyan + i18n.tSync('ui.general.password_requirements_title') + colors.reset);
133
166
  requirements.forEach(req => {
134
- console.log(colors.gray + ' ' + req + colors.reset);
167
+ headerLines.push(colors.gray + ' ' + req + colors.reset);
135
168
  });
136
- console.log('');
137
- console.log(colors.gray + i18n.tSync('ui.general.example_strong_password', generatePasswordExample()) + colors.reset);
138
- console.log('');
169
+ headerLines.push('');
170
+ headerLines.push(colors.gray + i18n.tSync('ui.general.example_strong_password', generatePasswordExample()) + colors.reset);
171
+ headerLines.push('');
172
+
173
+ screen.render(headerLines);
139
174
 
140
175
  let attempts = 0;
141
176
  const maxAttempts = 3;
@@ -149,9 +184,9 @@ async function setupNewPassword(apiManager, isFirstTime = false) {
149
184
 
150
185
  if (!newPassword) {
151
186
  forceStdinCleanup();
152
- console.log(colors.red + i18n.tSync('errors.password.empty') + colors.reset);
187
+ screen.write(colors.red + i18n.tSync('errors.password.empty') + colors.reset + '\n');
153
188
  if (attempts < maxAttempts) {
154
- console.log('');
189
+ screen.write('\n');
155
190
  continue;
156
191
  } else {
157
192
  return false;
@@ -162,27 +197,27 @@ async function setupNewPassword(apiManager, isFirstTime = false) {
162
197
  const validation = validatePasswordStrength(newPassword);
163
198
 
164
199
  if (!validation.valid) {
165
- console.log(colors.red + '❌ ' + i18n.tSync('errors.password.requirements_not_met') + colors.reset);
200
+ screen.write(colors.red + '❌ ' + i18n.tSync('errors.password.requirements_not_met') + colors.reset + '\n');
166
201
  validation.errors.forEach(error => {
167
- console.log(colors.red + ' • ' + error + colors.reset);
202
+ screen.write(colors.red + ' • ' + error + colors.reset + '\n');
168
203
  });
169
204
 
170
205
  if (validation.suggestions.length > 0) {
171
- console.log(colors.yellow + '💡 ' + i18n.tSync('ui.general.suggestions') + colors.reset);
206
+ screen.write(colors.yellow + '💡 ' + i18n.tSync('ui.general.suggestions') + colors.reset + '\n');
172
207
  validation.suggestions.forEach(suggestion => {
173
- console.log(colors.yellow + ' • ' + suggestion + colors.reset);
208
+ screen.write(colors.yellow + ' • ' + suggestion + colors.reset + '\n');
174
209
  });
175
210
  }
176
211
  // If password is invalid, force display strength as "Weak" regardless of technical score
177
212
  const strengthKey = validation.valid ? validation.strength : 'Weak';
178
213
  const translatedStrength = getTranslatedStrength(strengthKey);
179
- console.log(colors.gray + i18n.tSync('ui.general.current_password_strength', translatedStrength) + colors.reset);
214
+ screen.write(colors.gray + i18n.tSync('ui.general.current_password_strength', translatedStrength) + colors.reset + '\n');
180
215
 
181
216
  if (attempts < maxAttempts) {
182
- console.log('');
217
+ screen.write('\n');
183
218
  continue;
184
219
  } else {
185
- console.log(colors.red + i18n.tSync('ui.general.max_attempts_password_failed') + colors.reset);
220
+ screen.write(colors.red + i18n.tSync('ui.general.max_attempts_password_failed') + colors.reset + '\n');
186
221
  return false;
187
222
  }
188
223
  }
@@ -192,9 +227,9 @@ async function setupNewPassword(apiManager, isFirstTime = false) {
192
227
 
193
228
  if (newPassword !== confirmPassword) {
194
229
  forceStdinCleanup();
195
- console.log(colors.red + i18n.tSync('ui.general.passwords_mismatch') + colors.reset);
230
+ screen.write(colors.red + i18n.tSync('ui.general.passwords_mismatch') + colors.reset + '\n');
196
231
  if (attempts < maxAttempts) {
197
- console.log('');
232
+ screen.write('\n');
198
233
  continue;
199
234
  } else {
200
235
  return false;
@@ -203,8 +238,8 @@ async function setupNewPassword(apiManager, isFirstTime = false) {
203
238
 
204
239
  // Success - set the password
205
240
  apiManager.setExportPassword(newPassword);
206
- console.log('');
207
- console.log(colors.green + '✓ ' + i18n.tSync('password.setup.password_success', getTranslatedStrength(validation.strength)) + colors.reset);
241
+ screen.write('\n');
242
+ screen.write(colors.green + '✓ ' + i18n.tSync('password.setup.password_success', getTranslatedStrength(validation.strength)) + colors.reset + '\n');
208
243
  return true;
209
244
  }
210
245
 
@@ -213,9 +248,9 @@ async function setupNewPassword(apiManager, isFirstTime = false) {
213
248
  } catch (error) {
214
249
  forceStdinCleanup();
215
250
  if (error.message.includes('cancelled')) {
216
- console.log(colors.yellow + '\n' + i18n.tSync('errors.password.setup_cancelled') + colors.reset);
251
+ screen.write(colors.yellow + '\n' + i18n.tSync('errors.password.setup_cancelled') + colors.reset + '\n');
217
252
  } else {
218
- console.log(colors.red + `❌ Failed to set password: ${error.message}` + colors.reset);
253
+ screen.write(colors.red + `❌ Failed to set password: ${error.message}` + colors.reset + '\n');
219
254
  }
220
255
  return false;
221
256
  }
@@ -234,21 +269,21 @@ async function changePassword(apiManager) {
234
269
  return false;
235
270
  }
236
271
 
237
- console.log(colors.green + i18n.tSync('errors.password.current_password_verified') + colors.reset);
238
- console.log('');
272
+ screen.write(colors.green + i18n.tSync('errors.password.current_password_verified') + colors.reset + '\n');
273
+ screen.write('\n');
239
274
 
240
275
  // Set new password
241
276
  return await setupNewPassword(apiManager, false);
242
277
 
243
278
  } catch (error) {
244
279
  forceStdinCleanup();
245
- console.log(colors.red + `❌ Password change failed: ${error.message}` + colors.reset);
280
+ screen.write(colors.red + `❌ Password change failed: ${error.message}` + colors.reset + '\n');
246
281
  return false;
247
282
  }
248
283
  }
249
284
 
250
285
  module.exports = {
251
- verifyExportPassword,
286
+ passwordGuard,
252
287
  verifyCurrentPassword,
253
288
  setupNewPassword,
254
289
  changePassword
package/lib/i18n/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  const LanguageManager = require('./language-manager');
7
7
  const MessageFormatter = require('./formatter');
8
+ const screen = require('../ui/screen');
8
9
 
9
10
  class I18n {
10
11
  constructor() {
@@ -34,14 +35,14 @@ class I18n {
34
35
  for (const k of keys) {
35
36
  value = value?.[k];
36
37
  if (value === undefined) {
37
- console.warn(`Translation key not found: ${key}`);
38
+ screen.debug(`Translation key not found: ${key}`);
38
39
  return key; // Return key as fallback
39
40
  }
40
41
  }
41
42
 
42
43
  // If value is not a string, return the key as fallback
43
44
  if (typeof value !== 'string') {
44
- console.warn(`Translation value is not a string for key: ${key}`);
45
+ screen.debug(`Translation value is not a string for key: ${key}`);
45
46
  return key;
46
47
  }
47
48
 
@@ -56,7 +57,7 @@ class I18n {
56
57
 
57
58
  return result;
58
59
  } catch (error) {
59
- console.warn(`Translation error for key ${key}:`, error.message);
60
+ screen.debug(`Translation error for key ${key}: ` + error.message);
60
61
  return key; // Return key as fallback
61
62
  }
62
63
  }
@@ -6,6 +6,7 @@
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const os = require('os');
9
+ const screen = require('../ui/screen');
9
10
 
10
11
  class LanguageManager {
11
12
  constructor() {
@@ -83,7 +84,7 @@ class LanguageManager {
83
84
  this.languageCache.set(this.currentLanguage, languagePack);
84
85
  return languagePack;
85
86
  } catch (error) {
86
- console.warn(`Failed to load language pack for ${this.currentLanguage}, falling back to English. Error: ${error.message}`);
87
+ screen.debug(`Failed to load language pack for ${this.currentLanguage}, falling back to English. Error: ${error.message}`);
87
88
 
88
89
  // Fallback to English
89
90
  if (this.currentLanguage !== 'en') {
@@ -118,7 +119,7 @@ class LanguageManager {
118
119
 
119
120
  fs.writeFileSync(this.configFile, JSON.stringify(config, null, 2));
120
121
  } catch (error) {
121
- console.warn('Failed to save language preference:', error.message);
122
+ screen.debug('Failed to save language preference: ' + error.message);
122
123
  }
123
124
  }
124
125
 
@@ -134,7 +135,7 @@ class LanguageManager {
134
135
  }
135
136
  }
136
137
  } catch (error) {
137
- console.warn('Failed to load language preference, using default (English)');
138
+ screen.debug('Failed to load language preference, using default (English)');
138
139
  }
139
140
  }
140
141
 
@@ -10,10 +10,11 @@ module.exports = {
10
10
  title: "Hauptmenü",
11
11
  launch_default: "Claude Code starten",
12
12
  launch_skip: "Claude Code starten (Berechtigungsprüfung überspringen)",
13
+ launch_auto_mode: "Claude Code starten (Auto-Modus aktivieren)",
13
14
  launch_api: "Claude Code mit Drittanbieter-API starten",
14
15
  launch_api_skip: "Claude Code mit Drittanbieter-API starten (Berechtigungsprüfung überspringen)",
15
16
  api_management: "Drittanbieter-API-Verwaltung",
16
- language_settings: "Spracheinstellungen",
17
+ config_management: "Konfigurationsverwaltung",
17
18
  version_check: "Versionsaktualisierung prüfen",
18
19
  exit: "Beenden"
19
20
  },
@@ -21,11 +22,26 @@ module.exports = {
21
22
  title: "Drittanbieter-API-Verwaltung",
22
23
  add_new: "Neue Drittanbieter-API hinzufügen",
23
24
  remove: "API entfernen",
25
+ edit: "Edit API",
24
26
  switch: "Aktive API wechseln",
25
27
  statistics: "API-Statistiken anzeigen",
26
28
  export: "Konfiguration exportieren",
27
29
  import: "Konfiguration importieren",
28
30
  change_password: "Passwort ändern",
31
+ manual_upgrade: "Manuelles Modell-Upgrade",
32
+ back: "Zurück zum Hauptmenü"
33
+ },
34
+ config: {
35
+ title: "Konfigurationsverwaltung",
36
+ language: "Spracheinstellungen",
37
+ auto_model_upgrade: "Automatisches Modell-Upgrade",
38
+ model_upgrade_notification: "Modell-Upgrade-Benachrichtigung",
39
+ telemetry: "Anthropic Telemetrie",
40
+ api_launch_mode: "Drittanbieter-API-Startmodus",
41
+ back: "Zurück zum Hauptmenü"
42
+ },
43
+ api_select: {
44
+ title: "Wählen Sie eine API zum Starten:",
29
45
  back: "Zurück zum Hauptmenü"
30
46
  },
31
47
  remove_api: {
@@ -203,6 +219,23 @@ module.exports = {
203
219
  remove_confirm: "Zu entfernende API: {0}",
204
220
  cannot_undo: "Diese Aktion kann nicht rückgängig gemacht werden!",
205
221
  removed_info: "Entfernt: {0}"
222
+ },
223
+ edit: {
224
+ select_api: 'Select API to edit',
225
+ current_value: 'Current value: {0}',
226
+ new_value: 'New value: ',
227
+ success: '✅ {0} updated successfully',
228
+ cancelled: 'Edit cancelled',
229
+ back: 'Back',
230
+ field_name: 'Name',
231
+ field_provider: 'Provider',
232
+ field_base_url: 'Base URL',
233
+ field_model: 'Model',
234
+ name_required: 'Name cannot be empty when editing',
235
+ duplicate: 'This change would create a duplicate configuration',
236
+ provider_url_mismatch: 'Provider and URL may be inconsistent',
237
+ provider_url_mismatch_detail: 'Provider: {0} / URL suggests: {1}',
238
+ url_provider_hint: "URL matches provider '{0}' but current provider is '{1}'. Consider updating Provider field."
206
239
  }
207
240
  },
208
241
 
@@ -276,6 +309,10 @@ module.exports = {
276
309
  good: "Gut",
277
310
  strong: "Stark",
278
311
  very_strong: "Sehr stark"
312
+ },
313
+ guard: {
314
+ delete: { header: '🗑️ Remove API — Password required to verify identity' },
315
+ edit: { header: '✏️ Edit API — Password required to verify identity' }
279
316
  }
280
317
  },
281
318
 
@@ -323,15 +360,22 @@ module.exports = {
323
360
 
324
361
  // Navigation und UI
325
362
  navigation: {
326
- use_arrows: "Verwenden Sie ↑↓ Pfeiltasten zum Navigieren, Enter zum Auswählen, doppelt Ctrl+C zum Beenden",
327
- use_arrows_esc: "Verwenden Sie ↑↓ zum Navigieren, Enter zum {0}, ESC zum Hauptmenü zurückkehren",
363
+ use_arrows: "Verwenden Sie ↑↓ Pfeiltasten zum Navigieren, Enter/Leertaste zum Auswählen, doppelt Ctrl+C zum Beenden",
364
+ use_arrows_esc: "Verwenden Sie ↑↓ zum Navigieren, Enter zum {0}, ESC zum Abbrechen",
365
+ use_arrows_page_esc: "←→ Page {0}/{1}, ↑↓ to navigate, Enter to {2}, ESC to cancel",
328
366
  use_number_keys: "Verwenden Sie Zahlentasten zum Auswählen:",
329
367
  currently_active: "Derzeit aktive API",
330
368
  select_action: "Aktion auswählen:",
331
369
  no_options: "Keine Optionen verfügbar",
332
370
  enter_choice: "Geben Sie Ihre Wahl ein ({0}, oder beliebige andere Taste zum Hauptmenü zurückkehren):",
333
371
  arrow_keys_not_available: "Pfeiltasten nicht verfügbar. Geben Sie Auswahlnummer ein (1-{0}):",
334
- enter_choice_prompt: "[>] Geben Sie Ihre Wahl ein (1-2, oder beliebige andere Taste zum Hauptmenü zurückkehren): "
372
+ enter_choice_prompt: "[>] Geben Sie Ihre Wahl ein (1-2, oder beliebige andere Taste zum Hauptmenü zurückkehren): ",
373
+ action: {
374
+ edit: 'edit',
375
+ remove: 'remove',
376
+ switch: 'switch',
377
+ select: 'select'
378
+ }
335
379
  },
336
380
 
337
381
  // Start-Prozess
@@ -572,19 +616,15 @@ module.exports = {
572
616
  model_upgrade: {
573
617
  notification: "Modell-Upgrade verfügbar: {0} → {1}",
574
618
  notification_api: "API: {0}",
575
- notification_hint: "Gehen Sie zu \"Drittanbieter-API-Verwaltung > Modell-Upgrade-Einstellungen\" zum Upgraden",
619
+ notification_hint: "Auto-Upgrade: \"Konfigurationsverwaltung\" / Manuell: \"Drittanbieter-API-Verwaltung > Manuelles Modell-Upgrade\"",
576
620
  auto_upgraded: "Modell automatisch upgegradet: {0} → {1}",
577
621
 
578
- settings_title: "Modell-Upgrade-Einstellungen",
579
622
  current_config: "Aktuelle Konfiguration",
580
623
  auto_upgrade_label: "Neuestes Modell automatisch verwenden",
581
624
  auto_upgrade_on: "AN",
582
625
  auto_upgrade_off: "AUS",
583
626
 
584
- menu_toggle_auto_on: "Auto-Upgrade [● AN]",
585
- menu_toggle_auto_off: "Auto-Upgrade [○ AUS]",
586
627
  menu_manual_upgrade: "Alle Modelle manuell upgraden",
587
- menu_back: "Zurück",
588
628
 
589
629
  manual_title: "Modell-Upgrade-Prüfung",
590
630
  manual_checking: "{0} API-Konfigurationen werden geprüft...",
@@ -599,5 +639,49 @@ module.exports = {
599
639
  manual_complete: "Upgrade abgeschlossen!",
600
640
  manual_stats_upgraded: "Upgegradet: {0}",
601
641
  manual_stats_skipped: "Übersprungen: {0} ({1} bereits aktuell, {2} keine Upgrade-Info)"
642
+ },
643
+ hints: {
644
+ auto_mode_info: 'Nach dem Start Shift+Tab drücken, um in den automatischen Ausführungsmodus zu wechseln',
645
+ active_api_info: 'Aktiv: {0} / {1}',
646
+ no_active_api: 'Keine aktive API konfiguriert. Gehen Sie zur "API-Verwaltung", um eine hinzuzufügen.',
647
+ direct_mode_desc: 'Direktstartmodus, startet sofort mit der aktiven API',
648
+ direct_mode_api_info: 'API: {0} | Anbieter: {1}',
649
+ direct_mode_api_detail: 'Modell: {0} | Zuletzt verwendet: {1}',
650
+ direct_mode_change: 'Startmodus kann in "Konfigurationsverwaltung" geändert werden',
651
+ direct_mode_no_active: 'Direktstartmodus, aber keine aktive API ausgewählt',
652
+ direct_mode_no_active_detail: '{0} APIs konfiguriert, bitte wählen Sie eine in "Drittanbieter-API-Verwaltung"',
653
+ select_mode_desc: 'Auswahlmodus, wählen Sie eine API aus der Liste vor dem Start',
654
+ select_mode_change: 'Startmodus kann in "Konfigurationsverwaltung" geändert werden',
655
+ select_mode_api_count: '{0} APIs konfiguriert, aktiv: {1}',
656
+ select_mode_active_none: 'keine',
657
+ no_api_configured: 'Keine Drittanbieter-APIs konfiguriert. Fügen Sie zuerst eine in "Drittanbieter-API-Verwaltung" hinzu',
658
+ api_management_info: '{0} APIs konfiguriert, aktiv: {1}',
659
+ config_summary: 'Sprache: {0} | Startmodus: {1} | Telemetrie: {2}',
660
+ edit_password_required: '🔒 Password verification required to edit API configuration',
661
+ remove_password_required: '🔒 Password verification required to remove API',
662
+ export_password_required: '🔒 Password verification required to export configuration',
663
+ import_password_required: '🔒 Password verification required to import configuration',
664
+ config: {
665
+ language: 'Anzeigesprache wechseln, aktuell: {0}',
666
+ auto_upgrade: 'Modellversionen für Drittanbieter-APIs automatisch erkennen und upgraden',
667
+ upgrade_notification: 'Modell-Upgrade-Benachrichtigung oben im Hauptmenü anzeigen',
668
+ telemetry: 'Injiziert DISABLE_TELEMETRY=1 wenn deaktiviert. Empfohlen: AUS',
669
+ launch_mode: 'Direkt: mit aktiver API starten / Auswahl: zuerst aus Liste wählen'
670
+ },
671
+ api_select: {
672
+ info: 'API: {0}',
673
+ detail: 'Anbieter: {0} | Modell: {1}',
674
+ usage: 'Nutzung: {0} mal | Zuletzt verwendet: {1}'
675
+ }
676
+ },
677
+
678
+ config: {
679
+ values: {
680
+ on: 'AN',
681
+ off: 'AUS',
682
+ direct_mode: 'Direktmodus',
683
+ select_mode: 'Auswahlmodus',
684
+ recommended_off: 'AUS (Empfohlen)'
685
+ }
602
686
  }
603
687
  };