├── .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 |
17 |
18 | ### 支持 Windows、Mac系统,软件截图
19 |
20 |
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
--------------------------------------------------------------------------------