feat(v3.4.7): 重构重置功能和优化重启逻辑

1. 新增 CursorResetter 类,完整封装 cursor_win_id_modifier.ps1 的核心功能

2. 优化 AccountSwitcher 的重启逻辑,避免重复重启

3. 改进进程管理,移除 wmi 依赖,使用 tasklist 替代

4. 提升代码可维护性,后续只需更新 CursorResetter 即可适配脚本变更
This commit is contained in:
huangzhenpc
2025-02-14 15:06:05 +08:00
parent 10523de040
commit 8b2fbef54a
9 changed files with 1322 additions and 437 deletions

227
utils/cursor_resetter.py Normal file
View File

@@ -0,0 +1,227 @@
import os
import sys
import json
import logging
import subprocess
import uuid
from pathlib import Path
from datetime import datetime
from typing import Optional, Tuple, Dict
class CursorResetter:
"""Cursor重置工具类封装PowerShell脚本的核心功能"""
def __init__(self):
self.appdata = os.getenv('APPDATA')
self.localappdata = os.getenv('LOCALAPPDATA')
self.storage_file = Path(self.appdata) / "Cursor" / "User" / "globalStorage" / "storage.json"
self.backup_dir = Path(self.appdata) / "Cursor" / "User" / "globalStorage" / "backups"
self.cursor_path = Path(self.localappdata) / "Programs" / "cursor"
self.app_path = self.cursor_path / "resources" / "app"
self.package_json = self.app_path / "package.json"
def get_random_hex(self, length: int) -> str:
"""生成安全的随机十六进制字符串"""
import secrets
return secrets.token_hex(length)
def new_standard_machine_id(self) -> str:
"""生成标准格式的机器ID"""
template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
import random
def replace_char(match):
r = random.randint(0, 15)
v = r if match == 'x' else (r & 0x3 | 0x8)
return hex(v)[2:]
return ''.join(replace_char(c) for c in template)
def generate_ids(self) -> Dict[str, str]:
"""生成所有需要的ID"""
# 生成标准格式的ID
mac_machine_id = self.new_standard_machine_id()
uuid_str = str(uuid.uuid4())
# 生成带前缀的machineId
prefix = "auth0|user_"
prefix_hex = ''.join(hex(b)[2:].zfill(2) for b in prefix.encode())
random_part = self.get_random_hex(32)
machine_id = f"{prefix_hex}{random_part}"
# 生成大写的SQM ID
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
return {
"mac_machine_id": mac_machine_id,
"uuid": uuid_str,
"machine_id": machine_id,
"sqm_id": sqm_id
}
def backup_file(self, file_path: Path) -> Optional[Path]:
"""备份文件"""
try:
if not file_path.exists():
return None
self.backup_dir.mkdir(parents=True, exist_ok=True)
backup_name = f"{file_path.name}.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
backup_path = self.backup_dir / backup_name
import shutil
shutil.copy2(file_path, backup_path)
logging.info(f"已备份文件: {backup_path}")
return backup_path
except Exception as e:
logging.error(f"备份文件失败: {str(e)}")
return None
def update_machine_guid(self) -> bool:
"""更新系统MachineGuid"""
try:
import winreg
new_guid = str(uuid.uuid4())
registry_path = r"SOFTWARE\Microsoft\Cryptography"
# 备份原始值
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0,
winreg.KEY_READ | winreg.KEY_WOW64_64KEY) as key:
original_guid = winreg.QueryValueEx(key, "MachineGuid")[0]
# 备份到文件
self.backup_dir.mkdir(parents=True, exist_ok=True)
backup_path = self.backup_dir / f"MachineGuid.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
with open(backup_path, 'w', encoding='utf-8') as f:
f.write(original_guid)
# 更新GUID
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY) as key:
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
logging.info(f"已更新系统MachineGuid: {new_guid}")
return True
except Exception as e:
logging.error(f"更新MachineGuid失败: {str(e)}")
return False
def update_storage_json(self) -> bool:
"""更新storage.json文件"""
try:
if not self.storage_file.exists():
logging.error(f"未找到配置文件: {self.storage_file}")
return False
# 备份文件
if not self.backup_file(self.storage_file):
logging.warning("配置文件备份失败")
# 生成新ID
ids = self.generate_ids()
# 读取并更新配置
with open(self.storage_file, "r", encoding="utf-8") as f:
config = json.load(f)
# 更新ID
config['telemetry.machineId'] = ids['machine_id']
config['telemetry.macMachineId'] = ids['mac_machine_id']
config['telemetry.devDeviceId'] = ids['uuid']
config['telemetry.sqmId'] = ids['sqm_id']
# 保存更新
with open(self.storage_file, "w", encoding="utf-8", newline='\n') as f:
json.dump(config, f, indent=2)
logging.info("已更新配置文件")
return True
except Exception as e:
logging.error(f"更新配置文件失败: {str(e)}")
return False
def disable_auto_update(self) -> bool:
"""禁用自动更新"""
try:
updater_path = Path(self.localappdata) / "cursor-updater"
# 删除现有文件/目录
if updater_path.exists():
if updater_path.is_dir():
import shutil
shutil.rmtree(updater_path)
else:
updater_path.unlink()
# 创建空文件并设置只读
updater_path.touch()
import stat
updater_path.chmod(stat.S_IREAD)
# 设置文件权限
if os.name == 'nt':
subprocess.run(
f'icacls "{updater_path}" /inheritance:r /grant:r "{os.getenv("USERNAME")}:(R)"',
shell=True,
check=True
)
logging.info("已禁用自动更新")
return True
except Exception as e:
logging.error(f"禁用自动更新失败: {str(e)}")
return False
def reset_cursor(self, disable_update: bool = True) -> Tuple[bool, str]:
"""重置Cursor
Args:
disable_update: 是否禁用自动更新
Returns:
Tuple[bool, str]: (是否成功, 消息)
"""
try:
# 1. 检查管理员权限
if os.name == 'nt':
import ctypes
if not ctypes.windll.shell32.IsUserAnAdmin():
return False, "需要管理员权限来执行重置操作"
# 2. 更新配置文件
if not self.update_storage_json():
return False, "更新配置文件失败"
# 3. 更新系统MachineGuid
if not self.update_machine_guid():
return False, "更新系统MachineGuid失败"
# 4. 禁用自动更新(如果需要)
if disable_update and not self.disable_auto_update():
logging.warning("禁用自动更新失败")
# 5. 修改package.json
if self.package_json.exists():
try:
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
if "machineId" in data:
del data["machineId"]
data["updateUrl"] = ""
data["disableUpdate"] = True
with open(self.package_json, "w", encoding="utf-8", newline='\n') as f:
json.dump(data, f, indent=2)
except Exception as e:
logging.warning(f"修改package.json失败: {str(e)}")
return True, "Cursor重置成功"
except Exception as e:
logging.error(f"重置过程出错: {str(e)}")
return False, f"重置失败: {str(e)}"