226 lines
9.9 KiB
Python
226 lines
9.9 KiB
Python
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 |