@solazah/solazah-cli 0.2.18 → 0.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/commands/account/login.d.ts +3 -0
- package/commands/account/login.d.ts.map +1 -0
- package/commands/account/logout.d.ts +3 -0
- package/commands/account/logout.d.ts.map +1 -0
- package/commands/account/shared.d.ts +9 -0
- package/commands/account/shared.d.ts.map +1 -0
- package/commands/account/userinfo.d.ts +3 -0
- package/commands/account/userinfo.d.ts.map +1 -0
- package/commands/plugin/create.d.ts +15 -0
- package/commands/plugin/create.d.ts.map +1 -0
- package/commands/plugin/package.d.ts +6 -0
- package/commands/plugin/package.d.ts.map +1 -0
- package/commands/plugin/publish.d.ts +7 -0
- package/commands/plugin/publish.d.ts.map +1 -0
- package/commands/plugin/release.d.ts +6 -0
- package/commands/plugin/release.d.ts.map +1 -0
- package/commands/plugin/version.d.ts +7 -0
- package/commands/plugin/version.d.ts.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +1119 -0
- package/index.js.map +1 -0
- package/package.json +4 -14
- package/utils/auth-storage.d.ts +27 -0
- package/utils/auth-storage.d.ts.map +1 -0
- package/utils/file.d.ts +41 -0
- package/utils/file.d.ts.map +1 -0
- package/utils/oauth-device.d.ts +98 -0
- package/utils/oauth-device.d.ts.map +1 -0
- package/utils/open-browser.d.ts +5 -0
- package/utils/open-browser.d.ts.map +1 -0
- package/utils/validate.d.ts +21 -0
- package/utils/validate.d.ts.map +1 -0
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils/validate.ts","../src/utils/file.ts","../src/commands/plugin/create.ts","../src/commands/plugin/package.ts","../src/utils/auth-storage.ts","../src/utils/oauth-device.ts","../src/commands/plugin/publish.ts","../src/commands/plugin/version.ts","../src/commands/plugin/release.ts","../src/utils/open-browser.ts","../src/commands/account/shared.ts","../src/commands/account/login.ts","../src/commands/account/logout.ts","../src/commands/account/userinfo.ts","../src/index.ts"],"sourcesContent":["import validatePackageName from 'validate-npm-package-name';\r\n\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * 验证插件名称\r\n * 接受任何合法的 npm 包名\r\n */\r\nexport function validatePluginName(name: string): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n // 基本格式检查\r\n if (!name) {\r\n errors.push('Plugin name is required');\r\n return { valid: false, errors };\r\n }\r\n\r\n // 使用 npm 包名验证\r\n const validation = validatePackageName(name);\r\n if (!validation.validForNewPackages) {\r\n if (validation.errors) {\r\n errors.push(...validation.errors);\r\n }\r\n if (validation.warnings) {\r\n errors.push(...validation.warnings);\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors,\r\n };\r\n}\r\n\r\n/**\r\n * 从包名提取插件名称\r\n * @example extractPluginName('@solazah/solazah-plugin-example') => 'solazah-plugin-example'\r\n */\r\nexport function extractPluginName(packageName: string): string {\r\n // 移除作用域前缀(如 @solazah/)\r\n return packageName.replace(/^@[^/]+\\//, '');\r\n}\r\n\r\nexport function extractPluginSimpleName(packageName: string){\r\n // 移除作用域前缀(如 @solazah/)\r\n let name = packageName.replace(/^@[^/]+\\//, '');\r\n\r\n // 移除常见的插件前缀\r\n name = name.replace(/^plugin-/, '').replace(/^solazah-plugin-/, '');\r\n\r\n return name;\r\n}\r\n\r\n/**\r\n * 生成显示名称\r\n * @example generateDisplayName('example') => 'Example'\r\n */\r\nexport function generateDisplayName(name: string): string {\r\n return name\r\n .split('-')\r\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\r\n .join(' ');\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\nexport interface TemplateInfo {\r\n name: string;\r\n displayName: string;\r\n description: string;\r\n version: string;\r\n features?: string[];\r\n}\r\n\r\n/**\r\n * 检查目录是否为空\r\n */\r\nexport async function isDirectoryEmpty(dirPath: string): Promise<boolean> {\r\n try {\r\n const files = await fs.readdir(dirPath);\r\n return files.length === 0;\r\n } catch {\r\n return true; // 目录不存在,视为空\r\n }\r\n}\r\n\r\n/**\r\n * 确保目录存在\r\n */\r\nexport async function ensureDirectory(dirPath: string): Promise<void> {\r\n await fs.ensureDir(dirPath);\r\n}\r\n\r\n/**\r\n * 获取模板根目录路径\r\n */\r\nexport function getTemplatesRootDir(): string {\r\n return path.resolve(__dirname, '../templates');\r\n}\r\n\r\n/**\r\n * 获取指定模板的目录路径\r\n */\r\nexport function getTemplateDir(templateName: string): string {\r\n return path.join(getTemplatesRootDir(), templateName);\r\n}\r\n\r\n/**\r\n * 获取所有可用模板\r\n */\r\nexport async function getAvailableTemplates(): Promise<TemplateInfo[]> {\r\n const templatesRoot = getTemplatesRootDir();\r\n const templates: TemplateInfo[] = [];\r\n\r\n try {\r\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const templateJsonPath = path.join(templatesRoot, entry.name, 'template.json');\r\n\r\n if (await fs.pathExists(templateJsonPath)) {\r\n const templateInfo = await fs.readJson(templateJsonPath);\r\n templates.push(templateInfo);\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error('Failed to read templates:', error);\r\n }\r\n\r\n return templates;\r\n}\r\n\r\n/**\r\n * 复制模板文件\r\n */\r\nexport async function copyTemplate(templateDir: string, targetDir: string): Promise<void> {\r\n await fs.copy(templateDir, targetDir, {\r\n filter: (src) => {\r\n // 排除 node_modules 和其他不需要的文件\r\n const basename = path.basename(src);\r\n return !['node_modules', '.git', 'dist', 'target', 'release'].includes(basename);\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 替换文件内容中的占位符\r\n * 支持 {{PLACEHOLDER}} 格式\r\n */\r\nexport async function replacePlaceholders(\r\n filePath: string,\r\n replacements: Record<string, string>\r\n): Promise<void> {\r\n let content = await fs.readFile(filePath, 'utf-8');\r\n\r\n for (const [key, value] of Object.entries(replacements)) {\r\n // 支持 {{KEY}} 和直接字符串替换\r\n const placeholder = `{{${key}}}`;\r\n content = content.replaceAll(placeholder, value);\r\n // 兼容旧的直接替换方式\r\n content = content.replaceAll(key, value);\r\n }\r\n\r\n await fs.writeFile(filePath, content, 'utf-8');\r\n}\r\n\r\n/**\r\n * 批量替换目录中所有文件的占位符\r\n */\r\nexport async function replaceInDirectory(\r\n dirPath: string,\r\n replacements: Record<string, string>,\r\n extensions: string[] = ['.ts', '.tsx', '.js', '.jsx', '.json', '.html', '.md']\r\n): Promise<void> {\r\n const files = await fs.readdir(dirPath, { withFileTypes: true });\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dirPath, file.name);\r\n\r\n if (file.isDirectory()) {\r\n // 递归处理子目录\r\n await replaceInDirectory(filePath, replacements, extensions);\r\n } else if (file.isFile()) {\r\n // 检查文件扩展名\r\n const ext = path.extname(file.name);\r\n if (extensions.includes(ext) || file.name === 'manifest.json') {\r\n await replacePlaceholders(filePath, replacements);\r\n }\r\n }\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport inquirer from 'inquirer';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\nimport {\r\n validatePluginName,\r\n extractPluginName,\r\n generateDisplayName, extractPluginSimpleName,\r\n} from '../../utils/validate.js';\r\nimport {\r\n isDirectoryEmpty,\r\n ensureDirectory,\r\n getTemplateDir,\r\n getAvailableTemplates,\r\n copyTemplate,\r\n replaceInDirectory,\r\n} from '../../utils/file.js';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface CreatePluginOptions {\r\n name?: string;\r\n dir?: string;\r\n targetDir?: string;\r\n template?: string;\r\n displayName?: string;\r\n description?: string;\r\n author?: string;\r\n port?: number;\r\n yes?: boolean;\r\n skipInstall?: boolean;\r\n}\r\n\r\nexport async function createPluginCommand(options: CreatePluginOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n🚀 Solazah Plugin Creator\\n'));\r\n\r\n try {\r\n // 1. 获取可用模板\r\n const availableTemplates = await getAvailableTemplates();\r\n\r\n if (availableTemplates.length === 0) {\r\n console.error(chalk.red('❌ No templates found'));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 选择模板\r\n let selectedTemplate = options.template || 'react';\r\n\r\n if (!options.template && availableTemplates.length > 1) {\r\n const { template } = await inquirer.prompt([\r\n {\r\n type: 'list',\r\n name: 'template',\r\n message: 'Select a template:',\r\n choices: availableTemplates.map((t) => ({\r\n name: `${t.displayName} - ${t.description}`,\r\n value: t.name,\r\n })),\r\n default: 'react',\r\n },\r\n ]);\r\n selectedTemplate = template;\r\n }\r\n\r\n const templateInfo = availableTemplates.find((t) => t.name === selectedTemplate);\r\n if (!templateInfo) {\r\n console.error(chalk.red(`❌ Template \"${selectedTemplate}\" not found`));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.gray(`Using template: ${templateInfo.displayName}\\n`));\r\n\r\n // 3. 获取或询问插件名称\r\n let pluginName = options.name;\r\n\r\n if (!pluginName) {\r\n const answers = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'name',\r\n message: 'Plugin name (e.g., my-plugin or @scope/plugin-name):',\r\n validate: (input: string) => {\r\n const result = validatePluginName(input);\r\n return result.valid || result.errors.join(', ');\r\n },\r\n },\r\n ]);\r\n pluginName = answers.name;\r\n }\r\n\r\n // 验证插件名称\r\n const validation = validatePluginName(pluginName!);\r\n if (!validation.valid) {\r\n console.error(chalk.red('❌ Invalid plugin name:'));\r\n validation.errors.forEach((error) => console.error(chalk.red(` - ${error}`)));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 提取信息\r\n const packageName = extractPluginName(pluginName!)\r\n const simpleName = extractPluginSimpleName(pluginName!);\r\n const defaultDisplayName = generateDisplayName(simpleName);\r\n\r\n // 3. 确定目标目录\r\n const defaultTargetDir = options.targetDir\r\n ? path.resolve(options.targetDir)\r\n : (options.dir ? path.resolve(options.dir, packageName) : path.resolve('.'));\r\n\r\n let targetDir: string;\r\n if (options.targetDir || options.yes) {\r\n targetDir = defaultTargetDir;\r\n } else {\r\n const { targetDir: confirmedTargetDir } = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'targetDir',\r\n message: 'Target directory:',\r\n default: defaultTargetDir,\r\n },\r\n ]);\r\n targetDir = path.resolve(confirmedTargetDir);\r\n }\r\n\r\n // 4. 询问额外信息(命令行已提供则跳过)\r\n const defaults = {\r\n displayName: defaultDisplayName,\r\n description: `A Solazah plugin - ${defaultDisplayName}`,\r\n author: '',\r\n port: 5200,\r\n };\r\n const provided = {\r\n displayName: options.displayName,\r\n description: options.description,\r\n author: options.author,\r\n port: options.port,\r\n };\r\n const remainingPrompts: any[] = [];\r\n if (provided.displayName === undefined && !options.yes) {\r\n remainingPrompts.push({ type: 'input', name: 'displayName', message: 'Display name:', default: defaults.displayName });\r\n }\r\n if (provided.description === undefined && !options.yes) {\r\n remainingPrompts.push({ type: 'input', name: 'description', message: 'Description:', default: defaults.description });\r\n }\r\n if (provided.author === undefined && !options.yes) {\r\n remainingPrompts.push({ type: 'input', name: 'author', message: 'Author:', default: defaults.author });\r\n }\r\n if (provided.port === undefined && !options.yes) {\r\n remainingPrompts.push({ type: 'number', name: 'port', message: 'Development server port:', default: defaults.port });\r\n }\r\n const promptAnswers = remainingPrompts.length > 0 ? await inquirer.prompt(remainingPrompts) : {};\r\n const answers = {\r\n displayName: provided.displayName ?? promptAnswers.displayName ?? defaults.displayName,\r\n description: provided.description ?? promptAnswers.description ?? defaults.description,\r\n author: provided.author ?? promptAnswers.author ?? defaults.author,\r\n port: provided.port ?? promptAnswers.port ?? defaults.port,\r\n };\r\n\r\n // 检查目录是否存在且非空\r\n const dirExists = await fs.pathExists(targetDir);\r\n if (dirExists) {\r\n const isEmpty = await isDirectoryEmpty(targetDir);\r\n if (!isEmpty) {\r\n const { overwrite } = await inquirer.prompt([\r\n {\r\n type: 'confirm',\r\n name: 'overwrite',\r\n message: `Directory ${chalk.cyan(targetDir)} is not empty. Overwrite?`,\r\n default: false,\r\n },\r\n ]);\r\n\r\n if (!overwrite) {\r\n console.log(chalk.yellow('❌ Cancelled'));\r\n process.exit(0);\r\n }\r\n\r\n await fs.remove(targetDir);\r\n }\r\n }\r\n\r\n // 5. 创建目录\r\n await ensureDirectory(targetDir);\r\n\r\n // 6. 复制模板\r\n const spinner = ora('Copying template files...').start();\r\n const templateDir = getTemplateDir(selectedTemplate);\r\n\r\n if (!(await fs.pathExists(templateDir))) {\r\n spinner.fail(chalk.red(`Template directory not found: ${templateDir}`));\r\n process.exit(1);\r\n }\r\n\r\n await copyTemplate(templateDir, targetDir);\r\n spinner.succeed('Template files copied');\r\n\r\n // 7. 更新文件内容\r\n spinner.start('Updating files...');\r\n\r\n // 定义占位符替换映射\r\n const replacements: Record<string, string> = {\r\n PLUGIN_NAME: pluginName!,\r\n PLUGIN_DISPLAY_NAME: answers.displayName,\r\n PLUGIN_DESCRIPTION: answers.description,\r\n PLUGIN_AUTHOR: answers.author || '',\r\n PLUGIN_VERSION: '0.0.1',\r\n DEV_PORT: answers.port.toString(),\r\n PLUGIN_SIMPLE_NAME: simpleName,\r\n PLUGIN_CLASS_NAME: `solazah-${simpleName}`,\r\n };\r\n\r\n // 批量替换目录中的占位符\r\n await replaceInDirectory(targetDir, replacements);\r\n\r\n // 额外处理特定文件(JSON 文件需要特殊处理)\r\n const packageJsonPath = path.join(targetDir, 'package.json');\r\n const packageJson = await fs.readJson(packageJsonPath);\r\n packageJson.name = pluginName;\r\n packageJson.version = '0.0.1';\r\n packageJson.description = answers.description;\r\n if (answers.author) {\r\n packageJson.author = answers.author;\r\n }\r\n packageJson.scripts.dev = `vite --port ${answers.port}`;\r\n await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });\r\n\r\n const manifestPath = path.join(targetDir, 'manifest.json');\r\n const manifest = await fs.readJson(manifestPath);\r\n manifest.name = pluginName;\r\n manifest.displayName = answers.displayName;\r\n manifest.commands = [answers.displayName];\r\n manifest.development.main = `http://localhost:${answers.port}`;\r\n await fs.writeJson(manifestPath, manifest, { spaces: 2 });\r\n\r\n // 删除不需要的文件\r\n const filesToRemove = [\r\n 'template.json',\r\n ];\r\n for (const file of filesToRemove) {\r\n const filePath = path.join(targetDir, file);\r\n if (await fs.pathExists(filePath)) {\r\n await fs.remove(filePath);\r\n }\r\n }\r\n\r\n spinner.succeed('Files updated');\r\n\r\n // 8. 安装依赖\r\n if (!options.skipInstall) {\r\n spinner.start('Installing dependencies...');\r\n try {\r\n await execAsync('npm install', { cwd: targetDir });\r\n spinner.succeed('Dependencies installed');\r\n } catch (error) {\r\n spinner.fail('Failed to install dependencies');\r\n console.log(chalk.yellow('You can install them manually by running: npm install'));\r\n }\r\n }\r\n\r\n // 9. 完成\r\n console.log(chalk.green.bold('\\n✅ Plugin created successfully!\\n'));\r\n console.log(chalk.cyan('Next steps:'));\r\n const relativePath = path.relative(process.cwd(), targetDir);\r\n if (relativePath && relativePath !== '.') {\r\n console.log(chalk.gray(` cd ${relativePath}`));\r\n }\r\n if (options.skipInstall) {\r\n console.log(chalk.gray(' npm install'));\r\n }\r\n console.log(chalk.gray(' npm run dev'));\r\n console.log();\r\n } catch (error) {\r\n console.error(chalk.red('❌ Error:'), error);\r\n process.exit(1);\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface PackagePluginOptions {\r\n dir?: string;\r\n}\r\n\r\ntype FileCopyEntry = string | { from: string; to: string };\r\n\r\n/**\r\n * 根据 manifest.json 推断需要复制到 release/ 的文件列表\r\n */\r\nfunction resolveFilesToCopy(projectDir: string, manifest: Record<string, unknown>): FileCopyEntry[] {\r\n const entries: FileCopyEntry[] = [];\r\n\r\n // manifest.json 始终需要\r\n entries.push('manifest.json');\r\n\r\n // 可选文档文件\r\n for (const doc of ['CHANGELOG.md', 'README.md','icon.png']) {\r\n if (fs.existsSync(path.join(projectDir, doc))) {\r\n entries.push(doc);\r\n }\r\n }\r\n\r\n // 根据 manifest.main 推断构建产物目标目录\r\n // e.g. \"renderer/index.html\" → dist -> renderer\r\n // e.g. \"index.html\" → dist -> .\r\n const main = manifest.main as string | undefined;\r\n if (main) {\r\n const mainDir = path.dirname(main);\r\n entries.push({ from: 'dist', to: mainDir === '.' ? '.' : mainDir });\r\n } else {\r\n entries.push({ from: 'dist', to: '.' });\r\n }\r\n\r\n // preload 目录\r\n const preload = manifest.preload as string | undefined;\r\n if (preload) {\r\n const preloadDir = path.dirname(preload); // e.g. \"preload/preload.mjs\" -> \"preload\"\r\n const srcPreload = path.join(projectDir, 'src', 'preload');\r\n if (fs.existsSync(srcPreload)) {\r\n entries.push({ from: 'src/preload', to: preloadDir });\r\n }\r\n }\r\n\r\n // skills 目录\r\n const srcSkills = path.join(projectDir, 'src', 'skills');\r\n if (fs.existsSync(srcSkills)) {\r\n entries.push({ from: 'src/skills', to: 'skills' });\r\n }\r\n\r\n // screenshots 目录\r\n const srcScreenshots = path.join(projectDir, 'src', 'screenshots');\r\n if (fs.existsSync(srcScreenshots)) {\r\n entries.push({ from: 'src/screenshots', to: 'screenshots' });\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/**\r\n * 清理并初始化 release 目录\r\n */\r\nfunction cleanReleaseDir(releaseDir: string): void {\r\n if (fs.existsSync(releaseDir)) {\r\n fs.rmSync(releaseDir, { recursive: true, force: true });\r\n }\r\n fs.mkdirSync(releaseDir, { recursive: true });\r\n}\r\n\r\n/**\r\n * 按照文件列表复制到 release 目录\r\n */\r\nfunction copyFiles(projectDir: string, releaseDir: string, files: FileCopyEntry[]): void {\r\n for (const file of files) {\r\n if (typeof file === 'string') {\r\n const src = path.resolve(projectDir, file);\r\n const dest = path.resolve(releaseDir, file);\r\n fs.cpSync(src, dest, { recursive: true });\r\n } else {\r\n const src = path.resolve(projectDir, file.from);\r\n const dest = path.resolve(releaseDir, file.to);\r\n fs.cpSync(src, dest, { recursive: true });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 精简 package.json,只保留发布需要的字段\r\n */\r\nfunction processPackageJson(projectDir: string, releaseDir: string): void {\r\n const raw = fs.readFileSync(path.resolve(projectDir, 'package.json'), 'utf-8');\r\n const pkg = JSON.parse(raw);\r\n\r\n const cleaned: Record<string, unknown> = {\r\n name: pkg.name,\r\n main: pkg.main,\r\n type: pkg.type,\r\n version: pkg.version,\r\n description: pkg.description,\r\n author: pkg.author,\r\n license: pkg.license,\r\n homepage: pkg.homepage,\r\n repository: pkg.repository,\r\n keywords: pkg.keywords,\r\n peerDependencies: pkg.peerDependencies,\r\n };\r\n\r\n fs.writeFileSync(\r\n path.resolve(releaseDir, 'package.json'),\r\n JSON.stringify(cleaned, null, 2),\r\n );\r\n}\r\n\r\n/**\r\n * 清理构建产物临时目录 (dist/)\r\n * Windows 上 cpSync 之后文件句柄可能尚未完全释放,rmSync 会报 ENOTEMPTY。\r\n * 加重试逻辑,等待系统释放句柄后再删除。\r\n */\r\nfunction cleanDistDir(projectDir: string): void {\r\n const distDir = path.join(projectDir, 'dist');\r\n if (!fs.existsSync(distDir)) return;\r\n\r\n const maxRetries = 5;\r\n const retryDelayMs = 200;\r\n\r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n fs.rmSync(distDir, { recursive: true, force: true });\r\n return;\r\n } catch (err: any) {\r\n if (attempt === maxRetries) throw err;\r\n // 同步等待后重试(仅用于磁盘 I/O 竞态,不阻塞事件循环中的业务逻辑)\r\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelayMs);\r\n }\r\n }\r\n}\r\n\r\nexport async function packagePluginCommand(options: PackagePluginOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n console.log(chalk.cyan.bold('\\n📦 Solazah Plugin Packager\\n'));\r\n\r\n // 1. 验证项目目录\r\n const packageJsonPath = path.join(projectDir, 'package.json');\r\n if (!(await fs.pathExists(packageJsonPath))) {\r\n console.error(chalk.red('❌ No package.json found in the target directory.'));\r\n process.exit(1);\r\n }\r\n\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!(await fs.pathExists(manifestPath))) {\r\n console.error(chalk.red('❌ No manifest.json found. Is this a Solazah plugin project?'));\r\n process.exit(1);\r\n }\r\n\r\n const packageData = await fs.readJson(packageJsonPath);\r\n const manifest = await fs.readJson(manifestPath);\r\n console.log(chalk.gray(`Plugin: ${packageData.name || 'unknown'} v${packageData.version || '0.0.0'}\\n`));\r\n\r\n // 2. 构建项目\r\n const buildSpinner = ora('Building plugin...').start();\r\n try {\r\n await execAsync('npm run build', { cwd: projectDir });\r\n buildSpinner.succeed('Build completed');\r\n } catch (error: any) {\r\n buildSpinner.fail('Build failed');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 3. 打包到 release/\r\n const packageSpinner = ora('Packaging plugin...').start();\r\n try {\r\n const releaseDir = path.join(projectDir, 'release');\r\n const filesToCopy = resolveFilesToCopy(projectDir, manifest);\r\n\r\n cleanReleaseDir(releaseDir);\r\n copyFiles(projectDir, releaseDir, filesToCopy);\r\n processPackageJson(projectDir, releaseDir);\r\n cleanDistDir(projectDir);\r\n\r\n packageSpinner.succeed('Package created in release/');\r\n } catch (error: any) {\r\n packageSpinner.fail('Packaging failed');\r\n console.error(chalk.red(error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 4. 生成 .tgz 文件到 release/\r\n const tgzSpinner = ora('Creating .tgz archive...').start();\r\n try {\r\n const { stdout } = await execAsync('npm pack ./release --pack-destination ./release', { cwd: projectDir });\r\n const tgzFile = stdout.trim();\r\n tgzSpinner.succeed(`Archive created: ${chalk.cyan('release/' + tgzFile)}`);\r\n } catch (error: any) {\r\n tgzSpinner.fail('Failed to create .tgz archive');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.green.bold('\\n✅ Plugin packaged successfully!\\n'));\r\n}\r\n","import path from 'path';\r\nimport os from 'os';\r\nimport fs from 'fs-extra';\r\n\r\nexport interface StoredTokens {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n scope?: string;\r\n expires_at?: number; // epoch seconds\r\n issuer?: string;\r\n}\r\n\r\nexport interface TokenResponse {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n expires_in?: number;\r\n scope?: string;\r\n}\r\n\r\nconst AUTH_DIR = path.join(os.homedir(), '.solazah');\r\nconst AUTH_FILE = path.join(AUTH_DIR, 'auth.json');\r\nconst EXPIRY_SKEW_SECONDS = 30;\r\n\r\nexport async function saveTokens(tokens: StoredTokens): Promise<void> {\r\n await fs.ensureDir(AUTH_DIR);\r\n await fs.writeJson(AUTH_FILE, tokens, { spaces: 2 });\r\n try {\r\n await fs.chmod(AUTH_FILE, 0o600);\r\n } catch {\r\n // ignore chmod errors on Windows\r\n }\r\n}\r\n\r\nexport async function loadTokens(): Promise<StoredTokens | null> {\r\n try {\r\n return (await fs.readJson(AUTH_FILE)) as StoredTokens;\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return null;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function clearTokens(): Promise<void> {\r\n try {\r\n await fs.remove(AUTH_FILE);\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function isExpired(tokens: StoredTokens): boolean {\r\n if (!tokens.expires_at) return false;\r\n return Math.floor(Date.now() / 1000) >= tokens.expires_at - EXPIRY_SKEW_SECONDS;\r\n}\r\n\r\nexport function toStoredTokens(tokens: TokenResponse, issuer: string): StoredTokens {\r\n const now = Math.floor(Date.now() / 1000);\r\n return {\r\n access_token: tokens.access_token,\r\n refresh_token: tokens.refresh_token,\r\n id_token: tokens.id_token,\r\n token_type: tokens.token_type,\r\n scope: tokens.scope,\r\n expires_at: tokens.expires_in ? now + tokens.expires_in : undefined,\r\n issuer,\r\n };\r\n}\r\n\r\n/**\r\n * 合并刷新后的令牌:若服务器未返回新的 refresh_token 则保留原值。\r\n */\r\nexport function mergeRefreshedTokens(\r\n previous: StoredTokens,\r\n refreshed: TokenResponse,\r\n issuer: string,\r\n): StoredTokens {\r\n const next = toStoredTokens(refreshed, issuer);\r\n if (!next.refresh_token) {\r\n next.refresh_token = previous.refresh_token;\r\n }\r\n return next;\r\n}\r\n","import { TokenResponse } from './auth-storage.js';\r\n\r\nexport const DEFAULT_ISSUER = 'https://solazah.seayona.com/account';\r\nexport const DEFAULT_CLIENT_ID = 'solazah-cli';\r\nexport const DEFAULT_CLIENT_SECRET = 'solazah-cli';\r\nexport const DEFAULT_SCOPE = 'openid profile offline_access';\r\nexport const DEFAULT_TOKEN_TYPE = 'Bearer';\r\n\r\n/**\r\n * 授权服务器默认把 verification_uri 指向后端的表单端点\r\n * (/account/oauth2/device_verification),但我们的前端是 SPA,\r\n * 直接让用户访问后端表单会看到空白页。这里统一重写为 SPA 的\r\n * /#/device hash 路由,由前端引导用户登录并提交 user_code。\r\n */\r\nexport const DEFAULT_VERIFICATION_URI = 'https://solazah.seayona.com/account/#/device';\r\n\r\nexport const GRANT_TYPE = {\r\n DEVICE_CODE: 'urn:ietf:params:oauth:grant-type:device_code',\r\n REFRESH_TOKEN: 'refresh_token',\r\n} as const;\r\n\r\nexport const TOKEN_HINT = {\r\n ACCESS: 'access_token',\r\n REFRESH: 'refresh_token',\r\n} as const;\r\nexport type TokenTypeHint = typeof TOKEN_HINT[keyof typeof TOKEN_HINT];\r\n\r\nexport const DEVICE_ERROR = {\r\n AUTH_PENDING: 'authorization_pending',\r\n SLOW_DOWN: 'slow_down',\r\n ACCESS_DENIED: 'access_denied',\r\n EXPIRED_TOKEN: 'expired_token',\r\n} as const;\r\n\r\nexport interface OidcDiscovery {\r\n issuer: string;\r\n device_authorization_endpoint?: string;\r\n token_endpoint: string;\r\n userinfo_endpoint?: string;\r\n end_session_endpoint?: string;\r\n revocation_endpoint?: string;\r\n}\r\n\r\nexport interface DeviceAuthorizationResponse {\r\n device_code: string;\r\n user_code: string;\r\n verification_uri: string;\r\n verification_uri_complete?: string;\r\n expires_in: number;\r\n interval?: number;\r\n}\r\n\r\nexport interface UserInfo {\r\n sub: string;\r\n name?: string;\r\n username?: string;\r\n preferred_username?: string;\r\n email?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport async function discover(issuer: string = DEFAULT_ISSUER): Promise<OidcDiscovery> {\r\n const url = `${issuer.replace(/\\/+$/, '')}/.well-known/openid-configuration`;\r\n const res = await fetch(url);\r\n if (!res.ok) {\r\n throw new Error(`Failed to load OIDC discovery (${res.status}): ${url}`);\r\n }\r\n return (await res.json()) as OidcDiscovery;\r\n}\r\n\r\n/**\r\n * OAuth 端点 POST:application/x-www-form-urlencoded;\r\n * 使用 client_secret_post 方式认证,client_id 和 client_secret 放在表单中。\r\n */\r\nasync function postForm(\r\n url: string,\r\n params: Record<string, string>,\r\n): Promise<{ ok: boolean; status: number; data: Record<string, unknown> }> {\r\n const body = new URLSearchParams(params);\r\n const res = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n Accept: 'application/json',\r\n },\r\n body: body.toString(),\r\n redirect: 'manual',\r\n });\r\n\r\n // 服务器返回 3xx 重定向通常意味着客户端认证失败或端点配置错误\r\n if (res.status >= 300 && res.status < 400) {\r\n const location = res.headers.get('location') ?? '(unknown)';\r\n throw new Error(\r\n `服务器返回了重定向 (${res.status}) → ${location},` +\r\n `请确认客户端 client_id 已在授权服务器注册为公共客户端`,\r\n );\r\n }\r\n\r\n const text = await res.text();\r\n let data: Record<string, unknown>;\r\n try {\r\n data = JSON.parse(text) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(\r\n `服务器返回了非 JSON 响应 (${res.status}): ${text.slice(0, 200)}`,\r\n );\r\n }\r\n return { ok: res.ok, status: res.status, data };\r\n}\r\n\r\n/** 安全提取 string */\r\nfunction str(v: unknown): string | undefined {\r\n return typeof v === 'string' ? v : undefined;\r\n}\r\n/** 安全提取 number */\r\nfunction num(v: unknown): number | undefined {\r\n return typeof v === 'number' ? v : undefined;\r\n}\r\n\r\nexport async function requestDeviceCode(options: {\r\n discovery: OidcDiscovery;\r\n clientId?: string;\r\n scope?: string;\r\n}): Promise<DeviceAuthorizationResponse> {\r\n const { discovery } = options;\r\n if (!discovery.device_authorization_endpoint) {\r\n throw new Error('授权服务器未提供 device_authorization_endpoint');\r\n }\r\n const { ok, status, data } = await postForm(discovery.device_authorization_endpoint, {\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n scope: options.scope ?? DEFAULT_SCOPE,\r\n });\r\n if (!ok) {\r\n throw new Error(`设备授权请求失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n\r\n // 兼容 snake_case(RFC 8628 标准)和 camelCase 两种响应格式\r\n const deviceCode = str(data.device_code) ?? str(data.deviceCode);\r\n const userCode = str(data.user_code) ?? str(data.userCode);\r\n const expiresIn = num(data.expires_in) ?? num(data.expiresIn);\r\n const interval = num(data.interval);\r\n\r\n if (!deviceCode || !userCode || expiresIn == null) {\r\n throw new Error(\r\n `设备授权响应缺少必要字段(device_code/user_code/expires_in),` +\r\n `实际响应:${JSON.stringify(data)}`,\r\n );\r\n }\r\n\r\n // 授权服务器默认返回后端表单端点作为 verification_uri,\r\n // 这里覆写为 SPA 地址,保证用户打开后能看到前端页面。\r\n const verificationUri = DEFAULT_VERIFICATION_URI;\r\n const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(userCode)}`;\r\n\r\n return {\r\n device_code: deviceCode,\r\n user_code: userCode,\r\n verification_uri: verificationUri,\r\n verification_uri_complete: verificationUriComplete,\r\n expires_in: expiresIn,\r\n interval,\r\n };\r\n}\r\n\r\nexport type PollStatus = 'success' | 'pending' | 'slow_down' | 'denied' | 'expired' | 'error';\r\n\r\nexport interface PollResult {\r\n status: PollStatus;\r\n tokens?: TokenResponse;\r\n error?: string;\r\n errorDescription?: string;\r\n}\r\n\r\nexport async function pollToken(options: {\r\n discovery: OidcDiscovery;\r\n deviceCode: string;\r\n clientId?: string;\r\n}): Promise<PollResult> {\r\n const { ok, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.DEVICE_CODE,\r\n device_code: options.deviceCode,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n\r\n if (ok && typeof data.access_token === 'string') {\r\n return { status: 'success', tokens: tokenResponseFrom(data) };\r\n }\r\n\r\n const err = typeof data.error === 'string' ? data.error : 'error';\r\n const errorDescription =\r\n typeof data.error_description === 'string' ? data.error_description : undefined;\r\n const status: PollStatus =\r\n err === DEVICE_ERROR.AUTH_PENDING ? 'pending'\r\n : err === DEVICE_ERROR.SLOW_DOWN ? 'slow_down'\r\n : err === DEVICE_ERROR.ACCESS_DENIED ? 'denied'\r\n : err === DEVICE_ERROR.EXPIRED_TOKEN ? 'expired'\r\n : 'error';\r\n return { status, error: err, errorDescription };\r\n}\r\n\r\nexport async function fetchUserInfo(options: {\r\n discovery: OidcDiscovery;\r\n accessToken: string;\r\n}): Promise<UserInfo> {\r\n if (!options.discovery.userinfo_endpoint) {\r\n throw new Error('授权服务器未提供 userinfo_endpoint');\r\n }\r\n const res = await fetch(options.discovery.userinfo_endpoint, {\r\n headers: {\r\n Authorization: `Bearer ${options.accessToken}`,\r\n Accept: 'application/json',\r\n },\r\n });\r\n if (!res.ok) {\r\n const text = await res.text();\r\n throw new Error(`获取用户信息失败 (${res.status}): ${text}`);\r\n }\r\n return (await res.json()) as UserInfo;\r\n}\r\n\r\nexport async function refreshTokens(options: {\r\n discovery: OidcDiscovery;\r\n refreshToken: string;\r\n clientId?: string;\r\n}): Promise<TokenResponse> {\r\n const { ok, status, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.REFRESH_TOKEN,\r\n refresh_token: options.refreshToken,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n if (!ok) {\r\n throw new Error(`刷新令牌失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n return tokenResponseFrom(data);\r\n}\r\n\r\n/**\r\n * 撤销指定类型的令牌;best-effort,出错不抛出。\r\n */\r\nexport async function revokeToken(options: {\r\n discovery: OidcDiscovery;\r\n token: string;\r\n tokenTypeHint?: TokenTypeHint;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) {\r\n return;\r\n }\r\n const params: Record<string, string> = {\r\n token: options.token,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n };\r\n if (options.tokenTypeHint) {\r\n params.token_type_hint = options.tokenTypeHint;\r\n }\r\n await postForm(options.discovery.revocation_endpoint, params).catch(() => {\r\n // best-effort\r\n });\r\n}\r\n\r\n/**\r\n * 并发撤销 access_token 与 refresh_token。\r\n */\r\nexport async function revokeAll(options: {\r\n discovery: OidcDiscovery;\r\n accessToken?: string;\r\n refreshToken?: string;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) return;\r\n const jobs: Promise<void>[] = [];\r\n if (options.refreshToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.refreshToken,\r\n tokenTypeHint: TOKEN_HINT.REFRESH,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n if (options.accessToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.accessToken,\r\n tokenTypeHint: TOKEN_HINT.ACCESS,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n await Promise.all(jobs);\r\n}\r\n\r\nfunction tokenResponseFrom(data: Record<string, unknown>): TokenResponse {\r\n return {\r\n access_token: String(data.access_token ?? ''),\r\n refresh_token: typeof data.refresh_token === 'string' ? data.refresh_token : undefined,\r\n id_token: typeof data.id_token === 'string' ? data.id_token : undefined,\r\n token_type: typeof data.token_type === 'string' ? data.token_type : DEFAULT_TOKEN_TYPE,\r\n expires_in: typeof data.expires_in === 'number' ? data.expires_in : undefined,\r\n scope: typeof data.scope === 'string' ? data.scope : undefined,\r\n };\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n loadTokens,\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { discover, refreshTokens } from '../../utils/oauth-device.js';\r\n\r\nconst MARKETPLACE_BASE_URL = 'https://solazah.seayona.com/marketplace';\r\n\r\ninterface PublishOptions {\r\n file?: string;\r\n dir?: string;\r\n}\r\n\r\nasync function ensureAuth(): Promise<StoredTokens> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.red('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n if (isExpired(tokens) && tokens.refresh_token) {\r\n const spinner = ora('令牌已过期,正在刷新...').start();\r\n try {\r\n const discovery = await discover(tokens.issuer);\r\n const refreshed = await refreshTokens({ discovery, refreshToken: tokens.refresh_token });\r\n const updated = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(updated);\r\n spinner.succeed('令牌刷新成功');\r\n return updated;\r\n } catch {\r\n spinner.fail('令牌刷新失败,请重新登录:solazah-cli account login');\r\n process.exit(1);\r\n }\r\n }\r\n\r\n return tokens;\r\n}\r\n\r\n/**\r\n * 根据 manifest.json 的 name 和 version 推算 release/ 中的 .tgz 文件路径\r\n * npm pack 生成的文件名规则:@scope/pkg-name -> scope-pkg-name-version.tgz\r\n */\r\nfunction findTgzByManifest(projectDir: string): string {\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!fs.existsSync(manifestPath)) {\r\n console.log(chalk.red('❌ 未找到 manifest.json,无法确定插件包文件名'));\r\n process.exit(1);\r\n }\r\n\r\n const manifest = fs.readJsonSync(manifestPath);\r\n const name: string = manifest.name;\r\n const version: string = manifest.version;\r\n\r\n if (!name || !version) {\r\n console.log(chalk.red('❌ manifest.json 中缺少 name 或 version 字段'));\r\n process.exit(1);\r\n }\r\n\r\n // @scope/pkg-name -> scope-pkg-name, pkg-name -> pkg-name\r\n const tgzName = name.replace(/^@/, '').replace(/\\//, '-');\r\n const tgzFile = `${tgzName}-${version}.tgz`;\r\n\r\n return path.join(projectDir, 'release', tgzFile);\r\n}\r\n\r\nexport async function publishPluginCommand(options: PublishOptions): Promise<void> {\r\n let filePath: string;\r\n\r\n if (options.file) {\r\n filePath = path.resolve(options.file);\r\n } else {\r\n const projectDir = path.resolve(options.dir || '.');\r\n filePath = findTgzByManifest(projectDir);\r\n }\r\n\r\n // 检查文件是否存在\r\n if (!await fs.pathExists(filePath)) {\r\n console.log(chalk.red(`文件不存在:${filePath}`));\r\n process.exit(1);\r\n }\r\n\r\n // 检查文件扩展名\r\n if (!filePath.endsWith('.tgz')) {\r\n console.log(chalk.red('仅支持 .tgz 格式的插件包'));\r\n process.exit(1);\r\n }\r\n\r\n // 检查登录状态 & 刷新令牌\r\n const tokens = await ensureAuth();\r\n\r\n const fileName = path.basename(filePath);\r\n const spinner = ora(`正在发布插件包:${fileName}`).start();\r\n\r\n try {\r\n const fileBuffer = await fs.readFile(filePath);\r\n const blob = new Blob([fileBuffer], { type: 'application/gzip' });\r\n\r\n const formData = new FormData();\r\n formData.append('package', blob, fileName);\r\n\r\n const res = await fetch(`${MARKETPLACE_BASE_URL}/api/v1/publish-requests`, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${tokens.access_token}`,\r\n },\r\n body: formData,\r\n });\r\n\r\n const data = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const code = (data as Record<string, unknown>).code ?? '';\r\n const message = (data as Record<string, unknown>).message ?? res.statusText;\r\n if (code === 'DUPLICATE_SUBMISSION') {\r\n spinner.fail('发布失败:该版本已提交过发布申请,请勿重复提交');\r\n } else {\r\n spinner.fail(`发布失败 (${res.status}): ${message}`);\r\n }\r\n process.exit(1);\r\n }\r\n\r\n const result = data as Record<string, unknown>;\r\n spinner.succeed('插件发布申请已提交');\r\n console.log('');\r\n console.log(chalk.green(' 申请详情:'));\r\n console.log(` 申请ID: ${result.pluginPublishRequestId}`);\r\n console.log(` 插件名称: ${result.pluginName}`);\r\n console.log(` 状态: ${result.status}`);\r\n console.log('');\r\n console.log(chalk.gray(' 发布申请已提交,等待管理员审核。'));\r\n } catch (err) {\r\n spinner.fail(`发布失败:${(err as Error).message}`);\r\n process.exit(1);\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport semver from 'semver';\r\nimport { promisify } from 'util';\r\nimport { exec } from 'child_process';\r\nimport conventionalChangelog from 'conventional-changelog-core';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface VersionOptions {\r\n dir?: string;\r\n releaseAs?: string;\r\n}\r\n\r\n// 读取 .versionrc(如果存在),提取自定义 commit type → section 映射\r\nfunction loadVersionrc(projectDir: string): Record<string, unknown> {\r\n for (const name of ['.versionrc', '.versionrc.json']) {\r\n const p = path.join(projectDir, name);\r\n if (fs.existsSync(p)) {\r\n try {\r\n return fs.readJsonSync(p);\r\n } catch {\r\n // ignore malformed versionrc\r\n }\r\n }\r\n }\r\n return {};\r\n}\r\n\r\n// 把 .versionrc 中的 types 数组转成 conventional-changelog-core 需要的 parserOpts / writerOpts\r\nfunction buildChangelogConfig(versionrc: Record<string, unknown>) {\r\n const types = versionrc.types as Array<{ type: string; section?: string; hidden?: boolean }> | undefined;\r\n if (!types) return {};\r\n\r\n const parserOpts = {\r\n noteKeywords: ['BREAKING CHANGE', 'BREAKING-CHANGE'],\r\n };\r\n\r\n const writerOpts = {\r\n transform: (commit: Record<string, unknown>) => {\r\n const match = types.find((t) => t.type === commit.type);\r\n if (!match) return false;\r\n if (match.hidden) return false;\r\n commit.type = match.section ?? commit.type;\r\n return commit;\r\n },\r\n groupBy: 'type',\r\n commitGroupsSort: 'title',\r\n commitsSort: ['scope', 'subject'],\r\n };\r\n\r\n return { parserOpts, writerOpts };\r\n}\r\n\r\n// 生成本次发布的 changelog 片段\r\nasync function generateChangelogEntry(projectDir: string, newVersion: string, versionrc: Record<string, unknown>): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n const { parserOpts, writerOpts } = buildChangelogConfig(versionrc);\r\n const header = (versionrc.header as string | undefined) ?? '# CHANGELOG\\n\\n';\r\n\r\n const stream = conventionalChangelog(\r\n { preset: 'angular', parserOpts, writerOpts } as Parameters<typeof conventionalChangelog>[0],\r\n { version: newVersion } as Parameters<typeof conventionalChangelog>[1],\r\n undefined,\r\n undefined,\r\n undefined,\r\n );\r\n\r\n let content = '';\r\n stream.on('data', (chunk: Buffer | string) => { content += chunk.toString(); });\r\n stream.on('end', () => resolve(content));\r\n stream.on('error', reject);\r\n\r\n // cwd 必须在插件项目目录,conventional-changelog 默认从当前目录读 git log\r\n (stream as any).cwd = projectDir;\r\n });\r\n}\r\n\r\n// 更新文件中的 version 字段(package.json / manifest.json)\r\nfunction bumpVersionInFile(filePath: string, newVersion: string): void {\r\n if (!fs.existsSync(filePath)) return;\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n // 只替换顶层 \"version\" 字段,避免误改嵌套结构\r\n const updated = content.replace(\r\n /^(\\s*\"version\"\\s*:\\s*)\"[^\"]*\"/m,\r\n `$1\"${newVersion}\"`,\r\n );\r\n fs.writeFileSync(filePath, updated, 'utf-8');\r\n}\r\n\r\n// 追加 changelog 片段到 CHANGELOG.md(在现有内容之前)\r\nfunction prependChangelog(projectDir: string, entry: string, header: string): void {\r\n const changelogPath = path.join(projectDir, 'CHANGELOG.md');\r\n const existing = fs.existsSync(changelogPath) ? fs.readFileSync(changelogPath, 'utf-8') : '';\r\n // 去掉已有的 header 行,再重新写\r\n const body = existing.startsWith(header.trim())\r\n ? existing.slice(header.trim().length).trimStart()\r\n : existing;\r\n fs.writeFileSync(changelogPath, header.trim() + '\\n\\n' + entry + (body ? '\\n' + body : ''), 'utf-8');\r\n}\r\n\r\nexport async function versionPluginCommand(options: VersionOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n const packageJsonPath = path.join(projectDir, 'package.json');\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n\r\n if (!(await fs.pathExists(packageJsonPath))) {\r\n console.error(chalk.red('❌ No package.json found.'));\r\n process.exit(1);\r\n }\r\n\r\n const pkg = await fs.readJson(packageJsonPath);\r\n const currentVersion: string = pkg.version ?? '0.0.0';\r\n\r\n // 1. 确定 release type\r\n let releaseType = options.releaseAs || 'patch';\r\n\r\n // 2. 计算新版本号\r\n const newVersion = semver.inc(currentVersion, releaseType as semver.ReleaseType);\r\n if (!newVersion) {\r\n console.error(chalk.red(`❌ Invalid version: ${currentVersion} + ${releaseType}`));\r\n process.exit(1);\r\n }\r\n\r\n const versionrc = loadVersionrc(projectDir);\r\n const header = (versionrc.header as string | undefined) ?? '# CHANGELOG\\n\\n';\r\n\r\n console.log(chalk.gray(`\\n ${currentVersion} → ${chalk.green(newVersion)}\\n`));\r\n\r\n // 3. 生成 changelog 片段\r\n const changelogSpinner = ora('Generating changelog...').start();\r\n let changelogEntry = '';\r\n try {\r\n const prevCwd = process.cwd();\r\n process.chdir(projectDir);\r\n changelogEntry = await generateChangelogEntry(projectDir, newVersion, versionrc);\r\n process.chdir(prevCwd);\r\n changelogSpinner.succeed('Changelog generated');\r\n } catch (err: any) {\r\n changelogSpinner.warn(`Changelog generation skipped: ${err.message}`);\r\n }\r\n\r\n // 4. 更新版本号\r\n const bumpSpinner = ora('Bumping version files...').start();\r\n bumpVersionInFile(packageJsonPath, newVersion);\r\n bumpVersionInFile(path.join(projectDir, 'package-lock.json'), newVersion);\r\n bumpVersionInFile(manifestPath, newVersion);\r\n bumpSpinner.succeed('Version files updated');\r\n\r\n // 5. 写入 CHANGELOG.md\r\n if (changelogEntry.trim()) {\r\n prependChangelog(projectDir, changelogEntry.trim(), header);\r\n ora('').succeed('CHANGELOG.md updated');\r\n }\r\n\r\n // 6. git commit + tag\r\n const gitSpinner = ora('Creating git commit and tag...').start();\r\n try {\r\n const filesToStage = ['package.json', 'manifest.json', 'CHANGELOG.md', 'package-lock.json']\r\n .filter((f) => fs.existsSync(path.join(projectDir, f)));\r\n\r\n await execAsync(`git add ${filesToStage.join(' ')}`, { cwd: projectDir });\r\n await execAsync(`git commit -m \"chore(release): ${newVersion}\"`, { cwd: projectDir });\r\n\r\n // 尝试打 tag,已存在则跳过\r\n try {\r\n await execAsync(`git tag -a v${newVersion} -m \"chore(release): ${newVersion}\"`, { cwd: projectDir });\r\n } catch (tagErr: any) {\r\n if (!tagErr.message?.includes('already exists')) throw tagErr;\r\n gitSpinner.warn(`Tag v${newVersion} already exists, skipping`);\r\n return;\r\n }\r\n\r\n gitSpinner.succeed(`Tagged release v${newVersion}`);\r\n } catch (err: any) {\r\n gitSpinner.fail(`Git operations failed: ${err.stderr || err.message}`);\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.green.bold(`\\n✅ Version bumped to ${newVersion}\\n`));\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport { versionPluginCommand } from './version.js';\r\nimport { packagePluginCommand } from './package.js';\r\nimport { publishPluginCommand } from './publish.js';\r\n\r\ninterface ReleaseOptions {\r\n dir?: string;\r\n}\r\n\r\nexport async function releasePluginCommand(options: ReleaseOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n if (!(await fs.pathExists(path.join(projectDir, 'package.json')))) {\r\n console.error(chalk.red('❌ No package.json found in the target directory.'));\r\n process.exit(1);\r\n }\r\n\r\n // 1. 版本升级\r\n await versionPluginCommand({ dir: projectDir });\r\n\r\n // 2. 打包\r\n await packagePluginCommand({ dir: projectDir });\r\n\r\n // 3. 发布\r\n await publishPluginCommand({ dir: projectDir });\r\n}\r\n","import { spawn } from 'child_process';\r\n\r\n/**\r\n * 跨平台打开浏览器,best-effort:失败时静默返回 false,由调用方提示用户手动打开。\r\n */\r\nexport function openBrowser(url: string): boolean {\r\n try {\r\n const platform = process.platform;\r\n let command: string;\r\n let args: string[];\r\n\r\n if (platform === 'win32') {\r\n command = 'cmd';\r\n args = ['/c', 'start', '\"\"', url.replace(/&/g, '^&')];\r\n } else if (platform === 'darwin') {\r\n command = 'open';\r\n args = [url];\r\n } else {\r\n command = 'xdg-open';\r\n args = [url];\r\n }\r\n\r\n const child = spawn(command, args, {\r\n detached: true,\r\n stdio: 'ignore',\r\n shell: false,\r\n });\r\n child.on('error', () => {\r\n // ignore; caller already printed the URL\r\n });\r\n child.unref();\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import {\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n DEFAULT_ISSUER,\r\n discover,\r\n OidcDiscovery,\r\n refreshTokens,\r\n} from '../../utils/oauth-device.js';\r\n\r\nexport interface CommonOptions {\r\n issuer?: string;\r\n}\r\n\r\nexport const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\nexport function loadDiscovery(issuer?: string): Promise<OidcDiscovery> {\r\n return discover(issuer || DEFAULT_ISSUER);\r\n}\r\n\r\nexport async function ensureValidTokens(\r\n discovery: OidcDiscovery,\r\n tokens: StoredTokens,\r\n): Promise<StoredTokens> {\r\n if (!isExpired(tokens)) {\r\n return tokens;\r\n }\r\n if (!tokens.refresh_token) {\r\n throw new Error('访问令牌已过期且无 refresh_token,请重新登录');\r\n }\r\n const refreshed = await refreshTokens({\r\n discovery,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n const stored = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(stored);\r\n return stored;\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n saveTokens,\r\n toStoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n fetchUserInfo,\r\n pollToken,\r\n requestDeviceCode,\r\n} from '../../utils/oauth-device.js';\r\nimport { openBrowser } from '../../utils/open-browser.js';\r\nimport { CommonOptions, delay, loadDiscovery } from './shared.js';\r\n\r\nexport async function loginCommand(options: CommonOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\nSolazah 账户登录\\n'));\r\n\r\n const spinner = ora('加载授权服务器元数据...').start();\r\n let discovery;\r\n try {\r\n discovery = await loadDiscovery(options.issuer);\r\n spinner.succeed('已加载授权服务器元数据');\r\n } catch (err) {\r\n spinner.fail('加载授权服务器元数据失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n spinner.start('申请设备授权码...');\r\n let device;\r\n try {\r\n device = await requestDeviceCode({ discovery });\r\n spinner.succeed('已获取设备授权码');\r\n } catch (err) {\r\n spinner.fail('申请设备授权码失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n const verificationUri = device.verification_uri_complete || device.verification_uri;\r\n\r\n console.log();\r\n console.log(chalk.bold('请在浏览器中完成授权:'));\r\n if (device.verification_uri_complete) {\r\n console.log(` 一键链接:${chalk.cyan(device.verification_uri_complete)}`);\r\n }\r\n console.log(` 用户码 :${chalk.yellow.bold(device.user_code)}`);\r\n console.log(` 有效期 :${device.expires_in} 秒`);\r\n console.log();\r\n\r\n const opened = openBrowser(verificationUri);\r\n console.log(chalk.gray(\r\n opened\r\n ? '已尝试为你打开浏览器,若未自动打开请手动访问上面的一键链接。'\r\n : '请手动在浏览器中打开上面的一键链接。',\r\n ));\r\n console.log();\r\n\r\n // RFC 8628 建议的最小轮询间隔为 5 秒\r\n let interval = Math.max(device.interval ?? 5, 5);\r\n const deadline = Date.now() + device.expires_in * 1000;\r\n\r\n const poll = ora('等待用户授权...').start();\r\n while (Date.now() < deadline) {\r\n await delay(interval * 1000);\r\n let result;\r\n try {\r\n result = await pollToken({ discovery, deviceCode: device.device_code });\r\n } catch (err) {\r\n // 网络抖动等瞬时异常:继续轮询直到截止时间\r\n poll.text = `轮询出错,将重试:${(err as Error).message}`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'success' && result.tokens) {\r\n const stored = toStoredTokens(result.tokens, discovery.issuer);\r\n await saveTokens(stored);\r\n poll.succeed('授权成功');\r\n\r\n if (discovery.userinfo_endpoint) {\r\n try {\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: stored.access_token,\r\n });\r\n console.log();\r\n console.log(chalk.green('已登录:'));\r\n console.log(` sub :${info.sub}`);\r\n const username = info.username ?? info.preferred_username;\r\n if (username) console.log(` username:${username}`);\r\n if (info.name) console.log(` name :${info.name}`);\r\n if (info.email) console.log(` email :${info.email}`);\r\n } catch {\r\n // 打印用户信息失败不影响登录结果\r\n }\r\n }\r\n console.log();\r\n return;\r\n }\r\n\r\n if (result.status === 'pending') continue;\r\n\r\n if (result.status === 'slow_down') {\r\n interval += 5;\r\n poll.text = `服务器要求降低轮询频率(${interval}s)...`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'denied') {\r\n poll.fail('用户拒绝了授权');\r\n process.exit(1);\r\n }\r\n\r\n if (result.status === 'expired') {\r\n poll.fail('设备码已过期,请重新执行登录命令');\r\n process.exit(1);\r\n }\r\n\r\n // 其他错误视为瞬时错误,继续轮询直到截止时间\r\n poll.text = `授权失败(将重试):${result.error}${result.errorDescription ? ' - ' + result.errorDescription : ''}`;\r\n }\r\n poll.fail('等待授权超时,请重新执行登录命令');\r\n process.exit(1);\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n clearTokens,\r\n loadTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { revokeAll } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, loadDiscovery } from './shared.js';\r\n\r\nexport async function logoutCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录。'));\r\n return;\r\n }\r\n\r\n const spinner = ora('正在登出...').start();\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer).catch(() => null);\r\n if (discovery) {\r\n await revokeAll({\r\n discovery,\r\n accessToken: tokens.access_token,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n }\r\n await clearTokens();\r\n spinner.succeed('已登出');\r\n } catch (err) {\r\n await clearTokens();\r\n spinner.warn('本地凭证已清除,但撤销令牌时发生错误');\r\n console.error(chalk.gray((err as Error).message));\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadTokens } from '../../utils/auth-storage.js';\r\nimport { fetchUserInfo } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, ensureValidTokens, loadDiscovery } from './shared.js';\r\n\r\nexport async function userinfoCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer);\r\n const valid = await ensureValidTokens(discovery, tokens);\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: valid.access_token,\r\n });\r\n console.log(JSON.stringify(info, null, 2));\r\n } catch (err) {\r\n console.error(chalk.red('获取用户信息失败:'), (err as Error).message);\r\n process.exit(1);\r\n }\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\nimport { createPluginCommand } from './commands/plugin/create.js';\r\nimport { packagePluginCommand } from './commands/plugin/package.js';\r\nimport { publishPluginCommand } from './commands/plugin/publish.js';\r\nimport { versionPluginCommand } from './commands/plugin/version.js';\r\nimport { releasePluginCommand } from './commands/plugin/release.js';\r\nimport { loginCommand } from './commands/account/login.js';\r\nimport { logoutCommand } from './commands/account/logout.js';\r\nimport { userinfoCommand } from './commands/account/userinfo.js';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('solazah-cli')\r\n .description('Solazah CLI - Command line tool for Solazah plugin development')\r\n .version(\"0.0.1\");\r\n\r\n// Plugin commands\r\nconst plugin = program\r\n .command('plugin')\r\n .description('Manage Solazah plugins');\r\n\r\nplugin\r\n .command('create')\r\n .description('Create a new Solazah plugin')\r\n .option('-n, --name <name>', 'Plugin name (e.g., my-plugin or @scope/plugin-name)')\r\n .option('-d, --dir <directory>', 'Parent directory; final target is <dir>/<plugin-name>', '.')\r\n .option('--target-dir <path>', 'Full target directory; overrides --dir')\r\n .option('-t, --template <template>', 'Template to use (e.g., react)', 'react')\r\n .option('--display-name <name>', 'Display name (skips prompt if provided)')\r\n .option('--description <desc>', 'Plugin description (skips prompt if provided)')\r\n .option('--author <author>', 'Author (skips prompt if provided)')\r\n .option('--port <port>', 'Dev server port (skips prompt if provided)', (v) => parseInt(v, 10))\r\n .option('-y, --yes', 'Skip all prompts and accept defaults for any missing answers')\r\n .option('--skip-install', 'Skip npm install')\r\n .action(createPluginCommand);\r\n\r\nplugin\r\n .command('package')\r\n .description('Build and package a Solazah plugin for distribution')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(packagePluginCommand);\r\n\r\nplugin\r\n .command('publish')\r\n .description('Publish a plugin package to Solazah marketplace')\r\n .option('-f, --file <file>', 'Path to the .tgz plugin package file')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(publishPluginCommand);\r\n\r\nplugin\r\n .command('version')\r\n .description('Bump plugin version, update CHANGELOG and create git tag')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .option('-r, --release-as <type>', 'Specify release type: major, minor, patch')\r\n .action(versionPluginCommand);\r\n\r\nplugin\r\n .command('release')\r\n .description('Bump version, package and publish a plugin in one step')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(releasePluginCommand);\r\n\r\n// Account commands (OAuth2 Device Authorization Grant)\r\nconst account = program\r\n .command('account')\r\n .description('Manage Solazah account authentication');\r\n\r\nconst withIssuer = (cmd: Command) =>\r\n cmd.option('--issuer <issuer>', 'OAuth2 issuer URL');\r\n\r\nwithIssuer(account.command('login'))\r\n .description('Login via OAuth2 device code flow')\r\n .action(loginCommand);\r\n\r\nwithIssuer(account.command('logout'))\r\n .description('Logout and revoke stored tokens')\r\n .action(logoutCommand);\r\n\r\nwithIssuer(account.command('userinfo'))\r\n .description('Show current user info')\r\n .action(userinfoCommand);\r\n\r\nprogram.parseAsync().catch((err) => {\r\n console.error(err);\r\n process.exit(1);\r\n});\r\n"],"names":["__filename","__dirname","execAsync","answers"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@solazah/solazah-cli",
|
|
3
2
|
"bin": {
|
|
4
|
-
"solazah-cli": "
|
|
3
|
+
"solazah-cli": "index.js"
|
|
5
4
|
},
|
|
5
|
+
"name": "@solazah/solazah-cli",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.2.
|
|
7
|
+
"version": "0.2.20",
|
|
8
8
|
"description": "Solazah CLI - Command line tool for Solazah plugin development",
|
|
9
9
|
"author": "",
|
|
10
10
|
"license": "MIT",
|
|
@@ -13,15 +13,5 @@
|
|
|
13
13
|
"cli",
|
|
14
14
|
"plugin",
|
|
15
15
|
"scaffold"
|
|
16
|
-
]
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"chalk": "^5.4.1",
|
|
19
|
-
"commander": "^12.1.0",
|
|
20
|
-
"conventional-changelog-core": "^9.0.0",
|
|
21
|
-
"fs-extra": "^11.2.0",
|
|
22
|
-
"inquirer": "^10.0.0",
|
|
23
|
-
"ora": "^8.1.1",
|
|
24
|
-
"semver": "^7.7.4",
|
|
25
|
-
"validate-npm-package-name": "^6.0.0"
|
|
26
|
-
}
|
|
16
|
+
]
|
|
27
17
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface StoredTokens {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token?: string;
|
|
4
|
+
id_token?: string;
|
|
5
|
+
token_type: string;
|
|
6
|
+
scope?: string;
|
|
7
|
+
expires_at?: number;
|
|
8
|
+
issuer?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TokenResponse {
|
|
11
|
+
access_token: string;
|
|
12
|
+
refresh_token?: string;
|
|
13
|
+
id_token?: string;
|
|
14
|
+
token_type: string;
|
|
15
|
+
expires_in?: number;
|
|
16
|
+
scope?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function saveTokens(tokens: StoredTokens): Promise<void>;
|
|
19
|
+
export declare function loadTokens(): Promise<StoredTokens | null>;
|
|
20
|
+
export declare function clearTokens(): Promise<void>;
|
|
21
|
+
export declare function isExpired(tokens: StoredTokens): boolean;
|
|
22
|
+
export declare function toStoredTokens(tokens: TokenResponse, issuer: string): StoredTokens;
|
|
23
|
+
/**
|
|
24
|
+
* 合并刷新后的令牌:若服务器未返回新的 refresh_token 则保留原值。
|
|
25
|
+
*/
|
|
26
|
+
export declare function mergeRefreshedTokens(previous: StoredTokens, refreshed: TokenResponse, issuer: string): StoredTokens;
|
|
27
|
+
//# sourceMappingURL=auth-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/utils/auth-storage.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpE;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAS/D;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAMjD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAGvD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAWlF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,MAAM,GACb,YAAY,CAMd"}
|
package/utils/file.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface TemplateInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
description: string;
|
|
5
|
+
version: string;
|
|
6
|
+
features?: string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 检查目录是否为空
|
|
10
|
+
*/
|
|
11
|
+
export declare function isDirectoryEmpty(dirPath: string): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* 确保目录存在
|
|
14
|
+
*/
|
|
15
|
+
export declare function ensureDirectory(dirPath: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* 获取模板根目录路径
|
|
18
|
+
*/
|
|
19
|
+
export declare function getTemplatesRootDir(): string;
|
|
20
|
+
/**
|
|
21
|
+
* 获取指定模板的目录路径
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTemplateDir(templateName: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* 获取所有可用模板
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAvailableTemplates(): Promise<TemplateInfo[]>;
|
|
28
|
+
/**
|
|
29
|
+
* 复制模板文件
|
|
30
|
+
*/
|
|
31
|
+
export declare function copyTemplate(templateDir: string, targetDir: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* 替换文件内容中的占位符
|
|
34
|
+
* 支持 {{PLACEHOLDER}} 格式
|
|
35
|
+
*/
|
|
36
|
+
export declare function replacePlaceholders(filePath: string, replacements: Record<string, string>): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 批量替换目录中所有文件的占位符
|
|
39
|
+
*/
|
|
40
|
+
export declare function replaceInDirectory(dirPath: string, replacements: Record<string, string>, extensions?: string[]): Promise<void>;
|
|
41
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/utils/file.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOxE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAsBrE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQxF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,UAAU,GAAE,MAAM,EAA4D,GAC7E,OAAO,CAAC,IAAI,CAAC,CAiBf"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { TokenResponse } from './auth-storage.js';
|
|
2
|
+
export declare const DEFAULT_ISSUER = "https://solazah.seayona.com/account";
|
|
3
|
+
export declare const DEFAULT_CLIENT_ID = "solazah-cli";
|
|
4
|
+
export declare const DEFAULT_CLIENT_SECRET = "solazah-cli";
|
|
5
|
+
export declare const DEFAULT_SCOPE = "openid profile offline_access";
|
|
6
|
+
export declare const DEFAULT_TOKEN_TYPE = "Bearer";
|
|
7
|
+
/**
|
|
8
|
+
* 授权服务器默认把 verification_uri 指向后端的表单端点
|
|
9
|
+
* (/account/oauth2/device_verification),但我们的前端是 SPA,
|
|
10
|
+
* 直接让用户访问后端表单会看到空白页。这里统一重写为 SPA 的
|
|
11
|
+
* /#/device hash 路由,由前端引导用户登录并提交 user_code。
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_VERIFICATION_URI = "https://solazah.seayona.com/account/#/device";
|
|
14
|
+
export declare const GRANT_TYPE: {
|
|
15
|
+
readonly DEVICE_CODE: "urn:ietf:params:oauth:grant-type:device_code";
|
|
16
|
+
readonly REFRESH_TOKEN: "refresh_token";
|
|
17
|
+
};
|
|
18
|
+
export declare const TOKEN_HINT: {
|
|
19
|
+
readonly ACCESS: "access_token";
|
|
20
|
+
readonly REFRESH: "refresh_token";
|
|
21
|
+
};
|
|
22
|
+
export type TokenTypeHint = typeof TOKEN_HINT[keyof typeof TOKEN_HINT];
|
|
23
|
+
export declare const DEVICE_ERROR: {
|
|
24
|
+
readonly AUTH_PENDING: "authorization_pending";
|
|
25
|
+
readonly SLOW_DOWN: "slow_down";
|
|
26
|
+
readonly ACCESS_DENIED: "access_denied";
|
|
27
|
+
readonly EXPIRED_TOKEN: "expired_token";
|
|
28
|
+
};
|
|
29
|
+
export interface OidcDiscovery {
|
|
30
|
+
issuer: string;
|
|
31
|
+
device_authorization_endpoint?: string;
|
|
32
|
+
token_endpoint: string;
|
|
33
|
+
userinfo_endpoint?: string;
|
|
34
|
+
end_session_endpoint?: string;
|
|
35
|
+
revocation_endpoint?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface DeviceAuthorizationResponse {
|
|
38
|
+
device_code: string;
|
|
39
|
+
user_code: string;
|
|
40
|
+
verification_uri: string;
|
|
41
|
+
verification_uri_complete?: string;
|
|
42
|
+
expires_in: number;
|
|
43
|
+
interval?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface UserInfo {
|
|
46
|
+
sub: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
username?: string;
|
|
49
|
+
preferred_username?: string;
|
|
50
|
+
email?: string;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
export declare function discover(issuer?: string): Promise<OidcDiscovery>;
|
|
54
|
+
export declare function requestDeviceCode(options: {
|
|
55
|
+
discovery: OidcDiscovery;
|
|
56
|
+
clientId?: string;
|
|
57
|
+
scope?: string;
|
|
58
|
+
}): Promise<DeviceAuthorizationResponse>;
|
|
59
|
+
export type PollStatus = 'success' | 'pending' | 'slow_down' | 'denied' | 'expired' | 'error';
|
|
60
|
+
export interface PollResult {
|
|
61
|
+
status: PollStatus;
|
|
62
|
+
tokens?: TokenResponse;
|
|
63
|
+
error?: string;
|
|
64
|
+
errorDescription?: string;
|
|
65
|
+
}
|
|
66
|
+
export declare function pollToken(options: {
|
|
67
|
+
discovery: OidcDiscovery;
|
|
68
|
+
deviceCode: string;
|
|
69
|
+
clientId?: string;
|
|
70
|
+
}): Promise<PollResult>;
|
|
71
|
+
export declare function fetchUserInfo(options: {
|
|
72
|
+
discovery: OidcDiscovery;
|
|
73
|
+
accessToken: string;
|
|
74
|
+
}): Promise<UserInfo>;
|
|
75
|
+
export declare function refreshTokens(options: {
|
|
76
|
+
discovery: OidcDiscovery;
|
|
77
|
+
refreshToken: string;
|
|
78
|
+
clientId?: string;
|
|
79
|
+
}): Promise<TokenResponse>;
|
|
80
|
+
/**
|
|
81
|
+
* 撤销指定类型的令牌;best-effort,出错不抛出。
|
|
82
|
+
*/
|
|
83
|
+
export declare function revokeToken(options: {
|
|
84
|
+
discovery: OidcDiscovery;
|
|
85
|
+
token: string;
|
|
86
|
+
tokenTypeHint?: TokenTypeHint;
|
|
87
|
+
clientId?: string;
|
|
88
|
+
}): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* 并发撤销 access_token 与 refresh_token。
|
|
91
|
+
*/
|
|
92
|
+
export declare function revokeAll(options: {
|
|
93
|
+
discovery: OidcDiscovery;
|
|
94
|
+
accessToken?: string;
|
|
95
|
+
refreshToken?: string;
|
|
96
|
+
clientId?: string;
|
|
97
|
+
}): Promise<void>;
|
|
98
|
+
//# sourceMappingURL=oauth-device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-device.d.ts","sourceRoot":"","sources":["../../src/utils/oauth-device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/utils/open-browser.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA8BhD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
errors: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 验证插件名称
|
|
7
|
+
* 接受任何合法的 npm 包名
|
|
8
|
+
*/
|
|
9
|
+
export declare function validatePluginName(name: string): ValidationResult;
|
|
10
|
+
/**
|
|
11
|
+
* 从包名提取插件名称
|
|
12
|
+
* @example extractPluginName('@solazah/solazah-plugin-example') => 'solazah-plugin-example'
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractPluginName(packageName: string): string;
|
|
15
|
+
export declare function extractPluginSimpleName(packageName: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* 生成显示名称
|
|
18
|
+
* @example generateDisplayName('example') => 'Example'
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateDisplayName(name: string): string;
|
|
21
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAwBjE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,UAQ1D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKxD"}
|