mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2026-01-04 01:31:36 +03:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4485cc5571 | ||
|
|
df58b2e4ab | ||
|
|
5f380ebe5e | ||
|
|
c97bfd1475 | ||
|
|
28cd662e83 | ||
|
|
1b6ba5eab8 | ||
|
|
3e3cd40e3f | ||
|
|
8f35f163c5 | ||
|
|
f6ffb18427 | ||
|
|
b6bf62f841 | ||
|
|
1c1174fa6c | ||
|
|
4587fd9373 | ||
|
|
51fcf83ebb | ||
|
|
5cc7630ce6 | ||
|
|
bd9e10056f | ||
|
|
36f5356b4a | ||
|
|
18fc0532fd | ||
|
|
7405c61cc9 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v1.8.01
|
||||||
|
1. Add: Cursor Account Info | 增加 Cursor 賬號信息
|
||||||
|
2. Fix: Disable Auto Update | 修復禁用自動更新
|
||||||
|
3. Add: 0.48.x Version Support | 增加 0.48.x 版本支持
|
||||||
|
4. Revert: Totally Reser Cursor to Beta | 恢復完全重置 Cursor 到 Beta
|
||||||
|
5. Reopen: Totally Reset Cursor | 重新開啟完全重置 Cursor
|
||||||
|
6. Fix: Logo.py Center | 修復 Logo.py 居中
|
||||||
|
7. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
|
||||||
|
8. Fix: Some Issues | 修復一些問題
|
||||||
|
|
||||||
## v1.7.18
|
## v1.7.18
|
||||||
1. Fix: No Write Permission | 修復沒有寫入權限
|
1. Fix: No Write Permission | 修復沒有寫入權限
|
||||||
2. Fix: Improve Linux path detection and config handling | 修正 linux 路徑和config寫入讀取
|
2. Fix: Improve Linux path detection and config handling | 修正 linux 路徑和config寫入讀取
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -15,8 +15,15 @@
|
|||||||
</p>
|
</p>
|
||||||
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
|
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
|
||||||
|
|
||||||
This is a tool to automatically register, support Windows and macOS systems, complete Auth verification, and reset
|
This tool register accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes,reset and wipe Cursor data and hardware info.
|
||||||
Cursor's configuration.
|
|
||||||
|
Supports Windows, macOS and Linux.
|
||||||
|
|
||||||
|
For optimal performance, run with privileges and always stay up to date.
|
||||||
|
|
||||||
|
Always clean your browser cache and cookies. If possible, user a VPN to create new accounts.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ def setup_config(translator=None):
|
|||||||
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
|
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
|
||||||
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
|
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
|
||||||
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
|
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
|
||||||
'updater_path': os.path.join(localappdata, "cursor-updater")
|
'updater_path': os.path.join(localappdata, "cursor-updater"),
|
||||||
|
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml")
|
||||||
}
|
}
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
default_config['MacPaths'] = {
|
default_config['MacPaths'] = {
|
||||||
@@ -57,7 +58,8 @@ def setup_config(translator=None):
|
|||||||
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
|
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
|
||||||
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
|
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
|
||||||
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
|
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
|
||||||
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater")
|
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||||
|
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||||
}
|
}
|
||||||
elif sys.platform == "linux":
|
elif sys.platform == "linux":
|
||||||
sudo_user = os.environ.get('SUDO_USER')
|
sudo_user = os.environ.get('SUDO_USER')
|
||||||
@@ -68,7 +70,8 @@ def setup_config(translator=None):
|
|||||||
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/state.vscdb")),
|
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/state.vscdb")),
|
||||||
'machine_id_path': os.path.expanduser("~/.config/cursor/machineid"),
|
'machine_id_path': os.path.expanduser("~/.config/cursor/machineid"),
|
||||||
'cursor_path': get_linux_cursor_path(),
|
'cursor_path': get_linux_cursor_path(),
|
||||||
'updater_path': os.path.expanduser("~/.config/cursor-updater")
|
'updater_path': os.path.expanduser("~/.config/cursor-updater"),
|
||||||
|
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Read existing configuration and merge
|
# Read existing configuration and merge
|
||||||
|
|||||||
473
cursor_acc_info.py
Normal file
473
cursor_acc_info.py
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import sqlite3
|
||||||
|
from typing import Dict, Optional
|
||||||
|
import platform
|
||||||
|
from colorama import Fore, Style, init
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Initialize colorama
|
||||||
|
init()
|
||||||
|
|
||||||
|
# Setup logger
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Define emoji constants
|
||||||
|
EMOJI = {
|
||||||
|
"USER": "👤",
|
||||||
|
"USAGE": "📊",
|
||||||
|
"PREMIUM": "⭐",
|
||||||
|
"BASIC": "📝",
|
||||||
|
"SUBSCRIPTION": "💳",
|
||||||
|
"INFO": "ℹ️",
|
||||||
|
"ERROR": "❌",
|
||||||
|
"SUCCESS": "✅",
|
||||||
|
"WARNING": "⚠️",
|
||||||
|
"TIME": "🕒"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Config"""
|
||||||
|
NAME_LOWER = "cursor"
|
||||||
|
NAME_CAPITALIZE = "Cursor"
|
||||||
|
BASE_HEADERS = {
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsageManager:
|
||||||
|
"""Usage Manager"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_proxy():
|
||||||
|
"""get proxy"""
|
||||||
|
# from config import get_config
|
||||||
|
proxy = os.environ.get("HTTP_PROXY") or os.environ.get("HTTPS_PROXY")
|
||||||
|
if proxy:
|
||||||
|
return {"http": proxy, "https": proxy}
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_usage(token: str) -> Optional[Dict]:
|
||||||
|
"""get usage"""
|
||||||
|
url = f"https://www.{Config.NAME_LOWER}.com/api/usage"
|
||||||
|
headers = Config.BASE_HEADERS.copy()
|
||||||
|
headers.update({"Cookie": f"Workos{Config.NAME_CAPITALIZE}SessionToken=user_01OOOOOOOOOOOOOOOOOOOOOOOO%3A%3A{token}"})
|
||||||
|
try:
|
||||||
|
proxies = UsageManager.get_proxy()
|
||||||
|
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# 获取 GPT-4 使用量和限制
|
||||||
|
gpt4_data = data.get("gpt-4", {})
|
||||||
|
premium_usage = gpt4_data.get("numRequestsTotal", 0)
|
||||||
|
max_premium_usage = gpt4_data.get("maxRequestUsage", 999)
|
||||||
|
|
||||||
|
# 获取 GPT-3.5 使用量,但将限制设为 "No Limit"
|
||||||
|
gpt35_data = data.get("gpt-3.5-turbo", {})
|
||||||
|
basic_usage = gpt35_data.get("numRequestsTotal", 0)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'premium_usage': premium_usage,
|
||||||
|
'max_premium_usage': max_premium_usage,
|
||||||
|
'basic_usage': basic_usage,
|
||||||
|
'max_basic_usage': "No Limit" # 将 GPT-3.5 的限制设为 "No Limit"
|
||||||
|
}
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error(f"获取使用量失败: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_stripe_profile(token: str) -> Optional[Dict]:
|
||||||
|
"""get user subscription info"""
|
||||||
|
url = f"https://api2.{Config.NAME_LOWER}.sh/auth/full_stripe_profile"
|
||||||
|
headers = Config.BASE_HEADERS.copy()
|
||||||
|
headers.update({"Authorization": f"Bearer {token}"})
|
||||||
|
try:
|
||||||
|
proxies = UsageManager.get_proxy()
|
||||||
|
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error(f"获取订阅信息失败: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_token_from_config():
|
||||||
|
"""get path info from config"""
|
||||||
|
try:
|
||||||
|
from config import get_config
|
||||||
|
config = get_config()
|
||||||
|
if not config:
|
||||||
|
return None
|
||||||
|
|
||||||
|
system = platform.system()
|
||||||
|
if system == "Windows" and config.has_section('WindowsPaths'):
|
||||||
|
return {
|
||||||
|
'storage_path': config.get('WindowsPaths', 'storage_path'),
|
||||||
|
'sqlite_path': config.get('WindowsPaths', 'sqlite_path'),
|
||||||
|
'session_path': os.path.join(os.getenv("APPDATA"), "Cursor", "Session Storage")
|
||||||
|
}
|
||||||
|
elif system == "Darwin" and config.has_section('MacPaths'): # macOS
|
||||||
|
return {
|
||||||
|
'storage_path': config.get('MacPaths', 'storage_path'),
|
||||||
|
'sqlite_path': config.get('MacPaths', 'sqlite_path'),
|
||||||
|
'session_path': os.path.expanduser("~/Library/Application Support/Cursor/Session Storage")
|
||||||
|
}
|
||||||
|
elif system == "Linux" and config.has_section('LinuxPaths'):
|
||||||
|
return {
|
||||||
|
'storage_path': config.get('LinuxPaths', 'storage_path'),
|
||||||
|
'sqlite_path': config.get('LinuxPaths', 'sqlite_path'),
|
||||||
|
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取配置路径失败: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_token_from_storage(storage_path):
|
||||||
|
"""get token from storage.json"""
|
||||||
|
if not os.path.exists(storage_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(storage_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
# try to get accessToken
|
||||||
|
if 'cursorAuth/accessToken' in data:
|
||||||
|
return data['cursorAuth/accessToken']
|
||||||
|
|
||||||
|
# try other possible keys
|
||||||
|
for key in data:
|
||||||
|
if 'token' in key.lower() and isinstance(data[key], str) and len(data[key]) > 20:
|
||||||
|
return data[key]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get token from storage.json failed: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_token_from_sqlite(sqlite_path):
|
||||||
|
"""get token from sqlite"""
|
||||||
|
if not os.path.exists(sqlite_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(sqlite_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%token%'")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
try:
|
||||||
|
value = row[0]
|
||||||
|
if isinstance(value, str) and len(value) > 20:
|
||||||
|
return value
|
||||||
|
# try to parse JSON
|
||||||
|
data = json.loads(value)
|
||||||
|
if isinstance(data, dict) and 'token' in data:
|
||||||
|
return data['token']
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get token from sqlite failed: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_token_from_session(session_path):
|
||||||
|
"""get token from session"""
|
||||||
|
if not os.path.exists(session_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# try to find all possible session files
|
||||||
|
for file in os.listdir(session_path):
|
||||||
|
if file.endswith('.log'):
|
||||||
|
file_path = os.path.join(session_path, file)
|
||||||
|
try:
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
content = f.read().decode('utf-8', errors='ignore')
|
||||||
|
# find token pattern
|
||||||
|
token_match = re.search(r'"token":"([^"]+)"', content)
|
||||||
|
if token_match:
|
||||||
|
return token_match.group(1)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get token from session failed: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_token():
|
||||||
|
"""get Cursor token"""
|
||||||
|
# get path from config
|
||||||
|
paths = get_token_from_config()
|
||||||
|
if not paths:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# try to get token from different locations
|
||||||
|
token = get_token_from_storage(paths['storage_path'])
|
||||||
|
if token:
|
||||||
|
return token
|
||||||
|
|
||||||
|
token = get_token_from_sqlite(paths['sqlite_path'])
|
||||||
|
if token:
|
||||||
|
return token
|
||||||
|
|
||||||
|
token = get_token_from_session(paths['session_path'])
|
||||||
|
if token:
|
||||||
|
return token
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def format_subscription_type(subscription_data: Dict) -> str:
|
||||||
|
"""format subscription type"""
|
||||||
|
if not subscription_data:
|
||||||
|
return "Free"
|
||||||
|
|
||||||
|
# handle new API response format
|
||||||
|
if "membershipType" in subscription_data:
|
||||||
|
membership_type = subscription_data.get("membershipType", "").lower()
|
||||||
|
subscription_status = subscription_data.get("subscriptionStatus", "").lower()
|
||||||
|
|
||||||
|
if subscription_status == "active":
|
||||||
|
if membership_type == "pro":
|
||||||
|
return "Pro"
|
||||||
|
elif membership_type == "free_trial":
|
||||||
|
return "Free Trial"
|
||||||
|
elif membership_type == "pro_trial":
|
||||||
|
return "Pro Trial"
|
||||||
|
elif membership_type == "team":
|
||||||
|
return "Team"
|
||||||
|
elif membership_type == "enterprise":
|
||||||
|
return "Enterprise"
|
||||||
|
elif membership_type:
|
||||||
|
return membership_type.capitalize()
|
||||||
|
else:
|
||||||
|
return "Active Subscription"
|
||||||
|
elif subscription_status:
|
||||||
|
return f"{membership_type.capitalize()} ({subscription_status})"
|
||||||
|
|
||||||
|
# compatible with old API response format
|
||||||
|
subscription = subscription_data.get("subscription")
|
||||||
|
if subscription:
|
||||||
|
plan = subscription.get("plan", {}).get("nickname", "Unknown")
|
||||||
|
status = subscription.get("status", "unknown")
|
||||||
|
|
||||||
|
if status == "active":
|
||||||
|
if "pro" in plan.lower():
|
||||||
|
return "Pro"
|
||||||
|
elif "pro_trial" in plan.lower():
|
||||||
|
return "Pro Trial"
|
||||||
|
elif "free_trial" in plan.lower():
|
||||||
|
return "Free Trial"
|
||||||
|
elif "team" in plan.lower():
|
||||||
|
return "Team"
|
||||||
|
elif "enterprise" in plan.lower():
|
||||||
|
return "Enterprise"
|
||||||
|
else:
|
||||||
|
return plan
|
||||||
|
else:
|
||||||
|
return f"{plan} ({status})"
|
||||||
|
|
||||||
|
return "Free"
|
||||||
|
|
||||||
|
def get_email_from_storage(storage_path):
|
||||||
|
"""get email from storage.json"""
|
||||||
|
if not os.path.exists(storage_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(storage_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
# try to get email
|
||||||
|
if 'cursorAuth/cachedEmail' in data:
|
||||||
|
return data['cursorAuth/cachedEmail']
|
||||||
|
|
||||||
|
# try other possible keys
|
||||||
|
for key in data:
|
||||||
|
if 'email' in key.lower() and isinstance(data[key], str) and '@' in data[key]:
|
||||||
|
return data[key]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get email from storage.json failed: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_email_from_sqlite(sqlite_path):
|
||||||
|
"""get email from sqlite"""
|
||||||
|
if not os.path.exists(sqlite_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(sqlite_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
# try to query records containing email
|
||||||
|
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%email%' OR key LIKE '%cursorAuth%'")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
try:
|
||||||
|
value = row[0]
|
||||||
|
# if it's a string and contains @, it might be an email
|
||||||
|
if isinstance(value, str) and '@' in value:
|
||||||
|
return value
|
||||||
|
|
||||||
|
# try to parse JSON
|
||||||
|
try:
|
||||||
|
data = json.loads(value)
|
||||||
|
if isinstance(data, dict):
|
||||||
|
# check if there's an email field
|
||||||
|
if 'email' in data:
|
||||||
|
return data['email']
|
||||||
|
# check if there's a cachedEmail field
|
||||||
|
if 'cachedEmail' in data:
|
||||||
|
return data['cachedEmail']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"get email from sqlite failed: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def display_account_info(translator=None):
|
||||||
|
"""display account info"""
|
||||||
|
print(f"\n{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['USER']} {translator.get('account_info.title') if translator else 'Cursor Account Information'}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# get token
|
||||||
|
token = get_token()
|
||||||
|
if not token:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.token_not_found') if translator else 'Token not found. Please login to Cursor first.'}{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# get path info
|
||||||
|
paths = get_token_from_config()
|
||||||
|
if not paths:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.config_not_found') if translator else 'Configuration not found.'}{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# get email info - try multiple sources
|
||||||
|
email = get_email_from_storage(paths['storage_path'])
|
||||||
|
|
||||||
|
# if not found in storage, try from sqlite
|
||||||
|
if not email:
|
||||||
|
email = get_email_from_sqlite(paths['sqlite_path'])
|
||||||
|
|
||||||
|
# get subscription info
|
||||||
|
subscription_info = UsageManager.get_stripe_profile(token)
|
||||||
|
|
||||||
|
# if not found in storage and sqlite, try from subscription info
|
||||||
|
if not email and subscription_info:
|
||||||
|
# try to get email from subscription info
|
||||||
|
if 'customer' in subscription_info and 'email' in subscription_info['customer']:
|
||||||
|
email = subscription_info['customer']['email']
|
||||||
|
|
||||||
|
# get usage info
|
||||||
|
usage_info = UsageManager.get_usage(token)
|
||||||
|
|
||||||
|
# display account info
|
||||||
|
if email:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# display subscription type
|
||||||
|
if subscription_info:
|
||||||
|
subscription_type = format_subscription_type(subscription_info)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# display remaining trial days
|
||||||
|
days_remaining = subscription_info.get("daysRemainingOnTrial")
|
||||||
|
if days_remaining is not None and days_remaining > 0:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['TIME']} {translator.get('account_info.trial_remaining') if translator else 'Remaining Pro Trial'}: {Fore.WHITE}{days_remaining} {translator.get('account_info.days') if translator else 'days'}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.subscription_not_found') if translator else 'Subscription information not found'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# display usage info
|
||||||
|
if usage_info:
|
||||||
|
print(f"\n{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# GPT-4 usage
|
||||||
|
premium_usage = usage_info.get('premium_usage', 0)
|
||||||
|
max_premium_usage = usage_info.get('max_premium_usage', "No Limit")
|
||||||
|
|
||||||
|
# make sure the value is not None
|
||||||
|
if premium_usage is None:
|
||||||
|
premium_usage = 0
|
||||||
|
|
||||||
|
# handle "No Limit" case
|
||||||
|
if isinstance(max_premium_usage, str) and max_premium_usage == "No Limit":
|
||||||
|
premium_color = Fore.GREEN # when there is no limit, use green
|
||||||
|
premium_display = f"{premium_usage}/{max_premium_usage}"
|
||||||
|
else:
|
||||||
|
# calculate percentage when the value is a number
|
||||||
|
if max_premium_usage is None or max_premium_usage == 0:
|
||||||
|
max_premium_usage = 999
|
||||||
|
premium_percentage = 0
|
||||||
|
else:
|
||||||
|
premium_percentage = (premium_usage / max_premium_usage) * 100
|
||||||
|
|
||||||
|
# select color based on usage percentage
|
||||||
|
premium_color = Fore.GREEN
|
||||||
|
if premium_percentage > 70:
|
||||||
|
premium_color = Fore.YELLOW
|
||||||
|
if premium_percentage > 90:
|
||||||
|
premium_color = Fore.RED
|
||||||
|
|
||||||
|
premium_display = f"{premium_usage}/{max_premium_usage} ({premium_percentage:.1f}%)"
|
||||||
|
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['PREMIUM']} {translator.get('account_info.premium_usage') if translator else 'Fast Response'}: {premium_color}{premium_display}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Slow Response
|
||||||
|
basic_usage = usage_info.get('basic_usage', 0)
|
||||||
|
max_basic_usage = usage_info.get('max_basic_usage', "No Limit")
|
||||||
|
|
||||||
|
# make sure the value is not None
|
||||||
|
if basic_usage is None:
|
||||||
|
basic_usage = 0
|
||||||
|
|
||||||
|
# handle "No Limit" case
|
||||||
|
if isinstance(max_basic_usage, str) and max_basic_usage == "No Limit":
|
||||||
|
basic_color = Fore.GREEN # when there is no limit, use green
|
||||||
|
basic_display = f"{basic_usage}/{max_basic_usage}"
|
||||||
|
else:
|
||||||
|
# calculate percentage when the value is a number
|
||||||
|
if max_basic_usage is None or max_basic_usage == 0:
|
||||||
|
max_basic_usage = 999
|
||||||
|
basic_percentage = 0
|
||||||
|
else:
|
||||||
|
basic_percentage = (basic_usage / max_basic_usage) * 100
|
||||||
|
|
||||||
|
# select color based on usage percentage
|
||||||
|
basic_color = Fore.GREEN
|
||||||
|
if basic_percentage > 70:
|
||||||
|
basic_color = Fore.YELLOW
|
||||||
|
if basic_percentage > 90:
|
||||||
|
basic_color = Fore.RED
|
||||||
|
|
||||||
|
basic_display = f"{basic_usage}/{max_basic_usage} ({basic_percentage:.1f}%)"
|
||||||
|
|
||||||
|
print(f"{Fore.BLUE}{EMOJI['BASIC']} {translator.get('account_info.basic_usage') if translator else 'Slow Response'}: {basic_color}{basic_display}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.usage_not_found') if translator else 'Usage information not found'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def main(translator=None):
|
||||||
|
"""main function"""
|
||||||
|
try:
|
||||||
|
display_account_info(translator)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.error') if translator else 'Error'}: {str(e)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -31,10 +31,13 @@ class AutoUpdateDisabler:
|
|||||||
if config:
|
if config:
|
||||||
if self.system == "Windows":
|
if self.system == "Windows":
|
||||||
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
|
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
|
||||||
|
self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"))
|
||||||
elif self.system == "Darwin":
|
elif self.system == "Darwin":
|
||||||
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
|
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
|
||||||
|
self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml")
|
||||||
elif self.system == "Linux":
|
elif self.system == "Linux":
|
||||||
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
|
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
|
||||||
|
self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml"))
|
||||||
else:
|
else:
|
||||||
# If configuration loading fails, use default paths
|
# If configuration loading fails, use default paths
|
||||||
self.updater_paths = {
|
self.updater_paths = {
|
||||||
@@ -43,6 +46,13 @@ class AutoUpdateDisabler:
|
|||||||
"Linux": os.path.expanduser("~/.config/cursor-updater")
|
"Linux": os.path.expanduser("~/.config/cursor-updater")
|
||||||
}
|
}
|
||||||
self.updater_path = self.updater_paths.get(self.system)
|
self.updater_path = self.updater_paths.get(self.system)
|
||||||
|
|
||||||
|
self.update_yml_paths = {
|
||||||
|
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"),
|
||||||
|
"Darwin": "/Applications/Cursor.app/Contents/Resources/app-update.yml",
|
||||||
|
"Linux": os.path.expanduser("~/.config/cursor/resources/app-update.yml")
|
||||||
|
}
|
||||||
|
self.update_yml_path = self.update_yml_paths.get(self.system)
|
||||||
|
|
||||||
def _kill_cursor_processes(self):
|
def _kill_cursor_processes(self):
|
||||||
"""End all Cursor processes"""
|
"""End all Cursor processes"""
|
||||||
@@ -81,27 +91,70 @@ class AutoUpdateDisabler:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
|
||||||
|
# 即使删除失败,也返回 True,继续执行下一步
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _clear_update_yml_file(self):
|
||||||
|
"""Clear update.yml file"""
|
||||||
|
try:
|
||||||
|
update_yml_path = self.update_yml_path
|
||||||
|
if not update_yml_path:
|
||||||
|
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if os.path.exists(update_yml_path):
|
||||||
|
# 清空文件内容
|
||||||
|
with open(update_yml_path, 'w') as f:
|
||||||
|
f.write('')
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _create_blocking_file(self):
|
def _create_blocking_file(self):
|
||||||
"""Create blocking file"""
|
"""Create blocking files"""
|
||||||
try:
|
try:
|
||||||
|
# 检查 updater_path
|
||||||
updater_path = self.updater_path
|
updater_path = self.updater_path
|
||||||
if not updater_path:
|
if not updater_path:
|
||||||
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||||
|
|
||||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
# Create empty file
|
# 创建 updater_path 阻止文件
|
||||||
|
os.makedirs(os.path.dirname(updater_path), exist_ok=True)
|
||||||
open(updater_path, 'w').close()
|
open(updater_path, 'w').close()
|
||||||
|
|
||||||
# Set read-only attribute
|
# 设置 updater_path 为只读
|
||||||
if self.system == "Windows":
|
if self.system == "Windows":
|
||||||
os.system(f'attrib +r "{updater_path}"')
|
os.system(f'attrib +r "{updater_path}"')
|
||||||
else:
|
else:
|
||||||
os.chmod(updater_path, 0o444) # Set to read-only
|
os.chmod(updater_path, 0o444) # 设置为只读
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# 检查 update_yml_path
|
||||||
|
update_yml_path = self.update_yml_path
|
||||||
|
if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)):
|
||||||
|
# 创建 update_yml_path 阻止文件
|
||||||
|
with open(update_yml_path, 'w') as f:
|
||||||
|
f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
|
||||||
|
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}{Style.RESET_ALL}")
|
# 设置 update_yml_path 为只读
|
||||||
|
if self.system == "Windows":
|
||||||
|
os.system(f'attrib +r "{update_yml_path}"')
|
||||||
|
else:
|
||||||
|
os.chmod(update_yml_path, 0o444) # 设置为只读
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -117,11 +170,14 @@ class AutoUpdateDisabler:
|
|||||||
if not self._kill_cursor_processes():
|
if not self._kill_cursor_processes():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 2. Delete directory
|
# 2. Delete directory - 即使失败也继续执行
|
||||||
if not self._remove_updater_directory():
|
self._remove_updater_directory()
|
||||||
|
|
||||||
|
# 3. Clear update.yml file
|
||||||
|
if not self._clear_update_yml_file():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 3. Create blocking file
|
# 4. Create blocking file
|
||||||
if not self._create_blocking_file():
|
if not self._create_blocking_file():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,8 @@
|
|||||||
"stack_trace": "Stack Trace",
|
"stack_trace": "Stack Trace",
|
||||||
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
|
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
|
||||||
"no_write_permission": "No Write Permission: {path}",
|
"no_write_permission": "No Write Permission: {path}",
|
||||||
"path_not_found": "Path Not Found: {path}"
|
"path_not_found": "Path Not Found: {path}",
|
||||||
|
"modify_file_failed": "Modify File Failed: {error}"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"title": "Cursor Registration Tool",
|
"title": "Cursor Registration Tool",
|
||||||
@@ -287,7 +288,14 @@
|
|||||||
"removing_directory": "Removing Directory",
|
"removing_directory": "Removing Directory",
|
||||||
"directory_removed": "Directory Removed",
|
"directory_removed": "Directory Removed",
|
||||||
"creating_block_file": "Creating Block File",
|
"creating_block_file": "Creating Block File",
|
||||||
"block_file_created": "Block File Created"
|
"block_file_created": "Block File Created",
|
||||||
|
"clearing_update_yml": "Clearing update.yml file",
|
||||||
|
"update_yml_cleared": "update.yml file cleared",
|
||||||
|
"update_yml_not_found": "update.yml file not found",
|
||||||
|
"clear_update_yml_failed": "Failed to clear update.yml file: {error}",
|
||||||
|
"unsupported_os": "Unsupported OS: {system}",
|
||||||
|
"remove_directory_failed": "Failed to remove directory: {error}",
|
||||||
|
"create_block_file_failed": "Failed to create block file: {error}"
|
||||||
},
|
},
|
||||||
"updater": {
|
"updater": {
|
||||||
"checking": "Checking for updates...",
|
"checking": "Checking for updates...",
|
||||||
@@ -440,5 +448,36 @@
|
|||||||
"completed_successfully": "GitHub + Cursor registration completed successfully!",
|
"completed_successfully": "GitHub + Cursor registration completed successfully!",
|
||||||
"registration_encountered_issues": "GitHub + Cursor registration encountered issues.",
|
"registration_encountered_issues": "GitHub + Cursor registration encountered issues.",
|
||||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Check browser windows for manual intervention or try again later."
|
"check_browser_windows_for_manual_intervention_or_try_again_later": "Check browser windows for manual intervention or try again later."
|
||||||
}
|
},
|
||||||
|
"account_info": {
|
||||||
|
"subscription": "Subscription",
|
||||||
|
"trial_remaining": "Remaining Pro Trial",
|
||||||
|
"days": "days",
|
||||||
|
"subscription_not_found": "Subscription information not found",
|
||||||
|
"email_not_found": "Email not found",
|
||||||
|
"failed_to_get_account": "Failed to get account information",
|
||||||
|
"config_not_found": "Configuration not found.",
|
||||||
|
"failed_to_get_usage": "Failed to get usage information",
|
||||||
|
"failed_to_get_subscription": "Failed to get subscription information",
|
||||||
|
"failed_to_get_email": "Failed to get email address",
|
||||||
|
"failed_to_get_token": "Failed to get token",
|
||||||
|
"failed_to_get_account_info": "Failed to get account information",
|
||||||
|
"title": "Account Information",
|
||||||
|
"email": "Email",
|
||||||
|
"token": "Token",
|
||||||
|
"usage": "Usage",
|
||||||
|
"subscription_type": "Subscription Type",
|
||||||
|
"remaining_trial": "Remaining Trial",
|
||||||
|
"days_remaining": "Days Remaining",
|
||||||
|
"premium": "Premium",
|
||||||
|
"pro": "Pro",
|
||||||
|
"pro_trial": "Pro Trial",
|
||||||
|
"team": "Team",
|
||||||
|
"enterprise": "Enterprise",
|
||||||
|
"free": "Free",
|
||||||
|
"active": "Active",
|
||||||
|
"inactive": "Inactive",
|
||||||
|
"premium_usage": "Premium Usage",
|
||||||
|
"basic_usage": "Basic Usage"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,8 @@
|
|||||||
"stack_trace": "堆栈跟踪",
|
"stack_trace": "堆栈跟踪",
|
||||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||||
"no_write_permission": "没有写入权限: {path}",
|
"no_write_permission": "没有写入权限: {path}",
|
||||||
"path_not_found": "路径未找到: {path}"
|
"path_not_found": "路径未找到: {path}",
|
||||||
|
"modify_file_failed": "修改文件失败: {error}"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"title": "Cursor 注册工具",
|
"title": "Cursor 注册工具",
|
||||||
@@ -282,7 +283,14 @@
|
|||||||
"removing_directory": "删除目录",
|
"removing_directory": "删除目录",
|
||||||
"directory_removed": "目录已删除",
|
"directory_removed": "目录已删除",
|
||||||
"creating_block_file": "创建阻止文件",
|
"creating_block_file": "创建阻止文件",
|
||||||
"block_file_created": "阻止文件已创建"
|
"block_file_created": "阻止文件已创建",
|
||||||
|
"clearing_update_yml": "清空 update.yml 文件",
|
||||||
|
"update_yml_cleared": "update.yml 文件已清空",
|
||||||
|
"update_yml_not_found": "update.yml 文件未找到",
|
||||||
|
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
|
||||||
|
"unsupported_os": "不支持的操作系统: {system}",
|
||||||
|
"remove_directory_failed": "删除目录失败: {error}",
|
||||||
|
"create_block_file_failed": "创建阻止文件失败: {error}"
|
||||||
},
|
},
|
||||||
"updater": {
|
"updater": {
|
||||||
"checking": "检查更新...",
|
"checking": "检查更新...",
|
||||||
@@ -435,5 +443,37 @@
|
|||||||
"completed_successfully": "GitHub + Cursor 注册成功",
|
"completed_successfully": "GitHub + Cursor 注册成功",
|
||||||
"registration_encountered_issues": "GitHub + Cursor 注册遇到问题",
|
"registration_encountered_issues": "GitHub + Cursor 注册遇到问题",
|
||||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "检查浏览器窗口进行手动干预或稍后再试"
|
"check_browser_windows_for_manual_intervention_or_try_again_later": "检查浏览器窗口进行手动干预或稍后再试"
|
||||||
|
},
|
||||||
|
"account_info": {
|
||||||
|
"subscription": "订阅",
|
||||||
|
"trial_remaining": "剩余试用",
|
||||||
|
"days": "天",
|
||||||
|
"subscription_not_found": "订阅信息未找到",
|
||||||
|
"email_not_found": "邮箱未找到",
|
||||||
|
"failed_to_get_account": "获取账户信息失败",
|
||||||
|
"config_not_found": "配置未找到。",
|
||||||
|
"failed_to_get_usage": "获取使用信息失败",
|
||||||
|
"failed_to_get_subscription": "获取订阅信息失败",
|
||||||
|
"failed_to_get_email": "获取邮箱地址失败",
|
||||||
|
"failed_to_get_token": "获取 token 失败",
|
||||||
|
"failed_to_get_account_info": "获取账户信息失败",
|
||||||
|
"title": "账户信息",
|
||||||
|
"email": "邮箱",
|
||||||
|
"token": "Token",
|
||||||
|
"usage": "使用量",
|
||||||
|
"subscription_type": "订阅类型",
|
||||||
|
"remaining_trial": "剩余试用",
|
||||||
|
"days_remaining": "剩余天数",
|
||||||
|
"premium": "高级",
|
||||||
|
"pro": "专业",
|
||||||
|
"pro_trial": "专业试用",
|
||||||
|
"team": "团队",
|
||||||
|
"enterprise": "企业",
|
||||||
|
"free": "免费",
|
||||||
|
"active": "活跃",
|
||||||
|
"inactive": "非活跃",
|
||||||
|
"premium_usage": "高级使用量",
|
||||||
|
"basic_usage": "基础使用量"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,8 @@
|
|||||||
"stack_trace": "堆疊跟踪",
|
"stack_trace": "堆疊跟踪",
|
||||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||||
"no_write_permission": "沒有寫入權限: {path}",
|
"no_write_permission": "沒有寫入權限: {path}",
|
||||||
"path_not_found": "路徑未找到: {path}"
|
"path_not_found": "路徑未找到: {path}",
|
||||||
|
"modify_file_failed": "修改文件失敗: {error}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"register": {
|
"register": {
|
||||||
@@ -261,7 +262,14 @@
|
|||||||
"removing_directory": "刪除目錄",
|
"removing_directory": "刪除目錄",
|
||||||
"directory_removed": "目錄已刪除",
|
"directory_removed": "目錄已刪除",
|
||||||
"creating_block_file": "創建阻止文件",
|
"creating_block_file": "創建阻止文件",
|
||||||
"block_file_created": "阻止文件已創建"
|
"block_file_created": "阻止文件已創建",
|
||||||
|
"clearing_update_yml": "清空 update.yml 文件",
|
||||||
|
"update_yml_cleared": "update.yml 文件已清空",
|
||||||
|
"update_yml_not_found": "update.yml 文件未找到",
|
||||||
|
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
|
||||||
|
"unsupported_os": "不支持的操作系统: {system}",
|
||||||
|
"remove_directory_failed": "刪除目錄失败: {error}",
|
||||||
|
"create_block_file_failed": "創建阻止文件失败: {error}"
|
||||||
},
|
},
|
||||||
"updater": {
|
"updater": {
|
||||||
"checking": "檢查更新...",
|
"checking": "檢查更新...",
|
||||||
@@ -414,5 +422,36 @@
|
|||||||
"completed_successfully": "GitHub + Cursor 註冊成功",
|
"completed_successfully": "GitHub + Cursor 註冊成功",
|
||||||
"registration_encountered_issues": "GitHub + Cursor 註冊遇到問題",
|
"registration_encountered_issues": "GitHub + Cursor 註冊遇到問題",
|
||||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "檢查瀏覽器視窗進行手動干預或稍後再試"
|
"check_browser_windows_for_manual_intervention_or_try_again_later": "檢查瀏覽器視窗進行手動干預或稍後再試"
|
||||||
|
},
|
||||||
|
"account_info": {
|
||||||
|
"subscription": "訂閱",
|
||||||
|
"trial_remaining": "剩餘試用",
|
||||||
|
"days": "天",
|
||||||
|
"subscription_not_found": "訂閱信息未找到",
|
||||||
|
"email_not_found": "郵箱未找到",
|
||||||
|
"failed_to_get_account": "獲取帳戶信息失敗",
|
||||||
|
"config_not_found": "配置未找到。",
|
||||||
|
"failed_to_get_usage": "獲取使用信息失敗",
|
||||||
|
"failed_to_get_subscription": "獲取訂閱信息失敗",
|
||||||
|
"failed_to_get_email": "獲取郵箱地址失敗",
|
||||||
|
"failed_to_get_token": "獲取 token 失敗",
|
||||||
|
"failed_to_get_account_info": "獲取帳戶信息失敗",
|
||||||
|
"title": "帳戶信息",
|
||||||
|
"email": "郵箱",
|
||||||
|
"token": "Token",
|
||||||
|
"usage": "使用量",
|
||||||
|
"subscription_type": "訂閱類型",
|
||||||
|
"remaining_trial": "剩餘試用",
|
||||||
|
"days_remaining": "剩餘天數",
|
||||||
|
"premium": "高級",
|
||||||
|
"pro": "專業",
|
||||||
|
"pro_trial": "專業試用",
|
||||||
|
"team": "團隊",
|
||||||
|
"enterprise": "企業",
|
||||||
|
"free": "免費",
|
||||||
|
"active": "活躍",
|
||||||
|
"inactive": "非活躍",
|
||||||
|
"premium_usage": "高級使用量",
|
||||||
|
"basic_usage": "基礎使用量"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
logo.py
8
logo.py
@@ -20,7 +20,7 @@ init()
|
|||||||
# get terminal width
|
# get terminal width
|
||||||
def get_terminal_width():
|
def get_terminal_width():
|
||||||
try:
|
try:
|
||||||
columns, _ = shutil.get_terminal_size()
|
columns, _ = shutil.get_terminal_size()/2
|
||||||
return columns
|
return columns
|
||||||
except:
|
except:
|
||||||
return 80 # default width
|
return 80 # default width
|
||||||
@@ -34,7 +34,7 @@ def center_multiline_text(text, handle_chinese=False):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
# calculate actual display width (remove ANSI color codes)
|
# calculate actual display width (remove ANSI color codes)
|
||||||
clean_line = line
|
clean_line = line
|
||||||
for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Style.RESET_ALL]:
|
for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Fore.BLUE, Style.RESET_ALL]:
|
||||||
clean_line = clean_line.replace(color, '')
|
clean_line = clean_line.replace(color, '')
|
||||||
|
|
||||||
# remove all ANSI escape sequences to get the actual length
|
# remove all ANSI escape sequences to get the actual length
|
||||||
@@ -83,7 +83,7 @@ muhammedfurkan plamkatawe
|
|||||||
"""
|
"""
|
||||||
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
||||||
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
|
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
|
||||||
Press 7 to change language | 按下 7 键切换语言{Style.RESET_ALL}"""
|
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
|
||||||
|
|
||||||
# center display LOGO and DESCRIPTION
|
# center display LOGO and DESCRIPTION
|
||||||
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
|
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
|
||||||
@@ -98,4 +98,4 @@ def print_logo():
|
|||||||
print(CURSOR_OTHER_INFO)
|
print(CURSOR_OTHER_INFO)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print_logo()
|
print_logo()
|
||||||
|
|||||||
10
main.py
10
main.py
@@ -242,6 +242,12 @@ translator = Translator()
|
|||||||
|
|
||||||
def print_menu():
|
def print_menu():
|
||||||
"""Print menu options"""
|
"""Print menu options"""
|
||||||
|
try:
|
||||||
|
import cursor_acc_info
|
||||||
|
cursor_acc_info.display_account_info(translator)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.account_info_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
|
||||||
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
||||||
@@ -498,8 +504,8 @@ def main():
|
|||||||
print_menu()
|
print_menu()
|
||||||
elif choice == "10":
|
elif choice == "10":
|
||||||
import totally_reset_cursor
|
import totally_reset_cursor
|
||||||
# totally_reset_cursor.main(translator)
|
totally_reset_cursor.run(translator)
|
||||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
||||||
print_menu()
|
print_menu()
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ class NewTempEmail:
|
|||||||
# 创建浏览器选项
|
# 创建浏览器选项
|
||||||
co = ChromiumOptions()
|
co = ChromiumOptions()
|
||||||
co.set_argument("--headless=new")
|
co.set_argument("--headless=new")
|
||||||
|
co.set_argument("--no-sandbox")
|
||||||
|
|
||||||
|
|
||||||
co.auto_port() # 自动设置端口
|
co.auto_port() # 自动设置端口
|
||||||
|
|
||||||
|
|||||||
@@ -663,13 +663,10 @@ class MachineIDResetter:
|
|||||||
self.update_system_ids(new_ids)
|
self.update_system_ids(new_ids)
|
||||||
|
|
||||||
|
|
||||||
### Remove In v1.7.02
|
|
||||||
# Modify workbench.desktop.main.js
|
# Modify workbench.desktop.main.js
|
||||||
|
workbench_path = get_workbench_cursor_path(self.translator)
|
||||||
# workbench_path = get_workbench_cursor_path(self.translator)
|
modify_workbench_js(workbench_path, self.translator)
|
||||||
# modify_workbench_js(workbench_path, self.translator)
|
|
||||||
|
|
||||||
### Remove In v1.7.02
|
|
||||||
# Check Cursor version and perform corresponding actions
|
# Check Cursor version and perform corresponding actions
|
||||||
|
|
||||||
greater_than_0_45 = check_cursor_version(self.translator)
|
greater_than_0_45 = check_cursor_version(self.translator)
|
||||||
|
|||||||
@@ -2,195 +2,508 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import platform
|
import platform
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
import subprocess
|
import subprocess
|
||||||
from colorama import Fore, Style, init
|
from colorama import Fore, Style, init
|
||||||
|
from main import translator
|
||||||
|
from main import EMOJI
|
||||||
|
|
||||||
|
# Initialize colorama
|
||||||
init()
|
init()
|
||||||
|
|
||||||
|
# Define emoji and color constants
|
||||||
EMOJI = {
|
EMOJI = {
|
||||||
|
"FILE": "📄",
|
||||||
|
"BACKUP": "💾",
|
||||||
"SUCCESS": "✅",
|
"SUCCESS": "✅",
|
||||||
"ERROR": "❌",
|
"ERROR": "❌",
|
||||||
"INFO": "ℹ️",
|
"INFO": "ℹ️",
|
||||||
"RESET": "🔄",
|
"RESET": "🔄",
|
||||||
"MENU": "📋",
|
"MENU": "📋",
|
||||||
|
"ARROW": "➜",
|
||||||
|
"LANG": "🌐",
|
||||||
|
"UPDATE": "🔄",
|
||||||
|
"ADMIN": "🔐",
|
||||||
|
"STOP": "🛑",
|
||||||
|
"DISCLAIMER": "⚠️",
|
||||||
"WARNING": "⚠️"
|
"WARNING": "⚠️"
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete_directory(path, translator=None):
|
def display_banner():
|
||||||
"""Deletes a directory and all its contents."""
|
"""Displays a stylized banner for the tool."""
|
||||||
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('totally_reset.title')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def display_features():
|
||||||
|
"""Displays the features of the Cursor AI Reset Tool."""
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('totally_reset.feature_title')}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_1')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_2')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_3')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_4')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_5')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_6')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_7')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_8')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_9')} {Style.RESET_ALL}\n")
|
||||||
|
|
||||||
|
def display_disclaimer():
|
||||||
|
"""Displays a disclaimer for the user."""
|
||||||
|
print(f"\n{Fore.RED}{EMOJI['DISCLAIMER']} {translator.get('totally_reset.disclaimer_title')}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_1')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_2')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_3')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_4')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_5')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_6')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_7')} {Style.RESET_ALL} \n")
|
||||||
|
|
||||||
|
def get_confirmation():
|
||||||
|
"""Gets confirmation from the user to proceed."""
|
||||||
|
while True:
|
||||||
|
choice = input(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.confirm_title')} (Y/n): ").strip().lower()
|
||||||
|
if choice == "y" or choice == "":
|
||||||
|
return True
|
||||||
|
elif choice == "n":
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f"{EMOJI['ERROR']} {translator.get('totally_reset.invalid_choice')}")
|
||||||
|
|
||||||
|
def remove_dir(path):
|
||||||
|
"""Removes a directory if it exists and logs the action."""
|
||||||
|
# Safety check to ensure we're only deleting Cursor-related directories
|
||||||
|
if not is_cursor_related(path):
|
||||||
|
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.skipped_for_safety', path=path)} {Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.removed', path=path)}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.deleted', path=path)} {Style.RESET_ALL}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_remove', path=path, error=e)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_deleting', path=path, error=str(e))} {Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)} {Style.RESET_ALL}")
|
||||||
|
|
||||||
def delete_file(path, translator=None):
|
def remove_file(path):
|
||||||
"""Deletes a file if it exists."""
|
"""Removes a file if it exists and logs the action."""
|
||||||
|
# Safety check to ensure we're only deleting Cursor-related files
|
||||||
|
if not is_cursor_related(path):
|
||||||
|
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.skipped_for_safety', path=path)} {Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.removed', path=path)}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.deleted', path=path)} {Style.RESET_ALL}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_remove', path=path, error=e)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_deleting', path=path, error=str(e))} {Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)} {Style.RESET_ALL}")
|
||||||
|
|
||||||
def reset_machine_id(translator=None):
|
def is_cursor_related(path):
|
||||||
"""Resets the machine ID to a new UUID."""
|
"""
|
||||||
new_id = str(uuid.uuid4())
|
Safety function to verify a path is related to Cursor before deletion.
|
||||||
if platform.system() == "Windows":
|
Returns True if the path appears to be related to Cursor AI.
|
||||||
try:
|
"""
|
||||||
subprocess.run(
|
# Skip .vscode check as it's shared with VS Code
|
||||||
["reg", "add", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid", "/d", new_id, "/f"],
|
if path.endswith(".vscode"):
|
||||||
check=True,
|
return False
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE
|
# Check if path contains cursor-related terms
|
||||||
)
|
cursor_terms = ["cursor", "cursorai", "cursor-electron"]
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_guid_reset', new_id=new_id)}{Style.RESET_ALL}")
|
|
||||||
except subprocess.CalledProcessError as e:
|
# Convert path to lowercase for case-insensitive matching
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_guid', error=e)}{Style.RESET_ALL}")
|
lower_path = path.lower()
|
||||||
elif platform.system() == "Linux":
|
|
||||||
machine_id_paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"]
|
# Return True if any cursor term is present in the path
|
||||||
for path in machine_id_paths:
|
for term in cursor_terms:
|
||||||
if os.path.exists(path):
|
if term in lower_path:
|
||||||
try:
|
|
||||||
with open(path, 'w') as f:
|
|
||||||
f.write(new_id)
|
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_id_reset', path=path)}{Style.RESET_ALL}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_id', path=path, error=e)}{Style.RESET_ALL}")
|
|
||||||
elif platform.system() == "Darwin": # macOS
|
|
||||||
print("ℹ️ macOS does not use a machine-id file. Skipping machine ID reset.")
|
|
||||||
else:
|
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')}{Style.RESET_ALL}")
|
|
||||||
|
|
||||||
def display_features_and_warnings(translator=None):
|
|
||||||
"""Displays features and warnings before proceeding."""
|
|
||||||
print(f"\n{Fore.GREEN}{EMOJI['MENU']} {translator.get('totally_reset.title')}")
|
|
||||||
print("=====================================")
|
|
||||||
print(f"{translator.get('totally_reset.feature_title')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_1')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_2')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_3')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_4')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_5')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_6')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_7')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_8')}")
|
|
||||||
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_9')}")
|
|
||||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('totally_reset.warning_title')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_1')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_2')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_3')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_4')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_5')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_6')}")
|
|
||||||
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_7')}")
|
|
||||||
print("=====================================\n")
|
|
||||||
|
|
||||||
def get_user_confirmation(translator=None):
|
|
||||||
"""Prompts the user for confirmation to proceed."""
|
|
||||||
while True:
|
|
||||||
response = input(f"{Fore.YELLOW} {translator.get('totally_reset.confirm_title')} {translator.get('totally_reset.invalid_choice')}: ").lower().strip()
|
|
||||||
if response in ['yes', 'y']:
|
|
||||||
return True
|
return True
|
||||||
elif response in ['no', 'n']:
|
|
||||||
return False
|
# Check specific known Cursor file patterns
|
||||||
else:
|
cursor_patterns = [
|
||||||
print(f"{Fore.RED}{translator.get('totally_reset.invalid_choice')}{Style.RESET_ALL}")
|
r"\.cursor_.*$",
|
||||||
|
r"cursor-.*\.json$",
|
||||||
|
r"cursor_.*\.json$",
|
||||||
|
r"cursor-machine-id$",
|
||||||
|
r"trial_info\.json$",
|
||||||
|
r"license\.json$"
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in cursor_patterns:
|
||||||
|
if re.search(pattern, lower_path):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If it's a specific file that we know is only for Cursor
|
||||||
|
if os.path.basename(lower_path) in [
|
||||||
|
"cursor_trial_data",
|
||||||
|
"cursor-state.json",
|
||||||
|
"cursor-machine-id",
|
||||||
|
"ai-settings.json",
|
||||||
|
"cursor.desktop"
|
||||||
|
]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def reset_cursor(translator=None):
|
def find_cursor_license_files(base_path, pattern):
|
||||||
print(f"\n{Fore.GREEN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor')}\n")
|
"""Finds files matching a pattern that might contain license information."""
|
||||||
|
try:
|
||||||
|
matches = []
|
||||||
|
for root, dirnames, filenames in os.walk(base_path):
|
||||||
|
for filename in filenames:
|
||||||
|
# Check if filename matches any pattern before adding to matches
|
||||||
|
if any(p.lower() in filename.lower() for p in pattern):
|
||||||
|
full_path = os.path.join(root, filename)
|
||||||
|
# Extra safety check to ensure it's cursor-related
|
||||||
|
if is_cursor_related(full_path):
|
||||||
|
matches.append(full_path)
|
||||||
|
return matches
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.error_searching', path=base_path, error=str(e))} {Style.RESET_ALL}")
|
||||||
|
return []
|
||||||
|
|
||||||
# Platform-specific paths
|
def generate_new_machine_id():
|
||||||
paths = []
|
"""Generates a new random machine ID."""
|
||||||
if platform.system() == "Linux":
|
return str(uuid.uuid4())
|
||||||
paths = [
|
|
||||||
os.path.expanduser("~/.cursor"),
|
def create_fake_machine_id(path):
|
||||||
os.path.expanduser("~/.local/share/cursor"),
|
"""Creates a new machine ID file with random ID."""
|
||||||
os.path.expanduser("~/.config/cursor"),
|
if not is_cursor_related(path):
|
||||||
os.path.expanduser("~/.cache/cursor"),
|
return
|
||||||
"/usr/local/bin/cursor",
|
|
||||||
"/opt/cursor",
|
try:
|
||||||
"/usr/bin/cursor",
|
new_id = generate_new_machine_id()
|
||||||
os.path.expanduser("~/.cursor/machine-id.db"),
|
directory = os.path.dirname(path)
|
||||||
os.path.expanduser("~/.local/share/Cursor"),
|
|
||||||
os.path.expanduser("~/.config/Cursor"),
|
# Ensure directory exists
|
||||||
os.path.expanduser("~/.cache/Cursor")
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(new_id)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.created_machine_id', path=path)} {Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_creating_machine_id', path=path, error=str(e))} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def reset_machine_id(system, home):
|
||||||
|
"""Resets machine ID in all possible locations."""
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_machine_id')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Common machine ID locations based on OS
|
||||||
|
if system == "Windows":
|
||||||
|
machine_id_paths = [
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "AppData", "Local", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "cursor-electron", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "AppData", "Local", "cursor-electron", "cursor-machine-id"),
|
||||||
|
os.path.join(home, ".cursor-machine-id"),
|
||||||
]
|
]
|
||||||
elif platform.system() == "Darwin": # macOS
|
elif system == "Darwin": # macOS
|
||||||
paths = [
|
machine_id_paths = [
|
||||||
os.path.expanduser("~/Library/Application Support/Cursor"),
|
os.path.join(home, "Library", "Application Support", "Cursor", "cursor-machine-id"),
|
||||||
os.path.expanduser("~/Library/Caches/Cursor"),
|
os.path.join(home, "Library", "Application Support", "cursor-electron", "cursor-machine-id"),
|
||||||
"/Applications/Cursor.app",
|
os.path.join(home, ".cursor-machine-id"),
|
||||||
os.path.expanduser("~/Library/Preferences/com.cursor.app.plist"),
|
|
||||||
]
|
]
|
||||||
elif platform.system() == "Windows":
|
elif system == "Linux":
|
||||||
paths = [
|
machine_id_paths = [
|
||||||
os.path.expanduser("~\\AppData\\Local\\Cursor"),
|
os.path.join(home, ".config", "Cursor", "cursor-machine-id"),
|
||||||
os.path.expanduser("~\\AppData\\Roaming\\Cursor"),
|
os.path.join(home, ".config", "cursor-electron", "cursor-machine-id"),
|
||||||
os.path.expanduser("~\\.cursor"),
|
os.path.join(home, ".cursor-machine-id"),
|
||||||
os.path.expanduser("~\\.config\\Cursor"),
|
]
|
||||||
os.path.expanduser("~\\.cache\\Cursor"),
|
|
||||||
"C:\\Program Files\\Cursor",
|
# First remove existing machine IDs
|
||||||
"C:\\Program Files (x86)\\Cursor",
|
for path in machine_id_paths:
|
||||||
"C:\\Users\\%USERNAME%\\AppData\\Local\\Cursor",
|
remove_file(path)
|
||||||
"C:\\Users\\%USERNAME%\\AppData\\Roaming\\Cursor",
|
|
||||||
|
# Then create new randomized IDs
|
||||||
|
for path in machine_id_paths:
|
||||||
|
create_fake_machine_id(path)
|
||||||
|
|
||||||
|
# Try to reset system machine ID if possible (with appropriate permissions)
|
||||||
|
if system == "Windows":
|
||||||
|
try:
|
||||||
|
# Windows: Create a temporary VBS script to reset machine GUID
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.note_complete_machine_id_reset_may_require_running_as_administrator')} {Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.windows_machine_id_modification_skipped', error=str(e))} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
elif system == "Linux":
|
||||||
|
try:
|
||||||
|
# Linux: Create a random machine-id in /etc/ (needs sudo)
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.note_complete_system_machine_id_reset_may_require_sudo_privileges')} {Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.linux_machine_id_modification_skipped', error=str(e))} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def create_fake_trial_info(path, system, home):
|
||||||
|
"""Creates fake trial information to extend trial period."""
|
||||||
|
if not is_cursor_related(path):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Generate future expiry date (90 days from now)
|
||||||
|
future_date = (datetime.now().timestamp() + (90 * 24 * 60 * 60)) * 1000 # milliseconds
|
||||||
|
|
||||||
|
# Create fake trial info
|
||||||
|
fake_trial = {
|
||||||
|
"trialStartTimestamp": datetime.now().timestamp() * 1000,
|
||||||
|
"trialEndTimestamp": future_date,
|
||||||
|
"hasUsedTrial": False,
|
||||||
|
"machineId": generate_new_machine_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
directory = os.path.dirname(path)
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
json.dump(fake_trial, f)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.created_extended_trial_info', path=path)} {Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_creating_trial_info', path=path, error=str(e))} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def reset_cursor():
|
||||||
|
"""Completely resets Cursor AI by removing all settings, caches, and extensions."""
|
||||||
|
system = platform.system()
|
||||||
|
home = os.path.expanduser("~")
|
||||||
|
|
||||||
|
display_banner()
|
||||||
|
display_features()
|
||||||
|
display_disclaimer()
|
||||||
|
|
||||||
|
if not get_confirmation():
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['STOP']} {translator.get('totally_reset.reset_cancelled')} {Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor_ai_editor')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Define paths based on OS
|
||||||
|
if system == "Windows":
|
||||||
|
cursor_paths = [
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor"),
|
||||||
|
os.path.join(home, "AppData", "Local", "Cursor"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "cursor-electron"),
|
||||||
|
os.path.join(home, "AppData", "Local", "cursor-electron"),
|
||||||
|
os.path.join(home, "AppData", "Local", "CursorAI"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "CursorAI"),
|
||||||
|
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
|
||||||
|
os.path.join(home, "AppData", "Local", "Temp", "Cursor"), # Temporary data
|
||||||
|
os.path.join(home, "AppData", "Local", "Temp", "cursor-updater"),
|
||||||
|
os.path.join(home, "AppData", "Local", "Programs", "cursor"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Additional locations for license/trial files on Windows
|
||||||
|
license_search_paths = [
|
||||||
|
os.path.join(home, "AppData", "Roaming"),
|
||||||
|
os.path.join(home, "AppData", "Local"),
|
||||||
|
os.path.join(home, "AppData", "LocalLow"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Registry instructions for Windows
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.windows_registry_instructions')} {Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.windows_registry_instructions_2')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
elif system == "Darwin": # macOS
|
||||||
|
cursor_paths = [
|
||||||
|
os.path.join(home, "Library", "Application Support", "Cursor"),
|
||||||
|
os.path.join(home, "Library", "Application Support", "cursor-electron"),
|
||||||
|
os.path.join(home, "Library", "Caches", "Cursor"),
|
||||||
|
os.path.join(home, "Library", "Caches", "cursor-electron"),
|
||||||
|
os.path.join(home, "Library", "Preferences", "Cursor"),
|
||||||
|
os.path.join(home, "Library", "Preferences", "cursor-electron"),
|
||||||
|
os.path.join(home, "Library", "Saved Application State", "com.cursor.Cursor.savedState"),
|
||||||
|
os.path.join(home, "Library", "HTTPStorages", "com.cursor.Cursor"),
|
||||||
|
os.path.join(home, "Library", "WebKit", "com.cursor.Cursor"),
|
||||||
|
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
|
||||||
|
"/Applications/Cursor.app", # Main application location
|
||||||
|
]
|
||||||
|
|
||||||
|
# Additional locations for license/trial files on macOS
|
||||||
|
license_search_paths = [
|
||||||
|
os.path.join(home, "Library", "Application Support"),
|
||||||
|
os.path.join(home, "Library", "Preferences"),
|
||||||
|
os.path.join(home, "Library", "Caches"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Remove directories
|
elif system == "Linux":
|
||||||
for path in paths:
|
cursor_paths = [
|
||||||
delete_directory(path, translator)
|
os.path.join(home, ".config", "Cursor"),
|
||||||
|
os.path.join(home, ".config", "cursor-electron"),
|
||||||
|
os.path.join(home, ".cache", "Cursor"),
|
||||||
|
os.path.join(home, ".cache", "cursor-electron"),
|
||||||
|
os.path.join(home, ".local", "share", "Cursor"),
|
||||||
|
os.path.join(home, ".local", "share", "cursor-electron"),
|
||||||
|
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
|
||||||
|
os.path.join(home, ".local", "share", "applications", "cursor.desktop"),
|
||||||
|
os.path.join("/usr", "share", "applications", "cursor.desktop"),
|
||||||
|
os.path.join("/opt", "Cursor"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Additional locations for license/trial files on Linux
|
||||||
|
license_search_paths = [
|
||||||
|
os.path.join(home, ".config"),
|
||||||
|
os.path.join(home, ".local", "share"),
|
||||||
|
os.path.join(home, ".cache"),
|
||||||
|
]
|
||||||
|
|
||||||
# Remove common files related to Cursor
|
else:
|
||||||
files = [
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')} {Style.RESET_ALL}")
|
||||||
os.path.expanduser("~/.cursor/machine-id.db"),
|
return
|
||||||
os.path.expanduser("~/.local/share/cursor.db"),
|
|
||||||
os.path.expanduser("~/.config/cursor/preferences.json"),
|
# Remove main Cursor directories
|
||||||
os.path.expanduser("~/.cache/cursor.log"),
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_main_cursor_directories_and_files')} {Style.RESET_ALL}")
|
||||||
|
for path in cursor_paths:
|
||||||
|
remove_dir(path)
|
||||||
|
|
||||||
|
# Reset machine identifiers (this creates new ones)
|
||||||
|
reset_machine_id(system, home)
|
||||||
|
|
||||||
|
# Known trial/license file patterns
|
||||||
|
file_patterns = [
|
||||||
|
".cursor_trial_data",
|
||||||
|
"trial_info.json",
|
||||||
|
"license.json",
|
||||||
|
"cursor-license",
|
||||||
|
"cursor_license",
|
||||||
|
"cursor-auth",
|
||||||
|
"cursor_auth",
|
||||||
|
"cursor_subscription",
|
||||||
|
"cursor-subscription",
|
||||||
|
"cursor-state",
|
||||||
|
"cursorstate",
|
||||||
|
"cursorsettings",
|
||||||
|
"cursor-settings",
|
||||||
|
"ai-settings.json",
|
||||||
|
"cursor-machine-id",
|
||||||
|
"cursor_machine_id",
|
||||||
|
"cursor-storage"
|
||||||
]
|
]
|
||||||
|
|
||||||
for file in files:
|
# Direct known trial file paths
|
||||||
delete_file(file, translator)
|
cursor_trial_files = [
|
||||||
|
os.path.join(home, ".cursor_trial_data"),
|
||||||
|
os.path.join(home, ".cursor_license"),
|
||||||
|
os.path.join(home, ".cursor-machine-id"),
|
||||||
|
os.path.join(home, ".cursor-state.json"),
|
||||||
|
]
|
||||||
|
|
||||||
# Extra cleanup (wildcard search)
|
# OS-specific known trial/license files
|
||||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.deep_scanning')}")
|
if system == "Windows":
|
||||||
base_dirs = ["/tmp", "/var/tmp", os.path.expanduser("~")] # Linux and macOS
|
cursor_trial_files.extend([
|
||||||
if platform.system() == "Windows":
|
os.path.join(home, "AppData", "Local", "Cursor", "trial_info.json"),
|
||||||
base_dirs = ["C:\\Temp", "C:\\Windows\\Temp", os.path.expanduser("~")] # Windows
|
os.path.join(home, "AppData", "Local", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor", "trial_info.json"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "AppData", "Local", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "AppData", "Local", "Cursor", "ai-settings.json"),
|
||||||
|
os.path.join(home, "AppData", "Roaming", "Cursor", "ai-settings.json"),
|
||||||
|
])
|
||||||
|
elif system == "Darwin": # macOS
|
||||||
|
cursor_trial_files.extend([
|
||||||
|
os.path.join(home, "Library", "Application Support", "Cursor", "trial_info.json"),
|
||||||
|
os.path.join(home, "Library", "Application Support", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, "Library", "Preferences", "Cursor", "trial_info.json"),
|
||||||
|
os.path.join(home, "Library", "Preferences", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, "Library", "Application Support", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, "Library", "Application Support", "Cursor", "ai-settings.json"),
|
||||||
|
])
|
||||||
|
elif system == "Linux":
|
||||||
|
cursor_trial_files.extend([
|
||||||
|
os.path.join(home, ".config", "Cursor", "trial_info.json"),
|
||||||
|
os.path.join(home, ".config", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, ".local", "share", "Cursor", "trial_info.json"),
|
||||||
|
os.path.join(home, ".local", "share", "Cursor", "license.json"),
|
||||||
|
os.path.join(home, ".config", "Cursor", "cursor-machine-id"),
|
||||||
|
os.path.join(home, ".config", "Cursor", "ai-settings.json"),
|
||||||
|
])
|
||||||
|
|
||||||
for base in base_dirs:
|
# Remove known trial/license files
|
||||||
for root, dirs, files in os.walk(base):
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_known')} {Style.RESET_ALL}")
|
||||||
for dir in dirs:
|
for path in cursor_trial_files:
|
||||||
if "cursor" in dir.lower():
|
remove_file(path)
|
||||||
delete_directory(os.path.join(root, dir), translator)
|
|
||||||
for file in files:
|
|
||||||
if "cursor" in file.lower():
|
|
||||||
delete_file(os.path.join(root, file), translator)
|
|
||||||
|
|
||||||
# Reset machine ID
|
# Deep search for additional trial/license files
|
||||||
reset_machine_id(translator)
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.performing_deep_scan')} {Style.RESET_ALL}")
|
||||||
|
all_found_files = []
|
||||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.cursor_reset_completed')}")
|
for base_path in license_search_paths:
|
||||||
|
if os.path.exists(base_path):
|
||||||
def main(translator=None):
|
found_files = find_cursor_license_files(base_path, file_patterns)
|
||||||
start_time = time.time()
|
all_found_files.extend(found_files)
|
||||||
|
|
||||||
# Display features and warnings
|
if all_found_files:
|
||||||
display_features_and_warnings(translator)
|
print(f"\n🔎 {translator.get('totally_reset.found_additional_potential_license_trial_files', count=len(all_found_files))}\n")
|
||||||
|
for file_path in all_found_files:
|
||||||
# Get user confirmation
|
remove_file(file_path)
|
||||||
if get_user_confirmation(translator):
|
|
||||||
reset_cursor(translator)
|
|
||||||
end_time = time.time()
|
|
||||||
print(f"\n{Fore.GREEN}⏱️ {translator.get('totally_reset.completed_in', time=f'{end_time - start_time:.2f} seconds')}{Style.RESET_ALL}")
|
|
||||||
else:
|
else:
|
||||||
print(f"\n{Fore.RED}❌ {translator.get('totally_reset.operation_cancelled')}{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.no_additional_license_trial_files_found_in_deep_scan')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
# Check for and remove localStorage files that might contain settings
|
||||||
from main import translator
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.checking_for_electron_localstorage_files')} {Style.RESET_ALL}")
|
||||||
main(translator)
|
if system == "Windows":
|
||||||
|
local_storage_paths = glob.glob(os.path.join(home, "AppData", "Roaming", "*cursor*", "Local Storage", "leveldb", "*"))
|
||||||
|
local_storage_paths += glob.glob(os.path.join(home, "AppData", "Local", "*cursor*", "Local Storage", "leveldb", "*"))
|
||||||
|
elif system == "Darwin":
|
||||||
|
local_storage_paths = glob.glob(os.path.join(home, "Library", "Application Support", "*cursor*", "Local Storage", "leveldb", "*"))
|
||||||
|
elif system == "Linux":
|
||||||
|
local_storage_paths = glob.glob(os.path.join(home, ".config", "*cursor*", "Local Storage", "leveldb", "*"))
|
||||||
|
|
||||||
|
for path in local_storage_paths:
|
||||||
|
if is_cursor_related(path):
|
||||||
|
remove_file(path)
|
||||||
|
|
||||||
|
# Create new trial files with extended expiration
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.creating_new_trial_information_with_extended_period')} {Style.RESET_ALL}")
|
||||||
|
if system == "Windows":
|
||||||
|
create_fake_trial_info(os.path.join(home, "AppData", "Local", "Cursor", "trial_info.json"), system, home)
|
||||||
|
create_fake_trial_info(os.path.join(home, "AppData", "Roaming", "Cursor", "trial_info.json"), system, home)
|
||||||
|
elif system == "Darwin":
|
||||||
|
create_fake_trial_info(os.path.join(home, "Library", "Application Support", "Cursor", "trial_info.json"), system, home)
|
||||||
|
elif system == "Linux":
|
||||||
|
create_fake_trial_info(os.path.join(home, ".config", "Cursor", "trial_info.json"), system, home)
|
||||||
|
|
||||||
|
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.reset_log_1')}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_2')}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_3')}")
|
||||||
|
|
||||||
|
print(f"\n{Fore.GREEN}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_4')} {Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_5')} {Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_6')} {Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_7')} {Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.reset_log_8')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
print(f"\n{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_9')} {Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
reset_cursor()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.keyboard_interrupt')} {Style.RESET_ALL}")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unexpected_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.report_issue')}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def run(translator=None):
|
||||||
|
"""Entry point for the totally reset cursor functionality when called from the main menu."""
|
||||||
|
try:
|
||||||
|
reset_cursor()
|
||||||
|
input(f"\n\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.return_to_main_menu')} {Style.RESET_ALL}")
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.process_interrupted')} {Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unexpected_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
print(f" {translator.get('totally_reset.report_issue')}")
|
||||||
|
input(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.press_enter_to_return_to_main_menu')} {Style.RESET_ALL}")
|
||||||
Reference in New Issue
Block a user