@kikkimo/claude-launcher 2.5.0 → 3.1.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/CHANGELOG.md +70 -0
- package/README.md +23 -13
- package/claude-launcher +1244 -432
- package/docs/README-zh.md +23 -13
- package/lib/api-manager.js +629 -70
- package/lib/auth/password-input.js +8 -4
- package/lib/auth/password-validator.js +83 -48
- package/lib/i18n/index.js +4 -3
- package/lib/i18n/language-manager.js +4 -3
- package/lib/i18n/locales/de.js +229 -13
- package/lib/i18n/locales/en.js +235 -13
- package/lib/i18n/locales/es.js +229 -13
- package/lib/i18n/locales/fr.js +229 -13
- package/lib/i18n/locales/it.js +229 -13
- package/lib/i18n/locales/ja.js +229 -13
- package/lib/i18n/locales/ko.js +229 -13
- package/lib/i18n/locales/pt.js +229 -13
- package/lib/i18n/locales/ru.js +229 -13
- package/lib/i18n/locales/zh-TW.js +229 -13
- package/lib/i18n/locales/zh.js +235 -13
- package/lib/launcher.js +167 -110
- package/lib/presets/providers.js +143 -39
- package/lib/ui/api-editor.js +668 -0
- package/lib/ui/i18n-labels.js +16 -0
- package/lib/ui/interactive-table.js +216 -99
- package/lib/ui/menu.js +79 -62
- package/lib/ui/prompts.js +168 -139
- package/lib/ui/screen.js +125 -0
- package/lib/utils/stdin-manager.js +11 -9
- package/lib/utils/version-checker.js +65 -4
- package/lib/validators.js +102 -1
- package/package.json +2 -2
- package/docs/superpowers/plans/2026-03-31-update-models-and-auto-mode.md +0 -1414
- package/docs/superpowers/specs/2026-03-31-update-models-and-auto-mode-design.md +0 -187
|
@@ -11,6 +11,7 @@ const EventEmitter = require('events');
|
|
|
11
11
|
const crypto = require('crypto');
|
|
12
12
|
const colors = require('../ui/colors');
|
|
13
13
|
const i18n = require('../i18n');
|
|
14
|
+
const screen = require('../ui/screen');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Generate a cryptographically unique ID
|
|
@@ -111,7 +112,7 @@ class StdinManager extends EventEmitter {
|
|
|
111
112
|
*/
|
|
112
113
|
_debug(...args) {
|
|
113
114
|
if (this.debugMode) {
|
|
114
|
-
|
|
115
|
+
screen.debug('[StdinManager] ' + args.join(' '));
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -389,8 +390,8 @@ class StdinManager extends EventEmitter {
|
|
|
389
390
|
// Clear the warning text only in TTY mode to avoid spamming ANSI codes
|
|
390
391
|
if (process.stdout && process.stdout.isTTY) {
|
|
391
392
|
// Clear the warning text (2 lines: empty line + warning line)
|
|
392
|
-
|
|
393
|
-
|
|
393
|
+
screen.write('\x1b[1A\r\x1b[K'); // Up 1 line, clear warning
|
|
394
|
+
screen.write('\x1b[1A\r\x1b[K'); // Up 1 line, clear empty line
|
|
394
395
|
}
|
|
395
396
|
this._debug('Ctrl+C cancelled by user input');
|
|
396
397
|
}
|
|
@@ -432,8 +433,8 @@ class StdinManager extends EventEmitter {
|
|
|
432
433
|
// Clear the warning text only in TTY mode to avoid spamming ANSI codes
|
|
433
434
|
if (process.stdout && process.stdout.isTTY) {
|
|
434
435
|
// Clear the warning text (2 lines: empty line + warning line)
|
|
435
|
-
|
|
436
|
-
|
|
436
|
+
screen.write('\x1b[1A\r\x1b[K'); // Up 1 line, clear warning
|
|
437
|
+
screen.write('\x1b[1A\r\x1b[K'); // Up 1 line, clear empty line
|
|
437
438
|
}
|
|
438
439
|
this._debug('Ctrl+C timer expired - warning cleared, returning to normal operation');
|
|
439
440
|
}
|
|
@@ -464,8 +465,8 @@ class StdinManager extends EventEmitter {
|
|
|
464
465
|
|
|
465
466
|
if (this.ctrlCCount === 1) {
|
|
466
467
|
// First Ctrl+C - show warning
|
|
467
|
-
|
|
468
|
-
|
|
468
|
+
screen.write('\n');
|
|
469
|
+
screen.write(colors.yellow + '⚠️ ' + i18n.tSync('messages.prompts.ctrl_c_again') + colors.reset + '\n');
|
|
469
470
|
|
|
470
471
|
// Clear any existing timer
|
|
471
472
|
if (this.ctrlCTimer) {
|
|
@@ -486,8 +487,8 @@ class StdinManager extends EventEmitter {
|
|
|
486
487
|
this.ctrlCTimer = null;
|
|
487
488
|
}
|
|
488
489
|
|
|
489
|
-
|
|
490
|
-
|
|
490
|
+
screen.write('\n');
|
|
491
|
+
screen.write(colors.green + i18n.tSync('ui.general.goodbye') + colors.reset + '\n');
|
|
491
492
|
|
|
492
493
|
// Clean up stdin before exit
|
|
493
494
|
// Use clearAll=true since we're exiting anyway
|
|
@@ -497,6 +498,7 @@ class StdinManager extends EventEmitter {
|
|
|
497
498
|
// Ignore cleanup errors during exit
|
|
498
499
|
}
|
|
499
500
|
|
|
501
|
+
screen.exit();
|
|
500
502
|
process.exit(0);
|
|
501
503
|
}
|
|
502
504
|
}
|
|
@@ -27,17 +27,34 @@ async function loadConfig() {
|
|
|
27
27
|
// Add new fields for backward compatibility
|
|
28
28
|
if (config.autoModelUpgrade === undefined) config.autoModelUpgrade = false;
|
|
29
29
|
if (config.lastModelUpgradeCheck === undefined) config.lastModelUpgradeCheck = 0;
|
|
30
|
+
if (config.disableTelemetry === undefined) config.disableTelemetry = true;
|
|
31
|
+
if (config.showModelUpgradeNotification === undefined) config.showModelUpgradeNotification = true;
|
|
32
|
+
if (config.apiLaunchMode === undefined) config.apiLaunchMode = 'direct';
|
|
33
|
+
if (config.noFlicker === undefined) config.noFlicker = true;
|
|
30
34
|
|
|
31
35
|
return config;
|
|
32
36
|
} catch (error) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
language: 'zh',
|
|
37
|
+
const defaultConfig = {
|
|
38
|
+
language: 'en',
|
|
36
39
|
lastVersionCheck: 0,
|
|
37
40
|
cachedLatestVersion: null,
|
|
38
41
|
autoModelUpgrade: false,
|
|
39
|
-
lastModelUpgradeCheck: 0
|
|
42
|
+
lastModelUpgradeCheck: 0,
|
|
43
|
+
disableTelemetry: true,
|
|
44
|
+
showModelUpgradeNotification: true,
|
|
45
|
+
apiLaunchMode: 'direct',
|
|
46
|
+
noFlicker: false,
|
|
40
47
|
};
|
|
48
|
+
|
|
49
|
+
// Write default config on first run so subsequent reads are consistent
|
|
50
|
+
try {
|
|
51
|
+
const configPath = getConfigPath();
|
|
52
|
+
await fs.writeFile(configPath, JSON.stringify(defaultConfig, null, 2), 'utf8');
|
|
53
|
+
} catch (_) {
|
|
54
|
+
// Silently fail
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return defaultConfig;
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
|
|
@@ -249,11 +266,55 @@ async function setAutoModelUpgrade(enabled) {
|
|
|
249
266
|
await saveConfig(config);
|
|
250
267
|
}
|
|
251
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Load configuration from file synchronously
|
|
271
|
+
* Used by launcher.js where async is not available
|
|
272
|
+
*/
|
|
273
|
+
function loadConfigSync() {
|
|
274
|
+
try {
|
|
275
|
+
const fsSync = require('fs');
|
|
276
|
+
const data = fsSync.readFileSync(getConfigPath(), 'utf8');
|
|
277
|
+
const config = JSON.parse(data);
|
|
278
|
+
|
|
279
|
+
if (config.autoModelUpgrade === undefined) config.autoModelUpgrade = false;
|
|
280
|
+
if (config.lastModelUpgradeCheck === undefined) config.lastModelUpgradeCheck = 0;
|
|
281
|
+
if (config.disableTelemetry === undefined) config.disableTelemetry = true;
|
|
282
|
+
if (config.showModelUpgradeNotification === undefined) config.showModelUpgradeNotification = true;
|
|
283
|
+
if (config.apiLaunchMode === undefined) config.apiLaunchMode = 'direct';
|
|
284
|
+
if (config.noFlicker === undefined) config.noFlicker = true;
|
|
285
|
+
|
|
286
|
+
return config;
|
|
287
|
+
} catch (_) {
|
|
288
|
+
const defaultConfig = {
|
|
289
|
+
language: 'en',
|
|
290
|
+
lastVersionCheck: 0,
|
|
291
|
+
cachedLatestVersion: null,
|
|
292
|
+
autoModelUpgrade: false,
|
|
293
|
+
lastModelUpgradeCheck: 0,
|
|
294
|
+
disableTelemetry: true,
|
|
295
|
+
showModelUpgradeNotification: true,
|
|
296
|
+
apiLaunchMode: 'direct',
|
|
297
|
+
noFlicker: false,
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Write default config on first run so subsequent reads are consistent
|
|
301
|
+
try {
|
|
302
|
+
const fsSync = require('fs');
|
|
303
|
+
fsSync.writeFileSync(getConfigPath(), JSON.stringify(defaultConfig, null, 2), 'utf8');
|
|
304
|
+
} catch (__) {
|
|
305
|
+
// Silently fail
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return defaultConfig;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
252
312
|
module.exports = {
|
|
253
313
|
checkForUpdates,
|
|
254
314
|
forceCheckForUpdates,
|
|
255
315
|
clearCache,
|
|
256
316
|
loadConfig,
|
|
317
|
+
loadConfigSync,
|
|
257
318
|
saveConfig,
|
|
258
319
|
setAutoModelUpgrade
|
|
259
320
|
};
|
package/lib/validators.js
CHANGED
|
@@ -120,11 +120,112 @@ function maskApiToken(token) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// ============================================================
|
|
124
|
+
// Environment variable constants & validators
|
|
125
|
+
// ============================================================
|
|
126
|
+
|
|
127
|
+
const RESERVED_ENV_KEYS = [
|
|
128
|
+
'ANTHROPIC_BASE_URL',
|
|
129
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
130
|
+
'ANTHROPIC_API_KEY',
|
|
131
|
+
'ANTHROPIC_MODEL',
|
|
132
|
+
'ANTHROPIC_SMALL_FAST_MODEL',
|
|
133
|
+
'CLAUDE_CODE_OAUTH_TOKEN',
|
|
134
|
+
'DISABLE_TELEMETRY',
|
|
135
|
+
'CLAUDE_CODE_NO_FLICKER',
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const PREDEFINED_RUNTIME_KEYS = [
|
|
139
|
+
'API_TIMEOUT_MS',
|
|
140
|
+
'CLAUDE_CODE_ATTRIBUTION_HEADER',
|
|
141
|
+
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
|
|
142
|
+
'CLAUDE_CODE_EFFORT_LEVEL',
|
|
143
|
+
'CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS',
|
|
144
|
+
'CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK',
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const PREDEFINED_MODEL_ENV_KEYS = [
|
|
148
|
+
'ANTHROPIC_CUSTOM_MODEL_OPTION',
|
|
149
|
+
'ANTHROPIC_CUSTOM_MODEL_OPTION_NAME',
|
|
150
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
151
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
152
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
153
|
+
'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const TYPE_A_FIELDS = [
|
|
157
|
+
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
|
|
158
|
+
'CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS',
|
|
159
|
+
'CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK',
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const TYPE_B_FIELDS = [
|
|
163
|
+
'CLAUDE_CODE_ATTRIBUTION_HEADER',
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const ALL_PREDEFINED_KEYS = new Set([
|
|
167
|
+
...RESERVED_ENV_KEYS,
|
|
168
|
+
...PREDEFINED_RUNTIME_KEYS,
|
|
169
|
+
...PREDEFINED_MODEL_ENV_KEYS,
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
function validateEnvKey(key) {
|
|
173
|
+
if (typeof key !== 'string' || key.trim() === '') {
|
|
174
|
+
return { valid: false, error: 'custom_env_key_empty' };
|
|
175
|
+
}
|
|
176
|
+
if (ALL_PREDEFINED_KEYS.has(key.trim())) {
|
|
177
|
+
return { valid: false, error: 'custom_env_key_reserved' };
|
|
178
|
+
}
|
|
179
|
+
return { valid: true, value: key.trim() };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function validateTypeATriState(value) {
|
|
183
|
+
if (value === '' || value === '1' || value === 'off') {
|
|
184
|
+
return { valid: true, value };
|
|
185
|
+
}
|
|
186
|
+
return { valid: false, error: 'tri_state_type_a_invalid' };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function validateTypeBTriState(value) {
|
|
190
|
+
if (value === '' || value === '1' || value === '0') {
|
|
191
|
+
return { valid: true, value };
|
|
192
|
+
}
|
|
193
|
+
return { valid: false, error: 'tri_state_type_b_invalid' };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validateRuntimeEnvValue(key, value) {
|
|
197
|
+
if (typeof value !== 'string') {
|
|
198
|
+
return { valid: false, error: 'env_value_not_string' };
|
|
199
|
+
}
|
|
200
|
+
if (TYPE_A_FIELDS.includes(key)) return validateTypeATriState(value);
|
|
201
|
+
if (TYPE_B_FIELDS.includes(key)) return validateTypeBTriState(value);
|
|
202
|
+
if (key === 'API_TIMEOUT_MS') {
|
|
203
|
+
if (value === '') return { valid: true, value };
|
|
204
|
+
if (/^\d+$/.test(value) && parseInt(value, 10) > 0) return { valid: true, value };
|
|
205
|
+
return { valid: false, error: 'env_value_timeout_invalid' };
|
|
206
|
+
}
|
|
207
|
+
if (key === 'CLAUDE_CODE_EFFORT_LEVEL') {
|
|
208
|
+
if (value === '') return { valid: true, value };
|
|
209
|
+
if (['low','medium','high','xhigh','max','auto'].includes(value)) return { valid: true, value };
|
|
210
|
+
return { valid: false, error: 'env_value_effort_invalid' };
|
|
211
|
+
}
|
|
212
|
+
return { valid: true, value };
|
|
213
|
+
}
|
|
214
|
+
|
|
123
215
|
module.exports = {
|
|
124
216
|
validateBaseUrl,
|
|
125
217
|
validateAuthToken,
|
|
126
218
|
validateModel,
|
|
127
219
|
validateApiName,
|
|
128
220
|
maskSensitiveData,
|
|
129
|
-
maskApiToken
|
|
221
|
+
maskApiToken,
|
|
222
|
+
RESERVED_ENV_KEYS,
|
|
223
|
+
PREDEFINED_RUNTIME_KEYS,
|
|
224
|
+
PREDEFINED_MODEL_ENV_KEYS,
|
|
225
|
+
TYPE_A_FIELDS,
|
|
226
|
+
TYPE_B_FIELDS,
|
|
227
|
+
validateEnvKey,
|
|
228
|
+
validateTypeATriState,
|
|
229
|
+
validateTypeBTriState,
|
|
230
|
+
validateRuntimeEnvValue,
|
|
130
231
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kikkimo/claude-launcher",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Interactive launcher for Claude Code with beautiful Claude-style interface",
|
|
5
5
|
"main": "claude-launcher",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node claude-launcher",
|
|
11
|
-
"test": "node test/providers.test.js && node test/menu-hints.test.js",
|
|
11
|
+
"test": "node test/providers.test.js && node test/menu-hints.test.js && node test/version-checker.test.js && node test/api-manager.test.js && node test/launcher.test.js && node test/config-management.test.js && node test/api-select.test.js && node test/env-vars-validators.test.js && node test/env-vars-providers.test.js && node test/env-vars-config.test.js && node test/env-vars-migration.test.js && node test/env-vars-add-api.test.js && node test/env-vars-write-interfaces.test.js && node test/env-vars-import-export.test.js && node test/env-vars-ui.test.js",
|
|
12
12
|
"prepublishOnly": "echo \"Publishing claude-launcher...\"",
|
|
13
13
|
"postinstall": "echo \"Claude Launcher installed successfully! Run 'claude-launcher' to start.\"",
|
|
14
14
|
"publish:public": "npm publish --access public"
|