claude-key-manager
Version:
🚀 Claude Key Manager 4.1.3 - 修复密钥验证问题,支持更多密钥格式
428 lines (372 loc) • 12.2 kB
JavaScript
const inquirer = require('inquirer');
const chalk = require('chalk');
const boxen = require('boxen');
const ora = require('ora');
const VersionManager = require('./version-manager');
class InteractiveMenu {
constructor(keyManager) {
this.keyManager = keyManager;
this.versionManager = new VersionManager();
}
async start() {
console.clear();
console.log(boxen(
chalk.bold.blue('🚀 Claude Key Manager 4.0\n') +
chalk.gray('纯Node.js重构版 - 交互式菜单模式'),
{
padding: 1,
borderColor: 'blue',
borderStyle: 'round'
}
));
while (true) {
try {
const action = await this.showMainMenu();
if (action === 'exit') break;
await this.handleAction(action);
// 操作完成后暂停,让用户看到结果
await inquirer.prompt([{
type: 'input',
name: 'continue',
message: chalk.gray('按回车键继续...')
}]);
} catch (error) {
console.log(chalk.red('\n❌ 错误:'), error.message);
await inquirer.prompt([{
type: 'input',
name: 'continue',
message: chalk.gray('按回车键继续...')
}]);
}
}
console.log(chalk.green('\n👋 再见!'));
}
async showMainMenu() {
// 获取当前状态信息
const currentKey = await this.keyManager.getCurrentKey();
const allKeys = await this.keyManager.listKeys();
const statusInfo = [
`📊 状态信息:`,
` 密钥总数: ${allKeys.length}`,
` 当前密钥: ${currentKey ? chalk.green(currentKey.name) : chalk.yellow('未设置')}`,
''
].join('\n');
console.log('\n' + boxen(statusInfo, {
padding: { top: 0, bottom: 0, left: 1, right: 1 },
borderColor: 'gray'
}));
const choices = [
{
name: '📝 添加新的API密钥',
value: 'add',
short: 'Add'
},
{
name: `📋 列出所有密钥 (${allKeys.length}个)`,
value: 'list',
short: 'List'
},
{
name: '🔄 切换当前使用的密钥',
value: 'switch',
short: 'Switch',
disabled: allKeys.length === 0 ? '没有可用密钥' : false
},
{
name: '🗑️ 删除密钥 (你要的功能!)',
value: 'delete',
short: 'Delete',
disabled: allKeys.length === 0 ? '没有可删除的密钥' : false
},
{
name: '👁️ 查看当前密钥详情',
value: 'current',
short: 'Current',
disabled: !currentKey ? '没有当前密钥' : false
},
{
name: '🔧 高级选项...',
value: 'advanced',
short: 'Advanced'
},
new inquirer.Separator(),
{
name: '📋 版本信息和更新检查',
value: 'version',
short: 'Version'
},
{
name: '🔄 检查并更新到最新版本',
value: 'update',
short: 'Update'
},
{
name: '❓ 显示帮助信息',
value: 'help',
short: 'Help'
},
{
name: '🚪 退出',
value: 'exit',
short: 'Exit'
}
];
const { action } = await inquirer.prompt([{
type: 'list',
name: 'action',
message: '选择要执行的操作:',
choices: choices,
pageSize: 12
}]);
return action;
}
async handleAction(action) {
switch (action) {
case 'add':
await this.handleAddKey();
break;
case 'list':
await this.handleListKeys();
break;
case 'switch':
await this.handleSwitchKey();
break;
case 'delete':
await this.handleDeleteKey();
break;
case 'current':
await this.handleShowCurrent();
break;
case 'advanced':
await this.handleAdvanced();
break;
case 'version':
await this.handleVersion();
break;
case 'update':
await this.handleUpdate();
break;
case 'help':
await this.handleHelp();
break;
}
}
async handleAddKey() {
console.log(chalk.blue('\n📝 添加新的API密钥\n'));
const answers = await inquirer.prompt([
{
type: 'input',
name: 'key',
message: '请输入Claude API密钥:',
validate: (input) => {
if (!input.trim()) return '密钥不能为空';
if (!input.startsWith('sk-ant-')) return '密钥应该以 sk-ant- 开头';
if (input.length < 20) return '密钥长度太短';
return true;
}
},
{
type: 'input',
name: 'name',
message: '为此密钥设置名称:',
default: 'default',
validate: (input) => input.trim() ? true : '名称不能为空'
},
{
type: 'input',
name: 'description',
message: '密钥描述 (可选):',
default: ''
},
{
type: 'input',
name: 'apiUrl',
message: 'API Base URL (按回车使用默认):',
default: 'https://api.anthropic.com',
validate: (input) => {
if (!input.trim()) return true; // 允许空值,会使用默认
try {
new URL(input.trim());
return true;
} catch {
return '请输入有效的URL格式 (例如: https://api.anthropic.com)';
}
}
}
]);
const spinner = ora('正在保存密钥...').start();
try {
await this.keyManager.addKey(
answers.key.trim(),
answers.name.trim(),
answers.description.trim(),
answers.apiUrl.trim()
);
spinner.succeed(chalk.green('✅ 密钥添加成功!'));
} catch (error) {
spinner.fail(chalk.red('❌ 添加失败'));
throw error;
}
}
async handleListKeys() {
console.log(chalk.blue('\n📋 所有已保存的密钥\n'));
const spinner = ora('正在加载密钥列表...').start();
const keys = await this.keyManager.listKeys(true);
spinner.stop();
if (keys.length === 0) {
console.log(chalk.yellow('⚠️ 没有找到任何密钥'));
return;
}
console.log(boxen(
keys.map(k => {
const status = k.active ? chalk.green('👉 当前') : ' ';
const name = chalk.bold(k.name);
const desc = k.description ? chalk.gray(` - ${k.description}`) : '';
const created = chalk.gray(`\n 创建: ${new Date(k.createdAt).toLocaleString()}`);
const apiUrl = chalk.blue(`\n API: ${k.apiUrl}`);
const preview = k.preview ? chalk.gray(`\n 预览: ${k.preview}`) : '';
return `${status} ${name}${desc}${created}${apiUrl}${preview}`;
}).join('\n\n'),
{
padding: 1,
borderColor: 'blue',
title: `📋 密钥列表 (${keys.length}个)`,
titleAlignment: 'center'
}
));
}
async handleSwitchKey() {
console.log(chalk.blue('\n🔄 切换当前使用的密钥\n'));
const keys = await this.keyManager.listKeys();
const activeKey = await this.keyManager.getCurrentKey();
const choices = keys.map(k => ({
name: `${k.name}${k.description ? ` - ${k.description}` : ''}${k.active ? chalk.green(' (当前)') : ''}`,
value: k.name,
disabled: k.active ? '已经是当前密钥' : false
}));
if (choices.filter(c => !c.disabled).length === 0) {
console.log(chalk.yellow('⚠️ 没有可切换的密钥'));
return;
}
const { selectedKey } = await inquirer.prompt([{
type: 'list',
name: 'selectedKey',
message: '选择要切换到的密钥:',
choices: choices
}]);
const spinner = ora(`正在切换到 ${selectedKey}...`).start();
try {
await this.keyManager.switchKey(selectedKey);
spinner.succeed(chalk.green(`✅ 已切换到密钥: ${selectedKey}`));
} catch (error) {
spinner.fail(chalk.red('❌ 切换失败'));
throw error;
}
}
async handleDeleteKey() {
console.log(chalk.blue('\n🗑️ 删除密钥\n'));
const keys = await this.keyManager.listKeys();
const choices = keys.map(k => ({
name: `${k.name}${k.description ? ` - ${k.description}` : ''}${k.active ? chalk.red(' (当前使用中!)') : ''}`,
value: k.name
}));
const { keyToDelete } = await inquirer.prompt([{
type: 'list',
name: 'keyToDelete',
message: '选择要删除的密钥:',
choices: choices
}]);
// 二次确认 - 特别重要!
const { confirmDelete } = await inquirer.prompt([{
type: 'confirm',
name: 'confirmDelete',
message: chalk.red(`⚠️ 确定要删除密钥 '${keyToDelete}' 吗?此操作不可撤销!`),
default: false
}]);
if (!confirmDelete) {
console.log(chalk.yellow('❌ 删除操作已取消'));
return;
}
const spinner = ora(`正在删除 ${keyToDelete}...`).start();
try {
await this.keyManager.deleteKey(keyToDelete, true); // 强制删除,因为已经确认过了
spinner.succeed(chalk.green(`✅ 密钥 '${keyToDelete}' 删除成功`));
} catch (error) {
spinner.fail(chalk.red('❌ 删除失败'));
throw error;
}
}
async handleShowCurrent() {
console.log(chalk.blue('\n👁️ 当前密钥详情\n'));
const current = await this.keyManager.getCurrentKey();
const info = [
`🔑 名称: ${chalk.bold(current.name)}`,
`📝 描述: ${current.description || chalk.gray('无')}`,
`🔍 预览: ${current.preview}`,
`⏰ 完整密钥长度: ${current.key.length} 字符`
].join('\n');
console.log(boxen(info, {
padding: 1,
borderColor: 'green',
title: '当前密钥',
titleAlignment: 'center'
}));
}
async handleAdvanced() {
const { advancedAction } = await inquirer.prompt([{
type: 'list',
name: 'advancedAction',
message: '高级选项:',
choices: [
{ name: '🔍 密钥验证和测试', value: 'validate' },
{ name: '📊 使用统计', value: 'stats' },
{ name: '🔧 清理无效密钥', value: 'cleanup' },
{ name: '↩️ 返回主菜单', value: 'back' }
]
}]);
if (advancedAction === 'back') return;
// 实现高级功能...
console.log(chalk.yellow(`🚧 功能 "${advancedAction}" 正在开发中...`));
}
async handleVersion() {
console.log(chalk.blue('\n📋 版本信息和更新检查\n'));
await this.versionManager.showVersionInfo();
}
async handleUpdate() {
console.log(chalk.blue('\n🔄 检查并更新到最新版本\n'));
await this.versionManager.updateHelper();
}
async handleHelp() {
const helpText = `
${chalk.bold('🚀 Claude Key Manager 4.0 使用指南')}
${chalk.blue('基本命令:')}
claude-key add <key> 添加新密钥
claude-key list 列出所有密钥
claude-key switch <name> 切换密钥
claude-key delete <name> 删除密钥
claude-key current 显示当前密钥
${chalk.blue('版本管理:')}
claude-key version 显示版本信息并检查更新
claude-key update 更新到最新版本
${chalk.blue('交互模式:')}
claude-key 启动后输入 / 进入交互模式
claude-key interactive 直接进入交互模式
${chalk.blue('别名:')}
ck claude-key 的简短别名
${chalk.blue('特色功能:')}
• 🔒 安全的系统密钥存储 (keytar)
• 🎯 直观的交互式菜单
• 🗑️ 安全删除 (二次确认)
• 📊 密钥使用统计
• 🔄 自动版本检查和更新
• 🚀 纯Node.js,无需编译
${chalk.gray('提示: 在任何界面输入 Ctrl+C 退出')}
`;
console.log(boxen(helpText, {
padding: 1,
borderColor: 'cyan',
borderStyle: 'round'
}));
}
}
module.exports = InteractiveMenu;