添加项目核心文件和 .gitignore

This commit is contained in:
huangzhenpc
2025-02-12 09:33:04 +08:00
parent aff9928984
commit fdc56730bb
6 changed files with 409 additions and 70 deletions

View File

@@ -5,23 +5,34 @@ import logging
import subprocess
import uuid
import hashlib
import sys
import time
from typing import Optional, Dict, Tuple
from pathlib import Path
from utils.config import Config
from utils.cursor_registry import CursorRegistry
from cursor_auth_manager import CursorAuthManager
def get_hardware_id() -> str:
"""获取硬件唯一标识"""
try:
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = None
if sys.platform == "win32":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 获取CPU信息
cpu_info = subprocess.check_output('wmic cpu get ProcessorId').decode()
cpu_info = subprocess.check_output('wmic cpu get ProcessorId', startupinfo=startupinfo).decode()
cpu_id = cpu_info.split('\n')[1].strip()
# 获取主板序列号
board_info = subprocess.check_output('wmic baseboard get SerialNumber').decode()
board_info = subprocess.check_output('wmic baseboard get SerialNumber', startupinfo=startupinfo).decode()
board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号
bios_info = subprocess.check_output('wmic bios get SerialNumber').decode()
bios_info = subprocess.check_output('wmic bios get SerialNumber', startupinfo=startupinfo).decode()
bios_id = bios_info.split('\n')[1].strip()
# 组合信息并生成哈希
@@ -32,35 +43,6 @@ def get_hardware_id() -> str:
# 如果获取失败使用UUID作为备选方案
return str(uuid.uuid4())
class CursorAuthManager:
def __init__(self):
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"
def update_auth(self, email: str, access_token: str, refresh_token: str) -> bool:
"""更新Cursor认证信息"""
try:
# 读取package.json
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
# 更新认证信息
data["email"] = email
data["accessToken"] = access_token
data["refreshToken"] = refresh_token
# 保存更新后的文件
with open(self.package_json, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
logging.info(f"认证信息更新成功: {email}")
return True
except Exception as e:
logging.error(f"更新认证信息失败: {str(e)}")
return False
class AccountSwitcher:
def __init__(self):
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
@@ -69,6 +51,7 @@ class AccountSwitcher:
self.auth_manager = CursorAuthManager()
self.config = Config()
self.hardware_id = get_hardware_id()
self.registry = CursorRegistry() # 添加注册表操作工具类
def get_device_info(self) -> dict:
"""获取设备信息"""
@@ -384,6 +367,142 @@ class AccountSwitcher:
"activation_records": []
}
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
logging.info("正在重启Cursor...")
if sys.platform == "win32":
# Windows系统
# 关闭Cursor
os.system("taskkill /f /im Cursor.exe 2>nul")
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系统
os.system("killall Cursor 2>/dev/null")
time.sleep(2)
os.system("open -a Cursor")
logging.info("Cursor重启成功")
return True
elif sys.platform == "linux":
# Linux系统
os.system("pkill -f cursor")
time.sleep(2)
os.system("cursor &")
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授权
Returns:
Tuple[bool, str]: (是否成功, 提示消息)
"""
try:
# 获取未使用的账号
endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused"
data = {
"machine_id": self.hardware_id
}
headers = {
"Content-Type": "application/json"
}
try:
# 添加SSL验证选项和超时设置
response = requests.post(
endpoint,
json=data,
headers=headers,
timeout=30, # 增加超时时间
verify=False, # 禁用SSL验证
)
# 禁用SSL警告
requests.packages.urllib3.disable_warnings()
response_data = response.json()
if response_data.get("code") == 200:
account_data = response_data.get("data", {})
# 获取账号信息
email = account_data.get("email", "")
access_token = account_data.get("access_token", "")
refresh_token = account_data.get("refresh_token", "")
expire_time = account_data.get("expire_time", "")
days_left = account_data.get("days_left", 0)
if not all([email, access_token, refresh_token]):
return False, "获取账号信息不完整"
# 更新Cursor认证信息
if not self.auth_manager.update_auth(email, access_token, refresh_token):
return False, "更新Cursor认证信息失败"
# 重置机器码
if not self.reset_machine_id():
return False, "重置机器码失败"
# 刷新注册表
if not self.registry.refresh_registry():
logging.warning("注册表刷新失败,但不影响主要功能")
# 重启Cursor
if not self.auth_manager.restart_cursor():
return False, "重启Cursor失败"
# 重启Cursor
# if not self.restart_cursor():
# logging.warning("Cursor重启失败请手动重启")
# return True, f"授权刷新成功请手动重启Cursor编辑器\n邮箱: {email}\n到期时间: {expire_time}\n剩余天数: {days_left}天"
return True, f"授权刷新成功Cursor编辑器已重启\n邮箱: {email}\n"
elif response_data.get("code") == 404:
return False, "没有可用的未使用账号"
else:
error_msg = response_data.get("msg", "未知错误")
logging.error(f"获取未使用账号失败: {error_msg}")
return False, f"获取账号失败: {error_msg}"
except requests.exceptions.SSLError as e:
logging.error(f"SSL验证失败: {str(e)}")
return False, "SSL验证失败,请检查网络设置"
except requests.exceptions.ConnectionError as e:
logging.error(f"网络连接错误: {str(e)}")
return False, "网络连接失败,请检查网络设置"
except requests.exceptions.Timeout as e:
logging.error(f"请求超时: {str(e)}")
return False, "请求超时,请稍后重试"
except requests.RequestException as e:
logging.error(f"请求失败: {str(e)}")
return False, f"网络请求失败: {str(e)}"
except Exception as e:
logging.error(f"未知错误: {str(e)}")
return False, f"发生未知错误: {str(e)}"
except Exception as e:
logging.error(f"刷新授权过程出错: {str(e)}")
return False, f"刷新失败: {str(e)}"
def main():
"""主函数"""
try:

134
cursor_auth_manager.py Normal file
View File

@@ -0,0 +1,134 @@
import os
import json
import sys
import time
import logging
import sqlite3
from pathlib import Path
class CursorAuthManager:
"""Cursor认证信息管理器"""
def __init__(self):
# 判断操作系统
if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA")
if appdata is None:
raise EnvironmentError("APPDATA 环境变量未设置")
self.db_path = os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
)
elif sys.platform == "darwin": # macOS
self.db_path = os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
))
elif sys.platform == "linux" : # Linux 和其他类Unix系统
self.db_path = os.path.abspath(os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb"
))
else:
raise NotImplementedError(f"不支持的操作系统: {sys.platform}")
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
def update_auth(self, email=None, access_token=None, refresh_token=None):
"""
更新Cursor的认证信息
:param email: 新的邮箱地址
:param access_token: 新的访问令牌
:param refresh_token: 新的刷新令牌
:return: bool 是否成功更新
"""
updates = []
# 登录状态
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
if email is not None:
updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token))
if not updates:
logging.warning("没有提供任何要更新的值")
return False
conn = None
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
for key, value in updates:
# 检查key是否存在
check_query = f"SELECT COUNT(*) FROM itemTable WHERE key = ?"
cursor.execute(check_query, (key,))
if cursor.fetchone()[0] == 0:
insert_query = "INSERT INTO itemTable (key, value) VALUES (?, ?)"
cursor.execute(insert_query, (key, value))
else:
update_query = "UPDATE itemTable SET value = ? WHERE key = ?"
cursor.execute(update_query, (value, key))
if cursor.rowcount > 0:
logging.info(f"成功更新 {key.split('/')[-1]}")
else:
logging.warning(f"未找到 {key.split('/')[-1]} 或值未变化")
conn.commit()
logging.info(f"认证信息更新成功: {email}")
return True
except sqlite3.Error as e:
logging.error(f"数据库错误: {str(e)}")
return False
except Exception as e:
logging.error(f"更新认证信息失败: {str(e)}")
return False
finally:
if conn:
conn.close()
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
logging.info("正在重启Cursor...")
if sys.platform == "win32":
# Windows系统
# 关闭Cursor
os.system("taskkill /f /im Cursor.exe 2>nul")
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系统
os.system("killall Cursor 2>/dev/null")
time.sleep(2)
os.system("open -a Cursor")
logging.info("Cursor重启成功")
return True
elif sys.platform == "linux":
# Linux系统
os.system("pkill -f cursor")
time.sleep(2)
os.system("cursor &")
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

View File

@@ -110,8 +110,8 @@ class MainWindow:
btn_frame.pack(fill="x", pady=5)
self.style.configure("Action.TButton", padding=8)
ttk.Button(btn_frame, text="刷新Cursor授权", command=self.reset_machine_id, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="实现Cursor0.45.x限制", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="刷新Cursor编辑器授权", command=self.reset_machine_id, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="突破Cursor0.45.x限制", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="禁用Cursor版本更新", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
def copy_device_id(self):
@@ -283,21 +283,13 @@ class MainWindow:
messagebox.showinfo("提示", "此功能暂未实现")
def reset_machine_id(self):
"""重置机器码"""
# 先检查状态
if not self.check_status():
return
"""刷新Cursor编辑器授权"""
try:
if self.switcher.reset_machine_id():
messagebox.showinfo("成功", "机器码重置成功")
# 重置后检查一次状态
self.check_status()
# 刷新授权
success, message = self.switcher.refresh_cursor_auth()
if success:
messagebox.showinfo("成功", "Cursor编辑器授权刷新成功\n" + message)
else:
messagebox.showerror("错误", "机器码重置失败,请查看日志")
# 失败后也检查状态
self.check_status()
messagebox.showerror("错误", message)
except Exception as e:
messagebox.showerror("错误", f"重置失败: {str(e)}")
# 出错后也检查状态
self.check_status()
messagebox.showerror("错误", f"刷新失败: {str(e)}")

60
main.py
View File

@@ -1,37 +1,57 @@
import logging
import sys
import traceback
from pathlib import Path
from gui.main_window import MainWindow
def setup_logging():
"""设置日志"""
log_dir = Path.home() / ".cursor_switcher" / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
log_file = log_dir / "switcher.log"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(log_file, encoding="utf-8"),
logging.StreamHandler()
]
)
logging.info("日志系统初始化完成")
try:
log_dir = Path.home() / ".cursor_switcher" / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
log_file = log_dir / "switcher.log"
# 只输出到文件,不输出到控制台
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(log_file, encoding="utf-8"),
]
)
except Exception as e:
# 不打印错误信息,只记录到日志
pass
def main():
"""主函数"""
try:
setup_logging()
logging.info("启动GUI界面...")
# 检查Python版本
logging.info(f"Python版本: {sys.version}")
# 检查工作目录
logging.info(f"当前工作目录: {Path.cwd()}")
# 检查模块路径
logging.info("Python路径:")
for p in sys.path:
logging.info(f" - {p}")
logging.info("正在初始化主窗口...")
window = MainWindow()
logging.info("正在启动主窗口...")
window.run()
except KeyboardInterrupt:
logging.info("程序被用户中断")
except Exception as e:
logging.error(f"程序运行出错: {str(e)}")
finally:
logging.info("程序退出")
error_msg = f"程序运行出错: {str(e)}\n{traceback.format_exc()}"
logging.error(error_msg)
# 使用tkinter的消息框显示错误
from tkinter import messagebox
messagebox.showerror("错误", error_msg)
if __name__ == "__main__":
main()

View File

@@ -1,2 +1,4 @@
requests==2.31.0
pyinstaller==6.3.0
pyinstaller==6.3.0
pillow==10.2.0 # 用于处理图标
setuptools==65.5.1 # 解决pkg_resources.extern问题

72
utils/cursor_registry.py Normal file
View File

@@ -0,0 +1,72 @@
import os
import winreg
import logging
from pathlib import Path
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 refresh_registry(self) -> bool:
"""刷新Cursor相关的注册表项
Returns:
bool: 是否成功
"""
try:
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if not cursor_exe.exists():
logging.error("未找到Cursor.exe")
return False
# 刷新注册表项
try:
# 打开HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Cursor
key_path = r"Software\Classes\Directory\Background\shell\Cursor"
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, "Icon", 0, winreg.REG_SZ, str(cursor_exe))
# 打开command子键
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path + r"\command", 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, "", 0, winreg.REG_SZ, f'"{str(cursor_exe)}" "%V"')
logging.info("注册表刷新成功")
return True
except WindowsError as e:
logging.error(f"刷新注册表失败: {str(e)}")
return False
except Exception as e:
logging.error(f"刷新注册表过程出错: {str(e)}")
return False
def clean_registry(self) -> bool:
"""清理Cursor相关的注册表项
Returns:
bool: 是否成功
"""
try:
# 删除注册表项
try:
key_path = r"Software\Classes\Directory\Background\shell\Cursor"
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path + r"\command")
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path)
logging.info("注册表清理成功")
return True
except WindowsError as e:
if e.winerror == 2: # 找不到注册表项
logging.info("注册表项不存在,无需清理")
return True
logging.error(f"清理注册表失败: {str(e)}")
return False
except Exception as e:
logging.error(f"清理注册表过程出错: {str(e)}")
return False