UNPKG

create-yesimbot-extension

Version:

Scaffold tool for creating YesImBot extensions

998 lines (841 loc) 36.6 kB
#!/usr/bin/env node import inquirer from 'inquirer'; import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; import replace from 'replace-in-file'; import chalk from 'chalk'; import { execSync, exec } from 'child_process'; import { promisify } from 'util'; import os from 'os'; import axios from 'axios'; import AdmZip from 'adm-zip'; import ora from 'ora'; // 正确获取 __dirname const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const execAsync = promisify(exec); console.log(chalk.hex('#FF6B6B').bold(` ██╗ ██╗██████╗ ███████╗ ╚██╗ ██╔╝██╔══██╗██╔════╝ ╚████╔╝ ██████╔╝█████╗ ╚██╔╝ ██╔══██╗██╔══╝ ██║ ██████╔╝███████╗ ╚═╝ ╚═════╝ ╚══════╝ YesImBot 扩展脚手架工具 v1.2.1 `)); // 添加命令行执行函数 async function runCommand(command, options = {}) { const { cwd, hideOutput = true, context = "执行命令" } = options; const spinner = ora().start(chalk.hex('#4ECDC4')(`${context}: ${command}`)); try { const result = await execAsync(command, { stdio: hideOutput ? 'pipe' : 'inherit', cwd, env: { ...process.env, FORCE_COLOR: '1', NO_UPDATE_NOTIFIER: '1' } }); spinner.succeed(chalk.green(`${context}成功!`)); return result; } catch (error) { spinner.fail(chalk.red(`${context}失败!`)); if (hideOutput && (error.stderr || error.stdout)) { console.error(chalk.red('错误详情:')); console.error(error.stderr || error.stdout); } else if (error.message) { console.error(chalk.red('错误信息:'), error.message); } throw error; } } // 检查包管理器是否安装 async function ensurePackageManagersInstalled() { let bunInstalled = false; let yarnInstalled = false; // 检查 Bun 是否安装 try { await execAsync('bun --version'); bunInstalled = true; console.log(chalk.green('✅ Bun 已安装')); } catch (error) { console.log(chalk.yellow('⚠️ 未检测到 Bun 包管理工具')); } // 检查 Yarn 是否安装 try { await execAsync('yarn --version'); yarnInstalled = true; console.log(chalk.green('✅ Yarn 已安装')); } catch (error) { console.log(chalk.yellow('⚠️ 未检测到 Yarn 包管理工具')); } // 如果两个包管理器都可用,让用户选择 if (bunInstalled && yarnInstalled) { const answer = await inquirer.prompt([ { type: 'list', name: 'packageManager', message: '请选择要使用的包管理器:', choices: [ { name: 'Bun (推荐)', value: 'bun' }, { name: 'Yarn', value: 'yarn' }, ], default: 'bun' } ]); return answer.packageManager; } // 如果只有Yarn可用 if (yarnInstalled) return 'yarn'; // 如果只有Bun可用 if (bunInstalled) return 'bun'; // 两个包管理器都不可用,询问是否安装Bun console.log(chalk.yellow('⚠️ 未检测到任何包管理工具')); const answer = await inquirer.prompt([ { type: 'confirm', name: 'installBun', message: '是否要自动安装 Bun (推荐)?', default: true } ]); if (!answer.installBun) { console.log(chalk.red('请手动安装 Yarn 或 Bun')); console.log(' Yarn: https://classic.yarnpkg.com/en/docs/install'); console.log(' Bun: https://bun.sh'); return null; } // 尝试安装Bun try { const spinner = ora().start(chalk.hex('#F7B801')('使用 npm 安装 Bun...')); await execAsync('npm install -g bun'); spinner.succeed(chalk.green('✅ Bun 安装成功!')); return 'bun'; } catch (npmError) { console.error(chalk.red('❌ Bun 安装失败:'), npmError); return null; } } // 格式化字节大小 function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } // 下载函数 async function downloadFile(url, outputPath) { try { const response = await axios({ method: 'get', url: url, responseType: 'stream', timeout: 300000, // 5分钟超时 }); const writer = fs.createWriteStream(outputPath); const totalBytes = parseInt(response.headers['content-length'], 10) || 0; let downloadedBytes = 0; let lastProgress = 0; // 更新下载进度 const updateProgress = () => { const percent = Math.floor((downloadedBytes / totalBytes) * 100); if (percent > lastProgress) { console.log(chalk.hex('#4ECDC4')(` 🚚 下载进度: ${percent}% (${formatBytes(downloadedBytes)}/${formatBytes(totalBytes)})`)); lastProgress = percent; } }; // 设置进度更新间隔 const progressInterval = setInterval(updateProgress, 500); // 更新下载字节数 response.data.on('data', (chunk) => { downloadedBytes += chunk.length; }); // 将响应流管道到文件写入流 response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', () => { clearInterval(progressInterval); updateProgress(); console.log(chalk.green(' ✅ 下载完成!')); resolve(); }); writer.on('error', (err) => { clearInterval(progressInterval); reject(new Error(`文件写入失败: ${err.message}`)); }); response.data.on('error', (err) => { clearInterval(progressInterval); reject(new Error(`下载流错误: ${err.message}`)); }); }); } catch (error) { throw new Error(`下载请求失败: ${error.message}`); } } // 自动构建核心包 async function autoBuildCore(projectPath, packageManager) { console.log(chalk.hex('#FF6B6B').bold('\n🌍 检测到您在外部开发,需要构建 YesImBot 核心包')); console.log(chalk.hex('#FFD166')('⏳ 这可能需要几分钟时间,请耐心等待...')); try { // 1. 构建核心包 console.log(chalk.hex('#4ECDC4').bold('\n🚧 步骤 1/3: 构建 YesImBot 核心包')); const buildResult = await buildYesImBot(packageManager); console.log(chalk.green(`✅ 核心包构建成功! TGZ 文件: ${buildResult.tgzPath}`)); // 2. 进入项目目录 console.log(chalk.hex('#4ECDC4').bold('\n📂 步骤 2/3: 进入项目目录并安装核心包')); process.chdir(projectPath); // 3. 清理项目缓存 cleanProjectCache(projectPath); // 4. 使用 tgz 文件安装核心包 let installCmd; if (packageManager === 'yarn') { installCmd = `yarn add ${buildResult.tgzPath} --peer`; } else { installCmd = `bun add ${buildResult.tgzPath} --peer --force`; } await runCommand(installCmd, { hideOutput: true, context: "安装核心包" }); console.log(chalk.green('✅ 核心包安装成功!')); // 5. 安装其他依赖 console.log(chalk.hex('#4ECDC4').bold('\n🧩 步骤 3/3: 安装项目依赖')); let installDepsCmd; if (packageManager === 'yarn') { installDepsCmd = 'yarn install'; } else { installDepsCmd = 'bun install'; } await runCommand(installDepsCmd, { hideOutput: true, context: "安装项目依赖" }); console.log(chalk.green('✅ 依赖安装成功!')); // 完成提示 let devCommand; if (packageManager === 'yarn') { devCommand = 'yarn dev'; } else { devCommand = 'bun dev'; } console.log(chalk.hex('#06D6A0').bold('\n🎉 所有准备工作已完成!')); console.log(chalk.hex('#118AB2').bold('\n现在您可以开始开发:')); console.log(chalk.hex('#FFD166').bold(` cd ${path.basename(projectPath)}`)); console.log(chalk.hex('#FFD166').bold(` ${devCommand}\n`)); return true; } catch (buildError) { console.error(chalk.red('\n❌❌ 自动构建过程中出错:')); console.error(buildError); // 定义 devCommand 用于错误提示 const devCommand = packageManager === 'yarn' ? 'yarn dev' : 'bun dev'; console.log(chalk.hex('#FF6B6B').bold('\n🛠️ 请尝试手动完成以下步骤:')); console.log(` 1. 进入项目目录: ${chalk.hex('#4ECDC4')(`cd ${path.basename(projectPath)}`)}`); console.log(` 2. 清理缓存: ${chalk.hex('#4ECDC4')('rm -rf node_modules')} ${packageManager === 'yarn' ? 'yarn.lock' : 'bun.lockb'}`); let manualInstallCmd; // 提示用户使用手动构建的tgz文件 if (packageManager === 'yarn') { manualInstallCmd = `yarn add /path/to/koishi-plugin-yesimbot.tgz --peer`; } else { manualInstallCmd = `bun add /path/to/koishi-plugin-yesimbot.tgz --peer --force`; } console.log(` 3. 安装核心包: ${chalk.hex('#4ECDC4')(manualInstallCmd)}`); let manualDepsCmd; if (packageManager === 'yarn') { manualDepsCmd = 'yarn install'; } else { manualDepsCmd = 'bun install'; } console.log(` 4. 安装依赖: ${chalk.hex('#4ECDC4')(manualDepsCmd)}`); console.log(` 5. 开始开发: ${chalk.hex('#4ECDC4')(devCommand)}\n`); console.log(chalk.yellow('💡 提示: 核心包 tgz 文件可以在以下目录找到:')); console.log(chalk.hex('#4ECDC4')(` ${path.join(os.homedir(), '.ybe-build/*/YesImBot-dev/packages/core/*.tgz')}`)); return false; } } function isKoishiProject(cwd) { return fs.existsSync(path.join(cwd, 'koishi.yml')) || fs.existsSync(path.join(cwd, 'koishi.yaml')) || fs.existsSync(path.join(cwd, 'node_modules/koishi')); } async function getUpdatePackages() { const { packages } = await inquirer.prompt([ { type: 'checkbox', name: 'packages', message: '选择要更新的包:', choices: [ { name: '核心包 (koishi-plugin-yesimbot)', value: 'core' }, { name: '代码执行器扩展', value: 'code-interpreter' }, { name: '代码转图片扩展', value: 'code2image' }, { name: '好感度扩展', value: 'favor' }, { name: 'MCP 扩展', value: 'mcp' }, { name: '表情包管理扩展', value: 'sticker-manager' }, { name: '所有扩展包', value: 'all-extensions' } ] } ]); return packages; } async function installPackage(pkg, buildResult, packageManager) { let packageName, tgzPath; if (pkg === 'core') { packageName = 'koishi-plugin-yesimbot'; tgzPath = buildResult.tgzPath; // 直接使用核心包的 tgz 路径 } else if (pkg === 'all-extensions') { // 安装所有扩展 const extensions = ['code-interpreter', 'code2image', 'favor', 'mcp', 'sticker-manager']; for (const ext of extensions) { await installPackage(ext, buildResult, packageManager); } return; } else { packageName = `koishi-plugin-yesimbot-extension-${pkg}`; // 查找扩展包的 tgz 文件 const extPath = path.join(buildResult.projectPath, 'packages', pkg); const files = fs.readdirSync(extPath); const tgzFile = files.find(f => f.endsWith('.tgz')); if (!tgzFile) { throw new Error(`找不到 ${pkg} 扩展的 tgz 文件`); } tgzPath = path.join(extPath, tgzFile); } const installCmd = packageManager === 'yarn' ? `yarn add ${tgzPath}` : `bun add ${tgzPath} --force`; await runCommand(installCmd, { context: `安装 ${packageName}` }); } // 清理项目缓存 function cleanProjectCache(projectPath) { console.log(chalk.hex('#4ECDC4')('🧹 清理项目缓存...')); const pathsToClean = [ path.join(projectPath, 'node_modules'), path.join(projectPath, 'bun.lockb'), path.join(projectPath, 'package-lock.json'), path.join(projectPath, 'yarn.lock') ]; pathsToClean.forEach(item => { try { if (fs.existsSync(item)) { fs.removeSync(item); console.log(chalk.hex('#4ECDC4')(` ✅ 已删除: ${path.basename(item)}`)); } } catch (error) { console.log(chalk.yellow(` ⚠️ 清理失败: ${path.basename(item)}`)); } }); } // 移除所有package.json中的packageManager字段 function removePackageManagerFields(projectPath) { console.log(chalk.hex('#FF6B6B')('🔧 移除所有package.json中的packageManager字段...')); try { // 查找所有package.json文件 const packageJsonFiles = findFiles(projectPath, 'package.json'); packageJsonFiles.forEach(file => { try { const content = fs.readFileSync(file, 'utf8'); const packageJson = JSON.parse(content); if (packageJson.packageManager) { delete packageJson.packageManager; fs.writeFileSync(file, JSON.stringify(packageJson, null, 2)); console.log(chalk.hex('#4ECDC4')(` ✅ 已移除: ${path.relative(projectPath, file)}`)); } } catch (error) { console.log(chalk.yellow(` ⚠️ 处理失败: ${path.relative(projectPath, file)}`)); } }); return true; } catch (error) { console.log(chalk.yellow('⚠️ 移除packageManager字段失败:'), error.message); return false; } } // 添加 packageManager 字段以避免 Turbo 警告 async function addPackageManagerField(projectPath, packageManager) { const packageJsonPath = path.join(projectPath, 'package.json'); try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); if (!packageJson.packageManager) { let version; if (packageManager === 'yarn') { const { stdout } = await execAsync('yarn --version'); version = stdout.trim(); packageJson.packageManager = `yarn@1.22.22`; } else { const { stdout } = await execAsync('bun --version'); version = stdout.trim(); packageJson.packageManager = `bun@${version}`; } fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); console.log(chalk.hex('#4ECDC4')(` ✅ 添加 packageManager 字段: ${packageJson.packageManager}`)); } } catch (error) { console.log(chalk.yellow(' ⚠️ 添加 packageManager 字段失败:'), error.message); } } // 递归查找文件 function findFiles(dir, fileName) { let results = []; const list = fs.readdirSync(dir); list.forEach(file => { file = path.join(dir, file); const stat = fs.statSync(file); if (stat && stat.isDirectory()) { // 递归查找 results = results.concat(findFiles(file, fileName)); } else { if (path.basename(file) === fileName) { results.push(file); } } }); return results; } // 构建核心包 async function buildYesImBot(packageManager) { console.log(chalk.hex('#FF6B6B').bold('\n🔧🔧 开始构建 YesImBot 核心包...')); // 创建专用构建目录 const tempDir = path.join(os.homedir(), '.ybe-build', Date.now().toString()); const zipPath = path.join(tempDir, 'YesImBot-dev.zip'); let repoUrl = ''; try { // 确保使用全新的临时目录 fs.mkdirSync(tempDir, { recursive: true }); // 下载最新 dev 分支 console.log(chalk.hex('#4ECDC4')('⬇ ️正在下载 YesImBot dev 分支...')); // 提供中国大陆可用的镜像 const mirrorUrl = process.env.YBE_MIRROR || 'https://github.akams.cn/https://github.com'; repoUrl = `${mirrorUrl}/YesWeAreBot/YesImBot/archive/refs/heads/dev.zip`; // 下载文件 await downloadFile(repoUrl, zipPath); // 验证下载文件 const stats = fs.statSync(zipPath); if (stats.size === 0) { throw new Error('下载的文件大小为0,可能是下载失败'); } console.log(chalk.green(`✅ 下载完成! 文件大小: ${formatBytes(stats.size)}`)); // 解压文件 console.log(chalk.hex('#4ECDC4')('📦 正在解压文件...')); const zip = new AdmZip(zipPath); const extracted = zip.getEntries().length; // 解压到临时目录 zip.extractAllTo(tempDir, true); console.log(chalk.green(`✅ 解压完成,共提取 ${extracted} 个文件`)); // 找到解压后的目录 const files = fs.readdirSync(tempDir); const extractedDir = files.find(name => name.startsWith('YesImBot-dev')); if (!extractedDir) { throw new Error(`解压后找不到 YesImBot-dev 目录。找到的文件: ${files.join(', ')}`); } const projectPath = path.join(tempDir, extractedDir); // 安装依赖并构建 console.log(chalk.hex('#FF6B6B').bold('\n🔨 安装依赖并构建核心包...')); // 确保包管理器已安装 if (!packageManager) { throw new Error('没有可用的包管理器,无法继续构建'); } // 在项目目录中创建 package.json 以解决工作区问题 const tempPackageJson = path.join(projectPath, 'package.json'); if (!fs.existsSync(tempPackageJson)) { fs.writeFileSync(tempPackageJson, JSON.stringify({ name: 'yesimbot-core-temp', private: true, workspaces: [] // 明确指定空工作区 }, null, 2)); } // 创建空 yarn.lock 文件 (如果不存在) const yarnLockPath = path.join(projectPath, 'yarn.lock'); if (!fs.existsSync(yarnLockPath)) { fs.writeFileSync(yarnLockPath, ''); } // 处理Corepack问题 - 直接移除所有package.json中的packageManager字段 if (packageManager === 'yarn') { removePackageManagerFields(projectPath); } // 安装依赖 console.log(chalk.hex('#4ECDC4')('🧩 安装依赖...')); if (packageManager === 'yarn') { await runCommand('yarn install --ignore-engines', { cwd: projectPath, hideOutput: true, context: "安装项目依赖" }); } else { await runCommand('bun install --ignore-engines', { cwd: projectPath, hideOutput: true, context: "安装项目依赖" }); } // 添加 packageManager 字段以避免 Turbo 警告 await addPackageManagerField(projectPath, packageManager); // 构建核心包 console.log(chalk.hex('#4ECDC4')('🔨 构建核心包...')); if (packageManager === 'yarn') { await runCommand('yarn build', { cwd: projectPath, hideOutput: true, context: "构建核心包" }); } else { await runCommand('bun run build', { cwd: projectPath, hideOutput: true, context: "构建核心包" }); } // 读取核心包版本 const corePackageJsonPath = path.join(projectPath, 'packages/core/package.json'); const corePackage = JSON.parse(fs.readFileSync(corePackageJsonPath, 'utf-8')); console.log(chalk.green(`✅ 核心包版本: ${corePackage.version}`)); // 打包核心包为 tgz console.log(chalk.hex('#4ECDC4')('📦 打包核心包...')); const corePackagePath = path.join(projectPath, 'packages/core'); const originalCwd = process.cwd(); // 保存当前工作目录 process.chdir(corePackagePath); // 执行打包命令 if (packageManager === 'yarn') { await runCommand('yarn pack', { hideOutput: true }); } else { await runCommand('bun pm pack', { hideOutput: true }); } // 查找生成的 tgz 文件(兼容 Bun 和 Yarn 的命名格式) const tgz_files = fs.readdirSync(corePackagePath); const tgzFiles = tgz_files.filter(file => file.endsWith('.tgz') && file.includes(corePackage.name.replace('/', '-')) ); if (tgzFiles.length === 0) { // 如果找不到,尝试更宽松的匹配 const allTgzFiles = tgz_files.filter(file => file.endsWith('.tgz')); if (allTgzFiles.length > 0) { tgzFiles.push(allTgzFiles[0]); } else { throw new Error(`找不到任何 tgz 文件`); } } // 选择最新或唯一的 tgz 文件 const tgzFilename = tgzFiles[0]; const tgzPath = path.join(corePackagePath, tgzFilename); if (!fs.existsSync(tgzPath)) { throw new Error(`打包后找不到 tgz 文件: ${tgzFilename}`); } console.log(chalk.green(`✅ 核心包打包完成: ${tgzPath}`)); process.chdir(originalCwd); // 恢复原始工作目录 // 返回核心包路径和 tgz 文件路径 return { corePath: corePackagePath, tgzPath: tgzPath, projectPath: projectPath, version: corePackage.version }; } catch (error) { console.error(chalk.red('\n❌❌ 构建过程中出错:')); console.error(error); // 提供用户可操作的解决方案 console.log(chalk.hex('#FF6B6B').bold('\n🛠️ 可能的解决方案:')); console.log('1. 检查网络连接'); console.log('2. 尝试设置镜像: export YBE_MIRROR=https://github.akams.cn'); console.log('3. 手动下载源码:'); console.log(chalk.hex('#4ECDC4')(` curl -L ${repoUrl} -o ${zipPath}`)); console.log('4. 手动构建:'); console.log(chalk.hex('#4ECDC4')(` unzip ${zipPath} -d ${tempDir}`)); console.log(chalk.hex('#4ECDC4')(` cd ${tempDir}/YesImBot-dev`)); console.log(chalk.hex('#4ECDC4')(` ${packageManager || 'yarn'} install --ignore-engines && ${packageManager || 'yarn'} run build`)); console.log(chalk.hex('#4ECDC4')(` cd packages/core && ${packageManager || 'yarn'} pack`)); throw error; } } // 检查项目位置是否合适 function checkProjectLocation(projectPath) { const currentDir = path.dirname(projectPath); const parentDir = path.dirname(currentDir); // 检查是否在 YesImBot 的 packages/ 目录下 const inYesImBotPackages = path.basename(parentDir) === 'packages' && fs.existsSync(path.join(parentDir, '../package.json')); // 检查是否在 Koishi 的 external/ 目录下 const inKoishiExternal = path.basename(parentDir) === 'external' && (fs.existsSync(path.join(parentDir, '../koishi.yml')) || fs.existsSync(path.join(parentDir, '../koishi.yaml'))); // 检查是否在项目根目录的 external/ 下 const inRootExternal = path.basename(currentDir) === 'external' && (fs.existsSync(path.join(currentDir, '../koishi.yml')) || fs.existsSync(path.join(currentDir, '../koishi.yaml'))); return { isValid: inYesImBotPackages || inKoishiExternal || inRootExternal, isYesImBotPackages: inYesImBotPackages }; } async function updateCommand() { // 检查包管理器 const packageManager = await ensurePackageManagersInstalled(); if (!packageManager) { console.log(chalk.red('❌ 没有可用的包管理器,无法继续操作')); return; } // 检查当前目录是否是 Koishi 项目 if (!isKoishiProject(process.cwd())) { console.log(chalk.red('❌ 当前目录不是 Koishi 项目!请在 Koishi 项目根目录运行此命令')); return; } // 获取要更新的包列表 const packagesToUpdate = await getUpdatePackages(); if (packagesToUpdate.length === 0) { console.log(chalk.yellow('⚠️ 未选择任何包,操作取消')); return; } // 构建 YesImBot 项目 console.log(chalk.hex('#FF6B6B').bold('\n🔧 开始构建 YesImBot 项目...')); let buildResult; try { buildResult = await buildYesImBot(packageManager); console.log(chalk.green(`✅ YesImBot 构建成功! 版本: ${buildResult.version}`)); // 打包所有扩展包 console.log(chalk.hex('#4ECDC4').bold('\n📦 打包扩展包...')); const extensions = ['code-interpreter', 'code2image', 'favor', 'mcp', 'sticker-manager']; const originalCwd = process.cwd(); for (const ext of extensions) { const extPath = path.join(buildResult.projectPath, 'packages', ext); // 确保目录存在 if (!fs.existsSync(extPath)) { console.log(chalk.yellow(`⚠️ 跳过 ${ext} 扩展 (目录不存在)`)); continue; } // 进入扩展包目录 process.chdir(extPath); // 打包扩展包 try { console.log(chalk.hex('#4ECDC4')(` 打包 ${ext} 扩展...`)); if (packageManager === 'yarn') { await runCommand('yarn pack', { hideOutput: true }); } else { await runCommand('bun pm pack', { hideOutput: true }); } // 验证打包文件存在 const files = fs.readdirSync(extPath); const tgzFile = files.find(f => f.endsWith('.tgz')); if (!tgzFile) { throw new Error('打包后找不到 tgz 文件'); } console.log(chalk.green(` ✅ ${ext} 打包完成: ${tgzFile}`)); } catch (packError) { console.error(chalk.red(`❌ ${ext} 扩展打包失败:`), packError.message); } } // 恢复原始工作目录 process.chdir(originalCwd); } catch (error) { console.error(chalk.red('\n❌ YesImBot 构建失败:'), error); return; } // 安装选定的包 for (const pkg of packagesToUpdate) { try { await installPackage(pkg, buildResult, packageManager); console.log(chalk.green(`✅ ${pkg} 安装成功!`)); } catch (error) { console.error(chalk.red(`❌ 安装 ${pkg} 失败: `), error.message); } } console.log(chalk.hex('#06D6A0').bold('\n🎉 更新完成!')); console.log(chalk.hex('#118AB2')('请重启 Koishi 服务使更改生效\n')); } async function main() { const args = process.argv.slice(2); const command = args[0] || 'help'; try { if (command === 'create') { // 原有创建扩展逻辑 await createCommand(); } else if (command === 'update') { await updateCommand(); } else { // 显示帮助信息 console.log(chalk.hex('#FF6B6B').bold('\nYesImBot 扩展工具 v1.2.0')); console.log(chalk.hex('#4ECDC4')('可用命令:')); console.log(' create - 创建新扩展'); console.log(' update - 更新/安装 YesImBot 包\n'); console.log(chalk.hex('#118AB2')('示例:')); console.log(' ybe create 创建新扩展'); console.log(' ybe update 更新 YesImBot 包'); console.log(' ybe 显示帮助信息\n'); } } catch (error) { console.error(chalk.red('❌ 操作失败:'), error.message); process.exit(1); } } async function createCommand() { // 这是原有 main 函数中创建扩展的逻辑 // 需要将原来 main 函数中创建扩展的代码剪切到这里 // 检查包管理器是否安装 const packageManager = await ensurePackageManagersInstalled(); if (!packageManager) { console.log(chalk.red('❌❌❌❌ 没有可用的包管理器,无法继续操作')); return; } // 如果使用的是Yarn,提示用户 if (packageManager === 'yarn') { console.log(chalk.hex('#4ECDC4').bold('🎯 将使用 Yarn 作为包管理器')); } else { console.log(chalk.hex('#4ECDC4').bold('🎯 将使用 Bun 作为包管理器')); } const questions = [ { type: 'input', name: 'extensionName', message: chalk.hex('#FFD166')('请输入扩展名称 (kebab-case 格式:'), validate: input => /^[a-z0-9-]+$/.test(input) || '名称必须使用 kebab-case 格式 (小写字母、数字、连字符)' }, { type: 'input', name: 'friendlyName', message: chalk.hex('#FFD166')('请输入显示名称:'), default: answers => `${answers.extensionName.replace(/-/g, ' ')}` }, { type: 'input', name: 'description', message: chalk.hex('#FFD166')('请输入扩展描述:') }, { type: 'confirm', name: 'confirmCreate', message: chalk.hex('#FFD166')('确认使用以上设置创建扩展?'), default: true } ]; const answers = await inquirer.prompt(questions); if (!answers.confirmCreate) { console.log(chalk.yellow('\n扩展创建已取消')); return; } const projectName = answers.extensionName; const fullPackageName = `koishi-plugin-yesimbot-extension-${projectName}`; const projectPath = path.join(process.cwd(), projectName); try { // 创建项目目录 if (fs.existsSync(projectPath)) { console.log(chalk.red(`\n错误: 目录 "${projectName}" 已存在!`)); return; } fs.mkdirSync(projectPath); console.log(chalk.hex('#118AB2')(`\n📁 创建项目目录: ${projectName}`)); // 复制模板文件 await copyTemplate('base', projectPath); await copyTemplate('extension', path.join(projectPath, 'src')); // 生成 PascalCase 类名 (首字母大写) const className = answers.friendlyName .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(''); // 模板变量替换 const replaceOptions = { files: [ path.join(projectPath, 'src/index.ts'), path.join(projectPath, 'README.md'), path.join(projectPath, 'package.json') ], from: [ /{{name}}/g, /{{friendlyName}}/g, /{{description}}/g, /{{ClassName}}/g, /{{fullPackageName}}/g ], to: [ answers.extensionName, answers.friendlyName, answers.description, className, fullPackageName ] }; await replace.replaceInFile(replaceOptions); // 使用用户输入更新 package.json const packageJsonPath = path.join(projectPath, 'package.json'); await updatePackageJson(packageJsonPath, { name: fullPackageName, description: answers.description, scripts: { build: "tsc && node esbuild.config.mjs", dev: "tsc -w --preserveWatchOutput", lint: "eslint . --ext .ts", clean: "rm -rf lib .turbo tsconfig.tsbuildinfo *.tgz", pack: packageManager === 'yarn' ? "yarn pack" : "bun pm pack", "install-core": packageManager === 'yarn' ? `yarn add koishi-plugin-yesimbot@file:${path.join(os.homedir(), '.ybe-build/*/YesImBot-dev/packages/core')} --dev` : `bun add koishi-plugin-yesimbot@file:${path.join(os.homedir(), '.ybe-build/*/YesImBot-dev/packages/core')} --dev --force` }, keywords: [ "koishi", "plugin", "extension", "yesimbot" ] }); // 检查项目位置 const locationInfo = checkProjectLocation(projectPath); console.log(chalk.green(`\n✅ 成功创建 "${answers.friendlyName}" 扩展!`)); // 根据位置决定是否自动构建 if (locationInfo.isYesImBotPackages) { console.log(chalk.hex('#06D6A0').bold('\n🌍 检测到您在 YesImBot 项目内部创建扩展')); console.log(chalk.hex('#118AB2').bold('\n现在您可以开始开发:')); console.log(chalk.hex('#FFD166').bold(` cd ${projectName}`)); console.log(chalk.hex('#FFD166').bold(` ${packageManager} install`)); console.log(chalk.hex('#FFD166').bold(` ${packageManager} dev\n`)); } else { // 自动构建核心包并安装依赖(传入包管理器类型) const buildSuccess = await autoBuildCore(projectPath, packageManager); if (!buildSuccess) { console.log(chalk.yellow('项目创建完成,但自动构建失败,请按照提示手动完成剩余步骤')); } } console.log(chalk.hex('#FF6B6B').bold('\n💡 其他建议:')); console.log(' 1. 在 src/index.ts 中添加扩展逻辑'); console.log(' 2. 更新 README.md 中的使用说明'); console.log(` 3. 使用 ${packageManager === 'yarn' ? 'yarn add' : 'bun add'} <package> 添加额外依赖\n`); } catch (error) { console.error(chalk.red('\n创建扩展时出错:'), error); if (fs.existsSync(projectPath)) { fs.rmdirSync(projectPath, { recursive: true }); } } } // 复制模板函数 async function copyTemplate(templateName, destPath) { const sourcePath = path.join(__dirname, '../templates', templateName); await fs.copy(sourcePath, destPath); } // 更新 package.json async function updatePackageJson(packageJsonPath, updates) { let packageJson = await fs.readJson(packageJsonPath); // 处理版本号占位符 if (packageJson.version === "{{version}}") { packageJson.version = "0.1.0"; } // Merge updates into package.json packageJson = { ...packageJson, ...updates, keywords: [ ...(packageJson.keywords || []), ...(updates.keywords || []) ], scripts: { ...(packageJson.scripts || {}), ...(updates.scripts || {}) } }; await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); } // 启动主程序 main().catch(err => { console.error(chalk.red('❌❌ 程序意外终止:'), err); process.exit(1); });