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

224 lines
6.8 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.
"""
Trae.ai 账号切换 —— 写入本地配置文件Trae 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_trae_config_dir() -> str:
"""获取 Trae 配置目录路径"""
system = platform.system()
if system == "Darwin": # macOS
home = os.path.expanduser("~")
return os.path.join(home, "Library", "Application Support", "Trae", "User")
elif system == "Windows":
appdata = os.environ.get("APPDATA", "")
return os.path.join(appdata, "Trae", "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, "Trae", "User")
def _get_trae_storage_path() -> str:
"""获取 Trae storage.json 路径"""
config_dir = _get_trae_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_trae_account(
token: str,
user_id: str = "",
email: str = "",
region: str = ""
) -> Tuple[bool, str]:
"""
切换 Trae 账号(写入 storage.json需要重启 Trae
Args:
token: Trae API token
user_id: 用户 ID
email: 邮箱
region: 区域
Returns:
(success, message)
"""
try:
storage_path = _get_trae_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["trae.token"] = token
if user_id:
storage_data["trae.userId"] = user_id
if email:
storage_data["trae.email"] = email
if region:
storage_data["trae.region"] = region
# 原子写入
content = json.dumps(storage_data, indent=2, ensure_ascii=False)
_atomic_write(storage_path, content)
return True, "切换成功,请重启 Trae IDE 使新账号生效"
except Exception as e:
logger.error(f"Trae 账号切换失败: {e}")
return False, f"切换失败: {str(e)}"
def restart_trae_ide() -> Tuple[bool, str]:
"""关闭并重启 Trae IDE"""
system = platform.system()
try:
if system == "Darwin": # macOS
# 关闭 Trae
subprocess.run(
["osascript", "-e", 'quit app "Trae"'],
capture_output=True,
timeout=5
)
time.sleep(2.0)
# 启动 Trae
trae_app = "/Applications/Trae.app"
if os.path.exists(trae_app):
subprocess.Popen(["open", "-a", "Trae"])
return True, "Trae IDE 已重启"
return True, "已关闭 Trae IDE未找到应用路径请手动启动"
elif system == "Windows":
# 关闭 Trae
subprocess.run(
["taskkill", "/IM", "Trae.exe", "/F"],
capture_output=True,
creationflags=0x08000000, # CREATE_NO_WINDOW
timeout=5
)
time.sleep(1.5)
# 启动 Trae
localappdata = os.environ.get("LOCALAPPDATA", "")
trae_exe = os.path.join(localappdata, "Programs", "Trae", "Trae.exe")
if os.path.exists(trae_exe):
subprocess.Popen([trae_exe])
return True, "Trae IDE 已重启"
return True, "已关闭 Trae IDE未找到应用路径请手动启动"
else: # Linux
# 关闭 Trae
subprocess.run(["pkill", "-f", "trae"], capture_output=True, timeout=5)
time.sleep(1.5)
# 启动 Trae
for path in ["/usr/bin/trae", os.path.expanduser("~/.local/bin/trae")]:
if os.path.exists(path):
subprocess.Popen([path])
return True, "Trae IDE 已重启"
try:
subprocess.Popen(["trae"])
return True, "Trae IDE 已重启"
except FileNotFoundError:
return True, "已关闭 Trae IDE未找到应用路径请手动启动"
except Exception as e:
logger.error(f"Trae IDE 重启失败: {e}")
return False, f"重启失败: {str(e)}"
def read_current_trae_account() -> dict | None:
"""读取当前 Trae IDE 正在使用的账号信息"""
storage_path = _get_trae_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("trae.token")
if token:
return {
"token": token,
"user_id": storage_data.get("trae.userId", ""),
"email": storage_data.get("trae.email", ""),
"region": storage_data.get("trae.region", "")
}
return None
except Exception as e:
logger.error(f"读取 Trae 配置失败: {e}")
return None
def get_trae_user_info(token: str) -> dict | None:
"""通过 token 获取用户信息"""
from curl_cffi import requests as curl_req
try:
r = curl_req.post(
"https://api-sg-central.trae.ai/cloudide/api/v3/common/GetUserToken",
headers={
"Authorization": f"Cloud-IDE-JWT {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"
},
json={},
impersonate="chrome124",
timeout=15,
)
if r.status_code == 200:
return r.json()
return None
except Exception as e:
logger.error(f"获取 Trae 用户信息失败: {e}")
return None