├── .gitignore ├── Cargo.toml ├── Domain Address.txt ├── LICENSE ├── README.md ├── example.py ├── mac.png ├── reset_machine.rs └── win.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # RustRover 13 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 14 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 15 | # and can be added to the global gitignore or merged into this file. For a more nuclear 16 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 17 | #.idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cursor_reset_tool" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde_json = "1.0" 11 | uuid = { version = "1.6", features = ["v4", "serde"] } 12 | colored = "2.0" 13 | sysinfo = "0.30" 14 | rand = "0.8" 15 | chrono = "0.4" 16 | is_elevated = "0.1.2" # For checking admin privileges 17 | directories = "5.0" # For APPDATA, LOCALAPPDATA paths 18 | [target.'cfg(windows)'.dependencies] 19 | winreg = "0.52" -------------------------------------------------------------------------------- /Domain Address.txt: -------------------------------------------------------------------------------- 1 | 如大量滥用,可能导致下面域名封禁,购买的这批域名都是全新未使用未封禁的 2 | 通过大家打赏的钱会定期为大家购买注册新域名,您的支持是我前进的动力! 3 | michelsinc.elementfx.com 4 | passionfire.info 5 | parcival-store.net 6 | moreablle.com 7 | akunpro.web.id 8 | naknan.my.id 9 | should.mask-my.id 10 | mail.dwisudarsono.my.id 11 | brightsmart.web.id 12 | kryptexsecuremax.club 13 | x-star.space 14 | ixsus.website 15 | huobipools.cloud 16 | f2pools.online 17 | yaholo.cloud 18 | gailna.asia 19 | donganta.my.id 20 | plusfieldzone.com 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 agentcodee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cursor 免费助手 2 | 3 | ## 简介 4 | 5 | 完全免费的 Cursor 助手工具,提供以下功能: 6 | - 一键获取新额度 7 | - 自动满额度账号 8 | - 无需登录账号 9 | - 解决机器码问题 10 | 11 | ## Releases发布版本是免费使用的,打开就能用,无须账号和域名 12 | 13 | **请勿滥用,合理使用** 14 | 15 | ## 加入QQ群获取最新下载,交流学习 16 | QQ群二维码 17 | 18 | ### 支持 Windows、Mac系统,软件截图 19 | 20 | 软件截图 软件截图2 21 | 22 | ## 声明 23 | 24 | 本项目仅供学习交流使用,请勿用于商业用途。 25 | 26 | 本项目不承担任何法律责任,使用本项目造成的任何后果,由使用者自行承担。 27 | 28 | ## 特别鸣谢 29 | 30 | 本项目的开发过程中得到了众多大佬和社区成员的支持与帮助,在此特别感谢。 31 | 32 | ## 支持项目 33 | 34 | 如果您觉得这个项目对您有帮助,可以请我喝杯茶: 35 | 36 | 支付宝赞赏码 微信赞赏码 37 | 38 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | #怕很多小白不懂,所以用py写了这个 5 | """ 6 | 全网独家技术,请勿用于商业目的,仅供技术研究交流学习 7 | cursor注册机,邮箱验证码接收工具 8 | 9 | 使用说明: 10 | 首先到达cursor接收邮箱验证码部分 11 | 1. 修改下面的地址再运行: python example.py 12 | 将使用默认邮箱地址获取验证码 13 | 14 | 2. 指定邮箱: python example.py 网站 lordsem89@storetaikhoan.com 15 | 将使用指定的邮箱地址获取验证码 16 | 17 | 注意事项: 18 | - 邮箱前缀可以自定义,域名部分请使用提供的域名列表中的域名 19 | - 注册网站时先使用此邮箱,然后用此工具接收验证码 20 | - 验证码获取可能需要等待几秒钟 21 | 22 | ✓ QQ群883248964,Github开源地址https://github.com/agentcodee/cursor-free-everyday 23 | 24 | 运行结果预览: 25 | 邮箱地址: lordsem89@storetaikhoan.com 26 | 开始获取邮箱 lordsem89@storetaikhoan.com 的验证码... 27 | 尝试 1/3: 请求验证码... 28 | 成功获取验证码: 938298 29 | """ 30 | 31 | import sys 32 | import time 33 | import requests 34 | from urllib.parse import quote 35 | 36 | def get_verification_code(server_url, email, retry_interval=3): 37 | """ 38 | 从网站获取指定邮箱的验证码 39 | 40 | 参数: 41 | server_url: 网站URL,例如 "cjrom2ero@portaltrendsarena.com" 42 | email: 完整的邮箱地址 43 | retry_interval: 重试间隔(秒) 44 | 45 | 返回: 46 | 成功返回验证码字符串,失败返回None 47 | """ 48 | # URL编码邮箱地址 49 | encoded_email = quote(email) 50 | api_url = f"{server_url}/get_code?email={encoded_email}" 51 | 52 | print(f"开始获取邮箱 {email} 的验证码...") 53 | 54 | max_retries = 3 # 增加重试次数 55 | for attempt in range(max_retries): 56 | try: 57 | print(f"尝试 {attempt+1}/{max_retries}: 请求验证码...") 58 | response = requests.get(api_url, timeout=30) 59 | 60 | if response.status_code == 200: 61 | data = response.json() 62 | if data.get("success") and data.get("code"): 63 | print(f"成功获取验证码: {data['code']}") 64 | return data["code"] 65 | else: 66 | print(f"未找到验证码,响应: {data}") 67 | else: 68 | print(f"请求失败,状态码: {response.status_code}") 69 | 70 | # 如果不是最后一次尝试,等待后重试 71 | if attempt < max_retries - 1: 72 | retry_wait = retry_interval * (attempt + 1) # 递增等待时间 73 | print(f"等待 {retry_wait} 秒后重试...") 74 | time.sleep(retry_wait) 75 | 76 | except Exception as e: 77 | print(f"请求出错: {e}") 78 | if attempt < max_retries - 1: 79 | retry_wait = retry_interval * (attempt + 1) 80 | print(f"等待 {retry_wait} 秒后重试...") 81 | time.sleep(retry_wait) 82 | 83 | print("达到最大重试次数,获取验证码失败") 84 | return None 85 | 86 | def check_server_health(server_url): 87 | try: 88 | response = requests.get(f"{server_url}/health", timeout=5) 89 | if response.status_code == 200: 90 | data = response.json() 91 | return True 92 | else: 93 | print(f"失败,状态码: {response.status_code}") 94 | return False 95 | except Exception as e: 96 | print(f"出错: {e}") 97 | return False 98 | 99 | def main(): 100 | """主函数""" 101 | # 设置默认值 这个不变 102 | server_url = "http://14.103.190.198:5362" 103 | # server_url = "http://127.0.0.1:5362" 104 | 105 | # 在项目中的txt提供了大量的域名,从里面随便选一个 106 | # 前缀可以使用一个10位数的随机数 数字小大写字母混合,@后缀不能随机 107 | email = "lordsem89@storetaikhoan.com" #这个要修改,硬编码了一个测试邮箱 108 | 109 | # 如果提供了命令行参数则使用命令行参数 110 | if len(sys.argv) >= 3: 111 | server_url = sys.argv[1].rstrip('/') 112 | email = sys.argv[2] 113 | else: 114 | print("未提供命令行参数,使用默认值:") 115 | print(f"网站URL: {server_url}") 116 | print(f"邮箱地址: {email}") 117 | 118 | # 检查网站健康状态 119 | if not check_server_health(server_url): 120 | print("网站健康检查失败,退出程序") 121 | return 122 | 123 | # 获取验证码 124 | code = get_verification_code(server_url, email) 125 | 126 | 127 | if __name__ == "__main__": 128 | main() 129 | -------------------------------------------------------------------------------- /mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agentcodee/cursor-free-everyday/4c62f5e3bf94b86e8947876128160b85602d83b3/mac.png -------------------------------------------------------------------------------- /reset_machine.rs: -------------------------------------------------------------------------------- 1 | use colored::*; 2 | use directories::{BaseDirs, UserDirs}; 3 | use is_elevated::is_elevated; 4 | use serde::{Deserialize, Serialize}; 5 | use serde_json::Value; 6 | use std::collections::HashMap; 7 | use std::env; 8 | use std::fs; 9 | use std::io::{self, Write}; 10 | use std::path::{Path, PathBuf}; 11 | use std::process::{Command, Stdio}; 12 | use sysinfo::{System}; 13 | use uuid::Uuid; 14 | use winreg::enums::*; 15 | use winreg::RegKey; 16 | use chrono::Local; 17 | use rand::{thread_rng, Rng, distributions::Alphanumeric}; 18 | 19 | // Color definitions (approximated from PowerShell) 20 | const RED: &str = "red"; 21 | const GREEN: &str = "green"; 22 | const YELLOW: &str = "yellow"; 23 | const BLUE: &str = "blue"; 24 | // const NC: &str = "clear"; // `colored` crate handles reset implicitly or via `.normal()` 25 | 26 | // Max retries and wait time for process termination 27 | const MAX_RETRIES: u32 = 5; 28 | const WAIT_TIME_SECONDS: u64 = 1; 29 | 30 | // Configuration file paths 31 | fn get_storage_file_path() -> Option { 32 | if let Some(base_dirs) = BaseDirs::new() { 33 | let app_data_dir = base_dirs.config_dir(); // Typically %APPDATA% or ~/.config 34 | Some(app_data_dir.join("Cursor").join("User").join("globalStorage").join("storage.json")) 35 | } else { 36 | None 37 | } 38 | } 39 | 40 | fn get_backup_dir_path() -> Option { 41 | if let Some(base_dirs) = BaseDirs::new() { 42 | let app_data_dir = base_dirs.config_dir(); 43 | Some(app_data_dir.join("Cursor").join("User").join("globalStorage").join("backups")) 44 | } else { 45 | None 46 | } 47 | } 48 | 49 | fn get_cursor_package_path() -> Option { 50 | if let Some(user_dirs) = BaseDirs::new() { 51 | let local_app_data_dir = user_dirs.data_local_dir(); 52 | let primary_path = local_app_data_dir.join("Programs").join("cursor").join("resources").join("app").join("package.json"); 53 | if primary_path.exists() { 54 | return Some(primary_path); 55 | } 56 | let alt_path = local_app_data_dir.join("cursor").join("resources").join("app").join("package.json"); 57 | if alt_path.exists() { 58 | return Some(alt_path); 59 | } 60 | } 61 | None 62 | } 63 | 64 | fn get_cursor_updater_path() -> Option { 65 | if let Some(user_dirs) = BaseDirs::new() { 66 | let local_app_data_dir = user_dirs.data_local_dir(); 67 | Some(local_app_data_dir.join("cursor-updater")) 68 | } else { 69 | None 70 | } 71 | } 72 | 73 | 74 | fn press_enter_to_exit(exit_code: i32) { 75 | print!("Press Enter to exit..."); 76 | io::stdout().flush().unwrap(); 77 | let mut buffer = String::new(); 78 | io::stdin().read_line(&mut buffer).unwrap(); 79 | std::process::exit(exit_code); 80 | } 81 | 82 | fn main() { 83 | // Set output encoding to UTF-8 (Rust strings are UTF-8 by default, console might need setup on Windows) 84 | // On Windows, `chcp 65001` might be needed in the terminal before running for full UTF-8 display. 85 | // The script itself cannot reliably change the parent console's encoding. 86 | 87 | // Check administrator privileges 88 | if !is_elevated() { 89 | println!("{}", "[ERROR] Please run this script as administrator".color(RED)); 90 | println!("Right-click the executable and select 'Run as administrator'"); 91 | press_enter_to_exit(1); 92 | } 93 | 94 | // Display Logo 95 | // Using simple print for now, can be enhanced 96 | Command::new("cmd").args(&["/c", "cls"]).status().unwrap(); // Clear screen on Windows 97 | 98 | println!("{}", r#" 99 | ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ 100 | ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ 101 | ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ 102 | ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ 103 | ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ 104 | ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ 105 | 106 | "#.bright_cyan()); 107 | println!("{}", "================================".color(BLUE)); 108 | println!(" {}", "Cursor Device ID Modifier Tool".color(GREEN)); 109 | println!(" {}", "Cursor ID Reset Tool - Community Edition".color(YELLOW)); 110 | println!(" {}", "Free tool for Cursor device ID management".color(YELLOW)); 111 | println!(" {}", "[IMPORTANT] This is a free community tool".color(YELLOW)); 112 | println!("{}", "================================".color(BLUE)); 113 | println!(" {}", "QQ群: 951642519 (交流/下载纯免费自动账号切换工具)".color(YELLOW)); 114 | println!(""); 115 | 116 | // Get and display Cursor version 117 | let cursor_version = get_cursor_version(); 118 | match &cursor_version { 119 | Some(version) => println!("{} Current Cursor version: v{}", "[INFO]".color(GREEN), version), 120 | None => { 121 | println!("{} Unable to detect Cursor version", "[WARNING]".color(YELLOW)); 122 | println!("{} Please ensure Cursor is properly installed", "[TIP]".color(YELLOW)); 123 | } 124 | } 125 | println!(""); 126 | 127 | println!("{} Latest 0.45.x (supported)", "[IMPORTANT NOTE]".color(YELLOW)); 128 | println!(""); 129 | 130 | // Check and close Cursor processes 131 | println!("{} Checking Cursor processes...", "[INFO]".color(GREEN)); 132 | close_cursor_process("Cursor"); 133 | close_cursor_process("cursor"); 134 | println!(""); 135 | 136 | let storage_file_path = match get_storage_file_path() { 137 | Some(path) => path, 138 | None => { 139 | println!("{}", "[ERROR] Could not determine APPDATA path for storage file.".color(RED)); 140 | press_enter_to_exit(1); 141 | unreachable!(); // press_enter_to_exit exits 142 | } 143 | }; 144 | // println!("Storage file path: {:?}", storage_file_path); 145 | 146 | let backup_dir_path = match get_backup_dir_path() { 147 | Some(path) => path, 148 | None => { 149 | println!("{}", "[ERROR] Could not determine APPDATA path for backup directory.".color(RED)); 150 | press_enter_to_exit(1); 151 | unreachable!(); 152 | } 153 | }; 154 | // println!("Backup dir path: {:?}", backup_dir_path); 155 | 156 | // Create backup directory 157 | if !backup_dir_path.exists() { 158 | match fs::create_dir_all(&backup_dir_path) { 159 | Ok(_) => println!("{} Created backup directory at {:?}", "[INFO]".color(GREEN), backup_dir_path), 160 | Err(e) => { 161 | println!("{} Failed to create backup directory at {:?}: {}", "[ERROR]".color(RED), backup_dir_path, e); 162 | press_enter_to_exit(1); 163 | } 164 | } 165 | } 166 | 167 | // Backup existing configuration 168 | if storage_file_path.exists() { 169 | println!("{} Backing up configuration file...", "[INFO]".color(GREEN)); 170 | let backup_name = format!("storage.json.backup_{}", Local::now().format("%Y%m%d_%H%M%S")); 171 | let backup_file_path = backup_dir_path.join(backup_name); 172 | match fs::copy(&storage_file_path, &backup_file_path) { 173 | Ok(_) => println!("{} Configuration backed up to {:?}", "[INFO]".color(GREEN), backup_file_path), 174 | Err(e) => { 175 | println!("{} Failed to backup configuration file to {:?}: {}", "[ERROR]".color(RED), backup_file_path, e); 176 | // Decide if this is a fatal error or a warning 177 | } 178 | } 179 | } else { 180 | println!("{} No existing configuration file found at {:?} to back up.", "[INFO]".color(GREEN), storage_file_path); 181 | } 182 | println!(""); 183 | 184 | // Generate new IDs 185 | println!("{} Generating new IDs...", "[INFO]".color(GREEN)); 186 | let mac_machine_id = new_standard_machine_id(); 187 | let uuid_str = Uuid::new_v4().to_string(); 188 | let prefix_hex = "auth0|user_".as_bytes().iter().map(|b| format!("{:02x}", b)).collect::(); 189 | let random_part = get_random_hex(32); 190 | let machine_id = format!("{}{}", prefix_hex, random_part); 191 | let sqm_id = format!("{{{}}}", Uuid::new_v4().to_string().to_uppercase()); 192 | 193 | // println!("Generated MAC_MACHINE_ID: {}", mac_machine_id); 194 | // println!("Generated UUID_STR: {}", uuid_str); 195 | // println!("Generated MACHINE_ID: {}", machine_id); 196 | // println!("Generated SQM_ID: {}", sqm_id); 197 | // println!(""); 198 | 199 | // Update MachineGuid in registry 200 | let mut machine_guid_updated = false; 201 | if cfg!(target_os = "windows") { // Only run on Windows 202 | machine_guid_updated = update_machine_guid(&backup_dir_path); 203 | } else { 204 | println!("{} Skipping MachineGuid update (not on Windows)", "[INFO]".color(YELLOW)); 205 | } 206 | 207 | // Create or update configuration file 208 | println!("{} Updating configuration...", "[INFO]".color(GREEN)); 209 | let storage_update_successful = update_storage_file( 210 | &storage_file_path, 211 | &machine_id, 212 | &mac_machine_id, 213 | &uuid_str, // This was $UUID in PowerShell, which corresponds to devDeviceId 214 | &sqm_id 215 | ); 216 | 217 | if storage_update_successful { 218 | println!("{} Configuration updated successfully.", "[INFO]".color(GREEN)); 219 | // Display results 220 | println!(""); 221 | println!("{} Configuration updated details:", "[INFO]".color(GREEN)); 222 | println!("{} machineId: {}", "[DEBUG]".color(BLUE), machine_id); 223 | println!("{} macMachineId: {}", "[DEBUG]".color(BLUE), mac_machine_id); 224 | println!("{} devDeviceId: {}", "[DEBUG]".color(BLUE), uuid_str); 225 | println!("{} sqmId: {}", "[DEBUG]".color(BLUE), sqm_id); 226 | } else { 227 | println!("{} Main operation failed to update storage file.", "[ERROR]".color(RED)); 228 | // The PS script has an alternative method here, which is complex. 229 | // For now, we'll just indicate failure. 230 | press_enter_to_exit(1); 231 | } 232 | println!(""); 233 | 234 | // Display file tree structure 235 | println!("{} File structure:", "[INFO]".color(GREEN)); 236 | if let Some(user_dirs) = UserDirs::new() { 237 | // %APPDATA%\Cursor\User is not directly available via UserDirs or BaseDirs in a cross-platform way for this specific structure. 238 | // We'll construct it based on APPDATA which UserDirs doesn't directly give, BaseDirs::config_dir() is the closest. 239 | if let Some(base_dirs) = BaseDirs::new() { 240 | let app_data_dir_equivalent = base_dirs.config_dir(); // This is platform specific, e.g. %APPDATA% on Windows 241 | println!("{}", app_data_dir_equivalent.join("Cursor").join("User").display().to_string().color(BLUE)); 242 | } 243 | } else { 244 | println!("{} Could not determine APPDATA path for display.", "[WARNING]".color(YELLOW)); 245 | } 246 | println!("├── globalStorage"); 247 | println!("│ ├── storage.json (modified)"); 248 | println!("│ └── backups"); 249 | 250 | // List backup files 251 | match fs::read_dir(&backup_dir_path) { 252 | Ok(entries) => { 253 | let mut backup_files_found = false; 254 | for entry in entries { 255 | if let Ok(entry) = entry { 256 | if entry.path().is_file() { 257 | println!("│ └── {}", entry.file_name().to_string_lossy()); 258 | backup_files_found = true; 259 | } 260 | } 261 | } 262 | if !backup_files_found { 263 | println!("│ └── (empty)"); 264 | } 265 | } 266 | Err(e) => { 267 | println!("│ └── (Error reading backups: {})", e); 268 | } 269 | } 270 | println!(""); 271 | 272 | // Display completion message 273 | println!("{}", "================================".color(GREEN)); 274 | println!(" {}", "Cursor ID Reset Tool - Community Edition".color(YELLOW)); 275 | println!("{}", "================================".color(GREEN)); 276 | println!(""); 277 | println!("{} Please restart Cursor to apply new configuration", "[INFO]".color(GREEN)); 278 | println!(""); 279 | 280 | press_enter_to_exit(0); 281 | } 282 | 283 | fn get_random_hex(length: usize) -> String { 284 | thread_rng() 285 | .sample_iter(&Alphanumeric) 286 | .take(length) 287 | .map(char::from) 288 | .collect::() 289 | .to_lowercase() // PowerShell version produces lowercase hex 290 | } 291 | 292 | fn new_standard_machine_id() -> String { 293 | // Template: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx 294 | // y is one of 8, 9, a, b 295 | let mut rng = thread_rng(); 296 | let mut id = String::with_capacity(36); 297 | for (i, char_template) in "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".chars().enumerate() { 298 | if char_template == '-' || char_template == '4' { 299 | id.push(char_template); 300 | } else if char_template == 'x' { 301 | id.push_str(&format!("{:x}", rng.gen_range(0..16))); 302 | } else if char_template == 'y' { 303 | id.push_str(&format!("{:x}", rng.gen_range(8..12))); // 8, 9, a, b 304 | } 305 | } 306 | id 307 | } 308 | 309 | #[derive(Deserialize)] 310 | struct PackageJson { 311 | version: String, 312 | } 313 | 314 | fn get_cursor_version() -> Option { 315 | if let Some(package_path) = get_cursor_package_path() { 316 | if package_path.exists() { 317 | match fs::read_to_string(&package_path) { 318 | Ok(contents) => match serde_json::from_str::(&contents) { 319 | Ok(json) => Some(json.version), 320 | Err(e) => { 321 | println!("{} Failed to parse package.json: {}", "[ERROR]".color(RED), e); 322 | None 323 | } 324 | }, 325 | Err(e) => { 326 | println!("{} Failed to read package.json at {:?}: {}", "[ERROR]".color(RED), package_path, e); 327 | None 328 | } 329 | } 330 | } else { 331 | println!("{} package.json not found at {:?}", "[WARNING]".color(YELLOW), package_path); 332 | None 333 | } 334 | } else { 335 | println!("{} Could not determine path to Cursor's package.json", "[WARNING]".color(YELLOW)); 336 | None 337 | } 338 | } 339 | 340 | fn close_cursor_process(process_name: &str) { 341 | let mut sys = System::new_all(); 342 | sys.refresh_processes(); 343 | 344 | let processes_to_kill: Vec<_> = sys 345 | .processes() 346 | .values() 347 | .filter(|p| p.name().eq_ignore_ascii_case(process_name)) 348 | .collect(); 349 | 350 | if !processes_to_kill.is_empty() { 351 | println!("{} Found {} running", "[WARNING]".color(YELLOW), process_name); 352 | for p in &processes_to_kill { 353 | println!(" PID: {}, Name: {}, Path: {:?}", p.pid(), p.name(), p.exe()); 354 | } 355 | 356 | println!("{} Attempting to close {}...", "[WARNING]".color(YELLOW), process_name); 357 | for p in processes_to_kill { 358 | if !p.kill() { // kill() sends SIGKILL by default on Unix, TerminateProcess on Windows 359 | println!("{} Failed to send termination signal to {} (PID: {}). Trying to wait...", "[ERROR]".color(RED), process_name, p.pid()); 360 | } 361 | } 362 | 363 | let mut retry_count = 0; 364 | loop { 365 | sys.refresh_processes(); 366 | let still_running: Vec<_> = sys 367 | .processes() 368 | .values() 369 | .filter(|p| p.name().eq_ignore_ascii_case(process_name)) 370 | .collect(); 371 | 372 | if still_running.is_empty() { 373 | break; 374 | } 375 | 376 | retry_count += 1; 377 | if retry_count >= MAX_RETRIES { 378 | println!("{} Unable to close {} after {} attempts", "[ERROR]".color(RED), process_name, MAX_RETRIES); 379 | for p in still_running { 380 | println!(" Still running - PID: {}, Name: {}, Path: {:?}", p.pid(), p.name(), p.exe()); 381 | } 382 | println!("{} Please close the process manually and try again", "[ERROR]".color(RED)); 383 | press_enter_to_exit(1); 384 | } 385 | 386 | println!("{} Waiting for process to close, attempt {}/{}...", "[WARNING]".color(YELLOW), retry_count, MAX_RETRIES); 387 | std::thread::sleep(std::time::Duration::from_secs(WAIT_TIME_SECONDS)); 388 | } 389 | println!("{} {} successfully closed", "[INFO]".color(GREEN), process_name); 390 | } 391 | } 392 | 393 | #[cfg(target_os = "windows")] 394 | fn update_machine_guid(backup_dir: &Path) -> bool { 395 | println!("{} Updating MachineGuid in registry...", "[INFO]".color(GREEN)); 396 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 397 | let reg_path = "SOFTWARE\\Microsoft\\Cryptography"; 398 | let reg_key_name = "MachineGuid"; 399 | let full_reg_key_path_for_export = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography"; 400 | 401 | let crypto_key = match hklm.open_subkey_with_flags(reg_path, KEY_READ | KEY_WRITE) { 402 | Ok(key) => key, 403 | Err(e) => { 404 | println!("{} Failed to open registry key '{}': {}. Ensure you have admin rights.", "[ERROR]".color(RED), reg_path, e); 405 | return false; 406 | } 407 | }; 408 | 409 | let current_guid_val: Result = crypto_key.get_value(reg_key_name); 410 | let original_guid = match current_guid_val { 411 | Ok(guid) => { 412 | println!("{} Current registry value:", "[INFO]".color(GREEN)); 413 | println!(" {}", full_reg_key_path_for_export); 414 | println!(" {} REG_SZ {}", reg_key_name, guid); 415 | guid 416 | } 417 | Err(e) => { 418 | println!("{} Unable to get current {}: {}. This might indicate a problem or the value might not exist.", "[ERROR]".color(RED), reg_key_name, e); 419 | // Proceeding to set a new one if it doesn't exist, or fail if it's a permission issue. 420 | String::new() // Or handle as a more critical error if needed. 421 | } 422 | }; 423 | 424 | if !backup_dir.exists() { 425 | if let Err(e) = fs::create_dir_all(backup_dir) { 426 | println!("{} Failed to create backup directory for registry backup: {}. Proceeding without registry backup.", "[WARNING]".color(YELLOW), e); 427 | } 428 | } 429 | 430 | let backup_file_name = format!("MachineGuid_{}.reg", Local::now().format("%Y%m%d_%H%M%S")); 431 | let backup_file_path = backup_dir.join(&backup_file_name); 432 | let backup_command_str = format!("reg.exe export \"{}\" \"{}\" /y", full_reg_key_path_for_export, backup_file_path.display()); 433 | 434 | println!("{} Attempting to backup registry key to: {:?}", "[INFO]".color(GREEN), backup_file_path); 435 | match Command::new("cmd").args(&["/C", &backup_command_str]).status() { 436 | Ok(status) if status.success() => { 437 | println!("{} Registry key backed up successfully.", "[INFO]".color(GREEN)); 438 | } 439 | Ok(status) => { 440 | println!("{} Registry backup command finished with status: {}. Check permissions or if reg.exe is available.", "[WARNING]".color(YELLOW), status); 441 | } 442 | Err(e) => { 443 | println!("{} Failed to execute registry backup command: {}. Proceeding with caution.", "[WARNING]".color(YELLOW), e); 444 | } 445 | } 446 | 447 | let new_guid = Uuid::new_v4().to_string(); 448 | match crypto_key.set_value(reg_key_name, &new_guid) { 449 | Ok(_) => { 450 | println!("{} Registry value {} set to: {}", "[INFO]".color(GREEN), reg_key_name, new_guid); 451 | // Verification 452 | let verify_guid: Result = crypto_key.get_value(reg_key_name); 453 | match verify_guid { 454 | Ok(val) if val == new_guid => { 455 | println!("{} Registry update verified successfully.", "[INFO]".color(GREEN)); 456 | println!(" {}", full_reg_key_path_for_export); 457 | println!(" {} REG_SZ {}", reg_key_name, new_guid); 458 | true 459 | } 460 | Ok(val) => { 461 | println!("{} Registry verification failed: Updated value ({}) does not match expected value ({}).", "[ERROR]".color(RED), val, new_guid); 462 | // Attempt restore 463 | false // Placeholder for restore logic 464 | } 465 | Err(e) => { 466 | println!("{} Failed to verify registry update: {}.", "[ERROR]".color(RED), e); 467 | false // Placeholder for restore logic 468 | } 469 | } 470 | } 471 | Err(e) => { 472 | println!("{} Failed to set registry value {}: {}.", "[ERROR]".color(RED), reg_key_name, e); 473 | // Attempt restore if original_guid was present and backup_file_path exists 474 | if !original_guid.is_empty() && backup_file_path.exists() { 475 | println!("{} Attempting to restore registry from backup: {:?}", "[YELLOW]".color(YELLOW), backup_file_path); 476 | let restore_command_str = format!("reg.exe import \"{}\"", backup_file_path.display()); 477 | match Command::new("cmd").args(&["/C", &restore_command_str]).status() { 478 | Ok(status) if status.success() => println!("{} Registry restored successfully from backup.", "[INFO]".color(GREEN)), 479 | Ok(status) => println!("{} Registry restore command failed with status: {}. Manual restore may be needed from {:?}", "[ERROR]".color(RED), status, backup_file_path), 480 | Err(re) => println!("{} Failed to execute registry restore command: {}. Manual restore needed from {:?}", "[ERROR]".color(RED), re, backup_file_path), 481 | } 482 | } 483 | false 484 | } 485 | } 486 | } 487 | 488 | fn update_storage_file( 489 | storage_file_path: &Path, 490 | machine_id: &str, 491 | mac_machine_id: &str, 492 | dev_device_id: &str, 493 | sqm_id: &str, 494 | ) -> bool { 495 | if !storage_file_path.exists() { 496 | println!("{} Configuration file not found: {:?}", "[ERROR]".color(RED), storage_file_path); 497 | println!("{} Please install and run Cursor once before using this script", "[TIP]".color(YELLOW)); 498 | return false; 499 | } 500 | 501 | let original_content = match fs::read_to_string(storage_file_path) { 502 | Ok(content) => content, 503 | Err(e) => { 504 | println!("{} Failed to read configuration file {:?}: {}", "[ERROR]".color(RED), storage_file_path, e); 505 | return false; 506 | } 507 | }; 508 | 509 | let mut config: Value = match serde_json::from_str(&original_content) { 510 | Ok(json_value) => json_value, 511 | Err(e) => { 512 | println!("{} Failed to parse configuration file JSON: {}", "[ERROR]".color(RED), e); 513 | // Attempt to restore original content is not applicable here as we haven't written yet 514 | return false; 515 | } 516 | }; 517 | 518 | // Ensure the path to telemetry values exists or create it 519 | // serde_json::Value uses `pointer_mut` for this kind of access. 520 | // Example: /telemetry/machineId 521 | // We need to ensure `config["telemetry"]` is an object. 522 | if !config.get("telemetry").map_or(false, |v| v.is_object()) { 523 | if config.as_object_mut().is_some() { // Check if config itself is an object 524 | config["telemetry"] = serde_json::json!({}); 525 | } else { 526 | println!("{} Configuration root is not a JSON object. Cannot set telemetry.", "[ERROR]".color(RED)); 527 | return false; 528 | } 529 | } 530 | 531 | // Update specific values 532 | // Using .get_mut("telemetry") and then working with the resulting Option<&mut Value> 533 | if let Some(telemetry) = config.get_mut("telemetry") { 534 | if let Some(telemetry_obj) = telemetry.as_object_mut() { 535 | telemetry_obj.insert("machineId".to_string(), Value::String(machine_id.to_string())); 536 | telemetry_obj.insert("macMachineId".to_string(), Value::String(mac_machine_id.to_string())); 537 | telemetry_obj.insert("devDeviceId".to_string(), Value::String(dev_device_id.to_string())); 538 | telemetry_obj.insert("sqmId".to_string(), Value::String(sqm_id.to_string())); 539 | } else { 540 | println!("{} 'telemetry' field is not an object.", "[ERROR]".color(RED)); 541 | return false; // Or attempt to restore original_content 542 | } 543 | } else { 544 | // This case should ideally be covered by the creation logic above. 545 | println!("{} Failed to access or create 'telemetry' object.", "[ERROR]".color(RED)); 546 | return false; // Or attempt to restore original_content 547 | } 548 | 549 | match serde_json::to_string_pretty(&config) { // Using pretty for readability, PowerShell does compact 550 | Ok(updated_json) => { 551 | match fs::write(storage_file_path, updated_json.as_bytes()) { // .as_bytes() for UTF-8 552 | Ok(_) => { 553 | println!("{} Configuration file updated successfully at {:?}", "[INFO]".color(GREEN), storage_file_path); 554 | true 555 | } 556 | Err(e) => { 557 | println!("{} Failed to write updated configuration to {:?}: {}", "[ERROR]".color(RED), storage_file_path, e); 558 | // Attempt to restore original content 559 | if fs::write(storage_file_path, original_content.as_bytes()).is_err() { 560 | println!("{} CRITICAL: Failed to restore original content to {:?} after write error.", "[ERROR]".color(RED), storage_file_path); 561 | } 562 | false 563 | } 564 | } 565 | } 566 | Err(e) => { 567 | println!("{} Failed to serialize updated configuration to JSON: {}", "[ERROR]".color(RED), e); 568 | // Attempt to restore original content if we had changed it in memory (not the case here with direct write path) 569 | // No need to restore file if serialization failed before writing. 570 | false 571 | } 572 | } 573 | } 574 | -------------------------------------------------------------------------------- /win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agentcodee/cursor-free-everyday/4c62f5e3bf94b86e8947876128160b85602d83b3/win.png --------------------------------------------------------------------------------