UNPKG

claude-keys-manager

Version:

Claude Code API Key manager with template and repository management tools

245 lines (195 loc) 7.5 kB
#!/usr/bin/env node const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); const ora = require('ora'); const fetch = require('node-fetch'); const yauzl = require('yauzl'); class ClaudeCodeManager { constructor() { this.projectDir = process.cwd(); this.templatesDir = path.join(__dirname, 'templates'); } async downloadRepository(repoUrl, targetDir = null) { if (!repoUrl) { throw new Error('仓库URL不能为空'); } const spinner = ora('下载GitHub仓库...').start(); try { const { owner, repo } = this.parseGitHubUrl(repoUrl); const repoName = targetDir ? path.basename(targetDir) : repo; const downloadDir = targetDir || path.join(this.projectDir, repoName); // 检查目录是否已存在 if (await fs.pathExists(downloadDir)) { spinner.warn(`目录 ${downloadDir} 已存在,跳过下载`); return downloadDir; } // 下载ZIP文件 const zipUrl = `https://github.com/${owner}/${repo}/archive/refs/heads/main.zip`; spinner.text = '正在下载仓库文件...'; const response = await fetch(zipUrl); if (!response.ok) { // 尝试master分支 const masterZipUrl = `https://github.com/${owner}/${repo}/archive/refs/heads/master.zip`; const masterResponse = await fetch(masterZipUrl); if (!masterResponse.ok) { throw new Error(`无法下载仓库: ${response.statusText}`); } response = masterResponse; } const zipBuffer = await response.buffer(); const tempZipPath = path.join(require('os').tmpdir(), `${repo}.zip`); await fs.writeFile(tempZipPath, zipBuffer); // 解压ZIP文件 spinner.text = '正在解压文件...'; await this.extractZip(tempZipPath, downloadDir, repo); // 清理临时文件 await fs.remove(tempZipPath); spinner.succeed(`仓库下载完成: ${downloadDir}`); return downloadDir; } catch (error) { spinner.fail(`仓库下载失败: ${error.message}`); throw error; } } parseGitHubUrl(url) { // 支持多种GitHub URL格式 const patterns = [ /github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?(?:\/.*)?$/, /^([^\/]+)\/([^\/]+)$/ // 简化格式: owner/repo ]; for (const pattern of patterns) { const match = url.match(pattern); if (match) { return { owner: match[1], repo: match[2] }; } } throw new Error('无效的GitHub仓库URL格式'); } extractZip(zipPath, targetDir, repoName) { return new Promise((resolve, reject) => { yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => { if (err) return reject(err); zipfile.readEntry(); zipfile.on('entry', async (entry) => { // 跳过顶级目录 (repo-main/ 或 repo-master/) const relativePath = entry.fileName.split('/').slice(1).join('/'); if (!relativePath) { zipfile.readEntry(); return; } const outputPath = path.join(targetDir, relativePath); if (/\/$/.test(entry.fileName)) { // 目录 await fs.ensureDir(outputPath); zipfile.readEntry(); } else { // 文件 await fs.ensureDir(path.dirname(outputPath)); zipfile.openReadStream(entry, (err, readStream) => { if (err) return reject(err); const writeStream = fs.createWriteStream(outputPath); readStream.pipe(writeStream); writeStream.on('close', () => zipfile.readEntry()); writeStream.on('error', reject); }); } }); zipfile.on('end', resolve); zipfile.on('error', reject); }); }); } async createTemplate(templateName, sourceDir) { const spinner = ora('创建代码模板...').start(); try { await fs.ensureDir(this.templatesDir); const templateDir = path.join(this.templatesDir, templateName); if (await fs.pathExists(templateDir)) { spinner.warn(`模板 ${templateName} 已存在`); return templateDir; } await fs.copy(sourceDir, templateDir); // 创建模板配置文件 const config = { name: templateName, created: new Date().toISOString(), source: sourceDir }; await fs.writeJson(path.join(templateDir, '.template.json'), config, { spaces: 2 }); spinner.succeed(`模板创建完成: ${templateName}`); return templateDir; } catch (error) { spinner.fail(`模板创建失败: ${error.message}`); throw error; } } async useTemplate(templateName, targetDir) { const spinner = ora('应用代码模板...').start(); try { const templateDir = path.join(this.templatesDir, templateName); if (!await fs.pathExists(templateDir)) { throw new Error(`模板 ${templateName} 不存在`); } const finalTargetDir = targetDir || path.join(this.projectDir, templateName); if (await fs.pathExists(finalTargetDir)) { spinner.warn(`目录 ${finalTargetDir} 已存在`); return finalTargetDir; } // 复制模板文件,排除配置文件 await fs.copy(templateDir, finalTargetDir, { filter: (src) => !src.endsWith('.template.json') }); spinner.succeed(`模板应用完成: ${finalTargetDir}`); return finalTargetDir; } catch (error) { spinner.fail(`模板应用失败: ${error.message}`); throw error; } } async listTemplates() { try { if (!await fs.pathExists(this.templatesDir)) { return []; } const templates = []; const items = await fs.readdir(this.templatesDir); for (const item of items) { const templateDir = path.join(this.templatesDir, item); const configPath = path.join(templateDir, '.template.json'); if (await fs.pathExists(configPath)) { const config = await fs.readJson(configPath); templates.push({ name: item, ...config }); } } return templates; } catch (error) { return []; } } async run(repoUrl, options = {}) { try { console.log(chalk.blue.bold('Claude Code Manager 启动中...')); if (repoUrl) { const projectDir = await this.downloadRepository(repoUrl, options.targetDir); console.log(chalk.green.bold('✅ 下载完成!')); console.log(chalk.yellow(`项目目录: ${projectDir}`)); // 检查是否有package.json,提示安装依赖 const packageJsonPath = path.join(projectDir, 'package.json'); if (await fs.pathExists(packageJsonPath)) { console.log(chalk.cyan('💡 检测到package.json,可以运行: cd ' + path.basename(projectDir) + ' && npm install')); } return projectDir; } else { console.log(chalk.green.bold('✅ Claude Code Manager 准备就绪!')); } } catch (error) { console.error(chalk.red.bold(`❌ 错误: ${error.message}`)); throw error; } } } module.exports = ClaudeCodeManager;