Files
any-auto-register/platforms/cursor/switch.py
2026-03-22 21:01:31 +08:00

204 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Cursor 账号切换 —— 写入本地配置文件Cursor IDE 自动识别
支持 macOS / Windows / Linux
"""
import os
import json
import logging
import tempfile
import platform
import subprocess
import time
from typing import Tuple
logger = logging.getLogger(__name__)
def _get_cursor_config_dir() -> str:
"""获取 Cursor 配置目录路径"""
system = platform.system()
if system == "Darwin": # macOS
home = os.path.expanduser("~")
return os.path.join(home, "Library", "Application Support", "Cursor", "User")
elif system == "Windows":
appdata = os.environ.get("APPDATA", "")
return os.path.join(appdata, "Cursor", "User")
else: # Linux
home = os.path.expanduser("~")
config_home = os.environ.get("XDG_CONFIG_HOME", os.path.join(home, ".config"))
return os.path.join(config_home, "Cursor", "User")
def _get_cursor_storage_path() -> str:
"""获取 Cursor storage.json 路径"""
config_dir = _get_cursor_config_dir()
return os.path.join(config_dir, "globalStorage", "storage.json")
def _atomic_write(filepath: str, content: str):
"""原子写入:先写临时文件,再 rename"""
dir_path = os.path.dirname(filepath)
os.makedirs(dir_path, exist_ok=True)
fd, tmp_path = tempfile.mkstemp(dir=dir_path, suffix=".tmp")
try:
os.write(fd, content.encode("utf-8"))
os.close(fd)
os.replace(tmp_path, filepath)
except Exception:
try:
os.close(fd)
except:
pass
if os.path.exists(tmp_path):
os.unlink(tmp_path)
raise
def switch_cursor_account(token: str) -> Tuple[bool, str]:
"""
切换 Cursor 账号(写入 storage.json需要重启 Cursor
Args:
token: WorkosCursorSessionToken
Returns:
(success, message)
"""
try:
storage_path = _get_cursor_storage_path()
# 读取现有配置
storage_data = {}
if os.path.exists(storage_path):
try:
with open(storage_path, "r", encoding="utf-8") as f:
storage_data = json.load(f)
except Exception as e:
logger.warning(f"读取现有配置失败,将创建新配置: {e}")
# 更新 token
storage_data["workos.sessionToken"] = token
# 原子写入
content = json.dumps(storage_data, indent=2, ensure_ascii=False)
_atomic_write(storage_path, content)
return True, "切换成功,请重启 Cursor IDE 使新账号生效"
except Exception as e:
logger.error(f"Cursor 账号切换失败: {e}")
return False, f"切换失败: {str(e)}"
def restart_cursor_ide() -> Tuple[bool, str]:
"""关闭并重启 Cursor IDE"""
system = platform.system()
try:
if system == "Darwin": # macOS
# 关闭 Cursor
subprocess.run(
["osascript", "-e", 'quit app "Cursor"'],
capture_output=True,
timeout=5
)
time.sleep(2.0)
# 启动 Cursor
cursor_app = "/Applications/Cursor.app"
if os.path.exists(cursor_app):
subprocess.Popen(["open", "-a", "Cursor"])
return True, "Cursor IDE 已重启"
return True, "已关闭 Cursor IDE未找到应用路径请手动启动"
elif system == "Windows":
# 关闭 Cursor
subprocess.run(
["taskkill", "/IM", "Cursor.exe", "/F"],
capture_output=True,
creationflags=0x08000000, # CREATE_NO_WINDOW
timeout=5
)
time.sleep(1.5)
# 启动 Cursor
localappdata = os.environ.get("LOCALAPPDATA", "")
cursor_exe = os.path.join(localappdata, "Programs", "Cursor", "Cursor.exe")
if os.path.exists(cursor_exe):
subprocess.Popen([cursor_exe])
return True, "Cursor IDE 已重启"
return True, "已关闭 Cursor IDE未找到应用路径请手动启动"
else: # Linux
# 关闭 Cursor
subprocess.run(["pkill", "-f", "cursor"], capture_output=True, timeout=5)
time.sleep(1.5)
# 启动 Cursor
for path in ["/usr/bin/cursor", os.path.expanduser("~/.local/bin/cursor")]:
if os.path.exists(path):
subprocess.Popen([path])
return True, "Cursor IDE 已重启"
try:
subprocess.Popen(["cursor"])
return True, "Cursor IDE 已重启"
except FileNotFoundError:
return True, "已关闭 Cursor IDE未找到应用路径请手动启动"
except Exception as e:
logger.error(f"Cursor IDE 重启失败: {e}")
return False, f"重启失败: {str(e)}"
def read_current_cursor_account() -> dict | None:
"""读取当前 Cursor IDE 正在使用的账号 token"""
storage_path = _get_cursor_storage_path()
if not os.path.exists(storage_path):
return None
try:
with open(storage_path, "r", encoding="utf-8") as f:
storage_data = json.load(f)
token = storage_data.get("workos.sessionToken")
if token:
return {"token": token}
return None
except Exception as e:
logger.error(f"读取 Cursor 配置失败: {e}")
return None
def get_cursor_user_info(token: str) -> dict | None:
"""通过 token 获取用户信息"""
from curl_cffi import requests as curl_req
try:
r = curl_req.get(
"https://cursor.com/api/auth/me",
headers={
"Cookie": f"WorkosCursorSessionToken={token}",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/145.0.0.0 Safari/537.36"
},
impersonate="chrome124",
timeout=15,
)
if r.status_code == 200:
return r.json()
return None
except Exception as e:
logger.error(f"获取 Cursor 用户信息失败: {e}")
return None