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

View File

@@ -7,11 +7,53 @@ import uuid
import hashlib
import sys
import time
from typing import Optional, Dict, Tuple
import ctypes
from typing import Optional, Dict, Tuple, List
from pathlib import Path
from utils.config import Config
from utils.cursor_registry import CursorRegistry
from cursor_auth_manager import CursorAuthManager
from utils.cursor_resetter import CursorResetter # 添加导入
def is_admin() -> bool:
"""检查是否具有管理员权限
Returns:
bool: 是否具有管理员权限
"""
try:
return ctypes.windll.shell32.IsUserAnAdmin() != 0
except:
return False
def run_as_admin():
"""以管理员权限重新运行程序"""
try:
if not is_admin():
# 获取当前脚本的路径
script = sys.argv[0]
params = ' '.join(sys.argv[1:])
# 创建 startupinfo 对象来隐藏命令行窗口
startupinfo = None
if sys.platform == "win32":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 以管理员权限重新运行
ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
sys.executable,
f'"{script}" {params}',
None,
1
)
return True
except Exception as e:
logging.error(f"以管理员权限运行失败: {str(e)}")
return False
def get_hardware_id() -> str:
"""获取硬件唯一标识"""
@@ -45,6 +87,15 @@ def get_hardware_id() -> str:
class AccountSwitcher:
def __init__(self):
# 检查管理员权限
if not is_admin():
logging.warning("当前不是管理员权限运行")
if run_as_admin():
sys.exit(0)
else:
logging.error("无法获取管理员权限")
raise PermissionError("需要管理员权限才能运行此程序")
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
self.app_path = self.cursor_path / "resources" / "app"
self.package_json = self.app_path / "package.json"
@@ -52,6 +103,9 @@ class AccountSwitcher:
self.config = Config()
self.hardware_id = self.get_hardware_id() # 先获取硬件ID
self.registry = CursorRegistry() # 添加注册表操作工具类
self.resetter = CursorResetter() # 添加重置工具类
self.max_retries = 5
self.wait_time = 1
logging.info(f"初始化硬件ID: {self.hardware_id}")
@@ -159,166 +213,333 @@ class AccountSwitcher:
Returns:
tuple: (成功标志, 消息, 账号信息)
"""
try:
data = {
"machine_id": self.hardware_id,
"code": code
}
# 禁用SSL警告
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 设置请求参数
request_kwargs = {
"json": data,
"headers": {"Content-Type": "application/json"},
"timeout": 5, # 减少超时时间从10秒到5秒
"verify": False # 禁用SSL验证
}
# 尝试发送请求
max_retries = 3 # 最大重试次数
retry_delay = 1 # 重试间隔(秒)
for attempt in range(max_retries):
try:
response = requests.post(
self.config.get_api_url("activate"),
**request_kwargs
)
except requests.exceptions.SSLError:
# 如果发生SSL错误创建自定义SSL上下文
import ssl
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
data = {
"machine_id": self.hardware_id,
"code": code
}
# 使用自定义SSL上下文重试请求
# 禁用SSL警告
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 设置请求参数
request_kwargs = {
"json": data,
"headers": {
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
"Accept": "*/*",
"Connection": "keep-alive"
},
"timeout": 10, # 增加超时时间
"verify": False # 禁用SSL验证
}
# 创建session
session = requests.Session()
session.verify = False
response = session.post(
self.config.get_api_url("activate"),
**request_kwargs
# 设置重试策略
retry_strategy = urllib3.Retry(
total=3, # 总重试次数
backoff_factor=0.5, # 重试间隔
status_forcelist=[500, 502, 503, 504] # 需要重试的HTTP状态码
)
adapter = requests.adapters.HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
try:
# 尝试发送请求
response = session.post(
self.config.get_api_url("activate"),
**request_kwargs
)
response.raise_for_status() # 检查HTTP状态码
result = response.json()
# 激活成功
if result["code"] == 200:
api_data = result["data"]
# 构造标准的返回数据结构
account_info = {
"status": "active",
"expire_time": api_data.get("expire_time", ""),
"total_days": api_data.get("total_days", 0),
"days_left": api_data.get("days_left", 0),
"device_info": self.get_device_info()
}
return True, result["msg"], account_info
# 激活码无效或已被使用
elif result["code"] == 400:
logging.warning(f"激活码无效或已被使用: {result.get('msg', '未知错误')}")
return False, result.get("msg", "激活码无效或已被使用"), None
# 其他错误情况
else:
error_msg = result.get("msg", "未知错误")
if attempt < max_retries - 1: # 如果还有重试机会
logging.warning(f"{attempt + 1}次尝试失败: {error_msg}, 准备重试...")
time.sleep(retry_delay)
continue
logging.error(f"激活失败: {error_msg}")
return False, error_msg, None
except requests.exceptions.RequestException as e:
if attempt < max_retries - 1: # 如果还有重试机会
logging.warning(f"{attempt + 1}次网络请求失败: {str(e)}, 准备重试...")
time.sleep(retry_delay)
continue
error_msg = self._get_network_error_message(e)
logging.error(f"网络请求失败: {error_msg}")
return False, f"网络连接失败: {error_msg}", None
except Exception as e:
if attempt < max_retries - 1: # 如果还有重试机会
logging.warning(f"{attempt + 1}次请求发生错误: {str(e)}, 准备重试...")
time.sleep(retry_delay)
continue
logging.error(f"激活失败: {str(e)}")
return False, f"激活失败: {str(e)}", None
# 如果所有重试都失败了
return False, "多次尝试后激活失败,请检查网络连接或稍后重试", None
def _get_network_error_message(self, error: Exception) -> str:
"""获取网络错误的友好提示信息"""
if isinstance(error, requests.exceptions.SSLError):
return "SSL证书验证失败请检查系统时间是否正确"
elif isinstance(error, requests.exceptions.ConnectionError):
if "10054" in str(error):
return "连接被重置,可能是防火墙拦截,请检查防火墙设置"
elif "10061" in str(error):
return "无法连接到服务器,请检查网络连接"
return "网络连接错误,请检查网络设置"
elif isinstance(error, requests.exceptions.Timeout):
return "请求超时,请检查网络连接"
elif isinstance(error, requests.exceptions.RequestException):
return "网络请求失败,请稍后重试"
return str(error)
def get_process_details(self, process_name: str) -> List[Dict]:
"""获取进程详细信息
Args:
process_name: 进程名称
result = response.json()
# 激活成功
if result["code"] == 200:
api_data = result["data"]
# 构造标准的返回数据结构
account_info = {
"status": "active",
"expire_time": api_data.get("expire_time", ""),
"total_days": api_data.get("total_days", 0),
"days_left": api_data.get("days_left", 0),
"device_info": self.get_device_info()
}
return True, result["msg"], account_info
# 激活码无效或已被使用
elif result["code"] == 400:
logging.warning(f"激活码无效或已被使用: {result.get('msg', '未知错误')}")
return False, result.get("msg", "激活码无效或已被使用"), None
# 其他错误情况
else:
logging.error(f"激活失败: {result.get('msg', '未知错误')}")
return False, result["msg"], None # 返回 None 而不是空的账号信息
except Exception as e:
logging.error(f"激活失败: {str(e)}")
return False, f"激活失败: {str(e)}", None # 返回 None 而不是空的账号信息
def reset_machine_id(self) -> bool:
"""重置机器码"""
Returns:
List[Dict]: 进程详细信息列表
"""
try:
# 使用 tasklist 命令替代 wmi
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
output = subprocess.check_output(
f'tasklist /FI "IMAGENAME eq {process_name}" /FO CSV /NH',
startupinfo=startupinfo,
shell=True
).decode('gbk')
processes = []
if output.strip():
for line in output.strip().split('\n'):
if line.strip():
parts = line.strip('"').split('","')
if len(parts) >= 2:
processes.append({
'name': parts[0],
'pid': parts[1]
})
return processes
except Exception as e:
logging.error(f"获取进程信息失败: {str(e)}")
return []
def close_cursor_process(self) -> bool:
"""关闭所有Cursor进程
Returns:
bool: 是否成功关闭所有进程
"""
try:
# 1. 先关闭所有Cursor进程
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 使用subprocess.run来执行命令并隐藏窗口
# 获取进程详情
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info(f"发现 {len(processes)} 个Cursor进程")
for p in processes:
logging.info(f"进程信息: PID={p['pid']}, 路径={p['name']}")
# 尝试关闭进程
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
# 等待进程关闭
retry_count = 0
while retry_count < self.max_retries:
if not self.get_process_details("Cursor.exe"):
logging.info("所有Cursor进程已关闭")
return True
retry_count += 1
if retry_count >= self.max_retries:
processes = self.get_process_details("Cursor.exe")
if processes:
logging.error(f"无法关闭以下进程:")
for p in processes:
logging.error(f"PID={p['pid']}, 路径={p['name']}")
return False
logging.warning(f"等待进程关闭, 尝试 {retry_count}/{self.max_retries}...")
time.sleep(self.wait_time)
return True
else:
# 其他系统的处理
if sys.platform == "darwin":
subprocess.run("killall Cursor 2>/dev/null", shell=True)
else:
subprocess.run("pkill -f cursor", shell=True)
time.sleep(2)
# 2. 清理注册表(包括更新系统 MachineGuid
if not self.registry.clean_registry():
logging.warning("注册表清理失败")
# 3. 清理文件(包括备份 storage.json
if not self.registry.clean_cursor_files():
logging.warning("文件清理失败")
# 4. 修改 package.json 中的 machineId
if self.package_json.exists():
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
return True
if "machineId" in data:
del data["machineId"]
with open(self.package_json, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
except Exception as e:
logging.error(f"关闭进程失败: {str(e)}")
return False
# 5. 修改 storage.json 中的遥测 ID
storage_path = Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "storage.json"
if storage_path.exists():
with open(storage_path, "r", encoding="utf-8") as f:
storage_data = json.load(f)
def reset_machine_id(self) -> bool:
"""重置机器码"""
try:
# 1. 关闭所有Cursor进程
if not self.close_cursor_process():
logging.error("无法关闭所有Cursor进程")
return False
# 只修改 machineId保持其他遥测 ID 不变
if "telemetry.machineId" in storage_data:
# 生成新的 machineId使用与 GitHub 脚本类似的格式)
new_machine_id = hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest()
storage_data["telemetry.machineId"] = new_machine_id
# 2. 使用新的重置工具类执行重置
success, message = self.resetter.reset_cursor(disable_update=True)
if not success:
logging.error(f"重置失败: {message}")
return False
with open(storage_path, "w", encoding="utf-8") as f:
json.dump(storage_data, f, indent=2)
# 6. 重启Cursor
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
# 不在这里重启Cursor让调用者决定何时重启
logging.info("机器码重置完成")
return True
except Exception as e:
logging.error(f"重置机器码失败: {str(e)}")
return False
def bypass_version_limit(self) -> Tuple[bool, str]:
"""突破Cursor版本限制"""
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
# 1. 先关闭所有Cursor进程
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
logging.info("正在重启Cursor...")
# 确保进程已关闭
if not self.close_cursor_process():
logging.error("无法关闭Cursor进程")
return False
# 关闭Cursor
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
# 等待进程完全关闭
time.sleep(2)
# 2. 重置机器码
if not self.reset_machine_id():
return False, "重置机器码失败"
# 3. 等待Cursor启动
time.sleep(3)
return True, "突破版本限制成功Cursor已重启"
# 启动Cursor
if sys.platform == "win32":
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
try:
# 使用subprocess启动
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen(
str(cursor_exe),
startupinfo=startupinfo,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
# 等待进程启动
time.sleep(3)
# 验证进程是否启动
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info("Cursor启动成功")
return True
else:
logging.error("Cursor进程未找到")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(3)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
logging.error(f"启动Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(3)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
try:
subprocess.run("open -a Cursor", shell=True, check=True)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
elif sys.platform == "linux":
try:
subprocess.run("cursor &", shell=True, check=True)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
return False
except Exception as e:
logging.error(f"突破版本限制失败: {str(e)}")
return False, f"突破失败: {str(e)}"
logging.error(f"重启Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为最后的备选方案
try:
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
time.sleep(3)
logging.info("使用最终备选方案启动Cursor")
return True
except:
pass
return False
def activate_and_switch(self, activation_code: str) -> Tuple[bool, str]:
"""激活并切换账号
@@ -416,60 +637,6 @@ class AccountSwitcher:
"device_info": self.get_device_info()
}
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
logging.info("正在重启Cursor...")
if sys.platform == "win32":
# Windows系统
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 关闭Cursor
subprocess.run(
"taskkill /f /im Cursor.exe 2>nul",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
# 启动Cursor
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
# macOS系统
subprocess.run("killall Cursor 2>/dev/null", shell=True)
time.sleep(2)
subprocess.run("open -a Cursor", shell=True)
logging.info("Cursor重启成功")
return True
elif sys.platform == "linux":
# Linux系统
subprocess.run("pkill -f cursor", shell=True)
time.sleep(2)
subprocess.run("cursor &", shell=True)
logging.info("Cursor重启成功")
return True
else:
logging.error(f"不支持的操作系统: {sys.platform}")
return False
except Exception as e:
logging.error(f"重启Cursor时发生错误: {str(e)}")
return False
def refresh_cursor_auth(self) -> Tuple[bool, str]:
"""刷新Cursor授权
@@ -494,8 +661,8 @@ class AccountSwitcher:
request_kwargs = {
"json": data,
"headers": headers,
"timeout": 30, # 增加超时时间
"verify": False # 禁用SSL验证
"timeout": 30,
"verify": False
}
try:
@@ -530,29 +697,54 @@ class AccountSwitcher:
return False, "获取账号信息不完整"
# 2. 先关闭Cursor进程
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 使用subprocess.run来执行命令并隐藏窗口
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
logging.info("正在关闭Cursor进程...")
if not self.close_cursor_process():
logging.error("无法关闭Cursor进程")
return False, "无法关闭Cursor进程请手动关闭后重试"
# 3. 更新Cursor认证信息
logging.info("正在更新认证信息...")
if not self.auth_manager.update_auth(email, access_token, refresh_token):
return False, "更新Cursor认证信息失败"
# 4. 重置机器码使用现有的reset_machine_id方法
# 4. 验证认证信息是否正确写入
logging.info("正在验证认证信息...")
if not self.auth_manager.verify_auth(email, access_token, refresh_token):
return False, "认证信息验证失败"
# 5. 保存email到package.json
try:
if self.package_json.exists():
with open(self.package_json, "r", encoding="utf-8") as f:
package_data = json.load(f)
package_data["email"] = email
with open(self.package_json, "w", encoding="utf-8", newline='\n') as f:
json.dump(package_data, f, indent=2)
logging.info(f"已保存email到package.json: {email}")
except Exception as e:
logging.warning(f"保存email到package.json失败: {str(e)}")
# 6. 重置机器码
logging.info("正在重置机器码...")
if not self.reset_machine_id():
return False, "重置机器码失败"
return True, f"授权刷新成功Cursor编辑器已重启\n邮箱: {email}\n"
# 7. 重启Cursor只在这里执行一次重启
logging.info("正在重启Cursor...")
retry_count = 0
max_retries = 3
while retry_count < max_retries:
if self.restart_cursor():
break
retry_count += 1
if retry_count < max_retries:
logging.warning(f"重启失败,正在重试 ({retry_count}/{max_retries})...")
time.sleep(2)
if retry_count >= max_retries:
return True, f"授权刷新成功但Cursor重启失败请手动启动Cursor\n邮箱: {email}\n到期时间: {expire_time}\n剩余天数: {days_left}"
return True, f"授权刷新成功Cursor已重启\n邮箱: {email}\n到期时间: {expire_time}\n剩余天数: {days_left}"
elif response_data.get("code") == 404:
return False, "没有可用的未使用账号"
@@ -634,9 +826,112 @@ class AccountSwitcher:
logging.error(f"禁用Cursor更新失败: {str(e)}")
return False, f"禁用更新失败: {str(e)}"
def send_heartbeat(self) -> Tuple[bool, str]:
"""
发送心跳请求
Returns:
Tuple[bool, str]: (是否成功, 消息)
"""
max_retries = 3 # 最大重试次数
retry_delay = 1 # 重试间隔(秒)
# 获取硬件ID
hardware_id = self.get_hardware_id()
# 禁用SSL警告
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 设置请求参数
params = {
"machine_id": hardware_id
}
request_kwargs = {
"params": params, # 使用URL参数而不是JSON body
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
"Accept": "*/*",
"Connection": "keep-alive"
},
"timeout": 10,
"verify": False
}
for attempt in range(max_retries):
try:
# 创建session
session = requests.Session()
session.verify = False
# 设置重试策略
retry_strategy = urllib3.Retry(
total=3,
backoff_factor=0.5,
status_forcelist=[500, 502, 503, 504]
)
adapter = requests.adapters.HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
# 发送请求
response = session.get( # 改用GET请求
self.config.get_api_url("heartbeat"),
**request_kwargs
)
# 检查响应
if response.status_code == 200:
result = response.json()
if result.get("code") == 200: # 修改成功码为200
data = result.get("data", {})
expire_time = data.get("expire_time", "")
days_left = data.get("days_left", 0)
status = data.get("status", "")
return True, f"心跳发送成功 [到期时间: {expire_time}, 剩余天数: {days_left}, 状态: {status}]"
else:
error_msg = result.get("msg", "未知错误")
if attempt < max_retries - 1:
logging.warning(f"{attempt + 1}次心跳失败: {error_msg}, 准备重试...")
time.sleep(retry_delay)
continue
return False, f"心跳发送失败: {error_msg}"
else:
if attempt < max_retries - 1:
logging.warning(f"{attempt + 1}次心跳HTTP错误: {response.status_code}, 准备重试...")
time.sleep(retry_delay)
continue
return False, f"心跳请求失败: HTTP {response.status_code}"
except requests.exceptions.RequestException as e:
if attempt < max_retries - 1:
logging.warning(f"{attempt + 1}次网络请求失败: {str(e)}, 准备重试...")
time.sleep(retry_delay)
continue
error_message = self._get_network_error_message(e)
return False, f"心跳发送失败: {error_message}"
except Exception as e:
if attempt < max_retries - 1:
logging.warning(f"{attempt + 1}次发生异常: {str(e)}, 准备重试...")
time.sleep(retry_delay)
continue
logging.error(f"心跳发送异常: {str(e)}")
return False, f"心跳发送异常: {str(e)}"
return False, "多次尝试后心跳发送失败"
def main():
"""主函数"""
try:
# 检查管理员权限
if not is_admin():
print("\n[错误] 请以管理员身份运行此程序")
print("请右键点击程序,选择'以管理员身份运行'")
if run_as_admin():
return
input("\n按回车键退出...")
return
switcher = AccountSwitcher()
print("\n=== Cursor账号切换工具 ===")
@@ -665,6 +960,9 @@ def main():
else:
print("\n机器码重置失败,请查看日志了解详细信息")
except PermissionError as e:
print(f"\n[错误] {str(e)}")
print("请右键点击程序,选择'以管理员身份运行'")
except Exception as e:
logging.error(f"程序执行出错: {str(e)}")
print("\n程序执行出错,请查看日志了解详细信息")