添加项目核心文件和 .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 subprocess
import uuid import uuid
import hashlib import hashlib
import sys
import time
from typing import Optional, Dict, Tuple from typing import Optional, Dict, Tuple
from pathlib import Path from pathlib import Path
from utils.config import Config from utils.config import Config
from utils.cursor_registry import CursorRegistry
from cursor_auth_manager import CursorAuthManager
def get_hardware_id() -> str: def get_hardware_id() -> str:
"""获取硬件唯一标识""" """获取硬件唯一标识"""
try: try:
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = None
if sys.platform == "win32":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 获取CPU信息 # 获取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() 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() board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号 # 获取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() bios_id = bios_info.split('\n')[1].strip()
# 组合信息并生成哈希 # 组合信息并生成哈希
@@ -32,35 +43,6 @@ def get_hardware_id() -> str:
# 如果获取失败使用UUID作为备选方案 # 如果获取失败使用UUID作为备选方案
return str(uuid.uuid4()) 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: class AccountSwitcher:
def __init__(self): def __init__(self):
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor" self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
@@ -69,6 +51,7 @@ class AccountSwitcher:
self.auth_manager = CursorAuthManager() self.auth_manager = CursorAuthManager()
self.config = Config() self.config = Config()
self.hardware_id = get_hardware_id() self.hardware_id = get_hardware_id()
self.registry = CursorRegistry() # 添加注册表操作工具类
def get_device_info(self) -> dict: def get_device_info(self) -> dict:
"""获取设备信息""" """获取设备信息"""
@@ -384,6 +367,142 @@ class AccountSwitcher:
"activation_records": [] "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(): def main():
"""主函数""" """主函数"""
try: 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) btn_frame.pack(fill="x", pady=5)
self.style.configure("Action.TButton", padding=8) 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="刷新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="突破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) ttk.Button(btn_frame, text="禁用Cursor版本更新", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
def copy_device_id(self): def copy_device_id(self):
@@ -283,21 +283,13 @@ class MainWindow:
messagebox.showinfo("提示", "此功能暂未实现") messagebox.showinfo("提示", "此功能暂未实现")
def reset_machine_id(self): def reset_machine_id(self):
"""重置机器码""" """刷新Cursor编辑器授权"""
# 先检查状态
if not self.check_status():
return
try: try:
if self.switcher.reset_machine_id(): # 刷新授权
messagebox.showinfo("成功", "机器码重置成功") success, message = self.switcher.refresh_cursor_auth()
# 重置后检查一次状态 if success:
self.check_status() messagebox.showinfo("成功", "Cursor编辑器授权刷新成功\n" + message)
else: else:
messagebox.showerror("错误", "机器码重置失败,请查看日志") messagebox.showerror("错误", message)
# 失败后也检查状态
self.check_status()
except Exception as e: except Exception as e:
messagebox.showerror("错误", f"重置失败: {str(e)}") messagebox.showerror("错误", f"刷新失败: {str(e)}")
# 出错后也检查状态
self.check_status()

56
main.py
View File

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

View File

@@ -1,2 +1,4 @@
requests==2.31.0 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