diff --git a/bin/cli.js b/bin/cli.js index f9c9905..0d58710 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -5,25 +5,17 @@ const fs = require('fs'); const path = require('path'); const os = require('os'); const readline = require('readline'); -const { checkClaudeInPath, installClaudeCode, getAuthToken, getBaseUrl, getModel, getSmallFastModel, getOpusModel, updateClaudeSettings, readConfig, getProfileConfig, addProfile, ensureDefaultProfileKimiDefaults, updateClaudeEnvFile, resetClaudeEnvFile, CLAUDE_ENV_KEYS } = require('../lib/utils'); +const { checkClaudeInPath, installClaudeCode, getAuthToken, getBaseUrl, getModel, getSmallFastModel, getOpusModel, updateClaudeSettings, readConfig, getProfileConfig, addProfile, ensureDefaultProfileKimiDefaults, updateClaudeEnvFile, resetClaudeEnvFile, CLAUDE_ENV_KEYS, promptForPassword } = require('../lib/utils'); const { parseProviderFlag, getProviderBySlug } = require('../lib/providers'); const { version } = require('../package.json'); const CONFIG_FILE = path.join(os.homedir(), '.kimicc.json'); const CLAUDE_ENV_FILE = path.join(os.homedir(), '.claude', 'settings.json'); -function promptForTokenForProvider(providerName) { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); +async function promptForTokenForProvider(providerName) { const name = providerName || 'provider'; - return new Promise((resolve) => { - rl.question(`Enter API Auth Token for ${name}: `, (authToken) => { - rl.close(); - resolve((authToken || '').trim()); - }); - }); + const authToken = await promptForPassword(`Enter API Auth Token for ${name}: `); + return (authToken || '').trim(); } async function handleResetCommand() { diff --git a/lib/utils.js b/lib/utils.js index 97c28f9..739bcf7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -164,20 +164,77 @@ function resetClaudeEnvFile(keys = CLAUDE_ENV_KEYS) { return applyClaudeEnvUpdate({ remove: keys }); } -async function promptForAuthToken() { +async function promptForPassword(promptText = 'Please enter your API Auth Token: ') { const rl = readline.createInterface({ input: process.stdin, - output: process.stdout + output: null // Hide input }); return new Promise((resolve) => { - rl.question('Please enter your API Auth Token: ', (authToken) => { - rl.close(); - resolve(authToken.trim()); + let password = ''; + let asterisks = ''; + + // Write the prompt + process.stdout.write(promptText); + + // Handle keypress events for password masking + process.stdin.on('keypress', (char, key) => { + if (key && key.name === 'return') { + // Enter key pressed + process.stdout.write('\n'); + process.stdin.removeAllListeners('keypress'); + rl.close(); + resolve(password); + return; + } + + if (key && key.name === 'backspace') { + // Backspace key pressed + if (password.length > 0) { + password = password.slice(0, -1); + asterisks = asterisks.slice(0, -1); + // Remove last asterisk + process.stdout.write('\b \b'); + } + return; + } + + if (key && key.ctrl && key.name === 'c') { + // Ctrl+C pressed + process.stdout.write('\n'); + process.stdin.removeAllListeners('keypress'); + rl.close(); + process.exit(0); + } + + if (char && char.length === 1) { + // Regular character pressed + password += char; + asterisks += '*'; + process.stdout.write('*'); + } + }); + + // Set raw mode to capture individual keypresses + if (process.stdin.isTTY) { + process.stdin.setRawMode(true); + } + + // Clean up on exit + rl.on('close', () => { + process.stdin.removeAllListeners('keypress'); + if (process.stdin.isTTY) { + process.stdin.setRawMode(false); + } }); }); } +async function promptForAuthToken() { + const authToken = await promptForPassword('Please enter your API Auth Token: '); + return authToken.trim(); +} + function getProfileConfig(config, profileName) { if (!config.profiles || !config.profiles[profileName]) { return null; @@ -605,5 +662,6 @@ module.exports = { updateClaudeEnvFile, resetClaudeEnvFile, readClaudeConfig, + promptForPassword, CLAUDE_ENV_KEYS };