├── src-tauri ├── build.rs ├── icons │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ └── android-chrome-512x512.png ├── src │ ├── lib.rs │ ├── main.rs │ ├── events.rs │ ├── utils.rs │ ├── api_config.rs │ ├── commands.rs │ ├── auth_manager.rs │ ├── patcher.rs │ └── machine.rs ├── gen │ └── schemas │ │ └── capabilities.json ├── capabilities │ └── main.json ├── Cargo.toml └── tauri.conf.json ├── assets ├── wx.jpg ├── zfb.jpg ├── image.png └── yijianchongzhi.gif ├── tsconfig.node.json ├── src ├── main.ts ├── env.d.ts ├── locales │ ├── index.ts │ ├── zh-CN.json │ └── en.json ├── components │ ├── LanguageSwitch.vue │ ├── AuthConfig.vue │ ├── ApiConfig.vue │ └── MachineId.vue └── App.vue ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── release.yml │ └── ci.yml ├── index.html ├── vite.config.ts ├── tsconfig.json ├── .gitignore ├── package.json ├── LICENSE ├── README_EN.md ├── README.md └── pnpm-lock.yaml /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /assets/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/assets/wx.jpg -------------------------------------------------------------------------------- /assets/zfb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/assets/zfb.jpg -------------------------------------------------------------------------------- /assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/assets/image.png -------------------------------------------------------------------------------- /assets/yijianchongzhi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/assets/yijianchongzhi.gif -------------------------------------------------------------------------------- /src-tauri/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/favicon.ico -------------------------------------------------------------------------------- /src-tauri/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/favicon-16x16.png -------------------------------------------------------------------------------- /src-tauri/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/favicon-32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /src-tauri/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /src-tauri/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elawen-Carl/Cursor_Pro_Helper/HEAD/src-tauri/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod api_config; 2 | pub mod auth_manager; 3 | pub mod commands; 4 | pub mod events; 5 | pub mod machine; 6 | pub mod patcher; 7 | pub mod utils; 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } -------------------------------------------------------------------------------- /src-tauri/gen/schemas/capabilities.json: -------------------------------------------------------------------------------- 1 | {"main":{"identifier":"main","description":"Capabilities for the main window","local":true,"windows":["main"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default"]}} -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import naive from "naive-ui"; 4 | import i18n from "./locales"; 5 | 6 | // 导入 Naive UI 样式 7 | import "vfonts/Lato.css"; 8 | import "vfonts/FiraCode.css"; 9 | 10 | const app = createApp(App); 11 | app.use(naive); 12 | app.use(i18n); 13 | app.mount("#app"); 14 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | declare module "*.vue" { 7 | import type { DefineComponent } from "@vue/runtime-core"; 8 | const component: DefineComponent<{}, {}, any>; 9 | export default component; 10 | } 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能请求 3 | about: 为这个项目提出一个想法 4 | title: '[Feature] ' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## 问题描述 10 | 11 | 如果你的功能请求与某个问题有关,请清楚地描述问题所在。 12 | 13 | ## 解决方案 14 | 15 | 描述你想要的解决方案。 16 | 17 | ## 替代方案 18 | 19 | 描述你考虑过的任何替代解决方案或功能。 20 | 21 | ## 其他信息 22 | 23 | 添加关于功能请求的任何其他上下文或截图。 24 | 25 | ## 实现建议 26 | 27 | 如果你对如何实现这个功能有建议,请在这里说明。 -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # 描述 2 | 3 | 请描述此PR的目的和改动内容。 4 | 5 | ## 类型 6 | 7 | - [ ] 功能新增 8 | - [ ] Bug修复 9 | - [ ] 性能优化 10 | - [ ] 代码重构 11 | - [ ] 文档更新 12 | - [ ] 其他 13 | 14 | ## 测试 15 | 16 | - [ ] 单元测试已添加/更新 17 | - [ ] 集成测试已添加/更新 18 | - [ ] 手动测试已完成 19 | 20 | ## 检查清单 21 | 22 | - [ ] 代码遵循项目编码规范 23 | - [ ] 所有测试通过 24 | - [ ] 文档已更新 25 | - [ ] 代码已经过自查 26 | - [ ] 变更已在本地测试 27 | 28 | ## 相关Issue 29 | 30 | Fixes #(issue) -------------------------------------------------------------------------------- /src-tauri/capabilities/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "main", 4 | "description": "Capabilities for the main window", 5 | "windows": [ 6 | "main" 7 | ], 8 | "permissions": [ 9 | "core:path:default", 10 | "core:event:default", 11 | "core:window:default", 12 | "core:app:default", 13 | "core:resources:default", 14 | "core:menu:default", 15 | "core:tray:default" 16 | ] 17 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug报告 3 | about: 创建Bug报告以帮助我们改进 4 | title: '[Bug] ' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ## 问题描述 10 | 11 | 简要描述你遇到的问题。 12 | 13 | ## 复现步骤 14 | 15 | 1. 打开 '...' 16 | 2. 点击 '....' 17 | 3. 滚动到 '....' 18 | 4. 看到错误 19 | 20 | ## 期望行为 21 | 22 | 描述你期望看到的结果。 23 | 24 | ## 截图 25 | 26 | 如果可以,请添加截图以帮助解释你的问题。 27 | 28 | ## 环境信息 29 | 30 | - 操作系统: [例如 Windows 10] 31 | - 软件版本: [例如 v1.0.0] 32 | - Rust版本: [例如 1.70.0] 33 | - Node版本: [例如 18.0.0] 34 | 35 | ## 其他信息 36 | 37 | 添加关于这个问题的任何其他上下文。 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cursor Pro Helper 8 | 9 | 10 | 11 |
12 | 13 | 14 | 28 | 29 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import { resolve } from "path"; 4 | import DefineOptions from "unplugin-vue-define-options/vite"; 5 | 6 | export default defineConfig({ 7 | plugins: [vue(), DefineOptions()], 8 | // Tauri 推荐配置 9 | clearScreen: false, 10 | server: { 11 | strictPort: true, 12 | }, 13 | envPrefix: ["VITE_", "TAURI_"], 14 | build: { 15 | target: ["es2021", "chrome100", "safari13"], 16 | minify: !process.env.TAURI_DEBUG ? "esbuild" : false, 17 | sourcemap: !!process.env.TAURI_DEBUG, 18 | }, 19 | resolve: { 20 | alias: { 21 | "@": resolve(__dirname, "src"), 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": ["ESNext", "DOM"], 13 | "skipLibCheck": true, 14 | "noEmit": true, 15 | "types": ["vite/client", "naive-ui/volar", "@vue/runtime-core"], 16 | "baseUrl": ".", 17 | "paths": { 18 | "@/*": ["src/*"] 19 | } 20 | }, 21 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust 相关 2 | **/target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | 6 | # Python 相关 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | *.so 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | venv/ 29 | .venv/ 30 | 31 | # Python logs 32 | scripts/logs/ 33 | logs/ 34 | scripts/cursor_accounts.txt 35 | 36 | # 日志文件 37 | *.log 38 | 39 | # IDE 相关 40 | .idea/ 41 | .vscode/ 42 | *.swp 43 | *.swo 44 | .DS_Store 45 | 46 | # 前端相关 47 | node_modules/ 48 | frontend/dist/ 49 | frontend/.vite/ 50 | frontend/node_modules/ 51 | frontend/.env 52 | frontend/.env.* 53 | 54 | # 项目特定 55 | *.bak 56 | *.vscdb 57 | *.vscdb-journal -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cursor-pro-helper", 3 | "private": true, 4 | "version": "1.3.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc --noEmit && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@tauri-apps/api": "^2.2.0", 13 | "@tauri-apps/cli": "^2.2.5", 14 | "@vicons/antd": "^0.13.0", 15 | "vfonts": "^0.0.3", 16 | "vue-i18n": "9" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^20.17.13", 20 | "@types/vue": "^1.0.31", 21 | "@vitejs/plugin-vue": "^4.0.0", 22 | "@volar/vue-language-core": "^1.6.5", 23 | "@vue/runtime-core": "^3.5.13", 24 | "@vue/runtime-dom": "^3.5.13", 25 | "naive-ui": "^2.41.0", 26 | "typescript": "^5.7.3", 27 | "unplugin-vue-define-options": "^1.5.3", 28 | "vite": "^4.0.0", 29 | "vue": "^3.5.13", 30 | "vue-tsc": "^2.2.0" 31 | } 32 | } -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release 2 | #![cfg_attr( 3 | all(not(debug_assertions), target_os = "windows"), 4 | windows_subsystem = "windows" 5 | )] 6 | 7 | use cursor_pro_helper::commands::*; 8 | 9 | fn main() { 10 | // 初始化日志 11 | tracing_subscriber::fmt::init(); 12 | 13 | tauri::Builder::default() 14 | .invoke_handler(tauri::generate_handler![ 15 | get_all_ids, 16 | get_machine_id, 17 | reset_machine_id, 18 | backup_config, 19 | restore_config, 20 | update_machine_id, 21 | update_auth, 22 | reset_auth, 23 | get_api_config, 24 | save_api_config, 25 | reset_api_config, 26 | get_mainjs_ids, 27 | ]) 28 | .run(tauri::generate_context!()) 29 | .expect("error while running tauri application"); 30 | } 31 | -------------------------------------------------------------------------------- /src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from "vue-i18n"; 2 | import en from "./en.json"; 3 | import zhCN from "./zh-CN.json"; 4 | 5 | // 获取浏览器语言设置 6 | const getBrowserLanguage = () => { 7 | const language = navigator.language; 8 | if (language.startsWith("zh")) { 9 | return "zh-CN"; 10 | } 11 | return "en"; 12 | }; 13 | 14 | // 获取存储的语言设置或使用浏览器语言 15 | const getStoredLanguage = () => { 16 | return localStorage.getItem("language") || getBrowserLanguage(); 17 | }; 18 | 19 | export type SupportedLocale = "zh-CN" | "en"; 20 | 21 | // 创建 i18n 实例 22 | const i18n = createI18n({ 23 | legacy: false, // 使用组合式 API 24 | locale: getStoredLanguage() as SupportedLocale, 25 | fallbackLocale: "en", 26 | messages: { 27 | en: en, 28 | "zh-CN": zhCN, 29 | }, 30 | }); 31 | 32 | // 导出语言切换函数 33 | export const setLanguage = (lang: SupportedLocale) => { 34 | i18n.global.locale.value = lang; 35 | localStorage.setItem("language", lang); 36 | }; 37 | 38 | export default i18n; 39 | -------------------------------------------------------------------------------- /src/components/LanguageSwitch.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | 35 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cursor-pro-helper" 3 | version = "1.3.0" 4 | description = "An Elegant Cursor Assistant Tool" 5 | authors = ["Elawen Carl"] 6 | edition = "2021" 7 | 8 | [[bin]] 9 | name = "cursor-pro-helper" 10 | path = "src/main.rs" 11 | 12 | [build-dependencies] 13 | tauri-build = { version = "2.0.5", features = [] } 14 | 15 | [dependencies] 16 | # GUI框架 17 | tauri = { version = "2.2.3", features = [] } 18 | # 错误处理 19 | anyhow = "1.0" 20 | thiserror = "1.0" 21 | # 序列化 22 | serde = { version = "1.0", features = ["derive"] } 23 | serde_json = "1.0" 24 | # 日志 25 | tracing = "0.1" 26 | tracing-subscriber = "0.3" 27 | # 异步运行时 28 | tokio = { version = "1.32", features = ["full"] } 29 | # HTTP客户端 30 | reqwest = { version = "0.11", features = ["json"] } 31 | # 系统信息 32 | sysinfo = "0.29" 33 | # 工具库 34 | rand = "0.8" 35 | uuid = { version = "1.4", features = ["v4"] } 36 | dirs = "5.0.1" 37 | rusqlite = { version = "0.29.0", features = ["bundled"] } 38 | regex = "1.5" 39 | 40 | # Windows特定依赖 41 | [target.'cfg(windows)'.dependencies] 42 | winreg = "0.50" 43 | 44 | [dev-dependencies] 45 | rstest = "0.18" 46 | tokio-test = "0.4" 47 | tempfile = "3.8" 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0) 2 | 3 | Copyright (c) 2024 Elawen Carl 4 | 5 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 6 | 7 | To view a copy of this license, visit: 8 | https://creativecommons.org/licenses/by-nc/4.0/ 9 | 10 | or send a letter to: 11 | Creative Commons 12 | PO Box 1866 13 | Mountain View, CA 94042 14 | USA 15 | 16 | You are free to: 17 | - Share — copy and redistribute the material in any medium or format 18 | - Adapt — remix, transform, and build upon the material 19 | 20 | Under the following terms: 21 | - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. 22 | - NonCommercial — You may not use the material for commercial purposes. 23 | 24 | Notices: 25 | - You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 26 | - No warranties are given. The license may not give you all of the permissions necessary for your intended use. 27 | 28 | For the full license text, see: 29 | https://creativecommons.org/licenses/by-nc/4.0/legalcode -------------------------------------------------------------------------------- /src-tauri/src/events.rs: -------------------------------------------------------------------------------- 1 | use serde_json::json; 2 | use tauri::{AppHandle, Emitter}; 3 | use tracing::info; 4 | 5 | /// 进度事件发送器 trait 6 | pub trait ProgressEmitter: Send + Sync { 7 | fn emit_progress(&self, message: &str); 8 | fn clone_box(&self) -> Box; 9 | } 10 | 11 | impl Clone for Box { 12 | fn clone(&self) -> Self { 13 | self.clone_box() 14 | } 15 | } 16 | 17 | /// 空进度事件发送器(不执行任何操作) 18 | #[derive(Clone)] 19 | pub struct NoopProgressEmitter; 20 | 21 | impl NoopProgressEmitter { 22 | pub fn new() -> Self { 23 | Self 24 | } 25 | } 26 | 27 | impl Default for NoopProgressEmitter { 28 | fn default() -> Self { 29 | Self::new() 30 | } 31 | } 32 | 33 | impl ProgressEmitter for NoopProgressEmitter { 34 | fn emit_progress(&self, _message: &str) { 35 | // 不执行任何操作 36 | } 37 | 38 | fn clone_box(&self) -> Box { 39 | Box::new(self.clone()) 40 | } 41 | } 42 | 43 | /// Tauri 进度事件发送器 44 | #[derive(Clone)] 45 | pub struct TauriProgressEmitter { 46 | app_handle: AppHandle, 47 | } 48 | 49 | impl TauriProgressEmitter { 50 | /// 创建新的 Tauri 进度事件发送器 51 | pub fn new(app_handle: AppHandle) -> Self { 52 | Self { app_handle } 53 | } 54 | } 55 | 56 | impl ProgressEmitter for TauriProgressEmitter { 57 | fn emit_progress(&self, message: &str) { 58 | info!("发送进度事件: {}", message); 59 | if let Err(e) = self.app_handle.emit( 60 | "reset_progress", 61 | json!({ 62 | "message": message 63 | }), 64 | ) { 65 | info!("发送进度事件失败: {}", e); 66 | } 67 | } 68 | 69 | fn clone_box(&self) -> Box { 70 | Box::new(self.clone()) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2.0.0-rc", 3 | "build": { 4 | "beforeDevCommand": { 5 | "script": "pnpm dev", 6 | "cwd": "./", 7 | "shell": true 8 | }, 9 | "beforeBuildCommand": { 10 | "script": "pnpm build", 11 | "cwd": "./", 12 | "shell": true 13 | }, 14 | "frontendDist": "../dist", 15 | "devUrl": "http://localhost:5173" 16 | }, 17 | "identifier": "com.cursorprohelper.app", 18 | "productName": "Cursor Pro Helper", 19 | "version": "1.3.0", 20 | "app": { 21 | "security": { 22 | "csp": null 23 | }, 24 | "withGlobalTauri": true, 25 | "windows": [ 26 | { 27 | "fullscreen": false, 28 | "height": 820, 29 | "resizable": true, 30 | "title": "Cursor Pro Helper", 31 | "width": 800, 32 | "center": true, 33 | "decorations": true 34 | } 35 | ] 36 | }, 37 | "bundle": { 38 | "active": true, 39 | "category": "DeveloperTool", 40 | "copyright": "", 41 | "externalBin": [], 42 | "icon": [ 43 | "icons/favicon-32x32.png", 44 | "icons/android-chrome-512x512.png", 45 | "icons/android-chrome-192x192.png", 46 | "icons/favicon.ico", 47 | "icons/favicon-16x16.png" 48 | ], 49 | "targets": "all", 50 | "windows": { 51 | "webviewInstallMode": { 52 | "type": "downloadBootstrapper" 53 | }, 54 | "digestAlgorithm": "sha256", 55 | "timestampUrl": "", 56 | "wix": { 57 | "template": null 58 | }, 59 | "nsis": { 60 | "template": null 61 | } 62 | }, 63 | "macOS": { 64 | "frameworks": [] 65 | }, 66 | "linux": { 67 | "appimage": { 68 | "bundleMediaFramework": true, 69 | "files": {} 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/components/AuthConfig.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 60 | 61 | -------------------------------------------------------------------------------- /src-tauri/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! 工具函数模块 2 | 3 | use anyhow::{Context, Result}; 4 | use rand::{thread_rng, RngCore}; 5 | use std::fmt::Write; 6 | use std::path::PathBuf; 7 | use tokio::fs; 8 | use uuid::Uuid; 9 | 10 | /// 生成64位十六进制字符串 11 | pub fn generate_hex_string() -> String { 12 | let mut rng = thread_rng(); 13 | let mut bytes = [0u8; 32]; 14 | rng.fill_bytes(&mut bytes); 15 | bytes.iter().fold(String::with_capacity(64), |mut acc, b| { 16 | let _ = write!(acc, "{:02x}", b); 17 | acc 18 | }) 19 | } 20 | 21 | /// 生成标准的机器ID(改为64位十六进制) 22 | pub fn generate_machine_id() -> String { 23 | let mut rng = thread_rng(); 24 | let mut bytes = [0u8; 32]; 25 | rng.fill_bytes(&mut bytes); 26 | bytes.iter().fold(String::with_capacity(64), |mut acc, b| { 27 | let _ = write!(acc, "{:02x}", b); 28 | acc 29 | }) 30 | } 31 | 32 | /// 生成UUID(用于devDeviceId) 33 | pub fn generate_uuid() -> String { 34 | Uuid::new_v4().to_string() 35 | } 36 | 37 | /// 生成标准的 SQM ID(改为大括号包裹的大写UUID) 38 | pub fn generate_sqm_id() -> String { 39 | format!("{{{}}}", Uuid::new_v4().to_string().to_uppercase()) 40 | } 41 | 42 | /// 生成默认的机器配置 43 | pub fn generate_default_machine_config() -> serde_json::Value { 44 | let machine_id = generate_machine_id(); 45 | let mac_machine_id = generate_machine_id(); 46 | let dev_device_id = generate_uuid(); 47 | let sqm_id = generate_sqm_id(); 48 | let telemetry_machine_id = generate_machine_id(); // 复用同一个生成器 49 | 50 | serde_json::json!({ 51 | "machineId": machine_id, 52 | "telemetry.machineId": telemetry_machine_id, 53 | "telemetry.macMachineId": mac_machine_id, 54 | "telemetry.devDeviceId": dev_device_id, 55 | "telemetry.sqmId": sqm_id 56 | }) 57 | } 58 | 59 | /// 设置文件的权限 60 | pub async fn set_file_permissions(file_path: &PathBuf, readonly: bool) -> Result<()> { 61 | let mut perms = fs::metadata(file_path).await?.permissions(); 62 | 63 | #[cfg(unix)] 64 | { 65 | use std::os::unix::fs::PermissionsExt; 66 | if readonly { 67 | perms.set_mode(0o444); // r--r--r-- 68 | } else { 69 | perms.set_mode(0o644); // rw-r--r-- 70 | } 71 | } 72 | 73 | #[cfg(windows)] 74 | { 75 | perms.set_readonly(readonly); 76 | } 77 | 78 | fs::set_permissions(file_path, perms) 79 | .await 80 | .context(format!("设置文件权限失败: {:?}", file_path))?; 81 | 82 | Ok(()) 83 | } 84 | 85 | /// 删除文件,如果文件存在且为只读,先解除只读属性 86 | pub async fn remove_file_if_exists(file_path: &PathBuf) -> Result<()> { 87 | if file_path.exists() { 88 | // 先解除只读属性 89 | set_file_permissions(file_path, false).await?; 90 | // 然后删除文件 91 | fs::remove_file(file_path) 92 | .await 93 | .context(format!("删除文件失败: {:?}", file_path))?; 94 | } 95 | Ok(()) 96 | } 97 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | create-release: 13 | runs-on: ubuntu-latest 14 | outputs: 15 | release_id: ${{ steps.create-release.outputs.result }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 18 24 | 25 | - name: Create Release 26 | id: create-release 27 | uses: actions/github-script@v6 28 | with: 29 | script: | 30 | const { data } = await github.rest.repos.createRelease({ 31 | owner: context.repo.owner, 32 | repo: context.repo.repo, 33 | tag_name: context.ref.replace('refs/tags/', ''), 34 | name: `Release ${context.ref.replace('refs/tags/', '')}`, 35 | draft: true, 36 | prerelease: false 37 | }) 38 | return data.id 39 | 40 | build-tauri: 41 | needs: create-release 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | platform: [macos-latest, ubuntu-latest, windows-latest] 46 | 47 | runs-on: ${{ matrix.platform }} 48 | steps: 49 | - uses: actions/checkout@v3 50 | 51 | - name: Setup Node.js 52 | uses: actions/setup-node@v3 53 | with: 54 | node-version: 18 55 | 56 | - name: Install pnpm 57 | uses: pnpm/action-setup@v2 58 | with: 59 | version: 8 60 | run_install: false 61 | 62 | - name: Install Rust stable 63 | uses: dtolnay/rust-toolchain@stable 64 | 65 | - name: Install dependencies (ubuntu only) 66 | if: matrix.platform == 'ubuntu-latest' 67 | run: | 68 | sudo apt-get update 69 | sudo apt-get install -y \ 70 | libgtk-3-dev \ 71 | libwebkit2gtk-4.1-dev \ 72 | libappindicator3-dev \ 73 | librsvg2-dev \ 74 | patchelf 75 | 76 | - name: Install frontend dependencies 77 | run: pnpm install 78 | 79 | - uses: Swatinem/rust-cache@v2 80 | with: 81 | workspaces: src-tauri 82 | 83 | - name: Build the app 84 | uses: tauri-apps/tauri-action@v0 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | with: 88 | releaseId: ${{ needs.create-release.outputs.release_id }} 89 | 90 | publish-release: 91 | runs-on: ubuntu-latest 92 | needs: [create-release, build-tauri] 93 | 94 | steps: 95 | - name: Publish release 96 | id: publish-release 97 | uses: actions/github-script@v6 98 | env: 99 | release_id: ${{ needs.create-release.outputs.release_id }} 100 | with: 101 | script: | 102 | await github.rest.repos.updateRelease({ 103 | owner: context.repo.owner, 104 | repo: context.repo.repo, 105 | release_id: process.env.release_id, 106 | draft: false 107 | }) -------------------------------------------------------------------------------- /src/locales/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "confirm": "确认", 4 | "cancel": "取消", 5 | "reset": "重置", 6 | "apply": "应用", 7 | "test": "测试", 8 | "success": "成功", 9 | "error": "错误", 10 | "loading": "加载中..." 11 | }, 12 | "apiConfig": { 13 | "title": "API 配置", 14 | "urlLabel": "API URL", 15 | "testApi": "测试 API", 16 | "testing": "测试中...", 17 | "applying": "应用中...", 18 | "resetToDefault": "重置为默认", 19 | "responseFormat": "API 返回格式示例", 20 | "testResponse": "测试接口返回数据", 21 | "noTestData": "暂无测试数据", 22 | "validUrl": "请输入有效的URL", 23 | "testSuccess": "API测试成功", 24 | "testFailed": "API测试失败", 25 | "applySuccess": "配置已成功应用", 26 | "applyFailed": "应用配置失败", 27 | "resetSuccess": "已重置为默认API URL", 28 | "resetFailed": "重置失败", 29 | "loadFailed": "加载配置失败" 30 | }, 31 | "machineId": { 32 | "title": "机器 ID", 33 | "configPath": "配置文件路径", 34 | "currentId": "当前 ID", 35 | "progress": "进度消息", 36 | "resetId": "一键重置", 37 | "modifyId": "修改 ID", 38 | "backup": "备份", 39 | "restore": "还原", 40 | "resetSuccess": "重置机器ID成功", 41 | "resetFailed": "重置机器ID失败", 42 | "modifySuccess": "修改机器ID成功", 43 | "modifyFailed": "修改机器ID失败", 44 | "backupSuccess": "备份成功", 45 | "backupFailed": "备份失败", 46 | "restoreSuccess": "还原成功", 47 | "restoreFailed": "还原失败", 48 | "getIdsFailed": "获取ID信息失败", 49 | "resetId045": "重置ID (0.45)", 50 | "reset045Success": "重置ID成功 (0.45版本)", 51 | "reset045Failed": "重置ID失败 (0.45版本)", 52 | "mainJsPath": "Main.js 文件路径", 53 | "getMainJsPathFailed": "获取 Main.js 路径失败", 54 | "getMainJsIdsFailed": "获取 Main.js 中的 ID 失败" 55 | }, 56 | "authConfig": { 57 | "title": "认证配置", 58 | "emailLabel": "邮箱", 59 | "emailPlaceholder": "请输入邮箱", 60 | "tokenLabel": "Token", 61 | "tokenPlaceholder": "请输入Token", 62 | "updating": "更新中...", 63 | "updateSuccess": "认证信息更新成功", 64 | "updateFailed": "更新认证信息失败", 65 | "errors": { 66 | "networkError": "获取随机账号失败: 请检查网络连接或API服务是否可用", 67 | "serverError": "获取响应内容失败: 服务器响应异常", 68 | "parseError": "解析API响应失败: 服务器返回的数据格式异常", 69 | "noAccounts": "暂无可用账号:API中没有可用的账号,请稍后再试或联系管理员添加账号", 70 | "updateFailed": "更新认证信息失败:无法写入认证数据,请检查程序权限", 71 | "deleteAccountFailed": "删除旧账号失败(不影响使用),但认证信息已更新成功", 72 | "apiError": "API返回失败", 73 | "missingEmail": "API返回的数据缺少邮箱字段", 74 | "missingToken": "API返回的数据缺少Token字段", 75 | "missingUser": "API返回的数据缺少用户字段", 76 | "deleteFailed": "删除账号失败", 77 | "deleteNetworkError": "删除账号时网络错误", 78 | "httpError": "HTTP请求失败", 79 | "loadConfigFailed": "加载API配置失败", 80 | "createConfigManagerFailed": "创建API配置管理器失败" 81 | }, 82 | "progress": { 83 | "start": "开始重置认证信息...", 84 | "gettingAccount": "正在获取随机账号...", 85 | "accountReceived": "成功获取随机账号", 86 | "updating": "开始更新认证信息...", 87 | "updateSuccess": "认证信息更新成功", 88 | "complete": "重置认证信息完成" 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Cursor Pro Helper 2 | 3 |
4 | 5 | [![CI](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml/badge.svg)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml) 6 | [![Release](https://img.shields.io/github/v/release/Elawen-Carl/Cursor_Pro_Helper?include_prereleases&style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 7 | [![Downloads](https://img.shields.io/github/downloads/Elawen-Carl/Cursor_Pro_Helper/total?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 8 | [![Stars](https://img.shields.io/github/stars/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/stargazers) 9 | [![Forks](https://img.shields.io/github/forks/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/network/members) 10 | [![Issues](https://img.shields.io/github/issues/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues) 11 | [![License](https://img.shields.io/badge/license-CC%20BY--NC%204.0-blue?style=flat-square)](https://creativecommons.org/licenses/by-nc/4.0/) 12 | 13 | An Elegant Cursor Assistant Tool 14 | 15 | English | [简体中文](./README.md) 16 | 17 |
18 | 19 | ## ✨ Features 20 | 21 | - 🚀 One-click machine ID reset 22 | - 🛡️ Secure local operation 23 | - 🎨 Elegant UI with light/dark themes 24 | - 💻 Cross-platform: Windows, macOS, Linux 25 | - 🔄 Configuration backup & restore 26 | - 🎯 Precise progress feedback 27 | 28 | ## 📦 Installation 29 | 30 | Download the latest release from the [Release](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) page: 31 | 32 | - Windows: `.msi` installer 33 | - macOS: `.dmg` package 34 | - Linux: `.AppImage` or `.deb` package 35 | 36 | ## 🚀 Usage 37 | 38 | 1. Install and run Cursor Pro Helper 39 | 2. Click the "Reset" button 40 | 3. Wait for the reset to complete 41 | 4. Restart Cursor 42 | 43 | ## 🔧 Troubleshooting 44 | 45 | ### Trial Period Still Expired After Reset 46 | 47 | Please ensure: 48 | 1. Cursor is completely closed 49 | 2. Run Cursor Pro Helper as administrator 50 | 3. Wait for reset completion before starting Cursor 51 | 52 | ### Cannot Modify Configuration File 53 | 54 | 1. Check file permissions 55 | 2. Ensure Cursor is fully closed 56 | 3. Try running as administrator 57 | 58 | ## 🛡️ Disclaimer 59 | 60 | This tool is for educational purposes only. Not for commercial use. Users bear all risks associated with its use. 61 | 62 | ## 🌟 Contributing 63 | 64 | Issues and Pull Requests are welcome! 65 | 66 | ## 📄 License 67 | 68 | This project is licensed under [CC BY-NC](https://creativecommons.org/licenses/by-nc/4.0/). 69 | 70 | ### Permissions 71 | - ❌ Commercial use: Not allowed 72 | - ✅ Modification: Allowed 73 | - ✅ Distribution: Allowed 74 | - ✅ Private use: Allowed 75 | - ✅ Attribution: Required 76 | 77 | ### Limitations 78 | - Keep license and copyright notice 79 | - Provide attribution 80 | - No commercial use 81 | 82 | ### Disclaimer 83 | This software is provided "as is", without warranty of any kind. 84 | 85 | ## ⭐ Project Stats 86 | 87 |
88 | 89 | [![Star History Chart](https://api.star-history.com/svg?repos=Elawen-Carl/Cursor_Pro_Helper&type=Date)](https://star-history.com/#Elawen-Carl/Cursor_Pro_Helper&Date) 90 | 91 | ![Repobeats analytics image](https://repobeats.axiom.co/api/embed/38d3d5cd30fe7e5dc0b67c03996d212d0323a5c0.svg "Repobeats analytics image") 92 | 93 |
94 | -------------------------------------------------------------------------------- /src-tauri/src/api_config.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use serde::{Deserialize, Serialize}; 3 | use std::fs; 4 | use std::path::PathBuf; 5 | use tracing::info; 6 | 7 | const DEFAULT_API_URL: &str = "https://cursor-account-api.vercel.app/account/random"; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct ApiConfig { 11 | pub url: String, 12 | } 13 | 14 | impl Default for ApiConfig { 15 | fn default() -> Self { 16 | Self { 17 | url: DEFAULT_API_URL.to_string(), 18 | } 19 | } 20 | } 21 | 22 | pub struct ApiConfigManager { 23 | config_path: PathBuf, 24 | } 25 | 26 | impl ApiConfigManager { 27 | pub fn new() -> Result { 28 | let config_path = if cfg!(windows) { 29 | dirs::config_dir() 30 | .expect("Failed to get config directory") 31 | .join("Cursor") 32 | .join("api_config.json") 33 | } else if cfg!(target_os = "macos") { 34 | dirs::home_dir() 35 | .expect("Failed to get home directory") 36 | .join("Library") 37 | .join("Application Support") 38 | .join("Cursor") 39 | .join("api_config.json") 40 | } else { 41 | dirs::config_dir() 42 | .expect("Failed to get config directory") 43 | .join("cursor") 44 | .join("api_config.json") 45 | }; 46 | 47 | // 确保配置目录存在 48 | if let Some(parent) = config_path.parent() { 49 | fs::create_dir_all(parent)?; 50 | } 51 | 52 | Ok(Self { config_path }) 53 | } 54 | 55 | pub fn load(&self) -> Result { 56 | if !self.config_path.exists() { 57 | info!("Config file not found, using default configuration"); 58 | return Ok(ApiConfig::default()); 59 | } 60 | 61 | let content = fs::read_to_string(&self.config_path)?; 62 | let config: ApiConfig = serde_json::from_str(&content)?; 63 | Ok(config) 64 | } 65 | 66 | pub fn save(&self, config: &ApiConfig) -> Result<()> { 67 | let content = serde_json::to_string_pretty(config)?; 68 | fs::write(&self.config_path, content)?; 69 | info!("API configuration saved successfully"); 70 | Ok(()) 71 | } 72 | 73 | pub fn reset(&self) -> Result { 74 | let config = ApiConfig::default(); 75 | self.save(&config)?; 76 | info!("API configuration reset to default"); 77 | Ok(config) 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | use tempfile::tempdir; 85 | 86 | #[test] 87 | fn test_api_config_manager() { 88 | let temp_dir = tempdir().unwrap(); 89 | let config_path = temp_dir.path().join("api_config.json"); 90 | 91 | let manager = ApiConfigManager { 92 | config_path: config_path.clone(), 93 | }; 94 | 95 | // Test default config 96 | let config = manager.load().unwrap(); 97 | assert_eq!(config.url, DEFAULT_API_URL); 98 | 99 | // Test saving config 100 | let new_config = ApiConfig { 101 | url: "https://example.com/api".to_string(), 102 | }; 103 | manager.save(&new_config).unwrap(); 104 | 105 | // Test loading saved config 106 | let loaded_config = manager.load().unwrap(); 107 | assert_eq!(loaded_config.url, new_config.url); 108 | 109 | // Test resetting config 110 | let reset_config = manager.reset().unwrap(); 111 | assert_eq!(reset_config.url, DEFAULT_API_URL); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 50 | -------------------------------------------------------------------------------- /src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "confirm": "Confirm", 4 | "cancel": "Cancel", 5 | "reset": "Reset", 6 | "apply": "Apply", 7 | "test": "Test", 8 | "success": "Success", 9 | "error": "Error", 10 | "loading": "Loading..." 11 | }, 12 | "authConfig": { 13 | "title": "Auth Config", 14 | "emailLabel": "Email", 15 | "emailPlaceholder": "Enter email", 16 | "tokenLabel": "Token", 17 | "tokenPlaceholder": "Enter token", 18 | "updating": "Updating...", 19 | "updateSuccess": "Auth info updated successfully", 20 | "updateFailed": "Failed to update auth info", 21 | "errors": { 22 | "networkError": "Failed to get random account: Please check your network connection or API service availability", 23 | "serverError": "Failed to get response: Server response error", 24 | "parseError": "Failed to parse API response: Server returned invalid data format", 25 | "noAccounts": "No accounts available: Please try again later or contact administrator to add accounts", 26 | "updateFailed": "Failed to update auth info: Unable to write auth data, please check program permissions", 27 | "deleteAccountFailed": "Failed to delete old account (no impact on usage), but auth info updated successfully", 28 | "apiError": "API returned error", 29 | "missingEmail": "API response missing email field", 30 | "missingToken": "API response missing token field", 31 | "missingUser": "API response missing user field", 32 | "deleteFailed": "Failed to delete account", 33 | "deleteNetworkError": "Network error while deleting account", 34 | "httpError": "HTTP request failed", 35 | "loadConfigFailed": "Failed to load API configuration", 36 | "createConfigManagerFailed": "Failed to create API configuration manager" 37 | }, 38 | "progress": { 39 | "start": "Starting auth reset process...", 40 | "gettingAccount": "Getting random account...", 41 | "accountReceived": "Successfully received random account", 42 | "updating": "Updating auth info...", 43 | "updateSuccess": "Auth info updated successfully", 44 | "complete": "Auth reset complete" 45 | } 46 | }, 47 | "apiConfig": { 48 | "title": "API Configuration", 49 | "urlLabel": "API URL", 50 | "testApi": "Test API", 51 | "testing": "Testing...", 52 | "applying": "Applying...", 53 | "resetToDefault": "Reset to Default", 54 | "responseFormat": "API Response Format Example", 55 | "testResponse": "Test Response Data", 56 | "noTestData": "No test data available", 57 | "validUrl": "Please enter a valid URL", 58 | "testSuccess": "API test successful", 59 | "testFailed": "API test failed", 60 | "applySuccess": "Configuration applied successfully", 61 | "applyFailed": "Failed to apply configuration", 62 | "resetSuccess": "Reset to default API URL", 63 | "resetFailed": "Reset failed", 64 | "loadFailed": "Failed to load configuration" 65 | }, 66 | "machineId": { 67 | "title": "Machine ID", 68 | "configPath": "Config File Path", 69 | "currentId": "Current ID", 70 | "progress": "Progress Messages", 71 | "resetId": "One-Click Reset", 72 | "modifyId": "Modify ID", 73 | "backup": "Backup", 74 | "restore": "Restore", 75 | "resetSuccess": "Machine ID reset successfully", 76 | "resetFailed": "Failed to reset machine ID", 77 | "modifySuccess": "Machine ID modified successfully", 78 | "modifyFailed": "Failed to modify machine ID", 79 | "backupSuccess": "Backup successful", 80 | "backupFailed": "Backup failed", 81 | "restoreSuccess": "Restore successful", 82 | "restoreFailed": "Restore failed", 83 | "getIdsFailed": "Failed to get ID information", 84 | "resetId045": "Reset ID (0.45)", 85 | "reset045Success": "Reset ID successful (0.45 version)", 86 | "reset045Failed": "Reset ID failed (0.45 version)", 87 | "mainJsPath": "Main.js File Path", 88 | "getMainJsPathFailed": "Failed to get Main.js path", 89 | "getMainJsIdsFailed": "Failed to get IDs from Main.js" 90 | } 91 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: 1 12 | RUSTFLAGS: "-D warnings" 13 | 14 | jobs: 15 | check: 16 | name: Check 17 | runs-on: ubuntu-latest 18 | defaults: 19 | run: 20 | working-directory: ./src-tauri 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Install dependencies 25 | run: | 26 | sudo apt-get update 27 | sudo apt-get install -y \ 28 | libgtk-3-dev \ 29 | libwebkit2gtk-4.1-dev \ 30 | libappindicator3-dev \ 31 | librsvg2-dev \ 32 | patchelf 33 | 34 | - name: Setup Node.js 35 | uses: actions/setup-node@v3 36 | with: 37 | node-version: 18 38 | 39 | - name: Install pnpm 40 | uses: pnpm/action-setup@v2 41 | with: 42 | version: 8 43 | run_install: false 44 | 45 | - name: Get pnpm store directory 46 | shell: bash 47 | run: | 48 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 49 | 50 | - uses: actions/cache@v3 51 | name: Setup pnpm cache 52 | with: 53 | path: ${{ env.STORE_PATH }} 54 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 55 | restore-keys: | 56 | ${{ runner.os }}-pnpm-store- 57 | 58 | - name: Install dependencies 59 | working-directory: . 60 | run: pnpm install 61 | 62 | - uses: dtolnay/rust-toolchain@stable 63 | - uses: Swatinem/rust-cache@v2 64 | with: 65 | workspaces: src-tauri 66 | - run: cargo check --all-features 67 | 68 | test: 69 | name: Test Suite 70 | runs-on: ubuntu-latest 71 | defaults: 72 | run: 73 | working-directory: ./src-tauri 74 | steps: 75 | - uses: actions/checkout@v3 76 | 77 | - name: Install dependencies 78 | run: | 79 | sudo apt-get update 80 | sudo apt-get install -y \ 81 | libgtk-3-dev \ 82 | libwebkit2gtk-4.1-dev \ 83 | libappindicator3-dev \ 84 | librsvg2-dev \ 85 | patchelf 86 | 87 | - name: Setup Node.js 88 | uses: actions/setup-node@v3 89 | with: 90 | node-version: 18 91 | 92 | - name: Install pnpm 93 | uses: pnpm/action-setup@v2 94 | with: 95 | version: 8 96 | run_install: false 97 | 98 | - name: Install dependencies 99 | working-directory: . 100 | run: pnpm install 101 | 102 | - uses: dtolnay/rust-toolchain@stable 103 | - uses: Swatinem/rust-cache@v2 104 | with: 105 | workspaces: src-tauri 106 | - run: cargo test --all-features 107 | 108 | fmt: 109 | name: Rustfmt 110 | runs-on: ubuntu-latest 111 | defaults: 112 | run: 113 | working-directory: ./src-tauri 114 | steps: 115 | - uses: actions/checkout@v3 116 | - uses: dtolnay/rust-toolchain@stable 117 | with: 118 | components: rustfmt 119 | - run: cargo fmt --all -- --check 120 | 121 | clippy: 122 | name: Clippy 123 | runs-on: ubuntu-latest 124 | defaults: 125 | run: 126 | working-directory: ./src-tauri 127 | steps: 128 | - uses: actions/checkout@v3 129 | 130 | - name: Install dependencies 131 | run: | 132 | sudo apt-get update 133 | sudo apt-get install -y \ 134 | libgtk-3-dev \ 135 | libwebkit2gtk-4.1-dev \ 136 | libappindicator3-dev \ 137 | librsvg2-dev \ 138 | patchelf 139 | 140 | - uses: dtolnay/rust-toolchain@stable 141 | with: 142 | components: clippy 143 | - uses: Swatinem/rust-cache@v2 144 | with: 145 | workspaces: src-tauri 146 | - run: cargo clippy --all-features -- -D warnings 147 | 148 | security_audit: 149 | name: Security Audit 150 | runs-on: ubuntu-latest 151 | defaults: 152 | run: 153 | working-directory: ./src-tauri 154 | steps: 155 | - uses: actions/checkout@v3 156 | - uses: dtolnay/rust-toolchain@stable 157 | - name: Install cargo-audit 158 | run: cargo install cargo-audit 159 | - name: Run security audit 160 | run: cargo audit 161 | 162 | coverage: 163 | name: Code Coverage 164 | runs-on: ubuntu-latest 165 | defaults: 166 | run: 167 | working-directory: ./src-tauri 168 | steps: 169 | - uses: actions/checkout@v3 170 | 171 | - name: Install dependencies 172 | run: | 173 | sudo apt-get update 174 | sudo apt-get install -y \ 175 | libgtk-3-dev \ 176 | libwebkit2gtk-4.1-dev \ 177 | libappindicator3-dev \ 178 | librsvg2-dev \ 179 | patchelf 180 | 181 | - uses: dtolnay/rust-toolchain@stable 182 | - uses: Swatinem/rust-cache@v2 183 | with: 184 | workspaces: src-tauri 185 | - name: Install cargo-tarpaulin 186 | run: cargo install cargo-tarpaulin 187 | - name: Generate coverage report 188 | run: cargo tarpaulin --out Xml 189 | - name: Upload coverage to Codecov 190 | uses: codecov/codecov-action@v3 -------------------------------------------------------------------------------- /src-tauri/src/commands.rs: -------------------------------------------------------------------------------- 1 | use crate::api_config::{ApiConfig, ApiConfigManager}; 2 | use crate::auth_manager; 3 | use crate::events::TauriProgressEmitter; 4 | use crate::machine::MachineService; 5 | use serde::{Deserialize, Serialize}; 6 | use tauri::command; 7 | use tracing::{error, info}; 8 | 9 | #[derive(Serialize, Deserialize, Debug)] 10 | #[serde(rename_all = "camelCase")] 11 | pub struct MachineIds { 12 | machine_id: String, 13 | mac_machine_id: String, 14 | dev_device_id: String, 15 | sqm_id: String, 16 | config_path: String, 17 | } 18 | 19 | #[derive(Serialize, Deserialize, Debug)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct MainJsIds { 22 | machine_id: String, 23 | mac_machine_id: String, 24 | dev_device_id: String, 25 | sqm_id: String, 26 | js_path: String, 27 | } 28 | 29 | #[command] 30 | pub async fn get_all_ids() -> Result { 31 | info!("开始获取所有 ID"); 32 | let machine_service = MachineService::new().await.map_err(|e| { 33 | error!("创建机器服务失败: {}", e); 34 | e.to_string() 35 | })?; 36 | 37 | let (machine_id, mac_machine_id, dev_device_id, sqm_id, config_path) = 38 | machine_service.get_all_ids().await.map_err(|e| { 39 | error!("获取所有 ID 失败: {}", e); 40 | e.to_string() 41 | })?; 42 | 43 | let result = MachineIds { 44 | machine_id, 45 | mac_machine_id, 46 | dev_device_id, 47 | sqm_id, 48 | config_path, 49 | }; 50 | 51 | info!("返回给前端的数据: {:?}", result); 52 | Ok(result) 53 | } 54 | 55 | #[command] 56 | pub async fn get_machine_id() -> Result { 57 | info!("开始获取机器 ID"); 58 | let machine_service = MachineService::new().await.map_err(|e| { 59 | error!("创建机器服务失败: {}", e); 60 | e.to_string() 61 | })?; 62 | machine_service.get_machine_id().await.map_err(|e| { 63 | error!("获取机器 ID 失败: {}", e); 64 | e.to_string() 65 | }) 66 | } 67 | 68 | #[command] 69 | pub async fn reset_machine_id(app_handle: tauri::AppHandle) -> Result<(), String> { 70 | info!("开始重置机器 ID"); 71 | let progress_emitter = Box::new(TauriProgressEmitter::new(app_handle)); 72 | let machine_service = MachineService::with_progress(progress_emitter) 73 | .await 74 | .map_err(|e| { 75 | error!("创建机器服务失败: {}", e); 76 | e.to_string() 77 | })?; 78 | 79 | machine_service.reset_machine_id().await.map_err(|e| { 80 | error!("重置机器 ID 失败: {}", e); 81 | e.to_string() 82 | }) 83 | } 84 | 85 | #[command] 86 | pub async fn backup_config(app_handle: tauri::AppHandle) -> Result<(), String> { 87 | info!("开始备份配置"); 88 | let progress_emitter = Box::new(TauriProgressEmitter::new(app_handle)); 89 | let machine_service = MachineService::with_progress(progress_emitter) 90 | .await 91 | .map_err(|e| { 92 | error!("创建机器服务失败: {}", e); 93 | e.to_string() 94 | })?; 95 | 96 | // 备份配置文件 97 | machine_service.backup_config().await.map_err(|e| { 98 | error!("备份配置文件失败: {}", e); 99 | e.to_string() 100 | })?; 101 | 102 | // 备份 main.js 103 | let patcher = crate::patcher::Patcher::new(None).map_err(|e| { 104 | error!("创建 Patcher 失败: {}", e); 105 | e.to_string() 106 | })?; 107 | 108 | patcher.backup().await.map_err(|e| { 109 | error!("备份 main.js 失败: {}", e); 110 | e.to_string() 111 | })?; 112 | 113 | Ok(()) 114 | } 115 | 116 | #[command] 117 | pub async fn restore_config(app_handle: tauri::AppHandle) -> Result<(), String> { 118 | info!("开始还原配置"); 119 | let progress_emitter = Box::new(TauriProgressEmitter::new(app_handle)); 120 | let machine_service = MachineService::with_progress(progress_emitter.clone()) 121 | .await 122 | .map_err(|e| { 123 | error!("创建机器服务失败: {}", e); 124 | e.to_string() 125 | })?; 126 | 127 | // 还原配置文件 128 | machine_service.restore_config().await.map_err(|e| { 129 | error!("还原配置文件失败: {}", e); 130 | e.to_string() 131 | })?; 132 | 133 | // 还原 main.js 134 | let patcher = crate::patcher::Patcher::new(None).map_err(|e| { 135 | error!("创建 Patcher 失败: {}", e); 136 | e.to_string() 137 | })?; 138 | 139 | patcher.restore().await.map_err(|e| { 140 | error!("还原 main.js 失败: {}", e); 141 | e.to_string() 142 | })?; 143 | 144 | Ok(()) 145 | } 146 | 147 | #[command] 148 | pub async fn update_machine_id(app_handle: tauri::AppHandle) -> Result<(), String> { 149 | info!("开始更新机器 ID"); 150 | let progress_emitter = Box::new(TauriProgressEmitter::new(app_handle)); 151 | let machine_service = MachineService::with_progress(progress_emitter) 152 | .await 153 | .map_err(|e| { 154 | error!("创建机器服务失败: {}", e); 155 | e.to_string() 156 | })?; 157 | 158 | // 更新配置文件中的 ID 159 | machine_service.update_machine_id().await.map_err(|e| { 160 | error!("更新机器 ID 失败: {}", e); 161 | e.to_string() 162 | })?; 163 | Ok(()) 164 | } 165 | 166 | #[command] 167 | pub async fn update_auth(email: String, token: String) -> Result<(), String> { 168 | info!("开始更新认证信息"); 169 | match auth_manager::update_auth(Some(email), Some(token.clone()), Some(token)) { 170 | true => Ok(()), 171 | false => Err("更新认证信息失败".to_string()), 172 | } 173 | } 174 | 175 | #[command] 176 | pub async fn reset_auth(app_handle: tauri::AppHandle) -> bool { 177 | let progress_emitter = Box::new(TauriProgressEmitter::new(app_handle)); 178 | auth_manager::reset_auth(&*progress_emitter).await 179 | } 180 | 181 | #[tauri::command] 182 | pub fn get_api_config() -> Result { 183 | let manager = ApiConfigManager::new().map_err(|e| e.to_string())?; 184 | manager.load().map_err(|e| e.to_string()) 185 | } 186 | 187 | #[tauri::command] 188 | pub fn save_api_config(url: String) -> Result<(), String> { 189 | let manager = ApiConfigManager::new().map_err(|e| e.to_string())?; 190 | let config = ApiConfig { url }; 191 | manager.save(&config).map_err(|e| e.to_string()) 192 | } 193 | 194 | #[tauri::command] 195 | pub fn reset_api_config() -> Result { 196 | let manager = ApiConfigManager::new().map_err(|e| e.to_string())?; 197 | manager.reset().map_err(|e| e.to_string()) 198 | } 199 | 200 | #[command] 201 | pub fn get_mainjs_ids() -> Result { 202 | info!("开始获取 main.js 中的 ID"); 203 | let patcher = crate::patcher::Patcher::new(None).map_err(|e| { 204 | error!("创建 Patcher 失败: {}", e); 205 | e.to_string() 206 | })?; 207 | 208 | let (machine_id, mac_machine_id, dev_device_id, sqm_id) = 209 | patcher.get_current_ids().map_err(|e| { 210 | error!("获取 main.js 中的 ID 失败: {}", e); 211 | e.to_string() 212 | })?; 213 | 214 | Ok(MainJsIds { 215 | machine_id, 216 | mac_machine_id, 217 | dev_device_id, 218 | sqm_id, 219 | js_path: patcher.get_js_path().to_string_lossy().to_string(), 220 | }) 221 | } 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cursor Pro Helper 2 | 3 |
4 | 5 | [![CI](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml/badge.svg)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml) 6 | [![Release](https://img.shields.io/github/v/release/Elawen-Carl/Cursor_Pro_Helper?include_prereleases&style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 7 | [![Downloads](https://img.shields.io/github/downloads/Elawen-Carl/Cursor_Pro_Helper/total?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 8 | [![Stars](https://img.shields.io/github/stars/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/stargazers) 9 | [![Forks](https://img.shields.io/github/forks/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/network/members) 10 | [![Issues](https://img.shields.io/github/issues/Elawen-Carl/Cursor_Pro_Helper?style=flat-square)](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues) 11 | [![License](https://img.shields.io/badge/license-CC%20BY--NC%204.0-blue?style=flat-square)](https://creativecommons.org/licenses/by-nc/4.0/) 12 | 13 |

14 | Cursor Logo 15 |

16 | 17 | **简体中文** | [English](./README_EN.md) 18 | 19 | 一个强大而优雅的 Cursor 辅助增强工具,基于 Rust + Tauri + Vue3 开发。 20 | 21 | [立即下载](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) · 22 | [问题反馈](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues) · 23 | [功能建议](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues) 24 | 25 |
26 | 27 | 28 | **快速解决:** 29 | 1. 关闭 Cursor 30 | 2. 执行一键重置 31 | 3. 重新登录 32 | 33 | **备选方案:** 34 | 1. 更换账号 35 | 2. 清理浏览器缓存 36 | 3. 检查网络连接 37 | 38 | ### ⚡ 一键重置演示 39 |
40 | 重置演示 41 |
42 | 43 | ## 👥 社区 44 | 45 | ### 交流群组 46 | - [Telegram](https://t.me/+S02jM99kuhUwM2Vl) 47 | 48 | ## ✨ 核心特性 49 | 50 | ### 🚀 高效重置 51 | - 一键重置机器ID 52 | - 智能配置管理 53 | - 快速恢复运行 54 | 55 | ### 🎨 现代界面 56 | - 简洁优雅的设计 57 | - 亮暗主题无缝切换 58 | - 流畅的动画效果 59 | 60 | ### 💻 全平台支持 61 | - Windows 系统优化 62 | - macOS 原生适配 63 | - Linux 完整支持 64 | 65 | ### 📊 实时反馈 66 | - 详细的进度提示 67 | - 清晰的状态展示 68 | - 智能的错误处理 69 | 70 | ## 📦 安装 71 | 72 | 从 [Release](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 页面下载最新版本安装包: 73 | 74 | - Windows: `.msi` 安装包 75 | - macOS: `.dmg` 安装包 76 | - Linux: `.AppImage` 或 `.deb` 包 77 | 78 | ## 📥 安装指南 79 | 80 | ### Windows 安装 81 | 1. 从 [Release](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases) 页面下载最新的 `.msi` 安装包 82 | 2. 双击安装包,按照安装向导进行安装 83 | 3. 安装完成后,在开始菜单找到 "Cursor Pro Helper" 启动 84 | 85 | ### macOS 安装 86 | 1. 下载最新的 `.dmg` 安装包 87 | 2. 打开 DMG 文件,将应用拖入 Applications 文件夹 88 | 3. 首次运行时,按住 Control 键点击应用图标,选择"打开" 89 | 90 | ### Linux 安装 91 | #### Ubuntu/Debian 92 | ```bash 93 | # 安装依赖 94 | sudo apt update 95 | sudo apt install -y \ 96 | libwebkit2gtk-4.1-dev \ 97 | libgtk-3-dev \ 98 | libayatana-appindicator3-dev \ 99 | librsvg2-dev 100 | 101 | # 安装应用 102 | sudo dpkg -i cursor-pro-helper.deb 103 | ``` 104 | 105 | ## 🚀 使用指南 106 | 107 | ### 快速开始 108 | 1. 点击"一键重置"按钮 109 | 2. 等待重置完成 110 | 111 | ### 高级功能 112 | #### 配置备份 113 | 1. 点击"备份"按钮创建配置备份 114 | 2. 备份文件保存在安全位置 115 | 3. 需要时可一键还原 116 | 117 | #### 自定义设置 118 | - 切换暗色/亮色主题 119 | - 调整界面布局 120 | - 配置自动备份 121 | 122 | ### 使用技巧 123 | 1. 定期备份配置,防止意外情况 124 | 2. 遇到问题先查看常见问题解答 125 | 3. 保持软件更新到最新版本 126 | 4. 合理使用重置功能,**避免频繁操作** 127 | 128 | 129 | ## 🛡️ 免责声明 130 | 131 | 本工具仅供学习交流使用,请勿用于商业用途。使用本工具所造成的任何问题由使用者自行承担。 132 | 133 | ## 🌟 贡献 134 | 135 | 欢迎提交Issue和Pull Request! 136 | 137 | ## 📄 开源协议 138 | 139 | 本项目采用 [CC BY-NC](https://creativecommons.org/licenses/by-nc/4.0/) 开源协议。 140 | 141 | ### 权限说明 142 | - ❌ 商业用途:不允许 143 | - ✅ 修改:允许 144 | - ✅ 分发:允许 145 | - ✅ 私人使用:允许 146 | - ✅ 署名:必需 147 | 148 | ### 限制条件 149 | - ❗ 保留许可证和版权声明 150 | - ❗ 标明原作者署名 151 | - ❗ 不得用于商业目的 152 | 153 | ### 免责声明 154 | 本软件按"原样"提供,不提供任何形式的保证。 155 | 156 | ## 🔧 技术栈 157 | 158 | ### 后端技术 159 | - 🦀 Rust 1.70+ 160 | - Tauri 2.x (GUI框架) 161 | - Tokio (异步运行时) 162 | - SQLite (数据存储) 163 | - Serde (序列化) 164 | - Tracing (日志) 165 | 166 | ### 前端技术 167 | - 🎨 Vue 3.5+ 168 | - TypeScript 5.7+ 169 | - Naive UI 170 | - Vue Router 171 | - Pinia 172 | 173 | ### 开发工具 174 | - 🛠️ VS Code / WebStorm 175 | - 📦 pnpm 176 | - 🔄 Rust Toolchain 177 | - 🐛 DevTools 178 | 179 | ## 💻 系统要求 180 | 181 | ### Windows 182 | - Windows 10/11 64位 183 | - 最新版 WebView2 Runtime 184 | - Visual C++ 2019+ Redistributable 185 | - 4GB+ 内存 186 | - 1GB 可用存储空间 187 | 188 | ### macOS 189 | - macOS 10.15+ (Catalina) 190 | - Intel 或 Apple Silicon 处理器 191 | - 4GB+ 内存 192 | - 1GB 可用存储空间 193 | 194 | ### Linux 195 | - Ubuntu 20.04+ / Debian / Fedora 196 | - libwebkit2gtk-4.1-dev 197 | - libgtk-3-dev 198 | - libayatana-appindicator3-dev 199 | - librsvg2-dev 200 | - 4GB+ 内存 201 | - 1GB 可用存储空间 202 | 203 | ## 📊 性能指标 204 | 205 | ### 资源占用 206 | - 内存占用:< 100MB 207 | - CPU使用率:< 5% 208 | - 磁盘空间:< 100MB 209 | - 启动时间:< 3秒 210 | 211 | ## 👨‍💻 开发相关 212 | 213 | ### 环境配置 214 | 1. 安装 Rust 215 | ```bash 216 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 217 | ``` 218 | 219 | 2. 安装 Node.js 和 pnpm 220 | ```bash 221 | # 使用 nvm 安装 Node.js 222 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 223 | nvm install 18 224 | nvm use 18 225 | 226 | # 安装 pnpm 227 | npm install -g pnpm 228 | ``` 229 | 230 | 3. 安装依赖 231 | ```bash 232 | # 安装前端依赖 233 | pnpm install 234 | 235 | # 安装 Rust 依赖 236 | cargo install tauri-cli 237 | ``` 238 | 239 | ### 开发命令 240 | ```bash 241 | # 开发模式 242 | pnpm tauri dev 243 | 244 | # 构建应用 245 | pnpm tauri build 246 | 247 | # 代码格式化 248 | cargo fmt && pnpm format 249 | 250 | # 代码检查 251 | cargo clippy && pnpm lint 252 | ``` 253 | 254 | ### 本地打包 255 | ```bash 256 | cargo tauri dev --target x86_64-pc-windows-msvc 257 | ``` 258 | 259 | ### 贡献指南 260 | 1. Fork 本仓库 261 | 2. 创建特性分支 262 | 3. 提交代码 263 | 4. 创建 Pull Request 264 | 265 | 266 | 267 | ### 贡献者 268 | 269 | 270 | 271 | 272 | ### 赞助支持 273 | 如果您觉得这个项目对您有帮助,欢迎赞助支持我们的开发工作! 274 | 275 | 276 |
277 |

要个饭~

278 | 重置演示 279 |
280 |
281 | 282 | 重置演示 283 | 重置演示 284 |
285 | 286 | 287 | ## ⚠️ 免责声明 288 | 289 | 1. 使用须知 290 | - 本工具仅供学习和研究使用 291 | - 请勿用于商业用途 292 | - 使用本工具产生的任何后果由使用者自行承担 293 | 294 | 2. 风险提示 295 | - 使用本工具可能违反相关服务条款 296 | - 可能导致账号被限制或封禁 297 | - 建议在测试环境中使用 298 | 299 | 3. 合规建议 300 | - 遵守相关法律法规 301 | - 尊重知识产权 302 | - 合理使用相关服务 303 | 304 | ## 📊 项目统计 305 | 306 |
307 | 308 | [![Star History Chart](https://api.star-history.com/svg?repos=Elawen-Carl/Cursor_Pro_Helper&type=Date)](https://star-history.com/#Elawen-Carl/Cursor_Pro_Helper&Date) 309 | 310 | ![Repobeats analytics image](https://repobeats.axiom.co/api/embed/38d3d5cd30fe7e5dc0b67c03996d212d0323a5c0.svg "Repobeats analytics image") 311 | 312 |
-------------------------------------------------------------------------------- /src/components/ApiConfig.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 179 | 180 | -------------------------------------------------------------------------------- /src-tauri/src/auth_manager.rs: -------------------------------------------------------------------------------- 1 | use crate::events::ProgressEmitter; 2 | use rusqlite::{Connection, Result as SqliteResult}; 3 | use serde::Deserialize; 4 | use std::fs; 5 | #[cfg(unix)] 6 | use std::os::unix::fs::PermissionsExt; 7 | use std::path::PathBuf; 8 | use tracing::{error, info}; 9 | 10 | #[derive(Debug, Deserialize)] 11 | #[allow(dead_code)] 12 | struct AccountData { 13 | email: String, 14 | #[serde(default)] 15 | password: String, 16 | token: String, 17 | #[serde(default)] 18 | user: String, 19 | #[serde(default = "default_usage_limit")] 20 | usage_limit: String, 21 | } 22 | 23 | fn default_usage_limit() -> String { 24 | "unlimited".to_string() 25 | } 26 | 27 | impl AccountData { 28 | fn validate(&self) -> Result<(), String> { 29 | if self.email.is_empty() { 30 | return Err("authConfig.errors.missingEmail".to_string()); 31 | } 32 | if self.token.is_empty() { 33 | return Err("authConfig.errors.missingToken".to_string()); 34 | } 35 | Ok(()) 36 | } 37 | } 38 | 39 | #[derive(Debug, Deserialize)] 40 | #[allow(dead_code)] 41 | struct ApiResponse { 42 | success: bool, 43 | data: AccountData, 44 | message: String, 45 | } 46 | 47 | impl ApiResponse { 48 | fn validate(&self) -> Result<(), String> { 49 | if !self.success { 50 | return Err(if self.message.contains("No accounts available") { 51 | "authConfig.errors.noAccounts".to_string() 52 | } else { 53 | format!("authConfig.errors.apiError: {}", self.message) 54 | }); 55 | } 56 | self.data.validate() 57 | } 58 | } 59 | 60 | /// 删除账号 61 | pub async fn delete_account(email: &str, progress_emitter: &dyn ProgressEmitter) -> bool { 62 | info!("Starting account deletion process for email: {}", email); 63 | progress_emitter.emit_progress(&format!("开始删除账号: {}", email)); 64 | 65 | let client = reqwest::Client::new(); 66 | let url = format!("https://cursor-account-api.vercel.app/account/{}", email); 67 | 68 | match client.delete(&url).send().await { 69 | Ok(response) => { 70 | if response.status().is_success() { 71 | info!("Successfully deleted account for email: {}", email); 72 | progress_emitter.emit_progress("账号删除成功"); 73 | true 74 | } else { 75 | let error_msg = format!("authConfig.errors.deleteFailed: {}", response.status()); 76 | error!("{}", error_msg); 77 | progress_emitter.emit_progress(&error_msg); 78 | false 79 | } 80 | } 81 | Err(e) => { 82 | let error_msg = format!("authConfig.errors.deleteNetworkError: {}", e); 83 | error!("{}", error_msg); 84 | progress_emitter.emit_progress(&error_msg); 85 | false 86 | } 87 | } 88 | } 89 | 90 | pub async fn reset_auth(progress_emitter: &dyn ProgressEmitter) -> bool { 91 | info!("Starting auth reset process"); 92 | progress_emitter.emit_progress("authConfig.progress.start"); 93 | 94 | // 获取随机账号 95 | progress_emitter.emit_progress("authConfig.progress.gettingAccount"); 96 | let client = reqwest::Client::new(); 97 | 98 | // 获取 API 配置 99 | let api_config = match crate::api_config::ApiConfigManager::new() { 100 | Ok(manager) => match manager.load() { 101 | Ok(config) => config, 102 | Err(e) => { 103 | let error_msg = format!("authConfig.errors.loadConfigFailed: {}", e); 104 | error!("{}", error_msg); 105 | progress_emitter.emit_progress(&error_msg); 106 | return false; 107 | } 108 | }, 109 | Err(e) => { 110 | let error_msg = format!("authConfig.errors.createConfigManagerFailed: {}", e); 111 | error!("{}", error_msg); 112 | progress_emitter.emit_progress(&error_msg); 113 | return false; 114 | } 115 | }; 116 | 117 | let response = match client.get(&api_config.url).send().await { 118 | Ok(resp) => resp, 119 | Err(e) => { 120 | let error_msg = format!("authConfig.errors.networkError: {}", e); 121 | error!("{}", error_msg); 122 | progress_emitter.emit_progress(&error_msg); 123 | return false; 124 | } 125 | }; 126 | 127 | // 检查响应状态码 128 | if !response.status().is_success() { 129 | let error_msg = format!("authConfig.errors.httpError: {}", response.status()); 130 | error!("{}", error_msg); 131 | progress_emitter.emit_progress(&error_msg); 132 | return false; 133 | } 134 | 135 | // 获取响应内容 136 | let response_text = match response.text().await { 137 | Ok(text) => text, 138 | Err(e) => { 139 | let error_msg = format!("authConfig.errors.serverError: {}", e); 140 | error!("{}", error_msg); 141 | progress_emitter.emit_progress(&error_msg); 142 | return false; 143 | } 144 | }; 145 | 146 | // 解析 JSON 响应 147 | let api_response: ApiResponse = match serde_json::from_str(&response_text) { 148 | Ok(data) => data, 149 | Err(e) => { 150 | let error_msg = format!("authConfig.errors.parseError: {}", e); 151 | error!("{}", error_msg); 152 | progress_emitter.emit_progress(&error_msg); 153 | return false; 154 | } 155 | }; 156 | 157 | // 验证 API 响应 158 | if let Err(e) = api_response.validate() { 159 | error!("API response validation failed: {}", e); 160 | progress_emitter.emit_progress(&e); 161 | return false; 162 | } 163 | 164 | progress_emitter.emit_progress("authConfig.progress.accountReceived"); 165 | 166 | // 更新认证信息 167 | progress_emitter.emit_progress("authConfig.progress.updating"); 168 | let email = api_response.data.email.clone(); 169 | let success = update_auth( 170 | Some(email.clone()), 171 | Some(api_response.data.token.clone()), 172 | Some(api_response.data.token), 173 | ); 174 | 175 | if !success { 176 | progress_emitter.emit_progress("authConfig.errors.updateFailed"); 177 | return false; 178 | } 179 | 180 | progress_emitter.emit_progress("authConfig.progress.updateSuccess"); 181 | 182 | // 如果更新成功,删除旧账号 183 | if success && !delete_account(&email, progress_emitter).await { 184 | error!("Failed to delete account after reset"); 185 | progress_emitter.emit_progress("authConfig.errors.deleteAccountFailed"); 186 | // 即使删除失败,我们仍然继续,因为认证信息已经更新 187 | } 188 | 189 | progress_emitter.emit_progress("authConfig.progress.complete"); 190 | success 191 | } 192 | 193 | pub fn update_auth( 194 | email: Option, 195 | access_token: Option, 196 | refresh_token: Option, 197 | ) -> bool { 198 | let db_path = get_db_path(); 199 | 200 | // Ensure directory exists 201 | if let Some(dir) = db_path.parent() { 202 | if let Err(e) = fs::create_dir_all(dir) { 203 | error!("Failed to create directory: {}", e); 204 | return false; 205 | } 206 | // Set directory permissions on Unix systems 207 | #[cfg(unix)] 208 | if let Err(e) = fs::set_permissions(dir, fs::Permissions::from_mode(0o755)) { 209 | error!("Failed to set directory permissions: {}", e); 210 | // Continue anyway as this is not critical 211 | } 212 | } 213 | 214 | // Initialize database if it doesn't exist 215 | if !db_path.exists() { 216 | if let Err(e) = initialize_db(&db_path) { 217 | error!("Failed to initialize database: {}", e); 218 | return false; 219 | } 220 | // Set file permissions on Unix systems 221 | #[cfg(unix)] 222 | if let Err(e) = fs::set_permissions(&db_path, fs::Permissions::from_mode(0o644)) { 223 | error!("Failed to set file permissions: {}", e); 224 | // Continue anyway as this is not critical 225 | } 226 | } 227 | 228 | match update_auth_internal(db_path, email, access_token, refresh_token) { 229 | Ok(_) => true, 230 | Err(e) => { 231 | match e { 232 | rusqlite::Error::SqliteFailure(_, Some(msg)) => { 233 | error!("SQLite error: {}", msg); 234 | } 235 | rusqlite::Error::SqliteFailure(err, None) => { 236 | error!("SQLite error code: {}", err); 237 | } 238 | _ => error!("Database error: {}", e), 239 | } 240 | false 241 | } 242 | } 243 | } 244 | 245 | fn get_db_path() -> PathBuf { 246 | if cfg!(windows) { 247 | if let Some(appdata) = std::env::var_os("APPDATA") { 248 | PathBuf::from(appdata) 249 | .join("Cursor") 250 | .join("User") 251 | .join("globalStorage") 252 | .join("state.vscdb") 253 | } else { 254 | panic!("APPDATA environment variable not set"); 255 | } 256 | } else if cfg!(target_os = "macos") { 257 | dirs::home_dir() 258 | .expect("Could not find home directory") 259 | .join("Library") 260 | .join("Application Support") 261 | .join("Cursor") 262 | .join("User") 263 | .join("globalStorage") 264 | .join("state.vscdb") 265 | } else { 266 | // Linux 267 | dirs::home_dir() 268 | .expect("Could not find home directory") 269 | .join(".config") 270 | .join("Cursor") 271 | .join("User") 272 | .join("globalStorage") 273 | .join("state.vscdb") 274 | } 275 | } 276 | 277 | fn initialize_db(db_path: &PathBuf) -> SqliteResult<()> { 278 | let conn = Connection::open(db_path)?; 279 | conn.execute( 280 | "CREATE TABLE IF NOT EXISTS ItemTable ( 281 | key TEXT PRIMARY KEY, 282 | value TEXT 283 | )", 284 | [], 285 | )?; 286 | Ok(()) 287 | } 288 | 289 | fn update_auth_internal( 290 | db_path: PathBuf, 291 | email: Option, 292 | access_token: Option, 293 | refresh_token: Option, 294 | ) -> SqliteResult<()> { 295 | let mut conn = Connection::open(db_path)?; 296 | 297 | // Set pragmas for better performance 298 | conn.execute_batch( 299 | " 300 | PRAGMA busy_timeout = 5000; 301 | PRAGMA journal_mode = WAL; 302 | PRAGMA synchronous = NORMAL; 303 | ", 304 | )?; 305 | 306 | let tx = conn.transaction()?; 307 | 308 | let mut updates = Vec::new(); 309 | if let Some(email) = email { 310 | updates.push(("cursorAuth/cachedEmail", email)); 311 | } 312 | if let Some(token) = access_token { 313 | updates.push(("cursorAuth/accessToken", token)); 314 | } 315 | if let Some(token) = refresh_token { 316 | updates.push(("cursorAuth/refreshToken", token)); 317 | updates.push(("cursorAuth/cachedSignUpType", "Auth_0".to_string())); 318 | } 319 | 320 | let result: SqliteResult<()> = (|| { 321 | for (key, value) in updates { 322 | let count: i64 = tx.query_row( 323 | "SELECT COUNT(*) FROM ItemTable WHERE key = ?", 324 | [key], 325 | |row| row.get(0), 326 | )?; 327 | 328 | if count == 0 { 329 | tx.execute( 330 | "INSERT INTO ItemTable (key, value) VALUES (?, ?)", 331 | [key, &value], 332 | )?; 333 | } else { 334 | tx.execute( 335 | "UPDATE ItemTable SET value = ? WHERE key = ?", 336 | [&value, key], 337 | )?; 338 | } 339 | info!("Updating {}", key.split('/').last().unwrap_or(key)); 340 | } 341 | tx.commit()?; 342 | info!("Database updated successfully"); 343 | Ok(()) 344 | })(); 345 | 346 | // Ensure connection is closed even if there's an error 347 | drop(conn); 348 | 349 | result 350 | } 351 | -------------------------------------------------------------------------------- /src-tauri/src/patcher.rs: -------------------------------------------------------------------------------- 1 | use crate::utils; 2 | use anyhow::{Context, Result}; 3 | use rand::Rng; 4 | use regex::Regex; 5 | use std::fs; 6 | use std::path::PathBuf; 7 | use tracing::{error, info}; 8 | 9 | pub struct Patcher { 10 | js_path: PathBuf, 11 | data: String, 12 | } 13 | 14 | impl Patcher { 15 | pub fn new(js_path: Option<&str>) -> Result { 16 | let path = if let Some(p) = js_path { 17 | PathBuf::from(p) 18 | } else { 19 | Self::get_default_js_path().context("无法获取默认的 main.js 路径")? 20 | }; 21 | 22 | if !path.exists() { 23 | return Err(anyhow::anyhow!("main.js not found")); 24 | } 25 | 26 | let data = fs::read_to_string(&path).context("读取 main.js 失败")?; 27 | 28 | Ok(Self { 29 | js_path: path, 30 | data, 31 | }) 32 | } 33 | 34 | /// 备份 main.js 文件 35 | pub async fn backup(&self) -> Result<()> { 36 | info!("开始备份 main.js 文件"); 37 | let backup_path = self.js_path.with_extension("js.bak"); 38 | 39 | // 确保备份文件的父目录存在 40 | if let Some(parent) = backup_path.parent() { 41 | tokio::fs::create_dir_all(parent) 42 | .await 43 | .context(format!("创建备份目录失败: {:?}", parent))?; 44 | } 45 | 46 | // 删除旧的备份文件(如果存在) 47 | utils::remove_file_if_exists(&backup_path).await?; 48 | 49 | // 执行备份 50 | tokio::fs::copy(&self.js_path, &backup_path) 51 | .await 52 | .context(format!( 53 | "备份 main.js 失败: 从 {:?} 复制到 {:?}", 54 | self.js_path, backup_path 55 | ))?; 56 | 57 | // 设置备份文件为只读 58 | utils::set_file_permissions(&backup_path, true).await?; 59 | 60 | info!("main.js 备份完成"); 61 | Ok(()) 62 | } 63 | 64 | /// 还原 main.js 文件 65 | pub async fn restore(&self) -> Result<()> { 66 | info!("开始还原 main.js 文件"); 67 | let backup_path = self.js_path.with_extension("js.bak"); 68 | 69 | if !backup_path.exists() { 70 | let err_msg = format!("备份文件不存在: {:?}", backup_path); 71 | error!("{}", err_msg); 72 | return Err(anyhow::anyhow!(err_msg)); 73 | } 74 | 75 | // 删除当前文件 76 | utils::remove_file_if_exists(&self.js_path).await?; 77 | 78 | // 执行还原 79 | tokio::fs::copy(&backup_path, &self.js_path) 80 | .await 81 | .context(format!( 82 | "还原 main.js 失败: 从 {:?} 复制到 {:?}", 83 | backup_path, self.js_path 84 | ))?; 85 | 86 | info!("main.js 还原完成"); 87 | Ok(()) 88 | } 89 | 90 | pub fn get_js_path(&self) -> &PathBuf { 91 | &self.js_path 92 | } 93 | 94 | fn get_default_js_path() -> Result { 95 | #[cfg(target_os = "windows")] 96 | { 97 | if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") { 98 | let path = PathBuf::from(local_app_data) 99 | .join("Programs") 100 | .join("cursor") 101 | .join("resources") 102 | .join("app") 103 | .join("out") 104 | .join("main.js"); 105 | if path.exists() { 106 | return Ok(path); 107 | } 108 | } 109 | } 110 | 111 | #[cfg(target_os = "macos")] 112 | { 113 | let path = PathBuf::from("/Applications/Cursor.app/Contents/Resources/app/out/main.js"); 114 | if path.exists() { 115 | return Ok(path); 116 | } 117 | } 118 | 119 | #[cfg(target_os = "linux")] 120 | { 121 | let paths = vec![ 122 | "/opt/Cursor/resources/app/out/main.js", 123 | "/usr/share/cursor/resources/app/out/main.js", 124 | ]; 125 | for p in paths { 126 | let path = PathBuf::from(p); 127 | if path.exists() { 128 | return Ok(path); 129 | } 130 | } 131 | } 132 | 133 | Err(anyhow::anyhow!("Could not find default main.js path")) 134 | } 135 | 136 | fn generate_mac() -> String { 137 | let mut rng = rand::thread_rng(); 138 | let invalid_macs = [ 139 | "00:00:00:00:00:00", 140 | "ff:ff:ff:ff:ff:ff", 141 | "ac:de:48:00:11:22", 142 | ]; 143 | 144 | loop { 145 | let mac: String = (0..6) 146 | .map(|_| format!("{:02X}", rng.gen_range(0..=255))) 147 | .collect::>() 148 | .join(":"); 149 | 150 | if !invalid_macs.contains(&mac.as_str()) { 151 | return mac; 152 | } 153 | } 154 | } 155 | 156 | pub async fn patch( 157 | &mut self, 158 | machine_id: Option, 159 | mac_addr: Option, 160 | sqm_id: Option, 161 | dev_device_id: Option, 162 | ) -> Result<()> { 163 | // 备份原始文件 164 | let backup_path = self.js_path.with_extension("js.bak"); 165 | if !backup_path.exists() { 166 | fs::copy(&self.js_path, &backup_path).context("备份 main.js 失败")?; 167 | } 168 | 169 | // 设置文件为可写 170 | utils::set_file_permissions(&self.js_path, false).await?; 171 | 172 | let mut data = self.data.clone(); 173 | 174 | // 生成或使用提供的 ID 175 | let machine_id = machine_id.unwrap_or_else(utils::generate_uuid); 176 | let mac = mac_addr.unwrap_or_else(Self::generate_mac); 177 | let sqm = sqm_id.unwrap_or_default(); 178 | let dev_id = dev_device_id.unwrap_or_else(utils::generate_uuid); 179 | 180 | // Patch machine ID 181 | info!("Patching machine ID: {}", machine_id); 182 | data = self.replace_in_data( 183 | &data, 184 | r"=.{0,50}timeout.{0,10}5e3.*?,", 185 | &format!("=/*csp1*/\"{}\"/*1csp*/,", machine_id), 186 | r"=/\*csp1\*/.*?/\*1csp\*/,", 187 | )?; 188 | 189 | // Patch MAC address 190 | info!("Patching MAC address: {}", mac); 191 | data = self.replace_in_data( 192 | &data, 193 | r"(function .{0,50}\{).{0,300}Unable to retrieve mac address.*?(\})", 194 | &format!(r#"$1 return/*csp2*/"{}"/*2csp*/; $2"#, mac), 195 | r"()return/\*csp2\*/.*?/\*2csp\*/;()", 196 | )?; 197 | 198 | // Patch SQM ID 199 | info!("Patching SQM ID: {}", sqm); 200 | data = self.replace_in_data( 201 | &data, 202 | r#"return.{0,50}\.GetStringRegKey.*?HKEY_LOCAL_MACHINE.*?MachineId.*?\|\|.*?"""#, 203 | &format!("return/*csp3*/\"{}\"/*3csp*/", sqm), 204 | r"return/\*csp3\*/.*?/\*3csp\*/", 205 | )?; 206 | 207 | // Patch device ID 208 | info!("Patching device ID: {}", dev_id); 209 | data = self.replace_in_data( 210 | &data, 211 | r"return.{0,50}vscode/deviceid.*?getDeviceId\(\)", 212 | &format!("return/*csp4*/\"{}\"/*4csp*/", dev_id), 213 | r"return/\*csp4\*/.*?/\*4csp\*/", 214 | )?; 215 | 216 | // 保存修改 217 | fs::write(&self.js_path, &data).context("保存修改失败")?; 218 | 219 | // 更新内部数据 220 | self.data = data; 221 | 222 | // 恢复文件为只读 223 | utils::set_file_permissions(&self.js_path, true).await?; 224 | 225 | Ok(()) 226 | } 227 | 228 | fn replace_in_data( 229 | &self, 230 | data: &str, 231 | pattern: &str, 232 | replacement: &str, 233 | probe: &str, 234 | ) -> Result { 235 | let regex = Regex::new(pattern).context("创建正则表达式失败")?; 236 | let probe_regex = Regex::new(probe).context("创建探测正则表达式失败")?; 237 | 238 | let count = regex.find_iter(data).count(); 239 | let patched_count = probe_regex.find_iter(data).count(); 240 | 241 | if count == 0 { 242 | if patched_count > 0 { 243 | info!( 244 | "Found {} pattern{} already patched, will overwrite", 245 | patched_count, 246 | if patched_count == 1 { "" } else { "s" } 247 | ); 248 | } else { 249 | info!("Warning: Pattern not found, SKIPPED!"); 250 | return Ok(data.to_string()); 251 | } 252 | } 253 | 254 | let mut result = data.to_string(); 255 | let mut replaced1_count = 0; 256 | let mut replaced2_count = 0; 257 | 258 | // 先处理已经打过补丁的内容 259 | if patched_count > 0 { 260 | let new_result = probe_regex.replace_all(&result, replacement); 261 | replaced1_count = patched_count - probe_regex.find_iter(&new_result).count(); 262 | result = new_result.into_owned(); 263 | } 264 | 265 | // 处理新的匹配 266 | if count > 0 { 267 | let new_result = regex.replace_all(&result, replacement); 268 | replaced2_count = count - regex.find_iter(&new_result).count(); 269 | result = new_result.into_owned(); 270 | } 271 | 272 | let replaced_count = replaced1_count + replaced2_count; 273 | if replaced_count != count + patched_count { 274 | info!( 275 | "Warning: Patched {}/{}, failed {}", 276 | replaced_count, 277 | count + patched_count, 278 | count + patched_count - replaced_count 279 | ); 280 | } else { 281 | info!( 282 | "Patched {} pattern{}", 283 | replaced_count, 284 | if replaced_count == 1 { "" } else { "s" } 285 | ); 286 | } 287 | 288 | Ok(result) 289 | } 290 | 291 | pub fn restore_original(&self) -> Result<(), std::io::Error> { 292 | let backup_path = self.js_path.with_extension("js.bak"); 293 | if backup_path.exists() { 294 | fs::copy(&backup_path, &self.js_path)?; 295 | } 296 | Ok(()) 297 | } 298 | 299 | pub fn get_current_ids(&self) -> Result<(String, String, String, String)> { 300 | // 使用与 Python 版本相同的正则表达式格式 301 | let machine_id_regex = Regex::new(r#"/\*csp1\*/"([^"]*)"/\*1csp\*/"#) 302 | .context("创建 machine ID 正则表达式失败")?; 303 | let mac_regex = Regex::new(r#"/\*csp2\*/"([^"]*)"/\*2csp\*/"#) 304 | .context("创建 MAC address 正则表达式失败")?; 305 | let sqm_regex = 306 | Regex::new(r#"/\*csp3\*/"([^"]*)"/\*3csp\*/"#).context("创建 SQM ID 正则表达式失败")?; 307 | let device_id_regex = Regex::new(r#"/\*csp4\*/"([^"]*)"/\*4csp\*/"#) 308 | .context("创建 device ID 正则表达式失败")?; 309 | 310 | let machine_id = match machine_id_regex.captures(&self.data) { 311 | Some(cap) => { 312 | info!("Found machine_id match: {:?}", cap.get(1)); 313 | cap.get(1) 314 | .map(|m| m.as_str().to_string()) 315 | .unwrap_or_default() 316 | } 317 | None => { 318 | info!("No machine_id match found"); 319 | String::default() 320 | } 321 | }; 322 | 323 | let mac_machine_id = match mac_regex.captures(&self.data) { 324 | Some(cap) => { 325 | info!("Found mac match: {:?}", cap.get(1)); 326 | cap.get(1) 327 | .map(|m| m.as_str().to_string()) 328 | .unwrap_or_default() 329 | } 330 | None => { 331 | info!("No mac match found"); 332 | String::default() 333 | } 334 | }; 335 | 336 | let sqm_id = match sqm_regex.captures(&self.data) { 337 | Some(cap) => { 338 | info!("Found sqm match: {:?}", cap.get(1)); 339 | cap.get(1) 340 | .map(|m| m.as_str().to_string()) 341 | .unwrap_or_default() 342 | } 343 | None => { 344 | info!("No sqm match found"); 345 | String::default() 346 | } 347 | }; 348 | 349 | let dev_device_id = match device_id_regex.captures(&self.data) { 350 | Some(cap) => { 351 | info!("Found device match: {:?}", cap.get(1)); 352 | cap.get(1) 353 | .map(|m| m.as_str().to_string()) 354 | .unwrap_or_default() 355 | } 356 | None => { 357 | info!("No device match found"); 358 | String::default() 359 | } 360 | }; 361 | 362 | Ok((machine_id, mac_machine_id, dev_device_id, sqm_id)) 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/components/MachineId.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 319 | 320 | -------------------------------------------------------------------------------- /src-tauri/src/machine.rs: -------------------------------------------------------------------------------- 1 | //! 机器 ID 服务模块 2 | //! 3 | //! 提供机器 ID 的管理功能,包括: 4 | //! - 获取当前机器 ID 5 | //! - 生成新的机器 ID 6 | //! - 更新机器 ID 7 | //! - 备份和还原 8 | 9 | use crate::auth_manager; 10 | use crate::events::{NoopProgressEmitter, ProgressEmitter}; 11 | use crate::utils; 12 | use anyhow::{Context, Result}; 13 | use serde_json::Value; 14 | use std::path::PathBuf; 15 | use std::process::Command; 16 | use tokio::fs; 17 | use tokio::time::sleep; 18 | use tokio::time::Duration; 19 | use tracing::{error, info, warn}; 20 | 21 | #[cfg(windows)] 22 | use sysinfo::{ProcessExt, System, SystemExt}; 23 | #[cfg(windows)] 24 | use winreg::enums::*; 25 | #[cfg(windows)] 26 | use winreg::RegKey; 27 | 28 | /// 机器服务 29 | pub struct MachineService { 30 | /// 配置文件路径 31 | config_path: PathBuf, 32 | /// 备份文件路径 33 | backup_path: PathBuf, 34 | /// 进度事件发送器 35 | progress_emitter: Box, 36 | } 37 | 38 | impl MachineService { 39 | /// 创建新的机器服务实例 40 | pub async fn new() -> Result { 41 | let config_path = Self::get_storage_path(None)?; 42 | let backup_path = config_path.with_extension("json.bak"); 43 | 44 | // 确保父目录存在 45 | if let Some(parent) = config_path.parent() { 46 | fs::create_dir_all(parent) 47 | .await 48 | .context("创建配置目录失败")?; 49 | } 50 | 51 | let instance = Self { 52 | config_path, 53 | backup_path, 54 | progress_emitter: Box::new(NoopProgressEmitter), 55 | }; 56 | 57 | // 确保配置文件存在 58 | if !instance.config_path.exists() { 59 | info!("配置文件不存在,正在创建默认配置"); 60 | let default_config = utils::generate_default_machine_config(); 61 | instance.write_config(&default_config).await?; 62 | info!("默认配置创建成功"); 63 | } 64 | 65 | Ok(instance) 66 | } 67 | 68 | /// 使用进度发送器创建实例 69 | pub async fn with_progress(progress_emitter: Box) -> Result { 70 | let config_path = Self::get_storage_path(None)?; 71 | let backup_path = config_path.with_extension("json.bak"); 72 | 73 | // 确保父目录存在 74 | if let Some(parent) = config_path.parent() { 75 | fs::create_dir_all(parent) 76 | .await 77 | .context("创建配置目录失败")?; 78 | } 79 | 80 | let instance = Self { 81 | config_path, 82 | backup_path, 83 | progress_emitter, 84 | }; 85 | 86 | // 确保配置文件存在 87 | if !instance.config_path.exists() { 88 | info!("配置文件不存在,正在创建默认配置"); 89 | let default_config = utils::generate_default_machine_config(); 90 | instance.write_config(&default_config).await?; 91 | info!("默认配置创建成功"); 92 | } 93 | 94 | Ok(instance) 95 | } 96 | 97 | /// 获取配置文件路径 98 | fn get_storage_path(root_dir: Option) -> Result { 99 | let home_dir = root_dir.unwrap_or_else(|| dirs::home_dir().expect("无法获取用户主目录")); 100 | 101 | let path = match std::env::consts::OS { 102 | "windows" => home_dir 103 | .join("AppData") 104 | .join("Roaming") 105 | .join("Cursor") 106 | .join("User") 107 | .join("globalStorage") 108 | .join("storage.json"), 109 | "macos" => home_dir 110 | .join("Library") 111 | .join("Application Support") 112 | .join("Cursor") 113 | .join("User") 114 | .join("globalStorage") 115 | .join("storage.json"), 116 | "linux" => home_dir 117 | .join(".config") 118 | .join("Cursor") 119 | .join("User") 120 | .join("globalStorage") 121 | .join("storage.json"), 122 | _ => return Err(anyhow::anyhow!("不支持的操作系统")), 123 | }; 124 | 125 | Ok(path) 126 | } 127 | 128 | /// 发送进度事件 129 | fn emit_progress(&self, message: &str) { 130 | self.progress_emitter.emit_progress(message); 131 | } 132 | 133 | /// 读取配置文件 134 | async fn read_config(&self) -> Result { 135 | if !self.config_path.exists() { 136 | info!("配置文件不存在,创建默认配置"); 137 | let default_config = utils::generate_default_machine_config(); 138 | self.write_config(&default_config).await?; 139 | return Ok(default_config); 140 | } 141 | 142 | let content = fs::read_to_string(&self.config_path) 143 | .await 144 | .context("读取配置文件失败")?; 145 | 146 | let value: Value = serde_json::from_str(&content).context("解析配置文件失败")?; 147 | 148 | Ok(value) 149 | } 150 | 151 | /// 写入配置文件 152 | async fn write_config(&self, value: &Value) -> Result<()> { 153 | // 确保父目录存在 154 | if let Some(parent) = self.config_path.parent() { 155 | fs::create_dir_all(parent) 156 | .await 157 | .context("创建配置目录失败")?; 158 | } 159 | 160 | let content = serde_json::to_string_pretty(value).context("序列化配置失败")?; 161 | 162 | fs::write(&self.config_path, content) 163 | .await 164 | .context("写入配置文件失败")?; 165 | 166 | Ok(()) 167 | } 168 | 169 | /// 获取机器 ID 170 | pub async fn get_machine_id(&self) -> Result { 171 | let config = self.read_config().await?; 172 | Ok(config["telemetry.machineId"] 173 | .as_str() 174 | .unwrap_or_default() 175 | .to_string()) 176 | } 177 | 178 | /// 获取 Mac 机器 ID 179 | pub async fn get_mac_machine_id(&self) -> Result { 180 | let config = self.read_config().await?; 181 | Ok(config["telemetry.macMachineId"] 182 | .as_str() 183 | .unwrap_or_default() 184 | .to_string()) 185 | } 186 | 187 | /// 获取设备 ID 188 | pub async fn get_dev_device_id(&self) -> Result { 189 | let config = self.read_config().await?; 190 | Ok(config["telemetry.devDeviceId"] 191 | .as_str() 192 | .unwrap_or_default() 193 | .to_string()) 194 | } 195 | 196 | /// 获取 SQM ID 197 | pub async fn get_sqm_id(&self) -> Result { 198 | let config = self.read_config().await?; 199 | Ok(config["telemetry.sqmId"] 200 | .as_str() 201 | .unwrap_or_default() 202 | .to_string()) 203 | } 204 | 205 | /// 获取配置文件路径字符串 206 | pub async fn get_config_path_string(&self) -> Result { 207 | Ok(self.config_path.to_string_lossy().to_string()) 208 | } 209 | 210 | /// 备份配置 211 | pub async fn backup_config(&self) -> Result<()> { 212 | info!("开始备份配置文件"); 213 | self.emit_progress("开始备份配置文件..."); 214 | // 确保备份文件的父目录存在 215 | if let Some(parent) = self.backup_path.parent() { 216 | fs::create_dir_all(parent) 217 | .await 218 | .context(format!("创建备份目录失败: {:?}", parent))?; 219 | } 220 | self.emit_progress("备份目录创建成功"); 221 | // 删除旧的备份文件(如果存在) 222 | utils::remove_file_if_exists(&self.backup_path).await?; 223 | self.emit_progress("旧的备份文件删除成功"); 224 | // 执行备份 225 | fs::copy(&self.config_path, &self.backup_path) 226 | .await 227 | .context(format!( 228 | "备份配置文件失败: 从 {:?} 复制到 {:?}", 229 | self.config_path, self.backup_path 230 | ))?; 231 | self.emit_progress("配置文件备份成功"); 232 | // 设置备份文件为只读 233 | utils::set_file_permissions(&self.backup_path, true).await?; 234 | self.emit_progress("备份文件设置为只读成功"); 235 | info!("配置文件备份完成"); 236 | Ok(()) 237 | } 238 | 239 | /// 还原配置 240 | pub async fn restore_config(&self) -> Result<()> { 241 | info!("开始还原配置文件"); 242 | self.emit_progress("开始还原配置文件..."); 243 | if !self.backup_path.exists() { 244 | warn!("备份文件不存在,无法还原"); 245 | self.emit_progress("备份文件不存在,无法还原"); 246 | return Err(anyhow::anyhow!("备份文件不存在")); 247 | } 248 | 249 | self.emit_progress("正在删除当前配置文件..."); 250 | // 删除当前配置文件 251 | utils::remove_file_if_exists(&self.config_path).await?; 252 | self.emit_progress("当前配置文件删除成功"); 253 | 254 | self.emit_progress("正在复制备份文件到配置文件..."); 255 | // 复制备份文件到配置文件 256 | fs::copy(&self.backup_path, &self.config_path) 257 | .await 258 | .context(format!( 259 | "还原配置文件失败: 从 {:?} 复制到 {:?}", 260 | self.backup_path, self.config_path 261 | ))?; 262 | self.emit_progress("配置文件还原完成"); 263 | info!("配置文件还原完成"); 264 | Ok(()) 265 | } 266 | 267 | /// 设置配置文件的只读属性 268 | pub async fn set_readonly(&self, readonly: bool) -> Result<()> { 269 | utils::set_file_permissions(&self.config_path, readonly).await 270 | } 271 | 272 | /// 更新机器 ID 273 | pub async fn update_machine_id(&self) -> Result<()> { 274 | // 1. 解除只读属性 275 | self.emit_progress("正在解除文件只读属性..."); 276 | if let Err(e) = self.set_readonly(false).await { 277 | error!("解除只读属性失败: {}", e); 278 | return Err(e); 279 | } 280 | 281 | // 2. 备份当前配置 282 | self.emit_progress("正在备份当前配置..."); 283 | if let Err(e) = self.backup_config().await { 284 | error!("备份配置失败: {}", e); 285 | // 备份失败后,恢复只读属性 286 | let _ = self.set_readonly(true).await; 287 | return Err(e); 288 | } 289 | 290 | // 3. 读取当前配置 291 | self.emit_progress("正在读取当前配置..."); 292 | let mut config = match self.read_config().await { 293 | Ok(config) => config, 294 | Err(e) => { 295 | error!("读取配置失败: {}", e); 296 | // 读取失败后,恢复只读属性 297 | let _ = self.set_readonly(true).await; 298 | return Err(e); 299 | } 300 | }; 301 | 302 | // 4. 生成新的配置 303 | self.emit_progress("正在生成新的机器ID配置..."); 304 | let new_config = utils::generate_default_machine_config(); 305 | 306 | // 5. 更新配置 307 | self.emit_progress("正在更新机器ID配置..."); 308 | for (key, value) in new_config.as_object().unwrap() { 309 | config[key] = value.clone(); 310 | } 311 | 312 | // 6. 写入配置 313 | self.emit_progress("正在写入新配置到文件..."); 314 | if let Err(e) = self.write_config(&config).await { 315 | error!("写入配置失败: {}", e); 316 | // 写入失败后,恢复只读属性 317 | let _ = self.set_readonly(true).await; 318 | return Err(e); 319 | } 320 | 321 | // 等待文件系统完成写入 322 | sleep(Duration::from_millis(500)).await; 323 | 324 | // 7. 恢复只读属性 325 | self.emit_progress("正在恢复文件只读属性..."); 326 | for _ in 0..3 { 327 | // 最多尝试3次 328 | if self.set_readonly(true).await.is_ok() { 329 | break; 330 | } 331 | sleep(Duration::from_millis(500)).await; 332 | } 333 | 334 | // 8. 验证文件权限 335 | self.emit_progress("正在验证文件权限..."); 336 | let metadata = fs::metadata(&self.config_path).await?; 337 | let perms = metadata.permissions(); 338 | if !perms.readonly() { 339 | error!("文件权限验证失败:文件不是只读状态"); 340 | return Err(anyhow::anyhow!("文件权限验证失败:无法设置为只读状态")); 341 | } 342 | 343 | // 9. 更新 main.js 中的 ID 344 | self.emit_progress("正在更新 main.js 中的 ID..."); 345 | 346 | // 先创建 patcher 347 | let mut patcher = crate::patcher::Patcher::new(None).map_err(|e| { 348 | error!("创建 Patcher 失败: {}", e); 349 | anyhow::anyhow!("创建 Patcher 失败: {}", e) 350 | })?; 351 | 352 | // 执行 patch 操作并等待结果 353 | patcher 354 | .patch(None, None, None, None) 355 | .await 356 | .with_context(|| { 357 | error!("应用补丁失败"); 358 | "应用补丁失败" 359 | })?; 360 | 361 | self.emit_progress("机器ID更新完成!"); 362 | info!("更新机器 ID 完成"); 363 | Ok(()) 364 | } 365 | 366 | /// 从注册表获取Cursor安装路径 367 | #[cfg(windows)] 368 | fn get_cursor_path() -> Option { 369 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 370 | let subkey = r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Cursor.exe"; 371 | 372 | // 尝试打开注册表项 373 | if let Ok(cursor_key) = hklm.open_subkey(subkey) { 374 | // 获取默认值(安装路径) 375 | if let Ok(path) = cursor_key.get_value::("") { 376 | return Some(path); 377 | } 378 | } 379 | 380 | // 如果上面失败,尝试从卸载信息中获取 381 | let uninstall_key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; 382 | if let Ok(uninstall) = hklm.open_subkey(uninstall_key) { 383 | // 使用 flatten() 简化遍历逻辑 384 | for key_name in uninstall.enum_keys().flatten() { 385 | // 使用 and_then 链式处理结果 386 | let cursor_path = uninstall.open_subkey(&key_name).ok().and_then(|app_key| { 387 | app_key 388 | .get_value::("DisplayName") 389 | .ok() 390 | .filter(|name| name.contains("Cursor")) 391 | .and_then(|_| app_key.get_value::("InstallLocation").ok()) 392 | .map(|install_location| { 393 | format!("{}\\Cursor.exe", install_location.trim_end_matches('\\')) 394 | }) 395 | }); 396 | 397 | if let Some(path) = cursor_path { 398 | return Some(path); 399 | } 400 | } 401 | } 402 | 403 | None 404 | } 405 | 406 | /// 从运行中的进程获取Cursor路径 407 | #[cfg(windows)] 408 | fn get_cursor_process_path() -> Option { 409 | let mut system = System::new_all(); 410 | system.refresh_all(); 411 | 412 | // 查找所有cursor.exe进程 413 | for (pid, process) in system.processes() { 414 | if process.name().to_lowercase() == "cursor.exe" { 415 | if let Some(path) = process.exe().to_str() { 416 | info!("Found running Cursor process (PID: {}): {}", pid, path); 417 | return Some(path.to_string()); 418 | } 419 | } 420 | } 421 | None 422 | } 423 | 424 | /// 重启 Cursor 425 | pub async fn restart_cursor(&self) -> Result<()> { 426 | self.emit_progress("开始重启 Cursor..."); 427 | 428 | // Windows 429 | #[cfg(windows)] 430 | { 431 | // 先从运行中的进程获取路径 432 | self.emit_progress("正在查找运行中的Cursor进程..."); 433 | let cursor_path = if let Some(path) = Self::get_cursor_process_path() { 434 | self.emit_progress(&format!("找到运行中的Cursor进程: {}", path)); 435 | Some(path) 436 | } else { 437 | self.emit_progress("未找到运行中的Cursor进程,尝试从注册表查找..."); 438 | // 尝试从注册表获取 439 | if let Some(path) = Self::get_cursor_path() { 440 | info!("Found Cursor path in registry: {}", path); 441 | self.emit_progress(&format!("在注册表中找到Cursor路径: {}", path)); 442 | if std::path::Path::new(&path).exists() { 443 | Some(path) 444 | } else { 445 | self.emit_progress(&format!("注册表中的路径不存在: {}", path)); 446 | None 447 | } 448 | } else { 449 | self.emit_progress("注册表中未找到Cursor路径,尝试常见安装位置..."); 450 | let possible_paths = [ 451 | format!( 452 | "{}\\Cursor\\Cursor.exe", 453 | std::env::var("ProgramFiles") 454 | .unwrap_or_else(|_| "C:\\Program Files".to_string()) 455 | ), 456 | format!( 457 | "{}\\Cursor\\Cursor.exe", 458 | std::env::var("ProgramFiles(x86)") 459 | .unwrap_or_else(|_| "C:\\Program Files (x86)".to_string()) 460 | ), 461 | format!( 462 | "{}\\Cursor\\Cursor.exe", 463 | std::env::var("LocalAppData").unwrap_or_else(|_| format!( 464 | "{}\\AppData\\Local", 465 | std::env::var("USERPROFILE") 466 | .unwrap_or_else(|_| "C:\\Users\\Default".to_string()) 467 | )) 468 | ), 469 | ]; 470 | 471 | let mut found_path = None; 472 | for path in possible_paths { 473 | self.emit_progress(&format!("检查路径: {}", path)); 474 | if std::path::Path::new(&path).exists() { 475 | self.emit_progress(&format!("找到Cursor: {}", path)); 476 | found_path = Some(path); 477 | break; 478 | } 479 | } 480 | found_path 481 | } 482 | }; 483 | 484 | // 如果找不到Cursor路径,直接返回错误 485 | let cursor_path = match cursor_path { 486 | Some(path) => path, 487 | None => { 488 | let err_msg = "未找到Cursor安装路径,请确认Cursor是否正确安装"; 489 | error!("{}", err_msg); 490 | self.emit_progress(err_msg); 491 | return Err(anyhow::anyhow!(err_msg)); 492 | } 493 | }; 494 | 495 | // 找到路径后,执行关闭操作 496 | self.emit_progress("正在尝试关闭Cursor进程..."); 497 | match Command::new("wmic") 498 | .args(["process", "where", "name='cursor.exe'", "call", "terminate"]) 499 | .output() 500 | { 501 | Ok(output) => { 502 | let stdout = String::from_utf8_lossy(&output.stdout); 503 | let stderr = String::from_utf8_lossy(&output.stderr); 504 | info!("WMIC terminate output: {}", stdout); 505 | if !stderr.is_empty() { 506 | error!("WMIC terminate error: {}", stderr); 507 | } 508 | self.emit_progress(&format!("WMIC terminate output: {}", stdout)); 509 | } 510 | Err(e) => { 511 | error!("Failed to terminate Cursor: {}", e); 512 | self.emit_progress(&format!("关闭Cursor失败: {}", e)); 513 | } 514 | } 515 | 516 | // 增加等待时间,确保进程完全退出 517 | self.emit_progress("等待进程完全退出..."); 518 | sleep(Duration::from_secs(5)).await; 519 | 520 | // 验证进程是否完全退出 521 | let sys = System::new_all(); 522 | let mut retry_count = 0; 523 | while retry_count < 3 { 524 | let cursor_running = sys 525 | .processes() 526 | .values() 527 | .any(|process| process.name().to_lowercase() == "cursor.exe"); 528 | 529 | if !cursor_running { 530 | break; 531 | } 532 | 533 | retry_count += 1; 534 | self.emit_progress("进程仍在运行,等待额外时间..."); 535 | sleep(Duration::from_secs(2)).await; 536 | } 537 | 538 | // 启动 Cursor 539 | self.emit_progress("正在启动 Cursor..."); 540 | match Command::new(&cursor_path).spawn() { 541 | Ok(_) => { 542 | self.emit_progress("Cursor 启动成功"); 543 | } 544 | Err(e) => { 545 | error!("启动 Cursor 失败: {}", e); 546 | self.emit_progress(&format!("启动 Cursor 失败: {}", e)); 547 | return Err(anyhow::anyhow!("启动 Cursor 失败")); 548 | } 549 | } 550 | } 551 | 552 | // macOS 553 | #[cfg(target_os = "macos")] 554 | { 555 | // 关闭 Cursor 556 | Command::new("pkill") 557 | .args(["-x", "Cursor"]) 558 | .output() 559 | .context("关闭 Cursor 失败")?; 560 | 561 | sleep(Duration::from_secs(5)).await; 562 | 563 | // 启动 Cursor 564 | Command::new("open") 565 | .args(["-a", "Cursor"]) 566 | .spawn() 567 | .context("启动 Cursor 失败")?; 568 | } 569 | 570 | // Linux 571 | #[cfg(target_os = "linux")] 572 | { 573 | // 优雅关闭 Cursor 574 | Command::new("killall") 575 | .args(["-TERM", "cursor"]) 576 | .output() 577 | .context("关闭 Cursor 失败")?; 578 | 579 | sleep(Duration::from_secs(5)).await; 580 | 581 | // 验证进程是否完全退出 582 | let mut retry_count = 0; 583 | while retry_count < 3 { 584 | match Command::new("pgrep").arg("cursor").output() { 585 | Ok(output) => { 586 | if output.status.success() { 587 | retry_count += 1; 588 | self.emit_progress("进程仍在运行,等待额外时间..."); 589 | sleep(Duration::from_secs(2)).await; 590 | } else { 591 | break; 592 | } 593 | } 594 | Err(_) => break, 595 | } 596 | } 597 | 598 | // 尝试多个可能的启动路径 599 | let possible_paths = [ 600 | "/usr/bin/cursor", 601 | "/usr/local/bin/cursor", 602 | "/opt/cursor/cursor", 603 | ]; 604 | 605 | let mut started = false; 606 | for path in possible_paths { 607 | if std::path::Path::new(path).exists() { 608 | Command::new(path).spawn().context("启动 Cursor 失败")?; 609 | started = true; 610 | break; 611 | } 612 | } 613 | 614 | if !started { 615 | return Err(anyhow::anyhow!("找不到 Cursor 可执行文件")); 616 | } 617 | } 618 | 619 | self.emit_progress("Cursor 重启操作完成!"); 620 | Ok(()) 621 | } 622 | 623 | /// 重置机器 ID 并重启Cursor 624 | pub async fn reset_machine_id(&self) -> Result<()> { 625 | self.emit_progress("开始重置机器 ID"); 626 | 627 | // 重置认证信息 628 | self.emit_progress("开始重置认证信息..."); 629 | if !auth_manager::reset_auth(&*self.progress_emitter).await { 630 | return Err(anyhow::anyhow!("重置认证信息失败")); 631 | } 632 | self.emit_progress("认证信息重置成功"); 633 | 634 | // 更新机器ID 635 | self.emit_progress("开始更新机器ID..."); 636 | self.update_machine_id().await?; 637 | 638 | // 重启Cursor 639 | self.emit_progress("开始重启Cursor..."); 640 | self.restart_cursor().await?; 641 | 642 | self.emit_progress("重置机器ID完成!"); 643 | Ok(()) 644 | } 645 | 646 | /// 获取所有 ID 信息 647 | pub async fn get_all_ids(&self) -> Result<(String, String, String, String, String)> { 648 | let machine_id = self.get_machine_id().await?; 649 | let mac_machine_id = self.get_mac_machine_id().await?; 650 | let dev_device_id = self.get_dev_device_id().await?; 651 | let sqm_id = self.get_sqm_id().await?; 652 | let config_path = self.get_config_path_string().await?; 653 | 654 | Ok(( 655 | machine_id, 656 | mac_machine_id, 657 | dev_device_id, 658 | sqm_id, 659 | config_path, 660 | )) 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@tauri-apps/api': 12 | specifier: ^2.2.0 13 | version: 2.2.0 14 | '@tauri-apps/cli': 15 | specifier: ^2.2.5 16 | version: 2.2.5 17 | '@vicons/antd': 18 | specifier: ^0.13.0 19 | version: 0.13.0 20 | vfonts: 21 | specifier: ^0.0.3 22 | version: 0.0.3 23 | vue-i18n: 24 | specifier: '9' 25 | version: 9.14.2(vue@3.5.13(typescript@5.7.3)) 26 | devDependencies: 27 | '@types/node': 28 | specifier: ^20.17.13 29 | version: 20.17.14 30 | '@types/vue': 31 | specifier: ^1.0.31 32 | version: 1.0.31 33 | '@vitejs/plugin-vue': 34 | specifier: ^4.0.0 35 | version: 4.6.2(vite@4.5.7(@types/node@20.17.14))(vue@3.5.13(typescript@5.7.3)) 36 | '@volar/vue-language-core': 37 | specifier: ^1.6.5 38 | version: 1.6.5 39 | '@vue/runtime-core': 40 | specifier: ^3.5.13 41 | version: 3.5.13 42 | '@vue/runtime-dom': 43 | specifier: ^3.5.13 44 | version: 3.5.13 45 | naive-ui: 46 | specifier: ^2.41.0 47 | version: 2.41.0(vue@3.5.13(typescript@5.7.3)) 48 | typescript: 49 | specifier: ^5.7.3 50 | version: 5.7.3 51 | unplugin-vue-define-options: 52 | specifier: ^1.5.3 53 | version: 1.5.5(vue@3.5.13(typescript@5.7.3)) 54 | vite: 55 | specifier: ^4.0.0 56 | version: 4.5.7(@types/node@20.17.14) 57 | vue: 58 | specifier: ^3.5.13 59 | version: 3.5.13(typescript@5.7.3) 60 | vue-tsc: 61 | specifier: ^2.2.0 62 | version: 2.2.0(typescript@5.7.3) 63 | 64 | packages: 65 | 66 | '@babel/helper-string-parser@7.25.9': 67 | resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} 68 | engines: {node: '>=6.9.0'} 69 | 70 | '@babel/helper-validator-identifier@7.25.9': 71 | resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} 72 | engines: {node: '>=6.9.0'} 73 | 74 | '@babel/parser@7.26.5': 75 | resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} 76 | engines: {node: '>=6.0.0'} 77 | hasBin: true 78 | 79 | '@babel/types@7.26.5': 80 | resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} 81 | engines: {node: '>=6.9.0'} 82 | 83 | '@css-render/plugin-bem@0.15.14': 84 | resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==} 85 | peerDependencies: 86 | css-render: ~0.15.14 87 | 88 | '@css-render/vue3-ssr@0.15.14': 89 | resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==} 90 | peerDependencies: 91 | vue: ^3.0.11 92 | 93 | '@emotion/hash@0.8.0': 94 | resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} 95 | 96 | '@esbuild/android-arm64@0.18.20': 97 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} 98 | engines: {node: '>=12'} 99 | cpu: [arm64] 100 | os: [android] 101 | 102 | '@esbuild/android-arm@0.18.20': 103 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} 104 | engines: {node: '>=12'} 105 | cpu: [arm] 106 | os: [android] 107 | 108 | '@esbuild/android-x64@0.18.20': 109 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} 110 | engines: {node: '>=12'} 111 | cpu: [x64] 112 | os: [android] 113 | 114 | '@esbuild/darwin-arm64@0.18.20': 115 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} 116 | engines: {node: '>=12'} 117 | cpu: [arm64] 118 | os: [darwin] 119 | 120 | '@esbuild/darwin-x64@0.18.20': 121 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} 122 | engines: {node: '>=12'} 123 | cpu: [x64] 124 | os: [darwin] 125 | 126 | '@esbuild/freebsd-arm64@0.18.20': 127 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} 128 | engines: {node: '>=12'} 129 | cpu: [arm64] 130 | os: [freebsd] 131 | 132 | '@esbuild/freebsd-x64@0.18.20': 133 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} 134 | engines: {node: '>=12'} 135 | cpu: [x64] 136 | os: [freebsd] 137 | 138 | '@esbuild/linux-arm64@0.18.20': 139 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} 140 | engines: {node: '>=12'} 141 | cpu: [arm64] 142 | os: [linux] 143 | 144 | '@esbuild/linux-arm@0.18.20': 145 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} 146 | engines: {node: '>=12'} 147 | cpu: [arm] 148 | os: [linux] 149 | 150 | '@esbuild/linux-ia32@0.18.20': 151 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} 152 | engines: {node: '>=12'} 153 | cpu: [ia32] 154 | os: [linux] 155 | 156 | '@esbuild/linux-loong64@0.18.20': 157 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} 158 | engines: {node: '>=12'} 159 | cpu: [loong64] 160 | os: [linux] 161 | 162 | '@esbuild/linux-mips64el@0.18.20': 163 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} 164 | engines: {node: '>=12'} 165 | cpu: [mips64el] 166 | os: [linux] 167 | 168 | '@esbuild/linux-ppc64@0.18.20': 169 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} 170 | engines: {node: '>=12'} 171 | cpu: [ppc64] 172 | os: [linux] 173 | 174 | '@esbuild/linux-riscv64@0.18.20': 175 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} 176 | engines: {node: '>=12'} 177 | cpu: [riscv64] 178 | os: [linux] 179 | 180 | '@esbuild/linux-s390x@0.18.20': 181 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} 182 | engines: {node: '>=12'} 183 | cpu: [s390x] 184 | os: [linux] 185 | 186 | '@esbuild/linux-x64@0.18.20': 187 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} 188 | engines: {node: '>=12'} 189 | cpu: [x64] 190 | os: [linux] 191 | 192 | '@esbuild/netbsd-x64@0.18.20': 193 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} 194 | engines: {node: '>=12'} 195 | cpu: [x64] 196 | os: [netbsd] 197 | 198 | '@esbuild/openbsd-x64@0.18.20': 199 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} 200 | engines: {node: '>=12'} 201 | cpu: [x64] 202 | os: [openbsd] 203 | 204 | '@esbuild/sunos-x64@0.18.20': 205 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} 206 | engines: {node: '>=12'} 207 | cpu: [x64] 208 | os: [sunos] 209 | 210 | '@esbuild/win32-arm64@0.18.20': 211 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} 212 | engines: {node: '>=12'} 213 | cpu: [arm64] 214 | os: [win32] 215 | 216 | '@esbuild/win32-ia32@0.18.20': 217 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} 218 | engines: {node: '>=12'} 219 | cpu: [ia32] 220 | os: [win32] 221 | 222 | '@esbuild/win32-x64@0.18.20': 223 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} 224 | engines: {node: '>=12'} 225 | cpu: [x64] 226 | os: [win32] 227 | 228 | '@intlify/core-base@9.14.2': 229 | resolution: {integrity: sha512-DZyQ4Hk22sC81MP4qiCDuU+LdaYW91A6lCjq8AWPvY3+mGMzhGDfOCzvyR6YBQxtlPjFqMoFk9ylnNYRAQwXtQ==} 230 | engines: {node: '>= 16'} 231 | 232 | '@intlify/message-compiler@9.14.2': 233 | resolution: {integrity: sha512-YsKKuV4Qv4wrLNsvgWbTf0E40uRv+Qiw1BeLQ0LAxifQuhiMe+hfTIzOMdWj/ZpnTDj4RSZtkXjJM7JDiiB5LQ==} 234 | engines: {node: '>= 16'} 235 | 236 | '@intlify/shared@9.14.2': 237 | resolution: {integrity: sha512-uRAHAxYPeF+G5DBIboKpPgC/Waecd4Jz8ihtkpJQD5ycb5PwXp0k/+hBGl5dAjwF7w+l74kz/PKA8r8OK//RUw==} 238 | engines: {node: '>= 16'} 239 | 240 | '@jridgewell/sourcemap-codec@1.5.0': 241 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 242 | 243 | '@juggle/resize-observer@3.4.0': 244 | resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} 245 | 246 | '@tauri-apps/api@2.2.0': 247 | resolution: {integrity: sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg==} 248 | 249 | '@tauri-apps/cli-darwin-arm64@2.2.5': 250 | resolution: {integrity: sha512-qdPmypQE7qj62UJy3Wl/ccCJZwsv5gyBByOrAaG7u5c/PB3QSxhNPegice2k4EHeIuApaVJOoe/CEYVgm/og2Q==} 251 | engines: {node: '>= 10'} 252 | cpu: [arm64] 253 | os: [darwin] 254 | 255 | '@tauri-apps/cli-darwin-x64@2.2.5': 256 | resolution: {integrity: sha512-8JVlCAb2c3n0EcGW7n/1kU4Rq831SsoLDD/0hNp85Um8HGIH2Mg/qos/MLOc8Qv2mOaoKcRKf4hd0I1y0Rl9Cg==} 257 | engines: {node: '>= 10'} 258 | cpu: [x64] 259 | os: [darwin] 260 | 261 | '@tauri-apps/cli-linux-arm-gnueabihf@2.2.5': 262 | resolution: {integrity: sha512-mzxQCqZg7ljRVgekPpXQ5TOehCNgnXh/DNWU6kFjALaBvaw4fGzc369/hV94wOt29htNFyxf8ty2DaQaYljEHw==} 263 | engines: {node: '>= 10'} 264 | cpu: [arm] 265 | os: [linux] 266 | 267 | '@tauri-apps/cli-linux-arm64-gnu@2.2.5': 268 | resolution: {integrity: sha512-M9nkzx5jsSJSNpp7aSza0qv0/N13SUNzH8ysYSZ7IaCN8coGeMg2KgQ5qC6tqUVij2rbg8A/X1n0pPo/gtLx0A==} 269 | engines: {node: '>= 10'} 270 | cpu: [arm64] 271 | os: [linux] 272 | libc: [glibc] 273 | 274 | '@tauri-apps/cli-linux-arm64-musl@2.2.5': 275 | resolution: {integrity: sha512-tFhZu950HNRLR1RM5Q9Xj5gAlA6AhyyiZgeoXGFAWto+s2jpWmmA3Qq2GUxnVDr7Xui8PF4UY5kANDIOschuwg==} 276 | engines: {node: '>= 10'} 277 | cpu: [arm64] 278 | os: [linux] 279 | libc: [musl] 280 | 281 | '@tauri-apps/cli-linux-x64-gnu@2.2.5': 282 | resolution: {integrity: sha512-eaGhTQLr3EKeksGsp2tK/Ndi7/oyo3P53Pye6kg0zqXiqu8LQjg1CgvDm1l+5oit04S60zR4AqlDFpoeEtDGgw==} 283 | engines: {node: '>= 10'} 284 | cpu: [x64] 285 | os: [linux] 286 | libc: [glibc] 287 | 288 | '@tauri-apps/cli-linux-x64-musl@2.2.5': 289 | resolution: {integrity: sha512-NLAO/SymDxeGuOWWQZCpwoED1K1jaHUvW+u9ip+rTetnxFPLvf3zXthx4QVKfCZLdj2WLQz4cLjHyQdMDXAM+w==} 290 | engines: {node: '>= 10'} 291 | cpu: [x64] 292 | os: [linux] 293 | libc: [musl] 294 | 295 | '@tauri-apps/cli-win32-arm64-msvc@2.2.5': 296 | resolution: {integrity: sha512-yG5KFbqrHfGjkAQAaaCD4i7cJklBjmMxZ2C92DEnqCOujSsEuLxrwwoKxQ4+hqEHOmF3lyX0vfqhgZcS03H38w==} 297 | engines: {node: '>= 10'} 298 | cpu: [arm64] 299 | os: [win32] 300 | 301 | '@tauri-apps/cli-win32-ia32-msvc@2.2.5': 302 | resolution: {integrity: sha512-G5lq+2EdxOc8ttg3uhME5t9U3hMGTxwaKz0X4DplTG2Iv4lcNWqw/AESIJVHa5a+EB+ZCC8I+yOfIykp/Cd5mQ==} 303 | engines: {node: '>= 10'} 304 | cpu: [ia32] 305 | os: [win32] 306 | 307 | '@tauri-apps/cli-win32-x64-msvc@2.2.5': 308 | resolution: {integrity: sha512-vw4fPVOo0rIQIlqw6xUvK2nwiRFBHNgayDE2Z/SomJlQJAJ1q4VgpHOPl12ouuicmTjK1gWKm7RTouQe3Nig0Q==} 309 | engines: {node: '>= 10'} 310 | cpu: [x64] 311 | os: [win32] 312 | 313 | '@tauri-apps/cli@2.2.5': 314 | resolution: {integrity: sha512-PaefTQUCYYqvZWdH8EhXQkyJEjQwtoy/OHGoPcZx7Gk3D3K6AtGSxZ9OlHIz3Bu5LDGgVBk36vKtHW0WYsWnbw==} 315 | engines: {node: '>= 10'} 316 | hasBin: true 317 | 318 | '@types/katex@0.16.7': 319 | resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} 320 | 321 | '@types/lodash-es@4.17.12': 322 | resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} 323 | 324 | '@types/lodash@4.17.14': 325 | resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} 326 | 327 | '@types/node@20.17.14': 328 | resolution: {integrity: sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==} 329 | 330 | '@types/vue@1.0.31': 331 | resolution: {integrity: sha512-unhlPKHFYEc+NA7nBz8yYSg7MOhWFJF2M/tzjb0rgVuK4pQzUBGmBp3IhfuhYDo3wr3dgA5sm9GUHmUJz2Hb9w==} 332 | 333 | '@vicons/antd@0.13.0': 334 | resolution: {integrity: sha512-yrUGoUSz2BbGupk9ghQOahc04n5H3MwUDM9pVPsLh9U1uqB47oRWZvYRiZaT1JKPqgTgSE6BXcVw4i9MOF4M+g==} 335 | 336 | '@vitejs/plugin-vue@4.6.2': 337 | resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} 338 | engines: {node: ^14.18.0 || >=16.0.0} 339 | peerDependencies: 340 | vite: ^4.0.0 || ^5.0.0 341 | vue: ^3.2.25 342 | 343 | '@volar/language-core@1.4.1': 344 | resolution: {integrity: sha512-EIY+Swv+TjsWpxOxujjMf1ZXqOjg9MT2VMXZ+1dKva0wD8W0L6EtptFFcCJdBbcKmGMFkr57Qzz9VNMWhs3jXQ==} 345 | 346 | '@volar/language-core@2.4.11': 347 | resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} 348 | 349 | '@volar/source-map@1.4.1': 350 | resolution: {integrity: sha512-bZ46ad72dsbzuOWPUtJjBXkzSQzzSejuR3CT81+GvTEI2E994D8JPXzM3tl98zyCNnjgs4OkRyliImL1dvJ5BA==} 351 | 352 | '@volar/source-map@2.4.11': 353 | resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} 354 | 355 | '@volar/typescript@2.4.11': 356 | resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==} 357 | 358 | '@volar/vue-language-core@1.6.5': 359 | resolution: {integrity: sha512-IF2b6hW4QAxfsLd5mePmLgtkXzNi+YnH6ltCd80gb7+cbdpFMjM1I+w+nSg2kfBTyfu+W8useCZvW89kPTBpzg==} 360 | 361 | '@vue-macros/common@1.16.1': 362 | resolution: {integrity: sha512-Pn/AWMTjoMYuquepLZP813BIcq8DTZiNCoaceuNlvaYuOTd8DqBZWc5u0uOMQZMInwME1mdSmmBAcTluiV9Jtg==} 363 | engines: {node: '>=16.14.0'} 364 | peerDependencies: 365 | vue: ^2.7.0 || ^3.2.25 366 | peerDependenciesMeta: 367 | vue: 368 | optional: true 369 | 370 | '@vue/compiler-core@3.5.13': 371 | resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} 372 | 373 | '@vue/compiler-dom@3.5.13': 374 | resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} 375 | 376 | '@vue/compiler-sfc@3.5.13': 377 | resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} 378 | 379 | '@vue/compiler-ssr@3.5.13': 380 | resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} 381 | 382 | '@vue/compiler-vue2@2.7.16': 383 | resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} 384 | 385 | '@vue/devtools-api@6.6.4': 386 | resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} 387 | 388 | '@vue/language-core@2.2.0': 389 | resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} 390 | peerDependencies: 391 | typescript: '*' 392 | peerDependenciesMeta: 393 | typescript: 394 | optional: true 395 | 396 | '@vue/reactivity@3.5.13': 397 | resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} 398 | 399 | '@vue/runtime-core@3.5.13': 400 | resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} 401 | 402 | '@vue/runtime-dom@3.5.13': 403 | resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} 404 | 405 | '@vue/server-renderer@3.5.13': 406 | resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} 407 | peerDependencies: 408 | vue: 3.5.13 409 | 410 | '@vue/shared@3.5.13': 411 | resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} 412 | 413 | acorn@8.14.0: 414 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 415 | engines: {node: '>=0.4.0'} 416 | hasBin: true 417 | 418 | alien-signals@0.4.14: 419 | resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} 420 | 421 | ast-kit@1.4.0: 422 | resolution: {integrity: sha512-BlGeOw73FDsX7z0eZE/wuuafxYoek2yzNJ6l6A1nsb4+z/p87TOPbHaWuN53kFKNuUXiCQa2M+xLF71IqQmRSw==} 423 | engines: {node: '>=16.14.0'} 424 | 425 | ast-walker-scope@0.6.2: 426 | resolution: {integrity: sha512-1UWOyC50xI3QZkRuDj6PqDtpm1oHWtYs+NQGwqL/2R11eN3Q81PHAHPM0SWW3BNQm53UDwS//Jv8L4CCVLM1bQ==} 427 | engines: {node: '>=16.14.0'} 428 | 429 | async-validator@4.2.5: 430 | resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} 431 | 432 | balanced-match@1.0.2: 433 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 434 | 435 | brace-expansion@2.0.1: 436 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 437 | 438 | confbox@0.1.8: 439 | resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} 440 | 441 | css-render@0.15.14: 442 | resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==} 443 | 444 | csstype@3.0.11: 445 | resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} 446 | 447 | csstype@3.1.3: 448 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 449 | 450 | date-fns-tz@3.2.0: 451 | resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==} 452 | peerDependencies: 453 | date-fns: ^3.0.0 || ^4.0.0 454 | 455 | date-fns@3.6.0: 456 | resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} 457 | 458 | de-indent@1.0.2: 459 | resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} 460 | 461 | entities@4.5.0: 462 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 463 | engines: {node: '>=0.12'} 464 | 465 | esbuild@0.18.20: 466 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} 467 | engines: {node: '>=12'} 468 | hasBin: true 469 | 470 | estree-walker@2.0.2: 471 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 472 | 473 | evtd@0.2.4: 474 | resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==} 475 | 476 | fsevents@2.3.3: 477 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 478 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 479 | os: [darwin] 480 | 481 | he@1.2.0: 482 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 483 | hasBin: true 484 | 485 | highlight.js@11.11.1: 486 | resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} 487 | engines: {node: '>=12.0.0'} 488 | 489 | local-pkg@1.0.0: 490 | resolution: {integrity: sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==} 491 | engines: {node: '>=14'} 492 | 493 | lodash-es@4.17.21: 494 | resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} 495 | 496 | lodash@4.17.21: 497 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 498 | 499 | magic-string-ast@0.7.0: 500 | resolution: {integrity: sha512-686fgAHaJY7wLTFEq7nnKqeQrhqmXB19d1HnqT35Ci7BN6hbAYLZUezTQ062uUHM7ggZEQlqJ94Ftls+KDXU8Q==} 501 | engines: {node: '>=16.14.0'} 502 | 503 | magic-string@0.30.17: 504 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 505 | 506 | minimatch@9.0.5: 507 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 508 | engines: {node: '>=16 || 14 >=14.17'} 509 | 510 | mlly@1.7.4: 511 | resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} 512 | 513 | muggle-string@0.2.2: 514 | resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} 515 | 516 | muggle-string@0.4.1: 517 | resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} 518 | 519 | naive-ui@2.41.0: 520 | resolution: {integrity: sha512-KnmLg+xPLwXV8QVR7ZZ69eCjvel7R5vru8+eFe4VoAJHEgqAJgVph6Zno9K2IVQRpSF3GBGea3tjavslOR4FAA==} 521 | peerDependencies: 522 | vue: ^3.0.0 523 | 524 | nanoid@3.3.8: 525 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 526 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 527 | hasBin: true 528 | 529 | path-browserify@1.0.1: 530 | resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 531 | 532 | pathe@2.0.2: 533 | resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} 534 | 535 | picocolors@1.1.1: 536 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 537 | 538 | picomatch@4.0.2: 539 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 540 | engines: {node: '>=12'} 541 | 542 | pkg-types@1.3.1: 543 | resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} 544 | 545 | postcss@8.5.1: 546 | resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} 547 | engines: {node: ^10 || ^12 || >=14} 548 | 549 | rollup@3.29.5: 550 | resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} 551 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 552 | hasBin: true 553 | 554 | seemly@0.3.9: 555 | resolution: {integrity: sha512-bMLcaEqhIViiPbaumjLN8t1y+JpD/N8SiyYOyp0i0W6RgdyLWboIsUWAbZojF//JyerxPZR5Tgda+x3Pdne75A==} 556 | 557 | source-map-js@1.2.1: 558 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 559 | engines: {node: '>=0.10.0'} 560 | 561 | treemate@0.3.11: 562 | resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==} 563 | 564 | typescript@5.7.3: 565 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 566 | engines: {node: '>=14.17'} 567 | hasBin: true 568 | 569 | ufo@1.5.4: 570 | resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} 571 | 572 | undici-types@6.19.8: 573 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 574 | 575 | unplugin-vue-define-options@1.5.5: 576 | resolution: {integrity: sha512-V50sWbpoADsjyVgovxewoLo2IDW0zfgHJbKiAl2EdZT8OL3g3h1Mz3QKoAAu09i8+LnkDatIEQMgBVeHHxWXNg==} 577 | engines: {node: '>=16.14.0'} 578 | 579 | unplugin@1.16.1: 580 | resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} 581 | engines: {node: '>=14.0.0'} 582 | 583 | vdirs@0.1.8: 584 | resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==} 585 | peerDependencies: 586 | vue: ^3.0.11 587 | 588 | vfonts@0.0.3: 589 | resolution: {integrity: sha512-nguyw8L6Un8eelg1vQ31vIU2ESxqid7EYmy8V+MDeMaHBqaRSkg3dTBToC1PR00D89UzS/SLkfYPnx0Wf23IQQ==} 590 | 591 | vite@4.5.7: 592 | resolution: {integrity: sha512-vNeGkxk5lbs2CNXFz5nVSKjivMr1oYSb+xcClCSgH9oe/9FK3xzDgjjKTvO0Udc48eCV2AY4GYT61D6Y3iX9iQ==} 593 | engines: {node: ^14.18.0 || >=16.0.0} 594 | hasBin: true 595 | peerDependencies: 596 | '@types/node': '>= 14' 597 | less: '*' 598 | lightningcss: ^1.21.0 599 | sass: '*' 600 | stylus: '*' 601 | sugarss: '*' 602 | terser: ^5.4.0 603 | peerDependenciesMeta: 604 | '@types/node': 605 | optional: true 606 | less: 607 | optional: true 608 | lightningcss: 609 | optional: true 610 | sass: 611 | optional: true 612 | stylus: 613 | optional: true 614 | sugarss: 615 | optional: true 616 | terser: 617 | optional: true 618 | 619 | vooks@0.2.12: 620 | resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==} 621 | peerDependencies: 622 | vue: ^3.0.0 623 | 624 | vscode-uri@3.0.8: 625 | resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} 626 | 627 | vue-i18n@9.14.2: 628 | resolution: {integrity: sha512-JK9Pm80OqssGJU2Y6F7DcM8RFHqVG4WkuCqOZTVsXkEzZME7ABejAUqUdA931zEBedc4thBgSUWxeQh4uocJAQ==} 629 | engines: {node: '>= 16'} 630 | peerDependencies: 631 | vue: ^3.0.0 632 | 633 | vue-template-compiler@2.7.16: 634 | resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} 635 | 636 | vue-tsc@2.2.0: 637 | resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==} 638 | hasBin: true 639 | peerDependencies: 640 | typescript: '>=5.0.0' 641 | 642 | vue@3.5.13: 643 | resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} 644 | peerDependencies: 645 | typescript: '*' 646 | peerDependenciesMeta: 647 | typescript: 648 | optional: true 649 | 650 | vueuc@0.4.64: 651 | resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==} 652 | peerDependencies: 653 | vue: ^3.0.11 654 | 655 | webpack-virtual-modules@0.6.2: 656 | resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} 657 | 658 | snapshots: 659 | 660 | '@babel/helper-string-parser@7.25.9': {} 661 | 662 | '@babel/helper-validator-identifier@7.25.9': {} 663 | 664 | '@babel/parser@7.26.5': 665 | dependencies: 666 | '@babel/types': 7.26.5 667 | 668 | '@babel/types@7.26.5': 669 | dependencies: 670 | '@babel/helper-string-parser': 7.25.9 671 | '@babel/helper-validator-identifier': 7.25.9 672 | 673 | '@css-render/plugin-bem@0.15.14(css-render@0.15.14)': 674 | dependencies: 675 | css-render: 0.15.14 676 | 677 | '@css-render/vue3-ssr@0.15.14(vue@3.5.13(typescript@5.7.3))': 678 | dependencies: 679 | vue: 3.5.13(typescript@5.7.3) 680 | 681 | '@emotion/hash@0.8.0': {} 682 | 683 | '@esbuild/android-arm64@0.18.20': 684 | optional: true 685 | 686 | '@esbuild/android-arm@0.18.20': 687 | optional: true 688 | 689 | '@esbuild/android-x64@0.18.20': 690 | optional: true 691 | 692 | '@esbuild/darwin-arm64@0.18.20': 693 | optional: true 694 | 695 | '@esbuild/darwin-x64@0.18.20': 696 | optional: true 697 | 698 | '@esbuild/freebsd-arm64@0.18.20': 699 | optional: true 700 | 701 | '@esbuild/freebsd-x64@0.18.20': 702 | optional: true 703 | 704 | '@esbuild/linux-arm64@0.18.20': 705 | optional: true 706 | 707 | '@esbuild/linux-arm@0.18.20': 708 | optional: true 709 | 710 | '@esbuild/linux-ia32@0.18.20': 711 | optional: true 712 | 713 | '@esbuild/linux-loong64@0.18.20': 714 | optional: true 715 | 716 | '@esbuild/linux-mips64el@0.18.20': 717 | optional: true 718 | 719 | '@esbuild/linux-ppc64@0.18.20': 720 | optional: true 721 | 722 | '@esbuild/linux-riscv64@0.18.20': 723 | optional: true 724 | 725 | '@esbuild/linux-s390x@0.18.20': 726 | optional: true 727 | 728 | '@esbuild/linux-x64@0.18.20': 729 | optional: true 730 | 731 | '@esbuild/netbsd-x64@0.18.20': 732 | optional: true 733 | 734 | '@esbuild/openbsd-x64@0.18.20': 735 | optional: true 736 | 737 | '@esbuild/sunos-x64@0.18.20': 738 | optional: true 739 | 740 | '@esbuild/win32-arm64@0.18.20': 741 | optional: true 742 | 743 | '@esbuild/win32-ia32@0.18.20': 744 | optional: true 745 | 746 | '@esbuild/win32-x64@0.18.20': 747 | optional: true 748 | 749 | '@intlify/core-base@9.14.2': 750 | dependencies: 751 | '@intlify/message-compiler': 9.14.2 752 | '@intlify/shared': 9.14.2 753 | 754 | '@intlify/message-compiler@9.14.2': 755 | dependencies: 756 | '@intlify/shared': 9.14.2 757 | source-map-js: 1.2.1 758 | 759 | '@intlify/shared@9.14.2': {} 760 | 761 | '@jridgewell/sourcemap-codec@1.5.0': {} 762 | 763 | '@juggle/resize-observer@3.4.0': {} 764 | 765 | '@tauri-apps/api@2.2.0': {} 766 | 767 | '@tauri-apps/cli-darwin-arm64@2.2.5': 768 | optional: true 769 | 770 | '@tauri-apps/cli-darwin-x64@2.2.5': 771 | optional: true 772 | 773 | '@tauri-apps/cli-linux-arm-gnueabihf@2.2.5': 774 | optional: true 775 | 776 | '@tauri-apps/cli-linux-arm64-gnu@2.2.5': 777 | optional: true 778 | 779 | '@tauri-apps/cli-linux-arm64-musl@2.2.5': 780 | optional: true 781 | 782 | '@tauri-apps/cli-linux-x64-gnu@2.2.5': 783 | optional: true 784 | 785 | '@tauri-apps/cli-linux-x64-musl@2.2.5': 786 | optional: true 787 | 788 | '@tauri-apps/cli-win32-arm64-msvc@2.2.5': 789 | optional: true 790 | 791 | '@tauri-apps/cli-win32-ia32-msvc@2.2.5': 792 | optional: true 793 | 794 | '@tauri-apps/cli-win32-x64-msvc@2.2.5': 795 | optional: true 796 | 797 | '@tauri-apps/cli@2.2.5': 798 | optionalDependencies: 799 | '@tauri-apps/cli-darwin-arm64': 2.2.5 800 | '@tauri-apps/cli-darwin-x64': 2.2.5 801 | '@tauri-apps/cli-linux-arm-gnueabihf': 2.2.5 802 | '@tauri-apps/cli-linux-arm64-gnu': 2.2.5 803 | '@tauri-apps/cli-linux-arm64-musl': 2.2.5 804 | '@tauri-apps/cli-linux-x64-gnu': 2.2.5 805 | '@tauri-apps/cli-linux-x64-musl': 2.2.5 806 | '@tauri-apps/cli-win32-arm64-msvc': 2.2.5 807 | '@tauri-apps/cli-win32-ia32-msvc': 2.2.5 808 | '@tauri-apps/cli-win32-x64-msvc': 2.2.5 809 | 810 | '@types/katex@0.16.7': {} 811 | 812 | '@types/lodash-es@4.17.12': 813 | dependencies: 814 | '@types/lodash': 4.17.14 815 | 816 | '@types/lodash@4.17.14': {} 817 | 818 | '@types/node@20.17.14': 819 | dependencies: 820 | undici-types: 6.19.8 821 | 822 | '@types/vue@1.0.31': {} 823 | 824 | '@vicons/antd@0.13.0': {} 825 | 826 | '@vitejs/plugin-vue@4.6.2(vite@4.5.7(@types/node@20.17.14))(vue@3.5.13(typescript@5.7.3))': 827 | dependencies: 828 | vite: 4.5.7(@types/node@20.17.14) 829 | vue: 3.5.13(typescript@5.7.3) 830 | 831 | '@volar/language-core@1.4.1': 832 | dependencies: 833 | '@volar/source-map': 1.4.1 834 | 835 | '@volar/language-core@2.4.11': 836 | dependencies: 837 | '@volar/source-map': 2.4.11 838 | 839 | '@volar/source-map@1.4.1': 840 | dependencies: 841 | muggle-string: 0.2.2 842 | 843 | '@volar/source-map@2.4.11': {} 844 | 845 | '@volar/typescript@2.4.11': 846 | dependencies: 847 | '@volar/language-core': 2.4.11 848 | path-browserify: 1.0.1 849 | vscode-uri: 3.0.8 850 | 851 | '@volar/vue-language-core@1.6.5': 852 | dependencies: 853 | '@volar/language-core': 1.4.1 854 | '@volar/source-map': 1.4.1 855 | '@vue/compiler-dom': 3.5.13 856 | '@vue/compiler-sfc': 3.5.13 857 | '@vue/reactivity': 3.5.13 858 | '@vue/shared': 3.5.13 859 | minimatch: 9.0.5 860 | muggle-string: 0.2.2 861 | vue-template-compiler: 2.7.16 862 | 863 | '@vue-macros/common@1.16.1(vue@3.5.13(typescript@5.7.3))': 864 | dependencies: 865 | '@vue/compiler-sfc': 3.5.13 866 | ast-kit: 1.4.0 867 | local-pkg: 1.0.0 868 | magic-string-ast: 0.7.0 869 | pathe: 2.0.2 870 | picomatch: 4.0.2 871 | optionalDependencies: 872 | vue: 3.5.13(typescript@5.7.3) 873 | 874 | '@vue/compiler-core@3.5.13': 875 | dependencies: 876 | '@babel/parser': 7.26.5 877 | '@vue/shared': 3.5.13 878 | entities: 4.5.0 879 | estree-walker: 2.0.2 880 | source-map-js: 1.2.1 881 | 882 | '@vue/compiler-dom@3.5.13': 883 | dependencies: 884 | '@vue/compiler-core': 3.5.13 885 | '@vue/shared': 3.5.13 886 | 887 | '@vue/compiler-sfc@3.5.13': 888 | dependencies: 889 | '@babel/parser': 7.26.5 890 | '@vue/compiler-core': 3.5.13 891 | '@vue/compiler-dom': 3.5.13 892 | '@vue/compiler-ssr': 3.5.13 893 | '@vue/shared': 3.5.13 894 | estree-walker: 2.0.2 895 | magic-string: 0.30.17 896 | postcss: 8.5.1 897 | source-map-js: 1.2.1 898 | 899 | '@vue/compiler-ssr@3.5.13': 900 | dependencies: 901 | '@vue/compiler-dom': 3.5.13 902 | '@vue/shared': 3.5.13 903 | 904 | '@vue/compiler-vue2@2.7.16': 905 | dependencies: 906 | de-indent: 1.0.2 907 | he: 1.2.0 908 | 909 | '@vue/devtools-api@6.6.4': {} 910 | 911 | '@vue/language-core@2.2.0(typescript@5.7.3)': 912 | dependencies: 913 | '@volar/language-core': 2.4.11 914 | '@vue/compiler-dom': 3.5.13 915 | '@vue/compiler-vue2': 2.7.16 916 | '@vue/shared': 3.5.13 917 | alien-signals: 0.4.14 918 | minimatch: 9.0.5 919 | muggle-string: 0.4.1 920 | path-browserify: 1.0.1 921 | optionalDependencies: 922 | typescript: 5.7.3 923 | 924 | '@vue/reactivity@3.5.13': 925 | dependencies: 926 | '@vue/shared': 3.5.13 927 | 928 | '@vue/runtime-core@3.5.13': 929 | dependencies: 930 | '@vue/reactivity': 3.5.13 931 | '@vue/shared': 3.5.13 932 | 933 | '@vue/runtime-dom@3.5.13': 934 | dependencies: 935 | '@vue/reactivity': 3.5.13 936 | '@vue/runtime-core': 3.5.13 937 | '@vue/shared': 3.5.13 938 | csstype: 3.1.3 939 | 940 | '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.3))': 941 | dependencies: 942 | '@vue/compiler-ssr': 3.5.13 943 | '@vue/shared': 3.5.13 944 | vue: 3.5.13(typescript@5.7.3) 945 | 946 | '@vue/shared@3.5.13': {} 947 | 948 | acorn@8.14.0: {} 949 | 950 | alien-signals@0.4.14: {} 951 | 952 | ast-kit@1.4.0: 953 | dependencies: 954 | '@babel/parser': 7.26.5 955 | pathe: 2.0.2 956 | 957 | ast-walker-scope@0.6.2: 958 | dependencies: 959 | '@babel/parser': 7.26.5 960 | ast-kit: 1.4.0 961 | 962 | async-validator@4.2.5: {} 963 | 964 | balanced-match@1.0.2: {} 965 | 966 | brace-expansion@2.0.1: 967 | dependencies: 968 | balanced-match: 1.0.2 969 | 970 | confbox@0.1.8: {} 971 | 972 | css-render@0.15.14: 973 | dependencies: 974 | '@emotion/hash': 0.8.0 975 | csstype: 3.0.11 976 | 977 | csstype@3.0.11: {} 978 | 979 | csstype@3.1.3: {} 980 | 981 | date-fns-tz@3.2.0(date-fns@3.6.0): 982 | dependencies: 983 | date-fns: 3.6.0 984 | 985 | date-fns@3.6.0: {} 986 | 987 | de-indent@1.0.2: {} 988 | 989 | entities@4.5.0: {} 990 | 991 | esbuild@0.18.20: 992 | optionalDependencies: 993 | '@esbuild/android-arm': 0.18.20 994 | '@esbuild/android-arm64': 0.18.20 995 | '@esbuild/android-x64': 0.18.20 996 | '@esbuild/darwin-arm64': 0.18.20 997 | '@esbuild/darwin-x64': 0.18.20 998 | '@esbuild/freebsd-arm64': 0.18.20 999 | '@esbuild/freebsd-x64': 0.18.20 1000 | '@esbuild/linux-arm': 0.18.20 1001 | '@esbuild/linux-arm64': 0.18.20 1002 | '@esbuild/linux-ia32': 0.18.20 1003 | '@esbuild/linux-loong64': 0.18.20 1004 | '@esbuild/linux-mips64el': 0.18.20 1005 | '@esbuild/linux-ppc64': 0.18.20 1006 | '@esbuild/linux-riscv64': 0.18.20 1007 | '@esbuild/linux-s390x': 0.18.20 1008 | '@esbuild/linux-x64': 0.18.20 1009 | '@esbuild/netbsd-x64': 0.18.20 1010 | '@esbuild/openbsd-x64': 0.18.20 1011 | '@esbuild/sunos-x64': 0.18.20 1012 | '@esbuild/win32-arm64': 0.18.20 1013 | '@esbuild/win32-ia32': 0.18.20 1014 | '@esbuild/win32-x64': 0.18.20 1015 | 1016 | estree-walker@2.0.2: {} 1017 | 1018 | evtd@0.2.4: {} 1019 | 1020 | fsevents@2.3.3: 1021 | optional: true 1022 | 1023 | he@1.2.0: {} 1024 | 1025 | highlight.js@11.11.1: {} 1026 | 1027 | local-pkg@1.0.0: 1028 | dependencies: 1029 | mlly: 1.7.4 1030 | pkg-types: 1.3.1 1031 | 1032 | lodash-es@4.17.21: {} 1033 | 1034 | lodash@4.17.21: {} 1035 | 1036 | magic-string-ast@0.7.0: 1037 | dependencies: 1038 | magic-string: 0.30.17 1039 | 1040 | magic-string@0.30.17: 1041 | dependencies: 1042 | '@jridgewell/sourcemap-codec': 1.5.0 1043 | 1044 | minimatch@9.0.5: 1045 | dependencies: 1046 | brace-expansion: 2.0.1 1047 | 1048 | mlly@1.7.4: 1049 | dependencies: 1050 | acorn: 8.14.0 1051 | pathe: 2.0.2 1052 | pkg-types: 1.3.1 1053 | ufo: 1.5.4 1054 | 1055 | muggle-string@0.2.2: {} 1056 | 1057 | muggle-string@0.4.1: {} 1058 | 1059 | naive-ui@2.41.0(vue@3.5.13(typescript@5.7.3)): 1060 | dependencies: 1061 | '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) 1062 | '@css-render/vue3-ssr': 0.15.14(vue@3.5.13(typescript@5.7.3)) 1063 | '@types/katex': 0.16.7 1064 | '@types/lodash': 4.17.14 1065 | '@types/lodash-es': 4.17.12 1066 | async-validator: 4.2.5 1067 | css-render: 0.15.14 1068 | csstype: 3.1.3 1069 | date-fns: 3.6.0 1070 | date-fns-tz: 3.2.0(date-fns@3.6.0) 1071 | evtd: 0.2.4 1072 | highlight.js: 11.11.1 1073 | lodash: 4.17.21 1074 | lodash-es: 4.17.21 1075 | seemly: 0.3.9 1076 | treemate: 0.3.11 1077 | vdirs: 0.1.8(vue@3.5.13(typescript@5.7.3)) 1078 | vooks: 0.2.12(vue@3.5.13(typescript@5.7.3)) 1079 | vue: 3.5.13(typescript@5.7.3) 1080 | vueuc: 0.4.64(vue@3.5.13(typescript@5.7.3)) 1081 | 1082 | nanoid@3.3.8: {} 1083 | 1084 | path-browserify@1.0.1: {} 1085 | 1086 | pathe@2.0.2: {} 1087 | 1088 | picocolors@1.1.1: {} 1089 | 1090 | picomatch@4.0.2: {} 1091 | 1092 | pkg-types@1.3.1: 1093 | dependencies: 1094 | confbox: 0.1.8 1095 | mlly: 1.7.4 1096 | pathe: 2.0.2 1097 | 1098 | postcss@8.5.1: 1099 | dependencies: 1100 | nanoid: 3.3.8 1101 | picocolors: 1.1.1 1102 | source-map-js: 1.2.1 1103 | 1104 | rollup@3.29.5: 1105 | optionalDependencies: 1106 | fsevents: 2.3.3 1107 | 1108 | seemly@0.3.9: {} 1109 | 1110 | source-map-js@1.2.1: {} 1111 | 1112 | treemate@0.3.11: {} 1113 | 1114 | typescript@5.7.3: {} 1115 | 1116 | ufo@1.5.4: {} 1117 | 1118 | undici-types@6.19.8: {} 1119 | 1120 | unplugin-vue-define-options@1.5.5(vue@3.5.13(typescript@5.7.3)): 1121 | dependencies: 1122 | '@vue-macros/common': 1.16.1(vue@3.5.13(typescript@5.7.3)) 1123 | ast-walker-scope: 0.6.2 1124 | unplugin: 1.16.1 1125 | transitivePeerDependencies: 1126 | - vue 1127 | 1128 | unplugin@1.16.1: 1129 | dependencies: 1130 | acorn: 8.14.0 1131 | webpack-virtual-modules: 0.6.2 1132 | 1133 | vdirs@0.1.8(vue@3.5.13(typescript@5.7.3)): 1134 | dependencies: 1135 | evtd: 0.2.4 1136 | vue: 3.5.13(typescript@5.7.3) 1137 | 1138 | vfonts@0.0.3: {} 1139 | 1140 | vite@4.5.7(@types/node@20.17.14): 1141 | dependencies: 1142 | esbuild: 0.18.20 1143 | postcss: 8.5.1 1144 | rollup: 3.29.5 1145 | optionalDependencies: 1146 | '@types/node': 20.17.14 1147 | fsevents: 2.3.3 1148 | 1149 | vooks@0.2.12(vue@3.5.13(typescript@5.7.3)): 1150 | dependencies: 1151 | evtd: 0.2.4 1152 | vue: 3.5.13(typescript@5.7.3) 1153 | 1154 | vscode-uri@3.0.8: {} 1155 | 1156 | vue-i18n@9.14.2(vue@3.5.13(typescript@5.7.3)): 1157 | dependencies: 1158 | '@intlify/core-base': 9.14.2 1159 | '@intlify/shared': 9.14.2 1160 | '@vue/devtools-api': 6.6.4 1161 | vue: 3.5.13(typescript@5.7.3) 1162 | 1163 | vue-template-compiler@2.7.16: 1164 | dependencies: 1165 | de-indent: 1.0.2 1166 | he: 1.2.0 1167 | 1168 | vue-tsc@2.2.0(typescript@5.7.3): 1169 | dependencies: 1170 | '@volar/typescript': 2.4.11 1171 | '@vue/language-core': 2.2.0(typescript@5.7.3) 1172 | typescript: 5.7.3 1173 | 1174 | vue@3.5.13(typescript@5.7.3): 1175 | dependencies: 1176 | '@vue/compiler-dom': 3.5.13 1177 | '@vue/compiler-sfc': 3.5.13 1178 | '@vue/runtime-dom': 3.5.13 1179 | '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.3)) 1180 | '@vue/shared': 3.5.13 1181 | optionalDependencies: 1182 | typescript: 5.7.3 1183 | 1184 | vueuc@0.4.64(vue@3.5.13(typescript@5.7.3)): 1185 | dependencies: 1186 | '@css-render/vue3-ssr': 0.15.14(vue@3.5.13(typescript@5.7.3)) 1187 | '@juggle/resize-observer': 3.4.0 1188 | css-render: 0.15.14 1189 | evtd: 0.2.4 1190 | seemly: 0.3.9 1191 | vdirs: 0.1.8(vue@3.5.13(typescript@5.7.3)) 1192 | vooks: 0.2.12(vue@3.5.13(typescript@5.7.3)) 1193 | vue: 3.5.13(typescript@5.7.3) 1194 | 1195 | webpack-virtual-modules@0.6.2: {} 1196 | --------------------------------------------------------------------------------