1224 条记录
52 私有链接
52 私有链接
这个脚本旨在帮助用户更方便地体验 Cursor Pro,因为它能自动重置设备ID并写入有效的token,解决了手动操作的复杂性。脚本会从云端token池随机获取token,并更新本地Cursor的认证信息。使用前需安装依赖,运行脚本会自动退出Cursor程序,请确保保存工作。脚本运行后,重新打开Cursor Pro即可。
import json
import logging
import os
import platform
import sqlite3
import subprocess
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
import psutil
import requests
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# 常量配置
class Config:
"""配置常量类"""
API_URL = "https://cursor.ccopilot.org/api/get_next_token.php"
ACCESS_CODE = ""
PROCESS_TIMEOUT = 5
CURSOR_PROCESS_NAMES = ['cursor.exe', 'cursor']
DB_KEYS = {
'email': 'cursorAuth/cachedEmail',
'access_token': 'cursorAuth/accessToken',
'refresh_token': 'cursorAuth/refreshToken'
}
@dataclass
class TokenData:
"""Token数据类"""
mac_machine_id: str
machine_id: str
dev_device_id: str
email: str
token: str
@classmethod
def from_dict(cls, data: Dict[str, str]) -> 'TokenData':
"""从字典创建TokenData实例"""
return cls(
mac_machine_id=data['mac_machine_id'],
machine_id=data['machine_id'],
dev_device_id=data['dev_device_id'],
email=data['email'],
token=data['token']
)
class FilePathManager:
"""文件路径管理器"""
@staticmethod
def get_storage_path() -> Path:
"""获取storage.json文件路径"""
system = platform.system()
if system == "Windows":
return Path(os.getenv('APPDATA')) / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
elif system == "Darwin":
return Path.home() / 'Library' / 'Application Support' / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
elif system == "Linux":
return Path.home() / '.config' / 'Cursor' / 'User' / 'globalStorage' / 'storage.json'
raise OSError(f"不支持的操作系统: {system}")
@staticmethod
def get_db_path() -> Path:
"""获取数据库文件路径"""
if os.name == 'nt':
return Path(os.getenv('APPDATA')) / 'Cursor' / 'User' / 'globalStorage' / 'state.vscdb'
return Path.home() / 'Library' / 'Application Support' / 'Cursor' / 'User' / 'globalStorage' / 'state.vscdb'
class FilePermissionManager:
"""文件权限管理器"""
@staticmethod
def make_file_writable(file_path: Union[str, Path]) -> None:
"""修改文件权限为可写"""
file_path = Path(file_path)
if platform.system() == "Windows":
subprocess.run(['attrib', '-R', str(file_path)], check=True)
else:
os.chmod(file_path, 0o666)
@staticmethod
def make_file_readonly(file_path: Union[str, Path]) -> None:
"""修改文件权限为只读"""
file_path = Path(file_path)
if platform.system() == "Windows":
subprocess.run(['attrib', '+R', str(file_path)], check=True)
else:
os.chmod(file_path, 0o444)
class CursorAuthManager:
"""Cursor认证信息管理器"""
def __init__(self):
self.db_path = FilePathManager.get_db_path()
def update_auth(self, email: Optional[str] = None,
access_token: Optional[str] = None,
refresh_token: Optional[str] = None) -> bool:
"""更新或插入Cursor的认证信息"""
updates: List[Tuple[str, str]] = []
if email is not None:
updates.append((Config.DB_KEYS['email'], email))
if access_token is not None:
updates.append((Config.DB_KEYS['access_token'], access_token))
if refresh_token is not None:
updates.append((Config.DB_KEYS['refresh_token'], refresh_token))
if not updates:
logger.info("没有提供任何要更新的值")
return False
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
for key, value in updates:
cursor.execute("SELECT 1 FROM itemTable WHERE key = ?", (key,))
exists = cursor.fetchone() is not None
if exists:
cursor.execute("UPDATE itemTable SET value = ? WHERE key = ?", (value, key))
else:
cursor.execute("INSERT INTO itemTable (key, value) VALUES (?, ?)", (key, value))
logger.info(f"成功{'更新' if exists else '插入'} {key.split('/')[-1]}")
return True
except sqlite3.Error as e:
logger.error(f"数据库错误: {e}")
return False
except Exception as e:
logger.error(f"发生错误: {e}")
return False
class CursorManager:
"""Cursor管理器"""
@staticmethod
def reset_cursor_id(token_data: TokenData) -> bool:
"""重置Cursor ID"""
storage_path = FilePathManager.get_storage_path()
if not storage_path.exists():
logger.warning(f"未找到文件: {storage_path}")
return False
try:
FilePermissionManager.make_file_writable(storage_path)
data = json.loads(storage_path.read_text())
data.update({
"telemetry.macMachineId": token_data.mac_machine_id,
"telemetry.machineId": token_data.machine_id,
"telemetry.devDeviceId": token_data.dev_device_id
})
storage_path.write_text(json.dumps(data, indent=4))
FilePermissionManager.make_file_readonly(storage_path)
logger.info("storage.json文件已成功修改")
return True
except Exception as e:
logger.error(f"重置Cursor ID时发生错误: {e}")
return False
@staticmethod
def exit_cursor() -> bool:
"""安全退出Cursor进程"""
try:
logger.info("开始退出Cursor...")
cursor_processes = [
proc for proc in psutil.process_iter(['pid', 'name'])
if proc.info['name'].lower() in Config.CURSOR_PROCESS_NAMES
]
if not cursor_processes:
logger.info("未发现运行中的 Cursor 进程")
return True
# 温和地请求进程终止
for proc in cursor_processes:
try:
if proc.is_running():
proc.terminate()
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
# 等待进程终止
start_time = time.time()
while time.time() - start_time < Config.PROCESS_TIMEOUT:
still_running = [p for p in cursor_processes if p.is_running()]
if not still_running:
logger.info("所有 Cursor 进程已正常关闭")
return True
time.sleep(0.5)
if still_running := [p for p in cursor_processes if p.is_running()]:
process_list = ", ".join(str(p.pid) for p in still_running)
logger.warning(f"以下进程未能在规定时间内关闭: {process_list}")
return False
return True
except Exception as e:
logger.error(f"关闭 Cursor 进程时发生错误: {e}")
return False
class TokenManager:
"""Token管理器"""
@staticmethod
def fetch_token_data() -> Optional[TokenData]:
"""获取Token数据"""
logger.info("正在获取Token数据...")
try:
response = requests.get(f"{Config.API_URL}?accessCode={Config.ACCESS_CODE}")
data = response.json()
if data.get("code") == 0 and (token_data := data.get("data")):
logger.info("成功获取Token数据")
return TokenData.from_dict(token_data)
logger.warning(f"获取Token失败: {data.get('message', '未知错误')}")
return None
except Exception as e:
logger.error(f"获取Token数据时发生错误: {e}")
return None
@staticmethod
def update_token(token_data: TokenData) -> bool:
"""更新Cursor的token信息"""
try:
# 更新机器ID
if not CursorManager.reset_cursor_id(token_data):
return False
# 更新认证信息
auth_manager = CursorAuthManager()
if not auth_manager.update_auth(email=token_data.email, access_token=token_data.token, refresh_token=token_data.token):
logger.error("更新Token时发生错误")
return False
logger.info(f"成功更新Cursor认证信息! 邮箱: {token_data.email}")
return True
except Exception as e:
logger.error(f"更新Token时发生错误: {e}")
return False
def main() -> None:
"""主函数"""
if token_data := TokenManager.fetch_token_data():
logger.info("即将退出 Cursor,请确保所有工作已保存。")
logger.info("按任意键继续...")
input("")
if CursorManager.exit_cursor():
TokenManager.update_token(token_data)
if __name__ == "__main__":
main()