import os import winreg import logging import shutil from pathlib import Path import uuid from datetime import datetime import json import hashlib class CursorRegistry: """Cursor注册表操作工具类""" def __init__(self): self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor" self.app_path = self.cursor_path / "resources" / "app" def update_machine_guid(self) -> bool: """更新系统的 MachineGuid Returns: bool: 是否成功 """ try: # 生成新的 GUID new_guid = str(uuid.uuid4()) registry_path = r"SOFTWARE\Microsoft\Cryptography" try: # 使用管理员权限打开注册表项 key = None try: # 先尝试直接打开读取权限 key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY) # 读取原始值并备份 original_guid = winreg.QueryValueEx(key, "MachineGuid")[0] winreg.CloseKey(key) # 备份原始 MachineGuid backup_dir = Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "backups" backup_dir.mkdir(parents=True, exist_ok=True) backup_name = f"MachineGuid.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}" with open(backup_dir / backup_name, 'w', encoding='utf-8') as f: f.write(original_guid) logging.info(f"备份 MachineGuid 到: {backup_name}") # 重新打开写入权限 key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY) except WindowsError: # 如果失败,尝试以管理员权限运行 import ctypes if ctypes.windll.shell32.IsUserAnAdmin() == 0: logging.warning("需要管理员权限来修改 MachineGuid") return False key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_ALL_ACCESS | winreg.KEY_WOW64_64KEY) # 设置新的 GUID winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid) winreg.CloseKey(key) logging.info(f"更新系统 MachineGuid 成功: {new_guid}") return True except WindowsError as e: logging.error(f"更新系统 MachineGuid 失败: {str(e)}") return False except Exception as e: logging.error(f"更新 MachineGuid 过程出错: {str(e)}") return False def clean_registry(self) -> bool: """清理Cursor相关的注册表项 Returns: bool: 是否成功 """ try: # 需要清理的注册表路径列表 registry_paths = [ r"Software\Classes\Directory\Background\shell\Cursor\command", r"Software\Classes\Directory\Background\shell\Cursor", r"Software\Cursor\Auth", r"Software\Cursor\Updates", r"Software\Cursor" ] for path in registry_paths: try: winreg.DeleteKey(winreg.HKEY_CURRENT_USER, path) logging.info(f"删除注册表项成功: {path}") except WindowsError as e: if e.winerror == 2: # 找不到注册表项 logging.info(f"注册表项不存在,无需清理: {path}") else: logging.error(f"清理注册表失败: {path}, 错误: {str(e)}") # 更新系统 MachineGuid self.update_machine_guid() return True except Exception as e: logging.error(f"清理注册表过程出错: {str(e)}") return False def clean_cursor_files(self) -> bool: """清理Cursor相关的文件和目录,但保留重要的配置和历史记录""" try: local_app_data = Path(os.getenv('LOCALAPPDATA')) app_data = Path(os.getenv('APPDATA')) # 需要备份的文件 storage_path = app_data / "Cursor" / "User" / "globalStorage" / "storage.json" backup_dir = app_data / "Cursor" / "User" / "globalStorage" / "backups" global_storage_dir = app_data / "Cursor" / "User" / "globalStorage" # 如果存在 storage.json,先备份 if storage_path.exists(): # 确保备份目录存在 backup_dir.mkdir(parents=True, exist_ok=True) # 备份 storage.json backup_name = f"storage.json.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}" shutil.copy2(storage_path, backup_dir / backup_name) logging.info(f"备份 storage.json 到: {backup_name}") # 备份 global_storage 目录中的其他重要文件 if global_storage_dir.exists(): for item in global_storage_dir.iterdir(): if item.name != "storage.json" and item.name != "backups": try: backup_item_dir = backup_dir / f"other_files_{datetime.now().strftime('%Y%m%d_%H%M%S')}" backup_item_dir.mkdir(exist_ok=True) if item.is_file(): shutil.copy2(item, backup_item_dir / item.name) logging.info(f"备份文件: {item.name}") elif item.is_dir(): shutil.copytree(item, backup_item_dir / item.name) logging.info(f"备份目录: {item.name}") except Exception as e: logging.error(f"备份 {item} 失败: {str(e)}") # 读取当前内容 with open(storage_path, "r", encoding="utf-8") as f: storage_data = json.load(f) # 只修改 machineId,保持其他配置不变 if "telemetry.machineId" in storage_data: # 生成新的 machineId new_machine_id = hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest() storage_data["telemetry.machineId"] = new_machine_id logging.info(f"更新 machineId: {new_machine_id}") # 保存修改后的内容 with open(storage_path, "w", encoding="utf-8") as f: json.dump(storage_data, f, indent=2) # 处理 updater 目录 updater_path = local_app_data / "cursor-updater" try: # 如果是目录,则删除 if updater_path.is_dir(): shutil.rmtree(str(updater_path)) logging.info("删除 updater 目录成功") # 如果是文件,则删除 if updater_path.is_file(): updater_path.unlink() logging.info("删除 updater 文件成功") # 创建同名空文件来阻止更新 updater_path.touch() logging.info("创建 updater 空文件成功") except Exception as e: logging.error(f"处理 updater 文件失败: {str(e)}") # 只清理缓存相关的路径 paths_to_clean = [ local_app_data / "Cursor" / "Cache" ] for path in paths_to_clean: try: if path.is_dir(): shutil.rmtree(str(path), ignore_errors=True) logging.info(f"删除目录成功: {path}") elif path.exists(): path.unlink() logging.info(f"删除文件成功: {path}") except Exception as e: logging.error(f"清理文件/目录失败: {path}, 错误: {str(e)}") # 修复 Cursor 启动配置 self.fix_cursor_startup() return True except Exception as e: logging.error(f"清理文件过程出错: {str(e)}") return False def fix_cursor_startup(self) -> bool: """修复 Cursor 启动警告""" try: # 1. 修改 package.json 中的更新相关配置 if self.app_path.exists(): package_json = self.app_path / "package.json" if package_json.exists(): with open(package_json, "r", encoding="utf-8") as f: data = json.load(f) # 只修改更新相关配置,与 GitHub 脚本保持一致 data["updateUrl"] = "" # 清空更新 URL data["disableUpdate"] = True # 禁用更新 with open(package_json, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) logging.info("已修复 Cursor 启动配置") return True except Exception as e: logging.error(f"修复 Cursor 启动配置失败: {str(e)}") return False