Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 4 additions & 12 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
68 changes: 63 additions & 5 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +167 to +183

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Password prompt never captures input

The new promptForPassword adds a process.stdin.on('keypress') handler but never enables keypress events on the stream. Because the readline interface is constructed with output: null, it is not in terminal mode and readline.emitKeypressEvents(process.stdin) is never invoked, so no keypress events fire and the promise is never resolved when the user hits Enter. As a result, any code path that requests an auth token (e.g., creating a provider profile) will hang indefinitely waiting for input.

Useful? React with 👍 / 👎.

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;
Expand Down Expand Up @@ -605,5 +662,6 @@ module.exports = {
updateClaudeEnvFile,
resetClaudeEnvFile,
readClaudeConfig,
promptForPassword,
CLAUDE_ENV_KEYS
};