From 2b48478746c1977363bfe8befd121a4abadf9392 Mon Sep 17 00:00:00 2001 From: huangzhenpc Date: Tue, 11 Feb 2025 17:52:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E6=89=93=E5=8C=85=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=88=87=E6=8D=A2=E4=B8=8B=E4=B8=80=E6=AD=A5?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=AA=81=E7=A0=B4cursor0.45?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accounts.json | 96 +++++++++++++++ interactive/account_switcher.py | 170 ++++++++++++++++++++++----- interactive/cursor_auth_manager.py | 134 +++++++++++++++++++++ interactive/gui/main_window.py | 26 ++-- interactive/main.py | 64 ++++++---- interactive/requirements.txt | 4 +- interactive/utils/cursor_registry.py | 72 ++++++++++++ test_api_response.py | 41 ++++++- 8 files changed, 538 insertions(+), 69 deletions(-) create mode 100644 interactive/cursor_auth_manager.py create mode 100644 interactive/utils/cursor_registry.py diff --git a/accounts.json b/accounts.json index a08f02e..47c3f22 100644 --- a/accounts.json +++ b/accounts.json @@ -131,6 +131,102 @@ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", "registration_time": "2025-02-11T13:26:42.615884", "created_at": "2025-02-11T13:26:42.615884" + }, + { + "email": "fzbsolew265285@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Kdlynp", + "last_name": "Omcope", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNE45VFowOUc3R1haMDhHVlgxQzZYIiwidGltZSI6IjE3MzkyNjUzNDMiLCJyYW5kb21uZXNzIjoiOTRiZWM5YTctOWYyMi00NGFhIiwiZXhwIjo0MzMxMjY1MzQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ZnYwmu8RF4BvEaH7ZFWgnzgn6ZIBMHxKqS2-ZQu7W-0", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNE45VFowOUc3R1haMDhHVlgxQzZYIiwidGltZSI6IjE3MzkyNjUzNDMiLCJyYW5kb21uZXNzIjoiOTRiZWM5YTctOWYyMi00NGFhIiwiZXhwIjo0MzMxMjY1MzQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ZnYwmu8RF4BvEaH7ZFWgnzgn6ZIBMHxKqS2-ZQu7W-0", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:16:21.905458", + "created_at": "2025-02-11T17:16:21.905458" + }, + { + "email": "chvldgvz265405@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Nsgtsc", + "last_name": "Yrgcuy", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFJYODE0WVhQMlpaWkpENk1aUlI1IiwidGltZSI6IjE3MzkyNjU0NjIiLCJyYW5kb21uZXNzIjoiZDJiNDAzMDQtNTcyNy00ZTg5IiwiZXhwIjo0MzMxMjY1NDYyLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.dBB1AiYk06INf_YnN8Kuu1ZgI9kUi5eDqWz4C2ExfQg", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFJYODE0WVhQMlpaWkpENk1aUlI1IiwidGltZSI6IjE3MzkyNjU0NjIiLCJyYW5kb21uZXNzIjoiZDJiNDAzMDQtNTcyNy00ZTg5IiwiZXhwIjo0MzMxMjY1NDYyLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.dBB1AiYk06INf_YnN8Kuu1ZgI9kUi5eDqWz4C2ExfQg", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:18:16.076468", + "created_at": "2025-02-11T17:18:16.076468" + }, + { + "email": "skdjndzg265518@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Suykzr", + "last_name": "Fomxgs", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFdCWENBVFpCMkcwQkFCMlExQUdDIiwidGltZSI6IjE3MzkyNjU1NzQiLCJyYW5kb21uZXNzIjoiZTU1Mzk3YjgtYzU5Ny00ZWIwIiwiZXhwIjo0MzMxMjY1NTc0LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.bIrBvtQxgV8tiD0MK8ZkdBnvoCvWcmGWbMzXyJo__-Q", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFdCWENBVFpCMkcwQkFCMlExQUdDIiwidGltZSI6IjE3MzkyNjU1NzQiLCJyYW5kb21uZXNzIjoiZTU1Mzk3YjgtYzU5Ny00ZWIwIiwiZXhwIjo0MzMxMjY1NTc0LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.bIrBvtQxgV8tiD0MK8ZkdBnvoCvWcmGWbMzXyJo__-Q", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:20:12.770495", + "created_at": "2025-02-11T17:20:12.770495" + }, + { + "email": "qgmjlglr265641@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Ozyepu", + "last_name": "Nyrnjw", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTA1Vk5WOFE4Mko1OERKQlc0NFFKIiwidGltZSI6IjE3MzkyNjU2OTkiLCJyYW5kb21uZXNzIjoiOWQ2Y2NjYTMtYTU4Yy00OWI0IiwiZXhwIjo0MzMxMjY1Njk5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.cuR7IJ2SDyvWHzdXk6wfRDL1L2AH38AIs1hweN49u2s", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTA1Vk5WOFE4Mko1OERKQlc0NFFKIiwidGltZSI6IjE3MzkyNjU2OTkiLCJyYW5kb21uZXNzIjoiOWQ2Y2NjYTMtYTU4Yy00OWI0IiwiZXhwIjo0MzMxMjY1Njk5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.cuR7IJ2SDyvWHzdXk6wfRDL1L2AH38AIs1hweN49u2s", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:22:17.167933", + "created_at": "2025-02-11T17:22:17.167933" + }, + { + "email": "cetkjbyo265764@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Ykaokw", + "last_name": "Xgnibi", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTNXUUtRRkY1VEpQNTJQV1o1V1k2IiwidGltZSI6IjE3MzkyNjU4MjEiLCJyYW5kb21uZXNzIjoiYThkNTZiODgtYTQ2OS00NDY0IiwiZXhwIjo0MzMxMjY1ODIxLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.2VcD_cjuI1V6775SrHuJEPKMJz094yRywVC-SdgKMl8", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTNXUUtRRkY1VEpQNTJQV1o1V1k2IiwidGltZSI6IjE3MzkyNjU4MjEiLCJyYW5kb21uZXNzIjoiYThkNTZiODgtYTQ2OS00NDY0IiwiZXhwIjo0MzMxMjY1ODIxLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.2VcD_cjuI1V6775SrHuJEPKMJz094yRywVC-SdgKMl8", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:24:20.739727", + "created_at": "2025-02-11T17:24:20.739727" + }, + { + "email": "vosglsoj265881@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Tbkztl", + "last_name": "Cpcnyd", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTdGMjQ3QktYNlFHRlpTNjVOOFBXIiwidGltZSI6IjE3MzkyNjU5MzgiLCJyYW5kb21uZXNzIjoiOWIzNjdmN2QtNmRkMy00ZGNkIiwiZXhwIjo0MzMxMjY1OTM4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.6hR-fP769x368tTlBjmtIK0ZpHeJOE9Y4FynxAr48bI", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTdGMjQ3QktYNlFHRlpTNjVOOFBXIiwidGltZSI6IjE3MzkyNjU5MzgiLCJyYW5kb21uZXNzIjoiOWIzNjdmN2QtNmRkMy00ZGNkIiwiZXhwIjo0MzMxMjY1OTM4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.6hR-fP769x368tTlBjmtIK0ZpHeJOE9Y4FynxAr48bI", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:26:21.044597", + "created_at": "2025-02-11T17:26:21.044597" + }, + { + "email": "otzumrrm266001@586vip.cn", + "password": "HdI@COBPCO9x", + "first_name": "Mfmwza", + "last_name": "Axmmui", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUIzNEM2OEc4Szc1NlM2SE5YMFIyIiwidGltZSI6IjE3MzkyNjYwNTUiLCJyYW5kb21uZXNzIjoiYmM0NWIyZTYtNjgxMS00OTE4IiwiZXhwIjo0MzMxMjY2MDU1LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.geHcjT0DzddtOpxILQTawKvc90_dqJG6c-_ZDS9ZVUw", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUIzNEM2OEc4Szc1NlM2SE5YMFIyIiwidGltZSI6IjE3MzkyNjYwNTUiLCJyYW5kb21uZXNzIjoiYmM0NWIyZTYtNjgxMS00OTE4IiwiZXhwIjo0MzMxMjY2MDU1LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.geHcjT0DzddtOpxILQTawKvc90_dqJG6c-_ZDS9ZVUw", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:28:13.605804", + "created_at": "2025-02-11T17:28:13.605804" + }, + { + "email": "nrnivqap266115@nosqli.com", + "password": "HdI@COBPCO9x", + "first_name": "Veqlgf", + "last_name": "Qwxxdc", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUVGQlk0RDRZV1paWTVSNUpFUjhYIiwidGltZSI6IjE3MzkyNjYxNjYiLCJyYW5kb21uZXNzIjoiMjM4YzM5MGEtY2U4Mi00ZDQ2IiwiZXhwIjo0MzMxMjY2MTY2LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.fPCSlBZR8ZoKKfij6NYRz0lDvIFm3Rrs2KZMGJIyWAE", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUVGQlk0RDRZV1paWTVSNUpFUjhYIiwidGltZSI6IjE3MzkyNjYxNjYiLCJyYW5kb21uZXNzIjoiMjM4YzM5MGEtY2U4Mi00ZDQ2IiwiZXhwIjo0MzMxMjY2MTY2LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.fPCSlBZR8ZoKKfij6NYRz0lDvIFm3Rrs2KZMGJIyWAE", + "machine_id": "", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", + "registration_time": "2025-02-11T17:30:05.829510", + "created_at": "2025-02-11T17:30:05.829510" } ] } \ No newline at end of file diff --git a/interactive/account_switcher.py b/interactive/account_switcher.py index e0520c2..bd1d8d5 100644 --- a/interactive/account_switcher.py +++ b/interactive/account_switcher.py @@ -5,9 +5,13 @@ 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: """获取硬件唯一标识""" @@ -32,35 +36,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 +44,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 +360,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: diff --git a/interactive/cursor_auth_manager.py b/interactive/cursor_auth_manager.py new file mode 100644 index 0000000..15c5218 --- /dev/null +++ b/interactive/cursor_auth_manager.py @@ -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 \ No newline at end of file diff --git a/interactive/gui/main_window.py b/interactive/gui/main_window.py index fcf068d..823ed24 100644 --- a/interactive/gui/main_window.py +++ b/interactive/gui/main_window.py @@ -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() \ No newline at end of file + messagebox.showerror("错误", f"刷新失败: {str(e)}") \ No newline at end of file diff --git a/interactive/main.py b/interactive/main.py index 236612f..ca8a279 100644 --- a/interactive/main.py +++ b/interactive/main.py @@ -1,37 +1,61 @@ 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" + + # 同时输出到文件和控制台 + handlers = [logging.FileHandler(log_file, encoding="utf-8")] + if sys.stdout is not None: + handlers.append(logging.StreamHandler(sys.stdout)) + + logging.basicConfig( + level=logging.INFO, # 使用INFO级别以显示更多信息 + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=handlers + ) + logging.info("日志系统初始化成功") + except Exception as e: + print(f"设置日志系统失败: {str(e)}") def main(): """主函数""" try: + print("正在启动程序...") 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) + print(error_msg) # 直接打印错误信息 + + # 保持窗口不关闭 + input("按回车键退出...") if __name__ == "__main__": main() \ No newline at end of file diff --git a/interactive/requirements.txt b/interactive/requirements.txt index 34d8ed1..8b52759 100644 --- a/interactive/requirements.txt +++ b/interactive/requirements.txt @@ -1,2 +1,4 @@ requests==2.31.0 -pyinstaller==6.3.0 \ No newline at end of file +pyinstaller==6.3.0 +pillow==10.2.0 # 用于处理图标 +setuptools==65.5.1 # 解决pkg_resources.extern问题 \ No newline at end of file diff --git a/interactive/utils/cursor_registry.py b/interactive/utils/cursor_registry.py new file mode 100644 index 0000000..7966fec --- /dev/null +++ b/interactive/utils/cursor_registry.py @@ -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 \ No newline at end of file diff --git a/test_api_response.py b/test_api_response.py index efc65ea..a56f1b6 100644 --- a/test_api_response.py +++ b/test_api_response.py @@ -18,7 +18,7 @@ def test_member_status(machine_id: str): } print("\n发送请求...") - response = requests.post(endpoint, json=data, headers=headers, timeout=10) + response = requests.post(endpoint, json=data, headers=headers, timeout=10, verify=False) print(f"状态码: {response.status_code}") print("\n响应数据:") @@ -43,6 +43,43 @@ def test_member_status(machine_id: str): except Exception as e: print(f"\n请求失败: {str(e)}") +def test_get_unused_account(machine_id: str): + """测试获取未使用账号接口""" + print(f"\n=== 测试获取未使用账号 ===") + print(f"设备码: {machine_id}") + + try: + # 构造请求 + endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused" + data = { + "machine_id": machine_id + } + headers = { + "Content-Type": "application/json" + } + + print("\n发送请求...") + response = requests.post(endpoint, json=data, headers=headers, timeout=30, verify=False) + print(f"状态码: {response.status_code}") + + print("\n响应数据:") + response_data = response.json() + print(json.dumps(response_data, indent=2, ensure_ascii=False)) + + if response_data.get("code") == 200: + account_data = response_data.get("data", {}) + print("\n账号信息:") + print(f"邮箱: {account_data.get('email', '')}") + print(f"到期时间: {account_data.get('expire_time', '')}") + print(f"剩余天数: {account_data.get('days_left', 0)}") + + except Exception as e: + print(f"\n请求失败: {str(e)}") + if __name__ == "__main__": + # 禁用SSL警告 + requests.packages.urllib3.disable_warnings() + machine_id = "b2c32bdeff8d3f0d33dc88f2aadea6bc" - test_member_status(machine_id) \ No newline at end of file + test_member_status(machine_id) + test_get_unused_account(machine_id) \ No newline at end of file