├── 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 |
2 |
3 |
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 |
2 |
3 |
4 |
5 |
6 |
7 | {{ t('authConfig.title') }}
8 |
9 |
10 |
12 |
14 |
15 | {{ t('common.apply') }}
16 |
17 |
18 |
19 |
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 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml)
6 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases)
7 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases)
8 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/stargazers)
9 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/network/members)
10 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues)
11 | [](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 | [](https://star-history.com/#Elawen-Carl/Cursor_Pro_Helper&Date)
90 |
91 | 
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 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
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 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/actions/workflows/ci.yml)
6 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases)
7 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/releases)
8 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/stargazers)
9 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/network/members)
10 | [](https://github.com/Elawen-Carl/Cursor_Pro_Helper/issues)
11 | [](https://creativecommons.org/licenses/by-nc/4.0/)
12 |
13 |
14 |
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 | [](https://star-history.com/#Elawen-Carl/Cursor_Pro_Helper&Date)
309 |
310 | 
311 |
312 |
--------------------------------------------------------------------------------
/src/components/ApiConfig.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
12 | {{ isTestLoading ? t('apiConfig.testing') : t('apiConfig.testApi') }}
13 |
14 |
16 | {{ isApplyLoading ? t('apiConfig.applying') : t('common.apply') }}
17 |
18 |
20 | {{ t('apiConfig.resetToDefault') }}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
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 |
2 |
3 |
4 |
5 |
6 |
7 | {{ t('machineId.progress') }}
8 |
9 |
10 |
11 |
14 | {{ msg }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {{ t('machineId.resetId') }}
25 |
26 |
28 |
29 | {{ t('machineId.modifyId') }}
30 |
31 |
33 |
34 | {{ t('machineId.backup') }}
35 |
36 |
38 |
39 | {{ t('machineId.restore') }}
40 |
41 |
42 |
43 |
44 |
45 |
46 | {{ t('machineId.configPath') }}
47 |
48 |
50 |
51 |
52 |
{{ labels[key] }}
53 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {{ t('machineId.mainJsPath') }}
63 |
64 |
66 |
67 |
68 |
{{ labels[key] }}
69 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
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 |
--------------------------------------------------------------------------------