黄中银 2 недель назад
Сommit
4677c42e9f
76 измененных файлов с 22191 добавлено и 0 удалено
  1. 39 0
      .gitignore
  2. 7 0
      .vscode/settings.json
  3. 40 0
      CLAUDE.md
  4. 97 0
      build-win.bat
  5. 171 0
      electron/main.ts
  6. 61 0
      electron/modules/claude-code-installer.ts
  7. 182 0
      electron/modules/config.ts
  8. 155 0
      electron/modules/constants.ts
  9. 10 0
      electron/modules/index.ts
  10. 1207 0
      electron/modules/installer.ts
  11. 616 0
      electron/modules/ipc-handlers.ts
  12. 74 0
      electron/modules/ipc-registry.ts
  13. 262 0
      electron/modules/logger.ts
  14. 65 0
      electron/modules/source-updater.ts
  15. 4 0
      electron/modules/types.ts
  16. 297 0
      electron/modules/updater.ts
  17. 746 0
      electron/modules/utils.ts
  18. 742 0
      electron/modules/version-fetcher.ts
  19. 93 0
      electron/modules/vscode-extension.ts
  20. 170 0
      electron/preload.ts
  21. 25 0
      eslint.config.js
  22. 13 0
      index.html
  23. 164 0
      launcher/launcher.js
  24. 10574 0
      package-lock.json
  25. 184 0
      package.json
  26. BIN
      public/icons/mac/icon.icns
  27. BIN
      public/icons/png/1024x1024.png
  28. BIN
      public/icons/png/128x128.png
  29. BIN
      public/icons/png/16x16.png
  30. BIN
      public/icons/png/24x24.png
  31. BIN
      public/icons/png/256x256.png
  32. BIN
      public/icons/png/32x32.png
  33. BIN
      public/icons/png/48x48.png
  34. BIN
      public/icons/png/512x512.png
  35. BIN
      public/icons/png/64x64.png
  36. BIN
      public/icons/win/icon.ico
  37. 110 0
      release-win.bat
  38. 283 0
      scripts/README.md
  39. 468 0
      scripts/build.js
  40. 461 0
      scripts/release.js
  41. 226 0
      shared/types.ts
  42. 268 0
      src/App.vue
  43. 45 0
      src/components/common/ErrorBoundary.vue
  44. 62 0
      src/components/common/InstallLog.vue
  45. 99 0
      src/components/common/MirrorSelector.vue
  46. 42 0
      src/components/common/NetworkStatus.vue
  47. 76 0
      src/components/common/SettingsPanel.vue
  48. 24 0
      src/components/common/SkeletonLoader.vue
  49. 84 0
      src/components/common/SoftwareIcon.vue
  50. 128 0
      src/components/common/TitleBar.vue
  51. 204 0
      src/i18n/en-US.ts
  52. 41 0
      src/i18n/index.ts
  53. 204 0
      src/i18n/zh-CN.ts
  54. 28 0
      src/main.ts
  55. 10 0
      src/stores/index.ts
  56. 271 0
      src/stores/install.ts
  57. 111 0
      src/stores/settings.ts
  58. 182 0
      src/stores/system.ts
  59. 139 0
      src/stores/versions.ts
  60. 461 0
      src/styles/main.scss
  61. 64 0
      src/styles/variables.scss
  62. 4 0
      src/types/electron.d.ts
  63. 390 0
      src/views/BatchInstallView.vue
  64. 228 0
      src/views/ClaudeCodeView.vue
  65. 170 0
      src/views/GitView.vue
  66. 97 0
      src/views/IntroView.vue
  67. 260 0
      src/views/NodejsView.vue
  68. 287 0
      src/views/VscodeView.vue
  69. 7 0
      src/vite-env.d.ts
  70. 185 0
      tests/constants.test.ts
  71. 128 0
      tests/ipc-registry.test.ts
  72. 199 0
      tests/shared-types.test.ts
  73. 33 0
      tsconfig.json
  74. 11 0
      tsconfig.node.json
  75. 70 0
      vite.config.ts
  76. 33 0
      vitest.config.ts

+ 39 - 0
.gitignore

@@ -0,0 +1,39 @@
+# Dependencies
+node_modules/
+
+# Build outputs
+dist/
+dist-electron/
+release/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+logs/
+
+# Environment
+.env
+.env.local
+.env.*.local
+
+# TypeScript
+*.tsbuildinfo
+
+# Auto-generated
+src/auto-imports.d.ts
+src/components.d.ts
+
+# Backup
+_backup/
+
+# Temp
+nul

+ 7 - 0
.vscode/settings.json

@@ -0,0 +1,7 @@
+{
+  "files.watcherExclude": {
+    "**/release/**": true,
+    "**/dist-electron/**": true,
+    "**/dist/**": true
+  }
+}

+ 40 - 0
CLAUDE.md

@@ -0,0 +1,40 @@
+# ApqInstaller 项目配置
+
+## 项目概述
+ApqInstaller 是一个基于 Electron + Vue 3 的桌面应用,用于帮助用户安装 Claude Code 及 VS Code 插件。
+
+## 技术栈
+- **前端框架**: Vue 3 + TypeScript
+- **构建工具**: Vite
+- **桌面框架**: Electron
+- **状态管理**: Pinia
+- **国际化**: vue-i18n
+- **样式**: SCSS
+
+## 项目结构
+- `electron/` - Electron 主进程代码
+- `src/` - Vue 前端代码
+  - `components/` - Vue 组件
+  - `views/` - 页面视图
+  - `stores/` - Pinia 状态管理
+  - `i18n/` - 国际化配置
+  - `styles/` - 样式文件
+- `scripts/` - 构建和发布脚本
+- `launcher/` - 启动器相关代码
+
+## 开发命令
+- `npm run dev` - 启动开发服务器
+- `npm run build` - 构建生产版本
+- `build-win.bat` - Windows 平台构建
+- `release-win.bat` - Windows 平台发布
+
+## 编码规范
+- 使用 TypeScript 进行类型安全开发
+- 遵循 ESLint 配置的代码规范
+- 组件使用 Vue 3 Composition API
+
+## 编译构建规范
+- 在windows上构建时使用build-win.bat。
+
+## 测试规范
+- 不要在本机进行软件安装功能的测试,因为本项目支持安装的软件均已安装过了,重复安装可能会引起问题。

+ 97 - 0
build-win.bat

@@ -0,0 +1,97 @@
+@echo off
+chcp 65001 >nul 2>&1
+
+:: ============================================
+:: Claude AI Installer Windows 构建脚本
+:: Build script for Windows (NSIS + Portable)
+:: 此脚本作为 scripts/build.js 的简单入口
+:: ============================================
+
+:: 切换到脚本所在目录
+cd /d "%~dp0"
+
+:: 检查是否请求帮助
+if /i "%~1"=="--help" goto :show_help
+if /i "%~1"=="-h" goto :show_help
+
+:: 检查 Node.js
+where node >nul 2>&1
+if errorlevel 1 (
+    echo   [91mx[0m 未找到 Node.js,请先安装 Node.js
+    pause
+    exit /b 1
+)
+
+:: 检查 node_modules
+if not exist "node_modules" (
+    echo   [93m![0m 未找到 node_modules,正在安装依赖...
+    call npm install
+    if errorlevel 1 (
+        echo   [91mx[0m 安装依赖失败
+        pause
+        exit /b 1
+    )
+)
+
+:: 转换参数并调用 Node.js 脚本
+:: 将 bat 参数映射到 build.js 参数
+set "NODE_ARGS=-p win"
+
+:parse_args
+if "%~1"=="" goto :run_build
+if /i "%~1"=="--skip-build" set "NODE_ARGS=%NODE_ARGS% --skip-build" & shift & goto :parse_args
+if /i "%~1"=="-s" set "NODE_ARGS=%NODE_ARGS% --skip-build" & shift & goto :parse_args
+if /i "%~1"=="--skip-typecheck" set "NODE_ARGS=%NODE_ARGS% --skip-typecheck" & shift & goto :parse_args
+if /i "%~1"=="--arch" set "NODE_ARGS=%NODE_ARGS% --arch %~2" & shift & shift & goto :parse_args
+if /i "%~1"=="-a" set "NODE_ARGS=%NODE_ARGS% --arch %~2" & shift & shift & goto :parse_args
+shift
+goto :parse_args
+
+:run_build
+echo.
+echo   [96m^> 调用 Node.js 构建脚本...[0m
+echo   [94mi[0m 执行: node scripts/build.js %NODE_ARGS%
+echo.
+
+call node scripts/build.js %NODE_ARGS%
+if errorlevel 1 (
+    echo.
+    echo   [91mx[0m 构建失败!
+    echo.
+    pause
+    exit /b 1
+)
+
+echo.
+pause
+goto :eof
+
+:: ============================================
+:: 显示帮助信息
+:: ============================================
+:show_help
+echo.
+echo   Claude AI Installer Windows 构建脚本
+echo.
+echo   用法:
+echo     build-win.bat [参数]
+echo.
+echo   参数:
+echo     --skip-build, -s     跳过前端构建,仅执行打包
+echo     --skip-typecheck     跳过 TypeScript 类型检查
+echo     --arch, -a ^<arch^>    目标架构: x64, arm64, all (默认: x64)
+echo     --help, -h           显示此帮助信息
+echo.
+echo   示例:
+echo     build-win.bat                    完整构建
+echo     build-win.bat -s                 仅打包
+echo     build-win.bat -a all             构建所有架构
+echo     build-win.bat --skip-typecheck   跳过类型检查
+echo.
+echo   输出:
+echo     release\Claude AI Installer-x.x.x-win-x64-nsis.exe      (NSIS 安装程序)
+echo     release\Claude AI Installer-x.x.x-win-x64-portable.exe  (便携版)
+echo.
+echo   注意: 此脚本调用 scripts/build.js 执行实际构建
+echo.
+goto :eof

+ 171 - 0
electron/main.ts

@@ -0,0 +1,171 @@
+// electron/main.ts - Electron 主进程入口
+
+import { app, BrowserWindow, Menu } from 'electron'
+import * as path from 'path'
+import * as os from 'os'
+import { registerHandlers, removeHandlers } from './modules/ipc-handlers'
+import { logger } from './modules/logger'
+import { initConfig, getWindowBounds, saveWindowBounds } from './modules/config'
+import { initAutoUpdater } from './modules/updater'
+
+// 全局窗口实例
+let mainWindow: BrowserWindow | null = null
+
+// 开发环境判断
+const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
+
+// 根据平台获取图标路径
+function getIconPath(): string {
+  const platform = os.platform()
+  // 开发环境使用 public 目录,生产环境使用打包后的路径
+  const basePath = isDev
+    ? path.join(__dirname, '../public/icons')
+    : path.join(process.resourcesPath, 'icons')
+
+  if (platform === 'win32') {
+    return path.join(basePath, 'win/icon.ico')
+  } else if (platform === 'darwin') {
+    return path.join(basePath, 'mac/icon.icns')
+  } else {
+    return path.join(basePath, 'png/256x256.png')
+  }
+}
+
+/**
+ * 创建主窗口
+ */
+function createWindow(): void {
+  // 获取保存的窗口位置
+  const savedBounds = getWindowBounds()
+
+  mainWindow = new BrowserWindow({
+    width: savedBounds?.width ?? 1050,
+    height: savedBounds?.height ?? 810,
+    x: savedBounds?.x,
+    y: savedBounds?.y,
+    minWidth: 800,
+    minHeight: 810,
+    resizable: true, // 允许调整大小
+    frame: false, // 无边框窗口
+    titleBarStyle: 'hidden', // 隐藏标题栏
+    icon: getIconPath(),
+    webPreferences: {
+      preload: path.join(__dirname, 'preload.js'),
+      contextIsolation: true,
+      nodeIntegration: false,
+      sandbox: true
+    }
+  })
+
+  // 开发环境加载 Vite 开发服务器
+  if (isDev) {
+    mainWindow.loadURL('http://localhost:5173')
+    mainWindow.webContents.openDevTools({ mode: 'detach' })
+  } else {
+    // 生产环境加载打包后的文件
+    mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
+  }
+
+  // 窗口移动或调整大小时保存位置
+  mainWindow.on('moved', () => {
+    if (mainWindow) {
+      saveWindowBounds(mainWindow.getBounds())
+    }
+  })
+
+  mainWindow.on('resized', () => {
+    if (mainWindow) {
+      saveWindowBounds(mainWindow.getBounds())
+    }
+  })
+
+  mainWindow.on('closed', () => {
+    mainWindow = null
+  })
+
+  logger.info('主窗口已创建')
+}
+
+/**
+ * 应用初始化
+ */
+async function initialize(): Promise<void> {
+  // 初始化日志模块
+  logger.init()
+
+  // 写入安装日志,标记程序启动
+  logger.installInfo(`========== 程序启动 ${new Date().toLocaleString()} ==========`)
+
+  logger.info('应用启动', {
+    platform: os.platform(),
+    arch: os.arch(),
+    nodeVersion: process.version,
+    electronVersion: process.versions.electron,
+    isDev
+  })
+
+  // 初始化配置
+  await initConfig()
+
+  // 注册 IPC 处理器
+  registerHandlers()
+
+  // 隐藏菜单栏
+  Menu.setApplicationMenu(null)
+
+  // 创建窗口
+  createWindow()
+
+  // 生产环境初始化自动更新
+  if (!isDev && mainWindow) {
+    initAutoUpdater(mainWindow)
+  }
+
+  // 注册窗口级别的 F12 快捷键切换开发者工具
+  mainWindow?.webContents.on('before-input-event', (event, input) => {
+    if (input.key === 'F12' && input.type === 'keyDown') {
+      if (mainWindow?.webContents.isDevToolsOpened()) {
+        mainWindow.webContents.closeDevTools()
+      } else {
+        mainWindow?.webContents.openDevTools()
+      }
+      event.preventDefault()
+    }
+  })
+}
+
+// 应用就绪
+app.whenReady().then(initialize)
+
+// 所有窗口关闭
+app.on('window-all-closed', () => {
+  logger.info('所有窗口已关闭')
+
+  if (os.platform() !== 'darwin') {
+    app.quit()
+  }
+})
+
+// macOS 激活
+app.on('activate', () => {
+  if (BrowserWindow.getAllWindows().length === 0) {
+    createWindow()
+  }
+})
+
+// 应用退出前
+app.on('before-quit', () => {
+  logger.info('应用即将退出')
+  removeHandlers()
+  logger.close()
+})
+
+// 未捕获的异常
+process.on('uncaughtException', (error) => {
+  logger.error('未捕获的异常', error)
+})
+
+// 未处理的 Promise 拒绝
+process.on('unhandledRejection', (reason, promise) => {
+  logger.error('未处理的 Promise 拒绝', { reason, promise: String(promise) })
+})

+ 61 - 0
electron/modules/claude-code-installer.ts

@@ -0,0 +1,61 @@
+// electron/modules/claude-code-installer.ts - Claude Code 安装模块
+
+import { BrowserWindow } from 'electron'
+import type { SoftwareTypeWithAll } from './types'
+import { installClaudeCode as installClaudeCodeCore } from './installer'
+import logger from './logger'
+
+/**
+ * 发送状态到渲染进程
+ */
+function sendToRenderer(channel: string, data: unknown): void {
+  const windows = BrowserWindow.getAllWindows()
+  windows.forEach((win) => {
+    win.webContents.send(channel, data)
+  })
+}
+
+/**
+ * 安装 Claude Code
+ * 供 IPC handler 和 installer.ts 复用
+ * @returns 安装结果
+ */
+export async function installClaudeCode(): Promise<{ success: boolean; error?: string }> {
+  try {
+    // 状态回调,将状态发送到渲染进程
+    const onStatus = (software: SoftwareTypeWithAll, message: string, progress: number, skipLog?: boolean): void => {
+      sendToRenderer('install-status', { software, message, progress, skipLog })
+      if (!skipLog) {
+        logger.installInfo(`[${software}] ${message} (${progress}%)`)
+      }
+    }
+
+    await installClaudeCodeCore(onStatus)
+
+    sendToRenderer('install-complete', {
+      software: 'claudeCode',
+      message: '✅ Claude Code 安装完成!',
+      i18nKey: 'log.claudeCodeInstallSuccess'
+    })
+    return { success: true }
+  } catch (error) {
+    const execaError = error as { message?: string; stderr?: string; stdout?: string; shortMessage?: string }
+    let errorMessage = execaError.shortMessage || execaError.message || '未知错误'
+
+    // 过滤乱码字符
+    if (execaError.stderr) {
+      const stderrClean = execaError.stderr.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
+      if (stderrClean) {
+        errorMessage = `${errorMessage}\n${stderrClean}`
+      }
+    }
+
+    logger.installError('Claude Code 安装失败', error)
+    sendToRenderer('install-error', {
+      software: 'claudeCode',
+      message: `❌ 安装失败:${errorMessage}`,
+      i18nKey: 'log.claudeCodeInstallFailed'
+    })
+    return { success: false, error: errorMessage }
+  }
+}

+ 182 - 0
electron/modules/config.ts

@@ -0,0 +1,182 @@
+// electron/modules/config.ts - 配置管理模块
+
+import * as fs from 'fs/promises'
+import * as fsSync from 'fs'
+import * as path from 'path'
+import { app } from 'electron'
+import type { InstallHistoryItem, GitMirrorType, NodejsMirrorType } from './types'
+import { DEFAULT_GIT_MIRROR, DEFAULT_NODEJS_MIRROR } from './constants'
+import logger from './logger'
+
+interface WindowBounds {
+  x: number
+  y: number
+  width: number
+  height: number
+}
+
+interface AppConfig {
+  installHistory: InstallHistoryItem[]
+  theme: 'light' | 'dark' | 'system'
+  language: 'zh-CN' | 'en-US'
+  windowBounds?: WindowBounds
+  gitMirror: GitMirrorType
+  nodejsMirror: NodejsMirrorType
+}
+
+const DEFAULT_CONFIG: AppConfig = {
+  installHistory: [],
+  theme: 'system',
+  language: 'zh-CN',
+  gitMirror: DEFAULT_GIT_MIRROR,
+  nodejsMirror: DEFAULT_NODEJS_MIRROR
+}
+
+let configPath: string = ''
+let currentConfig: AppConfig = { ...DEFAULT_CONFIG }
+
+/**
+ * 初始化配置
+ */
+export async function initConfig(): Promise<void> {
+  configPath = path.join(app.getPath('userData'), 'config.json')
+  await loadConfig()
+}
+
+/**
+ * 加载配置
+ */
+async function loadConfig(): Promise<void> {
+  try {
+    if (fsSync.existsSync(configPath)) {
+      const data = await fs.readFile(configPath, 'utf-8')
+      const loaded = JSON.parse(data) as Partial<AppConfig>
+
+      currentConfig = {
+        ...DEFAULT_CONFIG,
+        ...loaded
+      }
+      logger.info('配置加载成功')
+    }
+  } catch (error) {
+    logger.error('加载配置失败,使用默认配置', error)
+    currentConfig = { ...DEFAULT_CONFIG }
+  }
+}
+
+/**
+ * 保存配置(异步,不阻塞主进程)
+ */
+function saveConfig(): void {
+  fs.writeFile(configPath, JSON.stringify(currentConfig, null, 2), 'utf-8')
+    .then(() => logger.debug('配置保存成功'))
+    .catch((error) => logger.error('保存配置失败', error))
+}
+
+/**
+ * 获取主题配置
+ */
+export function getThemeConfig(): 'light' | 'dark' | 'system' {
+  return currentConfig.theme
+}
+
+/**
+ * 保存主题配置
+ */
+export function saveThemeConfig(theme: 'light' | 'dark' | 'system'): void {
+  currentConfig.theme = theme
+  saveConfig()
+}
+
+/**
+ * 获取语言配置
+ */
+export function getLanguageConfig(): 'zh-CN' | 'en-US' {
+  return currentConfig.language
+}
+
+/**
+ * 保存语言配置
+ */
+export function saveLanguageConfig(language: 'zh-CN' | 'en-US'): void {
+  currentConfig.language = language
+  saveConfig()
+}
+
+/**
+ * 添加安装历史
+ */
+export function addInstallHistory(item: Omit<InstallHistoryItem, 'timestamp'>): void {
+  const historyItem: InstallHistoryItem = {
+    ...item,
+    timestamp: Date.now()
+  }
+  currentConfig.installHistory.unshift(historyItem)
+
+  // 只保留最近 100 条记录
+  if (currentConfig.installHistory.length > 100) {
+    currentConfig.installHistory = currentConfig.installHistory.slice(0, 100)
+  }
+
+  saveConfig()
+}
+
+/**
+ * 获取安装历史
+ */
+export function getInstallHistory(limit = 20): InstallHistoryItem[] {
+  return currentConfig.installHistory.slice(0, limit)
+}
+
+/**
+ * 清除安装历史
+ */
+export function clearInstallHistory(): void {
+  currentConfig.installHistory = []
+  saveConfig()
+}
+
+/**
+ * 获取窗口位置配置
+ */
+export function getWindowBounds(): WindowBounds | undefined {
+  return currentConfig.windowBounds
+}
+
+/**
+ * 保存窗口位置配置
+ */
+export function saveWindowBounds(bounds: WindowBounds): void {
+  currentConfig.windowBounds = bounds
+  saveConfig()
+}
+
+/**
+ * 获取 Git 镜像配置
+ */
+export function getGitMirrorFromConfig(): GitMirrorType {
+  return currentConfig.gitMirror
+}
+
+/**
+ * 保存 Git 镜像配置
+ */
+export function saveGitMirrorConfig(mirror: GitMirrorType): void {
+  currentConfig.gitMirror = mirror
+  saveConfig()
+}
+
+/**
+ * 获取 Node.js 镜像配置
+ */
+export function getNodejsMirrorFromConfig(): NodejsMirrorType {
+  return currentConfig.nodejsMirror
+}
+
+/**
+ * 保存 Node.js 镜像配置
+ */
+export function saveNodejsMirrorConfig(mirror: NodejsMirrorType): void {
+  currentConfig.nodejsMirror = mirror
+  saveConfig()
+}

+ 155 - 0
electron/modules/constants.ts

@@ -0,0 +1,155 @@
+// electron/modules/constants.ts - 常量定义
+
+import type { SoftwareType } from './types'
+
+// 支持的最低 Node.js 主版本
+export const MIN_SUPPORTED_NODE_VERSION = 18
+
+// 保留的主版本数量
+export const MAX_MAJOR_VERSIONS = 4
+
+// 网络请求超时时间(毫秒)
+export const REQUEST_TIMEOUT = 15000
+
+// 重试次数
+export const MAX_RETRIES = 3
+
+// 重试延迟(毫秒)
+export const RETRY_DELAY = 1000
+
+// 版本缓存时间(毫秒)- 30分钟(优化后)
+export const VERSION_CACHE_TTL = 30 * 60 * 1000
+
+// 包管理器源更新缓存时间(毫秒)- 1天
+export const SOURCE_UPDATE_CACHE_TTL = 24 * 60 * 60 * 1000
+
+// 需要版本管理的软件类型
+export type VersionedSoftwareType = 'nodejs' | 'vscode' | 'git'
+
+// npm 镜像地址
+export const NPM_REGISTRY = 'https://registry.npmmirror.com'
+
+// 需要版本管理的软件列表
+export const VERSIONED_SOFTWARE_LIST: VersionedSoftwareType[] = ['nodejs', 'vscode', 'git']
+
+// 软件显示名称
+export const SOFTWARE_NAMES: Record<SoftwareType | 'all', string> = {
+  nodejs: 'Node.js',
+  pnpm: 'pnpm',
+  vscode: 'VS Code',
+  git: 'Git',
+  claudeCode: 'Claude Code',
+  all: '一键安装'
+}
+
+// 错误消息
+export const ERROR_MESSAGES = {
+  NETWORK_ERROR: '网络连接失败,请检查网络设置',
+  TIMEOUT_ERROR: '请求超时,请稍后重试',
+  VERSION_FETCH_ERROR: '无法获取版本列表',
+  INSTALL_ERROR: '安装失败',
+  PERMISSION_ERROR: '权限不足,请以管理员身份运行',
+  UNSUPPORTED_PLATFORM: '不支持的操作系统',
+  UNKNOWN_SOFTWARE: '未知的软件',
+  INVALID_URL: '无效的 URL 地址',
+  PACKAGE_MANAGER_NOT_FOUND: '未检测到包管理器',
+  INSTALL_CANCELLED: '安装已被用户取消',
+  UNINSTALL_ERROR: '卸载失败'
+} as const
+
+// 状态消息
+export const STATUS_MESSAGES = {
+  PREPARING: '正在准备安装...',
+  INSTALLING: '正在安装',
+  CONFIGURING: '正在配置',
+  UPDATING_SOURCE: '正在更新软件源 (apt update)...',
+  COMPLETE: '安装完成',
+  READY: '就绪',
+  LOADING: '加载中...',
+  LOAD_FAILED: '加载失败',
+  UNINSTALLING: '正在卸载',
+  CHECKING_UPDATE: '正在检查更新'
+} as const
+
+// brew 包名映射
+export const BREW_PACKAGES = {
+  nodejs: {
+    default: 'node',
+    20: 'node@20',
+    18: 'node@18'
+  },
+  vscode: {
+    stable: 'visual-studio-code',
+    insiders: 'visual-studio-code-insiders'
+  },
+  git: {
+    stable: 'git',
+    lfs: 'git-lfs'
+  }
+} as const
+
+// 应用名称(用于 sudo-prompt)
+export const APP_NAME = 'ApqInstaller'
+
+// 备用版本(当无法获取最新版本时使用)
+export const FALLBACK_VERSIONS = {
+  nodejs: '22.12.0',
+  vscode: '1.96.0',
+  git: '2.47.1'
+} as const
+
+// Git for Windows 镜像配置
+export const GIT_MIRRORS = {
+  // 华为云镜像(默认,国内推荐)
+  huaweicloud: {
+    name: '华为云镜像',
+    // 华为云镜像下载地址格式: https://mirrors.huaweicloud.com/git-for-windows/v{version}.windows.1/Git-{version}-64-bit.exe
+    getDownloadUrl: (version: string, arch: '64' | '32') =>
+      `https://mirrors.huaweicloud.com/git-for-windows/v${version}.windows.1/Git-${version}-${arch}-bit.exe`,
+    // 华为云镜像版本列表 API
+    versionsUrl: 'https://mirrors.huaweicloud.com/git-for-windows/'
+  },
+  // GitHub 官方(需要代理)
+  github: {
+    name: 'GitHub 官方',
+    getDownloadUrl: (version: string, arch: '64' | '32') =>
+      `https://github.com/git-for-windows/git/releases/download/v${version}.windows.1/Git-${version}-${arch}-bit.exe`,
+    // GitHub API 获取版本列表
+    versionsUrl: 'https://api.github.com/repos/git-for-windows/git/releases'
+  }
+} as const
+
+// 默认 Git 镜像
+export const DEFAULT_GIT_MIRROR: 'huaweicloud' | 'github' = 'huaweicloud'
+
+// Node.js 镜像配置
+export const NODEJS_MIRRORS = {
+  // 官方源(默认)
+  official: {
+    name: 'Node.js 官方',
+    // 版本列表 API
+    versionsUrl: 'https://nodejs.org/dist/index.json',
+    // 下载地址格式
+    getDownloadUrl: (version: string, arch: 'x64' | 'x86' | 'arm64') =>
+      `https://nodejs.org/dist/v${version}/node-v${version}-win-${arch}.zip`
+  },
+  // npmmirror 镜像(国内推荐)
+  npmmirror: {
+    name: 'npmmirror 镜像',
+    versionsUrl: 'https://registry.npmmirror.com/-/binary/node/index.json',
+    getDownloadUrl: (version: string, arch: 'x64' | 'x86' | 'arm64') =>
+      `https://cdn.npmmirror.com/binaries/node/v${version}/node-v${version}-win-${arch}.zip`
+  }
+} as const
+
+// 默认 Node.js 镜像
+export const DEFAULT_NODEJS_MIRROR: 'official' | 'npmmirror' = 'npmmirror'
+
+// VS Code 版本 API(官方,国内可访问)
+export const VSCODE_API = {
+  // 版本列表 API
+  versionsUrl: 'https://update.code.visualstudio.com/api/releases/stable',
+  // 下载地址格式
+  getDownloadUrl: (version: string, arch: 'x64' | 'x86' | 'arm64', type: 'user' | 'system' = 'user') =>
+    `https://update.code.visualstudio.com/${version}/win32-${arch}-${type}/stable`
+} as const

+ 10 - 0
electron/modules/index.ts

@@ -0,0 +1,10 @@
+// electron/modules/index.ts - 模块导出
+
+export * from './types'
+export * from './constants'
+export { logger } from './logger'
+export * from './utils'
+export * from './config'
+export * from './version-fetcher'
+export * from './installer'
+export * from './ipc-handlers'

+ 1207 - 0
electron/modules/installer.ts

@@ -0,0 +1,1207 @@
+// electron/modules/installer.ts - 安装逻辑模块
+
+import * as os from 'os'
+import * as path from 'path'
+import * as fs from 'fs'
+import { execa, type ResultPromise } from 'execa'
+import * as sudo from 'sudo-prompt'
+import type { SoftwareType, SoftwareTypeWithAll, InstallOptions, CommandResult, InstalledInfo, AllInstalledInfo, Platform } from './types'
+import {
+  NPM_REGISTRY,
+  ERROR_MESSAGES,
+  STATUS_MESSAGES,
+  BREW_PACKAGES,
+  APP_NAME,
+  FALLBACK_VERSIONS
+} from './constants'
+import { commandExistsWithRefresh, getCommandVersionWithRefresh, downloadFile, getTempDir, cancelCurrentDownload } from './utils'
+import { installVscodeExtension } from './vscode-extension'
+import { needsAptUpdate, markAptUpdated } from './source-updater'
+import { getGitDownloadUrl, getNodejsDownloadUrl, getVSCodeDownloadUrl, getNodejsMirrorConfig } from './version-fetcher'
+import logger from './logger'
+
+// 当前安装进程
+let currentProcess: ResultPromise | null = null
+let installCancelled = false
+
+
+/**
+ * 取消当前安装
+ */
+export function cancelInstall(): boolean {
+  installCancelled = true
+  let cancelled = false
+
+  // 取消正在进行的下载
+  if (cancelCurrentDownload()) {
+    logger.installInfo('下载已取消')
+    cancelled = true
+  }
+
+  // 取消正在运行的进程
+  if (currentProcess) {
+    try {
+      currentProcess.kill('SIGTERM')
+      logger.installInfo('安装进程已取消')
+      cancelled = true
+    } catch (error) {
+      logger.installError('取消安装进程失败', error)
+    }
+  }
+
+  if (cancelled) {
+    logger.installInfo('安装已取消')
+  }
+
+  return cancelled
+}
+
+/**
+ * 重置取消状态
+ */
+function resetCancelState(): void {
+  installCancelled = false
+  currentProcess = null
+}
+
+/**
+ * 获取 npm 的完整路径
+ * Windows: 安装后 PATH 环境变量不会立即对当前进程生效,需要使用完整路径
+ * macOS/Linux: 通常不需要,但为了健壮性也提供完整路径
+ */
+function getNpmPath(): string {
+  const platform = os.platform() as Platform
+  switch (platform) {
+    case 'win32': {
+      const programFiles = process.env.ProgramFiles || 'C:\\Program Files'
+      return path.join(programFiles, 'nodejs', 'npm.cmd')
+    }
+    case 'darwin': {
+      // Homebrew 在 Apple Silicon 上安装到 /opt/homebrew,Intel 上安装到 /usr/local
+      const arch = os.arch()
+      if (arch === 'arm64') {
+        return '/opt/homebrew/bin/npm'
+      }
+      return '/usr/local/bin/npm'
+    }
+    case 'linux':
+      return '/usr/bin/npm'
+    default:
+      return 'npm'
+  }
+}
+
+/**
+ * 获取 pnpm 的完整路径
+ */
+function getPnpmPath(): string {
+  const platform = os.platform() as Platform
+  switch (platform) {
+    case 'win32': {
+      const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')
+      return path.join(appData, 'npm', 'pnpm.cmd')
+    }
+    case 'darwin': {
+      const arch = os.arch()
+      if (arch === 'arm64') {
+        return '/opt/homebrew/bin/pnpm'
+      }
+      return '/usr/local/bin/pnpm'
+    }
+    case 'linux':
+      return '/usr/local/bin/pnpm'
+    default:
+      return 'pnpm'
+  }
+}
+
+/**
+ * 检查是否已取消
+ */
+function checkCancelled(): void {
+  if (installCancelled) {
+    throw new Error(ERROR_MESSAGES.INSTALL_CANCELLED)
+  }
+}
+
+/**
+ * 使用 sudo-prompt 执行需要管理员权限的命令
+ */
+function sudoExec(command: string): Promise<{ stdout: string; stderr: string }> {
+  return new Promise((resolve, reject) => {
+    const options = { name: APP_NAME }
+    sudo.exec(command, options, (error, stdout, stderr) => {
+      // 过滤乱码字符的辅助函数(Windows 命令行输出可能是 GBK 编码)
+      const cleanOutput = (str: string | Buffer | undefined): string => {
+        if (!str) return ''
+        const raw = str.toString()
+        // 过滤掉非 ASCII 可打印字符和非中文字符,保留换行符
+        return raw.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
+      }
+
+      if (error) {
+        // 包含 stderr 和 stdout 信息以便调试
+        const exitCode = (error as Error & { code?: number }).code
+        const stderrStr = cleanOutput(stderr)
+        const stdoutStr = cleanOutput(stdout)
+        let errorMessage = error.message
+        if (exitCode !== undefined) {
+          errorMessage += `\n退出码: ${exitCode}`
+        }
+        if (stderrStr) {
+          errorMessage += `\n详细信息: ${stderrStr}`
+        }
+        if (stdoutStr) {
+          errorMessage += `\n输出: ${stdoutStr}`
+        }
+        const enhancedError = new Error(errorMessage)
+        reject(enhancedError)
+      } else {
+        resolve({
+          stdout: cleanOutput(stdout),
+          stderr: cleanOutput(stderr)
+        })
+      }
+    })
+  })
+}
+
+/**
+ * 为包含空格的路径添加引号(Windows)
+ */
+function quoteIfNeeded(str: string): string {
+  // 如果字符串包含空格且没有被引号包围,则添加引号
+  if (str.includes(' ') && !str.startsWith('"') && !str.endsWith('"')) {
+    return `"${str}"`
+  }
+  return str
+}
+
+/**
+ * 跨平台执行系统命令(带权限处理)
+ */
+async function executeCommand(
+  command: string,
+  args: string[] = [],
+  needAdmin = false
+): Promise<{ stdout: string; stderr: string }> {
+  checkCancelled()
+
+  const platform = os.platform() as Platform
+
+  // 构建完整命令时,对包含空格的部分添加引号
+  const quotedCommand = quoteIfNeeded(command)
+  const quotedArgs = args.map(arg => quoteIfNeeded(arg))
+  const fullCommand = `${quotedCommand} ${quotedArgs.join(' ')}`
+
+  logger.installDebug(`执行命令: ${fullCommand}`)
+
+  try {
+    if (needAdmin) {
+      // 使用 sudo-prompt 执行需要管理员权限的命令
+      if (platform === 'win32') {
+        // Windows: 直接执行命令,sudo-prompt 会处理 UAC
+        return await sudoExec(fullCommand)
+      } else {
+        // Linux/macOS: 使用 sudo
+        return await sudoExec(fullCommand)
+      }
+    } else {
+      // 无需提权,直接执行
+      // Windows 上执行 .cmd 文件时,使用 shell: true 避免参数解析问题
+      const useShell = platform === 'win32' && command.endsWith('.cmd')
+      const proc = execa(command, args, { stdio: 'pipe', shell: useShell })
+      currentProcess = proc
+      const result = await proc
+      currentProcess = null
+      return { stdout: result.stdout, stderr: result.stderr }
+    }
+  } catch (error) {
+    logger.installError(`命令执行失败: ${fullCommand}`, error)
+    throw error
+  }
+}
+
+/**
+ * 检测软件是否已安装
+ * Windows: 安装后 PATH 环境变量不会立即对当前进程生效,
+ * 所以使用 commandExistsWithRefresh 从注册表重新读取最新的 PATH
+ */
+export async function checkInstalled(software: SoftwareType): Promise<InstalledInfo> {
+  let command: string
+  let versionArgs: string[]
+
+  switch (software) {
+    case 'nodejs':
+      command = 'node'
+      versionArgs = ['--version']
+      break
+    case 'pnpm':
+      command = 'pnpm'
+      versionArgs = ['--version']
+      break
+    case 'vscode':
+      command = 'code'
+      versionArgs = ['--version']
+      break
+    case 'git':
+      command = 'git'
+      versionArgs = ['--version']
+      break
+    case 'claudeCode':
+      command = 'claude'
+      versionArgs = ['--version']
+      break
+    default:
+      return { installed: false, version: null }
+  }
+
+  // 使用刷新后的 PATH 检测命令是否存在(Windows 上会从注册表重新读取 PATH)
+  const exists = await commandExistsWithRefresh(command)
+  if (!exists) {
+    return { installed: false, version: null }
+  }
+
+  // 使用刷新后的 PATH 获取版本
+  const version = await getCommandVersionWithRefresh(command, versionArgs)
+  return { installed: true, version }
+}
+
+/**
+ * 检测所有软件的安装状态
+ */
+export async function checkAllInstalled(): Promise<AllInstalledInfo> {
+  const results = await Promise.allSettled([
+    checkInstalled('nodejs'),
+    checkInstalled('pnpm'),
+    checkInstalled('vscode'),
+    checkInstalled('git'),
+    checkInstalled('claudeCode')
+  ])
+
+  return {
+    nodejs: results[0].status === 'fulfilled' ? results[0].value : { installed: false, version: null },
+    pnpm: results[1].status === 'fulfilled' ? results[1].value : { installed: false, version: null },
+    vscode: results[2].status === 'fulfilled' ? results[2].value : { installed: false, version: null },
+    git: results[3].status === 'fulfilled' ? results[3].value : { installed: false, version: null },
+    claudeCode: results[4].status === 'fulfilled' ? results[4].value : { installed: false, version: null }
+  }
+}
+
+// ==================== 安装命令生成 ====================
+
+/**
+ * 获取 Node.js 安装命令 (macOS/Linux 使用包管理器)
+ * Windows 使用下载 msi 安装包方式,不使用此函数
+ * @param version 版本号
+ */
+function getNodeInstallArgs(version = 'lts'): CommandResult {
+  const platform = os.platform() as Platform
+  const major = version.split('.')[0]
+  const majorNum = parseInt(major)
+
+  let brewPkg: string
+
+  if (majorNum === 20) {
+    brewPkg = BREW_PACKAGES.nodejs['20']
+  } else if (majorNum === 18) {
+    brewPkg = BREW_PACKAGES.nodejs['18']
+  } else {
+    brewPkg = BREW_PACKAGES.nodejs.default
+  }
+
+  switch (platform) {
+    case 'darwin':
+      return { command: 'brew', args: ['install', brewPkg] }
+    case 'linux':
+      return { command: 'apt', args: ['install', '-y', 'nodejs', 'npm'] }
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+}
+
+/**
+ * 获取 VS Code 安装命令 (macOS/Linux 使用包管理器)
+ * Windows 使用下载安装包方式,不使用此函数(除了 insiders 版本)
+ * @param version 版本号
+ */
+function getVSCodeInstallArgs(version = 'stable'): CommandResult {
+  const platform = os.platform() as Platform
+
+  if (version === 'insiders') {
+    switch (platform) {
+      case 'darwin':
+        return {
+          command: 'brew',
+          args: ['install', '--cask', BREW_PACKAGES.vscode.insiders]
+        }
+      case 'linux':
+        return { command: 'snap', args: ['install', 'code', '--channel=insiders'] }
+      default:
+        throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+    }
+  }
+
+  switch (platform) {
+    case 'darwin':
+      return {
+        command: 'brew',
+        args: ['install', '--cask', BREW_PACKAGES.vscode.stable]
+      }
+    case 'linux':
+      return { command: 'snap', args: ['install', 'code', '--classic'] }
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+}
+
+/**
+ * 获取 Git 安装命令 (macOS/Linux 使用包管理器)
+ * Windows 使用下载安装包方式,不使用此函数
+ * @param version 版本号
+ */
+function getGitInstallArgs(version = 'stable'): CommandResult {
+  const platform = os.platform() as Platform
+
+  if (version === 'mingit') {
+    switch (platform) {
+      case 'darwin':
+        return { command: 'brew', args: ['install', BREW_PACKAGES.git.stable] }
+      case 'linux':
+        return { command: 'apt', args: ['install', '-y', 'git'] }
+      default:
+        throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+    }
+  }
+
+  if (version === 'lfs') {
+    switch (platform) {
+      case 'darwin':
+        return { command: 'brew', args: ['install', BREW_PACKAGES.git.lfs] }
+      case 'linux':
+        return { command: 'apt', args: ['install', '-y', 'git-lfs'] }
+      default:
+        throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+    }
+  }
+
+  switch (platform) {
+    case 'darwin':
+      return { command: 'brew', args: ['install', BREW_PACKAGES.git.stable] }
+    case 'linux':
+      return { command: 'apt', args: ['install', '-y', 'git'] }
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+}
+
+// 可卸载的软件类型
+type UninstallableSoftwareType = 'nodejs' | 'vscode' | 'git'
+
+/**
+ * 获取卸载命令 (macOS/Linux 使用包管理器)
+ * Windows 暂不支持通过此工具卸载,请使用系统设置
+ */
+function getUninstallArgs(software: UninstallableSoftwareType): CommandResult {
+  const platform = os.platform() as Platform
+
+  switch (platform) {
+    case 'win32':
+      // Windows 暂不支持通过此工具卸载,请使用系统设置中的"应用和功能"
+      throw new Error('Windows 暂不支持通过此工具卸载软件,请使用系统设置中的"应用和功能"')
+    case 'darwin': {
+      let brewPkg: string
+      switch (software) {
+        case 'nodejs':
+          brewPkg = BREW_PACKAGES.nodejs.default
+          break
+        case 'vscode':
+          brewPkg = BREW_PACKAGES.vscode.stable
+          break
+        case 'git':
+          brewPkg = BREW_PACKAGES.git.stable
+          break
+      }
+      return { command: 'brew', args: ['uninstall', brewPkg] }
+    }
+    case 'linux': {
+      let aptPkg: string
+      switch (software) {
+        case 'nodejs':
+          aptPkg = 'nodejs'
+          break
+        case 'vscode':
+          return { command: 'snap', args: ['remove', 'code'] }
+        case 'git':
+          aptPkg = 'git'
+          break
+      }
+      return { command: 'apt', args: ['remove', '-y', aptPkg] }
+    }
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+}
+
+// ==================== 安装流程 ====================
+
+export type StatusCallback = (software: SoftwareTypeWithAll, message: string, progress: number, skipLog?: boolean) => void
+export type CompleteCallback = (software: SoftwareTypeWithAll, message: string) => void
+export type ErrorCallback = (software: SoftwareTypeWithAll, message: string) => void
+
+/**
+ * Linux 下执行 apt update(带缓存,1天内只更新一次)
+ */
+export async function aptUpdate(onStatus?: StatusCallback, software?: SoftwareTypeWithAll): Promise<void> {
+  if (os.platform() !== 'linux') return
+  if (!needsAptUpdate()) {
+    logger.installInfo('apt 源已在缓存期内,跳过更新')
+    return
+  }
+
+  if (onStatus && software) {
+    onStatus(software, STATUS_MESSAGES.UPDATING_SOURCE, 10)
+  }
+  await executeCommand('apt', ['update'], true)
+  markAptUpdated()
+}
+
+/**
+ * 安装 Node.js (Windows 使用 msi 安装包)
+ * @param version 版本号
+ * @param installPnpm 是否安装 pnpm
+ * @param onStatus 状态回调
+ * @param customPath 自定义安装路径 (仅 Windows 支持)
+ */
+export async function installNodejs(
+  version = 'lts',
+  installPnpm = true,
+  onStatus: StatusCallback,
+  customPath?: string
+): Promise<void> {
+  resetCancelState()
+
+  const platform = os.platform() as Platform
+
+  // Windows: 直接下载 msi 安装包
+  if (platform === 'win32') {
+    // 确定目标版本
+    let targetVersion = version
+    if (version === 'lts' || !version || !/^\d+\.\d+\.\d+$/.test(version)) {
+      // 如果是 lts 或无效版本,使用备用版本
+      targetVersion = FALLBACK_VERSIONS.nodejs
+    }
+
+    checkCancelled()
+
+    // 下载 msi 安装包
+    const arch = os.arch() === 'x64' ? 'x64' : os.arch() === 'arm64' ? 'arm64' : 'x86'
+    const downloadUrl = getNodejsDownloadUrl(targetVersion).replace('.zip', '.msi').replace(`-win-${arch}`, `-${arch}`)
+    const tempDir = getTempDir()
+    const installerPath = path.join(tempDir, `node-v${targetVersion}-${arch}.msi`)
+
+    onStatus('nodejs', `正在下载 Node.js ${targetVersion}...`, 10)
+    logger.installInfo(`开始下载 Node.js: ${downloadUrl}`)
+
+    try {
+      let lastLoggedPercent = 0
+      await downloadFile(downloadUrl, installerPath, (downloaded, total, percent) => {
+        const progress = 10 + Math.round(percent * 0.4)
+        const downloadedMB = (downloaded / 1024 / 1024).toFixed(1)
+        const totalMB = (total / 1024 / 1024).toFixed(1)
+        // 每次都更新进度条,但只在每 5% 时记录日志
+        const shouldLog = percent - lastLoggedPercent >= 5
+        if (shouldLog) {
+          lastLoggedPercent = Math.floor(percent / 5) * 5
+        }
+        onStatus('nodejs', `正在下载 Node.js ${targetVersion}... (${downloadedMB}MB / ${totalMB}MB)`, progress, !shouldLog)
+      })
+    } catch (error) {
+      logger.installError('下载 Node.js 失败', error)
+      throw new Error(`下载 Node.js 失败: ${(error as Error).message}`)
+    }
+
+    checkCancelled()
+
+    // 执行 msi 静默安装
+    onStatus('nodejs', `${STATUS_MESSAGES.INSTALLING} Node.js ${targetVersion}...`, 55)
+    logger.installInfo(`开始安装 Node.js: ${installerPath}`)
+
+    try {
+      // msiexec 静默安装参数
+      const installArgs = ['/i', installerPath, '/qn', '/norestart']
+      if (customPath) {
+        installArgs.push(`INSTALLDIR="${customPath}"`)
+      }
+
+      await sudoExec(`msiexec ${installArgs.join(' ')}`)
+
+      // 清理安装包
+      try {
+        await fs.promises.unlink(installerPath)
+      } catch {
+        // 忽略清理失败
+      }
+    } catch (error) {
+      logger.installError('安装 Node.js 失败', error)
+      throw error
+    }
+
+    checkCancelled()
+
+    // 使用完整路径,避免 PATH 未生效的问题
+    const npmCmd = getNpmPath()
+
+    // 仅当使用国内镜像下载时,才配置 npm 国内镜像
+    const { mirror: nodejsMirror } = getNodejsMirrorConfig()
+    if (nodejsMirror === 'npmmirror') {
+      const npmConfigCmd = `${npmCmd} config set registry ${NPM_REGISTRY}`
+      onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} npm 镜像: ${npmConfigCmd}`, 70)
+      try {
+        await executeCommand(npmCmd, ['config', 'set', 'registry', NPM_REGISTRY], false)
+      } catch (error) {
+        logger.installWarn('配置 npm 镜像失败(npm 可能还未加入 PATH)', error)
+      }
+    }
+
+    checkCancelled()
+
+    // 可选安装 pnpm
+    if (installPnpm) {
+      onStatus('nodejs', `${STATUS_MESSAGES.INSTALLING} pnpm...`, 80)
+      await executeCommand(npmCmd, ['install', '-g', 'pnpm'], true)
+
+      checkCancelled()
+
+      // 获取刷新后的 PATH,确保能找到刚安装的 pnpm
+      const { getRefreshedPath } = await import('./utils')
+      const refreshedPath = await getRefreshedPath()
+      const execEnv = { ...process.env, PATH: refreshedPath }
+
+      // 运行 pnpm setup 配置全局 bin 目录
+      onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 全局目录...`, 88)
+      try {
+        await execa('pnpm', ['setup'], { env: execEnv, shell: platform === 'win32' })
+        logger.installInfo('pnpm setup 完成')
+      } catch (error) {
+        logger.installWarn('pnpm setup 失败', error)
+      }
+
+      checkCancelled()
+
+      // 配置 pnpm 镜像
+      onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 镜像...`, 95)
+      try {
+        await execa('pnpm', ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv, shell: platform === 'win32' })
+      } catch (error) {
+        logger.installWarn('配置 pnpm 镜像失败', error)
+      }
+    }
+
+    onStatus('nodejs', STATUS_MESSAGES.COMPLETE, 100)
+    return
+  }
+
+  // macOS/Linux: 使用包管理器安装
+  onStatus('nodejs', `${STATUS_MESSAGES.INSTALLING} Node.js...`, 20)
+  const args = getNodeInstallArgs(version)
+  await executeCommand(args.command, args.args, true)
+
+  checkCancelled()
+
+  // 使用完整路径,避免 PATH 未生效的问题
+  const npmCmd = getNpmPath()
+
+  // 仅当使用国内镜像下载时,才配置 npm 国内镜像
+  const { mirror: nodejsMirrorMac } = getNodejsMirrorConfig()
+  if (nodejsMirrorMac === 'npmmirror') {
+    const npmConfigCmd = `${npmCmd} config set registry ${NPM_REGISTRY}`
+    onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} npm 镜像: ${npmConfigCmd}`, 50)
+    try {
+      await executeCommand(npmCmd, ['config', 'set', 'registry', NPM_REGISTRY], false)
+    } catch (error) {
+      logger.installWarn('配置 npm 镜像失败(npm 可能还未加入 PATH)', error)
+    }
+  }
+
+  checkCancelled()
+
+  // 可选安装 pnpm
+  if (installPnpm) {
+    onStatus('nodejs', `${STATUS_MESSAGES.INSTALLING} pnpm...`, 70)
+    await executeCommand(npmCmd, ['install', '-g', 'pnpm'], true)
+
+    checkCancelled()
+
+    // 获取刷新后的 PATH,确保能找到刚安装的 pnpm
+    const { getRefreshedPath } = await import('./utils')
+    const refreshedPath = await getRefreshedPath()
+    const execEnv = { ...process.env, PATH: refreshedPath }
+
+    // 运行 pnpm setup 配置全局 bin 目录
+    onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 全局目录...`, 82)
+    try {
+      await execa('pnpm', ['setup'], { env: execEnv })
+      logger.installInfo('pnpm setup 完成')
+    } catch (error) {
+      logger.installWarn('pnpm setup 失败', error)
+    }
+
+    checkCancelled()
+
+    // 配置 pnpm 镜像
+    onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 镜像...`, 90)
+    try {
+      await execa('pnpm', ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv })
+    } catch (error) {
+      logger.installWarn('配置 pnpm 镜像失败', error)
+    }
+  }
+
+  onStatus('nodejs', STATUS_MESSAGES.COMPLETE, 100)
+}
+
+/**
+ * 单独安装 pnpm (需要 Node.js 已安装)
+ * @param onStatus 状态回调
+ */
+export async function installPnpm(onStatus: StatusCallback): Promise<void> {
+  resetCancelState()
+
+  const platform = os.platform() as Platform
+
+  // 检查 Node.js 是否已安装
+  const nodeInstalled = await checkInstalled('nodejs')
+  if (!nodeInstalled.installed) {
+    throw new Error('请先安装 Node.js')
+  }
+
+  // 刷新系统环境变量,确保能找到刚安装的 Node.js
+  const { getRefreshedPath } = await import('./utils')
+  await getRefreshedPath()
+
+  // 使用完整路径,避免 PATH 未生效的问题
+  const npmCmd = getNpmPath()
+
+  // 检查 npm 命令是否存在
+  if (platform === 'win32' && !fs.existsSync(npmCmd)) {
+    throw new Error(`未找到 npm 命令: ${npmCmd},请确保 Node.js 已正确安装`)
+  }
+
+  checkCancelled()
+
+  // 安装 pnpm,需要管理员权限进行全局安装
+  const installCommand = `${npmCmd} install -g pnpm`
+  onStatus('pnpm', `正在安装 pnpm...`, 20)
+  onStatus('pnpm', `执行命令: ${installCommand}`, 25)
+  try {
+    await executeCommand(npmCmd, ['install', '-g', 'pnpm'], true)
+  } catch (error) {
+    // 提取更有意义的错误信息,过滤乱码
+    const execaError = error as { message?: string; stderr?: string; stdout?: string; shortMessage?: string; exitCode?: number }
+    let errorMessage = `npm install -g pnpm 失败`
+    if (execaError.exitCode !== undefined) {
+      errorMessage += ` (退出码: ${execaError.exitCode})`
+    }
+    // 过滤乱码字符,只保留可读字符
+    if (execaError.stderr) {
+      const stderrClean = execaError.stderr.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
+      if (stderrClean) {
+        errorMessage += `\n${stderrClean}`
+      }
+    }
+    if (execaError.stdout) {
+      const stdoutClean = execaError.stdout.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
+      if (stdoutClean) {
+        errorMessage += `\n${stdoutClean}`
+      }
+    }
+    logger.installError('安装 pnpm 失败', error)
+    throw new Error(errorMessage)
+  }
+
+  checkCancelled()
+
+  // 重新刷新 PATH,确保能找到刚安装的 pnpm
+  const pnpmRefreshedPath = await getRefreshedPath()
+
+  // 使用 pnpm 的完整路径,因为刚安装的 pnpm 可能还不在 PATH 中
+  const pnpmCmd = getPnpmPath()
+
+  // 构建环境变量,将 pnpm 所在目录添加到 PATH 开头
+  const pnpmDir = path.dirname(pnpmCmd)
+  const execEnv = { ...process.env, PATH: `${pnpmDir}${platform === 'win32' ? ';' : ':'}${pnpmRefreshedPath}` }
+
+  // 运行 pnpm setup 配置全局 bin 目录
+  onStatus('pnpm', `${STATUS_MESSAGES.CONFIGURING} pnpm 全局目录...`, 60)
+  try {
+    await execa(pnpmCmd, ['setup'], { env: execEnv, shell: platform === 'win32' })
+    logger.installInfo('pnpm setup 完成')
+  } catch (error) {
+    logger.installWarn('pnpm setup 失败', error)
+  }
+
+  checkCancelled()
+
+  // 配置 pnpm 镜像
+  onStatus('pnpm', `${STATUS_MESSAGES.CONFIGURING} pnpm 镜像...`, 80)
+  try {
+    await execa(pnpmCmd, ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv, shell: platform === 'win32' })
+  } catch (error) {
+    logger.installWarn('配置 pnpm 镜像失败', error)
+  }
+
+  onStatus('pnpm', STATUS_MESSAGES.COMPLETE, 100)
+}
+
+/**
+ * 安装 VS Code (Windows 使用下载安装包)
+ * @param version 版本号
+ * @param onStatus 状态回调
+ * @param customPath 自定义安装路径 (仅 Windows 支持)
+ */
+export async function installVscode(version = 'stable', onStatus: StatusCallback, customPath?: string): Promise<void> {
+  resetCancelState()
+
+  const platform = os.platform() as Platform
+
+  // Windows: 直接下载安装包
+  if (platform === 'win32' && version !== 'insiders') {
+    // 确定目标版本
+    let targetVersion = version
+    if (version === 'stable' || !version || !/^\d+\.\d+\.\d+$/.test(version)) {
+      // 如果是 stable 或无效版本,使用备用版本
+      targetVersion = FALLBACK_VERSIONS.vscode
+    }
+
+    checkCancelled()
+
+    // 下载安装包
+    const downloadUrl = getVSCodeDownloadUrl(targetVersion)
+    const tempDir = getTempDir()
+    const installerPath = path.join(tempDir, `VSCodeUserSetup-${targetVersion}.exe`)
+
+    onStatus('vscode', `正在下载 VS Code ${targetVersion}...`, 10)
+    logger.installInfo(`开始下载 VS Code: ${downloadUrl}`)
+
+    try {
+      let lastLoggedPercent = 0
+      await downloadFile(downloadUrl, installerPath, (downloaded, total, percent) => {
+        const progress = 10 + Math.round(percent * 0.5)
+        const downloadedMB = (downloaded / 1024 / 1024).toFixed(1)
+        const totalMB = (total / 1024 / 1024).toFixed(1)
+        // 每次都更新进度条,但只在每 5% 时记录日志
+        const shouldLog = percent - lastLoggedPercent >= 5
+        if (shouldLog) {
+          lastLoggedPercent = Math.floor(percent / 5) * 5
+        }
+        onStatus('vscode', `正在下载 VS Code ${targetVersion}... (${downloadedMB}MB / ${totalMB}MB)`, progress, !shouldLog)
+      })
+    } catch (error) {
+      logger.installError('下载 VS Code 失败', error)
+      throw new Error(`下载 VS Code 失败: ${(error as Error).message}`)
+    }
+
+    checkCancelled()
+
+    // 执行静默安装
+    onStatus('vscode', `${STATUS_MESSAGES.INSTALLING} VS Code ${targetVersion}...`, 65)
+    logger.installInfo(`开始安装 VS Code: ${installerPath}`)
+
+    try {
+      // VS Code 安装程序支持的静默安装参数
+      // /VERYSILENT: 完全静默安装
+      // /NORESTART: 不重启
+      // /MERGETASKS: 指定安装任务
+      const installArgs = ['/VERYSILENT', '/NORESTART', '/MERGETASKS=!runcode,addcontextmenufiles,addcontextmenufolders,associatewithfiles,addtopath']
+      if (customPath) {
+        installArgs.push(`/DIR=${customPath}`)
+      }
+
+      await sudoExec(`"${installerPath}" ${installArgs.join(' ')}`)
+
+      // 清理安装包
+      try {
+        await fs.promises.unlink(installerPath)
+      } catch {
+        // 忽略清理失败
+      }
+
+      onStatus('vscode', STATUS_MESSAGES.COMPLETE, 100)
+    } catch (error) {
+      logger.installError('安装 VS Code 失败', error)
+      throw error
+    }
+
+    return
+  }
+
+  // macOS/Linux 或 insiders 版本: 使用包管理器安装
+  onStatus('vscode', `${STATUS_MESSAGES.INSTALLING} VS Code...`, 30)
+  const args = getVSCodeInstallArgs(version)
+  await executeCommand(args.command, args.args, true)
+  onStatus('vscode', STATUS_MESSAGES.COMPLETE, 100)
+}
+
+/**
+ * 安装 Git (Windows 直接下载安装)
+ * @param version 版本号
+ * @param onStatus 状态回调
+ * @param customPath 自定义安装路径 (仅 Windows 支持)
+ */
+export async function installGit(version = 'stable', onStatus: StatusCallback, customPath?: string): Promise<void> {
+  resetCancelState()
+
+  const platform = os.platform() as Platform
+
+  // Windows: 直接从镜像下载安装
+  if (platform === 'win32' && version !== 'mingit' && version !== 'lfs') {
+    // 如果是 stable,尝试获取最新版本号
+    let targetVersion = version
+    if (version === 'stable') {
+      onStatus('git', '正在获取最新版本...', 5)
+      try {
+        const response = await fetch('https://api.github.com/repos/git-for-windows/git/releases/latest', {
+          headers: {
+            'Accept': 'application/vnd.github.v3+json',
+            'User-Agent': 'ApqInstaller'
+          },
+          signal: AbortSignal.timeout(5000) // 5秒超时
+        })
+        if (response.ok) {
+          const release = await response.json() as { tag_name: string }
+          const match = release.tag_name.match(/^v?(\d+\.\d+\.\d+)/)
+          if (match) {
+            targetVersion = match[1]
+          }
+        }
+      } catch (error) {
+        logger.installWarn('获取最新 Git 版本失败,使用备用版本', error)
+        // 使用备用版本
+        targetVersion = FALLBACK_VERSIONS.git
+      }
+    }
+
+    checkCancelled()
+
+    // 下载安装包
+    const downloadUrl = getGitDownloadUrl(targetVersion)
+    const tempDir = getTempDir()
+    const installerPath = path.join(tempDir, `Git-${targetVersion}-installer.exe`)
+
+    onStatus('git', `正在下载 Git ${targetVersion}...`, 10)
+    logger.installInfo(`开始下载 Git: ${downloadUrl}`)
+
+    try {
+      let lastLoggedPercent = 0
+      await downloadFile(downloadUrl, installerPath, (downloaded, total, percent) => {
+        // 下载进度占 10% - 60%
+        const progress = 10 + Math.round(percent * 0.5)
+        const downloadedMB = (downloaded / 1024 / 1024).toFixed(1)
+        const totalMB = (total / 1024 / 1024).toFixed(1)
+        // 每次都更新进度条,但只在每 5% 时记录日志
+        const shouldLog = percent - lastLoggedPercent >= 5
+        if (shouldLog) {
+          lastLoggedPercent = Math.floor(percent / 5) * 5
+        }
+        onStatus('git', `正在下载 Git ${targetVersion}... (${downloadedMB}MB / ${totalMB}MB)`, progress, !shouldLog)
+      })
+    } catch (error) {
+      logger.installError('下载 Git 失败', error)
+      throw new Error(`下载 Git 失败: ${(error as Error).message}`)
+    }
+
+    checkCancelled()
+
+    // 执行静默安装
+    onStatus('git', `${STATUS_MESSAGES.INSTALLING} Git ${targetVersion}...`, 65)
+    logger.installInfo(`开始安装 Git: ${installerPath}`)
+
+    try {
+      // Git 安装程序支持的静默安装参数
+      // /VERYSILENT: 完全静默安装
+      // /NORESTART: 不重启
+      // /NOCANCEL: 不显示取消按钮
+      // /SP-: 不显示 "This will install..." 提示
+      // /CLOSEAPPLICATIONS: 自动关闭相关应用
+      // /DIR: 指定安装目录
+      const installArgs = ['/VERYSILENT', '/NORESTART', '/NOCANCEL', '/SP-', '/CLOSEAPPLICATIONS']
+      if (customPath) {
+        installArgs.push(`/DIR=${customPath}`)
+      }
+
+      await sudoExec(`"${installerPath}" ${installArgs.join(' ')}`)
+
+      // 清理安装包
+      try {
+        await fs.promises.unlink(installerPath)
+      } catch {
+        // 忽略清理失败
+      }
+
+      onStatus('git', STATUS_MESSAGES.COMPLETE, 100)
+    } catch (error) {
+      logger.installError('安装 Git 失败', error)
+      throw error
+    }
+
+    return
+  }
+
+  // Windows 上的特殊版本 (mingit/lfs) 暂不支持
+  if (platform === 'win32') {
+    throw new Error(`Windows 暂不支持安装 ${version} 版本,请选择具体版本号`)
+  }
+
+  // macOS/Linux: 使用包管理器安装
+  onStatus('git', `${STATUS_MESSAGES.INSTALLING} Git...`, 30)
+  const args = getGitInstallArgs(version)
+  await executeCommand(args.command, args.args, true)
+  onStatus('git', STATUS_MESSAGES.COMPLETE, 100)
+}
+
+/**
+ * 安装 Claude Code (通过 pnpm 或 npm 全局安装)
+ * @param onStatus 状态回调
+ */
+export async function installClaudeCode(onStatus: StatusCallback): Promise<void> {
+  resetCancelState()
+
+  const platform = os.platform() as Platform
+
+  onStatus('claudeCode', '正在安装 Claude Code...', 10)
+  logger.installInfo('开始安装 Claude Code...')
+
+  checkCancelled()
+
+  // 检测是否已安装 pnpm,优先使用 pnpm
+  const hasPnpm = await commandExistsWithRefresh('pnpm')
+  const pkgManager = hasPnpm ? 'pnpm' : 'npm'
+
+  // 获取刷新后的 PATH,确保能找到新安装的命令
+  const { getRefreshedPath } = await import('./utils')
+  const refreshedPath = await getRefreshedPath()
+  const execEnv: Record<string, string> = { ...process.env as Record<string, string>, PATH: refreshedPath }
+
+  if (hasPnpm) {
+    // 使用 pnpm 安装,需要确保 PNPM_HOME 环境变量已设置
+    onStatus('claudeCode', '配置 pnpm 全局目录...', 20)
+
+    // 先执行 pnpm setup
+    try {
+      await execa('pnpm', ['setup'], { env: execEnv, shell: platform === 'win32' })
+      logger.installInfo('pnpm setup 完成')
+    } catch (error) {
+      logger.installWarn('pnpm setup 失败', error)
+    }
+
+    // 手动设置 PNPM_HOME 环境变量
+    if (platform === 'win32') {
+      const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
+      const pnpmHome = path.join(localAppData, 'pnpm')
+      execEnv.PNPM_HOME = pnpmHome
+      execEnv.PATH = `${pnpmHome};${execEnv.PATH}`
+      logger.installInfo(`设置 PNPM_HOME: ${pnpmHome}`)
+    } else {
+      const pnpmHome = path.join(os.homedir(), '.local', 'share', 'pnpm')
+      execEnv.PNPM_HOME = pnpmHome
+      execEnv.PATH = `${pnpmHome}:${execEnv.PATH}`
+      logger.installInfo(`设置 PNPM_HOME: ${pnpmHome}`)
+    }
+  }
+
+  checkCancelled()
+
+  const installArgs = ['install', '-g', '@anthropic-ai/claude-code']
+  const fullCommand = `${pkgManager} ${installArgs.join(' ')}`
+  onStatus('claudeCode', `执行命令: ${fullCommand}`, 30)
+  logger.installInfo(`使用 ${pkgManager} 安装,执行命令: ${fullCommand}`)
+
+  try {
+    if (hasPnpm) {
+      // 使用 pnpm 安装 Claude Code
+      await execa('pnpm', installArgs, { env: execEnv, shell: platform === 'win32' })
+    } else {
+      // 使用 npm 安装
+      const npmCmd = getNpmPath()
+      await executeCommand(npmCmd, installArgs, true)
+    }
+  } catch (error) {
+    logger.installError('安装 Claude Code 失败', error)
+    throw error
+  }
+
+  checkCancelled()
+
+  // 验证安装
+  onStatus('claudeCode', '验证安装...', 90)
+  const claudeExists = await commandExistsWithRefresh('claude')
+
+  if (claudeExists) {
+    const { getCommandVersionWithRefresh } = await import('./utils')
+    const version = await getCommandVersionWithRefresh('claude', ['--version'])
+    logger.installInfo(`Claude Code 安装成功: ${version}`)
+  } else {
+    // 即使验证失败,安装可能已成功,只是 PATH 还没生效
+    logger.installInfo('Claude Code 安装完成,但验证失败(可能需要重启终端)')
+  }
+
+  onStatus('claudeCode', STATUS_MESSAGES.COMPLETE, 100)
+}
+
+/**
+ * 安装 Claude Code for VS Code 扩展
+ * 复用 ipc-handlers.ts 中的 installVscodeExtension 函数
+ * @param _onStatus 状态回调(已由 installVscodeExtension 内部处理)
+ */
+export async function installClaudeCodeExt(_onStatus: StatusCallback): Promise<void> {
+  resetCancelState()
+  checkCancelled()
+
+  const extensionId = 'anthropic.claude-code'
+  const result = await installVscodeExtension(extensionId)
+
+  if (!result.success) {
+    throw new Error(result.error || 'VS Code 插件安装失败')
+  }
+}
+
+/**
+ * 卸载软件
+ */
+export async function uninstallSoftware(software: SoftwareType): Promise<boolean> {
+  // 只有 nodejs, vscode, git 支持卸载
+  if (software !== 'nodejs' && software !== 'vscode' && software !== 'git') {
+    logger.installWarn(`${software} 不支持通过此方式卸载`)
+    return false
+  }
+  try {
+    const args = getUninstallArgs(software)
+    await executeCommand(args.command, args.args, true)
+    logger.installInfo(`${software} 卸载成功`)
+    return true
+  } catch (error) {
+    logger.installError(`${software} 卸载失败`, error)
+    return false
+  }
+}
+
+/**
+ * 一键安装所有软件
+ * 复用单独安装函数,避免代码重复
+ */
+export async function installAll(options: InstallOptions, onStatus: StatusCallback): Promise<string[]> {
+  resetCancelState()
+
+  onStatus('all', '正在准备安装...', 5)
+
+  const {
+    installNodejs: doNodejs = true,
+    nodejsVersion = 'lts',
+    nodejsPath,
+    installPnpm: doPnpm = true,
+    installVscode: doVscode = true,
+    vscodeVersion = 'stable',
+    vscodePath,
+    installGit: doGit = true,
+    gitVersion = 'stable',
+    gitPath,
+    installClaudeCode: doClaudeCode = false,
+    installClaudeCodeExt: doClaudeCodeExt = false
+  } = options
+
+  // 计算总步骤数
+  const steps =
+    (doNodejs ? 1 : 0) +
+    (doPnpm && !doNodejs ? 1 : 0) + // 如果安装 Node.js,pnpm 会一起安装
+    (doGit ? 1 : 0) +
+    (doVscode ? 1 : 0) +
+    (doClaudeCode ? 1 : 0) +
+    (doClaudeCodeExt ? 1 : 0)
+  let currentStep = 0
+  const getProgress = (): number => Math.round(((currentStep + 0.5) / steps) * 100)
+
+  // 创建包装的状态回调,将子安装的状态转发到 'all'
+  const createWrappedStatus = (stepName: string): StatusCallback => {
+    return (_software, message, _progress, skipLog) => {
+      onStatus('all', `[${stepName}] ${message}`, getProgress(), skipLog)
+    }
+  }
+
+  // 安装 Node.js(包含 pnpm)
+  if (doNodejs) {
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('Node.js')
+    await installNodejs(nodejsVersion, doPnpm, wrappedStatus, nodejsPath)
+    currentStep++
+  } else if (doPnpm) {
+    // 如果不安装 Node.js 但需要安装 pnpm,单独安装 pnpm
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('pnpm')
+    await installPnpm(wrappedStatus)
+    currentStep++
+  }
+
+  // 安装 Git (Claude Code 运行时需要 Git)
+  if (doGit) {
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('Git')
+    await installGit(gitVersion, wrappedStatus, gitPath)
+    currentStep++
+  }
+
+  // 安装 Claude Code (需要 Node.js 和 Git,应在 Git 之后安装)
+  if (doClaudeCode) {
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('Claude Code')
+    await installClaudeCode(wrappedStatus)
+    currentStep++
+  }
+
+  // 安装 VS Code (Claude Code Ext 需要 VS Code)
+  if (doVscode) {
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('VS Code')
+    await installVscode(vscodeVersion, wrappedStatus, vscodePath)
+    currentStep++
+
+    // 如果需要安装扩展,等待 VS Code CLI 准备就绪
+    if (doClaudeCodeExt) {
+      onStatus('all', '[VS Code] 等待 VS Code CLI 准备就绪...', getProgress(), true)
+      await new Promise(resolve => setTimeout(resolve, 3000))
+    }
+  }
+
+  // 安装 Claude Code for VS Code 扩展 (需要 VS Code 和 Claude Code)
+  if (doClaudeCodeExt) {
+    checkCancelled()
+    const wrappedStatus = createWrappedStatus('Claude Code 插件')
+    try {
+      await installClaudeCodeExt(wrappedStatus)
+    } catch (error) {
+      // 插件安装失败不阻止整体流程
+      logger.installWarn('Claude Code for VS Code 扩展安装失败', error)
+    }
+    currentStep++
+  }
+
+  // 生成完成消息
+  const installed: string[] = []
+  if (doNodejs) installed.push('Node.js')
+  if (doPnpm) installed.push('pnpm')
+  if (doVscode) installed.push('VS Code')
+  if (doGit) installed.push('Git')
+  if (doClaudeCode) installed.push('Claude Code')
+  if (doClaudeCodeExt) installed.push('Claude Code 插件')
+
+  return installed
+}
+
+export {
+  executeCommand,
+  getNodeInstallArgs,
+  getVSCodeInstallArgs,
+  getGitInstallArgs,
+  getNpmPath,
+  getPnpmPath
+}

+ 616 - 0
electron/modules/ipc-handlers.ts

@@ -0,0 +1,616 @@
+// electron/modules/ipc-handlers.ts - IPC 处理模块
+
+import { dialog, BrowserWindow } from 'electron'
+import { registerHandler } from './ipc-registry'
+import * as path from 'path'
+import * as os from 'os'
+import { execa } from 'execa'
+import type { SoftwareType, SoftwareTypeWithAll, Platform, InstallOptions, GitMirrorType, NodejsMirrorType } from './types'
+import { ERROR_MESSAGES } from './constants'
+import { commandExists, checkNetworkConnection, getVscodeCliPath } from './utils'
+import { installVscodeExtension } from './vscode-extension'
+import { installClaudeCode as installClaudeCodeWithStatus } from './claude-code-installer'
+import { getVersions, setGitMirror, getGitMirrorConfig, setNodejsMirror, getNodejsMirrorConfig } from './version-fetcher'
+import {
+  checkInstalled,
+  checkAllInstalled,
+  cancelInstall,
+  aptUpdate,
+  installNodejs,
+  installPnpm,
+  installVscode,
+  installGit,
+  installAll,
+  uninstallSoftware
+} from './installer'
+import { addInstallHistory, getInstallHistory } from './config'
+import logger from './logger'
+import {
+  checkForUpdates,
+  downloadUpdate,
+  installUpdate,
+  getCurrentVersion,
+  isPortableMode
+} from './updater'
+
+/**
+ * 检测管理员权限
+ */
+async function checkAdminPrivilege(): Promise<boolean> {
+  const platform = os.platform()
+
+  try {
+    if (platform === 'win32') {
+      const result = await execa('powershell', [
+        '-Command',
+        '([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)'
+      ])
+      return result.stdout.trim().toLowerCase() === 'true'
+    } else {
+      return process.getuid ? process.getuid() === 0 : false
+    }
+  } catch (error) {
+    logger.warn('检测管理员权限失败', error)
+    return false
+  }
+}
+
+/**
+ * 检测 brew 是否安装
+ */
+async function checkBrew(): Promise<boolean> {
+  return await commandExists('brew')
+}
+
+/**
+ * 安装 Homebrew
+ */
+async function installBrew(): Promise<void> {
+  const installScript = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
+  await execa('bash', ['-c', installScript])
+}
+
+/**
+ * 发送状态到渲染进程
+ */
+function sendToRenderer(channel: string, data: unknown): void {
+  const windows = BrowserWindow.getAllWindows()
+  windows.forEach((win) => {
+    win.webContents.send(channel, data)
+  })
+}
+
+/**
+ * 注册所有 IPC 处理器
+ */
+export function registerHandlers(): void {
+  // 检测管理员权限
+  registerHandler('check-admin', async () => {
+    return await checkAdminPrivilege()
+  })
+
+  // 检测包管理器
+  // Windows 使用直接下载方式安装,不需要包管理器
+  // macOS 需要 brew,Linux 使用 apt
+  registerHandler('check-package-manager', async () => {
+    const platform = os.platform()
+    if (platform === 'win32') {
+      // Windows 不需要包管理器,直接下载安装
+      return { exists: true, manager: 'none' as const }
+    } else if (platform === 'darwin') {
+      return { exists: await checkBrew(), manager: 'brew' }
+    } else {
+      return { exists: true, manager: 'apt' }
+    }
+  })
+
+  // 安装包管理器 (仅 macOS 需要安装 brew)
+  registerHandler('install-package-manager', async (_event, manager: string) => {
+    try {
+      logger.info(`开始安装包管理器: ${manager}`)
+      if (manager === 'brew') {
+        await installBrew()
+        logger.info(`包管理器安装成功: ${manager}`)
+        return { success: true }
+      }
+      // Windows 不需要包管理器,Linux apt 已预装
+      return { success: true }
+    } catch (error) {
+      logger.error(`包管理器安装失败: ${manager}`, error)
+      return { success: false, error: (error as Error).message }
+    }
+  })
+
+  // 获取平台信息
+  registerHandler('get-platform', (): Platform => {
+    return os.platform() as Platform
+  })
+
+  // 检测网络连接
+  registerHandler('check-network', async () => {
+    return await checkNetworkConnection()
+  })
+
+  // 获取软件版本列表
+  registerHandler('get-versions', async (_event, software: SoftwareType) => {
+    try {
+      return await getVersions(software)
+    } catch (error) {
+      logger.error(`获取 ${software} 版本列表失败`, error)
+      throw error
+    }
+  })
+
+  // 检查更新
+  registerHandler('check-update', async (_event, software: SoftwareType) => {
+    try {
+      const installed = await checkInstalled(software)
+      if (!installed.installed || !installed.version) {
+        return { hasUpdate: false }
+      }
+
+      const versions = await getVersions(software)
+      const latestVersion = versions.versions.find((v) => !v.disabled && !v.separator)
+      if (!latestVersion) {
+        return { hasUpdate: false }
+      }
+
+      const hasUpdate = latestVersion.value !== installed.version
+      return { hasUpdate, latestVersion: latestVersion.value }
+    } catch (error) {
+      logger.error(`检查 ${software} 更新失败`, error)
+      return { hasUpdate: false }
+    }
+  })
+
+  // 检测软件是否已安装
+  registerHandler('check-installed', async (_event, software: SoftwareTypeWithAll) => {
+    if (software === 'all') {
+      return await checkAllInstalled()
+    }
+    return await checkInstalled(software)
+  })
+
+  // 取消安装
+  registerHandler('cancel-install', () => {
+    return cancelInstall()
+  })
+
+  // 卸载软件
+  registerHandler('uninstall', async (_event, software: SoftwareType) => {
+    try {
+      return await uninstallSoftware(software)
+    } catch (error) {
+      logger.error(`卸载 ${software} 失败`, error)
+      return false
+    }
+  })
+
+  // 获取安装历史
+  registerHandler('get-install-history', (_event, limit?: number) => {
+    return getInstallHistory(limit)
+  })
+
+  // 获取日志
+  registerHandler('get-logs', () => {
+    return logger.getRecentLogs(200)
+  })
+
+  // 写入安装日志(供渲染进程调用)
+  registerHandler('write-install-log', (_event, message: string, level: 'info' | 'warn' | 'error' = 'info') => {
+    switch (level) {
+      case 'warn':
+        logger.installWarn(message)
+        break
+      case 'error':
+        logger.installError(message)
+        break
+      default:
+        logger.installInfo(message)
+    }
+  })
+
+  // 获取日志文件路径
+  registerHandler('get-log-paths', () => {
+    return {
+      appLog: logger.getAppLogPath(),
+      installLog: logger.getInstallLogPath()
+    }
+  })
+
+  // 设置窗口标题
+  registerHandler('set-window-title', (_event, title: string) => {
+    const windows = BrowserWindow.getAllWindows()
+    windows.forEach((win) => {
+      win.setTitle(title)
+    })
+  })
+
+  // 窗口最小化
+  registerHandler('window-minimize', () => {
+    const win = BrowserWindow.getFocusedWindow()
+    win?.minimize()
+  })
+
+  // 窗口最大化/还原
+  registerHandler('window-maximize', () => {
+    const win = BrowserWindow.getFocusedWindow()
+    if (win?.isMaximized()) {
+      win.unmaximize()
+    } else {
+      win?.maximize()
+    }
+    return win?.isMaximized() ?? false
+  })
+
+  // 关闭窗口
+  registerHandler('window-close', () => {
+    const win = BrowserWindow.getFocusedWindow()
+    win?.close()
+  })
+
+  // 获取窗口最大化状态
+  registerHandler('window-is-maximized', () => {
+    const win = BrowserWindow.getFocusedWindow()
+    return win?.isMaximized() ?? false
+  })
+
+  // 检测 Claude Code 是否已安装
+  registerHandler('check-claude-code', async () => {
+    try {
+      // 使用 checkInstalled 函数,它会刷新 PATH 环境变量来检测新安装的软件
+      const result = await checkInstalled('claudeCode')
+      if (result.installed) {
+        logger.info(`检测到 Claude Code: ${result.version}`)
+      } else {
+        logger.info('未检测到 Claude Code')
+      }
+      return result
+    } catch (error) {
+      logger.warn('检测 Claude Code 失败', error)
+      return { installed: false, version: null }
+    }
+  })
+
+  // 启动 Claude Code (打开 Git Bash 并执行 claude 命令)
+  registerHandler('launch-claude-code', async () => {
+    const platform = os.platform()
+
+    if (platform !== 'win32') {
+      throw new Error('此功能仅支持 Windows 系统')
+    }
+
+    // 查找 Git Bash 路径
+    const possiblePaths = [
+      'C:\\Program Files\\Git\\git-bash.exe',
+      'C:\\Program Files (x86)\\Git\\git-bash.exe',
+      path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Git', 'git-bash.exe')
+    ]
+
+    let gitBashPath = ''
+    for (const p of possiblePaths) {
+      try {
+        const fs = await import('fs')
+        if (fs.existsSync(p)) {
+          gitBashPath = p
+          break
+        }
+      } catch {
+        continue
+      }
+    }
+
+    if (!gitBashPath) {
+      throw new Error('未找到 Git Bash,请确保已安装 Git')
+    }
+
+    logger.info(`启动 Git Bash: ${gitBashPath}`)
+
+    // 获取 pnpm 全局安装路径(claude 命令所在目录)
+    // Windows 上 pnpm 全局安装的可执行文件在 %LOCALAPPDATA%\pnpm 目录下
+    const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
+    const pnpmGlobalBin = path.join(localAppData, 'pnpm')
+    // 转换为 Git Bash 可识别的路径格式(将反斜杠转为正斜杠,将 C: 转为 /c)
+    const pnpmGlobalBinUnix = pnpmGlobalBin.replace(/\\/g, '/').replace(/^([A-Za-z]):/, '/$1').toLowerCase()
+
+    logger.info(`pnpm 全局路径: ${pnpmGlobalBin} -> ${pnpmGlobalBinUnix}`)
+
+    // 获取 npm 全局安装路径(备用)
+    // Windows 上 npm 全局安装的可执行文件在 %APPDATA%\npm 目录下
+    const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')
+    const npmGlobalBin = path.join(appData, 'npm')
+    const npmGlobalBinUnix = npmGlobalBin.replace(/\\/g, '/').replace(/^([A-Za-z]):/, '/$1').toLowerCase()
+
+    logger.info(`npm 全局路径: ${npmGlobalBin} -> ${npmGlobalBinUnix}`)
+
+    // 获取 Node.js 路径
+    // 常见的 Node.js 安装路径
+    const fs = await import('fs')
+    const possibleNodePaths = [
+      'C:\\Program Files\\nodejs',
+      'C:\\Program Files (x86)\\nodejs',
+      path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'nodejs'),
+      // fnm 安装的 Node.js
+      path.join(os.homedir(), 'AppData', 'Local', 'fnm_multishells'),
+      // nvm-windows 安装的 Node.js
+      path.join(os.homedir(), 'AppData', 'Roaming', 'nvm')
+    ]
+
+    let nodePath = ''
+    for (const p of possibleNodePaths) {
+      if (fs.existsSync(p)) {
+        // 检查是否有 node.exe
+        const nodeExe = path.join(p, 'node.exe')
+        if (fs.existsSync(nodeExe)) {
+          nodePath = p
+          break
+        }
+      }
+    }
+
+    // 如果没找到,尝试从 PATH 环境变量中获取
+    if (!nodePath) {
+      const pathEnv = process.env.PATH || ''
+      const pathDirs = pathEnv.split(';')
+      for (const dir of pathDirs) {
+        const nodeExe = path.join(dir, 'node.exe')
+        if (fs.existsSync(nodeExe)) {
+          nodePath = dir
+          break
+        }
+      }
+    }
+
+    // 构建额外的 PATH(pnpm 优先,因为 claude 是通过 pnpm 安装的)
+    const pathParts: string[] = [pnpmGlobalBinUnix, npmGlobalBinUnix]
+    if (nodePath) {
+      const nodePathUnix = nodePath.replace(/\\/g, '/').replace(/^([A-Za-z]):/, '/$1').toLowerCase()
+      pathParts.unshift(nodePathUnix)
+      logger.info(`Node.js 路径: ${nodePath} -> ${nodePathUnix}`)
+    } else {
+      logger.warn('未找到 Node.js 路径,可能会导致 claude 命令无法运行')
+    }
+    const extraPaths = pathParts.join(':')
+    logger.info(`额外 PATH: ${extraPaths}`)
+
+    // 使用 execa 启动 Git Bash 并执行 claude 命令
+    // 先将 Node.js、pnpm 和 npm 全局路径添加到 PATH,然后执行 claude 命令
+    const bashCommand = `export PATH="${extraPaths}:$PATH"; claude; exec bash`
+    execa(gitBashPath, ['-c', bashCommand], {
+      detached: true,
+      stdio: 'ignore'
+    }).unref()
+
+    return { success: true }
+  })
+
+  // 安装 Claude Code(复用 installClaudeCodeWithStatus 函数)
+  registerHandler('install-claude-code', async () => {
+    return await installClaudeCodeWithStatus()
+  })
+
+  // 检查 VS Code 插件是否已安装
+  registerHandler('check-vscode-extension', async (_event, extensionId: string) => {
+    try {
+      const codePath = await getVscodeCliPath()
+      logger.info(`使用 VS Code CLI: ${codePath}`)
+      // 使用 --show-versions 参数获取插件版本信息
+      const result = await execa(codePath, ['--list-extensions', '--show-versions'], {
+        encoding: 'utf8',
+        stdout: 'pipe',
+        stderr: 'pipe'
+      })
+      // 输出格式为 "extensionId@version",例如 "[email protected]"
+      const extensions = result.stdout.split('\n').map(ext => ext.trim())
+      const targetExtLower = extensionId.toLowerCase()
+      let installed = false
+      let version: string | undefined
+
+      for (const ext of extensions) {
+        const [extId, extVersion] = ext.split('@')
+        if (extId && extId.toLowerCase() === targetExtLower) {
+          installed = true
+          version = extVersion
+          break
+        }
+      }
+
+      logger.info(`检查 VS Code 插件 ${extensionId}: ${installed ? `已安装 v${version}` : '未安装'}`)
+      return { installed, version }
+    } catch (error) {
+      logger.warn(`检查 VS Code 插件失败: ${extensionId}`, error)
+      return { installed: false }
+    }
+  })
+
+  // 安装 VS Code 插件(复用 installVscodeExtension 函数)
+  registerHandler('install-vscode-extension', async (_event, extensionId: string) => {
+    return await installVscodeExtension(extensionId)
+  })
+
+  // ==================== 文件夹选择 ====================
+
+  // 选择文件夹
+  registerHandler('select-directory', async (_event, defaultPath?: string) => {
+    const win = BrowserWindow.getFocusedWindow()
+    const result = await dialog.showOpenDialog(win!, {
+      properties: ['openDirectory'],
+      defaultPath: defaultPath || os.homedir()
+    })
+    if (result.canceled || result.filePaths.length === 0) {
+      return { canceled: true, path: null }
+    }
+    return { canceled: false, path: result.filePaths[0] }
+  })
+
+  // ==================== Git 镜像配置 ====================
+
+  // 设置 Git 镜像
+  registerHandler('set-git-mirror', (_event, mirror: GitMirrorType) => {
+    logger.info(`设置 Git 镜像: ${mirror}`)
+    setGitMirror(mirror)
+  })
+
+  // 获取 Git 镜像配置
+  registerHandler('get-git-mirror-config', () => {
+    return getGitMirrorConfig()
+  })
+
+  // ==================== Node.js 镜像配置 ====================
+
+  // 设置 Node.js 镜像
+  registerHandler('set-nodejs-mirror', (_event, mirror: NodejsMirrorType) => {
+    logger.info(`设置 Node.js 镜像: ${mirror}`)
+    setNodejsMirror(mirror)
+  })
+
+  // 获取 Node.js 镜像配置
+  registerHandler('get-nodejs-mirror-config', () => {
+    return getNodejsMirrorConfig()
+  })
+
+  // ==================== 自动更新相关 ====================
+
+  // 检查应用更新
+  registerHandler('updater:check', async () => {
+    return await checkForUpdates()
+  })
+
+  // 下载更新
+  registerHandler('updater:download', async () => {
+    return await downloadUpdate()
+  })
+
+  // 安装更新并重启
+  registerHandler('updater:install', () => {
+    installUpdate()
+  })
+
+  // 获取当前版本
+  registerHandler('updater:version', () => {
+    return getCurrentVersion()
+  })
+
+  // 检测是否为 Portable 模式
+  registerHandler('updater:is-portable', () => {
+    return isPortableMode()
+  })
+
+  // ==================== 软件安装相关 ====================
+
+  // 统一安装入口
+  registerHandler('install', async (_event, software: SoftwareTypeWithAll, options: InstallOptions = {}) => {
+    const { version, nodejsPath, vscodePath, gitPath } = options
+    const startTime = Date.now()
+
+    // 状态回调
+    const onStatus = (sw: SoftwareTypeWithAll, message: string, progress: number, skipLog?: boolean): void => {
+      sendToRenderer('install-status', { software: sw, message, progress, skipLog })
+      if (!skipLog) {
+        logger.installInfo(`[${sw}] ${message} (${progress}%)`)
+      }
+    }
+
+    try {
+      logger.installInfo(`开始安装: ${software}`, options)
+
+      // Linux 下先 apt update
+      if (os.platform() === 'linux' && ['nodejs', 'git', 'all'].includes(software)) {
+        await aptUpdate(onStatus, software)
+      }
+
+      switch (software) {
+        case 'nodejs':
+          await installNodejs(version, false, onStatus, nodejsPath)
+          sendToRenderer('install-complete', {
+            software: 'nodejs',
+            message: '✅ Node.js 安装完成!',
+            i18nKey: 'log.nodejsComplete'
+          })
+          break
+
+        case 'vscode':
+          await installVscode(version, onStatus, vscodePath)
+          sendToRenderer('install-complete', {
+            software: 'vscode',
+            message: '✅ VS Code 安装完成!',
+            i18nKey: 'log.vscodeComplete'
+          })
+          break
+
+        case 'git':
+          await installGit(version, onStatus, gitPath)
+          sendToRenderer('install-complete', {
+            software: 'git',
+            message: '✅ Git 安装完成!',
+            i18nKey: 'log.gitComplete'
+          })
+          break
+
+        case 'pnpm':
+          await installPnpm(onStatus)
+          sendToRenderer('install-complete', {
+            software: 'pnpm',
+            message: '✅ pnpm 安装完成!',
+            i18nKey: 'log.pnpmComplete'
+          })
+          break
+
+        case 'all': {
+          const installed = await installAll(options, onStatus)
+          sendToRenderer('install-complete', {
+            software: 'all',
+            message: `✅ ${installed.join('、')} 安装完成!`,
+            i18nKey: 'log.allComplete',
+            i18nParams: { software: installed.join(', ') }
+          })
+          break
+        }
+
+        default:
+          throw new Error(`${ERROR_MESSAGES.UNKNOWN_SOFTWARE}: ${software}`)
+      }
+
+      // 记录安装历史
+      const duration = Date.now() - startTime
+      addInstallHistory({
+        software,
+        version: version || 'latest',
+        options,
+        success: true,
+        duration
+      })
+    } catch (error) {
+      const errorMessage = (error as Error).message
+      const isCancelled = errorMessage.includes('取消')
+
+      if (!isCancelled) {
+        // 注意:dialog.showErrorBox 在主进程中无法使用 i18n,保留中文作为后备
+        dialog.showErrorBox('Installation Failed / 安装失败', errorMessage || 'Unknown error / 未知错误')
+      }
+
+      sendToRenderer('install-error', {
+        software,
+        message: isCancelled ? '⚠️ 安装已取消' : `❌ 安装失败:${errorMessage}`,
+        i18nKey: isCancelled ? 'log.installCancelled' : 'log.installError',
+        i18nParams: isCancelled ? undefined : { error: errorMessage }
+      })
+
+      // 记录失败历史
+      addInstallHistory({
+        software,
+        version: version || 'latest',
+        options,
+        success: false,
+        error: errorMessage,
+        cancelled: isCancelled
+      })
+    }
+  })
+}
+
+/**
+ * 移除所有 IPC 处理器
+ * 使用自动注册模块,无需手动维护 handler 列表
+ */
+export { removeAllHandlers as removeHandlers } from './ipc-registry'

+ 74 - 0
electron/modules/ipc-registry.ts

@@ -0,0 +1,74 @@
+// electron/modules/ipc-registry.ts - IPC 处理器自动注册模块
+
+import { ipcMain, type IpcMainInvokeEvent } from 'electron'
+import logger from './logger'
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type IpcHandler = (event: IpcMainInvokeEvent, ...args: any[]) => any
+
+// 存储所有已注册的 handler 名称
+const registeredHandlers = new Set<string>()
+
+/**
+ * 注册 IPC 处理器
+ * @param channel IPC 通道名称
+ * @param handler 处理函数
+ */
+export function registerHandler(channel: string, handler: IpcHandler): void {
+  if (registeredHandlers.has(channel)) {
+    logger.warn(`IPC handler "${channel}" 已存在,将被覆盖`)
+    ipcMain.removeHandler(channel)
+  }
+
+  ipcMain.handle(channel, handler)
+  registeredHandlers.add(channel)
+  logger.debug(`已注册 IPC handler: ${channel}`)
+}
+
+/**
+ * 批量注册 IPC 处理器
+ * @param handlers 处理器映射对象
+ */
+export function registerHandlers(handlers: Record<string, IpcHandler>): void {
+  for (const [channel, handler] of Object.entries(handlers)) {
+    registerHandler(channel, handler)
+  }
+}
+
+/**
+ * 移除指定的 IPC 处理器
+ * @param channel IPC 通道名称
+ */
+export function removeHandler(channel: string): void {
+  if (registeredHandlers.has(channel)) {
+    ipcMain.removeHandler(channel)
+    registeredHandlers.delete(channel)
+    logger.debug(`已移除 IPC handler: ${channel}`)
+  }
+}
+
+/**
+ * 移除所有已注册的 IPC 处理器
+ */
+export function removeAllHandlers(): void {
+  for (const channel of registeredHandlers) {
+    ipcMain.removeHandler(channel)
+  }
+  const count = registeredHandlers.size
+  registeredHandlers.clear()
+  logger.info(`已移除所有 IPC handlers (共 ${count} 个)`)
+}
+
+/**
+ * 获取所有已注册的 handler 名称
+ */
+export function getRegisteredHandlers(): string[] {
+  return Array.from(registeredHandlers)
+}
+
+/**
+ * 检查 handler 是否已注册
+ */
+export function isHandlerRegistered(channel: string): boolean {
+  return registeredHandlers.has(channel)
+}

+ 262 - 0
electron/modules/logger.ts

@@ -0,0 +1,262 @@
+// electron/modules/logger.ts - 日志模块 (基于 electron-log)
+
+import log from 'electron-log/main'
+import * as path from 'path'
+import * as fs from 'fs'
+import { app } from 'electron'
+import type { LogEntry, LogCategory } from './types'
+
+// 日志配置常量
+const MAX_LOG_FILE_SIZE = 5 * 1024 * 1024 // 5MB
+const DEFAULT_MAX_FILES = 3
+const DEFAULT_MAX_RECENT_LOGS = 500
+
+// 日志实例
+let appLog: ReturnType<typeof log.create> | null = null
+let installLog: ReturnType<typeof log.create> | null = null
+
+class Logger {
+  private recentLogs: LogEntry[] = []
+  private maxRecentLogs = DEFAULT_MAX_RECENT_LOGS
+  private logDir: string = ''
+  private initialized = false
+
+  init(): void {
+    if (this.initialized) return
+    this.initialized = true
+
+    // 日志目录设置 - 统一使用 Electron 推荐的用户数据目录
+    // Windows: C:\Users\用户名\AppData\Roaming\claude-ai-installer\logs
+    const userDataDir = app.getPath('userData')
+    console.log('[Logger] 使用用户数据目录:', userDataDir)
+    this.logDir = path.join(userDataDir, 'logs')
+
+    // 确保日志目录存在
+    console.log('[Logger] 尝试创建日志目录:', this.logDir)
+    try {
+      if (!fs.existsSync(this.logDir)) {
+        fs.mkdirSync(this.logDir, { recursive: true })
+        console.log('[Logger] 日志目录创建成功')
+      } else {
+        console.log('[Logger] 日志目录已存在')
+      }
+    } catch (err) {
+      console.error('[Logger] 创建日志目录失败:', err)
+    }
+
+    // 保存日志目录路径供 resolvePathFn 使用
+    const logDir = this.logDir
+
+    // 创建应用日志实例
+    console.log('[Logger] 创建 appLog 实例...')
+    appLog = log.create({ logId: 'app' })
+    const appLogPath = path.join(logDir, 'app.log')
+    appLog.transports.file.resolvePathFn = () => appLogPath
+    appLog.transports.file.level = 'debug'
+    appLog.transports.file.maxSize = MAX_LOG_FILE_SIZE
+    appLog.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'
+    appLog.transports.file.archiveLogFn = (file) => this.archiveLog(file)
+    appLog.transports.console.level = 'debug'
+    appLog.transports.console.format = '[{y}-{m}-{d} {h}:{i}:{s}] [{level}] {text}'
+    console.log('[Logger] appLog 配置完成,路径:', appLogPath)
+
+    // 创建安装日志实例
+    console.log('[Logger] 创建 installLog 实例...')
+    installLog = log.create({ logId: 'install' })
+    const installLogPath = path.join(logDir, 'install.log')
+    installLog.transports.file.resolvePathFn = () => installLogPath
+    installLog.transports.file.level = 'debug'
+    installLog.transports.file.maxSize = MAX_LOG_FILE_SIZE
+    installLog.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'
+    installLog.transports.file.archiveLogFn = (file) => this.archiveLog(file)
+    installLog.transports.console.level = 'debug'
+    installLog.transports.console.format = '[{y}-{m}-{d} {h}:{i}:{s}] [{level}] {text}'
+    console.log('[Logger] installLog 配置完成,路径:', installLogPath)
+
+    // 写入初始化日志
+    this.info('日志系统初始化完成', { logDir: this.logDir })
+    this.installInfo('安装日志系统初始化完成', { logDir: this.logDir })
+  }
+
+  /**
+   * 日志文件轮转
+   */
+  private archiveLog(file: { path: string; toString: () => string }): void {
+    const filePath = file.toString()
+    const info = path.parse(filePath)
+
+    // 删除最旧的轮转文件
+    for (let i = DEFAULT_MAX_FILES; i >= 1; i--) {
+      const oldFile = path.join(info.dir, `${info.name}.${i}${info.ext}`)
+      const newFile = path.join(info.dir, `${info.name}.${i + 1}${info.ext}`)
+      try {
+        if (fs.existsSync(oldFile)) {
+          if (i === DEFAULT_MAX_FILES) {
+            fs.unlinkSync(oldFile)
+          } else {
+            fs.renameSync(oldFile, newFile)
+          }
+        }
+      } catch {
+        // 忽略轮转错误
+      }
+    }
+
+    // 重命名当前日志文件
+    try {
+      fs.renameSync(filePath, path.join(info.dir, `${info.name}.1${info.ext}`))
+    } catch {
+      // 忽略重命名错误
+    }
+  }
+
+  getLogDir(): string {
+    return this.logDir
+  }
+
+  getAppLogPath(): string {
+    return path.join(this.logDir, 'app.log')
+  }
+
+  getInstallLogPath(): string {
+    return path.join(this.logDir, 'install.log')
+  }
+
+  private addToRecent(level: LogEntry['level'], message: string, category: LogCategory, data?: unknown): void {
+    const entry: LogEntry = {
+      level,
+      message,
+      timestamp: new Date().toISOString(),
+      category,
+      data
+    }
+    this.recentLogs.push(entry)
+    if (this.recentLogs.length > this.maxRecentLogs) {
+      this.recentLogs.shift()
+    }
+  }
+
+  // ==================== 应用日志方法 ====================
+
+  debug(message: string, data?: unknown): void {
+    if (appLog) {
+      if (data !== undefined) {
+        appLog.debug(message, data)
+      } else {
+        appLog.debug(message)
+      }
+    } else {
+      console.debug('[Logger]', message, data ?? '')
+    }
+    this.addToRecent('DEBUG', message, 'app', data)
+  }
+
+  info(message: string, data?: unknown): void {
+    if (appLog) {
+      if (data !== undefined) {
+        appLog.info(message, data)
+      } else {
+        appLog.info(message)
+      }
+    } else {
+      console.info('[Logger]', message, data ?? '')
+    }
+    this.addToRecent('INFO', message, 'app', data)
+  }
+
+  warn(message: string, data?: unknown): void {
+    if (appLog) {
+      if (data !== undefined) {
+        appLog.warn(message, data)
+      } else {
+        appLog.warn(message)
+      }
+    } else {
+      console.warn('[Logger]', message, data ?? '')
+    }
+    this.addToRecent('WARN', message, 'app', data)
+  }
+
+  error(message: string, data?: unknown): void {
+    if (appLog) {
+      if (data !== undefined) {
+        appLog.error(message, data)
+      } else {
+        appLog.error(message)
+      }
+    } else {
+      console.error('[Logger]', message, data ?? '')
+    }
+    this.addToRecent('ERROR', message, 'app', data)
+  }
+
+  // ==================== 安装日志方法 ====================
+
+  installDebug(message: string, data?: unknown): void {
+    if (installLog) {
+      if (data !== undefined) {
+        installLog.debug(message, data)
+      } else {
+        installLog.debug(message)
+      }
+    } else {
+      console.debug('[InstallLog]', message, data ?? '')
+    }
+    this.addToRecent('DEBUG', message, 'install', data)
+  }
+
+  installInfo(message: string, data?: unknown): void {
+    if (installLog) {
+      if (data !== undefined) {
+        installLog.info(message, data)
+      } else {
+        installLog.info(message)
+      }
+    } else {
+      console.info('[InstallLog]', message, data ?? '')
+    }
+    this.addToRecent('INFO', message, 'install', data)
+  }
+
+  installWarn(message: string, data?: unknown): void {
+    if (installLog) {
+      if (data !== undefined) {
+        installLog.warn(message, data)
+      } else {
+        installLog.warn(message)
+      }
+    } else {
+      console.warn('[InstallLog]', message, data ?? '')
+    }
+    this.addToRecent('WARN', message, 'install', data)
+  }
+
+  installError(message: string, data?: unknown): void {
+    if (installLog) {
+      if (data !== undefined) {
+        installLog.error(message, data)
+      } else {
+        installLog.error(message)
+      }
+    } else {
+      console.error('[InstallLog]', message, data ?? '')
+    }
+    this.addToRecent('ERROR', message, 'install', data)
+  }
+
+  // ==================== 日志查询方法 ====================
+
+  getRecentLogs(limit = 200, category?: LogCategory): LogEntry[] {
+    if (category) {
+      return this.recentLogs.filter(log => log.category === category).slice(-limit)
+    }
+    return this.recentLogs.slice(-limit)
+  }
+
+  close(): void {
+    this.recentLogs = []
+  }
+}
+
+export const logger = new Logger()
+export default logger

+ 65 - 0
electron/modules/source-updater.ts

@@ -0,0 +1,65 @@
+// electron/modules/source-updater.ts - 包管理器源更新模块
+
+import * as os from 'os'
+import { execa } from 'execa'
+import { needsSourceUpdate, markSourceUpdated } from './utils'
+import logger from './logger'
+
+// 正在进行中的更新操作(用于防止并发重复更新)
+let aptUpdatePromise: Promise<void> | null = null
+
+/**
+ * 检查 apt 源是否需要更新
+ * @returns 是否需要更新
+ */
+export function needsAptUpdate(): boolean {
+  if (os.platform() !== 'linux') return false
+  return needsSourceUpdate('apt')
+}
+
+/**
+ * 标记 apt 源已更新
+ */
+export function markAptUpdated(): void {
+  markSourceUpdated('apt')
+  logger.info('apt 源已更新')
+}
+
+/**
+ * 更新 apt 源(Linux 专用,带缓存,1天内只更新一次)
+ * 用于版本查询前的源更新
+ * 注意:这个函数用于版本查询,实际安装时使用 installer.ts 中的 aptUpdate
+ * 支持并发调用:多个调用会共享同一个更新操作
+ */
+export async function updateAptSourceForQuery(): Promise<void> {
+  if (os.platform() !== 'linux') return
+  if (!needsSourceUpdate('apt')) {
+    logger.info('apt 源已在缓存期内,跳过更新')
+    return
+  }
+
+  // 如果已有更新操作在进行中,等待它完成
+  if (aptUpdatePromise) {
+    logger.info('apt 源更新已在进行中,等待完成...')
+    return aptUpdatePromise
+  }
+
+  // 创建新的更新操作
+  aptUpdatePromise = (async () => {
+    try {
+      logger.info('正在更新 apt 源...')
+      // 使用 sudo 执行 apt update
+      await execa('sudo', ['apt', 'update'], { stdio: 'pipe' })
+      markSourceUpdated('apt')
+      logger.info('apt 源更新完成')
+    } catch (error) {
+      logger.warn('apt 源更新失败,继续查询', error)
+      // 即使更新失败也标记为已尝试,避免当天重复尝试
+      markSourceUpdated('apt')
+    } finally {
+      aptUpdatePromise = null
+    }
+  })()
+
+  return aptUpdatePromise
+}

+ 4 - 0
electron/modules/types.ts

@@ -0,0 +1,4 @@
+// electron/modules/types.ts - 类型定义
+// 从共享类型文件重新导出所有类型
+
+export * from '../../shared/types'

+ 297 - 0
electron/modules/updater.ts

@@ -0,0 +1,297 @@
+// electron/modules/updater.ts - 自动更新模块
+
+import { autoUpdater, UpdateInfo } from 'electron-updater'
+import { BrowserWindow, app } from 'electron'
+import * as path from 'path'
+import * as fs from 'fs'
+import logger from './logger'
+
+// 更新状态
+export type UpdateStatus =
+  | 'checking'
+  | 'available'
+  | 'not-available'
+  | 'downloading'
+  | 'downloaded'
+  | 'error'
+
+// 更新进度信息
+export interface UpdateProgress {
+  percent: number
+  bytesPerSecond: number
+  total: number
+  transferred: number
+}
+
+// 更新信息
+export interface UpdateResult {
+  status: UpdateStatus
+  info?: UpdateInfo
+  progress?: UpdateProgress
+  error?: string
+}
+
+// 主窗口引用
+let mainWindow: BrowserWindow | null = null
+
+/**
+ * 检测是否为 Windows Portable 模式(启动器模式)
+ * 启动器模式的特征:exe 位于 app 子目录中
+ */
+export function isPortableMode(): boolean {
+  const exePath = process.execPath.toLowerCase()
+  // 检查是否在 app 子目录中(启动器模式)
+  const isInAppDir = exePath.includes(`${path.sep}app${path.sep}`)
+  // 或者文件名包含 portable
+  const isPortableName = exePath.includes('portable')
+  return isInAppDir || isPortableName
+}
+
+/**
+ * 获取 Portable 根目录(启动器所在目录)
+ */
+function getPortableRootDir(): string {
+  const exeDir = path.dirname(process.execPath)
+  // 如果在 app 子目录中,返回上级目录
+  if (exeDir.toLowerCase().endsWith(`${path.sep}app`)) {
+    return path.dirname(exeDir)
+  }
+  return exeDir
+}
+
+/**
+ * 获取更新目录(Portable 模式专用)
+ */
+function getUpdateDir(): string {
+  return path.join(getPortableRootDir(), 'update')
+}
+
+/**
+ * 获取应用目录(Portable 模式专用)
+ */
+function getAppDir(): string {
+  return path.join(getPortableRootDir(), 'app')
+}
+
+/**
+ * 发送更新状态到渲染进程
+ */
+function sendUpdateStatus(result: UpdateResult): void {
+  if (mainWindow && !mainWindow.isDestroyed()) {
+    mainWindow.webContents.send('updater:status', result)
+  }
+}
+
+/**
+ * 初始化自动更新器
+ */
+export function initAutoUpdater(window: BrowserWindow): void {
+  mainWindow = window
+
+  // 配置
+  autoUpdater.autoDownload = false // 不自动下载,让用户确认
+  autoUpdater.autoInstallOnAppQuit = true
+  autoUpdater.autoRunAppAfterInstall = true
+
+  // 检查更新中
+  autoUpdater.on('checking-for-update', () => {
+    logger.info('正在检查更新...')
+    sendUpdateStatus({ status: 'checking' })
+  })
+
+  // 有可用更新
+  autoUpdater.on('update-available', (info: UpdateInfo) => {
+    logger.info('发现新版本', { version: info.version })
+    sendUpdateStatus({ status: 'available', info })
+  })
+
+  // 没有更新
+  autoUpdater.on('update-not-available', (info: UpdateInfo) => {
+    logger.info('当前已是最新版本', { version: info.version })
+    sendUpdateStatus({ status: 'not-available', info })
+  })
+
+  // 下载进度
+  autoUpdater.on('download-progress', (progress) => {
+    logger.debug('下载进度', { percent: progress.percent.toFixed(2) })
+    sendUpdateStatus({
+      status: 'downloading',
+      progress: {
+        percent: progress.percent,
+        bytesPerSecond: progress.bytesPerSecond,
+        total: progress.total,
+        transferred: progress.transferred
+      }
+    })
+  })
+
+  // 下载完成
+  autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
+    logger.info('更新下载完成', { version: info.version })
+    sendUpdateStatus({ status: 'downloaded', info })
+  })
+
+  // 错误
+  autoUpdater.on('error', (error) => {
+    logger.error('更新错误', error)
+    sendUpdateStatus({ status: 'error', error: error.message })
+  })
+
+  logger.info('自动更新器已初始化', {
+    isPortable: isPortableMode(),
+    currentVersion: app.getVersion()
+  })
+}
+
+/**
+ * 检查更新
+ */
+export async function checkForUpdates(): Promise<UpdateResult> {
+  try {
+    logger.info('手动检查更新')
+    const result = await autoUpdater.checkForUpdates()
+    if (result?.updateInfo) {
+      return { status: 'available', info: result.updateInfo }
+    }
+    return { status: 'not-available' }
+  } catch (error) {
+    const message = error instanceof Error ? error.message : String(error)
+    logger.error('检查更新失败', error)
+    return { status: 'error', error: message }
+  }
+}
+
+/**
+ * 下载更新
+ */
+export async function downloadUpdate(): Promise<UpdateResult> {
+  try {
+    logger.info('开始下载更新')
+    await autoUpdater.downloadUpdate()
+    return { status: 'downloading' }
+  } catch (error) {
+    const message = error instanceof Error ? error.message : String(error)
+    logger.error('下载更新失败', error)
+    return { status: 'error', error: message }
+  }
+}
+
+/**
+ * 安装更新并重启
+ * 自动检测是否为 Portable 模式并使用对应的更新方式
+ */
+export function installUpdate(): void {
+  logger.info('安装更新并重启', { isPortable: isPortableMode() })
+
+  if (isPortableMode()) {
+    // Portable 模式:通过启动器更新
+    installPortableUpdate()
+  } else {
+    // 标准模式:使用 electron-updater
+    autoUpdater.quitAndInstall(false, true)
+  }
+}
+
+/**
+ * 获取当前版本
+ */
+export function getCurrentVersion(): string {
+  return app.getVersion()
+}
+
+/**
+ * Portable 模式:将下载的更新文件移动到 update 目录
+ * 启动器会在下次启动时自动应用更新
+ */
+export async function preparePortableUpdate(): Promise<boolean> {
+  if (!isPortableMode()) {
+    logger.info('非 Portable 模式,跳过 Portable 更新准备')
+    return false
+  }
+
+  try {
+    const updateDir = getUpdateDir()
+
+    // 确保更新目录存在
+    if (!fs.existsSync(updateDir)) {
+      fs.mkdirSync(updateDir, { recursive: true })
+    }
+
+    // electron-updater 下载的文件位置
+    const downloadedUpdatePath = path.join(app.getPath('userData'), 'pending')
+
+    if (fs.existsSync(downloadedUpdatePath)) {
+      // 查找下载的 exe 文件
+      const files = fs.readdirSync(downloadedUpdatePath)
+      const exeFile = files.find(f => f.endsWith('.exe'))
+
+      if (exeFile) {
+        const srcPath = path.join(downloadedUpdatePath, exeFile)
+        const destPath = path.join(updateDir, exeFile)
+
+        // 复制到 update 目录
+        fs.copyFileSync(srcPath, destPath)
+        logger.info('Portable 更新文件已准备', { destPath })
+
+        return true
+      }
+    }
+
+    logger.warn('未找到下载的更新文件')
+    return false
+  } catch (error) {
+    logger.error('准备 Portable 更新失败', error)
+    return false
+  }
+}
+
+/**
+ * Portable 模式:安装更新
+ * 通过重启应用让启动器完成更新
+ */
+export function installPortableUpdate(): void {
+  if (!isPortableMode()) {
+    // 非 Portable 模式,使用标准更新
+    autoUpdater.quitAndInstall(false, true)
+    return
+  }
+
+  logger.info('Portable 模式:准备重启以应用更新')
+
+  // 查找启动器
+  const rootDir = getPortableRootDir()
+  const launcherPath = path.join(rootDir, 'Claude-AI-Installer.exe')
+
+  if (fs.existsSync(launcherPath)) {
+    // 通过启动器重启
+    const { spawn } = require('child_process')
+    spawn(launcherPath, [], {
+      detached: true,
+      stdio: 'ignore',
+      cwd: rootDir
+    }).unref()
+
+    logger.info('已启动启动器,准备退出当前应用')
+    app.quit()
+  } else {
+    // 没有启动器,直接退出(用户需要手动重启)
+    logger.warn('未找到启动器,请手动重启应用')
+    app.quit()
+  }
+}
+
+/**
+ * 获取 Portable 更新信息
+ */
+export function getPortableUpdateInfo(): { hasUpdate: boolean; updateDir: string; appDir: string } {
+  const updateDir = getUpdateDir()
+  const appDir = getAppDir()
+
+  let hasUpdate = false
+  if (fs.existsSync(updateDir)) {
+    const files = fs.readdirSync(updateDir)
+    hasUpdate = files.some(f => f.endsWith('.exe'))
+  }
+
+  return { hasUpdate, updateDir, appDir }
+}

+ 746 - 0
electron/modules/utils.ts

@@ -0,0 +1,746 @@
+// electron/modules/utils.ts - 工具函数
+
+import axios, { AxiosError, CancelTokenSource } from 'axios'
+import * as os from 'os'
+import * as fs from 'fs'
+import * as path from 'path'
+import { execa } from 'execa'
+import { REQUEST_TIMEOUT, MAX_RETRIES, RETRY_DELAY, ERROR_MESSAGES, SOURCE_UPDATE_CACHE_TTL } from './constants'
+
+// 版本缓存
+const versionCache = new Map<string, { data: unknown; expiry: number }>()
+
+// 包管理器源更新缓存(记录上次更新时间)
+const sourceUpdateCache = new Map<string, number>()
+
+// 创建 axios 实例
+const axiosInstance = axios.create({
+  headers: { 'User-Agent': 'ApqInstaller/2.0' },
+  timeout: REQUEST_TIMEOUT
+})
+
+/**
+ * 验证 URL 是否合法
+ */
+export function isValidUrl(url: string): boolean {
+  try {
+    const parsed = new URL(url)
+    return ['http:', 'https:'].includes(parsed.protocol)
+  } catch {
+    return false
+  }
+}
+
+/**
+ * 带超时和重试的 HTTP GET 请求(使用 axios)
+ */
+export async function httpsGet<T = unknown>(
+  url: string,
+  options: {
+    timeout?: number
+    retries?: number
+    retryDelay?: number
+  } = {}
+): Promise<T> {
+  const {
+    timeout = REQUEST_TIMEOUT,
+    retries = MAX_RETRIES,
+    retryDelay = RETRY_DELAY
+  } = options
+
+  let lastError: Error | null = null
+
+  for (let attempt = 1; attempt <= retries; attempt++) {
+    try {
+      const response = await axiosInstance.get<T>(url, {
+        timeout,
+        maxRedirects: 5
+      })
+      return response.data
+    } catch (error) {
+      lastError = error as Error
+      const axiosError = error as AxiosError
+
+      if (axiosError.code === 'ECONNABORTED' || axiosError.message.includes('timeout')) {
+        console.log(`请求超时,${retryDelay}ms 后重试... (剩余 ${retries - attempt} 次)`)
+      } else {
+        console.log(`请求失败: ${axiosError.message},${retryDelay}ms 后重试... (剩余 ${retries - attempt} 次)`)
+      }
+
+      if (attempt < retries) {
+        await delay(retryDelay)
+      }
+    }
+  }
+
+  if (lastError) {
+    const axiosError = lastError as AxiosError
+    if (axiosError.code === 'ECONNABORTED' || axiosError.message.includes('timeout')) {
+      throw new Error(ERROR_MESSAGES.TIMEOUT_ERROR)
+    }
+    throw new Error(ERROR_MESSAGES.NETWORK_ERROR + ': ' + lastError.message)
+  }
+
+  throw new Error(ERROR_MESSAGES.NETWORK_ERROR)
+}
+
+/**
+ * 获取 Windows 系统最新的 PATH 环境变量
+ * 安装软件后,系统 PATH 会更新,但当前进程的 PATH 不会自动更新
+ * 通过 PowerShell 从注册表读取最新的 PATH
+ * 同时添加 pnpm 和 npm 的全局 bin 目录,确保能找到全局安装的命令
+ */
+async function getRefreshedWindowsPath(): Promise<string> {
+  try {
+    const result = await execa('powershell', [
+      '-NoProfile',
+      '-Command',
+      `[Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User')`
+    ])
+    let newPath = result.stdout.trim()
+
+    // 添加 pnpm 全局 bin 目录(如果不在 PATH 中)
+    // pnpm 在 Windows 上的默认全局 bin 目录是 %LOCALAPPDATA%\pnpm
+    const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
+    const pnpmGlobalBin = path.join(localAppData, 'pnpm')
+    if (!newPath.toLowerCase().includes(pnpmGlobalBin.toLowerCase())) {
+      newPath = `${pnpmGlobalBin};${newPath}`
+    }
+
+    // 添加 npm 全局 bin 目录(如果不在 PATH 中)
+    // npm 在 Windows 上的默认全局 bin 目录是 %APPDATA%\npm
+    const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')
+    const npmGlobalBin = path.join(appData, 'npm')
+    if (!newPath.toLowerCase().includes(npmGlobalBin.toLowerCase())) {
+      newPath = `${npmGlobalBin};${newPath}`
+    }
+
+    return newPath
+  } catch {
+    // 如果失败,返回当前进程的 PATH,并添加常用的全局 bin 目录
+    let fallbackPath = process.env.PATH || ''
+    const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
+    const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')
+    fallbackPath = `${path.join(localAppData, 'pnpm')};${path.join(appData, 'npm')};${fallbackPath}`
+    return fallbackPath
+  }
+}
+
+/**
+ * 获取刷新后的 PATH 环境变量(跨平台)
+ * Windows: 从注册表读取最新的 PATH
+ * 其他平台: 返回当前进程的 PATH
+ */
+export async function getRefreshedPath(): Promise<string> {
+  if (os.platform() === 'win32') {
+    return await getRefreshedWindowsPath()
+  }
+  return process.env.PATH || ''
+}
+
+/**
+ * 检测命令是否存在
+ * Windows: 会尝试使用刷新后的 PATH 来检测新安装的软件
+ */
+export async function commandExists(command: string, refreshPath = false): Promise<boolean> {
+  try {
+    const platform = os.platform()
+    if (platform === 'win32') {
+      if (refreshPath) {
+        // 使用刷新后的 PATH 执行 where 命令
+        const newPath = await getRefreshedWindowsPath()
+        await execa('where', [command], { env: { ...process.env, PATH: newPath } })
+      } else {
+        await execa('where', [command])
+      }
+    } else {
+      await execa('which', [command])
+    }
+    return true
+  } catch {
+    return false
+  }
+}
+
+/**
+ * 使用刷新后的 PATH 检测命令是否存在
+ * Windows: 直接从注册表读取最新的 PATH 来检测
+ * 其他平台: 使用当前 PATH
+ */
+export async function commandExistsWithRefresh(command: string): Promise<boolean> {
+  const platform = os.platform()
+  if (platform === 'win32') {
+    // Windows 上直接使用刷新后的 PATH 检测
+    return await commandExists(command, true)
+  }
+  return await commandExists(command, false)
+}
+
+/**
+ * 从 Windows 注册表获取 VS Code 安装路径
+ * VS Code 安装后会在多个注册表位置写入信息
+ * 使用 reg query 命令直接查询,比 PowerShell 更快更可靠
+ * @returns VS Code 的 code.cmd 路径,如果找不到则返回 null
+ */
+async function getVscodePathFromRegistry(): Promise<string | null> {
+  if (os.platform() !== 'win32') return null
+
+  // VS Code 可能的注册表位置(使用固定的 GUID 键,与 ipc-handlers.ts 保持一致)
+  const registryPaths = [
+    // 系统安装 (64位)
+    { key: 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1', value: 'InstallLocation' },
+    // 系统安装 (32位 on 64位系统)
+    { key: 'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1', value: 'InstallLocation' },
+    // 用户安装
+    { key: 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1', value: 'InstallLocation' },
+    // VS Code Insiders 系统安装
+    { key: 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1', value: 'InstallLocation' },
+    // VS Code Insiders 用户安装
+    { key: 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1', value: 'InstallLocation' },
+  ]
+
+  for (const reg of registryPaths) {
+    try {
+      const result = await execa('reg', ['query', reg.key, '/v', reg.value], {
+        encoding: 'utf8',
+        timeout: 5000
+      })
+      // 解析注册表输出,格式如: "    InstallLocation    REG_SZ    C:\Program Files\Microsoft VS Code\"
+      const match = result.stdout.match(/InstallLocation\s+REG_SZ\s+(.+)/i)
+      if (match) {
+        const installLocation = match[1].trim()
+        // 构建 code.cmd 的完整路径
+        const codeCmd = path.join(installLocation, 'bin', 'code.cmd')
+        if (fs.existsSync(codeCmd)) {
+          return codeCmd
+        }
+        // 也检查 code.exe (某些版本可能直接使用 exe)
+        const codeExe = path.join(installLocation, 'bin', 'code.exe')
+        if (fs.existsSync(codeExe)) {
+          return codeExe
+        }
+      }
+    } catch {
+      // 该注册表项不存在,继续尝试下一个
+    }
+  }
+
+  return null
+}
+
+/**
+ * 获取命令的完整路径
+ * Windows: 使用刷新后的 PATH 执行 where 命令
+ * macOS/Linux: 使用 which 命令
+ * 注意:对于 VS Code,请使用专门的 getVscodeCliPath 函数
+ * @param command 命令名称
+ * @returns 命令的完整路径,如果找不到则返回 null
+ */
+export async function getCommandPath(command: string): Promise<string | null> {
+  try {
+    const platform = os.platform()
+
+    if (platform === 'win32') {
+      // 使用刷新后的 PATH 执行 where 命令
+      const newPath = await getRefreshedWindowsPath()
+      const result = await execa('where', [command], { env: { ...process.env, PATH: newPath } })
+      // where 可能返回多行(多个路径)
+      // 优先选择 .cmd 或 .exe 文件,因为这些是 Windows 上可直接执行的
+      const paths = result.stdout.trim().split(/\r?\n/)
+      const cmdPath = paths.find(p => p.toLowerCase().endsWith('.cmd'))
+      const exePath = paths.find(p => p.toLowerCase().endsWith('.exe'))
+      // 优先返回 .cmd,其次 .exe,最后返回第一个结果
+      return cmdPath || exePath || paths[0] || null
+    } else {
+      const result = await execa('which', [command])
+      // which 通常只返回一个路径
+      return result.stdout.trim() || null
+    }
+  } catch {
+    return null
+  }
+}
+
+/**
+ * 获取 VS Code CLI (code) 命令的路径
+ * 优先级:PATH 中的 code 命令 > 注册表路径 > 常见安装路径
+ * @returns VS Code CLI 的完整路径,如果都找不到则返回 'code'
+ */
+export async function getVscodeCliPath(): Promise<string> {
+  const platform = os.platform()
+
+  // 首先检查 code 命令是否在 PATH 中
+  try {
+    await execa('code', ['--version'], { timeout: 5000 })
+    return 'code'
+  } catch {
+    // code 命令不在 PATH 中,尝试其他方法
+  }
+
+  if (platform === 'win32') {
+    // Windows: 优先从注册表获取安装路径
+    const registryPath = await getVscodePathFromRegistry()
+    if (registryPath) {
+      return registryPath
+    }
+
+    // 注册表找不到,尝试常见安装路径作为后备
+    const fallbackPaths = [
+      path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Microsoft VS Code', 'bin', 'code.cmd'),
+      path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Microsoft VS Code', 'bin', 'code.cmd'),
+    ]
+
+    for (const codePath of fallbackPaths) {
+      if (fs.existsSync(codePath)) {
+        return codePath
+      }
+    }
+  } else if (platform === 'darwin') {
+    // macOS: 使用 mdfind 查找应用程序
+    try {
+      const result = await execa('mdfind', ['kMDItemCFBundleIdentifier == "com.microsoft.VSCode"'], {
+        encoding: 'utf8',
+        timeout: 5000
+      })
+      const appPath = result.stdout.trim().split('\n')[0]
+      if (appPath) {
+        const codePath = path.join(appPath, 'Contents', 'Resources', 'app', 'bin', 'code')
+        if (fs.existsSync(codePath)) {
+          return codePath
+        }
+      }
+    } catch {
+      // mdfind 失败,尝试常见路径
+    }
+
+    const fallbackPaths = [
+      '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
+      path.join(os.homedir(), 'Applications', 'Visual Studio Code.app', 'Contents', 'Resources', 'app', 'bin', 'code'),
+    ]
+
+    for (const codePath of fallbackPaths) {
+      if (fs.existsSync(codePath)) {
+        return codePath
+      }
+    }
+  } else {
+    // Linux: 使用 which 或检查常见路径
+    try {
+      const result = await execa('which', ['code'], { encoding: 'utf8', timeout: 5000 })
+      const codePath = result.stdout.trim()
+      if (codePath && fs.existsSync(codePath)) {
+        return codePath
+      }
+    } catch {
+      // which 失败
+    }
+
+    const fallbackPaths = [
+      '/usr/bin/code',
+      '/usr/share/code/bin/code',
+      '/snap/bin/code',
+    ]
+
+    for (const codePath of fallbackPaths) {
+      if (fs.existsSync(codePath)) {
+        return codePath
+      }
+    }
+  }
+
+  // 如果都找不到,返回 'code' 让系统尝试
+  return 'code'
+}
+
+/**
+ * 获取命令版本
+ * @param command 命令名称
+ * @param versionArgs 版本参数
+ * @param refreshPath 是否使用刷新后的 PATH(仅 Windows)
+ */
+export async function getCommandVersion(
+  command: string,
+  versionArgs: string[] = ['--version'],
+  refreshPath = false
+): Promise<string | null> {
+  try {
+    let result
+    if (refreshPath && os.platform() === 'win32') {
+      const newPath = await getRefreshedWindowsPath()
+      result = await execa(command, versionArgs, { env: { ...process.env, PATH: newPath } })
+    } else {
+      result = await execa(command, versionArgs)
+    }
+    const output = result.stdout || result.stderr
+    const match = output.match(/(\d+\.\d+\.\d+)/)
+    return match ? match[1] : null
+  } catch {
+    return null
+  }
+}
+
+/**
+ * 使用刷新后的 PATH 获取命令版本
+ * Windows: 直接从注册表读取最新的 PATH 来执行命令
+ * 其他平台: 使用当前 PATH
+ */
+export async function getCommandVersionWithRefresh(
+  command: string,
+  versionArgs: string[] = ['--version']
+): Promise<string | null> {
+  const platform = os.platform()
+  if (platform === 'win32') {
+    // Windows 上直接使用刷新后的 PATH
+    return await getCommandVersion(command, versionArgs, true)
+  }
+  return await getCommandVersion(command, versionArgs, false)
+}
+
+/**
+ * 设置版本缓存
+ */
+export function setCache(key: string, data: unknown, ttl: number): void {
+  versionCache.set(key, {
+    data,
+    expiry: Date.now() + ttl
+  })
+}
+
+/**
+ * 获取版本缓存
+ */
+export function getCache<T = unknown>(key: string): T | null {
+  const cached = versionCache.get(key)
+  if (cached && cached.expiry > Date.now()) {
+    return cached.data as T
+  }
+  versionCache.delete(key)
+  return null
+}
+
+/**
+ * 清除版本缓存
+ */
+export function clearCache(key?: string): void {
+  if (key) {
+    versionCache.delete(key)
+  } else {
+    versionCache.clear()
+  }
+}
+
+/**
+ * 延迟函数
+ */
+export function delay(ms: number): Promise<void> {
+  return new Promise((resolve) => setTimeout(resolve, ms))
+}
+
+/**
+ * 检测网络连接
+ */
+export async function checkNetworkConnection(): Promise<boolean> {
+  try {
+    await httpsGet('https://www.baidu.com', { timeout: 5000, retries: 1 })
+    return true
+  } catch {
+    try {
+      await httpsGet('https://www.google.com', { timeout: 5000, retries: 1 })
+      return true
+    } catch {
+      return false
+    }
+  }
+}
+
+/**
+ * 下载进度回调类型
+ */
+export type DownloadProgressCallback = (downloaded: number, total: number, percent: number) => void
+
+/**
+ * 下载任务信息
+ */
+interface DownloadTask {
+  cancelSource: CancelTokenSource
+  tempPath: string
+  destPath: string
+}
+
+// 活跃的下载任务(支持多个并行下载)
+const activeDownloads = new Map<string, DownloadTask>()
+
+/**
+ * 取消指定下载并删除临时文件
+ * @param downloadId 下载ID(通常是目标文件路径)
+ * @returns 是否成功取消
+ */
+export function cancelDownload(downloadId: string): boolean {
+  const task = activeDownloads.get(downloadId)
+  if (task) {
+    task.cancelSource.cancel('用户取消下载')
+    // 删除临时文件
+    fs.unlink(task.tempPath, () => {})
+    activeDownloads.delete(downloadId)
+    return true
+  }
+  return false
+}
+
+/**
+ * 取消所有下载并删除临时文件
+ * @returns 取消的下载数量
+ */
+export function cancelAllDownloads(): number {
+  let count = 0
+  for (const [downloadId, task] of activeDownloads) {
+    task.cancelSource.cancel('用户取消下载')
+    fs.unlink(task.tempPath, () => {})
+    activeDownloads.delete(downloadId)
+    count++
+  }
+  return count
+}
+
+/**
+ * 取消当前下载并删除临时文件(兼容旧接口)
+ * @returns 是否有下载被取消
+ */
+export function cancelCurrentDownload(): boolean {
+  return cancelAllDownloads() > 0
+}
+
+/**
+ * 获取已下载的文件大小
+ */
+function getDownloadedSize(tempPath: string): number {
+  try {
+    if (fs.existsSync(tempPath)) {
+      return fs.statSync(tempPath).size
+    }
+  } catch {
+    // 忽略错误
+  }
+  return 0
+}
+
+/**
+ * 下载文件到指定路径(支持断点续传和多文件并行下载)
+ * @param url 下载地址
+ * @param destPath 目标文件路径
+ * @param onProgress 进度回调
+ * @param options 下载选项
+ */
+export async function downloadFile(
+  url: string,
+  destPath: string,
+  onProgress?: DownloadProgressCallback,
+  options: {
+    timeout?: number
+  } = {}
+): Promise<string> {
+  const { timeout = 60000 } = options
+
+  // 临时文件路径(用于断点续传)
+  const tempPath = destPath + '.downloading'
+
+  // 创建取消令牌
+  const cancelSource = axios.CancelToken.source()
+
+  // 注册下载任务
+  const downloadId = destPath
+  activeDownloads.set(downloadId, {
+    cancelSource,
+    tempPath,
+    destPath
+  })
+
+  try {
+    // 获取已下载的文件大小
+    const downloadedSize = getDownloadedSize(tempPath)
+
+    // 构建请求头,支持断点续传
+    const headers: Record<string, string> = {}
+    if (downloadedSize > 0) {
+      headers['Range'] = `bytes=${downloadedSize}-`
+      console.log(`断点续传: 从 ${(downloadedSize / 1024 / 1024).toFixed(1)}MB 处继续下载`)
+    }
+
+    // 发起请求
+    const response = await axiosInstance.get(url, {
+      headers,
+      timeout,
+      responseType: 'stream',
+      cancelToken: cancelSource.token,
+      maxRedirects: 5,
+      // 不验证状态码,手动处理
+      validateStatus: () => true
+    })
+
+    // 处理 HTTP 错误状态码
+    if (response.status >= 400) {
+      // 416 表示 Range 请求无效(可能文件已完成或服务器不支持)
+      if (response.status === 416 && downloadedSize > 0) {
+        console.log('文件可能已下载完成,尝试验证...')
+        try {
+          if (fs.existsSync(destPath)) {
+            fs.unlinkSync(destPath)
+          }
+          fs.renameSync(tempPath, destPath)
+          activeDownloads.delete(downloadId)
+          return destPath
+        } catch {
+          // 如果重命名失败,删除临时文件重新下载
+          fs.unlinkSync(tempPath)
+        }
+      }
+      throw new Error(`HTTP ${response.status}: 下载失败`)
+    }
+
+    // 206 表示部分内容(断点续传成功)
+    const isPartialContent = response.status === 206
+
+    // 计算总大小
+    let totalSize = 0
+    if (isPartialContent) {
+      // 从 Content-Range 头获取总大小: bytes 0-999/1000
+      const contentRange = response.headers['content-range']
+      if (contentRange) {
+        const match = contentRange.match(/\/(\d+)$/)
+        if (match) {
+          totalSize = parseInt(match[1], 10)
+        }
+      }
+    } else {
+      // 新下载,获取 Content-Length
+      totalSize = parseInt(response.headers['content-length'] || '0', 10)
+      // 如果是新下载但存在临时文件,说明服务器不支持断点续传,删除重新下载
+      if (downloadedSize > 0) {
+        console.log('服务器不支持断点续传,重新下载')
+        fs.unlinkSync(tempPath)
+      }
+    }
+
+    // 确保目标目录存在
+    const dir = path.dirname(destPath)
+    if (!fs.existsSync(dir)) {
+      fs.mkdirSync(dir, { recursive: true })
+    }
+
+    // 以追加模式打开文件(断点续传)或创建新文件
+    const fileStream = fs.createWriteStream(tempPath, {
+      flags: isPartialContent ? 'a' : 'w'
+    })
+
+    let currentDownloaded = isPartialContent ? downloadedSize : 0
+
+    // 返回 Promise
+    return new Promise<string>((resolve, reject) => {
+      response.data.on('data', (chunk: Buffer) => {
+        currentDownloaded += chunk.length
+        if (onProgress && totalSize > 0) {
+          const percent = Math.round((currentDownloaded / totalSize) * 100)
+          onProgress(currentDownloaded, totalSize, percent)
+        }
+      })
+
+      response.data.on('error', (err: Error) => {
+        fileStream.close()
+        activeDownloads.delete(downloadId)
+        reject(new Error(ERROR_MESSAGES.NETWORK_ERROR + ': ' + err.message))
+      })
+
+      response.data.pipe(fileStream)
+
+      fileStream.on('finish', () => {
+        fileStream.close()
+
+        // 验证文件大小
+        const finalSize = getDownloadedSize(tempPath)
+        if (totalSize > 0 && finalSize < totalSize) {
+          // 文件不完整,保留临时文件以便下次续传
+          console.log(`下载不完整: ${finalSize}/${totalSize} 字节,可点击重新安装继续下载`)
+          activeDownloads.delete(downloadId)
+          reject(new Error('下载不完整,请重试'))
+          return
+        }
+
+        // 重命名临时文件为最终文件
+        try {
+          if (fs.existsSync(destPath)) {
+            fs.unlinkSync(destPath)
+          }
+          fs.renameSync(tempPath, destPath)
+          activeDownloads.delete(downloadId)
+          resolve(destPath)
+        } catch (err) {
+          activeDownloads.delete(downloadId)
+          reject(err)
+        }
+      })
+
+      fileStream.on('error', (err) => {
+        // 保留临时文件以便续传
+        activeDownloads.delete(downloadId)
+        reject(err)
+      })
+    })
+  } catch (error) {
+    activeDownloads.delete(downloadId)
+
+    if (axios.isCancel(error)) {
+      // 用户取消,删除临时文件
+      fs.unlink(tempPath, () => {})
+      throw new Error(ERROR_MESSAGES.INSTALL_CANCELLED || '下载已取消')
+    }
+
+    const axiosError = error as AxiosError
+    if (axiosError.code === 'ECONNABORTED' || axiosError.message?.includes('timeout')) {
+      // 超时,保留临时文件以便续传
+      throw new Error(ERROR_MESSAGES.TIMEOUT_ERROR)
+    }
+
+    // 其他错误,保留临时文件以便续传
+    throw error
+  }
+}
+
+/**
+ * 获取临时目录路径
+ */
+export function getTempDir(): string {
+  return os.tmpdir()
+}
+
+/**
+ * 检查包管理器源是否需要更新
+ * @param manager 包管理器名称 (apt, brew)
+ * @returns 是否需要更新
+ */
+export function needsSourceUpdate(manager: string): boolean {
+  const lastUpdate = sourceUpdateCache.get(manager)
+  if (!lastUpdate) {
+    return true
+  }
+  return Date.now() - lastUpdate > SOURCE_UPDATE_CACHE_TTL
+}
+
+/**
+ * 标记包管理器源已更新
+ * @param manager 包管理器名称
+ */
+export function markSourceUpdated(manager: string): void {
+  sourceUpdateCache.set(manager, Date.now())
+}
+
+/**
+ * 清除源更新缓存
+ * @param manager 可选,指定清除某个包管理器的缓存,不传则清除所有
+ */
+export function clearSourceUpdateCache(manager?: string): void {
+  if (manager) {
+    sourceUpdateCache.delete(manager)
+  } else {
+    sourceUpdateCache.clear()
+  }
+}

+ 742 - 0
electron/modules/version-fetcher.ts

@@ -0,0 +1,742 @@
+// electron/modules/version-fetcher.ts - 版本获取模块
+// 通过各平台包管理器查询可用版本
+
+import * as os from 'os'
+import { execa } from 'execa'
+import type { SoftwareType, VersionItem, VersionResult, Platform, GitMirrorType, NodejsMirrorType } from './types'
+import {
+  MIN_SUPPORTED_NODE_VERSION,
+  MAX_MAJOR_VERSIONS,
+  VERSION_CACHE_TTL,
+  ERROR_MESSAGES,
+  BREW_PACKAGES,
+  GIT_MIRRORS,
+  NODEJS_MIRRORS,
+  VSCODE_API
+} from './constants'
+import { setCache, getCache, clearCache } from './utils'
+import { updateAptSourceForQuery } from './source-updater'
+import {
+  getGitMirrorFromConfig,
+  saveGitMirrorConfig,
+  getNodejsMirrorFromConfig,
+  saveNodejsMirrorConfig
+} from './config'
+
+/**
+ * 版本比较函数(降序)
+ */
+function compareVersions(a: string, b: string): number {
+  const aParts = a.split('.').map(Number)
+  const bParts = b.split('.').map(Number)
+  for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
+    const diff = (bParts[i] || 0) - (aParts[i] || 0)
+    if (diff !== 0) return diff
+  }
+  return 0
+}
+
+/**
+ * 添加版本到分组 Map
+ */
+function addToVersionMap(
+  map: Map<string, VersionItem[]>,
+  fullVersion: string,
+  label: string,
+  options?: { extra?: Partial<VersionItem>; groupKey?: string }
+): void {
+  const parts = fullVersion.split('.')
+  const key = options?.groupKey ?? `${parts[0]}.${parts[1]}`
+  const list = map.get(key) ?? []
+  // 检查是否已存在相同版本,避免重复
+  if (!list.some((item) => item.value === fullVersion)) {
+    list.push({ value: fullVersion, label, ...options?.extra })
+  }
+  map.set(key, list)
+}
+
+/**
+ * 通用版本列表构建器
+ */
+function buildVersionList(
+  versionsByMajor: Map<string, VersionItem[]>,
+  options: {
+    maxMajors?: number
+    specialVersions?: VersionItem[]
+  } = {}
+): VersionItem[] {
+  const { maxMajors = MAX_MAJOR_VERSIONS, specialVersions = [] } = options
+
+  const versions: VersionItem[] = []
+
+  // 按版本号降序排列
+  const sortedMajors = Array.from(versionsByMajor.keys())
+    .sort(compareVersions)
+    .slice(0, maxMajors)
+
+  sortedMajors.forEach((major, index) => {
+    const isFirst = index === 0
+    const label = isFirst ? `── v${major}.x (最新) ──` : `── v${major}.x ──`
+    versions.push({ value: '', label, disabled: true, separator: true })
+
+    const majorVersions = versionsByMajor.get(major) ?? []
+    const sorted = majorVersions.sort((a, b) => compareVersions(a.value, b.value))
+    versions.push(...sorted)
+  })
+
+  // 添加特殊版本
+  if (specialVersions.length > 0) {
+    versions.push({ value: '', label: '── 其他版本 ──', disabled: true, separator: true })
+    versions.push(...specialVersions)
+  }
+
+  return versions
+}
+
+/**
+ * 获取当前平台
+ */
+function getPlatform(): Platform {
+  return os.platform() as Platform
+}
+
+// ==================== Node.js 镜像配置 ====================
+
+/**
+ * 设置 Node.js 镜像(会持久化保存)
+ */
+export function setNodejsMirror(mirror: NodejsMirrorType): void {
+  saveNodejsMirrorConfig(mirror)
+  // 清除 Node.js 版本缓存,以便重新获取
+  clearCache('versions_nodejs')
+}
+
+/**
+ * 获取当前 Node.js 镜像配置
+ */
+export function getNodejsMirrorConfig(): { mirror: NodejsMirrorType } {
+  return { mirror: getNodejsMirrorFromConfig() }
+}
+
+/**
+ * 获取当前 Node.js 镜像类型(内部使用)
+ */
+function getCurrentNodejsMirror(): NodejsMirrorType {
+  return getNodejsMirrorFromConfig()
+}
+
+/**
+ * 获取 Node.js 下载 URL
+ */
+export function getNodejsDownloadUrl(version: string): string {
+  const arch = os.arch() === 'x64' ? 'x64' : os.arch() === 'arm64' ? 'arm64' : 'x86'
+  const mirror = NODEJS_MIRRORS[getCurrentNodejsMirror()]
+  return mirror.getDownloadUrl(version, arch)
+}
+
+// ==================== Node.js API 版本数据类型 ====================
+
+interface NodejsVersionInfo {
+  version: string
+  date: string
+  lts: boolean | string
+  security: boolean
+}
+
+// ==================== macOS brew 版本查询 ====================
+
+/**
+ * 使用 brew 查询软件信息
+ * brew 不支持安装历史版本,只返回当前可安装的版本
+ */
+async function getBrewVersion(formula: string): Promise<string | null> {
+  try {
+    const result = await execa('brew', ['info', '--json=v2', formula], {
+      timeout: 30000
+    })
+
+    const data = JSON.parse(result.stdout)
+    // 处理 formula 和 cask 两种情况
+    if (data.formulae && data.formulae.length > 0) {
+      return data.formulae[0].versions?.stable || null
+    }
+    if (data.casks && data.casks.length > 0) {
+      return data.casks[0].version || null
+    }
+    return null
+  } catch (error) {
+    console.error(`brew 查询 ${formula} 版本失败:`, error)
+    return null
+  }
+}
+
+/**
+ * 获取 brew 可用的 Node.js 版本
+ * brew 支持 node (最新), node@20, node@18 等
+ */
+async function getBrewNodeVersions(): Promise<Map<string, VersionItem[]>> {
+  const versionsByMajor = new Map<string, VersionItem[]>()
+
+  // 查询各个 formula 的版本
+  const formulas = [
+    { name: BREW_PACKAGES.nodejs.default, majorHint: null },
+    { name: BREW_PACKAGES.nodejs['20'], majorHint: '20' },
+    { name: BREW_PACKAGES.nodejs['18'], majorHint: '18' }
+  ]
+
+  const results = await Promise.allSettled(
+    formulas.map(async (f) => {
+      const version = await getBrewVersion(f.name)
+      return { formula: f.name, majorHint: f.majorHint, version }
+    })
+  )
+
+  for (const result of results) {
+    if (result.status === 'fulfilled' && result.value.version) {
+      const { formula, version } = result.value
+      const major = version.split('.')[0]
+      const majorNum = parseInt(major)
+
+      if (majorNum >= MIN_SUPPORTED_NODE_VERSION) {
+        const label = formula === 'node'
+          ? `Node.js ${version} (最新)`
+          : `Node.js ${version}`
+        addToVersionMap(versionsByMajor, version, label, { groupKey: major })
+      }
+    }
+  }
+
+  return versionsByMajor
+}
+
+// ==================== Linux apt 版本查询 ====================
+
+/**
+ * 使用 apt 查询软件可用版本
+ * apt 通常只有仓库中的一个版本
+ */
+async function getAptVersion(packageName: string): Promise<string | null> {
+  try {
+    const result = await execa('apt-cache', ['policy', packageName], {
+      timeout: 30000
+    })
+
+    // 解析输出,查找候选版本
+    const match = result.stdout.match(/Candidate:\s*(\S+)/)
+    if (match) {
+      // 提取版本号(去除 epoch 和 debian 修订号)
+      const fullVersion = match[1]
+      const versionMatch = fullVersion.match(/(\d+\.\d+\.\d+)/)
+      return versionMatch ? versionMatch[1] : fullVersion
+    }
+    return null
+  } catch (error) {
+    console.error(`apt 查询 ${packageName} 版本失败:`, error)
+    return null
+  }
+}
+
+// ==================== Node.js 版本获取 ====================
+
+/**
+ * 从 Node.js API 获取版本列表
+ */
+async function getNodeVersionsFromAPI(): Promise<NodejsVersionInfo[]> {
+  const mirror = NODEJS_MIRRORS[getCurrentNodejsMirror()]
+
+  try {
+    const response = await fetch(mirror.versionsUrl, {
+      headers: { 'User-Agent': 'ApqInstaller' },
+      signal: AbortSignal.timeout(15000)
+    })
+
+    if (!response.ok) {
+      throw new Error(`API 请求失败: ${response.status}`)
+    }
+
+    return await response.json() as NodejsVersionInfo[]
+  } catch (error) {
+    console.error(`从 ${mirror.name} 获取 Node.js 版本失败:`, error)
+    return []
+  }
+}
+
+async function getNodeVersionsWindows(): Promise<VersionResult> {
+  const versionsByMajor = new Map<string, VersionItem[]>()
+
+  const versions = await getNodeVersionsFromAPI()
+
+  if (versions.length === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  // 用于记录每个主版本的最新 LTS 版本
+  const ltsVersions = new Map<string, string>()
+
+  for (const info of versions) {
+    // 版本格式: v22.11.0 -> 22.11.0
+    const version = info.version.replace(/^v/, '')
+    const major = version.split('.')[0]
+    const majorNum = parseInt(major)
+
+    if (majorNum >= MIN_SUPPORTED_NODE_VERSION) {
+      const isLts = info.lts !== false
+      const ltsName = typeof info.lts === 'string' ? info.lts : null
+
+      // 记录每个主版本的第一个 LTS 版本(最新的)
+      if (isLts && !ltsVersions.has(major)) {
+        ltsVersions.set(major, version)
+      }
+
+      let label = `Node.js ${version}`
+      if (ltsName) {
+        label += ` (${ltsName})`
+      }
+
+      addToVersionMap(versionsByMajor, version, label, {
+        groupKey: major,
+        extra: { lts: isLts }
+      })
+    }
+  }
+
+  if (versionsByMajor.size === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const mirrorName = NODEJS_MIRRORS[getCurrentNodejsMirror()].name
+
+  return {
+    versions: buildVersionList(versionsByMajor),
+    warning: `下载源: ${mirrorName}`
+  }
+}
+
+async function getNodeVersionsMac(): Promise<VersionResult> {
+  const versionsByMajor = await getBrewNodeVersions()
+
+  if (versionsByMajor.size === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  return {
+    versions: buildVersionList(versionsByMajor),
+    warning: 'brew 仅支持安装当前可用版本,不支持选择历史版本'
+  }
+}
+
+async function getNodeVersionsLinux(): Promise<VersionResult> {
+  const version = await getAptVersion('nodejs')
+
+  if (!version) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const major = version.split('.')[0]
+  const versionsByMajor = new Map<string, VersionItem[]>()
+  addToVersionMap(versionsByMajor, version, `Node.js ${version}`, { groupKey: major })
+
+  return {
+    versions: buildVersionList(versionsByMajor),
+    warning: 'apt 仅支持安装仓库中的版本,如需其他版本请使用 nvm'
+  }
+}
+
+async function getNodeVersions(): Promise<VersionResult> {
+  const cacheKey = 'versions_nodejs'
+  const cached = getCache<VersionResult>(cacheKey)
+  if (cached) return cached
+
+  const platform = getPlatform()
+  let result: VersionResult
+
+  switch (platform) {
+    case 'win32':
+      result = await getNodeVersionsWindows()
+      break
+    case 'darwin':
+      result = await getNodeVersionsMac()
+      break
+    case 'linux':
+      result = await getNodeVersionsLinux()
+      break
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+
+  setCache(cacheKey, result, VERSION_CACHE_TTL)
+  return result
+}
+
+// ==================== VS Code 版本获取 ====================
+
+/**
+ * 从 VS Code API 获取版本列表
+ */
+async function getVSCodeVersionsFromAPI(): Promise<string[]> {
+  try {
+    const response = await fetch(VSCODE_API.versionsUrl, {
+      headers: { 'User-Agent': 'ApqInstaller' },
+      signal: AbortSignal.timeout(15000)
+    })
+
+    if (!response.ok) {
+      throw new Error(`API 请求失败: ${response.status}`)
+    }
+
+    return await response.json() as string[]
+  } catch (error) {
+    console.error('从 VS Code API 获取版本失败:', error)
+    return []
+  }
+}
+
+/**
+ * 获取 VS Code 下载 URL
+ */
+export function getVSCodeDownloadUrl(version: string): string {
+  const arch = os.arch() === 'x64' ? 'x64' : os.arch() === 'arm64' ? 'arm64' : 'x86'
+  return VSCODE_API.getDownloadUrl(version, arch, 'user')
+}
+
+async function getVSCodeVersionsWindows(): Promise<VersionResult> {
+  const versionsByMajor = new Map<string, VersionItem[]>()
+
+  const versions = await getVSCodeVersionsFromAPI()
+
+  if (versions.length === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  for (const version of versions) {
+    if (/^\d+\.\d+\.\d+$/.test(version)) {
+      addToVersionMap(versionsByMajor, version, `VS Code ${version}`)
+    }
+  }
+
+  if (versionsByMajor.size === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  return {
+    versions: buildVersionList(versionsByMajor, {
+      specialVersions: [{ value: 'insiders', label: 'Insiders (预览版)' }]
+    }),
+    warning: null
+  }
+}
+
+async function getVSCodeVersionsMac(): Promise<VersionResult> {
+  const version = await getBrewVersion(BREW_PACKAGES.vscode.stable)
+
+  if (!version) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const versionsByMajor = new Map<string, VersionItem[]>()
+  addToVersionMap(versionsByMajor, version, `VS Code ${version}`)
+
+  return {
+    versions: buildVersionList(versionsByMajor, {
+      specialVersions: [{ value: 'insiders', label: 'Insiders (预览版)' }]
+    }),
+    warning: 'brew 仅支持安装当前最新版本'
+  }
+}
+
+async function getVSCodeVersionsLinux(): Promise<VersionResult> {
+  // Linux 使用 snap 安装 VS Code,snap 不支持版本选择
+  // 返回一个占位版本
+  const versions: VersionItem[] = [
+    { value: 'stable', label: 'VS Code (最新稳定版)' },
+    { value: 'insiders', label: 'Insiders (预览版)' }
+  ]
+
+  return {
+    versions,
+    warning: 'snap 仅支持安装最新版本'
+  }
+}
+
+async function getVSCodeVersions(): Promise<VersionResult> {
+  const cacheKey = 'versions_vscode'
+  const cached = getCache<VersionResult>(cacheKey)
+  if (cached) return cached
+
+  const platform = getPlatform()
+  let result: VersionResult
+
+  switch (platform) {
+    case 'win32':
+      result = await getVSCodeVersionsWindows()
+      break
+    case 'darwin':
+      result = await getVSCodeVersionsMac()
+      break
+    case 'linux':
+      result = await getVSCodeVersionsLinux()
+      break
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+
+  setCache(cacheKey, result, VERSION_CACHE_TTL)
+  return result
+}
+
+// ==================== Git 版本获取 ====================
+
+/**
+ * 设置 Git 镜像(会持久化保存)
+ */
+export function setGitMirror(mirror: GitMirrorType): void {
+  saveGitMirrorConfig(mirror)
+  // 清除版本缓存,以便重新获取
+  clearCache()
+}
+
+/**
+ * 获取当前 Git 镜像配置
+ */
+export function getGitMirrorConfig(): { mirror: GitMirrorType } {
+  return { mirror: getGitMirrorFromConfig() }
+}
+
+/**
+ * 获取当前 Git 镜像类型(内部使用)
+ */
+function getCurrentGitMirror(): GitMirrorType {
+  return getGitMirrorFromConfig()
+}
+
+/**
+ * 获取 Git 下载 URL
+ */
+export function getGitDownloadUrl(version: string): string {
+  const arch = os.arch() === 'x64' ? '64' : '32'
+  const mirror = GIT_MIRRORS[getCurrentGitMirror()]
+  return mirror.getDownloadUrl(version, arch)
+}
+
+// 备用版本列表(当无法从任何源获取时使用)
+const FALLBACK_GIT_VERSIONS = [
+  '2.47.1', '2.47.0',
+  '2.46.2', '2.46.1', '2.46.0',
+  '2.45.2', '2.45.1', '2.45.0',
+  '2.44.0',
+  '2.43.0',
+  '2.42.0'
+]
+
+/**
+ * 从 GitHub API 获取版本列表
+ */
+async function getGitVersionsFromGitHub(): Promise<string[]> {
+  try {
+    const response = await fetch('https://api.github.com/repos/git-for-windows/git/releases', {
+      headers: {
+        'Accept': 'application/vnd.github.v3+json',
+        'User-Agent': 'ApqInstaller'
+      },
+      signal: AbortSignal.timeout(10000)
+    })
+
+    if (!response.ok) {
+      throw new Error(`GitHub API 请求失败: ${response.status}`)
+    }
+
+    const releases = await response.json() as Array<{ tag_name: string; prerelease: boolean }>
+    const versions: string[] = []
+
+    for (const release of releases) {
+      if (release.prerelease) continue
+      const match = release.tag_name.match(/^v?(\d+\.\d+\.\d+)/)
+      if (match && !versions.includes(match[1])) {
+        versions.push(match[1])
+      }
+    }
+
+    return versions
+  } catch (error) {
+    console.error('从 GitHub 获取 Git 版本失败:', error)
+    return []
+  }
+}
+
+/**
+ * 从华为云镜像获取版本列表
+ * 通过解析目录页面获取可用版本
+ */
+async function getGitVersionsFromHuaweicloud(): Promise<string[]> {
+  try {
+    const response = await fetch('https://mirrors.huaweicloud.com/git-for-windows/', {
+      headers: { 'User-Agent': 'ApqInstaller' },
+      signal: AbortSignal.timeout(10000)
+    })
+
+    if (!response.ok) {
+      throw new Error(`华为云镜像请求失败: ${response.status}`)
+    }
+
+    const html = await response.text()
+    const versions: string[] = []
+
+    // 解析 HTML 页面,查找版本目录链接
+    // 格式: <a href="v2.47.1.windows.1/">v2.47.1.windows.1/</a>
+    const regex = /href="v(\d+\.\d+\.\d+)\.windows\.\d+\/"/g
+    let match
+    while ((match = regex.exec(html)) !== null) {
+      const version = match[1]
+      if (!versions.includes(version)) {
+        versions.push(version)
+      }
+    }
+
+    // 按版本号降序排序
+    versions.sort((a, b) => {
+      const partsA = a.split('.').map(Number)
+      const partsB = b.split('.').map(Number)
+      for (let i = 0; i < 3; i++) {
+        if (partsA[i] !== partsB[i]) {
+          return partsB[i] - partsA[i]
+        }
+      }
+      return 0
+    })
+
+    return versions
+  } catch (error) {
+    console.error('从华为云镜像获取 Git 版本失败:', error)
+    return []
+  }
+}
+
+async function getGitVersionsWindows(): Promise<VersionResult> {
+  const versionsByMajor = new Map<string, VersionItem[]>()
+
+  // 根据当前镜像源获取版本列表
+  let versions: string[] = []
+  const currentMirror = getCurrentGitMirror()
+
+  if (currentMirror === 'github') {
+    versions = await getGitVersionsFromGitHub()
+  } else {
+    // 华为云镜像
+    versions = await getGitVersionsFromHuaweicloud()
+  }
+
+  // 如果获取失败,使用备用版本列表
+  if (versions.length === 0) {
+    console.log('使用备用 Git 版本列表')
+    versions = FALLBACK_GIT_VERSIONS
+  }
+
+  for (const version of versions) {
+    if (/^\d+\.\d+\.\d+/.test(version)) {
+      addToVersionMap(versionsByMajor, version, `Git ${version}`)
+    }
+  }
+
+  if (versionsByMajor.size === 0) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const mirrorName = GIT_MIRRORS[getCurrentGitMirror()].name
+
+  return {
+    versions: buildVersionList(versionsByMajor, {
+      specialVersions: [
+        { value: 'mingit', label: 'MinGit (精简版)' },
+        { value: 'lfs', label: 'Git LFS (大文件支持)' }
+      ]
+    }),
+    warning: `下载源: ${mirrorName}`
+  }
+}
+
+async function getGitVersionsMac(): Promise<VersionResult> {
+  const version = await getBrewVersion(BREW_PACKAGES.git.stable)
+
+  if (!version) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const versionsByMajor = new Map<string, VersionItem[]>()
+  addToVersionMap(versionsByMajor, version, `Git ${version}`)
+
+  return {
+    versions: buildVersionList(versionsByMajor, {
+      specialVersions: [{ value: 'lfs', label: 'Git LFS (大文件支持)' }]
+    }),
+    warning: 'brew 仅支持安装当前最新版本'
+  }
+}
+
+async function getGitVersionsLinux(): Promise<VersionResult> {
+  const version = await getAptVersion('git')
+
+  if (!version) {
+    throw new Error(ERROR_MESSAGES.VERSION_FETCH_ERROR)
+  }
+
+  const versionsByMajor = new Map<string, VersionItem[]>()
+  addToVersionMap(versionsByMajor, version, `Git ${version}`)
+
+  return {
+    versions: buildVersionList(versionsByMajor, {
+      specialVersions: [{ value: 'lfs', label: 'Git LFS (大文件支持)' }]
+    }),
+    warning: 'apt 仅支持安装仓库中的版本'
+  }
+}
+
+async function getGitVersions(): Promise<VersionResult> {
+  const cacheKey = 'versions_git'
+  const cached = getCache<VersionResult>(cacheKey)
+  if (cached) return cached
+
+  const platform = getPlatform()
+  let result: VersionResult
+
+  switch (platform) {
+    case 'win32':
+      result = await getGitVersionsWindows()
+      break
+    case 'darwin':
+      result = await getGitVersionsMac()
+      break
+    case 'linux':
+      result = await getGitVersionsLinux()
+      break
+    default:
+      throw new Error(`${ERROR_MESSAGES.UNSUPPORTED_PLATFORM}: ${platform}`)
+  }
+
+  setCache(cacheKey, result, VERSION_CACHE_TTL)
+  return result
+}
+
+// ==================== 统一导出 ====================
+
+export async function getVersions(software: SoftwareType): Promise<VersionResult> {
+  // Linux 下获取版本前先更新 apt 源(带缓存,1天内只更新一次)
+  const platform = getPlatform()
+  if (platform === 'linux') {
+    await updateAptSourceForQuery()
+  }
+  // Windows 使用 API 获取版本,不需要更新源
+  // macOS 的 brew 不需要手动更新源,brew info 会自动获取最新信息
+
+  switch (software) {
+    case 'nodejs':
+      return await getNodeVersions()
+    case 'vscode':
+      return await getVSCodeVersions()
+    case 'git':
+      return await getGitVersions()
+    default:
+      return { versions: [], warning: ERROR_MESSAGES.UNKNOWN_SOFTWARE }
+  }
+}
+
+export { clearCache }

+ 93 - 0
electron/modules/vscode-extension.ts

@@ -0,0 +1,93 @@
+// electron/modules/vscode-extension.ts - VS Code 插件安装模块
+
+import { BrowserWindow } from 'electron'
+import { execa } from 'execa'
+import { getVscodeCliPath } from './utils'
+import logger from './logger'
+
+/**
+ * 发送状态到渲染进程
+ */
+function sendToRenderer(channel: string, data: unknown): void {
+  const windows = BrowserWindow.getAllWindows()
+  windows.forEach((win) => {
+    win.webContents.send(channel, data)
+  })
+}
+
+/**
+ * 安装 VS Code 插件
+ * 供 IPC handler 和 installer.ts 复用
+ * @param extensionId 插件 ID
+ * @returns 安装结果
+ */
+export async function installVscodeExtension(extensionId: string): Promise<{ success: boolean; error?: string }> {
+  try {
+    const codePath = await getVscodeCliPath()
+    const fullCommand = `${codePath} --install-extension ${extensionId}`
+    logger.installInfo(`开始安装 VS Code 插件: ${extensionId},使用 CLI: ${codePath}`)
+    // 使用 install-status 事件发送日志,这样可以被 App.vue 中的监听器捕获
+    sendToRenderer('install-status', {
+      software: 'vscode',
+      message: `正在安装 VS Code 插件: ${extensionId}...`,
+      progress: 30,
+      i18nKey: 'log.vscodeExtInstalling',
+      i18nParams: { extensionId }
+    })
+    // 发送执行命令的日志
+    sendToRenderer('install-status', {
+      software: 'vscode',
+      message: `执行命令: ${fullCommand}`,
+      progress: 40,
+      i18nKey: 'log.executingCommand',
+      i18nParams: { command: fullCommand }
+    })
+    const result = await execa(codePath, ['--install-extension', extensionId], {
+      encoding: 'utf8',
+      stdout: 'pipe',
+      stderr: 'pipe'
+    })
+    // 记录命令输出
+    if (result.stdout) {
+      logger.installInfo(`VS Code 插件安装输出: ${result.stdout}`)
+      sendToRenderer('install-status', {
+        software: 'vscode',
+        message: `code 输出: ${result.stdout}`,
+        progress: 80,
+        i18nKey: 'log.vscodeExtOutput',
+        i18nParams: { output: result.stdout }
+      })
+    }
+    logger.installInfo(`VS Code 插件安装成功: ${extensionId}`)
+    sendToRenderer('install-status', {
+      software: 'vscode',
+      message: `✅ VS Code 插件安装成功: ${extensionId}`,
+      progress: 100,
+      i18nKey: 'log.vscodeExtInstallSuccess',
+      i18nParams: { extensionId }
+    })
+    return { success: true }
+  } catch (error) {
+    const execaError = error as { message?: string; stderr?: string; shortMessage?: string }
+    // 过滤乱码字符,只保留可读字符(ASCII 可打印字符和中文)
+    let errorMessage = execaError.shortMessage || execaError.message || '未知错误'
+    if (execaError.stderr) {
+      const stderrClean = execaError.stderr.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
+      if (stderrClean) {
+        errorMessage = stderrClean
+      }
+    }
+    // 从错误消息中也过滤乱码
+    errorMessage = errorMessage.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r:.\-_/\\]/g, '').trim()
+    logger.installError(`VS Code 插件安装失败: ${extensionId}`, error)
+    // 发送错误到渲染进程的安装日志
+    sendToRenderer('install-status', {
+      software: 'vscode',
+      message: `❌ VS Code 插件安装失败: ${errorMessage}`,
+      progress: 0,
+      i18nKey: 'log.vscodeExtInstallFailed',
+      i18nParams: { extensionId, error: errorMessage }
+    })
+    return { success: false, error: errorMessage }
+  }
+}

+ 170 - 0
electron/preload.ts

@@ -0,0 +1,170 @@
+// electron/preload.ts - Electron 预加载脚本
+
+import { contextBridge, ipcRenderer } from 'electron'
+import type { ElectronAPI } from './modules/types'
+
+// 向渲染进程暴露安全的 API
+const electronAPI: ElectronAPI = {
+  // ==================== 安装相关 ====================
+
+  // 安装指定软件
+  install: (software, options) => ipcRenderer.invoke('install', software, options),
+
+  // 取消安装
+  cancelInstall: () => ipcRenderer.invoke('cancel-install'),
+
+  // 检测软件是否已安装
+  checkInstalled: (software) => ipcRenderer.invoke('check-installed', software),
+
+  // 卸载软件
+  uninstall: (software) => ipcRenderer.invoke('uninstall', software),
+
+  // ==================== 系统检测 ====================
+
+  // 检测管理员权限
+  checkAdmin: () => ipcRenderer.invoke('check-admin'),
+
+  // 检测包管理器
+  checkPackageManager: () => ipcRenderer.invoke('check-package-manager'),
+
+  // 安装包管理器
+  installPackageManager: (manager) => ipcRenderer.invoke('install-package-manager', manager),
+
+  // 获取平台信息
+  getPlatform: () => ipcRenderer.invoke('get-platform'),
+
+  // 检测网络连接
+  checkNetwork: () => ipcRenderer.invoke('check-network'),
+
+  // ==================== 版本 ====================
+
+  // 获取软件版本列表
+  getVersions: (software) => ipcRenderer.invoke('get-versions', software),
+
+  // 检查更新
+  checkUpdate: (software) => ipcRenderer.invoke('check-update', software),
+
+  // ==================== Git 镜像配置 ====================
+
+  // 设置 Git 镜像
+  setGitMirror: (mirror) => ipcRenderer.invoke('set-git-mirror', mirror),
+
+  // 获取 Git 镜像配置
+  getGitMirrorConfig: () => ipcRenderer.invoke('get-git-mirror-config'),
+
+  // ==================== Node.js 镜像配置 ====================
+
+  // 设置 Node.js 镜像
+  setNodejsMirror: (mirror) => ipcRenderer.invoke('set-nodejs-mirror', mirror),
+
+  // 获取 Node.js 镜像配置
+  getNodejsMirrorConfig: () => ipcRenderer.invoke('get-nodejs-mirror-config'),
+
+  // ==================== 历史和日志 ====================
+
+  // 获取安装历史
+  getInstallHistory: (limit) => ipcRenderer.invoke('get-install-history', limit),
+
+  // 获取日志
+  getLogs: () => ipcRenderer.invoke('get-logs'),
+
+  // 写入安装日志
+  writeInstallLog: (message: string, level?: 'info' | 'warn' | 'error') => ipcRenderer.invoke('write-install-log', message, level),
+
+  // 获取日志文件路径
+  getLogPaths: () => ipcRenderer.invoke('get-log-paths'),
+
+  // ==================== 窗口操作 ====================
+
+  // 设置窗口标题
+  setWindowTitle: (title) => ipcRenderer.invoke('set-window-title', title),
+
+  // ==================== Claude Code ====================
+
+  // 检测 Claude Code 是否已安装
+  checkClaudeCode: () => ipcRenderer.invoke('check-claude-code'),
+
+  // 启动 Claude Code (打开 Git Bash 并执行 claude 命令)
+  launchClaudeCode: () => ipcRenderer.invoke('launch-claude-code'),
+
+  // 安装 Claude Code
+  installClaudeCode: () => ipcRenderer.invoke('install-claude-code'),
+
+  // ==================== VS Code Extensions ====================
+
+  // 检查 VS Code 插件是否已安装
+  checkVscodeExtension: (extensionId: string) => ipcRenderer.invoke('check-vscode-extension', extensionId),
+
+  // 安装 VS Code 插件
+  installVscodeExtension: (extensionId: string) => ipcRenderer.invoke('install-vscode-extension', extensionId),
+
+  // 选择文件夹
+  selectDirectory: (defaultPath?: string) => ipcRenderer.invoke('select-directory', defaultPath),
+
+  // 窗口最小化
+  windowMinimize: () => ipcRenderer.invoke('window-minimize'),
+
+  // 窗口最大化/还原
+  windowMaximize: () => ipcRenderer.invoke('window-maximize'),
+
+  // 关闭窗口
+  windowClose: () => ipcRenderer.invoke('window-close'),
+
+  // 获取窗口最大化状态
+  windowIsMaximized: () => ipcRenderer.invoke('window-is-maximized'),
+
+  // ==================== 事件监听 ====================
+
+  // 监听安装状态
+  onInstallStatus: (callback) => {
+    ipcRenderer.on('install-status', (_event, data) => callback(data))
+  },
+
+  // 监听安装完成
+  onInstallComplete: (callback) => {
+    ipcRenderer.on('install-complete', (_event, data) => callback(data))
+  },
+
+  // 监听安装错误
+  onInstallError: (callback) => {
+    ipcRenderer.on('install-error', (_event, data) => callback(data))
+  },
+
+  // 监听网络状态变化
+  onNetworkChange: (callback) => {
+    ipcRenderer.on('network-change', (_event, online) => callback(online))
+  },
+
+  // 移除所有监听(避免内存泄漏)
+  removeAllListeners: () => {
+    ipcRenderer.removeAllListeners('install-status')
+    ipcRenderer.removeAllListeners('install-complete')
+    ipcRenderer.removeAllListeners('install-error')
+    ipcRenderer.removeAllListeners('network-change')
+    ipcRenderer.removeAllListeners('updater:status')
+  },
+
+  // ==================== 自动更新 ====================
+
+  // 检查应用更新
+  updaterCheck: () => ipcRenderer.invoke('updater:check'),
+
+  // 下载更新
+  updaterDownload: () => ipcRenderer.invoke('updater:download'),
+
+  // 安装更新并重启
+  updaterInstall: () => ipcRenderer.invoke('updater:install'),
+
+  // 获取当前版本
+  updaterVersion: () => ipcRenderer.invoke('updater:version'),
+
+  // 检测是否为 Portable 模式
+  updaterIsPortable: () => ipcRenderer.invoke('updater:is-portable'),
+
+  // 监听更新状态
+  onUpdaterStatus: (callback) => {
+    ipcRenderer.on('updater:status', (_event, data) => callback(data))
+  }
+}
+
+contextBridge.exposeInMainWorld('electronAPI', electronAPI)

+ 25 - 0
eslint.config.js

@@ -0,0 +1,25 @@
+import js from '@eslint/js'
+import pluginVue from 'eslint-plugin-vue'
+import vueTsEslintConfig from '@vue/eslint-config-typescript'
+import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
+
+export default [
+  {
+    name: 'app/files-to-lint',
+    files: ['**/*.{ts,mts,tsx,vue}']
+  },
+  {
+    name: 'app/files-to-ignore',
+    ignores: ['**/dist/**', '**/dist-electron/**', '**/node_modules/**', '**/release/**']
+  },
+  js.configs.recommended,
+  ...pluginVue.configs['flat/essential'],
+  ...vueTsEslintConfig(),
+  skipFormatting,
+  {
+    rules: {
+      'vue/multi-word-component-names': 'off',
+      '@typescript-eslint/no-unused-vars': 'warn'
+    }
+  }
+]

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://nodejs.org https://api.github.com https://npmmirror.com https://registry.npmmirror.com;">
+  <title>Claude AI安装器</title>
+</head>
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.ts"></script>
+</body>
+</html>

+ 164 - 0
launcher/launcher.js

@@ -0,0 +1,164 @@
+#!/usr/bin/env node
+/**
+ * Claude AI Installer - Portable 启动器
+ *
+ * 功能:
+ * 1. 检查 update 目录是否有新版本
+ * 2. 如果有,替换 app 目录中的旧版本
+ * 3. 启动主程序
+ */
+
+const fs = require('fs')
+const path = require('path')
+const { spawn } = require('child_process')
+
+// 获取启动器所在目录
+const launcherDir = path.dirname(process.execPath)
+const appDir = path.join(launcherDir, 'app')
+const updateDir = path.join(launcherDir, 'update')
+const logFile = path.join(launcherDir, 'launcher.log')
+
+/**
+ * 写入日志
+ */
+function log(message) {
+  const timestamp = new Date().toISOString()
+  const logMessage = `[${timestamp}] ${message}\n`
+  console.log(message)
+  try {
+    fs.appendFileSync(logFile, logMessage)
+  } catch (e) {
+    // 忽略日志写入错误
+  }
+}
+
+/**
+ * 查找目录中的 exe 文件
+ */
+function findExeFile(dir) {
+  try {
+    const files = fs.readdirSync(dir)
+    const exeFile = files.find(f => f.endsWith('.exe') && f.includes('Claude-AI-Installer'))
+    return exeFile ? path.join(dir, exeFile) : null
+  } catch (e) {
+    return null
+  }
+}
+
+/**
+ * 删除目录及其内容
+ */
+function removeDir(dir) {
+  if (fs.existsSync(dir)) {
+    fs.rmSync(dir, { recursive: true, force: true })
+  }
+}
+
+/**
+ * 复制文件
+ */
+function copyFile(src, dest) {
+  fs.copyFileSync(src, dest)
+}
+
+/**
+ * 应用更新
+ */
+function applyUpdate() {
+  const updateExe = findExeFile(updateDir)
+
+  if (!updateExe) {
+    log('没有找到更新文件')
+    return false
+  }
+
+  log(`发现更新文件: ${updateExe}`)
+
+  try {
+    // 确保 app 目录存在
+    if (!fs.existsSync(appDir)) {
+      fs.mkdirSync(appDir, { recursive: true })
+    }
+
+    // 删除旧版本
+    const oldExe = findExeFile(appDir)
+    if (oldExe) {
+      log(`删除旧版本: ${oldExe}`)
+      fs.unlinkSync(oldExe)
+    }
+
+    // 移动新版本到 app 目录
+    const newExeName = path.basename(updateExe)
+    const destPath = path.join(appDir, newExeName)
+    log(`安装新版本: ${destPath}`)
+
+    // 复制文件(而不是移动,以便保留更新目录结构)
+    copyFile(updateExe, destPath)
+
+    // 清理更新目录
+    removeDir(updateDir)
+    log('更新完成')
+
+    return true
+  } catch (error) {
+    log(`更新失败: ${error.message}`)
+    return false
+  }
+}
+
+/**
+ * 启动主程序
+ */
+function launchApp() {
+  const appExe = findExeFile(appDir)
+
+  if (!appExe) {
+    log('错误: 找不到主程序')
+
+    // 尝试在启动器目录查找
+    const fallbackExe = findExeFile(launcherDir)
+    if (fallbackExe && !fallbackExe.includes('launcher')) {
+      log(`使用备用程序: ${fallbackExe}`)
+      spawn(fallbackExe, [], { detached: true, stdio: 'ignore' }).unref()
+      return
+    }
+
+    process.exit(1)
+  }
+
+  log(`启动主程序: ${appExe}`)
+
+  // 启动主程序并退出启动器
+  const child = spawn(appExe, [], {
+    detached: true,
+    stdio: 'ignore',
+    cwd: appDir
+  })
+
+  child.unref()
+}
+
+/**
+ * 主函数
+ */
+function main() {
+  log('========== 启动器开始 ==========')
+  log(`启动器目录: ${launcherDir}`)
+  log(`应用目录: ${appDir}`)
+  log(`更新目录: ${updateDir}`)
+
+  // 检查并应用更新
+  if (fs.existsSync(updateDir)) {
+    log('检测到更新目录,尝试应用更新...')
+    applyUpdate()
+  }
+
+  // 启动主程序
+  launchApp()
+
+  log('启动器退出')
+  process.exit(0)
+}
+
+// 运行
+main()

+ 10574 - 0
package-lock.json

@@ -0,0 +1,10574 @@
+{
+  "name": "claude-ai-installer",
+  "version": "0.0.5",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "claude-ai-installer",
+      "version": "0.0.5",
+      "dependencies": {
+        "@element-plus/icons-vue": "^2.3.1",
+        "axios": "^1.13.2",
+        "electron-log": "^5.4.3",
+        "electron-updater": "^6.6.2",
+        "element-plus": "^2.9.0",
+        "execa": "^9.6.1",
+        "pinia": "^3.0.4",
+        "sudo-prompt": "^9.2.1",
+        "vue": "^3.5.0",
+        "vue-i18n": "^11.2.2"
+      },
+      "devDependencies": {
+        "@eslint/js": "^9.39.1",
+        "@intlify/unplugin-vue-i18n": "^6.0.0",
+        "@types/node": "^24.10.1",
+        "@typescript-eslint/eslint-plugin": "^8.48.1",
+        "@typescript-eslint/parser": "^8.48.1",
+        "@vitejs/plugin-vue": "^6.0.2",
+        "@vitest/coverage-v8": "^3.2.4",
+        "@vue/eslint-config-prettier": "^10.2.0",
+        "@vue/eslint-config-typescript": "^14.6.0",
+        "electron": "^39.2.5",
+        "electron-builder": "^26.0.12",
+        "eslint": "^9.39.1",
+        "eslint-plugin-vue": "^10.6.2",
+        "sass": "^1.89.0",
+        "typescript": "^5.8.0",
+        "unplugin-auto-import": "^20.3.0",
+        "unplugin-vue-components": "^30.0.0",
+        "vite": "^7.2.6",
+        "vite-plugin-electron": "^0.29.0",
+        "vite-plugin-electron-renderer": "^0.14.6",
+        "vitest": "^3.2.4",
+        "vue-tsc": "^3.1.5"
+      }
+    },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.5"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@bcoe/v8-coverage": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
+      "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
+      "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@develar/schema-utils": {
+      "version": "2.6.5",
+      "resolved": "https://registry.npmmirror.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
+      "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.0",
+        "ajv-keywords": "^3.4.1"
+      },
+      "engines": {
+        "node": ">= 8.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/@electron/asar": {
+      "version": "3.2.18",
+      "resolved": "https://registry.npmmirror.com/@electron/asar/-/asar-3.2.18.tgz",
+      "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "commander": "^5.0.0",
+        "glob": "^7.1.6",
+        "minimatch": "^3.0.4"
+      },
+      "bin": {
+        "asar": "bin/asar.js"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/@electron/asar/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/@electron/asar/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/@electron/fuses": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/@electron/fuses/-/fuses-1.8.0.tgz",
+      "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.1",
+        "fs-extra": "^9.0.1",
+        "minimist": "^1.2.5"
+      },
+      "bin": {
+        "electron-fuses": "dist/bin.js"
+      }
+    },
+    "node_modules/@electron/fuses/node_modules/fs-extra": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@electron/fuses/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/fuses/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/get": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/@electron/get/-/get-2.0.3.tgz",
+      "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "env-paths": "^2.2.0",
+        "fs-extra": "^8.1.0",
+        "got": "^11.8.5",
+        "progress": "^2.0.3",
+        "semver": "^6.2.0",
+        "sumchecker": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "global-agent": "^3.0.0"
+      }
+    },
+    "node_modules/@electron/get/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@electron/node-gyp": {
+      "version": "10.2.0-electron.1",
+      "resolved": "git+ssh://[email protected]/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
+      "integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "env-paths": "^2.2.0",
+        "exponential-backoff": "^3.1.1",
+        "glob": "^8.1.0",
+        "graceful-fs": "^4.2.6",
+        "make-fetch-happen": "^10.2.1",
+        "nopt": "^6.0.0",
+        "proc-log": "^2.0.1",
+        "semver": "^7.3.5",
+        "tar": "^6.2.1",
+        "which": "^2.0.2"
+      },
+      "bin": {
+        "node-gyp": "bin/node-gyp.js"
+      },
+      "engines": {
+        "node": ">=12.13.0"
+      }
+    },
+    "node_modules/@electron/node-gyp/node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@electron/node-gyp/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@electron/notarize": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.5.0.tgz",
+      "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "fs-extra": "^9.0.1",
+        "promise-retry": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/notarize/node_modules/fs-extra": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@electron/notarize/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/notarize/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/osx-sign": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.3.1.tgz",
+      "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "compare-version": "^0.1.2",
+        "debug": "^4.3.4",
+        "fs-extra": "^10.0.0",
+        "isbinaryfile": "^4.0.8",
+        "minimist": "^1.2.6",
+        "plist": "^3.0.5"
+      },
+      "bin": {
+        "electron-osx-flat": "bin/electron-osx-flat.js",
+        "electron-osx-sign": "bin/electron-osx-sign.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@electron/osx-sign/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/gjtorikian/"
+      }
+    },
+    "node_modules/@electron/osx-sign/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/osx-sign/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/rebuild": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-3.7.0.tgz",
+      "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
+        "@malept/cross-spawn-promise": "^2.0.0",
+        "chalk": "^4.0.0",
+        "debug": "^4.1.1",
+        "detect-libc": "^2.0.1",
+        "fs-extra": "^10.0.0",
+        "got": "^11.7.0",
+        "node-abi": "^3.45.0",
+        "node-api-version": "^0.2.0",
+        "ora": "^5.1.0",
+        "read-binary-file-arch": "^1.0.6",
+        "semver": "^7.3.5",
+        "tar": "^6.0.5",
+        "yargs": "^17.0.1"
+      },
+      "bin": {
+        "electron-rebuild": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=12.13.0"
+      }
+    },
+    "node_modules/@electron/rebuild/node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@electron/rebuild/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@electron/rebuild/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/rebuild/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/universal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.1.tgz",
+      "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@electron/asar": "^3.2.7",
+        "@malept/cross-spawn-promise": "^2.0.0",
+        "debug": "^4.3.1",
+        "dir-compare": "^4.2.0",
+        "fs-extra": "^11.1.1",
+        "minimatch": "^9.0.3",
+        "plist": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=16.4"
+      }
+    },
+    "node_modules/@electron/universal/node_modules/fs-extra": {
+      "version": "11.3.2",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.2.tgz",
+      "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/@electron/universal/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/universal/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@electron/windows-sign": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/@electron/windows-sign/-/windows-sign-1.2.2.tgz",
+      "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "optional": true,
+      "dependencies": {
+        "cross-dirname": "^0.1.0",
+        "debug": "^4.3.4",
+        "fs-extra": "^11.1.1",
+        "minimist": "^1.2.8",
+        "postject": "^1.0.0-alpha.6"
+      },
+      "bin": {
+        "electron-windows-sign": "bin/electron-windows-sign.js"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/@electron/windows-sign/node_modules/fs-extra": {
+      "version": "11.3.2",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.2.tgz",
+      "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/@electron/windows-sign/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@electron/windows-sign/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@element-plus/icons-vue": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
+      "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+      "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+      "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+      "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+      "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+      "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+      "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+      "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+      "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+      "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+      "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+      "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+      "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+      "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+      "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+      "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+      "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+      "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+      "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+      "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+      "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+      "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.21.1",
+      "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz",
+      "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.7",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/@eslint/config-array/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/@eslint/config-helpers": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+      "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.1",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.1",
+      "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.1.tgz",
+      "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz",
+      "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.10"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.4.tgz",
+      "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/core": "^1.7.3",
+        "@floating-ui/utils": "^0.2.10"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.10",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz",
+      "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+      "license": "MIT"
+    },
+    "node_modules/@gar/promisify": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz",
+      "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.7",
+      "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz",
+      "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@intlify/bundle-utils": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-10.0.1.tgz",
+      "integrity": "sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "^11.1.2",
+        "@intlify/shared": "^11.1.2",
+        "acorn": "^8.8.2",
+        "escodegen": "^2.1.0",
+        "estree-walker": "^2.0.2",
+        "jsonc-eslint-parser": "^2.3.0",
+        "mlly": "^1.2.0",
+        "source-map-js": "^1.0.1",
+        "yaml-eslint-parser": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.2.2.tgz",
+      "integrity": "sha512-0mCTBOLKIqFUP3BzwuFW23hYEl9g/wby6uY//AC5hTgQfTsM2srCYF2/hYGp+a5DZ/HIFIgKkLJMzXTt30r0JQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "11.2.2",
+        "@intlify/shared": "11.2.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.2.2.tgz",
+      "integrity": "sha512-XS2p8Ff5JxWsKhgfld4/MRQzZRQ85drMMPhb7Co6Be4ZOgqJX1DzcZt0IFgGTycgqL8rkYNwgnD443Q+TapOoA==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "11.2.2",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-11.2.2.tgz",
+      "integrity": "sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/unplugin-vue-i18n": {
+      "version": "6.0.8",
+      "resolved": "https://registry.npmmirror.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-6.0.8.tgz",
+      "integrity": "sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@intlify/bundle-utils": "^10.0.1",
+        "@intlify/shared": "^11.1.2",
+        "@intlify/vue-i18n-extensions": "^8.0.0",
+        "@rollup/pluginutils": "^5.1.0",
+        "@typescript-eslint/scope-manager": "^8.13.0",
+        "@typescript-eslint/typescript-estree": "^8.13.0",
+        "debug": "^4.3.3",
+        "fast-glob": "^3.2.12",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.3",
+        "pathe": "^1.0.0",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2",
+        "unplugin": "^1.1.0",
+        "vue": "^3.4"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "petite-vue-i18n": "*",
+        "vue": "^3.2.25",
+        "vue-i18n": "*"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz",
+      "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.24.6",
+        "@intlify/shared": "^10.0.0",
+        "@vue/compiler-dom": "^3.2.45",
+        "vue-i18n": "^10.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0",
+        "@vue/compiler-dom": "^3.0.0",
+        "vue": "^3.0.0",
+        "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@intlify/shared": {
+          "optional": true
+        },
+        "@vue/compiler-dom": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/core-base": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-10.0.8.tgz",
+      "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "10.0.8",
+        "@intlify/shared": "10.0.8"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/message-compiler": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
+      "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "10.0.8",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-10.0.8.tgz",
+      "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-10.0.8.tgz",
+      "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "10.0.8",
+        "@intlify/shared": "10.0.8",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/@isaacs/balanced-match": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+      "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@isaacs/brace-expansion": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+      "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@isaacs/balanced-match": "^4.0.1"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@isaacs/cliui/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@malept/cross-spawn-promise": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
+      "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/malept"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
+        }
+      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "cross-spawn": "^7.0.1"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      }
+    },
+    "node_modules/@malept/flatpak-bundler": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
+      "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "fs-extra": "^9.0.0",
+        "lodash": "^4.17.15",
+        "tmp-promise": "^3.0.2"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/@malept/flatpak-bundler/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@npmcli/fs": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-2.1.2.tgz",
+      "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@gar/promisify": "^1.1.3",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@npmcli/move-file": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@npmcli/move-file/-/move-file-2.0.1.tgz",
+      "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+      "deprecated": "This functionality has been moved to @npmcli/fs",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mkdirp": "^1.0.4",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
+      "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3",
+        "is-glob": "^4.0.3",
+        "micromatch": "^4.0.5",
+        "node-addon-api": "^7.0.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.5.1",
+        "@parcel/watcher-darwin-arm64": "2.5.1",
+        "@parcel/watcher-darwin-x64": "2.5.1",
+        "@parcel/watcher-freebsd-x64": "2.5.1",
+        "@parcel/watcher-linux-arm-glibc": "2.5.1",
+        "@parcel/watcher-linux-arm-musl": "2.5.1",
+        "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+        "@parcel/watcher-linux-arm64-musl": "2.5.1",
+        "@parcel/watcher-linux-x64-glibc": "2.5.1",
+        "@parcel/watcher-linux-x64-musl": "2.5.1",
+        "@parcel/watcher-win32-arm64": "2.5.1",
+        "@parcel/watcher-win32-ia32": "2.5.1",
+        "@parcel/watcher-win32-x64": "2.5.1"
+      }
+    },
+    "node_modules/@parcel/watcher-android-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+      "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+      "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+      "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-freebsd-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+      "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+      "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+      "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+      "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+      "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+      "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+      "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+      "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-ia32": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+      "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+      "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher/node_modules/node-addon-api": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
+      "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@pkgr/core": {
+      "version": "0.2.9",
+      "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz",
+      "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/pkgr"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "name": "@sxzz/popperjs-es",
+      "version": "2.11.7",
+      "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+      "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.50",
+      "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz",
+      "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@rollup/pluginutils": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+      "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "estree-walker": "^2.0.2",
+        "picomatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+      "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+      "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+      "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+      "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+      "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+      "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+      "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+      "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+      "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+      "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+      "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+      "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+      "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+      "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+      "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+      "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+      "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+      "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+      "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+      "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@sec-ant/readable-stream": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+      "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+      "license": "MIT"
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@sindresorhus/merge-streams": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+      "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@szmarczak/http-timer": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defer-to-connect": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@tootallnate/once": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz",
+      "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@types/cacheable-request": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+      "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-cache-semantics": "*",
+        "@types/keyv": "^3.1.4",
+        "@types/node": "*",
+        "@types/responselike": "^1.0.0"
+      }
+    },
+    "node_modules/@types/chai": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz",
+      "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/deep-eql": "*",
+        "assertion-error": "^2.0.1"
+      }
+    },
+    "node_modules/@types/debug": {
+      "version": "4.1.12",
+      "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz",
+      "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*"
+      }
+    },
+    "node_modules/@types/deep-eql": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+      "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/fs-extra": {
+      "version": "9.0.13",
+      "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz",
+      "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/http-cache-semantics": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/keyv": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz",
+      "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/lodash-es": {
+      "version": "4.17.12",
+      "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+      "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz",
+      "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "24.10.1",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.10.1.tgz",
+      "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/@types/plist": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz",
+      "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@types/node": "*",
+        "xmlbuilder": ">=11.0.1"
+      }
+    },
+    "node_modules/@types/responselike": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz",
+      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/verror": {
+      "version": "1.10.11",
+      "resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.11.tgz",
+      "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.16",
+      "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+      "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/yauzl": {
+      "version": "2.10.3",
+      "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz",
+      "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz",
+      "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "8.48.1",
+        "@typescript-eslint/type-utils": "8.48.1",
+        "@typescript-eslint/utils": "8.48.1",
+        "@typescript-eslint/visitor-keys": "8.48.1",
+        "graphemer": "^1.4.0",
+        "ignore": "^7.0.0",
+        "natural-compare": "^1.4.0",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^8.48.1",
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.48.1.tgz",
+      "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "8.48.1",
+        "@typescript-eslint/types": "8.48.1",
+        "@typescript-eslint/typescript-estree": "8.48.1",
+        "@typescript-eslint/visitor-keys": "8.48.1",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.48.1.tgz",
+      "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.48.1",
+        "@typescript-eslint/types": "^8.48.1",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz",
+      "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.48.1",
+        "@typescript-eslint/visitor-keys": "8.48.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/tsconfig-utils": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz",
+      "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz",
+      "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.48.1",
+        "@typescript-eslint/typescript-estree": "8.48.1",
+        "@typescript-eslint/utils": "8.48.1",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.48.1.tgz",
+      "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz",
+      "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/project-service": "8.48.1",
+        "@typescript-eslint/tsconfig-utils": "8.48.1",
+        "@typescript-eslint/types": "8.48.1",
+        "@typescript-eslint/visitor-keys": "8.48.1",
+        "debug": "^4.3.4",
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.48.1.tgz",
+      "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.7.0",
+        "@typescript-eslint/scope-manager": "8.48.1",
+        "@typescript-eslint/types": "8.48.1",
+        "@typescript-eslint/typescript-estree": "8.48.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz",
+      "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.48.1",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz",
+      "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@rolldown/pluginutils": "1.0.0-beta.50"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vitest/coverage-v8": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz",
+      "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.3.0",
+        "@bcoe/v8-coverage": "^1.0.2",
+        "ast-v8-to-istanbul": "^0.3.3",
+        "debug": "^4.4.1",
+        "istanbul-lib-coverage": "^3.2.2",
+        "istanbul-lib-report": "^3.0.1",
+        "istanbul-lib-source-maps": "^5.0.6",
+        "istanbul-reports": "^3.1.7",
+        "magic-string": "^0.30.17",
+        "magicast": "^0.3.5",
+        "std-env": "^3.9.0",
+        "test-exclude": "^7.0.1",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@vitest/browser": "3.2.4",
+        "vitest": "3.2.4"
+      },
+      "peerDependenciesMeta": {
+        "@vitest/browser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-3.2.4.tgz",
+      "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/chai": "^5.2.2",
+        "@vitest/spy": "3.2.4",
+        "@vitest/utils": "3.2.4",
+        "chai": "^5.2.0",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-3.2.4.tgz",
+      "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "3.2.4",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.17"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/mocker/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
+      "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-3.2.4.tgz",
+      "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "3.2.4",
+        "pathe": "^2.0.3",
+        "strip-literal": "^3.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-3.2.4.tgz",
+      "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "3.2.4",
+        "magic-string": "^0.30.17",
+        "pathe": "^2.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@vitest/spy": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-3.2.4.tgz",
+      "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^4.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-3.2.4.tgz",
+      "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "3.2.4",
+        "loupe": "^3.1.4",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@volar/language-core": {
+      "version": "2.4.23",
+      "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.23.tgz",
+      "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@volar/source-map": "2.4.23"
+      }
+    },
+    "node_modules/@volar/source-map": {
+      "version": "2.4.23",
+      "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.23.tgz",
+      "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@volar/typescript": {
+      "version": "2.4.23",
+      "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.23.tgz",
+      "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@volar/language-core": "2.4.23",
+        "path-browserify": "^1.0.1",
+        "vscode-uri": "^3.0.8"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
+      "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.25",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
+      "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
+      "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.25",
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
+      "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/@vue/devtools-kit": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+      "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-shared": "^7.7.9",
+        "birpc": "^2.3.0",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.2"
+      }
+    },
+    "node_modules/@vue/devtools-shared": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+      "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+      "license": "MIT",
+      "dependencies": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "node_modules/@vue/eslint-config-prettier": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz",
+      "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-config-prettier": "^10.0.1",
+        "eslint-plugin-prettier": "^5.2.2"
+      },
+      "peerDependencies": {
+        "eslint": ">= 8.21.0",
+        "prettier": ">= 3.0.0"
+      }
+    },
+    "node_modules/@vue/eslint-config-typescript": {
+      "version": "14.6.0",
+      "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-14.6.0.tgz",
+      "integrity": "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/utils": "^8.35.1",
+        "fast-glob": "^3.3.3",
+        "typescript-eslint": "^8.35.1",
+        "vue-eslint-parser": "^10.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "peerDependencies": {
+        "eslint": "^9.10.0",
+        "eslint-plugin-vue": "^9.28.0 || ^10.0.0",
+        "typescript": ">=4.8.4"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/language-core": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.1.5.tgz",
+      "integrity": "sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@volar/language-core": "2.4.23",
+        "@vue/compiler-dom": "^3.5.0",
+        "@vue/shared": "^3.5.0",
+        "alien-signals": "^3.0.0",
+        "muggle-string": "^0.4.1",
+        "path-browserify": "^1.0.1",
+        "picomatch": "^4.0.2"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/language-core/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz",
+      "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz",
+      "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz",
+      "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/runtime-core": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz",
+      "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "vue": "3.5.25"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz",
+      "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
+      "license": "MIT"
+    },
+    "node_modules/@vueuse/core": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
+      "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.13.0",
+        "@vueuse/shared": "9.13.0",
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
+      "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
+      "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+      "license": "MIT",
+      "dependencies": {
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@xmldom/xmldom": {
+      "version": "0.8.11",
+      "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
+      "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/7zip-bin": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/7zip-bin/-/7zip-bin-5.2.0.tgz",
+      "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/acorn": {
+      "version": "8.15.0",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
+      "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/agentkeepalive": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+      "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "humanize-ms": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
+    "node_modules/aggregate-error": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz",
+      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/alien-signals": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.1.1.tgz",
+      "integrity": "sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/app-builder-bin": {
+      "version": "5.0.0-alpha.12",
+      "resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz",
+      "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/app-builder-lib": {
+      "version": "26.0.12",
+      "resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-26.0.12.tgz",
+      "integrity": "sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@develar/schema-utils": "~2.6.5",
+        "@electron/asar": "3.2.18",
+        "@electron/fuses": "^1.8.0",
+        "@electron/notarize": "2.5.0",
+        "@electron/osx-sign": "1.3.1",
+        "@electron/rebuild": "3.7.0",
+        "@electron/universal": "2.0.1",
+        "@malept/flatpak-bundler": "^0.4.0",
+        "@types/fs-extra": "9.0.13",
+        "async-exit-hook": "^2.0.1",
+        "builder-util": "26.0.11",
+        "builder-util-runtime": "9.3.1",
+        "chromium-pickle-js": "^0.2.0",
+        "config-file-ts": "0.2.8-rc1",
+        "debug": "^4.3.4",
+        "dotenv": "^16.4.5",
+        "dotenv-expand": "^11.0.6",
+        "ejs": "^3.1.8",
+        "electron-publish": "26.0.11",
+        "fs-extra": "^10.1.0",
+        "hosted-git-info": "^4.1.0",
+        "is-ci": "^3.0.0",
+        "isbinaryfile": "^5.0.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.3",
+        "lazy-val": "^1.0.5",
+        "minimatch": "^10.0.0",
+        "plist": "3.1.0",
+        "resedit": "^1.7.0",
+        "semver": "^7.3.8",
+        "tar": "^6.1.12",
+        "temp-file": "^3.4.0",
+        "tiny-async-pool": "1.3.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "dmg-builder": "26.0.12",
+        "electron-builder-squirrel-windows": "26.0.12"
+      }
+    },
+    "node_modules/app-builder-lib/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/app-builder-lib/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/app-builder-lib/node_modules/minimatch": {
+      "version": "10.1.1",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.1.1.tgz",
+      "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/brace-expansion": "^5.0.0"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/app-builder-lib/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "license": "Python-2.0"
+    },
+    "node_modules/assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/ast-v8-to-istanbul": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmmirror.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz",
+      "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.31",
+        "estree-walker": "^3.0.3",
+        "js-tokens": "^9.0.1"
+      }
+    },
+    "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/async-exit-hook": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
+      "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/birpc": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz",
+      "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/boolean": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz",
+      "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-crc32": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/builder-util": {
+      "version": "26.0.11",
+      "resolved": "https://registry.npmmirror.com/builder-util/-/builder-util-26.0.11.tgz",
+      "integrity": "sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/debug": "^4.1.6",
+        "7zip-bin": "~5.2.0",
+        "app-builder-bin": "5.0.0-alpha.12",
+        "builder-util-runtime": "9.3.1",
+        "chalk": "^4.1.2",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.4",
+        "fs-extra": "^10.1.0",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.0",
+        "is-ci": "^3.0.0",
+        "js-yaml": "^4.1.0",
+        "sanitize-filename": "^1.6.3",
+        "source-map-support": "^0.5.19",
+        "stat-mode": "^1.0.0",
+        "temp-file": "^3.4.0",
+        "tiny-async-pool": "1.3.0"
+      }
+    },
+    "node_modules/builder-util-runtime": {
+      "version": "9.3.1",
+      "resolved": "https://registry.npmmirror.com/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz",
+      "integrity": "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.4",
+        "sax": "^1.2.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/builder-util/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/builder-util/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/builder-util/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cacache": {
+      "version": "16.1.3",
+      "resolved": "https://registry.npmmirror.com/cacache/-/cacache-16.1.3.tgz",
+      "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/fs": "^2.1.0",
+        "@npmcli/move-file": "^2.0.0",
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.1.0",
+        "glob": "^8.0.1",
+        "infer-owner": "^1.0.4",
+        "lru-cache": "^7.7.1",
+        "minipass": "^3.1.6",
+        "minipass-collect": "^1.0.2",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "mkdirp": "^1.0.4",
+        "p-map": "^4.0.0",
+        "promise-inflight": "^1.0.1",
+        "rimraf": "^3.0.2",
+        "ssri": "^9.0.0",
+        "tar": "^6.1.11",
+        "unique-filename": "^2.0.0"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/cacache/node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/cacache/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cacache/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/cacheable-lookup": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.6.0"
+      }
+    },
+    "node_modules/cacheable-request": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz",
+      "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^4.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^6.0.1",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cacheable-request/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/chai": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmmirror.com/chai/-/chai-5.3.3.tgz",
+      "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/check-error/-/check-error-2.1.1.tgz",
+      "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chromium-pickle-js": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
+      "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ci-info": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz",
+      "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz",
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz",
+      "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "slice-ansi": "^3.0.0",
+        "string-width": "^4.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/clone-response": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz",
+      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz",
+      "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/compare-version": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz",
+      "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/confbox": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
+      "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/config-file-ts": {
+      "version": "0.2.8-rc1",
+      "resolved": "https://registry.npmmirror.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz",
+      "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "glob": "^10.3.12",
+        "typescript": "^5.4.3"
+      }
+    },
+    "node_modules/config-file-ts/node_modules/glob": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/config-file-ts/node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/copy-anything": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz",
+      "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+      "license": "MIT",
+      "dependencies": {
+        "is-what": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/crc": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmmirror.com/crc/-/crc-3.8.0.tgz",
+      "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "buffer": "^5.1.0"
+      }
+    },
+    "node_modules/cross-dirname": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/cross-dirname/-/cross-dirname-0.1.0.tgz",
+      "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT"
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.19",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+      "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/decompress-response/node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/defer-to-connect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
+      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "define-data-property": "^1.0.1",
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/detect-node": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/dir-compare": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
+      "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimatch": "^3.0.5",
+        "p-limit": "^3.1.0 "
+      }
+    },
+    "node_modules/dir-compare/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/dir-compare/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/dmg-builder": {
+      "version": "26.0.12",
+      "resolved": "https://registry.npmmirror.com/dmg-builder/-/dmg-builder-26.0.12.tgz",
+      "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "app-builder-lib": "26.0.12",
+        "builder-util": "26.0.11",
+        "builder-util-runtime": "9.3.1",
+        "fs-extra": "^10.1.0",
+        "iconv-lite": "^0.6.2",
+        "js-yaml": "^4.1.0"
+      },
+      "optionalDependencies": {
+        "dmg-license": "^1.0.11"
+      }
+    },
+    "node_modules/dmg-builder/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/dmg-builder/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/dmg-builder/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/dmg-license": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/dmg-license/-/dmg-license-1.0.11.tgz",
+      "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "dependencies": {
+        "@types/plist": "^3.0.1",
+        "@types/verror": "^1.10.3",
+        "ajv": "^6.10.0",
+        "crc": "^3.8.0",
+        "iconv-corefoundation": "^1.1.7",
+        "plist": "^3.0.4",
+        "smart-buffer": "^4.0.2",
+        "verror": "^1.10.0"
+      },
+      "bin": {
+        "dmg-license": "bin/dmg-license.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "11.0.7",
+      "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
+      "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dotenv": "^16.4.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ejs": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
+      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "jake": "^10.8.5"
+      },
+      "bin": {
+        "ejs": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/electron": {
+      "version": "39.2.5",
+      "resolved": "https://registry.npmmirror.com/electron/-/electron-39.2.5.tgz",
+      "integrity": "sha512-LXlOcH3CNopcVTQWjp680fMygdNrWKdIe3hyMtlyceO+Jd0b2hdMw1iWz36I+UUXHXPH87i937gXYi0jze2fCw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "@electron/get": "^2.0.0",
+        "@types/node": "^22.7.7",
+        "extract-zip": "^2.0.1"
+      },
+      "bin": {
+        "electron": "cli.js"
+      },
+      "engines": {
+        "node": ">= 12.20.55"
+      }
+    },
+    "node_modules/electron-builder": {
+      "version": "26.0.12",
+      "resolved": "https://registry.npmmirror.com/electron-builder/-/electron-builder-26.0.12.tgz",
+      "integrity": "sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "app-builder-lib": "26.0.12",
+        "builder-util": "26.0.11",
+        "builder-util-runtime": "9.3.1",
+        "chalk": "^4.1.2",
+        "dmg-builder": "26.0.12",
+        "fs-extra": "^10.1.0",
+        "is-ci": "^3.0.0",
+        "lazy-val": "^1.0.5",
+        "simple-update-notifier": "2.0.0",
+        "yargs": "^17.6.2"
+      },
+      "bin": {
+        "electron-builder": "cli.js",
+        "install-app-deps": "install-app-deps.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/electron-builder-squirrel-windows": {
+      "version": "26.0.12",
+      "resolved": "https://registry.npmmirror.com/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.0.12.tgz",
+      "integrity": "sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "app-builder-lib": "26.0.12",
+        "builder-util": "26.0.11",
+        "electron-winstaller": "5.4.0"
+      }
+    },
+    "node_modules/electron-builder/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/electron-builder/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/electron-builder/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/electron-log": {
+      "version": "5.4.3",
+      "resolved": "https://registry.npmmirror.com/electron-log/-/electron-log-5.4.3.tgz",
+      "integrity": "sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/electron-publish": {
+      "version": "26.0.11",
+      "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-26.0.11.tgz",
+      "integrity": "sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/fs-extra": "^9.0.11",
+        "builder-util": "26.0.11",
+        "builder-util-runtime": "9.3.1",
+        "chalk": "^4.1.2",
+        "form-data": "^4.0.0",
+        "fs-extra": "^10.1.0",
+        "lazy-val": "^1.0.5",
+        "mime": "^2.5.2"
+      }
+    },
+    "node_modules/electron-publish/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/electron-publish/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/electron-publish/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/electron-updater": {
+      "version": "6.6.2",
+      "resolved": "https://registry.npmmirror.com/electron-updater/-/electron-updater-6.6.2.tgz",
+      "integrity": "sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==",
+      "license": "MIT",
+      "dependencies": {
+        "builder-util-runtime": "9.3.1",
+        "fs-extra": "^10.1.0",
+        "js-yaml": "^4.1.0",
+        "lazy-val": "^1.0.5",
+        "lodash.escaperegexp": "^4.1.2",
+        "lodash.isequal": "^4.5.0",
+        "semver": "^7.6.3",
+        "tiny-typed-emitter": "^2.1.0"
+      }
+    },
+    "node_modules/electron-updater/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/electron-updater/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/electron-updater/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/electron-winstaller": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmmirror.com/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
+      "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "@electron/asar": "^3.2.1",
+        "debug": "^4.1.1",
+        "fs-extra": "^7.0.1",
+        "lodash": "^4.17.21",
+        "temp": "^0.9.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@electron/windows-sign": "^1.1.2"
+      }
+    },
+    "node_modules/electron-winstaller/node_modules/fs-extra": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-7.0.1.tgz",
+      "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/electron/node_modules/@types/node": {
+      "version": "22.19.1",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.19.1.tgz",
+      "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "node_modules/electron/node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/element-plus": {
+      "version": "2.12.0",
+      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.12.0.tgz",
+      "integrity": "sha512-M9YLSn2np9OnqrSKWsiXvGe3qnF8pd94+TScsHj1aTMCD+nSEvucXermf807qNt6hOP040le0e5Aft7E9ZfHmA==",
+      "license": "MIT",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.3.2",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.17.20",
+        "@types/lodash-es": "^4.17.12",
+        "@vueuse/core": "^9.1.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.19",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.3",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/encoding": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "iconv-lite": "^0.6.2"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
+      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/err-code": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz",
+      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es6-error": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz",
+      "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.12",
+        "@esbuild/android-arm": "0.25.12",
+        "@esbuild/android-arm64": "0.25.12",
+        "@esbuild/android-x64": "0.25.12",
+        "@esbuild/darwin-arm64": "0.25.12",
+        "@esbuild/darwin-x64": "0.25.12",
+        "@esbuild/freebsd-arm64": "0.25.12",
+        "@esbuild/freebsd-x64": "0.25.12",
+        "@esbuild/linux-arm": "0.25.12",
+        "@esbuild/linux-arm64": "0.25.12",
+        "@esbuild/linux-ia32": "0.25.12",
+        "@esbuild/linux-loong64": "0.25.12",
+        "@esbuild/linux-mips64el": "0.25.12",
+        "@esbuild/linux-ppc64": "0.25.12",
+        "@esbuild/linux-riscv64": "0.25.12",
+        "@esbuild/linux-s390x": "0.25.12",
+        "@esbuild/linux-x64": "0.25.12",
+        "@esbuild/netbsd-arm64": "0.25.12",
+        "@esbuild/netbsd-x64": "0.25.12",
+        "@esbuild/openbsd-arm64": "0.25.12",
+        "@esbuild/openbsd-x64": "0.25.12",
+        "@esbuild/openharmony-arm64": "0.25.12",
+        "@esbuild/sunos-x64": "0.25.12",
+        "@esbuild/win32-arm64": "0.25.12",
+        "@esbuild/win32-ia32": "0.25.12",
+        "@esbuild/win32-x64": "0.25.12"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/escodegen": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz",
+      "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^5.2.0",
+        "esutils": "^2.0.2"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.39.1",
+      "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.1.tgz",
+      "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.1",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.1",
+        "@eslint/js": "9.39.1",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-config-prettier": {
+      "version": "10.1.8",
+      "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+      "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "eslint-config-prettier": "bin/cli.js"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-config-prettier"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "5.5.4",
+      "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
+      "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.11.7"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-plugin-prettier"
+      },
+      "peerDependencies": {
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+        "prettier": ">=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-plugin-vue": {
+      "version": "10.6.2",
+      "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.6.2.tgz",
+      "integrity": "sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.1.1",
+        "postcss-selector-parser": "^7.1.0",
+        "semver": "^7.6.3",
+        "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "peerDependencies": {
+        "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
+        "@typescript-eslint/parser": "^7.0.0 || ^8.0.0",
+        "eslint": "^8.57.0 || ^9.0.0",
+        "vue-eslint-parser": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@stylistic/eslint-plugin": {
+          "optional": true
+        },
+        "@typescript-eslint/parser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz",
+      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/execa": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/execa/-/execa-9.6.1.tgz",
+      "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==",
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/merge-streams": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "figures": "^6.1.0",
+        "get-stream": "^9.0.0",
+        "human-signals": "^8.0.1",
+        "is-plain-obj": "^4.1.0",
+        "is-stream": "^4.0.1",
+        "npm-run-path": "^6.0.0",
+        "pretty-ms": "^9.2.0",
+        "signal-exit": "^4.1.0",
+        "strip-final-newline": "^4.0.0",
+        "yoctocolors": "^2.1.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/execa/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.2.tgz",
+      "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/exponential-backoff": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
+      "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/exsolve": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz",
+      "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/extract-zip": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz",
+      "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "get-stream": "^5.1.0",
+        "yauzl": "^2.10.0"
+      },
+      "bin": {
+        "extract-zip": "cli.js"
+      },
+      "engines": {
+        "node": ">= 10.17.0"
+      },
+      "optionalDependencies": {
+        "@types/yauzl": "^2.9.1"
+      }
+    },
+    "node_modules/extract-zip/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/extsprintf": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz",
+      "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-diff": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz",
+      "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fd-slicer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz",
+      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pend": "~1.2.0"
+      }
+    },
+    "node_modules/figures": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz",
+      "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
+      "license": "MIT",
+      "dependencies": {
+        "is-unicode-supported": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/figures/node_modules/is-unicode-supported": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/filelist": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz",
+      "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "minimatch": "^5.0.1"
+      }
+    },
+    "node_modules/filelist/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz",
+      "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/foreground-child/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz",
+      "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+      "license": "MIT",
+      "dependencies": {
+        "@sec-ant/readable-stream": "^0.4.1",
+        "is-stream": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/global-agent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz",
+      "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "dependencies": {
+        "boolean": "^3.0.1",
+        "es6-error": "^4.1.1",
+        "matcher": "^3.0.0",
+        "roarr": "^2.15.3",
+        "semver": "^7.3.2",
+        "serialize-error": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=10.0"
+      }
+    },
+    "node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globalthis": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/got": {
+      "version": "11.8.6",
+      "resolved": "https://registry.npmmirror.com/got/-/got-11.8.6.tgz",
+      "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/is": "^4.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.2",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.5.2",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/got?sponsor=1"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "license": "ISC"
+    },
+    "node_modules/graphemer": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
+      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+      "license": "MIT"
+    },
+    "node_modules/hosted-git-info": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+      "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+      "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/http2-wrapper": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/human-signals": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-8.0.1.tgz",
+      "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/humanize-ms": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz",
+      "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.0.0"
+      }
+    },
+    "node_modules/iconv-corefoundation": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmmirror.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
+      "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "dependencies": {
+        "cli-truncate": "^2.1.0",
+        "node-addon-api": "^1.6.3"
+      },
+      "engines": {
+        "node": "^8.11.2 || >=10"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.4.tgz",
+      "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/infer-owner": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz",
+      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/ip-address": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz",
+      "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/is-ci": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz",
+      "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ci-info": "^3.2.0"
+      },
+      "bin": {
+        "is-ci": "bin.js"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-lambda": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz",
+      "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-4.0.1.tgz",
+      "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-what": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz",
+      "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/isbinaryfile": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
+      "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/gjtorikian/"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC"
+    },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^4.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
+      "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.23",
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+      "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/jake": {
+      "version": "10.9.4",
+      "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz",
+      "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "async": "^3.2.6",
+        "filelist": "^1.0.4",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "jake": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
+      "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz",
+      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+      "dev": true,
+      "license": "ISC",
+      "optional": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonc-eslint-parser": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.1.tgz",
+      "integrity": "sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.5.0",
+        "eslint-visitor-keys": "^3.0.0",
+        "espree": "^9.0.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      }
+    },
+    "node_modules/jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+      "dev": true,
+      "license": "MIT",
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/lazy-val": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz",
+      "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
+      "license": "MIT"
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/local-pkg": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
+      "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mlly": "^1.7.4",
+        "pkg-types": "^2.3.0",
+        "quansync": "^0.2.11"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/local-pkg/node_modules/confbox": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
+      "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/local-pkg/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/local-pkg/node_modules/pkg-types": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
+      "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.2.2",
+        "exsolve": "^1.0.7",
+        "pathe": "^2.0.3"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash-unified": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
+      "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/lodash-es": "*",
+        "lodash": "*",
+        "lodash-es": "*"
+      }
+    },
+    "node_modules/lodash.escaperegexp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+      "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isequal": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+      "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+      "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+      "license": "MIT"
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/loupe": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/loupe/-/loupe-3.2.1.tgz",
+      "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lowercase-keys": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/magicast": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmmirror.com/magicast/-/magicast-0.3.5.tgz",
+      "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.25.4",
+        "@babel/types": "^7.25.4",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-fetch-happen": {
+      "version": "10.2.1",
+      "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+      "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "agentkeepalive": "^4.2.1",
+        "cacache": "^16.1.0",
+        "http-cache-semantics": "^4.1.0",
+        "http-proxy-agent": "^5.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "is-lambda": "^1.0.1",
+        "lru-cache": "^7.7.1",
+        "minipass": "^3.1.6",
+        "minipass-collect": "^1.0.2",
+        "minipass-fetch": "^2.0.3",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "negotiator": "^0.6.3",
+        "promise-retry": "^2.0.1",
+        "socks-proxy-agent": "^7.0.0",
+        "ssri": "^9.0.0"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/make-fetch-happen/node_modules/agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/make-fetch-happen/node_modules/http-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@tootallnate/once": "2",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/make-fetch-happen/node_modules/https-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/make-fetch-happen/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/matcher": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz",
+      "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "escape-string-regexp": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-collect": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz",
+      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minipass-fetch": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+      "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minipass": "^3.1.6",
+        "minipass-sized": "^1.0.3",
+        "minizlib": "^2.1.2"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      },
+      "optionalDependencies": {
+        "encoding": "^0.1.13"
+      }
+    },
+    "node_modules/minipass-flush": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.5.tgz",
+      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-sized": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz",
+      "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+      "license": "MIT"
+    },
+    "node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/mlly": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz",
+      "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "pathe": "^2.0.3",
+        "pkg-types": "^1.3.1",
+        "ufo": "^1.6.1"
+      }
+    },
+    "node_modules/mlly/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/muggle-string": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
+      "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/node-abi": {
+      "version": "3.85.0",
+      "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.85.0.tgz",
+      "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-1.7.2.tgz",
+      "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/node-api-version": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz",
+      "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.3.5"
+      }
+    },
+    "node_modules/nopt": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/nopt/-/nopt-6.0.0.tgz",
+      "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "abbrev": "^1.0.0"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/normalize-wheel-es": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+      "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/npm-run-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz",
+      "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0",
+        "unicorn-magic": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/npm-run-path/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-cancelable": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz",
+      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-map": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz",
+      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "aggregate-error": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-ms": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz",
+      "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/path-scurry/node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
+    "node_modules/pe-library": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz",
+      "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jet2jet"
+      }
+    },
+    "node_modules/pend": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz",
+      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+      "license": "MIT"
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
+      "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^7.7.7"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.5.0",
+        "vue": "^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/@vue/devtools-api": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+      "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-kit": "^7.7.9"
+      }
+    },
+    "node_modules/pkg-types": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
+      "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.1.8",
+        "mlly": "^1.7.4",
+        "pathe": "^2.0.1"
+      }
+    },
+    "node_modules/pkg-types/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/plist": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.0.tgz",
+      "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@xmldom/xmldom": "^0.8.8",
+        "base64-js": "^1.5.1",
+        "xmlbuilder": "^15.1.1"
+      },
+      "engines": {
+        "node": ">=10.4.0"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
+      "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postject": {
+      "version": "1.0.0-alpha.6",
+      "resolved": "https://registry.npmmirror.com/postject/-/postject-1.0.0-alpha.6.tgz",
+      "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "commander": "^9.4.0"
+      },
+      "bin": {
+        "postject": "dist/cli.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/postject/node_modules/commander": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz",
+      "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": "^12.20.0 || >=14"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.4.tgz",
+      "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/pretty-ms": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz",
+      "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==",
+      "license": "MIT",
+      "dependencies": {
+        "parse-ms": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/proc-log": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/proc-log/-/proc-log-2.0.1.tgz",
+      "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/promise-retry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz",
+      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "err-code": "^2.0.2",
+        "retry": "^0.12.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/pump": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz",
+      "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/quansync": {
+      "version": "0.2.11",
+      "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz",
+      "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/antfu"
+        },
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/sxzz"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-binary-file-arch": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
+      "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.4"
+      },
+      "bin": {
+        "read-binary-file-arch": "cli.js"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resedit": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz",
+      "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pe-library": "^0.4.1"
+      },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jet2jet"
+      }
+    },
+    "node_modules/resolve-alpn": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/responselike": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz",
+      "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lowercase-keys": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "license": "MIT"
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/roarr": {
+      "version": "2.15.4",
+      "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz",
+      "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "dependencies": {
+        "boolean": "^3.0.1",
+        "detect-node": "^2.0.4",
+        "globalthis": "^1.0.1",
+        "json-stringify-safe": "^5.0.1",
+        "semver-compare": "^1.0.0",
+        "sprintf-js": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz",
+      "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.53.3",
+        "@rollup/rollup-android-arm64": "4.53.3",
+        "@rollup/rollup-darwin-arm64": "4.53.3",
+        "@rollup/rollup-darwin-x64": "4.53.3",
+        "@rollup/rollup-freebsd-arm64": "4.53.3",
+        "@rollup/rollup-freebsd-x64": "4.53.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+        "@rollup/rollup-linux-arm64-musl": "4.53.3",
+        "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+        "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-musl": "4.53.3",
+        "@rollup/rollup-openharmony-arm64": "4.53.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+        "@rollup/rollup-win32-x64-gnu": "4.53.3",
+        "@rollup/rollup-win32-x64-msvc": "4.53.3",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/sanitize-filename": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmmirror.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
+      "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
+      "dev": true,
+      "license": "WTFPL OR ISC",
+      "dependencies": {
+        "truncate-utf8-bytes": "^1.0.0"
+      }
+    },
+    "node_modules/sass": {
+      "version": "1.94.2",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.94.2.tgz",
+      "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "chokidar": "^4.0.0",
+        "immutable": "^5.0.2",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher": "^2.4.1"
+      }
+    },
+    "node_modules/sax": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.3.tgz",
+      "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/scule": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
+      "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
+      "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver-compare": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz",
+      "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/serialize-error": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz",
+      "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "type-fest": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/serialize-error/node_modules/type-fest": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz",
+      "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "optional": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/simple-update-notifier": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+      "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/slice-ansi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz",
+      "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks": {
+      "version": "2.8.7",
+      "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.7.tgz",
+      "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ip-address": "^10.0.1",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks-proxy-agent": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+      "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^6.0.2",
+        "debug": "^4.3.3",
+        "socks": "^2.6.2"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/socks-proxy-agent/node_modules/agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sprintf-js": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz",
+      "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "optional": true
+    },
+    "node_modules/ssri": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmmirror.com/ssri/-/ssri-9.0.1.tgz",
+      "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/stat-mode": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz",
+      "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/std-env": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz",
+      "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+      "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/strip-literal": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.1.0.tgz",
+      "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^9.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/sudo-prompt": {
+      "version": "9.2.1",
+      "resolved": "https://registry.npmmirror.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
+      "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "license": "MIT"
+    },
+    "node_modules/sumchecker": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz",
+      "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "debug": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 8.0"
+      }
+    },
+    "node_modules/superjson": {
+      "version": "2.2.6",
+      "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz",
+      "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+      "license": "MIT",
+      "dependencies": {
+        "copy-anything": "^4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/synckit": {
+      "version": "0.11.11",
+      "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz",
+      "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pkgr/core": "^0.2.9"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/synckit"
+      }
+    },
+    "node_modules/tar": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz",
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^5.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tar/node_modules/minipass": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz",
+      "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/temp": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmmirror.com/temp/-/temp-0.9.4.tgz",
+      "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mkdirp": "^0.5.1",
+        "rimraf": "~2.6.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/temp-file": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmmirror.com/temp-file/-/temp-file-3.4.0.tgz",
+      "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "async-exit-hook": "^2.0.1",
+        "fs-extra": "^10.0.0"
+      }
+    },
+    "node_modules/temp-file/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/temp-file/node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/temp-file/node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/temp/node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/temp/node_modules/rimraf": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      }
+    },
+    "node_modules/test-exclude": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/test-exclude/-/test-exclude-7.0.1.tgz",
+      "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^10.4.1",
+        "minimatch": "^9.0.4"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/test-exclude/node_modules/glob": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/test-exclude/node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/tiny-async-pool": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz",
+      "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^5.5.0"
+      }
+    },
+    "node_modules/tiny-async-pool/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/tiny-typed-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
+      "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==",
+      "license": "MIT"
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.15",
+      "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
+      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tinyglobby/node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tinyglobby/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/tinypool": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/tinypool/-/tinypool-1.1.1.tgz",
+      "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
+      "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmmirror.com/tinyspy/-/tinyspy-4.0.4.tgz",
+      "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tmp": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz",
+      "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/tmp-promise": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz",
+      "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tmp": "^0.2.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/truncate-utf8-bytes": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+      "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
+      "dev": true,
+      "license": "WTFPL",
+      "dependencies": {
+        "utf8-byte-length": "^1.0.1"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+      "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.12"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typescript-eslint": {
+      "version": "8.48.1",
+      "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.48.1.tgz",
+      "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.48.1",
+        "@typescript-eslint/parser": "8.48.1",
+        "@typescript-eslint/typescript-estree": "8.48.1",
+        "@typescript-eslint/utils": "8.48.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/ufo": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
+      "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/undici-types": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
+      "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unicorn-magic": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+      "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/unimport": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.5.0.tgz",
+      "integrity": "sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "escape-string-regexp": "^5.0.0",
+        "estree-walker": "^3.0.3",
+        "local-pkg": "^1.1.2",
+        "magic-string": "^0.30.19",
+        "mlly": "^1.8.0",
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.3",
+        "pkg-types": "^2.3.0",
+        "scule": "^1.3.0",
+        "strip-literal": "^3.1.0",
+        "tinyglobby": "^0.2.15",
+        "unplugin": "^2.3.10",
+        "unplugin-utils": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      }
+    },
+    "node_modules/unimport/node_modules/confbox": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
+      "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unimport/node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/unimport/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/unimport/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unimport/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/unimport/node_modules/pkg-types": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
+      "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.2.2",
+        "exsolve": "^1.0.7",
+        "pathe": "^2.0.3"
+      }
+    },
+    "node_modules/unimport/node_modules/unplugin": {
+      "version": "2.3.11",
+      "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz",
+      "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/remapping": "^2.3.5",
+        "acorn": "^8.15.0",
+        "picomatch": "^4.0.3",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      }
+    },
+    "node_modules/unique-filename": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-2.0.1.tgz",
+      "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "unique-slug": "^3.0.0"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/unique-slug": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-3.0.0.tgz",
+      "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/unplugin": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz",
+      "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.14.0",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/unplugin-auto-import": {
+      "version": "20.3.0",
+      "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-20.3.0.tgz",
+      "integrity": "sha512-RcSEQiVv7g0mLMMXibYVKk8mpteKxvyffGuDKqZZiFr7Oq3PB1HwgHdK5O7H4AzbhzHoVKG0NnMnsk/1HIVYzQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "local-pkg": "^1.1.2",
+        "magic-string": "^0.30.21",
+        "picomatch": "^4.0.3",
+        "unimport": "^5.5.0",
+        "unplugin": "^2.3.11",
+        "unplugin-utils": "^0.3.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@nuxt/kit": "^4.0.0",
+        "@vueuse/core": "*"
+      },
+      "peerDependenciesMeta": {
+        "@nuxt/kit": {
+          "optional": true
+        },
+        "@vueuse/core": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/unplugin-auto-import/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/unplugin-auto-import/node_modules/unplugin": {
+      "version": "2.3.11",
+      "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz",
+      "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/remapping": "^2.3.5",
+        "acorn": "^8.15.0",
+        "picomatch": "^4.0.3",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      }
+    },
+    "node_modules/unplugin-utils": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+      "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sxzz"
+      }
+    },
+    "node_modules/unplugin-utils/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unplugin-utils/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/unplugin-vue-components": {
+      "version": "30.0.0",
+      "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz",
+      "integrity": "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": "^4.0.3",
+        "debug": "^4.4.3",
+        "local-pkg": "^1.1.2",
+        "magic-string": "^0.30.19",
+        "mlly": "^1.8.0",
+        "tinyglobby": "^0.2.15",
+        "unplugin": "^2.3.10",
+        "unplugin-utils": "^0.3.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@babel/parser": "^7.15.8",
+        "@nuxt/kit": "^3.2.2 || ^4.0.0",
+        "vue": "2 || 3"
+      },
+      "peerDependenciesMeta": {
+        "@babel/parser": {
+          "optional": true
+        },
+        "@nuxt/kit": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/unplugin-vue-components/node_modules/unplugin": {
+      "version": "2.3.11",
+      "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz",
+      "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/remapping": "^2.3.5",
+        "acorn": "^8.15.0",
+        "picomatch": "^4.0.3",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      }
+    },
+    "node_modules/unplugin-vue-components/node_modules/unplugin/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/utf8-byte-length": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+      "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
+      "dev": true,
+      "license": "(WTFPL OR MIT)"
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/verror": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz",
+      "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=0.6.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "7.2.6",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.6.tgz",
+      "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3",
+        "postcss": "^8.5.6",
+        "rollup": "^4.43.0",
+        "tinyglobby": "^0.2.15"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^20.19.0 || >=22.12.0",
+        "jiti": ">=1.21.0",
+        "less": "^4.0.0",
+        "lightningcss": "^1.21.0",
+        "sass": "^1.70.0",
+        "sass-embedded": "^1.70.0",
+        "stylus": ">=0.54.8",
+        "sugarss": "^5.0.0",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-node": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/vite-node/-/vite-node-3.2.4.tgz",
+      "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.4.1",
+        "es-module-lexer": "^1.7.0",
+        "pathe": "^2.0.3",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/vite-node/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vite-plugin-electron": {
+      "version": "0.29.0",
+      "resolved": "https://registry.npmmirror.com/vite-plugin-electron/-/vite-plugin-electron-0.29.0.tgz",
+      "integrity": "sha512-HP0DI9Shg41hzt55IKYVnbrChWXHX95QtsEQfM+szQBpWjVhVGMlqRjVco6ebfQjWNr+Ga+PeoBjMIl8zMaufw==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "vite-plugin-electron-renderer": "*"
+      },
+      "peerDependenciesMeta": {
+        "vite-plugin-electron-renderer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-plugin-electron-renderer": {
+      "version": "0.14.6",
+      "resolved": "https://registry.npmmirror.com/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
+      "integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/vite/node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/vitest": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/vitest/-/vitest-3.2.4.tgz",
+      "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/chai": "^5.2.2",
+        "@vitest/expect": "3.2.4",
+        "@vitest/mocker": "3.2.4",
+        "@vitest/pretty-format": "^3.2.4",
+        "@vitest/runner": "3.2.4",
+        "@vitest/snapshot": "3.2.4",
+        "@vitest/spy": "3.2.4",
+        "@vitest/utils": "3.2.4",
+        "chai": "^5.2.0",
+        "debug": "^4.4.1",
+        "expect-type": "^1.2.1",
+        "magic-string": "^0.30.17",
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.2",
+        "std-env": "^3.9.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.2",
+        "tinyglobby": "^0.2.14",
+        "tinypool": "^1.1.1",
+        "tinyrainbow": "^2.0.0",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
+        "vite-node": "3.2.4",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/debug": "^4.1.12",
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "@vitest/browser": "3.2.4",
+        "@vitest/ui": "3.2.4",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/debug": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vitest/node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/vscode-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
+      "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vue": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
+      "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-sfc": "3.5.25",
+        "@vue/runtime-dom": "3.5.25",
+        "@vue/server-renderer": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-eslint-parser": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",
+      "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "eslint-scope": "^8.2.0",
+        "eslint-visitor-keys": "^4.2.0",
+        "espree": "^10.3.0",
+        "esquery": "^1.6.0",
+        "semver": "^7.6.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/vue-i18n": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.2.2.tgz",
+      "integrity": "sha512-ULIKZyRluUPRCZmihVgUvpq8hJTtOqnbGZuv4Lz+byEKZq4mU0g92og414l6f/4ju+L5mORsiUuEPYrAuX2NJg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@intlify/core-base": "11.2.2",
+        "@intlify/shared": "11.2.2",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vue-tsc": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.1.5.tgz",
+      "integrity": "sha512-L/G9IUjOWhBU0yun89rv8fKqmKC+T0HfhrFjlIml71WpfBv9eb4E9Bev8FMbyueBIU9vxQqbd+oOsVcDa5amGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@volar/typescript": "2.4.23",
+        "@vue/language-core": "3.1.5"
+      },
+      "bin": {
+        "vue-tsc": "bin/vue-tsc.js"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.0.0"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/webpack-virtual-modules": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
+      "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "15.1.1",
+      "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
+      "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yaml": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.2.tgz",
+      "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "yaml": "bin.mjs"
+      },
+      "engines": {
+        "node": ">= 14.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/eemeli"
+      }
+    },
+    "node_modules/yaml-eslint-parser": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz",
+      "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.0.0",
+        "yaml": "^2.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yauzl": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz",
+      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-crc32": "~0.2.3",
+        "fd-slicer": "~1.1.0"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/yoctocolors/-/yoctocolors-2.1.2.tgz",
+      "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    }
+  }
+}

+ 184 - 0
package.json

@@ -0,0 +1,184 @@
+{
+  "name": "claude-ai-installer",
+  "version": "0.0.0",
+  "description": "Claude AI安装器 - Claude AI Installer",
+  "main": "dist-electron/main.js",
+  "scripts": {
+    "dev": "chcp 65001 >nul && vite",
+    "build": "vue-tsc --noEmit && vite build && electron-builder",
+    "preview": "vite preview",
+    "electron:dev": "vite --mode electron",
+    "electron:build": "vite build && electron-builder",
+    "typecheck": "vue-tsc --noEmit",
+    "lint": "eslint . --fix",
+    "build:win": "node scripts/build.js -p win",
+    "build:mac": "node scripts/build.js -p mac",
+    "build:linux": "node scripts/build.js -p linux",
+    "build:all": "node scripts/build.js -p all",
+    "release": "node scripts/release.js",
+    "release:patch": "node scripts/release.js patch --tag",
+    "release:minor": "node scripts/release.js minor --tag",
+    "release:major": "node scripts/release.js major --tag",
+    "test": "vitest",
+    "test:run": "vitest run",
+    "test:coverage": "vitest run --coverage"
+  },
+  "dependencies": {
+    "@element-plus/icons-vue": "^2.3.1",
+    "axios": "^1.13.2",
+    "electron-log": "^5.4.3",
+    "electron-updater": "^6.6.2",
+    "element-plus": "^2.9.0",
+    "execa": "^9.6.1",
+    "pinia": "^3.0.4",
+    "sudo-prompt": "^9.2.1",
+    "vue": "^3.5.0",
+    "vue-i18n": "^11.2.2"
+  },
+  "devDependencies": {
+    "@eslint/js": "^9.39.1",
+    "@intlify/unplugin-vue-i18n": "^6.0.0",
+    "@types/node": "^24.10.1",
+    "@typescript-eslint/eslint-plugin": "^8.48.1",
+    "@typescript-eslint/parser": "^8.48.1",
+    "@vitejs/plugin-vue": "^6.0.2",
+    "@vitest/coverage-v8": "^3.2.4",
+    "@vue/eslint-config-prettier": "^10.2.0",
+    "@vue/eslint-config-typescript": "^14.6.0",
+    "electron": "^39.2.5",
+    "electron-builder": "^26.0.12",
+    "eslint": "^9.39.1",
+    "eslint-plugin-vue": "^10.6.2",
+    "sass": "^1.89.0",
+    "typescript": "^5.8.0",
+    "unplugin-auto-import": "^20.3.0",
+    "unplugin-vue-components": "^30.0.0",
+    "vite": "^7.2.6",
+    "vite-plugin-electron": "^0.29.0",
+    "vite-plugin-electron-renderer": "^0.14.6",
+    "vitest": "^3.2.4",
+    "vue-tsc": "^3.1.5"
+  },
+  "build": {
+    "appId": "com.claude.ai.installer",
+    "productName": "Claude AI Installer",
+    "artifactName": "Claude-AI-Installer-${version}-${os}-${arch}.${ext}",
+    "directories": {
+      "output": "release"
+    },
+    "publish": {
+      "provider": "github",
+      "owner": "anthropics",
+      "repo": "claude-ai-installer"
+    },
+    "files": [
+      "dist/**/*",
+      "dist-electron/**/*"
+    ],
+    "extraResources": [
+      {
+        "from": "public/icons",
+        "to": "icons"
+      }
+    ],
+    "win": {
+      "target": [
+        {
+          "target": "nsis",
+          "arch": [
+            "x64"
+          ]
+        },
+        {
+          "target": "portable",
+          "arch": [
+            "x64"
+          ]
+        }
+      ],
+      "icon": "public/icons/win/icon.ico"
+    },
+    "portable": {
+      "artifactName": "Claude-AI-Installer-${version}-portable.${ext}"
+    },
+    "nsis": {
+      "oneClick": false,
+      "perMachine": false,
+      "allowToChangeInstallationDirectory": true,
+      "deleteAppDataOnUninstall": false,
+      "installerIcon": "public/icons/win/icon.ico",
+      "uninstallerIcon": "public/icons/win/icon.ico",
+      "installerHeaderIcon": "public/icons/win/icon.ico",
+      "createDesktopShortcut": true,
+      "createStartMenuShortcut": true,
+      "shortcutName": "Claude AI Installer",
+      "artifactName": "Claude-AI-Installer-${version}-${os}-${arch}-setup.${ext}"
+    },
+    "mac": {
+      "target": [
+        {
+          "target": "dmg",
+          "arch": [
+            "x64",
+            "arm64"
+          ]
+        },
+        {
+          "target": "zip",
+          "arch": [
+            "x64",
+            "arm64"
+          ]
+        }
+      ],
+      "icon": "public/icons/mac/icon.icns",
+      "category": "public.app-category.developer-tools",
+      "hardenedRuntime": true,
+      "gatekeeperAssess": false
+    },
+    "dmg": {
+      "contents": [
+        {
+          "x": 130,
+          "y": 220
+        },
+        {
+          "x": 410,
+          "y": 220,
+          "type": "link",
+          "path": "/Applications"
+        }
+      ],
+      "window": {
+        "width": 540,
+        "height": 380
+      }
+    },
+    "linux": {
+      "target": [
+        {
+          "target": "AppImage",
+          "arch": [
+            "x64"
+          ]
+        },
+        {
+          "target": "deb",
+          "arch": [
+            "x64"
+          ]
+        },
+        {
+          "target": "rpm",
+          "arch": [
+            "x64"
+          ]
+        }
+      ],
+      "icon": "public/icons/png",
+      "category": "Development",
+      "maintainer": "Claude AI <[email protected]>",
+      "vendor": "Claude AI"
+    }
+  }
+}

BIN
public/icons/mac/icon.icns


BIN
public/icons/png/1024x1024.png


BIN
public/icons/png/128x128.png


BIN
public/icons/png/16x16.png


BIN
public/icons/png/24x24.png


BIN
public/icons/png/256x256.png


BIN
public/icons/png/32x32.png


BIN
public/icons/png/48x48.png


BIN
public/icons/png/512x512.png


BIN
public/icons/png/64x64.png


BIN
public/icons/win/icon.ico


+ 110 - 0
release-win.bat

@@ -0,0 +1,110 @@
+@echo off
+chcp 65001 >nul 2>&1
+
+:: ============================================
+:: Claude AI Installer Windows 发布脚本
+:: Release script for Windows
+:: 此脚本作为 scripts/release.js 的简单入口
+:: ============================================
+
+:: 切换到脚本所在目录
+cd /d "%~dp0"
+
+:: 检查是否请求帮助
+if /i "%~1"=="--help" goto :show_help
+if /i "%~1"=="-h" goto :show_help
+
+:: 检查 Node.js
+where node >nul 2>&1
+if errorlevel 1 (
+    echo   [91mx[0m 未找到 Node.js,请先安装 Node.js
+    pause
+    exit /b 1
+)
+
+:: 检查 node_modules
+if not exist "node_modules" (
+    echo   [93m![0m 未找到 node_modules,正在安装依赖...
+    call npm install
+    if errorlevel 1 (
+        echo   [91mx[0m 安装依赖失败
+        pause
+        exit /b 1
+    )
+)
+
+:: 转换参数并调用 Node.js 脚本
+:: release.js 在 Windows 上已默认只构建 win 平台
+:: 默认版本类型为 patch
+set "NODE_ARGS=patch"
+
+:parse_args
+if "%~1"=="" goto :run_release
+if /i "%~1"=="patch" set "NODE_ARGS=patch" & shift & goto :parse_args
+if /i "%~1"=="minor" set "NODE_ARGS=minor" & shift & goto :parse_args
+if /i "%~1"=="major" set "NODE_ARGS=major" & shift & goto :parse_args
+if /i "%~1"=="--tag" set "NODE_ARGS=%NODE_ARGS% --tag" & shift & goto :parse_args
+if /i "%~1"=="-t" set "NODE_ARGS=%NODE_ARGS% --tag" & shift & goto :parse_args
+if /i "%~1"=="--push" set "NODE_ARGS=%NODE_ARGS% --push" & shift & goto :parse_args
+if /i "%~1"=="--dry-run" set "NODE_ARGS=%NODE_ARGS% --dry-run" & shift & goto :parse_args
+if /i "%~1"=="--platform" set "NODE_ARGS=%NODE_ARGS% --platform %~2" & shift & shift & goto :parse_args
+if /i "%~1"=="-p" set "NODE_ARGS=%NODE_ARGS% --platform %~2" & shift & shift & goto :parse_args
+shift
+goto :parse_args
+
+:run_release
+echo.
+echo   [96m^> 调用 Node.js 发布脚本...[0m
+echo   [94mi[0m 执行: node scripts/release.js %NODE_ARGS%
+echo.
+
+call node scripts/release.js %NODE_ARGS%
+if errorlevel 1 (
+    echo.
+    echo   [91mx[0m 发布失败!
+    echo.
+    pause
+    exit /b 1
+)
+
+echo.
+pause
+goto :eof
+
+:: ============================================
+:: 显示帮助信息
+:: ============================================
+:show_help
+echo.
+echo   Claude AI Installer Windows 发布脚本
+echo.
+echo   用法:
+echo     release-win.bat [版本类型] [参数]
+echo.
+echo   版本类型:
+echo     patch              补丁版本 +1 (默认, 如: 0.0.1 -^> 0.0.2)
+echo     minor              次版本 +1 (如: 0.0.1 -^> 0.1.0)
+echo     major              主版本 +1 (如: 0.0.1 -^> 1.0.0)
+echo.
+echo   参数:
+echo     --tag, -t          创建 Git tag
+echo     --push             推送 tag 到远程仓库 (需要先指定 --tag)
+echo     --dry-run          仅显示操作,不实际执行
+echo     --platform, -p     指定平台 (逗号分隔): win,mac,linux
+echo     --help, -h         显示此帮助信息
+echo.
+echo   示例:
+echo     release-win.bat                    更新 patch 版本并构建
+echo     release-win.bat minor              更新 minor 版本并构建
+echo     release-win.bat patch --tag        更新版本、构建并创建 tag
+echo     release-win.bat --tag --push       更新版本、构建、创建 tag 并推送
+echo     release-win.bat --dry-run          预览发布操作
+echo.
+echo   输出:
+echo     release\Claude-AI-Installer-x.x.x-win-x64-setup.exe     (NSIS 安装程序)
+echo     release\Claude-AI-Installer-x.x.x-win-x64.exe           (便携版)
+echo.
+echo   注意: 此脚本调用 scripts/release.js 执行实际发布
+echo         Windows 上默认只构建 win 平台
+echo.
+goto :eof

+ 283 - 0
scripts/README.md

@@ -0,0 +1,283 @@
+# 构建与发布脚本
+
+本目录包含 Claude AI Installer 的构建和发布脚本。
+
+## 两种使用方式
+
+### 1. 只构建,不发布
+
+```bash
+npm run build:win      # 仅构建 Windows
+npm run build:mac      # 仅构建 macOS
+npm run build:linux    # 仅构建 Linux
+npm run build:all      # 构建所有平台
+```
+
+只生成安装包到 `release/` 目录,不修改版本号,不创建 Git tag。
+
+### 2. 发布(包含构建)
+
+```bash
+npm run release:patch  # 更新版本 → 构建 → 创建 tag
+npm run release:minor  # 次版本发布
+npm run release:major  # 主版本发布
+```
+
+完整流程:更新版本号 → 构建所有平台 → 生成发布说明 → 创建 Git tag。
+
+**简单来说:**
+- `build.js` = 单纯构建
+- `release.js` = 更新版本 + 构建 + 发布相关操作
+
+---
+
+## 快速开始
+
+```bash
+# 进入项目目录
+cd ApqInstaller
+
+# 安装依赖
+npm install
+
+# 构建当前平台
+npm run build:win      # Windows
+npm run build:mac      # macOS
+npm run build:linux    # Linux
+npm run build:all      # 所有平台
+```
+
+## 脚本说明
+
+### build.js - 构建脚本
+
+多平台构建脚本,用于编译和打包应用。
+
+#### 用法
+
+```bash
+node scripts/build.js [选项]
+```
+
+#### 选项
+
+| 选项 | 简写 | 说明 | 默认值 |
+|------|------|------|--------|
+| `--platform` | `-p` | 目标平台: `win`, `mac`, `linux`, `all` | 当前平台 |
+| `--arch` | `-a` | 目标架构: `x64`, `arm64`, `all` | 当前架构 |
+| `--publish` | `-P` | 发布模式: `always`, `onTag`, `never` | `never` |
+| `--skip-build` | - | 跳过前端构建,仅打包 | - |
+| `--help` | `-h` | 显示帮助信息 | - |
+
+#### 示例
+
+```bash
+# 构建 Windows 版本
+node scripts/build.js -p win
+
+# 构建 macOS ARM64 版本
+node scripts/build.js -p mac -a arm64
+
+# 构建所有平台
+node scripts/build.js -p all
+
+# 跳过前端构建,仅打包
+node scripts/build.js -p win --skip-build
+```
+
+---
+
+### release.js - 发布脚本
+
+自动化发布脚本,按以下顺序执行:
+1. 更新版本号(可选)
+2. 构建所有平台安装包
+3. 生成发布说明
+4. 创建 Git tag(可选)
+5. 推送到远程(可选)
+
+#### 用法
+
+```bash
+node scripts/release.js [版本] [选项]
+```
+
+#### 版本参数
+
+| 参数 | 说明 | 示例 |
+|------|------|------|
+| `patch` | 补丁版本 +1 | 0.0.1 → 0.0.2 |
+| `minor` | 次版本 +1 | 0.0.1 → 0.1.0 |
+| `major` | 主版本 +1 | 0.0.1 → 1.0.0 |
+| `x.y.z` | 指定版本号 | 1.2.3 |
+
+#### 选项
+
+| 选项 | 简写 | 说明 |
+|------|------|------|
+| `--tag` | `-t` | 创建 Git tag |
+| `--push` | - | 推送 tag 到远程仓库 |
+| `--platform` | `-p` | 指定平台 (逗号分隔) |
+| `--dry-run` | - | 仅显示操作,不实际执行 |
+| `--help` | `-h` | 显示帮助信息 |
+
+#### 示例
+
+```bash
+# 更新补丁版本并构建
+node scripts/release.js patch
+
+# 设置版本为 1.0.0 并创建 tag
+node scripts/release.js 1.0.0 --tag
+
+# 更新次版本,仅构建 Windows 和 macOS
+node scripts/release.js minor -p win,mac
+
+# 完整发布流程:更新版本、构建、创建 tag、推送
+node scripts/release.js patch --tag --push
+
+# 预览发布操作(不实际执行)
+node scripts/release.js patch --dry-run
+```
+
+---
+
+## NPM Scripts
+
+在 `package.json` 中预定义了以下命令:
+
+### 构建命令
+
+| 命令 | 说明 |
+|------|------|
+| `npm run build:win` | 构建 Windows 版本 |
+| `npm run build:mac` | 构建 macOS 版本 |
+| `npm run build:linux` | 构建 Linux 版本 |
+| `npm run build:all` | 构建所有平台 |
+
+### 发布命令
+
+| 命令 | 说明 |
+|------|------|
+| `npm run release` | 发布(不更新版本) |
+| `npm run release:patch` | 发布补丁版本 + 创建 tag |
+| `npm run release:minor` | 发布次版本 + 创建 tag |
+| `npm run release:major` | 发布主版本 + 创建 tag |
+
+---
+
+## 构建产物
+
+构建完成后,安装包会输出到 `release/` 目录:
+
+### Windows
+
+| 格式 | 文件名 | 说明 |
+|------|--------|------|
+| NSIS | `Claude AI Installer-x.x.x-win-x64-nsis.exe` | 安装程序 |
+| Portable | `Claude AI Installer-x.x.x-win-x64-portable.exe` | 便携版 |
+
+### macOS
+
+| 格式 | 文件名 | 说明 |
+|------|--------|------|
+| DMG | `Claude AI Installer-x.x.x-mac-x64-dmg.dmg` | Intel 版 |
+| DMG | `Claude AI Installer-x.x.x-mac-arm64-dmg.dmg` | Apple Silicon 版 |
+| ZIP | `Claude AI Installer-x.x.x-mac-x64-zip.zip` | Intel 压缩包 |
+| ZIP | `Claude AI Installer-x.x.x-mac-arm64-zip.zip` | Apple Silicon 压缩包 |
+
+### Linux
+
+| 格式 | 文件名 | 说明 |
+|------|--------|------|
+| AppImage | `Claude AI Installer-x.x.x-linux-x64-AppImage.AppImage` | 通用格式 |
+| DEB | `Claude AI Installer-x.x.x-linux-x64-deb.deb` | Debian/Ubuntu |
+| RPM | `Claude AI Installer-x.x.x-linux-x64-rpm.rpm` | Fedora/RHEL |
+
+---
+
+## 跨平台构建注意事项
+
+### 在 Windows 上构建
+
+- **Windows**: 原生支持
+- **macOS**: 不支持(需要 macOS 系统)
+- **Linux**: 需要 WSL 或 Docker
+
+### 在 macOS 上构建
+
+- **Windows**: 需要安装 Wine (`brew install --cask wine-stable`)
+- **macOS**: 原生支持
+- **Linux**: 原生支持
+
+### 在 Linux 上构建
+
+- **Windows**: 需要安装 Wine
+- **macOS**: 不支持(需要 macOS 系统)
+- **Linux**: 原生支持
+
+### 使用 CI/CD 构建所有平台
+
+推荐使用 GitHub Actions 等 CI/CD 服务在各自平台的 runner 上构建:
+
+```yaml
+# .github/workflows/build.yml 示例
+jobs:
+  build:
+    strategy:
+      matrix:
+        os: [windows-latest, macos-latest, ubuntu-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-node@v4
+      - run: npm install
+      - run: npm run build
+```
+
+---
+
+## 发布流程
+
+### 标准发布流程
+
+1. 确保所有更改已提交
+2. 运行发布命令:
+   ```bash
+   npm run release:patch  # 或 minor/major
+   ```
+3. 检查 `release/` 目录中的构建产物
+4. 手动推送(如果没有使用 `--push`):
+   ```bash
+   git push && git push --tags
+   ```
+5. 在 GitHub 创建 Release,上传构建产物
+
+### 自动发布流程
+
+```bash
+# 一键完成:更新版本 → 构建 → 创建 tag → 推送
+node scripts/release.js patch --tag --push
+```
+
+---
+
+## 故障排除
+
+### 构建失败
+
+1. 确保已安装所有依赖:`npm install`
+2. 清理旧的构建文件:删除 `dist/` 和 `dist-electron/` 目录
+3. 检查 Node.js 版本(推荐 18+)
+
+### 图标问题
+
+确保以下图标文件存在:
+- Windows: `public/icons/win/icon.ico`
+- macOS: `public/icons/mac/icon.icns`
+- Linux: `public/icons/png/` (包含各尺寸 PNG)
+
+### 签名问题
+
+- **Windows**: 需要代码签名证书(可选)
+- **macOS**: 需要 Apple Developer 证书进行公证(发布到 App Store 必需)

+ 468 - 0
scripts/build.js

@@ -0,0 +1,468 @@
+#!/usr/bin/env node
+
+/**
+ * 多平台构建脚本
+ * Multi-platform build script for Claude AI Installer
+ *
+ * 用法 / Usage:
+ *   node scripts/build.js [options]
+ *
+ * 选项 / Options:
+ *   --platform, -p    目标平台: win, mac, linux, all (默认: 当前平台)
+ *   --arch, -a        架构: x64, arm64, all (默认: 当前架构)
+ *   --publish, -P     发布模式: always, onTag, never (默认: never)
+ *   --skip-build      跳过前端构建,仅打包
+ *   --skip-typecheck  跳过 TypeScript 类型检查
+ *   --help, -h        显示帮助信息
+ */
+
+const { execSync } = require('child_process');
+const path = require('path');
+const fs = require('fs');
+
+// 颜色输出
+const colors = {
+  reset: '\x1b[0m',
+  bright: '\x1b[1m',
+  red: '\x1b[31m',
+  green: '\x1b[32m',
+  yellow: '\x1b[33m',
+  blue: '\x1b[34m',
+  cyan: '\x1b[36m',
+};
+
+const log = {
+  info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
+  success: (msg) => console.log(`${colors.green}✔${colors.reset} ${msg}`),
+  warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
+  error: (msg) => console.log(`${colors.red}✖${colors.reset} ${msg}`),
+  step: (msg) => console.log(`\n${colors.cyan}${colors.bright}▶ ${msg}${colors.reset}`),
+};
+
+// 解析命令行参数
+function parseArgs() {
+  const args = process.argv.slice(2);
+  const options = {
+    platform: process.platform === 'win32' ? 'win' : process.platform === 'darwin' ? 'mac' : 'linux',
+    arch: process.arch,
+    publish: 'never',
+    skipBuild: false,
+    skipTypecheck: false,
+    help: false,
+  };
+
+  for (let i = 0; i < args.length; i++) {
+    const arg = args[i];
+    switch (arg) {
+      case '--platform':
+      case '-p':
+        options.platform = args[++i];
+        break;
+      case '--arch':
+      case '-a':
+        options.arch = args[++i];
+        break;
+      case '--publish':
+      case '-P':
+        options.publish = args[++i];
+        break;
+      case '--skip-build':
+      case '-s':
+        options.skipBuild = true;
+        break;
+      case '--skip-typecheck':
+        options.skipTypecheck = true;
+        break;
+      case '--help':
+      case '-h':
+        options.help = true;
+        break;
+    }
+  }
+
+  return options;
+}
+
+// 显示帮助信息
+function showHelp() {
+  console.log(`
+${colors.bright}Claude AI Installer 构建脚本${colors.reset}
+
+${colors.cyan}用法:${colors.reset}
+  node scripts/build.js [options]
+
+${colors.cyan}选项:${colors.reset}
+  --platform, -p <platform>   目标平台
+                              win    - Windows (NSIS 安装包)
+                              mac    - macOS (DMG)
+                              linux  - Linux (AppImage)
+                              all    - 所有平台
+                              默认: 当前平台
+
+  --arch, -a <arch>           目标架构
+                              x64    - 64位 Intel/AMD
+                              arm64  - ARM64 (Apple Silicon, etc.)
+                              all    - 所有架构
+                              默认: 当前架构
+
+  --publish, -P <mode>        发布模式
+                              always - 总是发布
+                              onTag  - 仅在 Git tag 时发布
+                              never  - 不发布 (默认)
+
+  --skip-build, -s            跳过前端构建,仅执行打包
+
+  --skip-typecheck            跳过 TypeScript 类型检查
+
+  --help, -h                  显示此帮助信息
+
+${colors.cyan}示例:${colors.reset}
+  node scripts/build.js                          # 构建当前平台
+  node scripts/build.js -p win                   # 构建 Windows 版本
+  node scripts/build.js -p mac -a arm64          # 构建 macOS ARM64 版本
+  node scripts/build.js -p all                   # 构建所有平台
+  node scripts/build.js -p win -P always         # 构建并发布 Windows 版本
+`);
+}
+
+// 执行命令
+function exec(command, options = {}) {
+  log.info(`执行: ${command}`);
+  try {
+    execSync(command, {
+      stdio: 'inherit',
+      cwd: path.resolve(__dirname, '..'),
+      ...options,
+    });
+    return true;
+  } catch (error) {
+    log.error(`命令执行失败: ${command}`);
+    return false;
+  }
+}
+
+// 获取 electron-builder 平台参数
+function getPlatformArgs(platform, arch) {
+  const platformMap = {
+    win: '--win',
+    mac: '--mac',
+    linux: '--linux',
+  };
+
+  const archMap = {
+    x64: '--x64',
+    arm64: '--arm64',
+    all: '--x64 --arm64',
+  };
+
+  let args = [];
+
+  if (platform === 'all') {
+    args.push('--win', '--mac', '--linux');
+  } else {
+    args.push(platformMap[platform] || platformMap.win);
+  }
+
+  if (arch && arch !== 'all') {
+    args.push(archMap[arch] || '');
+  } else if (arch === 'all') {
+    args.push('--x64', '--arm64');
+  }
+
+  return args.join(' ');
+}
+
+// 清理旧的构建文件
+function cleanBuild() {
+  log.step('清理旧的构建文件...');
+
+  const dirsToClean = ['dist', 'dist-electron'];
+  const projectRoot = path.resolve(__dirname, '..');
+
+  for (const dir of dirsToClean) {
+    const fullPath = path.join(projectRoot, dir);
+    if (fs.existsSync(fullPath)) {
+      fs.rmSync(fullPath, { recursive: true, force: true });
+      log.info(`已删除: ${dir}`);
+    }
+  }
+
+  // 清空 release 目录内容,但保留目录本身
+  const releaseDir = path.join(projectRoot, 'release');
+  if (fs.existsSync(releaseDir)) {
+    const files = fs.readdirSync(releaseDir);
+    for (const file of files) {
+      const filePath = path.join(releaseDir, file);
+      fs.rmSync(filePath, { recursive: true, force: true });
+    }
+    log.info('已清空: release');
+  }
+
+  log.success('清理完成');
+}
+
+// 类型检查
+function typeCheck() {
+  log.step('执行类型检查...');
+  if (!exec('npx vue-tsc --noEmit')) {
+    throw new Error('类型检查失败');
+  }
+  log.success('类型检查通过');
+}
+
+// 构建前端
+function buildFrontend() {
+  log.step('构建前端资源...');
+  if (!exec('npx vite build')) {
+    throw new Error('前端构建失败');
+  }
+  log.success('前端构建完成');
+}
+
+// 打包 Electron 应用
+function packageApp(options) {
+  log.step(`打包应用 (平台: ${options.platform}, 架构: ${options.arch})...`);
+
+  const platformArgs = getPlatformArgs(options.platform, options.arch);
+  const publishArg = options.publish !== 'never' ? `--publish ${options.publish}` : '';
+
+  const command = `npx electron-builder ${platformArgs} ${publishArg}`.trim();
+
+  if (!exec(command)) {
+    throw new Error('应用打包失败');
+  }
+
+  log.success('应用打包完成');
+}
+
+// 显示构建结果
+function showResults() {
+  log.step('构建结果:');
+
+  const releaseDir = path.resolve(__dirname, '..', 'release');
+
+  if (!fs.existsSync(releaseDir)) {
+    log.warn('release 目录不存在');
+    return;
+  }
+
+  const files = fs.readdirSync(releaseDir);
+  const packages = files.filter(f => {
+    const ext = path.extname(f).toLowerCase();
+    return ['.exe', '.dmg', '.appimage', '.deb', '.rpm', '.zip'].includes(ext);
+  });
+
+  if (packages.length === 0) {
+    log.warn('未找到打包文件');
+    return;
+  }
+
+  console.log('');
+  for (const pkg of packages) {
+    const filePath = path.join(releaseDir, pkg);
+    const stats = fs.statSync(filePath);
+    const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
+    console.log(`  ${colors.green}•${colors.reset} ${pkg} (${sizeMB} MB)`);
+  }
+  console.log('');
+  log.info(`输出目录: ${releaseDir}`);
+}
+
+// ==================== Portable 版本构建 ====================
+
+const LAUNCHER_DIR = path.resolve(__dirname, '..', 'launcher');
+const RELEASE_DIR = path.resolve(__dirname, '..', 'release');
+
+/**
+ * 查找 portable exe 文件
+ */
+function findPortableExe() {
+  const files = fs.readdirSync(RELEASE_DIR);
+  return files.find(f => f.includes('portable') && f.endsWith('.exe'));
+}
+
+/**
+ * 使用 pkg 打包启动器
+ */
+function buildLauncher() {
+  log.step('构建启动器...');
+
+  // 检查是否安装了 pkg
+  try {
+    execSync('npx pkg --version', { stdio: 'ignore' });
+  } catch {
+    log.info('安装 pkg...');
+    execSync('npm install -g pkg', { stdio: 'inherit' });
+  }
+
+  const launcherScript = path.join(LAUNCHER_DIR, 'launcher.js');
+  const outputPath = path.join(RELEASE_DIR, 'launcher.exe');
+
+  // 使用 pkg 打包
+  execSync(`npx pkg "${launcherScript}" --target node18-win-x64 --output "${outputPath}"`, {
+    stdio: 'inherit',
+    cwd: path.resolve(__dirname, '..'),
+  });
+
+  log.success(`启动器构建完成: ${outputPath}`);
+  return outputPath;
+}
+
+/**
+ * 创建 Portable 目录结构
+ */
+function createPortableStructure(portableExe, launcherExe) {
+  const packageJson = require(path.resolve(__dirname, '..', 'package.json'));
+  const version = packageJson.version;
+  const portableDirName = `Claude-AI-Installer-${version}-portable`;
+  const portableDir = path.join(RELEASE_DIR, portableDirName);
+  const appDir = path.join(portableDir, 'app');
+  const updateDir = path.join(portableDir, 'update');
+
+  log.step('创建 Portable 目录结构...');
+
+  // 清理旧目录
+  if (fs.existsSync(portableDir)) {
+    fs.rmSync(portableDir, { recursive: true });
+  }
+
+  // 创建目录
+  fs.mkdirSync(appDir, { recursive: true });
+  fs.mkdirSync(updateDir, { recursive: true });
+
+  // 复制启动器
+  const launcherDest = path.join(portableDir, 'Claude-AI-Installer.exe');
+  fs.copyFileSync(launcherExe, launcherDest);
+  log.info(`复制启动器: ${launcherDest}`);
+
+  // 复制主程序到 app 目录
+  const portableExePath = path.join(RELEASE_DIR, portableExe);
+  const appExeName = `Claude-AI-Installer-${version}.exe`;
+  const appExeDest = path.join(appDir, appExeName);
+  fs.copyFileSync(portableExePath, appExeDest);
+  log.info(`复制主程序: ${appExeDest}`);
+
+  // 创建 .gitkeep 文件保持 update 目录
+  fs.writeFileSync(path.join(updateDir, '.gitkeep'), '');
+
+  // 创建 README
+  const readme = `Claude AI Installer - Portable 版本
+
+使用方法:
+1. 双击 Claude-AI-Installer.exe 启动程序
+2. 程序会自动检查更新
+3. 更新下载后,重启程序即可完成更新
+
+目录说明:
+- Claude-AI-Installer.exe: 启动器
+- app/: 主程序目录
+- update/: 更新文件临时目录
+
+版本: ${version}
+`;
+  fs.writeFileSync(path.join(portableDir, 'README.txt'), readme, 'utf8');
+
+  log.success(`Portable 目录创建完成: ${portableDir}`);
+
+  // 创建 zip 包
+  createPortableZip(portableDir, portableDirName);
+
+  return portableDir;
+}
+
+/**
+ * 创建 ZIP 压缩包
+ */
+function createPortableZip(sourceDir, name) {
+  const zipPath = path.join(RELEASE_DIR, `${name}.zip`);
+
+  log.step('创建 ZIP 压缩包...');
+
+  // 使用 PowerShell 创建 zip
+  const psCommand = `Compress-Archive -Path "${sourceDir}\\*" -DestinationPath "${zipPath}" -Force`;
+  execSync(`powershell -Command "${psCommand}"`, { stdio: 'inherit' });
+
+  log.success(`ZIP 压缩包创建完成: ${zipPath}`);
+}
+
+/**
+ * 构建 Portable 版本(带启动器)
+ */
+function buildPortableVersion() {
+  log.step('构建 Portable 版本(带启动器)...');
+
+  // 查找 portable exe
+  const portableExe = findPortableExe();
+  if (!portableExe) {
+    throw new Error('未找到 portable exe 文件');
+  }
+  log.info(`找到 Portable 文件: ${portableExe}`);
+
+  // 构建启动器
+  const launcherExe = buildLauncher();
+
+  // 创建 Portable 目录结构
+  createPortableStructure(portableExe, launcherExe);
+
+  log.success('Portable 版本构建完成');
+}
+
+// 主函数
+async function main() {
+  const options = parseArgs();
+
+  if (options.help) {
+    showHelp();
+    process.exit(0);
+  }
+
+  console.log(`
+${colors.bright}╔════════════════════════════════════════════╗
+║     Claude AI Installer 构建脚本           ║
+╚════════════════════════════════════════════╝${colors.reset}
+`);
+
+  log.info(`目标平台: ${options.platform}`);
+  log.info(`目标架构: ${options.arch}`);
+  log.info(`发布模式: ${options.publish}`);
+  log.info(`跳过构建: ${options.skipBuild}`);
+  log.info(`跳过类型检查: ${options.skipTypecheck}`);
+
+  const startTime = Date.now();
+
+  try {
+    // 清理旧的构建文件
+    cleanBuild();
+
+    if (!options.skipBuild) {
+      // 类型检查
+      if (!options.skipTypecheck) {
+        typeCheck();
+      } else {
+        log.warn('跳过类型检查');
+      }
+      // 构建前端
+      buildFrontend();
+    } else {
+      log.warn('跳过前端构建');
+    }
+
+    packageApp(options);
+
+    // Windows 平台自动构建带启动器的 Portable 版本
+    if (options.platform === 'win' || options.platform === 'all') {
+      buildPortableVersion();
+    }
+
+    showResults();
+
+    const duration = ((Date.now() - startTime) / 1000).toFixed(1);
+    log.success(`构建完成! 耗时: ${duration}s`);
+
+  } catch (error) {
+    log.error(error.message);
+    process.exit(1);
+  }
+}
+
+main();

+ 461 - 0
scripts/release.js

@@ -0,0 +1,461 @@
+#!/usr/bin/env node
+
+/**
+ * 发布脚本
+ * Release script for Claude AI Installer
+ *
+ * 功能:
+ * - 自动更新版本号
+ * - 构建所有平台的安装包
+ * - 生成发布说明
+ * - 可选: 创建 Git tag 并推送
+ *
+ * 用法 / Usage:
+ *   node scripts/release.js [version] [options]
+ *
+ * 示例:
+ *   node scripts/release.js patch          # 0.0.1 -> 0.0.2
+ *   node scripts/release.js minor          # 0.0.1 -> 0.1.0
+ *   node scripts/release.js major          # 0.0.1 -> 1.0.0
+ *   node scripts/release.js 1.2.3          # 设置为指定版本
+ *   node scripts/release.js patch --tag    # 更新版本并创建 Git tag
+ */
+
+const { execSync } = require('child_process');
+const path = require('path');
+const fs = require('fs');
+
+// 颜色输出
+const colors = {
+  reset: '\x1b[0m',
+  bright: '\x1b[1m',
+  red: '\x1b[31m',
+  green: '\x1b[32m',
+  yellow: '\x1b[33m',
+  blue: '\x1b[34m',
+  cyan: '\x1b[36m',
+  magenta: '\x1b[35m',
+};
+
+const log = {
+  info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
+  success: (msg) => console.log(`${colors.green}✔${colors.reset} ${msg}`),
+  warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
+  error: (msg) => console.log(`${colors.red}✖${colors.reset} ${msg}`),
+  step: (msg) => console.log(`\n${colors.cyan}${colors.bright}▶ ${msg}${colors.reset}`),
+};
+
+const projectRoot = path.resolve(__dirname, '..');
+const packageJsonPath = path.join(projectRoot, 'package.json');
+
+// 解析命令行参数
+function parseArgs() {
+  const args = process.argv.slice(2);
+  // 每个平台默认只构建对应的版本,避免跨平台构建失败
+  const platformMap = { win32: 'win', darwin: 'mac', linux: 'linux' };
+  const currentPlatform = platformMap[process.platform] || 'linux';
+  const defaultPlatforms = [currentPlatform];
+  const options = {
+    version: null,
+    tag: false,
+    push: false,
+    platforms: defaultPlatforms,
+    dryRun: false,
+    help: false,
+  };
+
+  for (let i = 0; i < args.length; i++) {
+    const arg = args[i];
+    switch (arg) {
+      case '--tag':
+      case '-t':
+        options.tag = true;
+        break;
+      case '--push':
+        options.push = true;
+        break;
+      case '--platform':
+      case '-p':
+        options.platforms = args[++i].split(',');
+        break;
+      case '--dry-run':
+        options.dryRun = true;
+        break;
+      case '--help':
+      case '-h':
+        options.help = true;
+        break;
+      default:
+        if (!arg.startsWith('-') && !options.version) {
+          options.version = arg;
+        }
+    }
+  }
+
+  return options;
+}
+
+// 显示帮助信息
+function showHelp() {
+  console.log(`
+${colors.bright}Claude AI Installer 发布脚本${colors.reset}
+
+${colors.cyan}用法:${colors.reset}
+  node scripts/release.js [version] [options]
+
+${colors.cyan}版本参数:${colors.reset}
+  patch              补丁版本 (0.0.1 -> 0.0.2)
+  minor              次版本 (0.0.1 -> 0.1.0)
+  major              主版本 (0.0.1 -> 1.0.0)
+  x.y.z              指定版本号
+
+${colors.cyan}选项:${colors.reset}
+  --tag, -t          创建 Git tag
+  --push             推送 tag 到远程仓库
+  --platform, -p     指定平台 (逗号分隔): win,mac,linux
+  --dry-run          仅显示将要执行的操作,不实际执行
+  --help, -h         显示此帮助信息
+
+${colors.cyan}示例:${colors.reset}
+  node scripts/release.js patch                    # 更新补丁版本并构建
+  node scripts/release.js 1.0.0 --tag              # 设置版本为 1.0.0 并创建 tag
+  node scripts/release.js minor -p win,mac         # 更新次版本,仅构建 Win 和 Mac
+  node scripts/release.js patch --tag --push       # 更新版本、创建 tag 并推送
+`);
+}
+
+// 读取 package.json
+function readPackageJson() {
+  return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
+}
+
+// 写入 package.json
+function writePackageJson(pkg) {
+  fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n');
+}
+
+// 计算新版本号
+function calculateNewVersion(currentVersion, versionArg) {
+  if (!versionArg) {
+    return currentVersion;
+  }
+
+  // 如果是具体版本号
+  if (/^\d+\.\d+\.\d+/.test(versionArg)) {
+    return versionArg;
+  }
+
+  const parts = currentVersion.split('.').map(Number);
+
+  switch (versionArg) {
+    case 'major':
+      return `${parts[0] + 1}.0.0`;
+    case 'minor':
+      return `${parts[0]}.${parts[1] + 1}.0`;
+    case 'patch':
+      return `${parts[0]}.${parts[1]}.${parts[2] + 1}`;
+    default:
+      throw new Error(`无效的版本参数: ${versionArg}`);
+  }
+}
+
+// 执行命令
+function exec(command, options = {}) {
+  log.info(`执行: ${command}`);
+  try {
+    return execSync(command, {
+      stdio: options.silent ? 'pipe' : 'inherit',
+      cwd: projectRoot,
+      encoding: 'utf-8',
+      ...options,
+    });
+  } catch (error) {
+    if (!options.ignoreError) {
+      throw error;
+    }
+    return null;
+  }
+}
+
+// 检查 Git 状态
+function checkGitStatus() {
+  log.step('检查 Git 状态...');
+
+  try {
+    const status = execSync('git status --porcelain', {
+      cwd: projectRoot,
+      encoding: 'utf-8',
+    });
+
+    if (status.trim()) {
+      log.warn('工作目录有未提交的更改:');
+      console.log(status);
+      return false;
+    }
+
+    log.success('Git 工作目录干净');
+    return true;
+  } catch (error) {
+    log.warn('无法检查 Git 状态');
+    return true;
+  }
+}
+
+// 更新版本号
+function updateVersion(newVersion, dryRun) {
+  log.step(`更新版本号到 ${newVersion}...`);
+
+  if (dryRun) {
+    log.info('[DRY RUN] 将更新 package.json 版本号');
+    return;
+  }
+
+  const pkg = readPackageJson();
+  pkg.version = newVersion;
+  writePackageJson(pkg);
+
+  log.success(`版本号已更新: ${newVersion}`);
+}
+
+// 构建所有平台
+function buildAllPlatforms(platforms, dryRun) {
+  log.step('构建应用...');
+
+  for (const platform of platforms) {
+    log.info(`构建 ${platform} 平台...`);
+
+    if (dryRun) {
+      log.info(`[DRY RUN] 将构建 ${platform} 平台`);
+      continue;
+    }
+
+    exec(`node scripts/build.js -p ${platform}`);
+  }
+
+  log.success('所有平台构建完成');
+}
+
+// 创建 Git tag
+function createGitTag(version, dryRun) {
+  log.step(`创建 Git tag v${version}...`);
+
+  if (dryRun) {
+    log.info(`[DRY RUN] 将创建 tag: v${version}`);
+    return;
+  }
+
+  // 提交版本更新
+  exec('git add package.json');
+  exec(`git commit -m "chore: release v${version}"`);
+
+  // 创建 tag
+  exec(`git tag -a v${version} -m "Release v${version}"`);
+
+  log.success(`Git tag v${version} 已创建`);
+}
+
+// 推送到远程
+function pushToRemote(version, dryRun) {
+  log.step('推送到远程仓库...');
+
+  if (dryRun) {
+    log.info('[DRY RUN] 将推送 commits 和 tags');
+    return;
+  }
+
+  exec('git push');
+  exec('git push --tags');
+
+  log.success('已推送到远程仓库');
+}
+
+// 生成发布说明
+function generateReleaseNotes(version) {
+  log.step('生成发布说明...');
+
+  const releaseDir = path.join(projectRoot, 'release');
+  const notesPath = path.join(releaseDir, `RELEASE_NOTES_v${version}.md`);
+
+  // 获取最近的 commits
+  let commits = '';
+  try {
+    commits = execSync('git log --oneline -20', {
+      cwd: projectRoot,
+      encoding: 'utf-8',
+    });
+  } catch (e) {
+    commits = '无法获取提交历史';
+  }
+
+  // 获取构建产物列表
+  let artifacts = [];
+  if (fs.existsSync(releaseDir)) {
+    artifacts = fs.readdirSync(releaseDir).filter(f => {
+      const ext = path.extname(f).toLowerCase();
+      return ['.exe', '.dmg', '.appimage', '.deb', '.rpm', '.zip'].includes(ext);
+    });
+  }
+
+  const notes = `# Claude AI Installer v${version}
+
+## 发布日期
+${new Date().toISOString().split('T')[0]}
+
+## 下载
+
+${artifacts.map(a => `- ${a}`).join('\n') || '暂无构建产物'}
+
+## 平台支持
+
+| 平台 | 文件格式 | 架构 |
+|------|----------|------|
+| Windows | NSIS 安装包 (.exe) | x64 |
+| macOS | DMG 镜像 (.dmg) | x64, arm64 |
+| Linux | AppImage (.AppImage) | x64 |
+
+## 更新内容
+
+请查看提交历史了解详细更新内容。
+
+## 最近提交
+
+\`\`\`
+${commits}
+\`\`\`
+
+## 安装说明
+
+### Windows
+1. 下载 \`.exe\` 安装包
+2. 双击运行安装程序
+3. 按照向导完成安装
+
+### macOS
+1. 下载 \`.dmg\` 文件
+2. 双击打开 DMG 镜像
+3. 将应用拖拽到 Applications 文件夹
+
+### Linux
+1. 下载 \`.AppImage\` 文件
+2. 添加执行权限: \`chmod +x *.AppImage\`
+3. 双击运行或在终端执行
+
+---
+🤖 Generated with Claude AI Installer Build System
+`;
+
+  fs.writeFileSync(notesPath, notes);
+  log.success(`发布说明已生成: ${notesPath}`);
+}
+
+// 显示发布摘要
+function showSummary(version, platforms) {
+  const releaseDir = path.join(projectRoot, 'release');
+
+  console.log(`
+${colors.bright}╔════════════════════════════════════════════╗
+║           发布摘要 - v${version.padEnd(20)}║
+╚════════════════════════════════════════════╝${colors.reset}
+`);
+
+  if (fs.existsSync(releaseDir)) {
+    const files = fs.readdirSync(releaseDir).filter(f => {
+      const ext = path.extname(f).toLowerCase();
+      return ['.exe', '.dmg', '.appimage', '.deb', '.rpm', '.zip'].includes(ext);
+    });
+
+    if (files.length > 0) {
+      console.log(`${colors.cyan}构建产物:${colors.reset}`);
+      for (const file of files) {
+        const filePath = path.join(releaseDir, file);
+        const stats = fs.statSync(filePath);
+        const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
+        console.log(`  ${colors.green}•${colors.reset} ${file} (${sizeMB} MB)`);
+      }
+    }
+  }
+
+  console.log(`
+${colors.cyan}输出目录:${colors.reset} ${releaseDir}
+${colors.cyan}版本号:${colors.reset} v${version}
+${colors.cyan}构建平台:${colors.reset} ${platforms.join(', ')}
+`);
+}
+
+// 主函数
+async function main() {
+  const options = parseArgs();
+
+  if (options.help) {
+    showHelp();
+    process.exit(0);
+  }
+
+  console.log(`
+${colors.bright}${colors.magenta}╔════════════════════════════════════════════╗
+║     Claude AI Installer 发布脚本           ║
+╚════════════════════════════════════════════╝${colors.reset}
+`);
+
+  if (options.dryRun) {
+    log.warn('DRY RUN 模式 - 不会执行实际操作');
+  }
+
+  const startTime = Date.now();
+
+  try {
+    // 读取当前版本
+    const pkg = readPackageJson();
+    const currentVersion = pkg.version;
+    log.info(`当前版本: ${currentVersion}`);
+
+    // 计算新版本
+    const newVersion = calculateNewVersion(currentVersion, options.version);
+    log.info(`目标版本: ${newVersion}`);
+
+    // 检查 Git 状态
+    if (options.tag && !options.dryRun) {
+      const isClean = checkGitStatus();
+      if (!isClean) {
+        log.warn('建议先提交或暂存更改');
+      }
+    }
+
+    // 更新版本号
+    if (options.version) {
+      updateVersion(newVersion, options.dryRun);
+    }
+
+    // 构建所有平台
+    buildAllPlatforms(options.platforms, options.dryRun);
+
+    // 生成发布说明
+    if (!options.dryRun) {
+      generateReleaseNotes(newVersion);
+    }
+
+    // 创建 Git tag
+    if (options.tag) {
+      createGitTag(newVersion, options.dryRun);
+    }
+
+    // 推送到远程
+    if (options.push) {
+      pushToRemote(newVersion, options.dryRun);
+    }
+
+    // 显示摘要
+    if (!options.dryRun) {
+      showSummary(newVersion, options.platforms);
+    }
+
+    const duration = ((Date.now() - startTime) / 1000).toFixed(1);
+    log.success(`发布完成! 耗时: ${duration}s`);
+
+  } catch (error) {
+    log.error(error.message);
+    process.exit(1);
+  }
+}
+
+main();

+ 226 - 0
shared/types.ts

@@ -0,0 +1,226 @@
+// shared/types.ts - 共享类型定义
+// 此文件在 Electron 主进程和 Vue 渲染进程之间共享
+
+// ==================== 基础类型 ====================
+
+export type SoftwareType = 'nodejs' | 'pnpm' | 'vscode' | 'git' | 'claudeCode'
+export type SoftwareTypeWithAll = SoftwareType | 'all'
+export type Platform = 'win32' | 'darwin' | 'linux'
+export type ToastType = 'success' | 'warning' | 'error' | 'info'
+export type SystemStatusType = 'success' | 'warning' | 'error'
+
+// Tab ID 类型
+export type TabId = 'intro' | 'nodejs' | 'vscode' | 'git' | 'claudeCode' | 'all'
+
+// ==================== 版本相关 ====================
+
+export interface VersionItem {
+  value: string
+  label: string
+  lts?: boolean
+  disabled?: boolean
+  separator?: boolean
+}
+
+export interface VersionResult {
+  versions: VersionItem[]
+  warning: string | null
+}
+
+// ==================== 安装相关 ====================
+
+export interface InstallOptions {
+  version?: string
+  installPnpm?: boolean
+  installNodejs?: boolean
+  nodejsVersion?: string
+  nodejsPath?: string // Node.js 自定义安装路径 (仅 Windows)
+  installVscode?: boolean
+  vscodeVersion?: string
+  vscodePath?: string // VS Code 自定义安装路径 (仅 Windows)
+  installGit?: boolean
+  gitVersion?: string
+  gitPath?: string // Git 自定义安装路径 (仅 Windows)
+  installClaudeCode?: boolean
+  installClaudeCodeExt?: boolean // Claude Code for VS Code 扩展
+  customPath?: string
+}
+
+export interface InstallStatus {
+  software: SoftwareTypeWithAll
+  message: string
+  progress: number
+  i18nKey?: string
+  i18nParams?: Record<string, string>
+  skipLog?: boolean
+}
+
+export interface InstallResult {
+  software: SoftwareTypeWithAll
+  message: string
+  i18nKey?: string
+  i18nParams?: Record<string, string>
+}
+
+export interface InstalledInfo {
+  installed: boolean
+  version: string | null
+}
+
+export interface AllInstalledInfo {
+  nodejs: InstalledInfo
+  pnpm: InstalledInfo
+  vscode: InstalledInfo
+  git: InstalledInfo
+  claudeCode: InstalledInfo
+}
+
+export interface InstallHistoryItem {
+  software: SoftwareTypeWithAll
+  version: string
+  options: InstallOptions
+  success: boolean
+  error?: string
+  cancelled?: boolean
+  duration?: number
+  timestamp: number
+}
+
+// ==================== 系统相关 ====================
+
+export interface PackageManagerResult {
+  exists: boolean
+  manager: 'none' | 'brew' | 'apt'
+}
+
+export interface CommandResult {
+  command: string
+  args: string[]
+}
+
+export type LogCategory = 'app' | 'install'
+
+export interface LogEntry {
+  level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
+  message: string
+  timestamp: string
+  category?: LogCategory
+  data?: unknown
+}
+
+// ==================== 镜像配置 ====================
+
+export type GitMirrorType = 'huaweicloud' | 'github'
+export type NodejsMirrorType = 'official' | 'npmmirror'
+
+export interface GitMirrorConfig {
+  mirror: GitMirrorType
+}
+
+export interface NodejsMirrorConfig {
+  mirror: NodejsMirrorType
+}
+
+// ==================== 自动更新 ====================
+
+export type UpdateStatus =
+  | 'checking'
+  | 'available'
+  | 'not-available'
+  | 'downloading'
+  | 'downloaded'
+  | 'error'
+
+export interface UpdateProgress {
+  percent: number
+  bytesPerSecond: number
+  total: number
+  transferred: number
+}
+
+export interface UpdateResult {
+  status: UpdateStatus
+  info?: {
+    version: string
+    releaseDate?: string
+    releaseNotes?: string
+  }
+  progress?: UpdateProgress
+  error?: string
+}
+
+// ==================== Electron API ====================
+
+export interface ElectronAPI {
+  // 安装相关
+  install: (software: SoftwareTypeWithAll, options: InstallOptions) => Promise<void>
+  cancelInstall: () => Promise<boolean>
+  checkInstalled: (software: SoftwareTypeWithAll) => Promise<InstalledInfo | AllInstalledInfo>
+  uninstall: (software: SoftwareType) => Promise<boolean>
+
+  // 系统检测
+  checkAdmin: () => Promise<boolean>
+  checkPackageManager: () => Promise<PackageManagerResult>
+  installPackageManager: (manager: string) => Promise<{ success: boolean; error?: string }>
+  getPlatform: () => Promise<Platform>
+  checkNetwork: () => Promise<boolean>
+
+  // 版本
+  getVersions: (software: SoftwareType) => Promise<VersionResult>
+  checkUpdate: (software: SoftwareType) => Promise<{ hasUpdate: boolean; latestVersion?: string }>
+
+  // Git 镜像配置
+  setGitMirror: (mirror: GitMirrorType) => Promise<void>
+  getGitMirrorConfig: () => Promise<GitMirrorConfig>
+
+  // Node.js 镜像配置
+  setNodejsMirror: (mirror: NodejsMirrorType) => Promise<void>
+  getNodejsMirrorConfig: () => Promise<NodejsMirrorConfig>
+
+  // 历史和日志
+  getInstallHistory: (limit?: number) => Promise<InstallHistoryItem[]>
+  getLogs: () => Promise<LogEntry[]>
+  writeInstallLog: (message: string, level?: 'info' | 'warn' | 'error') => Promise<void>
+  getLogPaths: () => Promise<{ appLog: string; installLog: string }>
+
+  // 文件夹选择
+  selectDirectory: (defaultPath?: string) => Promise<{ canceled: boolean; path: string | null }>
+
+  // 窗口操作
+  setWindowTitle: (title: string) => Promise<void>
+  windowMinimize: () => Promise<void>
+  windowMaximize: () => Promise<boolean>
+  windowClose: () => Promise<void>
+  windowIsMaximized: () => Promise<boolean>
+
+  // Claude Code
+  checkClaudeCode: () => Promise<{ installed: boolean; version: string | null }>
+  launchClaudeCode: () => Promise<{ success: boolean }>
+  installClaudeCode: () => Promise<{ success: boolean; error?: string }>
+
+  // VS Code Extensions
+  checkVscodeExtension: (extensionId: string) => Promise<{ installed: boolean; version?: string }>
+  installVscodeExtension: (extensionId: string) => Promise<{ success: boolean; error?: string }>
+
+  // 事件监听
+  onInstallStatus: (callback: (data: InstallStatus) => void) => void
+  onInstallComplete: (callback: (data: InstallResult) => void) => void
+  onInstallError: (callback: (data: InstallResult) => void) => void
+  onNetworkChange: (callback: (online: boolean) => void) => void
+  removeAllListeners: () => void
+
+  // 自动更新
+  updaterCheck: () => Promise<UpdateResult>
+  updaterDownload: () => Promise<UpdateResult>
+  updaterInstall: () => Promise<void>
+  updaterVersion: () => Promise<string>
+  updaterIsPortable: () => Promise<boolean>
+  onUpdaterStatus: (callback: (data: UpdateResult) => void) => void
+}
+
+// Window.electronAPI 类型声明
+declare global {
+  interface Window {
+    electronAPI: ElectronAPI
+  }
+}

+ 268 - 0
src/App.vue

@@ -0,0 +1,268 @@
+<script setup lang="ts">
+import { onMounted, onUnmounted, computed, defineAsyncComponent, markRaw, type Component } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useVersionsStore } from '@/stores/versions'
+import { useInstallStore } from '@/stores/install'
+import { useSettingsStore } from '@/stores/settings'
+import { useSystemStore } from '@/stores/system'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+import InstallLog from '@/components/common/InstallLog.vue'
+import TitleBar from '@/components/common/TitleBar.vue'
+import SettingsPanel from '@/components/common/SettingsPanel.vue'
+import MirrorSelector from '@/components/common/MirrorSelector.vue'
+import ErrorBoundary from '@/components/common/ErrorBoundary.vue'
+import type { SoftwareType, SoftwareTypeWithAll, TabId } from '@/types/electron'
+
+// 懒加载视图组件
+const IntroView = defineAsyncComponent(() => import('@/views/IntroView.vue'))
+const NodejsView = defineAsyncComponent(() => import('@/views/NodejsView.vue'))
+const VscodeView = defineAsyncComponent(() => import('@/views/VscodeView.vue'))
+const GitView = defineAsyncComponent(() => import('@/views/GitView.vue'))
+const ClaudeCodeView = defineAsyncComponent(() => import('@/views/ClaudeCodeView.vue'))
+const BatchInstallView = defineAsyncComponent(() => import('@/views/BatchInstallView.vue'))
+
+const { t } = useI18n()
+const versionsStore = useVersionsStore()
+const installStore = useInstallStore()
+const settingsStore = useSettingsStore()
+const systemStore = useSystemStore()
+
+// Tab 配置
+interface TabConfig {
+  id: TabId
+  label: string
+  component: Component
+  showInstalledDot?: boolean
+  softwareType?: SoftwareType
+}
+
+const tabs: TabConfig[] = [
+  { id: 'intro', label: 'tabs.intro', component: markRaw(IntroView) },
+  { id: 'nodejs', label: 'tabs.nodejs', component: markRaw(NodejsView), showInstalledDot: true, softwareType: 'nodejs' },
+  { id: 'git', label: 'tabs.git', component: markRaw(GitView), showInstalledDot: true, softwareType: 'git' },
+  { id: 'claudeCode', label: 'tabs.claudeCode', component: markRaw(ClaudeCodeView), showInstalledDot: true, softwareType: 'claudeCode' },
+  { id: 'vscode', label: 'tabs.vscode', component: markRaw(VscodeView), showInstalledDot: true, softwareType: 'vscode' },
+  { id: 'all', label: 'tabs.all', component: markRaw(BatchInstallView) }
+]
+
+// 当前激活的 Tab 配置
+const currentTab = computed(() => {
+  return tabs.find(tab => tab.id === systemStore.activeTab) || tabs[0]
+})
+
+// 当前组件
+const currentComponent = computed(() => currentTab.value.component)
+
+async function handleInstall(software: SoftwareType, installOptions?: { customPath?: string }) {
+  // 只有 nodejs, vscode, git 需要版本选择
+  const versionedSoftware = software as 'nodejs' | 'vscode' | 'git'
+  const hasVersion = software === 'nodejs' || software === 'vscode' || software === 'git'
+  const options: Record<string, string | undefined> = {
+    version: hasVersion ? versionsStore.selectedVersions[versionedSoftware] : undefined
+  }
+  // 添加自定义安装路径(根据软件类型使用不同的参数名)
+  if (installOptions?.customPath) {
+    if (software === 'nodejs') {
+      options.nodejsPath = installOptions.customPath
+    } else if (software === 'vscode') {
+      options.vscodePath = installOptions.customPath
+    } else if (software === 'git') {
+      options.gitPath = installOptions.customPath
+    }
+  }
+  await installStore.doInstall(software, options)
+}
+
+async function handleCancel(_software: SoftwareTypeWithAll) {
+  await installStore.cancelInstall()
+}
+
+// 获取翻译后的消息,如果有 i18nKey 则使用翻译,否则使用原始消息
+function getTranslatedMessage(message: string, i18nKey?: string, i18nParams?: Record<string, string>): string {
+  if (i18nKey) {
+    return t(i18nKey, i18nParams || {})
+  }
+  return message
+}
+
+function bindInstallListeners() {
+  window.electronAPI.onInstallStatus((data) => {
+    const translatedMessage = getTranslatedMessage(data.message, data.i18nKey, data.i18nParams)
+    installStore.updateStatus(data.software, translatedMessage, data.progress)
+    // 只在 skipLog 不为 true 时记录日志
+    if (!data.skipLog) {
+      installStore.addLog(translatedMessage)
+    }
+  })
+
+  window.electronAPI.onInstallComplete((data) => {
+    const translatedMessage = getTranslatedMessage(data.message, data.i18nKey, data.i18nParams)
+    installStore.setSuccess(data.software, translatedMessage)
+    installStore.addLog(translatedMessage)
+    // 重新检测所有软件的安装状态(包括 claudeCode)
+    installStore.checkInstalledSoftware()
+    // 重新检测 Claude Code VS Code 扩展的安装状态
+    installStore.checkClaudeCodeExtInstalled()
+  })
+
+  window.electronAPI.onInstallError((data) => {
+    const translatedMessage = getTranslatedMessage(data.message, data.i18nKey, data.i18nParams)
+    installStore.setError(data.software, translatedMessage)
+    installStore.addLog(translatedMessage, 'error')
+    // 安装失败后也重新检测所有软件的安装状态
+    installStore.checkInstalledSoftware()
+    // 重新检测 Claude Code VS Code 扩展的安装状态
+    installStore.checkClaudeCodeExtInstalled()
+  })
+}
+
+onMounted(async () => {
+  bindInstallListeners()
+  settingsStore.watchSystemTheme()
+
+  // 先加载代理设置,确保版本获取时使用正确的代理配置
+  await settingsStore.initSettings()
+
+  await Promise.all([
+    systemStore.initSystem(),
+    versionsStore.loadAllVersions(),
+    installStore.checkInstalledSoftware()
+  ])
+
+  // 显示系统状态警告
+  if (!systemStore.isAdmin) {
+    systemStore.showSystemStatus('warning', 'system.noAdmin')
+  }
+
+  if (!systemStore.packageManager.exists) {
+    systemStore.showSystemStatus('error', 'system.noPackageManager', { manager: systemStore.packageManagerName }, {
+      textKey: 'system.installManager',
+      textParams: { manager: systemStore.packageManager.manager },
+      handler: async () => {
+        const success = await systemStore.installPackageManager()
+        if (success) {
+          systemStore.showSystemStatus('success', 'system.managerInstalled', { manager: systemStore.packageManager.manager })
+        }
+      }
+    })
+  }
+
+  // 设置窗口标题
+  window.electronAPI.setWindowTitle(t('app.title'))
+})
+
+onUnmounted(() => {
+  window.electronAPI.removeAllListeners()
+})
+</script>
+
+<template>
+  <div class="app-wrapper">
+    <!-- 自定义标题栏 -->
+    <TitleBar />
+
+    <div class="app-container">
+      <!-- 主面板 -->
+      <div class="main-panel">
+        <!-- 固定头部区域 -->
+        <div class="header-fixed">
+          <!-- 顶部菜单栏 -->
+          <div class="menu-bar">
+            <div class="menu-left">
+              <h1>{{ t('app.title') }}</h1>
+            </div>
+            <div class="menu-right">
+              <SettingsPanel />
+            </div>
+          </div>
+
+          <!-- 下载源选择栏 -->
+          <MirrorSelector />
+
+          <!-- 系统状态提示 -->
+          <el-alert
+            v-if="systemStore.systemStatus.visible"
+            :type="systemStore.systemStatus.type"
+            :title="t(systemStore.systemStatus.messageKey, systemStore.systemStatus.messageParams)"
+            :closable="true"
+            show-icon
+            @close="systemStore.hideSystemStatus"
+          >
+            <template v-if="systemStore.systemStatus.actionTextKey" #default>
+              <el-button size="small" type="primary" @click="systemStore.systemStatus.actionHandler?.()">
+                {{ t(systemStore.systemStatus.actionTextKey, systemStore.systemStatus.actionTextParams) }}
+              </el-button>
+            </template>
+          </el-alert>
+
+          <!-- 标签页 -->
+          <div class="tabs">
+            <button
+              v-for="tab in tabs"
+              :key="tab.id"
+              :class="['tab-btn', { active: systemStore.activeTab === tab.id }]"
+              @click="systemStore.setActiveTab(tab.id)"
+            >
+              <SoftwareIcon :software="tab.id" :size="18" />
+              <span class="tab-label">{{ t(tab.label) }}</span>
+              <span
+                v-if="tab.showInstalledDot && tab.softwareType && installStore.isInstalled(tab.softwareType)"
+                class="installed-dot"
+              ></span>
+            </button>
+          </div>
+        </div>
+
+        <!-- 可滚动内容区域 -->
+        <div class="content-scrollable">
+          <!-- 标签页内容 - 使用动态组件实现懒加载 -->
+          <div class="tab-content">
+            <ErrorBoundary>
+              <KeepAlive>
+                <component
+                  :is="currentComponent"
+                  :key="currentTab.id"
+                  @install="handleInstall"
+                  @cancel="handleCancel"
+                />
+              </KeepAlive>
+            </ErrorBoundary>
+          </div>
+
+          <!-- 安装日志 -->
+          <InstallLog v-if="installStore.isAnyInstalling || installStore.installLogs.length > 0" />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.menu-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: var(--spacing-sm);
+  padding-bottom: var(--spacing-sm);
+  border-bottom: 1px solid var(--border-color-lighter);
+
+  .menu-left {
+    h1 {
+      margin: 0;
+      font-size: 24px;
+      font-weight: 600;
+      color: var(--text-primary);
+    }
+  }
+
+  .menu-right {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-sm);
+  }
+}
+
+.el-alert {
+  margin-bottom: var(--spacing-md);
+}
+</style>

+ 45 - 0
src/components/common/ErrorBoundary.vue

@@ -0,0 +1,45 @@
+<script setup lang="ts">
+import { ref, onErrorCaptured } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+
+const hasError = ref(false)
+const errorMessage = ref('')
+
+onErrorCaptured((error: Error) => {
+  hasError.value = true
+  errorMessage.value = error.message || t('common.error')
+  console.error('ErrorBoundary caught error:', error)
+  // 返回 false 阻止错误继续传播
+  return false
+})
+
+function handleRetry() {
+  hasError.value = false
+  errorMessage.value = ''
+}
+</script>
+
+<template>
+  <div v-if="hasError" class="error-boundary">
+    <el-result icon="error" :title="t('common.error')" :sub-title="errorMessage">
+      <template #extra>
+        <el-button type="primary" @click="handleRetry">
+          {{ t('common.retry') }}
+        </el-button>
+      </template>
+    </el-result>
+  </div>
+  <slot v-else />
+</template>
+
+<style scoped lang="scss">
+.error-boundary {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 200px;
+  padding: var(--spacing-lg);
+}
+</style>

+ 62 - 0
src/components/common/InstallLog.vue

@@ -0,0 +1,62 @@
+<script setup lang="ts">
+import { ref, watch, nextTick } from 'vue'
+import { useInstallStore } from '@/stores/install'
+import { useI18n } from 'vue-i18n'
+
+const installStore = useInstallStore()
+const { t } = useI18n()
+const logContainer = ref<HTMLElement | null>(null)
+
+watch(() => installStore.installLogs.length, async () => {
+  await nextTick()
+  if (logContainer.value) {
+    logContainer.value.scrollTop = logContainer.value.scrollHeight
+  }
+})
+</script>
+
+<template>
+  <div class="install-log-wrapper">
+    <div class="log-header">
+      <span>{{ t('log.title') }}</span>
+      <el-button size="small" text @click="installStore.clearLogs">{{ t('log.clear') }}</el-button>
+    </div>
+    <div ref="logContainer" class="install-log">
+      <div v-if="installStore.installLogs.length === 0" class="log-empty">{{ t('log.empty') }}</div>
+      <div v-for="(log, index) in installStore.installLogs" :key="index" class="log-entry">{{ log }}</div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.install-log-wrapper {
+  .log-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: var(--spacing-sm);
+    font-weight: 500;
+  }
+}
+
+.install-log {
+  background-color: var(--card-bg-color);
+  border: 1px solid var(--border-color-light);
+  border-radius: var(--border-radius);
+  padding: var(--spacing-md);
+  max-height: 200px;
+  overflow-y: auto;
+  font-family: 'Consolas', 'Monaco', monospace;
+  font-size: 12px;
+  line-height: 1.6;
+
+  .log-empty {
+    color: var(--text-placeholder);
+    text-align: center;
+  }
+
+  .log-entry {
+    color: var(--text-secondary);
+  }
+}
+</style>

+ 99 - 0
src/components/common/MirrorSelector.vue

@@ -0,0 +1,99 @@
+<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+import { useSettingsStore } from '@/stores/settings'
+import { useVersionsStore } from '@/stores/versions'
+import SoftwareIcon from './SoftwareIcon.vue'
+import type { GitMirrorType, NodejsMirrorType } from '@/types/electron'
+
+const { t } = useI18n()
+const settingsStore = useSettingsStore()
+const versionsStore = useVersionsStore()
+
+const gitMirrorOptions: { value: GitMirrorType; label: string }[] = [
+  { value: 'huaweicloud', label: 'software.git.mirrorHuaweicloud' },
+  { value: 'github', label: 'software.git.mirrorGithub' }
+]
+
+const nodejsMirrorOptions: { value: NodejsMirrorType; label: string }[] = [
+  { value: 'official', label: 'software.nodejs.mirrorOfficial' },
+  { value: 'npmmirror', label: 'software.nodejs.mirrorNpmmirror' }
+]
+
+async function handleGitMirrorChange(mirror: GitMirrorType) {
+  await settingsStore.setGitMirror(mirror)
+  // 刷新 Git 版本列表
+  versionsStore.loadVersionForSoftware('git')
+}
+
+async function handleNodejsMirrorChange(mirror: NodejsMirrorType) {
+  await settingsStore.setNodejsMirror(mirror)
+  // 刷新 Node.js 版本列表
+  versionsStore.loadVersionForSoftware('nodejs')
+}
+</script>
+
+<template>
+  <div class="mirror-bar">
+    <div class="mirror-wrapper">
+      <SoftwareIcon software="nodejs" :size="16" />
+      <span class="mirror-label">{{ t('software.nodejs.mirror') }}:</span>
+      <el-select
+        :model-value="settingsStore.nodejsMirror"
+        @change="(val: NodejsMirrorType) => handleNodejsMirrorChange(val)"
+        class="mirror-select"
+        size="small"
+      >
+        <el-option
+          v-for="opt in nodejsMirrorOptions"
+          :key="opt.value"
+          :value="opt.value"
+          :label="t(opt.label)"
+        />
+      </el-select>
+    </div>
+    <div class="mirror-wrapper">
+      <SoftwareIcon software="git" :size="16" />
+      <span class="mirror-label">{{ t('software.git.mirror') }}:</span>
+      <el-select
+        :model-value="settingsStore.gitMirror"
+        @change="(val: GitMirrorType) => handleGitMirrorChange(val)"
+        class="mirror-select"
+        size="small"
+      >
+        <el-option
+          v-for="opt in gitMirrorOptions"
+          :key="opt.value"
+          :value="opt.value"
+          :label="t(opt.label)"
+        />
+      </el-select>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.mirror-bar {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+  margin-bottom: var(--spacing-md);
+  padding-bottom: var(--spacing-sm);
+  border-bottom: 1px solid var(--border-color-lighter);
+
+  .mirror-wrapper {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+
+    .mirror-label {
+      font-size: 12px;
+      color: var(--text-color-secondary);
+      white-space: nowrap;
+    }
+  }
+
+  .mirror-select {
+    width: 160px;
+  }
+}
+</style>

+ 42 - 0
src/components/common/NetworkStatus.vue

@@ -0,0 +1,42 @@
+<script setup lang="ts">
+import { useSystemStore } from '@/stores/system'
+import { useI18n } from 'vue-i18n'
+
+const systemStore = useSystemStore()
+const { t } = useI18n()
+</script>
+
+<template>
+  <div :class="['network-status', systemStore.isOnline ? 'online' : 'offline']">
+    <span class="status-dot"></span>
+    <span>{{ systemStore.isOnline ? t('common.online') : t('common.offline') }}</span>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.network-status {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  padding: var(--spacing-xs) var(--spacing-sm);
+  border-radius: var(--border-radius);
+  font-size: 12px;
+
+  &.online {
+    background-color: rgba(103, 194, 58, 0.1);
+    color: var(--el-color-success);
+  }
+
+  &.offline {
+    background-color: rgba(245, 108, 108, 0.1);
+    color: var(--el-color-danger);
+  }
+
+  .status-dot {
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background-color: currentColor;
+  }
+}
+</style>

+ 76 - 0
src/components/common/SettingsPanel.vue

@@ -0,0 +1,76 @@
+<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+import { useSettingsStore } from '@/stores/settings'
+import { setLocale, type LocaleType } from '@/i18n'
+import NetworkStatus from './NetworkStatus.vue'
+
+type ThemeType = 'light' | 'dark' | 'system'
+
+const { t } = useI18n()
+const settingsStore = useSettingsStore()
+
+const themeOptions: { value: ThemeType; label: string }[] = [
+  { value: 'light', label: 'settings.themeLight' },
+  { value: 'dark', label: 'settings.themeDark' },
+  { value: 'system', label: 'settings.themeSystem' }
+]
+
+const languageOptions: { value: LocaleType; label: string }[] = [
+  { value: 'zh-CN', label: '中文' },
+  { value: 'en-US', label: 'English' }
+]
+
+function handleThemeChange(theme: ThemeType) {
+  settingsStore.setTheme(theme)
+}
+
+function handleLanguageChange(locale: LocaleType) {
+  settingsStore.setLanguage(locale)
+  setLocale(locale)
+  window.electronAPI.setWindowTitle(t('app.title'))
+}
+</script>
+
+<template>
+  <div class="settings-panel">
+    <NetworkStatus />
+    <el-select
+      :model-value="settingsStore.theme"
+      @change="(val: ThemeType) => handleThemeChange(val)"
+      class="settings-select"
+      size="small"
+    >
+      <el-option
+        v-for="opt in themeOptions"
+        :key="opt.value"
+        :value="opt.value"
+        :label="t(opt.label)"
+      />
+    </el-select>
+    <el-select
+      :model-value="settingsStore.locale"
+      @change="handleLanguageChange"
+      class="settings-select"
+      size="small"
+    >
+      <el-option
+        v-for="opt in languageOptions"
+        :key="opt.value"
+        :value="opt.value"
+        :label="opt.label"
+      />
+    </el-select>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.settings-panel {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+
+  .settings-select {
+    width: 110px;
+  }
+}
+</style>

+ 24 - 0
src/components/common/SkeletonLoader.vue

@@ -0,0 +1,24 @@
+<script setup lang="ts">
+// 骨架屏加载组件
+defineProps<{
+  loading: boolean
+}>()
+</script>
+
+<template>
+  <div v-if="loading" class="skeleton-wrapper">
+    <div class="skeleton skeleton-text medium"></div>
+    <div class="skeleton skeleton-select"></div>
+    <div class="skeleton skeleton-text short"></div>
+    <div class="skeleton skeleton-button"></div>
+  </div>
+  <slot v-else></slot>
+</template>
+
+<style scoped lang="scss">
+.skeleton-wrapper {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-md);
+}
+</style>

+ 84 - 0
src/components/common/SoftwareIcon.vue

@@ -0,0 +1,84 @@
+<script setup lang="ts">
+import type { SoftwareType } from '@/types/electron'
+
+defineProps<{
+  software: SoftwareType | 'all' | 'intro'
+  size?: number
+}>()
+</script>
+
+<template>
+  <span class="software-icon" :style="{ width: `${size || 20}px`, height: `${size || 20}px` }">
+    <!-- Node.js -->
+    <svg v-if="software === 'nodejs'" viewBox="0 0 256 289" xmlns="http://www.w3.org/2000/svg">
+      <path d="M128 288.464c-3.975 0-7.685-1.06-11.13-2.915l-35.247-20.936c-5.3-2.915-2.65-3.975-1.06-4.505 7.155-2.385 8.48-2.915 15.9-7.156.796-.53 1.856-.265 2.65.265l27.032 16.166c1.06.53 2.385.53 3.18 0l105.74-61.217c1.06-.53 1.59-1.59 1.59-2.915V83.08c0-1.325-.53-2.385-1.59-2.915l-105.74-60.953c-1.06-.53-2.385-.53-3.18 0L20.705 80.166c-1.06.53-1.59 1.855-1.59 2.915v122.17c0 1.06.53 2.385 1.59 2.915l28.887 16.695c15.636 7.95 25.44-1.325 25.44-10.6V93.68c0-1.59 1.326-3.18 3.181-3.18h13.516c1.59 0 3.18 1.325 3.18 3.18v120.58c0 20.936-11.396 33.126-31.272 33.126-6.095 0-10.865 0-24.38-6.625l-27.827-15.9C4.24 220.355 0 212.404 0 203.983V81.814c0-8.42 4.24-16.37 11.13-20.61L116.87 0c6.625-3.71 15.635-3.71 22.26 0l105.74 61.204c6.89 3.975 11.13 12.16 11.13 20.61v122.17c0 8.42-4.24 16.37-11.13 20.61l-105.74 61.217c-3.445 1.855-7.42 2.65-11.13 2.65zm32.596-84.009c-46.377 0-55.917-21.2-55.917-39.221 0-1.59 1.325-3.18 3.18-3.18h13.78c1.59 0 2.916 1.06 2.916 2.65 2.12 14.045 8.215 20.936 36.307 20.936 22.26 0 31.802-5.035 31.802-16.96 0-6.891-2.65-11.926-37.367-15.372-28.887-2.915-46.907-9.275-46.907-32.33 0-21.467 18.02-34.187 48.232-34.187 33.921 0 50.617 11.66 52.737 37.102 0 .795-.265 1.59-.795 2.385-.53.53-1.325 1.06-2.12 1.06h-13.78c-1.326 0-2.65-1.06-2.916-2.385-3.18-14.575-11.395-19.345-33.126-19.345-24.38 0-27.296 8.48-27.296 14.84 0 7.686 3.445 10.07 36.307 14.31 32.596 4.24 47.967 10.336 47.967 33.127-.265 23.175-19.345 36.572-53.002 36.572z" fill="#539E43"/>
+    </svg>
+
+    <!-- VS Code -->
+    <svg v-else-if="software === 'vscode'" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
+      <defs>
+        <linearGradient id="vscode-a" x1="50%" x2="50%" y1="0%" y2="100%">
+          <stop offset="0%" stop-color="#FFF"/>
+          <stop offset="100%" stop-color="#FFF" stop-opacity=".6"/>
+        </linearGradient>
+        <linearGradient id="vscode-b" x1="50%" x2="50%" y1="0%" y2="100%">
+          <stop offset="0%" stop-color="#FFF" stop-opacity=".6"/>
+          <stop offset="100%" stop-color="#FFF"/>
+        </linearGradient>
+      </defs>
+      <mask id="vscode-c">
+        <path fill="#fff" d="M180.828 252.605a15.872 15.872 0 0 0 12.65.486l52.501-20.014a15.94 15.94 0 0 0 10.02-14.787V37.71a15.939 15.939 0 0 0-10.02-14.787L193.477 2.909a15.876 15.876 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.857 15.857 0 0 0 5.493 3.897zm10.588-183.391-77.417 58.786 77.417 58.787V69.214z"/>
+      </mask>
+      <path fill="#0065A9" d="M246.135 26.873 193.593 6.863a15.886 15.886 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.875 15.875 0 0 0 18.142 3.627l52.542-20.01a15.938 15.938 0 0 0 10.02-14.787V41.66a15.939 15.939 0 0 0-10.02-14.787z" mask="url(#vscode-c)"/>
+      <g mask="url(#vscode-c)">
+        <path fill="#0065A9" d="M246.135 26.873 193.593 6.863a15.886 15.886 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.875 15.875 0 0 0 18.142 3.627l52.542-20.01a15.938 15.938 0 0 0 10.02-14.787V41.66a15.939 15.939 0 0 0-10.02-14.787z"/>
+        <path fill="url(#vscode-a)" fill-opacity=".25" d="M180.828 252.605a15.874 15.874 0 0 0 12.65.486l52.5-20.014a15.938 15.938 0 0 0 10.022-14.787V37.71a15.939 15.939 0 0 0-10.021-14.787L193.477 2.909a15.877 15.877 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.857 15.857 0 0 0 5.493 3.897z"/>
+      </g>
+      <g mask="url(#vscode-c)">
+        <path fill="#007ACC" d="m180.828 252.605 65.308-24.787a15.938 15.938 0 0 0 10.02-14.787V41.66a15.939 15.939 0 0 0-10.02-14.787L193.593 6.863a15.886 15.886 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.857 15.857 0 0 0 5.493 3.897z"/>
+        <path fill="url(#vscode-b)" fill-opacity=".25" d="m180.828 252.605 65.308-24.787a15.938 15.938 0 0 0 10.02-14.787V41.66a15.939 15.939 0 0 0-10.02-14.787L193.593 6.863a15.886 15.886 0 0 0-18.142 3.627L73.6 97.391 30.473 65.073a10.627 10.627 0 0 0-13.584.637l-14.111 12.84a10.628 10.628 0 0 0-.002 15.7l37.63 34.377-37.63 34.378a10.628 10.628 0 0 0 .002 15.7l14.11 12.839a10.627 10.627 0 0 0 13.585.637l43.127-32.318 101.735 90.855a15.857 15.857 0 0 0 5.493 3.897z"/>
+      </g>
+    </svg>
+
+    <!-- Git -->
+    <svg v-else-if="software === 'git'" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
+      <path fill="#F05133" d="m251.172 116.594-111.766-111.766a16.46 16.46 0 0 0-23.28 0l-23.21 23.21 29.428 29.428a19.554 19.554 0 0 1 24.752 24.932l28.356 28.356a19.554 19.554 0 1 1-11.716 11.024l-26.448-26.448v69.596a19.554 19.554 0 1 1-16.088-.572V95.14a19.554 19.554 0 0 1-10.628-25.66L81.2 40.2 4.828 116.594a16.46 16.46 0 0 0 0 23.28l111.766 111.766a16.46 16.46 0 0 0 23.28 0l111.298-111.298a16.46 16.46 0 0 0 0-23.28"/>
+    </svg>
+
+    <!-- pnpm -->
+    <svg v-else-if="software === 'pnpm'" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
+      <path fill="#F9AD00" d="M0 0h78v78H0zm89 0h78v78H89zm89 0h78v78h-78zM89 89h78v78H89zm89 0h78v78h-78zM0 178h78v78H0zm89 0h78v78H89zm89 0h78v78h-78z"/>
+    </svg>
+
+    <!-- Claude Code -->
+    <svg v-else-if="software === 'claudeCode'" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
+      <rect width="256" height="256" rx="40" fill="#D97757"/>
+      <path fill="#FFF" d="M162.5 128c0 19.054-15.446 34.5-34.5 34.5S93.5 147.054 93.5 128 108.946 93.5 128 93.5s34.5 15.446 34.5 34.5zm-34.5-52c-28.719 0-52 23.281-52 52s23.281 52 52 52 52-23.281 52-52-23.281-52-52-52z"/>
+      <circle cx="128" cy="128" r="20" fill="#FFF"/>
+    </svg>
+
+    <!-- All (批量安装) -->
+    <svg v-else-if="software === 'all'" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+      <path fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
+    </svg>
+
+    <!-- Intro (介绍) -->
+    <svg v-else-if="software === 'intro'" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+      <path fill="currentColor" d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>
+    </svg>
+  </span>
+</template>
+
+<style scoped>
+.software-icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+.software-icon svg {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 128 - 0
src/components/common/TitleBar.vue

@@ -0,0 +1,128 @@
+<script setup lang="ts">
+import { ref, onMounted, onUnmounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+const isMaximized = ref(false)
+
+async function handleMinimize() {
+  await window.electronAPI.windowMinimize()
+}
+
+async function handleMaximize() {
+  isMaximized.value = await window.electronAPI.windowMaximize()
+}
+
+async function handleClose() {
+  await window.electronAPI.windowClose()
+}
+
+async function checkMaximized() {
+  isMaximized.value = await window.electronAPI.windowIsMaximized()
+}
+
+// 监听窗口大小变化
+function onResize() {
+  checkMaximized()
+}
+
+onMounted(() => {
+  checkMaximized()
+  window.addEventListener('resize', onResize)
+})
+
+onUnmounted(() => {
+  window.removeEventListener('resize', onResize)
+})
+</script>
+
+<template>
+  <div class="title-bar">
+    <div class="title-bar-drag">
+      <img src="/icons/png/32x32.png" alt="icon" class="title-icon" />
+      <span class="title">{{ t('app.title') }}</span>
+    </div>
+    <div class="title-bar-controls">
+      <button class="control-btn minimize" @click="handleMinimize" :title="t('titleBar.minimize')">
+        <svg width="12" height="12" viewBox="0 0 12 12">
+          <rect x="1" y="5.5" width="10" height="1" fill="currentColor" />
+        </svg>
+      </button>
+      <button class="control-btn maximize" @click="handleMaximize" :title="isMaximized ? t('titleBar.restore') : t('titleBar.maximize')">
+        <svg v-if="!isMaximized" width="12" height="12" viewBox="0 0 12 12">
+          <rect x="1.5" y="1.5" width="9" height="9" fill="none" stroke="currentColor" stroke-width="1" />
+        </svg>
+        <svg v-else width="12" height="12" viewBox="0 0 12 12">
+          <rect x="3" y="0.5" width="8" height="8" fill="none" stroke="currentColor" stroke-width="1" />
+          <rect x="0.5" y="3" width="8" height="8" fill="var(--panel-bg-color)" stroke="currentColor" stroke-width="1" />
+        </svg>
+      </button>
+      <button class="control-btn close" @click="handleClose" :title="t('titleBar.close')">
+        <svg width="12" height="12" viewBox="0 0 12 12">
+          <path d="M1 1L11 11M11 1L1 11" stroke="currentColor" stroke-width="1.2" />
+        </svg>
+      </button>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.title-bar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 32px;
+  background-color: var(--panel-bg-color);
+  border-bottom: 1px solid var(--border-color-light);
+  user-select: none;
+}
+
+.title-bar-drag {
+  flex: 1;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  padding-left: 12px;
+  -webkit-app-region: drag;
+}
+
+.title-icon {
+  width: 18px;
+  height: 18px;
+  margin-right: 8px;
+}
+
+.title {
+  font-size: 13px;
+  font-weight: 500;
+  color: var(--text-primary);
+}
+
+.title-bar-controls {
+  display: flex;
+  height: 100%;
+  -webkit-app-region: no-drag;
+}
+
+.control-btn {
+  width: 46px;
+  height: 100%;
+  border: none;
+  background: transparent;
+  color: var(--text-regular);
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: background-color 0.15s;
+
+  &:hover {
+    background-color: var(--border-color-lighter);
+  }
+
+  &.close:hover {
+    background-color: #e81123;
+    color: white;
+  }
+}
+</style>

+ 204 - 0
src/i18n/en-US.ts

@@ -0,0 +1,204 @@
+// src/i18n/en-US.ts - English language pack
+
+export default {
+  common: {
+    loading: 'Loading...',
+    ready: 'Ready',
+    error: 'Error',
+    warning: 'Warning',
+    success: 'Success',
+    info: 'Info',
+    confirm: 'Confirm',
+    cancel: 'Cancel',
+    close: 'Close',
+    retry: 'Retry',
+    install: 'Install',
+    uninstall: 'Uninstall',
+    installing: 'Installing...',
+    installed: 'Installed',
+    notInstalled: 'Not Installed',
+    version: 'Version',
+    selectVersion: 'Select Version',
+    loadFailed: 'Load Failed',
+    networkError: 'Network Error',
+    offline: 'No Network',
+    online: 'Connected'
+  },
+  app: {
+    title: 'Claude AI Installer',
+    subtitle: 'One-stop installation for frontend development tools'
+  },
+  tabs: {
+    intro: 'Introduction',
+    nodejs: 'Node.js',
+    vscode: 'VS Code',
+    git: 'Git',
+    claudeCode: 'Claude Code',
+    all: 'Install All'
+  },
+  intro: {
+    welcome: 'Welcome to Claude AI Installer',
+    subtitle: 'One-stop installation for frontend development tools',
+    nodejsDesc: 'JavaScript runtime environment with multi-version support and optional pnpm package manager',
+    vscodeDesc: 'Lightweight but powerful code editor, supports stable and insider versions',
+    gitDesc: 'Distributed version control system, supports full, minimal, and LFS versions',
+    claudeCodeDesc: 'Anthropic official AI programming assistant, interact with Claude in terminal for coding',
+    oneClickDesc: 'Automatically install all missing tools at once',
+    tip: 'Tip: Version lists are queried from local package manager, showing installable versions'
+  },
+  software: {
+    nodejs: {
+      name: 'Node.js',
+      description: 'JavaScript runtime environment for server-side development',
+      installPnpm: 'Also install pnpm (fast package manager) [Recommended for China users]',
+      pnpmRequiresNodejs: 'Requires Node.js to be installed first',
+      mirror: 'Node.js Download Source',
+      mirrorOfficial: 'Node.js Official',
+      mirrorNpmmirror: 'npmmirror (China)',
+      pnpmTitle: 'pnpm Package Manager',
+      pnpmDesc: 'Fast, disk space efficient package manager [Recommended for China users]',
+      installPnpmBtn: 'Install pnpm',
+      installNodejsFirst: 'Please install Node.js first'
+    },
+    vscode: {
+      name: 'Visual Studio Code',
+      description: 'Lightweight but powerful code editor',
+      claudeCodeExt: 'Claude Code for VS Code Extension',
+      claudeCodeExtDesc: 'Use Claude Code\'s powerful features directly in VS Code without leaving your editor',
+      claudeCodeExtRequiresCli: 'Requires Claude Code to be installed first (install in Claude Code tab)',
+      claudeCodeExtRequiresVscodeAndCli: 'Requires VS Code and Claude Code',
+      installClaudeCodeExt: 'Install Extension',
+      installVscodeFirst: 'Please install VS Code first',
+      installingClaudeCodeExt: 'Installing Claude Code for VS Code extension...',
+      claudeCodeExtInstallSuccess: 'Claude Code for VS Code extension installed successfully',
+      claudeCodeExtInstallFailed: 'Claude Code for VS Code extension installation failed'
+    },
+    git: {
+      name: 'Git',
+      description: 'Distributed version control system',
+      mirror: 'Git Download Source',
+      mirrorHuaweicloud: 'Huawei Cloud Mirror (China Recommended)',
+      mirrorGithub: 'GitHub Official (Proxy Required)'
+    },
+    all: {
+      name: 'Install All',
+      description: 'Customize installation of Node.js, VS Code, Git'
+    },
+    claudeCode: {
+      name: 'Claude Code',
+      description: 'Anthropic official AI programming assistant CLI tool'
+    }
+  },
+  claudeCode: {
+    launch: 'Launch Claude Code',
+    launching: 'Launching...',
+    launchSuccess: 'Git Bash launched, claude command executed',
+    launchFailed: 'Launch failed',
+    install: 'Install Claude Code',
+    installing: 'Installing...',
+    installFailed: 'Installation failed',
+    nodejsRequired: 'Node.js installation required',
+    nodejsRequiredDesc: 'Please install Node.js first in the Node.js tab before installing Claude Code',
+    gitRequired: 'Git installation required',
+    gitRequiredDesc: 'Please install Git first in the Git tab before launching Claude Code',
+    infoTitle: 'Instructions',
+    infoDesc: 'Click the button below to open Git Bash terminal and automatically execute the claude command.',
+    notInstalled: 'Claude Code not detected',
+    installHint: 'Please run npm install -g @anthropic-ai/claude-code to install Claude Code first',
+    installHintWithButton: 'Click the "Install Claude Code" button below to install',
+    refresh: 'Refresh Status',
+    requiresNodejsGit: 'Requires Node.js and Git'
+  },
+  install: {
+    start: 'Install',
+    startAll: 'Start Installation',
+    installing: 'Installing...',
+    complete: 'Installation Complete',
+    failed: 'Installation Failed',
+    cancelled: 'Installation Cancelled',
+    reinstall: 'Reinstall',
+    preparing: 'Preparing installation...',
+    configuring: 'Configuring',
+    updatingSource: 'Updating package sources...',
+    allInstalled: 'All Installed',
+    allInstalledHint: 'All software is already installed',
+    noSelection: 'Select Software',
+    noSelectionHint: 'Please select software to install',
+    customPath: 'Install Path',
+    customPathPlaceholder: 'Leave empty for default path',
+    browse: 'Browse...'
+  },
+  settings: {
+    title: 'Settings',
+    theme: 'Theme',
+    themeLight: 'Light',
+    themeDark: 'Dark',
+    themeSystem: 'System',
+    language: 'Language'
+  },
+  system: {
+    noAdmin: 'Administrator privileges not detected, authorization may be required during installation',
+    noPackageManager: '{manager} not detected, installation required',
+    installManager: 'Install {manager}',
+    managerInstalled: '{manager} installed successfully!',
+    managerFailed: '{manager} installation failed: {error}'
+  },
+  update: {
+    checking: 'Checking for updates...',
+    available: 'New version {version} available',
+    upToDate: 'Already up to date',
+    updateNow: 'Update Now'
+  },
+  history: {
+    title: 'Installation History',
+    empty: 'No installation records',
+    clear: 'Clear History'
+  },
+  log: {
+    title: 'Installation Log',
+    empty: 'No logs',
+    clear: 'Clear Log',
+    // Installation log messages
+    startInstalling: 'Starting installation of {software}...',
+    executingCommand: 'Executing command: {command}',
+    installingClaudeCode: 'Installing Claude Code...',
+    installComplete: 'Installation complete, verifying...',
+    claudeCodeInstallSuccess: 'Claude Code installed successfully',
+    claudeCodeInstallSuccessNeedRestart: 'Claude Code installed (may need to restart terminal to use)',
+    claudeCodeInstallFailed: 'Claude Code installation failed',
+    npmInstallStdout: 'npm output: {output}',
+    npmInstallStderr: 'npm warning: {output}',
+    // VS Code extension installation messages
+    vscodeExtInstalling: 'Installing VS Code extension: {extensionId}...',
+    vscodeExtOutput: 'code output: {output}',
+    vscodeExtInstallSuccess: 'VS Code extension installed successfully: {extensionId}',
+    vscodeExtInstallFailed: 'VS Code extension installation failed: {extensionId}',
+    // Installation complete messages
+    nodejsComplete: 'Node.js installation complete!',
+    nodejsPnpmComplete: 'Node.js + pnpm installation complete!',
+    pnpmComplete: 'pnpm installation complete!',
+    vscodeComplete: 'VS Code installation complete!',
+    gitComplete: 'Git installation complete!',
+    allComplete: '{software} installation complete!',
+    installCancelled: 'Installation cancelled',
+    installError: 'Installation failed: {error}'
+  },
+  error: {
+    installFailed: 'Installation Failed',
+    unknownError: 'Unknown error, please check permissions/network',
+    windowsOnly: 'This feature is only supported on Windows',
+    gitBashNotFound: 'Git Bash not found, please make sure Git is installed'
+  },
+  queue: {
+    title: 'Installation Queue',
+    empty: 'Queue is empty',
+    clear: 'Clear Queue',
+    processing: 'Processing...'
+  },
+  titleBar: {
+    minimize: 'Minimize',
+    maximize: 'Maximize',
+    restore: 'Restore',
+    close: 'Close'
+  }
+}

+ 41 - 0
src/i18n/index.ts

@@ -0,0 +1,41 @@
+// src/i18n/index.ts - 国际化配置
+
+import { createI18n } from 'vue-i18n'
+import zhCN from './zh-CN'
+import enUS from './en-US'
+
+export type LocaleType = 'zh-CN' | 'en-US'
+
+const messages = {
+  'zh-CN': zhCN,
+  'en-US': enUS
+}
+
+// 获取默认语言(默认中文)
+function getDefaultLocale(): LocaleType {
+  const savedLocale = localStorage.getItem('locale') as LocaleType
+  if (savedLocale && ['zh-CN', 'en-US'].includes(savedLocale)) {
+    return savedLocale
+  }
+  // 默认使用中文
+  return 'zh-CN'
+}
+
+const i18n = createI18n({
+  legacy: false, // 使用 Composition API 模式
+  locale: getDefaultLocale(),
+  fallbackLocale: 'zh-CN', // 默认回退到中文
+  messages
+})
+
+export function setLocale(locale: LocaleType): void {
+  i18n.global.locale.value = locale
+  localStorage.setItem('locale', locale)
+  document.documentElement.setAttribute('lang', locale)
+}
+
+export function getLocale(): LocaleType {
+  return i18n.global.locale.value as LocaleType
+}
+
+export default i18n

+ 204 - 0
src/i18n/zh-CN.ts

@@ -0,0 +1,204 @@
+// src/i18n/zh-CN.ts - 中文语言包
+
+export default {
+  common: {
+    loading: '加载中...',
+    ready: '就绪',
+    error: '错误',
+    warning: '警告',
+    success: '成功',
+    info: '提示',
+    confirm: '确认',
+    cancel: '取消',
+    close: '关闭',
+    retry: '重试',
+    install: '安装',
+    uninstall: '卸载',
+    installing: '安装中...',
+    installed: '已安装',
+    notInstalled: '未安装',
+    version: '版本',
+    selectVersion: '选择版本',
+    loadFailed: '加载失败',
+    networkError: '网络连接失败',
+    offline: '无网络',
+    online: '已联网'
+  },
+  app: {
+    title: 'Claude AI安装器',
+    subtitle: '一站式安装前端开发必备工具'
+  },
+  tabs: {
+    intro: '功能介绍',
+    nodejs: 'Node.js',
+    vscode: 'VS Code',
+    git: 'Git',
+    claudeCode: 'Claude Code',
+    all: '一键全装'
+  },
+  intro: {
+    welcome: '欢迎使用Claude AI安装器',
+    subtitle: '一站式安装前端开发必备工具',
+    nodejsDesc: 'JavaScript 运行时环境,支持多版本选择,可同时安装 pnpm 包管理器',
+    vscodeDesc: '轻量级但功能强大的代码编辑器,支持稳定版和预览版',
+    gitDesc: '分布式版本控制系统,支持完整版、精简版和 LFS 扩展',
+    claudeCodeDesc: 'Anthropic 官方 AI 编程助手,在终端中与 Claude 交互进行代码开发',
+    oneClickDesc: '自动安装所有未安装的工具,一次性完成全部配置',
+    tip: '提示:版本列表通过本地包管理器查询,显示的是可安装的版本'
+  },
+  software: {
+    nodejs: {
+      name: 'Node.js',
+      description: 'JavaScript 运行时环境,用于服务端开发',
+      installPnpm: '同时安装 pnpm (快速包管理器) [国内用户推荐]',
+      pnpmRequiresNodejs: '需要先安装 Node.js',
+      mirror: '选择Node.js下载源',
+      mirrorOfficial: 'Node.js 官方',
+      mirrorNpmmirror: 'npmmirror 镜像',
+      pnpmTitle: 'pnpm 包管理器',
+      pnpmDesc: '快速、节省磁盘空间的包管理器 [国内用户推荐]',
+      installPnpmBtn: '安装 pnpm',
+      installNodejsFirst: '请先安装 Node.js'
+    },
+    vscode: {
+      name: 'Visual Studio Code',
+      description: '轻量级但功能强大的代码编辑器',
+      claudeCodeExt: 'Claude Code for VS Code 插件',
+      claudeCodeExtDesc: '在 VS Code 中使用 Claude Code 的强大功能,无需离开编辑器',
+      claudeCodeExtRequiresCli: '需要先安装 Claude Code(在 Claude Code 标签页安装)',
+      claudeCodeExtRequiresVscodeAndCli: '需要 VS Code 和 Claude Code',
+      installClaudeCodeExt: '安装插件',
+      installVscodeFirst: '请先安装 VS Code',
+      installingClaudeCodeExt: '正在安装 Claude Code for VS Code 插件...',
+      claudeCodeExtInstallSuccess: 'Claude Code for VS Code 插件安装成功',
+      claudeCodeExtInstallFailed: 'Claude Code for VS Code 插件安装失败'
+    },
+    git: {
+      name: 'Git',
+      description: '分布式版本控制系统',
+      mirror: '选择Git下载源',
+      mirrorHuaweicloud: '华为云镜像(国内推荐)',
+      mirrorGithub: 'GitHub 官方(需代理)'
+    },
+    all: {
+      name: '一键安装全部',
+      description: '自定义安装 Node.js、VS Code、Git'
+    },
+    claudeCode: {
+      name: 'Claude Code',
+      description: 'Anthropic 官方 AI 编程助手命令行工具'
+    }
+  },
+  claudeCode: {
+    launch: '启动 Claude Code',
+    launching: '正在启动...',
+    launchSuccess: 'Git Bash 已启动,claude 命令已执行',
+    launchFailed: '启动失败',
+    install: '安装 Claude Code',
+    installing: '正在安装...',
+    installFailed: '安装失败',
+    nodejsRequired: '需要先安装 Node.js',
+    nodejsRequiredDesc: '请先在 Node.js 标签页安装 Node.js,安装完成后才能安装 Claude Code',
+    gitRequired: '需要先安装 Git',
+    gitRequiredDesc: '请先在 Git 标签页安装 Git,安装完成后才能启动 Claude Code',
+    infoTitle: '使用说明',
+    infoDesc: '点击下方按钮将打开 Git Bash 终端并自动执行 claude 命令。',
+    notInstalled: '未检测到 Claude Code',
+    installHint: '请先运行 npm install -g @anthropic-ai/claude-code 安装 Claude Code',
+    installHintWithButton: '点击下方"安装 Claude Code"按钮进行安装',
+    refresh: '刷新状态',
+    requiresNodejsGit: '需要 Node.js 和 Git'
+  },
+  install: {
+    start: '开始安装',
+    startAll: '开始一键安装',
+    installing: '安装中...',
+    complete: '安装完成',
+    failed: '安装失败',
+    cancelled: '安装已取消',
+    reinstall: '重新安装',
+    preparing: '正在准备安装...',
+    configuring: '正在配置',
+    updatingSource: '正在更新软件源...',
+    allInstalled: '全部已安装',
+    allInstalledHint: '所有软件均已安装',
+    noSelection: '请选择软件',
+    noSelectionHint: '请勾选要安装的软件',
+    customPath: '安装路径',
+    customPathPlaceholder: '留空使用默认路径',
+    browse: '浏览...'
+  },
+  settings: {
+    title: '设置',
+    theme: '主题',
+    themeLight: '浅色',
+    themeDark: '深色',
+    themeSystem: '跟随系统',
+    language: '语言'
+  },
+  system: {
+    noAdmin: '未检测到管理员权限,安装时可能需要授权',
+    noPackageManager: '未检测到 {manager},需要先安装',
+    installManager: '安装 {manager}',
+    managerInstalled: '{manager} 安装成功!',
+    managerFailed: '{manager} 安装失败:{error}'
+  },
+  update: {
+    checking: '正在检查更新...',
+    available: '发现新版本 {version}',
+    upToDate: '已是最新版本',
+    updateNow: '立即更新'
+  },
+  history: {
+    title: '安装历史',
+    empty: '暂无安装记录',
+    clear: '清空历史'
+  },
+  log: {
+    title: '安装日志',
+    empty: '暂无日志',
+    clear: '清空日志',
+    // 安装日志消息
+    startInstalling: '开始安装 {software}...',
+    executingCommand: '执行命令: {command}',
+    installingClaudeCode: '正在安装 Claude Code...',
+    installComplete: '安装完成,正在验证...',
+    claudeCodeInstallSuccess: 'Claude Code 安装成功',
+    claudeCodeInstallSuccessNeedRestart: 'Claude Code 安装完成(可能需要重启终端才能使用)',
+    claudeCodeInstallFailed: 'Claude Code 安装失败',
+    npmInstallStdout: 'npm 输出: {output}',
+    npmInstallStderr: 'npm 警告: {output}',
+    // VS Code 插件安装消息
+    vscodeExtInstalling: '正在安装 VS Code 插件: {extensionId}...',
+    vscodeExtOutput: 'code 输出: {output}',
+    vscodeExtInstallSuccess: 'VS Code 插件安装成功: {extensionId}',
+    vscodeExtInstallFailed: 'VS Code 插件安装失败: {extensionId}',
+    // 安装完成消息
+    nodejsComplete: 'Node.js 安装完成!',
+    nodejsPnpmComplete: 'Node.js + pnpm 安装完成!',
+    pnpmComplete: 'pnpm 安装完成!',
+    vscodeComplete: 'VS Code 安装完成!',
+    gitComplete: 'Git 安装完成!',
+    allComplete: '{software} 安装完成!',
+    installCancelled: '安装已取消',
+    installError: '安装失败:{error}'
+  },
+  error: {
+    installFailed: '安装失败',
+    unknownError: '未知错误,请检查权限/网络',
+    windowsOnly: '此功能仅支持 Windows 系统',
+    gitBashNotFound: '未找到 Git Bash,请确保已安装 Git'
+  },
+  queue: {
+    title: '安装队列',
+    empty: '队列为空',
+    clear: '清空队列',
+    processing: '正在处理...'
+  },
+  titleBar: {
+    minimize: '最小化',
+    maximize: '最大化',
+    restore: '还原',
+    close: '关闭'
+  }
+}

+ 28 - 0
src/main.ts

@@ -0,0 +1,28 @@
+// src/main.ts - Vue 应用入口
+
+import { createApp } from 'vue'
+import ElementPlus from 'element-plus'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import 'element-plus/dist/index.css'
+import 'element-plus/theme-chalk/dark/css-vars.css'
+
+import App from './App.vue'
+import { pinia } from './stores'
+import i18n from './i18n'
+import './styles/main.scss'
+
+// 创建应用
+const app = createApp(App)
+
+// 注册 Element Plus 图标
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component)
+}
+
+// 使用插件
+app.use(pinia)
+app.use(i18n)
+app.use(ElementPlus)
+
+// 挂载应用
+app.mount('#app')

+ 10 - 0
src/stores/index.ts

@@ -0,0 +1,10 @@
+// src/stores/index.ts - Pinia 状态管理入口
+
+import { createPinia } from 'pinia'
+
+export const pinia = createPinia()
+
+export * from './versions'
+export * from './install'
+export * from './settings'
+export * from './system'

+ 271 - 0
src/stores/install.ts

@@ -0,0 +1,271 @@
+// src/stores/install.ts - 安装状态管理
+
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { ElMessage } from 'element-plus'
+import type { SoftwareType, SoftwareTypeWithAll, AllInstalledInfo, InstallOptions, InstallHistoryItem } from '@/types/electron'
+import i18n from '@/i18n'
+
+// 获取翻译文本的辅助函数
+const t = (key: string) => i18n.global.t(key)
+
+// 错误通知辅助函数
+const notifyError = (message: string): void => {
+  ElMessage.error(message)
+  console.error(message)
+}
+
+interface InstallStatusState {
+  installing: boolean
+  message: string
+  progress: number
+  success: boolean
+  error: boolean
+}
+
+interface InstallOptionsState {
+  nodejs: { customPath?: string }
+  vscode: { customPath?: string }
+  git: { customPath?: string }
+  all: {
+    installNodejs: boolean
+    installVscode: boolean
+    installGit: boolean
+    installPnpm: boolean
+    installClaudeCode: boolean
+    installClaudeCodeExt: boolean
+    nodejsPath?: string
+    vscodePath?: string
+    gitPath?: string
+  }
+}
+
+// 创建默认状态的辅助函数
+const createDefaultStatus = (): InstallStatusState => ({
+  installing: false,
+  message: t('common.ready'),
+  progress: 0,
+  success: false,
+  error: false
+})
+
+export const useInstallStore = defineStore('install', () => {
+  // 安装状态
+  const installStatus = ref<Record<SoftwareTypeWithAll, InstallStatusState>>({
+    nodejs: createDefaultStatus(),
+    pnpm: createDefaultStatus(),
+    vscode: createDefaultStatus(),
+    git: createDefaultStatus(),
+    claudeCode: createDefaultStatus(),
+    all: createDefaultStatus()
+  })
+
+  // 已安装信息
+  const installedInfo = ref<AllInstalledInfo>({
+    nodejs: { installed: false, version: null },
+    pnpm: { installed: false, version: null },
+    vscode: { installed: false, version: null },
+    git: { installed: false, version: null },
+    claudeCode: { installed: false, version: null }
+  })
+
+  // Claude Code VS Code 扩展安装状态(单独管理,因为不是通过 checkInstalled API 获取)
+  const claudeCodeExtInfo = ref<{ installed: boolean; version: string | null }>({
+    installed: false,
+    version: null
+  })
+
+  // 安装选项
+  const installOptions = ref<InstallOptionsState>({
+    nodejs: {},
+    vscode: {},
+    git: {},
+    all: {
+      installNodejs: true,
+      installVscode: true,
+      installGit: true,
+      installPnpm: true,
+      installClaudeCode: true,
+      installClaudeCodeExt: true
+    }
+  })
+
+  // 安装历史
+  const installHistory = ref<InstallHistoryItem[]>([])
+
+  // 安装日志
+  const installLogs = ref<string[]>([])
+
+  // 计算属性
+  const isAnyInstalling = computed(() => {
+    return Object.values(installStatus.value).some((s) => s.installing)
+  })
+
+  // 方法
+  function updateStatus(software: SoftwareTypeWithAll, message: string, progress: number): void {
+    installStatus.value[software].message = message
+    installStatus.value[software].progress = progress
+  }
+
+  function setInstalling(software: SoftwareTypeWithAll, installing: boolean): void {
+    installStatus.value[software].installing = installing
+    if (installing) {
+      installStatus.value[software].success = false
+      installStatus.value[software].error = false
+    }
+  }
+
+  function setSuccess(software: SoftwareTypeWithAll, message: string): void {
+    installStatus.value[software].installing = false
+    installStatus.value[software].success = true
+    installStatus.value[software].error = false
+    installStatus.value[software].message = message
+    installStatus.value[software].progress = 100
+  }
+
+  function setError(software: SoftwareTypeWithAll, message: string): void {
+    installStatus.value[software].installing = false
+    installStatus.value[software].success = false
+    installStatus.value[software].error = true
+    installStatus.value[software].message = message
+  }
+
+  function resetStatus(software: SoftwareTypeWithAll): void {
+    installStatus.value[software] = createDefaultStatus()
+  }
+
+  async function checkInstalledSoftware(): Promise<void> {
+    try {
+      const result = await window.electronAPI.checkInstalled('all') as AllInstalledInfo
+      if (result) {
+        installedInfo.value = result
+      }
+    } catch (error) {
+      const errorMsg = error instanceof Error ? error.message : String(error)
+      notifyError(`${t('common.error')}: ${errorMsg}`)
+    }
+  }
+
+  async function doInstall(software: SoftwareTypeWithAll, options: InstallOptions): Promise<void> {
+    setInstalling(software, true)
+    updateStatus(software, t('install.preparing'), 0)
+
+    try {
+      await window.electronAPI.install(software, options)
+    } catch (error) {
+      const errorMsg = error instanceof Error ? error.message : String(error)
+      setError(software, `${t('install.failed')}: ${errorMsg}`)
+      notifyError(`${t('install.failed')}: ${errorMsg}`)
+    }
+  }
+
+  async function cancelInstall(): Promise<boolean> {
+    try {
+      return await window.electronAPI.cancelInstall()
+    } catch (error) {
+      const errorMsg = error instanceof Error ? error.message : String(error)
+      notifyError(`${t('common.error')}: ${errorMsg}`)
+      return false
+    }
+  }
+
+  async function uninstallSoftware(software: SoftwareType): Promise<boolean> {
+    try {
+      const result = await window.electronAPI.uninstall(software)
+      if (result) {
+        installedInfo.value[software] = { installed: false, version: null }
+      }
+      return result
+    } catch (error) {
+      const errorMsg = error instanceof Error ? error.message : String(error)
+      notifyError(`${t('common.error')}: ${errorMsg}`)
+      return false
+    }
+  }
+
+  async function loadInstallHistory(limit = 20): Promise<void> {
+    try {
+      installHistory.value = await window.electronAPI.getInstallHistory(limit)
+    } catch (error) {
+      const errorMsg = error instanceof Error ? error.message : String(error)
+      notifyError(`${t('common.error')}: ${errorMsg}`)
+    }
+  }
+
+  function addLog(message: string, level: 'info' | 'warn' | 'error' = 'info'): void {
+    const timestamp = new Date().toLocaleTimeString()
+    installLogs.value.push(`[${timestamp}] ${message}`)
+    // 限制日志数量
+    if (installLogs.value.length > 500) {
+      installLogs.value = installLogs.value.slice(-500)
+    }
+    // 同时写入日志文件
+    window.electronAPI.writeInstallLog(message, level).catch(() => {
+      // 忽略写入失败
+    })
+  }
+
+  function clearLogs(): void {
+    installLogs.value = []
+  }
+
+  function getStatus(software: SoftwareTypeWithAll): InstallStatusState {
+    return installStatus.value[software]
+  }
+
+  function isInstalled(software: SoftwareType): boolean {
+    return installedInfo.value[software].installed
+  }
+
+  function getInstalledVersion(software: SoftwareType): string | null {
+    return installedInfo.value[software].version
+  }
+
+  // Claude Code VS Code 扩展相关方法
+  async function checkClaudeCodeExtInstalled(): Promise<void> {
+    try {
+      const result = await window.electronAPI.checkVscodeExtension('anthropic.claude-code')
+      claudeCodeExtInfo.value.installed = result.installed
+      claudeCodeExtInfo.value.version = result.version || null
+    } catch {
+      claudeCodeExtInfo.value.installed = false
+      claudeCodeExtInfo.value.version = null
+    }
+  }
+
+  function isClaudeCodeExtInstalled(): boolean {
+    return claudeCodeExtInfo.value.installed
+  }
+
+  function getClaudeCodeExtVersion(): string | null {
+    return claudeCodeExtInfo.value.version
+  }
+
+  return {
+    installStatus,
+    installedInfo,
+    claudeCodeExtInfo,
+    installOptions,
+    installHistory,
+    installLogs,
+    isAnyInstalling,
+    updateStatus,
+    setInstalling,
+    setSuccess,
+    setError,
+    resetStatus,
+    checkInstalledSoftware,
+    doInstall,
+    cancelInstall,
+    uninstallSoftware,
+    loadInstallHistory,
+    addLog,
+    clearLogs,
+    getStatus,
+    isInstalled,
+    getInstalledVersion,
+    checkClaudeCodeExtInstalled,
+    isClaudeCodeExtInstalled,
+    getClaudeCodeExtVersion
+  }
+})

+ 111 - 0
src/stores/settings.ts

@@ -0,0 +1,111 @@
+// src/stores/settings.ts - 设置状态管理
+
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { setLocale, getLocale, type LocaleType } from '@/i18n'
+import type { GitMirrorType, NodejsMirrorType } from '@/types/electron'
+
+type ThemeType = 'light' | 'dark' | 'system'
+
+export const useSettingsStore = defineStore('settings', () => {
+  // 主题设置
+  const theme = ref<ThemeType>('system')
+
+  // 语言设置
+  const locale = ref<LocaleType>(getLocale())
+
+  // Git 镜像设置
+  const gitMirror = ref<GitMirrorType>('huaweicloud')
+
+  // Node.js 镜像设置
+  const nodejsMirror = ref<NodejsMirrorType>('npmmirror')
+
+  // 初始化设置
+  async function initSettings(): Promise<void> {
+    // 加载主题设置
+    const savedTheme = localStorage.getItem('theme') as ThemeType
+    if (savedTheme && ['light', 'dark', 'system'].includes(savedTheme)) {
+      theme.value = savedTheme
+    }
+
+    // 应用主题
+    applyTheme(theme.value)
+
+    // 加载 Git 镜像配置
+    try {
+      const config = await window.electronAPI.getGitMirrorConfig()
+      gitMirror.value = config.mirror
+    } catch {
+      // 使用默认值
+    }
+
+    // 加载 Node.js 镜像配置
+    try {
+      const config = await window.electronAPI.getNodejsMirrorConfig()
+      nodejsMirror.value = config.mirror
+    } catch {
+      // 使用默认值
+    }
+  }
+
+  // 应用主题
+  function applyTheme(newTheme: ThemeType): void {
+    const html = document.documentElement
+
+    if (newTheme === 'system') {
+      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
+      html.classList.toggle('dark', prefersDark)
+    } else {
+      html.classList.toggle('dark', newTheme === 'dark')
+    }
+
+    localStorage.setItem('theme', newTheme)
+  }
+
+  // 设置主题
+  function setTheme(newTheme: ThemeType): void {
+    theme.value = newTheme
+    applyTheme(newTheme)
+  }
+
+  // 设置语言
+  function setLanguage(newLocale: LocaleType): void {
+    locale.value = newLocale
+    setLocale(newLocale)
+  }
+
+  // 设置 Git 镜像
+  async function setGitMirror(newMirror: GitMirrorType): Promise<void> {
+    gitMirror.value = newMirror
+    await window.electronAPI.setGitMirror(newMirror)
+  }
+
+  // 设置 Node.js 镜像
+  async function setNodejsMirror(newMirror: NodejsMirrorType): Promise<void> {
+    nodejsMirror.value = newMirror
+    await window.electronAPI.setNodejsMirror(newMirror)
+  }
+
+  // 监听系统主题变化
+  function watchSystemTheme(): void {
+    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+    mediaQuery.addEventListener('change', (e) => {
+      if (theme.value === 'system') {
+        document.documentElement.classList.toggle('dark', e.matches)
+      }
+    })
+  }
+
+  return {
+    theme,
+    locale,
+    gitMirror,
+    nodejsMirror,
+    initSettings,
+    setTheme,
+    setLanguage,
+    setGitMirror,
+    setNodejsMirror,
+    watchSystemTheme
+  }
+})

+ 182 - 0
src/stores/system.ts

@@ -0,0 +1,182 @@
+// src/stores/system.ts - 系统状态管理
+
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import type { Platform, PackageManagerResult } from '@/types/electron'
+
+type SystemStatusType = 'success' | 'warning' | 'error' | 'info'
+
+interface SystemStatus {
+  visible: boolean
+  type: SystemStatusType
+  messageKey: string
+  messageParams: Record<string, string>
+  actionTextKey: string
+  actionTextParams: Record<string, string>
+  actionHandler: (() => void) | null
+}
+
+export const useSystemStore = defineStore('system', () => {
+  // 系统状态
+  const systemStatus = ref<SystemStatus>({
+    visible: false,
+    type: 'info',
+    messageKey: '',
+    messageParams: {},
+    actionTextKey: '',
+    actionTextParams: {},
+    actionHandler: null
+  })
+
+  // 平台信息
+  const platform = ref<Platform>('win32')
+
+  // 管理员权限
+  const isAdmin = ref(false)
+
+  // 包管理器状态
+  // Windows 使用直接下载方式,不需要包管理器
+  const packageManager = ref<PackageManagerResult>({
+    exists: true,
+    manager: 'none'
+  })
+
+  // 网络状态
+  const isOnline = ref(true)
+
+  // 当前激活的标签页
+  const activeTab = ref<string>('intro')
+
+  // 计算属性
+  const platformName = computed(() => {
+    switch (platform.value) {
+      case 'win32':
+        return 'Windows'
+      case 'darwin':
+        return 'macOS'
+      case 'linux':
+        return 'Linux'
+      default:
+        return 'Unknown'
+    }
+  })
+
+  const packageManagerName = computed(() => {
+    switch (packageManager.value.manager) {
+      case 'none':
+        return '直接下载安装'
+      case 'brew':
+        return 'Homebrew'
+      case 'apt':
+        return 'APT'
+      default:
+        return packageManager.value.manager
+    }
+  })
+
+  // 方法
+  async function initSystem(): Promise<void> {
+    // 获取平台信息
+    try {
+      platform.value = await window.electronAPI.getPlatform()
+    } catch (error) {
+      console.error('获取平台信息失败:', error)
+    }
+
+    // 检测管理员权限
+    try {
+      isAdmin.value = await window.electronAPI.checkAdmin()
+    } catch (error) {
+      console.error('检测管理员权限失败:', error)
+    }
+
+    // 检测包管理器
+    try {
+      packageManager.value = await window.electronAPI.checkPackageManager()
+    } catch (error) {
+      console.error('检测包管理器失败:', error)
+    }
+
+    // 检测网络状态
+    try {
+      isOnline.value = await window.electronAPI.checkNetwork()
+    } catch (error) {
+      console.error('检测网络状态失败:', error)
+      isOnline.value = navigator.onLine
+    }
+
+    // 监听网络状态变化
+    window.addEventListener('online', () => {
+      isOnline.value = true
+    })
+    window.addEventListener('offline', () => {
+      isOnline.value = false
+    })
+  }
+
+  function showSystemStatus(
+    type: SystemStatusType,
+    messageKey: string,
+    messageParams: Record<string, string> = {},
+    action?: { textKey: string; textParams?: Record<string, string>; handler: () => void }
+  ): void {
+    systemStatus.value = {
+      visible: true,
+      type,
+      messageKey,
+      messageParams,
+      actionTextKey: action?.textKey || '',
+      actionTextParams: action?.textParams || {},
+      actionHandler: action?.handler || null
+    }
+  }
+
+  function hideSystemStatus(): void {
+    systemStatus.value.visible = false
+  }
+
+  async function installPackageManager(): Promise<boolean> {
+    try {
+      const result = await window.electronAPI.installPackageManager(packageManager.value.manager)
+      if (result.success) {
+        packageManager.value.exists = true
+        return true
+      }
+      return false
+    } catch (error) {
+      console.error('安装包管理器失败:', error)
+      return false
+    }
+  }
+
+  function setActiveTab(tab: string): void {
+    activeTab.value = tab
+  }
+
+  async function checkNetwork(): Promise<boolean> {
+    try {
+      isOnline.value = await window.electronAPI.checkNetwork()
+      return isOnline.value
+    } catch (error) {
+      isOnline.value = navigator.onLine
+      return isOnline.value
+    }
+  }
+
+  return {
+    systemStatus,
+    platform,
+    isAdmin,
+    packageManager,
+    isOnline,
+    activeTab,
+    platformName,
+    packageManagerName,
+    initSystem,
+    showSystemStatus,
+    hideSystemStatus,
+    installPackageManager,
+    setActiveTab,
+    checkNetwork
+  }
+})

+ 139 - 0
src/stores/versions.ts

@@ -0,0 +1,139 @@
+// src/stores/versions.ts - 版本状态管理
+
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import type { VersionItem, VersionResult } from '@/types/electron'
+
+interface VersionState {
+  list: VersionItem[]
+  loading: boolean
+  error: boolean
+  warning: string | null
+}
+
+// 需要版本管理的软件类型
+type VersionedSoftwareType = 'nodejs' | 'vscode' | 'git'
+
+export const useVersionsStore = defineStore('versions', () => {
+  // 各软件的版本状态
+  const versions = ref<Record<VersionedSoftwareType, VersionState>>({
+    nodejs: { list: [], loading: true, error: false, warning: null },
+    vscode: { list: [], loading: true, error: false, warning: null },
+    git: { list: [], loading: true, error: false, warning: null }
+  })
+
+  // 选中的版本
+  const selectedVersions = ref<Record<VersionedSoftwareType, string>>({
+    nodejs: '',
+    vscode: '',
+    git: ''
+  })
+
+  // 计算属性
+  const isLoadingVersions = computed(() => {
+    return (
+      versions.value.nodejs.loading ||
+      versions.value.vscode.loading ||
+      versions.value.git.loading
+    )
+  })
+
+  const hasVersionError = computed(() => {
+    return (
+      versions.value.nodejs.error ||
+      versions.value.vscode.error ||
+      versions.value.git.error
+    )
+  })
+
+  const allVersionsLoaded = computed(() => {
+    const nodejs = versions.value.nodejs
+    const vscode = versions.value.vscode
+    const git = versions.value.git
+    return (
+      !nodejs.loading &&
+      !nodejs.error &&
+      !vscode.loading &&
+      !vscode.error &&
+      !git.loading &&
+      !git.error
+    )
+  })
+
+  // 加载单个软件的版本
+  async function loadVersionForSoftware(software: VersionedSoftwareType): Promise<boolean> {
+    const state = versions.value[software]
+    state.loading = true
+    state.error = false
+    state.warning = null
+
+    try {
+      const result: VersionResult = await window.electronAPI.getVersions(software)
+      const versionList = result.versions || []
+
+      state.list = versionList
+      state.loading = false
+      state.warning = result.warning
+
+      // 自动选中第一个可选版本(如果当前没有选中或选中的版本不在列表中)
+      const firstSelectable = versionList.find((v) => !v.disabled && !v.separator)
+      if (firstSelectable) {
+        const currentSelected = selectedVersions.value[software]
+        const isCurrentInList = versionList.some(
+          (v) => v.value === currentSelected && !v.disabled && !v.separator
+        )
+        if (!currentSelected || !isCurrentInList) {
+          selectedVersions.value[software] = firstSelectable.value
+        }
+      }
+
+      return true
+    } catch (error) {
+      console.error(`加载 ${software} 版本失败:`, error)
+      state.loading = false
+      state.error = true
+      return false
+    }
+  }
+
+  // 加载所有软件的版本
+  async function loadAllVersions(): Promise<PromiseSettledResult<boolean>[]> {
+    const softwareList: VersionedSoftwareType[] = ['nodejs', 'vscode', 'git']
+    return Promise.allSettled(softwareList.map((software) => loadVersionForSoftware(software)))
+  }
+
+  function setSelectedVersion(software: VersionedSoftwareType, version: string): void {
+    selectedVersions.value[software] = version
+  }
+
+  function getVersionList(software: VersionedSoftwareType): VersionItem[] {
+    return versions.value[software].list
+  }
+
+  function getWarning(software: VersionedSoftwareType): string | null {
+    return versions.value[software].warning
+  }
+
+  function isLoading(software: VersionedSoftwareType): boolean {
+    return versions.value[software].loading
+  }
+
+  function hasError(software: VersionedSoftwareType): boolean {
+    return versions.value[software].error
+  }
+
+  return {
+    versions,
+    selectedVersions,
+    isLoadingVersions,
+    hasVersionError,
+    allVersionsLoaded,
+    loadVersionForSoftware,
+    loadAllVersions,
+    setSelectedVersion,
+    getVersionList,
+    getWarning,
+    isLoading,
+    hasError
+  }
+})

+ 461 - 0
src/styles/main.scss

@@ -0,0 +1,461 @@
+// src/styles/main.scss - 主样式文件
+
+// 重置样式
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+}
+
+html,
+body {
+  height: 100%;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.5;
+  color: var(--text-primary);
+  background-color: var(--app-bg-color);
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+#app {
+  height: 100%;
+  overflow: hidden;
+}
+
+// 应用外层包装器
+.app-wrapper {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+}
+
+// 应用容器
+.app-container {
+  display: flex;
+  flex: 1;
+  min-height: 0; // 允许 flex 子元素收缩
+  padding: var(--spacing-md);
+  gap: var(--spacing-md);
+  overflow: hidden;
+}
+
+// 主面板
+.main-panel {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  background-color: var(--panel-bg-color);
+  border-radius: var(--border-radius-large);
+  box-shadow: var(--box-shadow-light);
+  padding: var(--spacing-lg);
+  overflow: hidden;
+  min-height: 0; // 允许 flex 子元素收缩
+
+  h1 {
+    font-size: 24px;
+    font-weight: 600;
+    margin-bottom: var(--spacing-md);
+    color: var(--text-primary);
+  }
+}
+
+// 固定头部区域(不滚动)
+.header-fixed {
+  flex-shrink: 0;
+}
+
+// 可滚动内容区域
+.content-scrollable {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0; // 允许 flex 子元素收缩
+}
+
+
+// 标签页
+.tabs {
+  display: flex;
+  gap: var(--spacing-xs);
+  margin-bottom: var(--spacing-md);
+  border-bottom: 1px solid var(--border-color-light);
+  padding-bottom: var(--spacing-sm);
+}
+
+.tab-btn {
+  position: relative;
+  display: inline-flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  padding: var(--spacing-sm) var(--spacing-md);
+  border: none;
+  background: transparent;
+  color: var(--text-regular);
+  font-size: 14px;
+  cursor: pointer;
+  border-radius: var(--border-radius);
+  transition: all var(--transition-duration);
+
+  &:hover {
+    color: var(--el-color-primary);
+    background-color: rgba(64, 158, 255, 0.1);
+  }
+
+  &.active {
+    color: var(--el-color-primary);
+    background-color: rgba(64, 158, 255, 0.1);
+    font-weight: 500;
+  }
+
+  .tab-label {
+    line-height: 1;
+  }
+
+  .installed-dot {
+    position: absolute;
+    top: 6px;
+    right: 6px;
+    width: 6px;
+    height: 6px;
+    background-color: var(--el-color-success);
+    border-radius: 50%;
+  }
+}
+
+// 标签页内容
+.tab-content {
+  flex: 1;
+}
+
+.tab-pane {
+  display: none;
+  animation: fadeIn 0.3s ease;
+
+  &.active {
+    display: block;
+  }
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+// 软件信息
+.software-info {
+  margin-bottom: var(--spacing-lg);
+
+  h2 {
+    font-size: 20px;
+    font-weight: 600;
+    margin-bottom: var(--spacing-xs);
+  }
+
+  p {
+    color: var(--text-secondary);
+    margin-bottom: var(--spacing-sm);
+  }
+}
+
+// 已安装徽章
+.installed-badge {
+  display: inline-block;
+  padding: 2px 8px;
+  background-color: rgba(103, 194, 58, 0.1);
+  color: var(--el-color-success);
+  border-radius: var(--border-radius);
+  font-size: 12px;
+}
+
+// 版本选择
+.version-select {
+  margin-bottom: var(--spacing-md);
+
+  label {
+    display: block;
+    margin-bottom: var(--spacing-xs);
+    color: var(--text-regular);
+    font-weight: 500;
+  }
+}
+
+// 按钮组
+.button-group {
+  display: flex;
+  gap: var(--spacing-sm);
+  margin-bottom: var(--spacing-md);
+}
+
+// 状态容器
+.status-container {
+  .status-text {
+    color: var(--text-secondary);
+    font-size: 13px;
+    margin-bottom: var(--spacing-sm);
+
+    &.success {
+      color: var(--el-color-success);
+    }
+
+    &.error {
+      color: var(--el-color-danger);
+    }
+  }
+}
+
+// 进度条
+.progress-bar {
+  width: 100%;
+  height: 6px;
+  border-radius: 3px;
+  overflow: hidden;
+  background-color: var(--border-color-lighter);
+
+  &::-webkit-progress-bar {
+    background-color: var(--border-color-lighter);
+  }
+
+  &::-webkit-progress-value {
+    background-color: var(--el-color-primary);
+    transition: width 0.3s ease;
+  }
+}
+
+// 设置组
+.setting-group {
+  margin-bottom: var(--spacing-lg);
+
+  h3 {
+    font-size: 14px;
+    font-weight: 600;
+    margin-bottom: var(--spacing-sm);
+    color: var(--text-primary);
+  }
+}
+
+.setting-item {
+  margin-bottom: var(--spacing-sm);
+}
+
+// 网络状态指示器
+.network-status {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  padding: var(--spacing-xs) var(--spacing-sm);
+  border-radius: var(--border-radius);
+  font-size: 12px;
+
+  &.online {
+    background-color: rgba(103, 194, 58, 0.1);
+    color: var(--el-color-success);
+  }
+
+  &.offline {
+    background-color: rgba(245, 108, 108, 0.1);
+    color: var(--el-color-danger);
+  }
+
+  .status-dot {
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background-color: currentColor;
+  }
+}
+
+// 骨架屏
+.skeleton {
+  background: linear-gradient(
+    90deg,
+    var(--border-color-lighter) 25%,
+    var(--border-color-light) 50%,
+    var(--border-color-lighter) 75%
+  );
+  background-size: 200% 100%;
+  animation: skeleton-loading 1.5s infinite;
+  border-radius: var(--border-radius);
+}
+
+@keyframes skeleton-loading {
+  0% {
+    background-position: 200% 0;
+  }
+  100% {
+    background-position: -200% 0;
+  }
+}
+
+.skeleton-text {
+  height: 14px;
+  margin-bottom: var(--spacing-sm);
+
+  &.short {
+    width: 60%;
+  }
+
+  &.medium {
+    width: 80%;
+  }
+
+  &.long {
+    width: 100%;
+  }
+}
+
+.skeleton-select {
+  height: 32px;
+  width: 100%;
+}
+
+.skeleton-button {
+  height: 36px;
+  width: 120px;
+}
+
+// 安装日志
+.install-log {
+  background-color: var(--card-bg-color);
+  border: 1px solid var(--border-color-light);
+  border-radius: var(--border-radius);
+  padding: var(--spacing-md);
+  max-height: 200px;
+  overflow-y: auto;
+  font-family: 'Consolas', 'Monaco', monospace;
+  font-size: 12px;
+  line-height: 1.6;
+
+  .log-entry {
+    color: var(--text-secondary);
+
+    &.info {
+      color: var(--el-color-info);
+    }
+
+    &.success {
+      color: var(--el-color-success);
+    }
+
+    &.warning {
+      color: var(--el-color-warning);
+    }
+
+    &.error {
+      color: var(--el-color-danger);
+    }
+  }
+}
+
+// 介绍页面
+.intro-content {
+  .intro-header {
+    text-align: center;
+    margin-bottom: var(--spacing-xl);
+
+    h2 {
+      font-size: 24px;
+      margin-bottom: var(--spacing-sm);
+    }
+
+    .intro-subtitle {
+      color: var(--text-secondary);
+      font-size: 16px;
+    }
+  }
+
+  .intro-features {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: var(--spacing-lg);
+    margin-bottom: var(--spacing-xl);
+  }
+
+  .feature-item {
+    display: flex;
+    gap: var(--spacing-md);
+    padding: var(--spacing-md);
+    background-color: var(--card-bg-color);
+    border-radius: var(--border-radius-large);
+    border: 1px solid var(--border-color-lighter);
+
+    .feature-icon {
+      font-size: 32px;
+      flex-shrink: 0;
+    }
+
+    .feature-text {
+      h3 {
+        font-size: 16px;
+        margin-bottom: var(--spacing-xs);
+      }
+
+      p {
+        color: var(--text-secondary);
+        font-size: 13px;
+      }
+    }
+  }
+
+  .intro-tips {
+    text-align: center;
+    padding: var(--spacing-md);
+    background-color: rgba(64, 158, 255, 0.1);
+    border-radius: var(--border-radius);
+    color: var(--el-color-primary);
+    font-size: 13px;
+  }
+}
+
+// 一键安装选项
+.all-options {
+  .option-group {
+    padding: var(--spacing-md);
+    background-color: var(--card-bg-color);
+    border: 1px solid var(--border-color-lighter);
+    border-radius: var(--border-radius);
+    margin-bottom: var(--spacing-sm);
+
+    .option-details {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-md);
+      margin-top: var(--spacing-sm);
+      padding-left: var(--spacing-lg);
+    }
+  }
+
+  .installed-tag {
+    font-size: 12px;
+    color: var(--el-color-success);
+    margin-left: var(--spacing-sm);
+  }
+}
+
+// 滚动条样式
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: var(--border-color);
+  border-radius: 3px;
+
+  &:hover {
+    background-color: var(--text-placeholder);
+  }
+}

+ 64 - 0
src/styles/variables.scss

@@ -0,0 +1,64 @@
+// src/styles/variables.scss - SCSS 变量定义
+
+// 颜色变量 - 浅色主题
+:root {
+  // 主色调
+  --el-color-primary: #409eff;
+  --el-color-success: #67c23a;
+  --el-color-warning: #e6a23c;
+  --el-color-danger: #f56c6c;
+  --el-color-info: #909399;
+
+  // 背景色
+  --app-bg-color: #f5f7fa;
+  --panel-bg-color: #ffffff;
+  --card-bg-color: #ffffff;
+
+  // 文字颜色
+  --text-primary: #303133;
+  --text-regular: #606266;
+  --text-secondary: #909399;
+  --text-placeholder: #c0c4cc;
+
+  // 边框颜色
+  --border-color: #dcdfe6;
+  --border-color-light: #e4e7ed;
+  --border-color-lighter: #ebeef5;
+
+  // 阴影
+  --box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  --box-shadow-light: 0 2px 4px rgba(0, 0, 0, 0.12);
+
+  // 圆角
+  --border-radius: 4px;
+  --border-radius-large: 8px;
+
+  // 间距
+  --spacing-xs: 4px;
+  --spacing-sm: 8px;
+  --spacing-md: 16px;
+  --spacing-lg: 24px;
+  --spacing-xl: 32px;
+
+  // 过渡
+  --transition-duration: 0.3s;
+}
+
+// 深色主题
+.dark {
+  --app-bg-color: #1a1a1a;
+  --panel-bg-color: #242424;
+  --card-bg-color: #2c2c2c;
+
+  --text-primary: #e5eaf3;
+  --text-regular: #cfd3dc;
+  --text-secondary: #a3a6ad;
+  --text-placeholder: #8d9095;
+
+  --border-color: #4c4d4f;
+  --border-color-light: #414243;
+  --border-color-lighter: #363637;
+
+  --box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3);
+  --box-shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
+}

+ 4 - 0
src/types/electron.d.ts

@@ -0,0 +1,4 @@
+// src/types/electron.d.ts - Electron API 类型声明
+// 从共享类型文件重新导出所有类型
+
+export * from '../../shared/types'

+ 390 - 0
src/views/BatchInstallView.vue

@@ -0,0 +1,390 @@
+<script setup lang="ts">
+import { computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useVersionsStore } from '@/stores/versions'
+import { useInstallStore } from '@/stores/install'
+import { useSystemStore } from '@/stores/system'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const { t } = useI18n()
+const versionsStore = useVersionsStore()
+const installStore = useInstallStore()
+const systemStore = useSystemStore()
+
+// 是否为 Windows 平台(只有 Windows 支持自定义安装路径)
+const isWindows = computed(() => systemStore.platform === 'win32')
+
+const status = computed(() => installStore.getStatus('all'))
+
+// Claude Code VS Code 扩展状态(使用 store 中的共享数据)
+const claudeCodeExtInstalled = computed(() => installStore.isClaudeCodeExtInstalled())
+const claudeCodeExtVersion = computed(() => installStore.getClaudeCodeExtVersion() || '')
+
+onMounted(() => {
+  // 检查扩展安装状态
+  installStore.checkClaudeCodeExtInstalled()
+})
+
+// 用户想要安装的软件(未安装且用户勾选了)
+const wantInstallNodejs = computed(() => !installStore.isInstalled('nodejs') && installStore.installOptions.all.installNodejs)
+const wantInstallPnpm = computed(() => !installStore.isInstalled('pnpm') && installStore.installOptions.all.installPnpm)
+const wantInstallVscode = computed(() => !installStore.isInstalled('vscode') && installStore.installOptions.all.installVscode)
+const wantInstallGit = computed(() => !installStore.isInstalled('git') && installStore.installOptions.all.installGit)
+const wantInstallClaudeCode = computed(() => !installStore.isInstalled('claudeCode') && installStore.installOptions.all.installClaudeCode)
+const wantInstallClaudeCodeExt = computed(() => !claudeCodeExtInstalled.value && installStore.installOptions.all.installClaudeCodeExt)
+
+// 是否有用户选择要安装的软件
+const hasAnythingToInstall = computed(() =>
+  wantInstallNodejs.value || wantInstallPnpm.value || wantInstallVscode.value || wantInstallGit.value || wantInstallClaudeCode.value || wantInstallClaudeCodeExt.value
+)
+
+// 是否所有软件都已安装
+const isAllInstalled = computed(() =>
+  installStore.isInstalled('nodejs') &&
+  installStore.isInstalled('pnpm') &&
+  installStore.isInstalled('vscode') &&
+  installStore.isInstalled('git') &&
+  installStore.isInstalled('claudeCode') &&
+  claudeCodeExtInstalled.value
+)
+
+const isDisabled = computed(() =>
+  status.value.installing ||
+  versionsStore.isLoadingVersions ||
+  versionsStore.hasVersionError ||
+  !hasAnythingToInstall.value
+)
+
+const buttonText = computed(() => {
+  if (status.value.installing) return t('install.installing')
+  if (status.value.success) return t('install.complete')
+  if (status.value.error) return t('install.reinstall')
+  if (isAllInstalled.value) return t('install.allInstalled')
+  if (!hasAnythingToInstall.value) return t('install.noSelection')
+  return t('install.startAll')
+})
+
+const statusText = computed(() => {
+  if (status.value.installing) return status.value.message
+  if (status.value.success) return status.value.message
+  if (status.value.error) return status.value.message
+  if (versionsStore.isLoadingVersions) return t('common.loading')
+  if (versionsStore.hasVersionError) return t('common.loadFailed')
+  if (isAllInstalled.value) return t('install.allInstalledHint')
+  if (!hasAnythingToInstall.value) return t('install.noSelectionHint')
+  return t('common.ready')
+})
+
+
+async function handleInstall() {
+  if (isDisabled.value) return
+
+  const options = {
+    installNodejs: wantInstallNodejs.value,
+    nodejsVersion: versionsStore.selectedVersions.nodejs,
+    nodejsPath: installStore.installOptions.all.nodejsPath,
+    installPnpm: wantInstallPnpm.value,
+    installVscode: wantInstallVscode.value,
+    vscodeVersion: versionsStore.selectedVersions.vscode,
+    vscodePath: installStore.installOptions.all.vscodePath,
+    installGit: wantInstallGit.value,
+    gitVersion: versionsStore.selectedVersions.git,
+    gitPath: installStore.installOptions.all.gitPath,
+    installClaudeCode: wantInstallClaudeCode.value,
+    installClaudeCodeExt: wantInstallClaudeCodeExt.value
+  }
+
+  await installStore.doInstall('all', options)
+}
+
+async function handleCancel() {
+  await installStore.cancelInstall()
+}
+
+async function selectDirectory(software: 'nodejs' | 'vscode' | 'git') {
+  const currentPath = software === 'nodejs'
+    ? installStore.installOptions.all.nodejsPath
+    : software === 'vscode'
+      ? installStore.installOptions.all.vscodePath
+      : installStore.installOptions.all.gitPath
+
+  const result = await window.electronAPI.selectDirectory(currentPath || undefined)
+  if (!result.canceled && result.path) {
+    if (software === 'nodejs') {
+      installStore.installOptions.all.nodejsPath = result.path
+    } else if (software === 'vscode') {
+      installStore.installOptions.all.vscodePath = result.path
+    } else {
+      installStore.installOptions.all.gitPath = result.path
+    }
+  }
+}
+</script>
+
+<template>
+  <div class="batch-install">
+    <div class="software-info">
+      <h2>{{ t('software.all.name') }}</h2>
+      <p>{{ t('software.all.description') }}</p>
+    </div>
+
+    <div class="all-options">
+      <!-- Node.js -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!installStore.isInstalled('nodejs') && installStore.installOptions.all.installNodejs"
+            :disabled="installStore.isInstalled('nodejs')"
+            @update:model-value="installStore.installOptions.all.installNodejs = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="nodejs" :size="18" />
+              <span>Node.js</span>
+            </span>
+            <el-tag v-if="installStore.isInstalled('nodejs')" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }} v{{ installStore.getInstalledVersion('nodejs') }}
+            </el-tag>
+          </el-checkbox>
+          <div v-if="wantInstallNodejs" class="option-details">
+            <el-select v-model="versionsStore.selectedVersions.nodejs" size="small" style="width: 150px">
+              <el-option
+                v-for="(v, index) in versionsStore.getVersionList('nodejs')"
+                :key="v.value || `separator-${index}`"
+                :value="v.value"
+                :label="v.label"
+                :disabled="v.disabled || v.separator"
+              />
+            </el-select>
+          </div>
+        </div>
+        <div v-if="wantInstallNodejs && isWindows" class="path-row">
+          <span class="path-label">{{ t('install.customPath') }}</span>
+          <el-input
+            v-model="installStore.installOptions.all.nodejsPath"
+            size="small"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button size="small" @click="selectDirectory('nodejs')">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <!-- pnpm -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!installStore.isInstalled('pnpm') && installStore.installOptions.all.installPnpm"
+            :disabled="installStore.isInstalled('pnpm')"
+            @update:model-value="installStore.installOptions.all.installPnpm = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="nodejs" :size="18" />
+              <span>pnpm</span>
+            </span>
+            <el-tag v-if="installStore.isInstalled('pnpm')" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }} v{{ installStore.getInstalledVersion('pnpm') }}
+            </el-tag>
+          </el-checkbox>
+          <span v-if="wantInstallPnpm && !installStore.isInstalled('nodejs') && !wantInstallNodejs" class="option-hint">
+            {{ t('software.nodejs.pnpmRequiresNodejs') }}
+          </span>
+        </div>
+      </div>
+
+      <!-- Git (Claude Code 运行时需要) -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!installStore.isInstalled('git') && installStore.installOptions.all.installGit"
+            :disabled="installStore.isInstalled('git')"
+            @update:model-value="installStore.installOptions.all.installGit = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="git" :size="18" />
+              <span>Git</span>
+            </span>
+            <el-tag v-if="installStore.isInstalled('git')" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }} v{{ installStore.getInstalledVersion('git') }}
+            </el-tag>
+          </el-checkbox>
+          <div v-if="wantInstallGit" class="option-details">
+            <el-select v-model="versionsStore.selectedVersions.git" size="small" style="width: 150px">
+              <el-option
+                v-for="(v, index) in versionsStore.getVersionList('git')"
+                :key="v.value || `separator-${index}`"
+                :value="v.value"
+                :label="v.label"
+                :disabled="v.disabled || v.separator"
+              />
+            </el-select>
+          </div>
+        </div>
+        <div v-if="wantInstallGit && isWindows" class="path-row">
+          <span class="path-label">{{ t('install.customPath') }}</span>
+          <el-input
+            v-model="installStore.installOptions.all.gitPath"
+            size="small"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button size="small" @click="selectDirectory('git')">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <!-- Claude Code (需要 Node.js 和 Git) -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!installStore.isInstalled('claudeCode') && installStore.installOptions.all.installClaudeCode"
+            :disabled="installStore.isInstalled('claudeCode')"
+            @update:model-value="installStore.installOptions.all.installClaudeCode = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="claudeCode" :size="18" />
+              <span>Claude Code</span>
+            </span>
+            <el-tag v-if="installStore.isInstalled('claudeCode')" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }} v{{ installStore.getInstalledVersion('claudeCode') }}
+            </el-tag>
+          </el-checkbox>
+          <span v-if="wantInstallClaudeCode" class="option-hint">
+            {{ t('claudeCode.requiresNodejsGit') }}
+          </span>
+        </div>
+      </div>
+
+      <!-- VS Code -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!installStore.isInstalled('vscode') && installStore.installOptions.all.installVscode"
+            :disabled="installStore.isInstalled('vscode')"
+            @update:model-value="installStore.installOptions.all.installVscode = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="vscode" :size="18" />
+              <span>VS Code</span>
+            </span>
+            <el-tag v-if="installStore.isInstalled('vscode')" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }} v{{ installStore.getInstalledVersion('vscode') }}
+            </el-tag>
+          </el-checkbox>
+          <div v-if="wantInstallVscode" class="option-details">
+            <el-select v-model="versionsStore.selectedVersions.vscode" size="small" style="width: 150px">
+              <el-option
+                v-for="(v, index) in versionsStore.getVersionList('vscode')"
+                :key="v.value || `separator-${index}`"
+                :value="v.value"
+                :label="v.label"
+                :disabled="v.disabled || v.separator"
+              />
+            </el-select>
+          </div>
+        </div>
+        <div v-if="wantInstallVscode && isWindows" class="path-row">
+          <span class="path-label">{{ t('install.customPath') }}</span>
+          <el-input
+            v-model="installStore.installOptions.all.vscodePath"
+            size="small"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button size="small" @click="selectDirectory('vscode')">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <!-- Claude Code for VS Code 扩展 (需要 VS Code 和 Claude Code) -->
+      <div class="option-group">
+        <div class="option-row">
+          <el-checkbox
+            :model-value="!claudeCodeExtInstalled && installStore.installOptions.all.installClaudeCodeExt"
+            :disabled="claudeCodeExtInstalled"
+            @update:model-value="installStore.installOptions.all.installClaudeCodeExt = $event as boolean"
+          >
+            <span class="software-label">
+              <SoftwareIcon software="vscode" :size="18" />
+              <span>{{ t('software.vscode.claudeCodeExt') }}</span>
+            </span>
+            <el-tag v-if="claudeCodeExtInstalled" type="success" size="small" style="margin-left: 8px">
+              {{ t('common.installed') }}{{ claudeCodeExtVersion ? ` v${claudeCodeExtVersion}` : '' }}
+            </el-tag>
+          </el-checkbox>
+          <span v-if="wantInstallClaudeCodeExt && !installStore.isInstalled('vscode') && !wantInstallVscode" class="option-hint">
+            {{ t('software.vscode.installVscodeFirst') }}
+          </span>
+        </div>
+      </div>
+    </div>
+
+    <div class="button-group">
+      <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
+        {{ buttonText }}
+      </el-button>
+      <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
+    </div>
+
+    <div class="status-container">
+      <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
+      <el-progress v-if="status.installing" :percentage="status.progress" :stroke-width="6" :show-text="false" />
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.batch-install {
+  .all-options {
+    margin-bottom: var(--spacing-lg);
+
+    .option-group {
+      padding: var(--spacing-sm) var(--spacing-md);
+      background-color: var(--card-bg-color);
+      border: 1px solid var(--border-color-lighter);
+      border-radius: var(--border-radius);
+      margin-bottom: var(--spacing-sm);
+
+      .option-row {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: var(--spacing-md);
+      }
+
+      .option-details {
+        display: flex;
+        align-items: center;
+        gap: var(--spacing-md);
+      }
+
+      .option-hint {
+        color: var(--text-color-secondary);
+        font-size: 12px;
+      }
+
+      .path-row {
+        display: flex;
+        align-items: center;
+        gap: var(--spacing-sm);
+        margin-top: var(--spacing-sm);
+        padding-left: 24px;
+
+        .path-label {
+          color: var(--text-color-secondary);
+          font-size: 12px;
+          white-space: nowrap;
+        }
+
+        .el-input {
+          flex: 1;
+          max-width: 400px;
+        }
+      }
+    }
+
+    .software-label {
+      display: inline-flex;
+      align-items: center;
+      gap: var(--spacing-xs);
+    }
+  }
+}
+</style>

+ 228 - 0
src/views/ClaudeCodeView.vue

@@ -0,0 +1,228 @@
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useInstallStore } from '@/stores/install'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const { t } = useI18n()
+const installStore = useInstallStore()
+
+const isLaunching = ref(false)
+const isInstalling = ref(false)
+const launchError = ref('')
+const launchSuccess = ref(false)
+const installError = ref('')
+const isChecking = ref(false)
+
+const nodejsInstalled = computed(() => installStore.isInstalled('nodejs'))
+const gitInstalled = computed(() => installStore.isInstalled('git'))
+const claudeCodeInstalled = computed(() => installStore.isInstalled('claudeCode'))
+const claudeCodeVersion = computed(() => installStore.getInstalledVersion('claudeCode'))
+
+// 启动按钮只有在 Claude Code 已安装时才可点击
+const canLaunch = computed(() => gitInstalled.value && claudeCodeInstalled.value && !isLaunching.value && !isChecking.value && !isInstalling.value)
+
+// 安装按钮只有在 Node.js 已安装且 Claude Code 未安装时才可点击
+const canInstall = computed(() => nodejsInstalled.value && !claudeCodeInstalled.value && !isInstalling.value && !isChecking.value)
+
+const statusText = computed(() => {
+  if (isChecking.value) return t('common.loading')
+  if (isInstalling.value) return t('claudeCode.installing')
+  if (isLaunching.value) return t('claudeCode.launching')
+  if (launchSuccess.value) return t('claudeCode.launchSuccess')
+  if (launchError.value) return launchError.value
+  if (installError.value) return installError.value
+  if (!nodejsInstalled.value) return t('claudeCode.nodejsRequired')
+  if (!gitInstalled.value) return t('claudeCode.gitRequired')
+  if (!claudeCodeInstalled.value) return t('claudeCode.notInstalled')
+  return t('common.ready')
+})
+
+async function refreshStatus() {
+  isChecking.value = true
+  try {
+    await installStore.checkInstalledSoftware()
+  } finally {
+    isChecking.value = false
+  }
+}
+
+async function handleInstall() {
+  if (!canInstall.value) return
+
+  isInstalling.value = true
+  installError.value = ''
+  launchError.value = ''
+
+  try {
+    const result = await window.electronAPI.installClaudeCode()
+    if (result.success) {
+      // 安装成功后刷新状态
+      await refreshStatus()
+    } else {
+      installError.value = result.error || t('claudeCode.installFailed')
+    }
+  } catch (error) {
+    installError.value = (error as Error).message || t('claudeCode.installFailed')
+  } finally {
+    isInstalling.value = false
+  }
+}
+
+async function handleLaunch() {
+  if (!canLaunch.value) return
+
+  isLaunching.value = true
+  launchError.value = ''
+  launchSuccess.value = false
+
+  try {
+    await window.electronAPI.launchClaudeCode()
+    launchSuccess.value = true
+  } catch (error) {
+    launchError.value = (error as Error).message || t('claudeCode.launchFailed')
+  } finally {
+    isLaunching.value = false
+  }
+}
+</script>
+
+<template>
+  <div class="claude-code-install">
+    <!-- 第一部分:软件信息和安装 -->
+    <div class="section">
+      <div class="software-info">
+        <h2 class="software-title">
+          <SoftwareIcon software="claudeCode" :size="24" />
+          <span>{{ t('software.claudeCode.name') }}</span>
+        </h2>
+        <p>{{ t('software.claudeCode.description') }}</p>
+        <el-tag v-if="claudeCodeInstalled && claudeCodeVersion" type="success" size="small">
+          {{ t('common.installed') }} v{{ claudeCodeVersion }}
+        </el-tag>
+        <el-tag v-else-if="claudeCodeInstalled" type="success" size="small">
+          {{ t('common.installed') }}
+        </el-tag>
+      </div>
+
+      <div v-if="!nodejsInstalled" class="warning-card">
+        <el-alert type="warning" :closable="false" show-icon>
+          <template #title>
+            {{ t('claudeCode.nodejsRequired') }}
+          </template>
+          <p>{{ t('claudeCode.nodejsRequiredDesc') }}</p>
+        </el-alert>
+      </div>
+
+      <div v-else-if="!claudeCodeInstalled && !isChecking" class="warning-card">
+        <el-alert type="warning" :closable="false" show-icon>
+          <template #title>
+            {{ t('claudeCode.notInstalled') }}
+          </template>
+          <p>{{ t('claudeCode.installHintWithButton') }}</p>
+        </el-alert>
+      </div>
+
+      <div class="button-group">
+        <el-button
+          v-if="!claudeCodeInstalled"
+          type="primary"
+          :disabled="!canInstall"
+          :loading="isInstalling"
+          @click="handleInstall"
+        >
+          {{ isInstalling ? t('claudeCode.installing') : t('claudeCode.install') }}
+        </el-button>
+        <el-button :disabled="isChecking || isInstalling" @click="refreshStatus">
+          {{ t('claudeCode.refresh') }}
+        </el-button>
+      </div>
+
+      <div class="status-container">
+        <p :class="['status-text', { success: launchSuccess, error: !!launchError || !!installError }]">
+          {{ statusText }}
+        </p>
+      </div>
+    </div>
+
+    <!-- 第二部分:使用说明和启动(仅在已安装 Claude Code 后显示) -->
+    <div v-if="claudeCodeInstalled" class="section">
+      <div class="info-card">
+        <el-alert type="info" :closable="false" show-icon>
+          <template #title>
+            {{ t('claudeCode.infoTitle') }}
+          </template>
+          <p>{{ t('claudeCode.infoDesc') }}</p>
+        </el-alert>
+      </div>
+
+      <div v-if="!gitInstalled" class="warning-card">
+        <el-alert type="warning" :closable="false" show-icon>
+          <template #title>
+            {{ t('claudeCode.gitRequired') }}
+          </template>
+          <p>{{ t('claudeCode.gitRequiredDesc') }}</p>
+        </el-alert>
+      </div>
+
+      <div class="button-group">
+        <el-button
+          type="primary"
+          :disabled="!canLaunch"
+          :loading="isLaunching"
+          @click="handleLaunch"
+        >
+          {{ isLaunching ? t('claudeCode.launching') : t('claudeCode.launch') }}
+        </el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.claude-code-install {
+  .section {
+    margin-bottom: var(--spacing-lg);
+    padding-bottom: var(--spacing-lg);
+    border-bottom: 1px solid var(--border-color);
+
+    &:last-child {
+      margin-bottom: 0;
+      padding-bottom: 0;
+      border-bottom: none;
+    }
+  }
+
+  .software-info {
+    .software-title {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+    }
+
+    .version-info {
+      color: var(--text-color-secondary);
+      font-size: 0.9em;
+      margin-top: var(--spacing-xs);
+    }
+  }
+
+  .info-card {
+    margin-bottom: var(--spacing-md);
+
+    p {
+      margin: var(--spacing-sm) 0 0 0;
+      color: var(--text-color-secondary);
+    }
+  }
+
+  .warning-card {
+    margin-bottom: var(--spacing-md);
+
+    p {
+      margin: var(--spacing-sm) 0 0 0;
+      color: var(--text-color-secondary);
+    }
+  }
+}
+</style>

+ 170 - 0
src/views/GitView.vue

@@ -0,0 +1,170 @@
+<script setup lang="ts">
+import { computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useVersionsStore } from '@/stores/versions'
+import { useInstallStore } from '@/stores/install'
+import { useSystemStore } from '@/stores/system'
+import SkeletonLoader from '@/components/common/SkeletonLoader.vue'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const emit = defineEmits<{
+  install: [software: 'git', options?: { customPath?: string }]
+  cancel: []
+}>()
+
+const { t } = useI18n()
+const versionsStore = useVersionsStore()
+const installStore = useInstallStore()
+const systemStore = useSystemStore()
+
+// 是否为 Windows 平台(只有 Windows 支持自定义安装路径)
+const isWindows = computed(() => systemStore.platform === 'win32')
+
+// 自定义安装路径(使用 store 共享数据)
+const customPath = computed({
+  get: () => installStore.installOptions.all.gitPath || '',
+  set: (val) => { installStore.installOptions.all.gitPath = val || undefined }
+})
+
+const isLoading = computed(() => versionsStore.isLoading('git'))
+const hasError = computed(() => versionsStore.hasError('git'))
+const versionList = computed(() => versionsStore.getVersionList('git'))
+const selectedVersion = computed({
+  get: () => versionsStore.selectedVersions.git,
+  set: (val) => versionsStore.setSelectedVersion('git', val)
+})
+
+const status = computed(() => installStore.getStatus('git'))
+const isInstalled = computed(() => installStore.isInstalled('git'))
+const installedVersion = computed(() => installStore.getInstalledVersion('git'))
+
+const isDisabled = computed(() => status.value.installing || isLoading.value || hasError.value)
+
+const buttonText = computed(() => {
+  if (status.value.installing) return t('install.installing')
+  if (status.value.success) return t('install.complete')
+  if (status.value.error) return t('install.reinstall')
+  return t('install.start')
+})
+
+const statusText = computed(() => {
+  if (status.value.installing) return status.value.message
+  if (status.value.success) return status.value.message
+  if (status.value.error) return status.value.message
+  if (isLoading.value) return t('common.loading')
+  if (hasError.value) return t('common.loadFailed')
+  return t('common.ready')
+})
+
+function handleInstall() {
+  if (!isDisabled.value) {
+    emit('install', 'git', customPath.value ? { customPath: customPath.value } : undefined)
+  }
+}
+
+async function handleSelectDirectory() {
+  const result = await window.electronAPI.selectDirectory(customPath.value || undefined)
+  if (!result.canceled && result.path) {
+    installStore.installOptions.all.gitPath = result.path
+  }
+}
+
+function handleCancel() {
+  emit('cancel')
+}
+</script>
+
+<template>
+  <div class="software-view">
+    <div class="software-info">
+      <h2 class="software-title">
+        <SoftwareIcon software="git" :size="24" />
+        <span>{{ t('software.git.name') }}</span>
+      </h2>
+      <p>{{ t('software.git.description') }}</p>
+      <el-tag v-if="isInstalled" type="success" size="small">
+        {{ t('common.installed') }} v{{ installedVersion }}
+      </el-tag>
+    </div>
+
+    <SkeletonLoader :loading="isLoading">
+      <div class="version-select">
+        <label>{{ t('common.selectVersion') }}</label>
+        <el-select v-model="selectedVersion" :disabled="isLoading || hasError" style="width: 100%">
+          <template v-if="hasError">
+            <el-option value="" :label="t('common.loadFailed')" disabled />
+          </template>
+          <template v-else>
+            <el-option
+              v-for="(v, index) in versionList"
+              :key="v.value || `separator-${index}`"
+              :value="v.value"
+              :label="v.label"
+              :disabled="v.disabled || v.separator"
+            />
+          </template>
+        </el-select>
+      </div>
+
+      <!-- 自定义安装路径 (仅 Windows) -->
+      <div v-if="isWindows" class="custom-path-section">
+        <label>{{ t('install.customPath') }}</label>
+        <div class="path-input-group">
+          <el-input
+            v-model="customPath"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button @click="handleSelectDirectory">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <div class="button-group">
+        <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
+          {{ buttonText }}
+        </el-button>
+        <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
+      </div>
+
+      <div class="status-container">
+        <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
+        <el-progress
+          v-if="status.installing"
+          :percentage="status.progress"
+          :stroke-width="6"
+          :show-text="false"
+        />
+      </div>
+    </SkeletonLoader>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.software-view {
+  .software-title {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-sm);
+  }
+
+  .custom-path-section {
+    margin-bottom: var(--spacing-md);
+
+    label {
+      display: block;
+      margin-bottom: var(--spacing-xs);
+      color: var(--text-secondary);
+      font-size: 14px;
+    }
+
+    .path-input-group {
+      display: flex;
+      gap: var(--spacing-sm);
+
+      .el-input {
+        flex: 1;
+      }
+    }
+  }
+}
+</style>

+ 97 - 0
src/views/IntroView.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+import { useSystemStore } from '@/stores/system'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const { t } = useI18n()
+const systemStore = useSystemStore()
+
+const features = [
+  { key: 'nodejs', tab: 'nodejs' },
+  { key: 'git', tab: 'git' },
+  { key: 'claudeCode', tab: 'claudeCode' },
+  { key: 'vscode', tab: 'vscode' },
+  { key: 'oneClick', tab: 'all' }
+]
+
+function navigateToTab(tab: string) {
+  systemStore.setActiveTab(tab)
+}
+</script>
+
+<template>
+  <div class="intro-content">
+    <div class="intro-header">
+      <h2>{{ t('intro.welcome') }}</h2>
+    </div>
+
+    <div class="intro-features">
+      <div v-for="feature in features" :key="feature.key" class="feature-item" @click="navigateToTab(feature.tab)">
+        <div class="feature-icon">
+          <SoftwareIcon :software="feature.tab as any" :size="32" />
+        </div>
+        <div class="feature-text">
+          <h3>{{ feature.key === 'oneClick' ? t('tabs.all') : t(`software.${feature.key}.name`) }}</h3>
+          <p>{{ t(`intro.${feature.key}Desc`) }}</p>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<style scoped lang="scss">
+.intro-content {
+  .intro-header {
+    text-align: center;
+    margin-bottom: var(--spacing-xl);
+
+    h2 {
+      font-size: 24px;
+      margin-bottom: 0;
+    }
+  }
+
+  .intro-features {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: var(--spacing-lg);
+    margin-bottom: var(--spacing-xl);
+  }
+
+  .feature-item {
+    display: flex;
+    gap: var(--spacing-md);
+    padding: var(--spacing-md);
+    background-color: var(--card-bg-color);
+    border-radius: var(--border-radius-large);
+    border: 1px solid var(--border-color-lighter);
+    transition: all var(--transition-duration);
+    cursor: pointer;
+
+    &:hover {
+      border-color: var(--el-color-primary);
+      box-shadow: var(--box-shadow-light);
+    }
+
+    .feature-icon {
+      font-size: 32px;
+      flex-shrink: 0;
+    }
+
+    .feature-text {
+      h3 {
+        font-size: 16px;
+        margin-bottom: var(--spacing-xs);
+      }
+
+      p {
+        color: var(--text-secondary);
+        font-size: 13px;
+        line-height: 1.5;
+      }
+    }
+  }
+
+}
+</style>

+ 260 - 0
src/views/NodejsView.vue

@@ -0,0 +1,260 @@
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useVersionsStore } from '@/stores/versions'
+import { useInstallStore } from '@/stores/install'
+import { useSystemStore } from '@/stores/system'
+import SkeletonLoader from '@/components/common/SkeletonLoader.vue'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const emit = defineEmits<{
+  install: [software: 'nodejs', options?: { customPath?: string }]
+  cancel: []
+}>()
+
+const { t } = useI18n()
+const versionsStore = useVersionsStore()
+const installStore = useInstallStore()
+const systemStore = useSystemStore()
+
+// 是否为 Windows 平台(只有 Windows 支持自定义安装路径)
+const isWindows = computed(() => systemStore.platform === 'win32')
+
+// 自定义安装路径(使用 store 共享数据)
+const customPath = computed({
+  get: () => installStore.installOptions.all.nodejsPath || '',
+  set: (val) => { installStore.installOptions.all.nodejsPath = val || undefined }
+})
+
+const isLoading = computed(() => versionsStore.isLoading('nodejs'))
+const hasError = computed(() => versionsStore.hasError('nodejs'))
+const versionList = computed(() => versionsStore.getVersionList('nodejs'))
+const selectedVersion = computed({
+  get: () => versionsStore.selectedVersions.nodejs,
+  set: (val) => versionsStore.setSelectedVersion('nodejs', val)
+})
+
+const status = computed(() => installStore.getStatus('nodejs'))
+const isInstalled = computed(() => installStore.isInstalled('nodejs'))
+const installedVersion = computed(() => installStore.getInstalledVersion('nodejs'))
+
+const isDisabled = computed(() => status.value.installing || isLoading.value || hasError.value)
+
+const buttonText = computed(() => {
+  if (status.value.installing) return t('install.installing')
+  if (status.value.success) return t('install.complete')
+  if (status.value.error) return t('install.reinstall')
+  return t('install.start')
+})
+
+const statusText = computed(() => {
+  if (status.value.installing) return status.value.message
+  if (status.value.success) return status.value.message
+  if (status.value.error) return status.value.message
+  if (isLoading.value) return t('common.loading')
+  if (hasError.value) return t('common.loadFailed')
+  return t('common.ready')
+})
+
+function handleInstall() {
+  if (!isDisabled.value) {
+    emit('install', 'nodejs', customPath.value ? { customPath: customPath.value } : undefined)
+  }
+}
+
+async function handleSelectDirectory() {
+  const result = await window.electronAPI.selectDirectory(customPath.value || undefined)
+  if (!result.canceled && result.path) {
+    installStore.installOptions.all.nodejsPath = result.path
+  }
+}
+
+function handleCancel() {
+  emit('cancel')
+}
+
+// pnpm 安装相关
+const pnpmInstalling = ref(false)
+const pnpmError = ref('')
+
+const pnpmInstalled = computed(() => installStore.isInstalled('pnpm'))
+const pnpmInstalledVersion = computed(() => installStore.getInstalledVersion('pnpm'))
+
+async function handleInstallPnpm() {
+  if (pnpmInstalling.value || pnpmInstalled.value) return
+
+  pnpmInstalling.value = true
+  pnpmError.value = ''
+
+  try {
+    await window.electronAPI.install('pnpm', {})
+  } catch (error) {
+    pnpmError.value = (error as Error).message
+  } finally {
+    pnpmInstalling.value = false
+    await installStore.checkInstalledSoftware()
+  }
+}
+</script>
+
+<template>
+  <div class="software-view">
+    <div class="software-info">
+      <h2 class="software-title">
+        <SoftwareIcon software="nodejs" :size="24" />
+        <span>{{ t('software.nodejs.name') }}</span>
+      </h2>
+      <p>{{ t('software.nodejs.description') }}</p>
+      <el-tag v-if="isInstalled" type="success" size="small">
+        {{ t('common.installed') }} v{{ installedVersion }}
+      </el-tag>
+    </div>
+
+    <SkeletonLoader :loading="isLoading">
+      <div class="version-select">
+        <label>{{ t('common.selectVersion') }}</label>
+        <el-select v-model="selectedVersion" :disabled="isLoading || hasError" style="width: 100%">
+          <template v-if="hasError">
+            <el-option value="" :label="t('common.loadFailed')" disabled />
+          </template>
+          <template v-else>
+            <el-option
+              v-for="(v, index) in versionList"
+              :key="v.value || `separator-${index}`"
+              :value="v.value"
+              :label="v.label"
+              :disabled="v.disabled || v.separator"
+            />
+          </template>
+        </el-select>
+      </div>
+
+      <!-- 自定义安装路径 (仅 Windows) -->
+      <div v-if="isWindows" class="custom-path-section">
+        <label>{{ t('install.customPath') }}</label>
+        <div class="path-input-group">
+          <el-input
+            v-model="customPath"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button @click="handleSelectDirectory">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <div class="button-group">
+        <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
+          {{ buttonText }}
+        </el-button>
+        <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
+      </div>
+
+      <!-- pnpm 安装选项 -->
+      <div class="pnpm-option">
+        <div class="ext-header">
+          <span class="ext-title">{{ t('software.nodejs.pnpmTitle') }}</span>
+          <el-tag v-if="pnpmInstalled" type="success" size="small">
+            {{ t('common.installed') }} v{{ pnpmInstalledVersion }}
+          </el-tag>
+        </div>
+        <p class="ext-desc">{{ t('software.nodejs.pnpmDesc') }}</p>
+        <div class="ext-actions">
+          <el-button
+            size="small"
+            :type="pnpmInstalled ? 'success' : 'primary'"
+            :disabled="pnpmInstalled || pnpmInstalling || !isInstalled"
+            :loading="pnpmInstalling"
+            @click="handleInstallPnpm"
+          >
+            {{ pnpmInstalled ? t('common.installed') : pnpmInstalling ? t('common.installing') : t('software.nodejs.installPnpmBtn') }}
+          </el-button>
+          <span v-if="!isInstalled" class="ext-hint">{{ t('software.nodejs.installNodejsFirst') }}</span>
+        </div>
+        <p v-if="pnpmError" class="ext-error">{{ pnpmError }}</p>
+      </div>
+
+      <div class="status-container">
+        <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
+        <el-progress
+          v-if="status.installing"
+          :percentage="status.progress"
+          :stroke-width="6"
+          :show-text="false"
+        />
+      </div>
+    </SkeletonLoader>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.software-view {
+  .software-title {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-sm);
+  }
+
+  .custom-path-section {
+    margin-bottom: var(--spacing-md);
+
+    label {
+      display: block;
+      margin-bottom: var(--spacing-xs);
+      color: var(--text-secondary);
+      font-size: 14px;
+    }
+
+    .path-input-group {
+      display: flex;
+      gap: var(--spacing-sm);
+
+      .el-input {
+        flex: 1;
+      }
+    }
+  }
+
+  .pnpm-option {
+    margin-bottom: var(--spacing-md);
+    padding: var(--spacing-sm) var(--spacing-md);
+    background-color: var(--card-bg-color);
+    border: 1px solid var(--border-color-lighter);
+    border-radius: var(--border-radius);
+
+    .ext-header {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+      margin-bottom: var(--spacing-xs);
+    }
+
+    .ext-title {
+      font-weight: 500;
+      color: var(--text-primary);
+    }
+
+    .ext-desc {
+      font-size: 12px;
+      color: var(--text-secondary);
+      margin: 0 0 var(--spacing-sm) 0;
+    }
+
+    .ext-actions {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+    }
+
+    .ext-hint {
+      font-size: 12px;
+      color: var(--text-secondary);
+    }
+
+    .ext-error {
+      font-size: 12px;
+      color: var(--el-color-danger);
+      margin: var(--spacing-xs) 0 0 0;
+    }
+  }
+}
+</style>

+ 287 - 0
src/views/VscodeView.vue

@@ -0,0 +1,287 @@
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useVersionsStore } from '@/stores/versions'
+import { useInstallStore } from '@/stores/install'
+import { useSystemStore } from '@/stores/system'
+import SkeletonLoader from '@/components/common/SkeletonLoader.vue'
+import SoftwareIcon from '@/components/common/SoftwareIcon.vue'
+
+const emit = defineEmits<{
+  install: [software: 'vscode', options?: { customPath?: string }]
+  cancel: []
+}>()
+
+const { t } = useI18n()
+const versionsStore = useVersionsStore()
+const installStore = useInstallStore()
+const systemStore = useSystemStore()
+
+// 是否为 Windows 平台(只有 Windows 支持自定义安装路径)
+const isWindows = computed(() => systemStore.platform === 'win32')
+
+// 自定义安装路径(使用 store 共享数据)
+const customPath = computed({
+  get: () => installStore.installOptions.all.vscodePath || '',
+  set: (val) => { installStore.installOptions.all.vscodePath = val || undefined }
+})
+
+const isLoading = computed(() => versionsStore.isLoading('vscode'))
+const hasError = computed(() => versionsStore.hasError('vscode'))
+const versionList = computed(() => versionsStore.getVersionList('vscode'))
+const selectedVersion = computed({
+  get: () => versionsStore.selectedVersions.vscode,
+  set: (val) => versionsStore.setSelectedVersion('vscode', val)
+})
+
+const status = computed(() => installStore.getStatus('vscode'))
+const isInstalled = computed(() => installStore.isInstalled('vscode'))
+const installedVersion = computed(() => installStore.getInstalledVersion('vscode'))
+
+const isDisabled = computed(() => status.value.installing || isLoading.value || hasError.value)
+
+const buttonText = computed(() => {
+  if (status.value.installing) return t('install.installing')
+  if (status.value.success) return t('install.complete')
+  if (status.value.error) return t('install.reinstall')
+  return t('install.start')
+})
+
+const statusText = computed(() => {
+  if (status.value.installing) return status.value.message
+  if (status.value.success) return status.value.message
+  if (status.value.error) return status.value.message
+  if (isLoading.value) return t('common.loading')
+  if (hasError.value) return t('common.loadFailed')
+  return t('common.ready')
+})
+
+function handleInstall() {
+  if (!isDisabled.value) {
+    emit('install', 'vscode', customPath.value ? { customPath: customPath.value } : undefined)
+  }
+}
+
+async function handleSelectDirectory() {
+  const result = await window.electronAPI.selectDirectory(customPath.value || undefined)
+  if (!result.canceled && result.path) {
+    installStore.installOptions.all.vscodePath = result.path
+  }
+}
+
+function handleCancel() {
+  emit('cancel')
+}
+
+// Claude Code for VS Code 插件安装相关
+const claudeCodeExtInstalling = ref(false)
+const claudeCodeExtError = ref('')
+
+// 使用 store 中的共享数据
+const claudeCodeExtInstalled = computed(() => installStore.isClaudeCodeExtInstalled())
+const claudeCodeExtVersion = computed(() => installStore.getClaudeCodeExtVersion() || '')
+
+// 检查 Claude Code CLI 是否已安装(插件需要 CLI)
+const claudeCodeCliInstalled = computed(() => installStore.isInstalled('claudeCode'))
+
+async function handleInstallClaudeCodeExt() {
+  if (claudeCodeExtInstalling.value) return
+
+  claudeCodeExtInstalling.value = true
+  claudeCodeExtError.value = ''
+
+  // 记录开始安装日志
+  installStore.addLog(t('software.vscode.installingClaudeCodeExt'))
+
+  try {
+    const result = await window.electronAPI.installVscodeExtension('anthropic.claude-code')
+    if (result.success) {
+      // 重新检查以获取版本号(更新 store 中的共享数据)
+      await installStore.checkClaudeCodeExtInstalled()
+      installStore.addLog(t('software.vscode.claudeCodeExtInstallSuccess'))
+    } else {
+      claudeCodeExtError.value = result.error || t('common.error')
+      installStore.addLog(`${t('software.vscode.claudeCodeExtInstallFailed')}: ${claudeCodeExtError.value}`, 'error')
+    }
+  } catch (error) {
+    claudeCodeExtError.value = (error as Error).message
+    installStore.addLog(`${t('software.vscode.claudeCodeExtInstallFailed')}: ${claudeCodeExtError.value}`, 'error')
+  } finally {
+    claudeCodeExtInstalling.value = false
+  }
+}
+
+// 初始化检查插件状态
+installStore.checkClaudeCodeExtInstalled()
+</script>
+
+<template>
+  <div class="software-view">
+    <div class="software-info">
+      <h2 class="software-title">
+        <SoftwareIcon software="vscode" :size="24" />
+        <span>{{ t('software.vscode.name') }}</span>
+      </h2>
+      <p>{{ t('software.vscode.description') }}</p>
+      <el-tag v-if="isInstalled" type="success" size="small">
+        {{ t('common.installed') }} v{{ installedVersion }}
+      </el-tag>
+    </div>
+
+    <SkeletonLoader :loading="isLoading">
+      <div class="version-select">
+        <label>{{ t('common.selectVersion') }}</label>
+        <el-select v-model="selectedVersion" :disabled="isLoading || hasError" style="width: 100%">
+          <template v-if="hasError">
+            <el-option value="" :label="t('common.loadFailed')" disabled />
+          </template>
+          <template v-else>
+            <el-option
+              v-for="(v, index) in versionList"
+              :key="v.value || `separator-${index}`"
+              :value="v.value"
+              :label="v.label"
+              :disabled="v.disabled || v.separator"
+            />
+          </template>
+        </el-select>
+      </div>
+
+      <!-- 自定义安装路径 (仅 Windows) -->
+      <div v-if="isWindows" class="custom-path-section">
+        <label>{{ t('install.customPath') }}</label>
+        <div class="path-input-group">
+          <el-input
+            v-model="customPath"
+            :placeholder="t('install.customPathPlaceholder')"
+            clearable
+          />
+          <el-button @click="handleSelectDirectory">{{ t('install.browse') }}</el-button>
+        </div>
+      </div>
+
+      <div class="button-group">
+        <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
+          {{ buttonText }}
+        </el-button>
+        <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
+      </div>
+
+      <!-- Claude Code for VS Code 插件安装选项 -->
+      <div class="claude-code-ext-option">
+        <div class="ext-header">
+          <span class="ext-title">{{ t('software.vscode.claudeCodeExt') }}</span>
+          <el-tag v-if="claudeCodeExtInstalled" type="success" size="small">
+            {{ t('common.installed') }}{{ claudeCodeExtVersion ? ` v${claudeCodeExtVersion}` : '' }}
+          </el-tag>
+        </div>
+        <p class="ext-desc">{{ t('software.vscode.claudeCodeExtDesc') }}</p>
+        <div v-if="!claudeCodeCliInstalled" class="ext-warning">
+          <el-alert type="warning" :closable="false" show-icon>
+            {{ t('software.vscode.claudeCodeExtRequiresCli') }}
+          </el-alert>
+        </div>
+        <div v-else class="ext-actions">
+          <el-button
+            size="small"
+            :type="claudeCodeExtInstalled ? 'success' : 'primary'"
+            :disabled="claudeCodeExtInstalled || claudeCodeExtInstalling || !isInstalled"
+            :loading="claudeCodeExtInstalling"
+            @click="handleInstallClaudeCodeExt"
+          >
+            {{ claudeCodeExtInstalled ? t('common.installed') : claudeCodeExtInstalling ? t('common.installing') : t('software.vscode.installClaudeCodeExt') }}
+          </el-button>
+          <span v-if="!isInstalled" class="ext-hint">{{ t('software.vscode.installVscodeFirst') }}</span>
+        </div>
+        <p v-if="claudeCodeExtError" class="ext-error">{{ claudeCodeExtError }}</p>
+      </div>
+
+      <div class="status-container">
+        <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
+        <el-progress
+          v-if="status.installing"
+          :percentage="status.progress"
+          :stroke-width="6"
+          :show-text="false"
+        />
+      </div>
+    </SkeletonLoader>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.software-view {
+  .software-title {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-sm);
+  }
+
+  .custom-path-section {
+    margin-bottom: var(--spacing-md);
+
+    label {
+      display: block;
+      margin-bottom: var(--spacing-xs);
+      color: var(--text-secondary);
+      font-size: 14px;
+    }
+
+    .path-input-group {
+      display: flex;
+      gap: var(--spacing-sm);
+
+      .el-input {
+        flex: 1;
+      }
+    }
+  }
+
+  .claude-code-ext-option {
+    margin-bottom: var(--spacing-md);
+    padding: var(--spacing-sm) var(--spacing-md);
+    background-color: var(--card-bg-color);
+    border: 1px solid var(--border-color-lighter);
+    border-radius: var(--border-radius);
+
+    .ext-header {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+      margin-bottom: var(--spacing-xs);
+    }
+
+    .ext-title {
+      font-weight: 500;
+      color: var(--text-primary);
+    }
+
+    .ext-desc {
+      font-size: 12px;
+      color: var(--text-secondary);
+      margin: 0 0 var(--spacing-sm) 0;
+    }
+
+    .ext-warning {
+      margin-bottom: var(--spacing-sm);
+    }
+
+    .ext-actions {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+    }
+
+    .ext-hint {
+      font-size: 12px;
+      color: var(--text-secondary);
+    }
+
+    .ext-error {
+      font-size: 12px;
+      color: var(--el-color-danger);
+      margin: var(--spacing-xs) 0 0 0;
+    }
+  }
+}
+</style>

+ 7 - 0
src/vite-env.d.ts

@@ -0,0 +1,7 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

+ 185 - 0
tests/constants.test.ts

@@ -0,0 +1,185 @@
+import { describe, it, expect } from 'vitest'
+import {
+  FALLBACK_VERSIONS,
+  ERROR_MESSAGES,
+  STATUS_MESSAGES,
+  NPM_REGISTRY,
+  GIT_MIRRORS,
+  NODEJS_MIRRORS,
+  VSCODE_API,
+  MIN_SUPPORTED_NODE_VERSION,
+  MAX_MAJOR_VERSIONS,
+  REQUEST_TIMEOUT,
+  MAX_RETRIES,
+  RETRY_DELAY,
+  VERSION_CACHE_TTL,
+  SOURCE_UPDATE_CACHE_TTL,
+  BREW_PACKAGES,
+  APP_NAME
+} from '../electron/modules/constants'
+
+describe('constants', () => {
+  describe('FALLBACK_VERSIONS', () => {
+    it('should have valid Node.js version format', () => {
+      expect(FALLBACK_VERSIONS.nodejs).toMatch(/^\d+\.\d+\.\d+$/)
+    })
+
+    it('should have valid VS Code version format', () => {
+      expect(FALLBACK_VERSIONS.vscode).toMatch(/^\d+\.\d+\.\d+$/)
+    })
+
+    it('should have valid Git version format', () => {
+      expect(FALLBACK_VERSIONS.git).toMatch(/^\d+\.\d+\.\d+$/)
+    })
+  })
+
+  describe('ERROR_MESSAGES', () => {
+    it('should have all required error messages', () => {
+      expect(ERROR_MESSAGES.NETWORK_ERROR).toBeDefined()
+      expect(ERROR_MESSAGES.TIMEOUT_ERROR).toBeDefined()
+      expect(ERROR_MESSAGES.VERSION_FETCH_ERROR).toBeDefined()
+      expect(ERROR_MESSAGES.INSTALL_ERROR).toBeDefined()
+      expect(ERROR_MESSAGES.PERMISSION_ERROR).toBeDefined()
+      expect(ERROR_MESSAGES.UNSUPPORTED_PLATFORM).toBeDefined()
+      expect(ERROR_MESSAGES.UNKNOWN_SOFTWARE).toBeDefined()
+      expect(ERROR_MESSAGES.INSTALL_CANCELLED).toBeDefined()
+    })
+  })
+
+  describe('STATUS_MESSAGES', () => {
+    it('should have all required status messages', () => {
+      expect(STATUS_MESSAGES.PREPARING).toBeDefined()
+      expect(STATUS_MESSAGES.INSTALLING).toBeDefined()
+      expect(STATUS_MESSAGES.CONFIGURING).toBeDefined()
+      expect(STATUS_MESSAGES.COMPLETE).toBeDefined()
+      expect(STATUS_MESSAGES.READY).toBeDefined()
+    })
+  })
+
+  describe('NPM_REGISTRY', () => {
+    it('should be a valid URL', () => {
+      expect(NPM_REGISTRY).toMatch(/^https?:\/\//)
+    })
+  })
+
+  describe('GIT_MIRRORS', () => {
+    it('should have huaweicloud mirror', () => {
+      expect(GIT_MIRRORS.huaweicloud).toBeDefined()
+      expect(GIT_MIRRORS.huaweicloud.name).toBeDefined()
+      expect(GIT_MIRRORS.huaweicloud.getDownloadUrl).toBeTypeOf('function')
+      expect(GIT_MIRRORS.huaweicloud.versionsUrl).toBeDefined()
+    })
+
+    it('should have github mirror', () => {
+      expect(GIT_MIRRORS.github).toBeDefined()
+      expect(GIT_MIRRORS.github.name).toBeDefined()
+      expect(GIT_MIRRORS.github.getDownloadUrl).toBeTypeOf('function')
+      expect(GIT_MIRRORS.github.versionsUrl).toBeDefined()
+    })
+
+    it('should generate valid download URLs', () => {
+      const huaweiUrl = GIT_MIRRORS.huaweicloud.getDownloadUrl('2.47.1', '64')
+      expect(huaweiUrl).toContain('2.47.1')
+      expect(huaweiUrl).toContain('64')
+
+      const githubUrl = GIT_MIRRORS.github.getDownloadUrl('2.47.1', '64')
+      expect(githubUrl).toContain('2.47.1')
+      expect(githubUrl).toContain('64')
+    })
+  })
+
+  describe('NODEJS_MIRRORS', () => {
+    it('should have official mirror', () => {
+      expect(NODEJS_MIRRORS.official).toBeDefined()
+      expect(NODEJS_MIRRORS.official.name).toBeDefined()
+      expect(NODEJS_MIRRORS.official.getDownloadUrl).toBeTypeOf('function')
+      expect(NODEJS_MIRRORS.official.versionsUrl).toBeDefined()
+    })
+
+    it('should have npmmirror mirror', () => {
+      expect(NODEJS_MIRRORS.npmmirror).toBeDefined()
+      expect(NODEJS_MIRRORS.npmmirror.name).toBeDefined()
+      expect(NODEJS_MIRRORS.npmmirror.getDownloadUrl).toBeTypeOf('function')
+      expect(NODEJS_MIRRORS.npmmirror.versionsUrl).toBeDefined()
+    })
+
+    it('should generate valid download URLs', () => {
+      const officialUrl = NODEJS_MIRRORS.official.getDownloadUrl('22.12.0', 'x64')
+      expect(officialUrl).toContain('22.12.0')
+      expect(officialUrl).toContain('x64')
+
+      const npmmirrorUrl = NODEJS_MIRRORS.npmmirror.getDownloadUrl('22.12.0', 'x64')
+      expect(npmmirrorUrl).toContain('22.12.0')
+      expect(npmmirrorUrl).toContain('x64')
+    })
+  })
+
+  describe('VSCODE_API', () => {
+    it('should have versionsUrl', () => {
+      expect(VSCODE_API.versionsUrl).toBeDefined()
+      expect(VSCODE_API.versionsUrl).toMatch(/^https?:\/\//)
+    })
+
+    it('should generate valid download URLs', () => {
+      const url = VSCODE_API.getDownloadUrl('1.96.0', 'x64', 'user')
+      expect(url).toContain('1.96.0')
+      expect(url).toContain('x64')
+      expect(url).toContain('user')
+    })
+  })
+
+  describe('Configuration values', () => {
+    it('should have valid MIN_SUPPORTED_NODE_VERSION', () => {
+      expect(MIN_SUPPORTED_NODE_VERSION).toBeGreaterThanOrEqual(16)
+    })
+
+    it('should have valid MAX_MAJOR_VERSIONS', () => {
+      expect(MAX_MAJOR_VERSIONS).toBeGreaterThan(0)
+    })
+
+    it('should have valid REQUEST_TIMEOUT', () => {
+      expect(REQUEST_TIMEOUT).toBeGreaterThan(0)
+    })
+
+    it('should have valid MAX_RETRIES', () => {
+      expect(MAX_RETRIES).toBeGreaterThan(0)
+    })
+
+    it('should have valid RETRY_DELAY', () => {
+      expect(RETRY_DELAY).toBeGreaterThan(0)
+    })
+
+    it('should have valid VERSION_CACHE_TTL', () => {
+      expect(VERSION_CACHE_TTL).toBeGreaterThan(0)
+    })
+
+    it('should have valid SOURCE_UPDATE_CACHE_TTL', () => {
+      expect(SOURCE_UPDATE_CACHE_TTL).toBeGreaterThan(0)
+    })
+  })
+
+  describe('BREW_PACKAGES', () => {
+    it('should have nodejs packages', () => {
+      expect(BREW_PACKAGES.nodejs).toBeDefined()
+      expect(BREW_PACKAGES.nodejs.default).toBeDefined()
+    })
+
+    it('should have vscode packages', () => {
+      expect(BREW_PACKAGES.vscode).toBeDefined()
+      expect(BREW_PACKAGES.vscode.stable).toBeDefined()
+      expect(BREW_PACKAGES.vscode.insiders).toBeDefined()
+    })
+
+    it('should have git packages', () => {
+      expect(BREW_PACKAGES.git).toBeDefined()
+      expect(BREW_PACKAGES.git.stable).toBeDefined()
+    })
+  })
+
+  describe('APP_NAME', () => {
+    it('should be defined', () => {
+      expect(APP_NAME).toBeDefined()
+      expect(APP_NAME).toBeTypeOf('string')
+    })
+  })
+})

+ 128 - 0
tests/ipc-registry.test.ts

@@ -0,0 +1,128 @@
+import { describe, it, expect, beforeEach, vi } from 'vitest'
+
+// Mock electron's ipcMain
+vi.mock('electron', () => ({
+  ipcMain: {
+    handle: vi.fn(),
+    removeHandler: vi.fn()
+  }
+}))
+
+// Import after mocking
+import {
+  registerHandler,
+  registerHandlers,
+  removeHandler,
+  removeAllHandlers,
+  getRegisteredHandlers,
+  isHandlerRegistered
+} from '../electron/modules/ipc-registry'
+import { ipcMain } from 'electron'
+
+describe('ipc-registry', () => {
+  beforeEach(() => {
+    // Clear all registered handlers before each test
+    removeAllHandlers()
+    vi.clearAllMocks()
+  })
+
+  describe('registerHandler', () => {
+    it('should register a handler', () => {
+      const handler = vi.fn()
+      registerHandler('test-channel', handler)
+
+      expect(ipcMain.handle).toHaveBeenCalledWith('test-channel', handler)
+      expect(isHandlerRegistered('test-channel')).toBe(true)
+    })
+
+    it('should replace existing handler with same name', () => {
+      const handler1 = vi.fn()
+      const handler2 = vi.fn()
+
+      registerHandler('test-channel', handler1)
+      registerHandler('test-channel', handler2)
+
+      expect(ipcMain.removeHandler).toHaveBeenCalledWith('test-channel')
+      expect(ipcMain.handle).toHaveBeenCalledTimes(2)
+    })
+  })
+
+  describe('registerHandlers', () => {
+    it('should register multiple handlers', () => {
+      const handlers = {
+        'channel-1': vi.fn(),
+        'channel-2': vi.fn(),
+        'channel-3': vi.fn()
+      }
+
+      registerHandlers(handlers)
+
+      expect(ipcMain.handle).toHaveBeenCalledTimes(3)
+      expect(isHandlerRegistered('channel-1')).toBe(true)
+      expect(isHandlerRegistered('channel-2')).toBe(true)
+      expect(isHandlerRegistered('channel-3')).toBe(true)
+    })
+  })
+
+  describe('removeHandler', () => {
+    it('should remove a registered handler', () => {
+      const handler = vi.fn()
+      registerHandler('test-channel', handler)
+
+      removeHandler('test-channel')
+
+      expect(ipcMain.removeHandler).toHaveBeenCalledWith('test-channel')
+      expect(isHandlerRegistered('test-channel')).toBe(false)
+    })
+
+    it('should do nothing for non-existent handler', () => {
+      removeHandler('non-existent')
+
+      expect(ipcMain.removeHandler).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('removeAllHandlers', () => {
+    it('should remove all registered handlers', () => {
+      registerHandler('channel-1', vi.fn())
+      registerHandler('channel-2', vi.fn())
+      registerHandler('channel-3', vi.fn())
+
+      removeAllHandlers()
+
+      expect(ipcMain.removeHandler).toHaveBeenCalledTimes(3)
+      expect(getRegisteredHandlers()).toHaveLength(0)
+    })
+  })
+
+  describe('getRegisteredHandlers', () => {
+    it('should return all registered handler names', () => {
+      registerHandler('channel-1', vi.fn())
+      registerHandler('channel-2', vi.fn())
+
+      const handlers = getRegisteredHandlers()
+
+      expect(handlers).toContain('channel-1')
+      expect(handlers).toContain('channel-2')
+      expect(handlers).toHaveLength(2)
+    })
+
+    it('should return empty array when no handlers registered', () => {
+      const handlers = getRegisteredHandlers()
+
+      expect(handlers).toHaveLength(0)
+    })
+  })
+
+  describe('isHandlerRegistered', () => {
+    it('should return true for registered handler', () => {
+      registerHandler('test-channel', vi.fn())
+
+      expect(isHandlerRegistered('test-channel')).toBe(true)
+    })
+
+    it('should return false for non-registered handler', () => {
+      expect(isHandlerRegistered('non-existent')).toBe(false)
+    })
+  })
+})

+ 199 - 0
tests/shared-types.test.ts

@@ -0,0 +1,199 @@
+import { describe, it, expect } from 'vitest'
+import type {
+  SoftwareType,
+  SoftwareTypeWithAll,
+  Platform,
+  TabId,
+  VersionItem,
+  VersionResult,
+  InstallOptions,
+  InstallStatus,
+  InstallResult,
+  InstalledInfo,
+  AllInstalledInfo,
+  InstallHistoryItem,
+  PackageManagerResult,
+  GitMirrorType,
+  NodejsMirrorType,
+  GitMirrorConfig,
+  NodejsMirrorConfig
+} from '../shared/types'
+
+describe('shared types', () => {
+  describe('SoftwareType', () => {
+    it('should accept valid software types', () => {
+      const validTypes: SoftwareType[] = ['nodejs', 'pnpm', 'vscode', 'git', 'claudeCode']
+      expect(validTypes).toHaveLength(5)
+    })
+  })
+
+  describe('SoftwareTypeWithAll', () => {
+    it('should include all software types plus "all"', () => {
+      const validTypes: SoftwareTypeWithAll[] = ['nodejs', 'pnpm', 'vscode', 'git', 'claudeCode', 'all']
+      expect(validTypes).toHaveLength(6)
+    })
+  })
+
+  describe('Platform', () => {
+    it('should accept valid platform types', () => {
+      const validPlatforms: Platform[] = ['win32', 'darwin', 'linux']
+      expect(validPlatforms).toHaveLength(3)
+    })
+  })
+
+  describe('TabId', () => {
+    it('should accept valid tab IDs', () => {
+      const validTabs: TabId[] = ['intro', 'nodejs', 'vscode', 'git', 'claudeCode', 'all']
+      expect(validTabs).toHaveLength(6)
+    })
+  })
+
+  describe('VersionItem', () => {
+    it('should have correct structure', () => {
+      const item: VersionItem = {
+        value: '22.12.0',
+        label: 'v22.12.0 (LTS)',
+        lts: true,
+        disabled: false,
+        separator: false
+      }
+      expect(item.value).toBe('22.12.0')
+      expect(item.label).toBe('v22.12.0 (LTS)')
+      expect(item.lts).toBe(true)
+    })
+  })
+
+  describe('VersionResult', () => {
+    it('should have correct structure', () => {
+      const result: VersionResult = {
+        versions: [
+          { value: '22.12.0', label: 'v22.12.0' }
+        ],
+        warning: null
+      }
+      expect(result.versions).toHaveLength(1)
+      expect(result.warning).toBeNull()
+    })
+  })
+
+  describe('InstallOptions', () => {
+    it('should have correct structure', () => {
+      const options: InstallOptions = {
+        version: '22.12.0',
+        installPnpm: true,
+        installNodejs: true,
+        nodejsVersion: '22.12.0',
+        nodejsPath: 'C:\\nodejs',
+        installVscode: true,
+        vscodeVersion: '1.96.0',
+        vscodePath: 'C:\\vscode',
+        installGit: true,
+        gitVersion: '2.47.1',
+        gitPath: 'C:\\git',
+        installClaudeCode: true,
+        customPath: 'C:\\custom'
+      }
+      expect(options.version).toBe('22.12.0')
+      expect(options.installPnpm).toBe(true)
+    })
+  })
+
+  describe('InstallStatus', () => {
+    it('should have correct structure', () => {
+      const status: InstallStatus = {
+        software: 'nodejs',
+        message: 'Installing...',
+        progress: 50,
+        i18nKey: 'install.installing',
+        i18nParams: { software: 'Node.js' }
+      }
+      expect(status.software).toBe('nodejs')
+      expect(status.progress).toBe(50)
+    })
+  })
+
+  describe('InstallResult', () => {
+    it('should have correct structure', () => {
+      const result: InstallResult = {
+        software: 'nodejs',
+        message: 'Installation complete',
+        i18nKey: 'install.complete'
+      }
+      expect(result.software).toBe('nodejs')
+      expect(result.message).toBe('Installation complete')
+    })
+  })
+
+  describe('InstalledInfo', () => {
+    it('should have correct structure', () => {
+      const info: InstalledInfo = {
+        installed: true,
+        version: '22.12.0'
+      }
+      expect(info.installed).toBe(true)
+      expect(info.version).toBe('22.12.0')
+    })
+  })
+
+  describe('AllInstalledInfo', () => {
+    it('should have correct structure', () => {
+      const info: AllInstalledInfo = {
+        nodejs: { installed: true, version: '22.12.0' },
+        pnpm: { installed: true, version: '9.0.0' },
+        vscode: { installed: true, version: '1.96.0' },
+        git: { installed: true, version: '2.47.1' },
+        claudeCode: { installed: false, version: null }
+      }
+      expect(info.nodejs.installed).toBe(true)
+      expect(info.claudeCode.installed).toBe(false)
+    })
+  })
+
+  describe('InstallHistoryItem', () => {
+    it('should have correct structure', () => {
+      const item: InstallHistoryItem = {
+        software: 'nodejs',
+        version: '22.12.0',
+        options: { installPnpm: true },
+        success: true,
+        duration: 5000,
+        timestamp: Date.now()
+      }
+      expect(item.software).toBe('nodejs')
+      expect(item.success).toBe(true)
+    })
+  })
+
+  describe('PackageManagerResult', () => {
+    it('should have correct structure', () => {
+      const result: PackageManagerResult = {
+        exists: true,
+        manager: 'brew'
+      }
+      expect(result.exists).toBe(true)
+      expect(result.manager).toBe('brew')
+    })
+  })
+
+  describe('Mirror types', () => {
+    it('should accept valid GitMirrorType', () => {
+      const mirrors: GitMirrorType[] = ['huaweicloud', 'github']
+      expect(mirrors).toHaveLength(2)
+    })
+
+    it('should accept valid NodejsMirrorType', () => {
+      const mirrors: NodejsMirrorType[] = ['official', 'npmmirror']
+      expect(mirrors).toHaveLength(2)
+    })
+
+    it('should have correct GitMirrorConfig structure', () => {
+      const config: GitMirrorConfig = { mirror: 'huaweicloud' }
+      expect(config.mirror).toBe('huaweicloud')
+    })
+
+    it('should have correct NodejsMirrorConfig structure', () => {
+      const config: NodejsMirrorConfig = { mirror: 'official' }
+      expect(config.mirror).toBe('official')
+    })
+  })
+})

+ 33 - 0
tsconfig.json

@@ -0,0 +1,33 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "preserve",
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "paths": {
+      "@/*": ["./src/*"],
+      "@electron/*": ["./electron/*"],
+      "@shared/*": ["./shared/*"]
+    },
+    "types": ["node", "vite/client"]
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "electron/**/*.ts",
+    "shared/**/*.ts"
+  ],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}

+ 11 - 0
tsconfig.node.json

@@ -0,0 +1,11 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true,
+    "strict": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 70 - 0
vite.config.ts

@@ -0,0 +1,70 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import electron from 'vite-plugin-electron'
+import renderer from 'vite-plugin-electron-renderer'
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import { resolve } from 'path'
+
+export default defineConfig({
+  plugins: [
+    vue(),
+    AutoImport({
+      imports: ['vue', 'vue-i18n', 'pinia'],
+      resolvers: [ElementPlusResolver()],
+      dts: 'src/auto-imports.d.ts'
+    }),
+    Components({
+      resolvers: [ElementPlusResolver()],
+      dts: 'src/components.d.ts'
+    }),
+    electron([
+      {
+        entry: 'electron/main.ts',
+        onstart(options) {
+          options.startup()
+        },
+        vite: {
+          build: {
+            outDir: 'dist-electron',
+            rollupOptions: {
+              external: ['electron', 'sudo-prompt', 'execa']
+            }
+          }
+        }
+      },
+      {
+        entry: 'electron/preload.ts',
+        onstart(options) {
+          options.reload()
+        },
+        vite: {
+          build: {
+            outDir: 'dist-electron'
+          }
+        }
+      }
+    ]),
+    renderer()
+  ],
+  resolve: {
+    alias: {
+      '@': resolve(__dirname, 'src'),
+      '@electron': resolve(__dirname, 'electron'),
+      '@shared': resolve(__dirname, 'shared')
+    }
+  },
+  css: {
+    preprocessorOptions: {
+      scss: {
+        api: 'modern-compiler',
+        additionalData: `@use "@/styles/variables.scss" as *;`
+      }
+    }
+  },
+  build: {
+    outDir: 'dist',
+    emptyOutDir: true
+  }
+})

+ 33 - 0
vitest.config.ts

@@ -0,0 +1,33 @@
+import { defineConfig } from 'vitest/config'
+import vue from '@vitejs/plugin-vue'
+import { resolve } from 'path'
+
+export default defineConfig({
+  plugins: [vue()],
+  test: {
+    globals: true,
+    environment: 'node',
+    include: ['tests/**/*.{test,spec}.{js,ts}'],
+    exclude: ['node_modules', 'dist', 'dist-electron'],
+    coverage: {
+      provider: 'v8',
+      reporter: ['text', 'json', 'html'],
+      exclude: [
+        'node_modules',
+        'dist',
+        'dist-electron',
+        'tests',
+        '**/*.d.ts',
+        'vite.config.ts',
+        'vitest.config.ts'
+      ]
+    }
+  },
+  resolve: {
+    alias: {
+      '@': resolve(__dirname, 'src'),
+      '@electron': resolve(__dirname, 'electron'),
+      '@shared': resolve(__dirname, 'shared')
+    }
+  }
+})