mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-26 05:30:36 +03:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d70c1738 | ||
|
|
650e2d33db | ||
|
|
2d1347c8c6 | ||
|
|
67d3554664 | ||
|
|
cb202e08e5 | ||
|
|
0c0ec47432 | ||
|
|
f00678ddcb | ||
|
|
dbc220053d | ||
|
|
d7ab362c4c | ||
|
|
4448a62458 | ||
|
|
50d09e5172 | ||
|
|
ffff3bdeb1 | ||
|
|
816a09d4de | ||
|
|
e5b7e5727c | ||
|
|
087e3ebf78 | ||
|
|
bd96107911 | ||
|
|
17799e0209 | ||
|
|
c15ea25cb3 | ||
|
|
350d781ce8 | ||
|
|
4485cc5571 | ||
|
|
df58b2e4ab | ||
|
|
5f380ebe5e | ||
|
|
c97bfd1475 | ||
|
|
28cd662e83 | ||
|
|
1b6ba5eab8 | ||
|
|
3e3cd40e3f | ||
|
|
8f35f163c5 | ||
|
|
f6ffb18427 | ||
|
|
b6bf62f841 | ||
|
|
1c1174fa6c | ||
|
|
4587fd9373 | ||
|
|
51fcf83ebb | ||
|
|
5cc7630ce6 | ||
|
|
bd9e10056f | ||
|
|
36f5356b4a | ||
|
|
18fc0532fd | ||
|
|
7405c61cc9 | ||
|
|
80e9946294 | ||
|
|
4ef293da81 | ||
|
|
eb6ef6bb3b | ||
|
|
3700239c99 | ||
|
|
a8966de771 | ||
|
|
8943266f6e | ||
|
|
35ed9cb6f6 | ||
|
|
3d1450ced0 | ||
|
|
892fc85efa | ||
|
|
feaf9906ba | ||
|
|
568e38e70c | ||
|
|
ab7281921e | ||
|
|
58b4b0dece |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
version:
|
||||
description: 'Version number (e.g. 1.0.9)'
|
||||
required: true
|
||||
default: '1.7.16'
|
||||
default: '1.8.02'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,6 +14,8 @@ build.py
|
||||
build.sh
|
||||
ENV/
|
||||
test.py
|
||||
new_tempemail_smail.py
|
||||
new_tempemail_api.py
|
||||
|
||||
install.bat
|
||||
run.bat
|
||||
|
||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,6 +1,42 @@
|
||||
# Change Log
|
||||
|
||||
## v1.7.16 (Pre-Release)
|
||||
## v1.8.03
|
||||
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.02
|
||||
1. Add: New Temp Email | 增加新臨時郵箱
|
||||
2. Add: Config Options | 增加配置選項
|
||||
3. Add: Update Windows Machine ID | 增加更新 Windows 機器 ID
|
||||
4. Add: Contributors Options | 增加貢獻者選項
|
||||
5. Add: Check update enable Options In config | 增加在 config 中檢查更新選項
|
||||
6. Add: Show account info enabled options in config | 增加在 config 中顯示賬號信息選項
|
||||
7. Optimize Row & Colume Options | 優化行與列選項
|
||||
8. Fix: Too Many Free Trial On Some Machine | 修復某些機器上太多免費試用
|
||||
9. Fix: Disable Auto Update | 修復禁用自動更新
|
||||
10. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
|
||||
11. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## 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
|
||||
1. Fix: No Write Permission | 修復沒有寫入權限
|
||||
2. Fix: Improve Linux path detection and config handling | 修正 linux 路徑和config寫入讀取
|
||||
3. Fix: Locale path_no_exist missing | 修正 path_no_exist 語言遺失
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.7.17
|
||||
1. Fix: Remove 10 options Totally Reset Cursor | 修復完全重置 Cursor 選項
|
||||
|
||||
## v1.7.16
|
||||
1. Add bulgarian language | 增加保加利亚语
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
3. Add: Contributors | 增加貢獻者
|
||||
|
||||
28
README.md
28
README.md
@@ -10,13 +10,19 @@
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
|
||||
</p>
|
||||
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
|
||||
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
|
||||
|
||||
This tool registers accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes, resets and wipes Cursor data and hardware info.
|
||||
|
||||
Supports Windows, macOS and Linux.
|
||||
|
||||
For optimal performance, run with privileges and always stay up to date.
|
||||
|
||||
Always clean your browser's cache and cookies. If possible, use a VPN to create new accounts.
|
||||
|
||||
This is a tool to automatically register, support Windows and macOS systems, complete Auth verification, and reset
|
||||
Cursor's configuration.
|
||||
|
||||
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||
|
||||
@@ -94,7 +100,7 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
||||
|
||||
</details>
|
||||
|
||||
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
||||
If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
||||
|
||||
## ❗ Note | 注意事項
|
||||
|
||||
@@ -109,9 +115,9 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
||||
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
|
||||
|
||||
[Turnstile]
|
||||
# Handle Tuenstile Wait Time | 等待人機驗證時間
|
||||
# Handle Turnstile Wait Time | 等待人機驗證時間
|
||||
handle_turnstile_time = 2
|
||||
# Handle Tuenstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
|
||||
# Handle Turnstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
|
||||
handle_turnstile_random_time = 1-3
|
||||
|
||||
[OSPaths]
|
||||
@@ -152,11 +158,17 @@ failed_retry_time = 0.5-1
|
||||
retry_interval = 8-12
|
||||
# Max Timeout | 最大超時時間
|
||||
max_timeout = 160
|
||||
|
||||
[Utils]
|
||||
# Check Update | 檢查更新
|
||||
check_update = True
|
||||
# Show Account Info | 顯示賬號信息
|
||||
show_account_info = True
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
* Use administrator to run the script <br>請使用管理員身份運行腳本
|
||||
* Use administrator privileges to run the script <br>請使用管理員身份運行腳本
|
||||
|
||||
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
||||
|
||||
|
||||
@@ -16,3 +16,7 @@ ikomail.com
|
||||
mailpull.com
|
||||
drewzen.com
|
||||
begemail.com
|
||||
dugmail.com
|
||||
solerbe.net
|
||||
corhash.net
|
||||
mailshou.com
|
||||
151
config.py
151
config.py
@@ -4,6 +4,18 @@ import configparser
|
||||
from colorama import Fore, Style
|
||||
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
|
||||
|
||||
EMOJI = {
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"ERROR": "❌",
|
||||
"SUCCESS": "✅",
|
||||
"ADMIN": "🔒",
|
||||
"ARROW": "➡️",
|
||||
"USER": "👤",
|
||||
"KEY": "🔑",
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
def setup_config(translator=None):
|
||||
"""Setup configuration file and return config object"""
|
||||
try:
|
||||
@@ -37,6 +49,10 @@ def setup_config(translator=None):
|
||||
'failed_retry_time': '0.5-1',
|
||||
'retry_interval': '8-12',
|
||||
'max_timeout': '160'
|
||||
},
|
||||
'Utils': {
|
||||
'enabled_update_check': 'True',
|
||||
'enabled_account_info': 'True'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,26 +65,106 @@ def setup_config(translator=None):
|
||||
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
|
||||
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
|
||||
'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")
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
default_config['MacPaths'] = {
|
||||
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
|
||||
'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"),
|
||||
'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":
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "linux":
|
||||
# Get the actual user's home directory, handling both sudo and normal cases
|
||||
current_user = os.getenv('USER') or os.getenv('USERNAME') or os.getenv('SUDO_USER')
|
||||
if not current_user:
|
||||
current_user = os.path.expanduser('~').split('/')[-1]
|
||||
|
||||
actual_home = f"/home/{current_user}"
|
||||
if not os.path.exists(actual_home):
|
||||
actual_home = os.path.expanduser("~")
|
||||
|
||||
# Define base config directory
|
||||
config_base = os.path.join(actual_home, ".config")
|
||||
|
||||
# Try both "Cursor" and "cursor" directory names
|
||||
cursor_dir = None
|
||||
for dir_name in ["Cursor", "cursor"]:
|
||||
test_dir = os.path.join(config_base, dir_name)
|
||||
if os.path.exists(test_dir):
|
||||
cursor_dir = test_dir
|
||||
break
|
||||
|
||||
if not cursor_dir:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
|
||||
# Define Linux paths using the found cursor directory
|
||||
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
|
||||
storage_dir = os.path.dirname(storage_path) if storage_path else ""
|
||||
|
||||
# Verify paths and permissions
|
||||
try:
|
||||
# Check storage directory
|
||||
if storage_dir and not os.path.exists(storage_dir):
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
|
||||
# Check storage.json with more detailed verification
|
||||
if storage_path and os.path.exists(storage_path):
|
||||
# Get file stats
|
||||
try:
|
||||
stat = os.stat(storage_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', stat.st_size)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', oct(stat.st_mode & 0o777))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', stat.st_uid)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', stat.st_gid)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
# Check if file is readable and writable
|
||||
if not os.access(storage_path, os.R_OK | os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', current_user=current_user, storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Try to read the file to verify it's not corrupted
|
||||
try:
|
||||
with open(storage_path, 'r') as f:
|
||||
content = f.read()
|
||||
if not content.strip():
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor')}{Style.RESET_ALL}")
|
||||
elif storage_path:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
# Define all paths using the found cursor directory
|
||||
default_config['LinuxPaths'] = {
|
||||
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/storage.json")),
|
||||
'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"),
|
||||
'storage_path': storage_path,
|
||||
'sqlite_path': os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/state.vscdb")) if cursor_dir else "",
|
||||
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
|
||||
'cursor_path': get_linux_cursor_path(),
|
||||
'updater_path': os.path.expanduser("~/.config/cursor-updater")
|
||||
'updater_path': os.path.join(config_base, "cursor-updater"),
|
||||
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else ""
|
||||
}
|
||||
|
||||
# Read existing configuration and merge
|
||||
@@ -85,13 +181,13 @@ def setup_config(translator=None):
|
||||
config.set(section, option, str(value))
|
||||
config_modified = True
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}ℹ️ {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
|
||||
|
||||
if config_modified:
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.config_updated')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_updated')}{Style.RESET_ALL}")
|
||||
else:
|
||||
for section, options in default_config.items():
|
||||
config.add_section(section)
|
||||
@@ -101,16 +197,41 @@ def setup_config(translator=None):
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
|
||||
|
||||
return config
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ Error setting up config: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def print_config(config, translator=None):
|
||||
"""Print configuration in a readable format"""
|
||||
if not config:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration')}:{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
for section in config.sections():
|
||||
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
|
||||
for key, value in config.items(section):
|
||||
# 对布尔值进行特殊处理,使其显示为彩色
|
||||
if value.lower() in ('true', 'yes', 'on', '1'):
|
||||
value_display = f"{Fore.GREEN}{translator.get('config.enabled')}{Style.RESET_ALL}"
|
||||
elif value.lower() in ('false', 'no', 'off', '0'):
|
||||
value_display = f"{Fore.RED}{translator.get('config.disabled')}{Style.RESET_ALL}"
|
||||
else:
|
||||
value_display = value
|
||||
|
||||
print(f" {key} = {value_display}")
|
||||
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory')}: {config_dir}{Style.RESET_ALL}")
|
||||
|
||||
print()
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
|
||||
552
cursor_acc_info.py
Normal file
552
cursor_acc_info.py
Normal file
@@ -0,0 +1,552 @@
|
||||
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()
|
||||
|
||||
# get Premium usage and limit
|
||||
gpt4_data = data.get("gpt-4", {})
|
||||
premium_usage = gpt4_data.get("numRequestsTotal", 0)
|
||||
max_premium_usage = gpt4_data.get("maxRequestUsage", 999)
|
||||
|
||||
# get Basic usage, but set limit to "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" # set Basic limit to "No Limit"
|
||||
}
|
||||
except requests.RequestException as e:
|
||||
# only log error
|
||||
logger.error(f"Get usage info failed: {str(e)}")
|
||||
return None
|
||||
except Exception as e:
|
||||
# catch all other exceptions
|
||||
logger.error(f"Get usage info failed: {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"Get subscription info failed: {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"Get config path failed: {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}{'─' * 70}{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}{'─' * 70}{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
|
||||
try:
|
||||
subscription_info = UsageManager.get_stripe_profile(token)
|
||||
except Exception as e:
|
||||
logger.error(f"Get subscription info failed: {str(e)}")
|
||||
subscription_info = None
|
||||
|
||||
# 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 - silently handle errors
|
||||
try:
|
||||
usage_info = UsageManager.get_usage(token)
|
||||
except Exception as e:
|
||||
logger.error(f"Get usage info failed: {str(e)}")
|
||||
usage_info = None
|
||||
|
||||
# Prepare left and right info
|
||||
left_info = []
|
||||
right_info = []
|
||||
|
||||
# Left side shows account info
|
||||
if email:
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
|
||||
else:
|
||||
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
|
||||
|
||||
# Add an empty line
|
||||
# left_info.append("")
|
||||
|
||||
# Show subscription type
|
||||
if subscription_info:
|
||||
subscription_type = format_subscription_type(subscription_info)
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
|
||||
|
||||
# Show remaining trial days
|
||||
days_remaining = subscription_info.get("daysRemainingOnTrial")
|
||||
if days_remaining is not None and days_remaining > 0:
|
||||
left_info.append(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:
|
||||
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.subscription_not_found') if translator else 'Subscription information not found'}{Style.RESET_ALL}")
|
||||
|
||||
# Right side shows usage info - only if available
|
||||
if usage_info:
|
||||
right_info.append(f"{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
|
||||
|
||||
# Premium 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}%)"
|
||||
|
||||
right_info.append(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}%)"
|
||||
|
||||
right_info.append(f"{Fore.BLUE}{EMOJI['BASIC']} {translator.get('account_info.basic_usage') if translator else 'Slow Response'}: {basic_color}{basic_display}{Style.RESET_ALL}")
|
||||
else:
|
||||
# if get usage info failed, only log in log, not show in interface
|
||||
# you can choose to not show any usage info, or show a simple prompt
|
||||
# right_info.append(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_info.usage_unavailable') if translator else 'Usage information unavailable'}{Style.RESET_ALL}")
|
||||
pass # not show any usage info
|
||||
|
||||
# Calculate the maximum display width of left info
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
|
||||
def get_display_width(s):
|
||||
"""Calculate the display width of a string, considering Chinese characters and emojis"""
|
||||
# Remove ANSI color codes
|
||||
clean_s = ansi_escape.sub('', s)
|
||||
width = 0
|
||||
for c in clean_s:
|
||||
# Chinese characters and some emojis occupy two character widths
|
||||
if ord(c) > 127:
|
||||
width += 2
|
||||
else:
|
||||
width += 1
|
||||
return width
|
||||
|
||||
max_left_width = 0
|
||||
for item in left_info:
|
||||
width = get_display_width(item)
|
||||
max_left_width = max(max_left_width, width)
|
||||
|
||||
# Set the starting position of right info
|
||||
fixed_spacing = 4 # Fixed spacing
|
||||
right_start = max_left_width + fixed_spacing
|
||||
|
||||
# Calculate the number of spaces needed for right info
|
||||
spaces_list = []
|
||||
for i in range(len(left_info)):
|
||||
if i < len(left_info):
|
||||
left_item = left_info[i]
|
||||
left_width = get_display_width(left_item)
|
||||
spaces = right_start - left_width
|
||||
spaces_list.append(spaces)
|
||||
|
||||
# Print info
|
||||
max_rows = max(len(left_info), len(right_info))
|
||||
|
||||
for i in range(max_rows):
|
||||
# Print left info
|
||||
if i < len(left_info):
|
||||
left_item = left_info[i]
|
||||
print(left_item, end='')
|
||||
|
||||
# Use pre-calculated spaces
|
||||
spaces = spaces_list[i]
|
||||
else:
|
||||
# If left side has no items, print only spaces
|
||||
spaces = right_start
|
||||
print('', end='')
|
||||
|
||||
# Print right info
|
||||
if i < len(right_info):
|
||||
print(' ' * spaces + right_info[i])
|
||||
else:
|
||||
print() # Change line
|
||||
|
||||
print(f"{Fore.CYAN}{'─' * 70}{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()
|
||||
@@ -5,6 +5,8 @@ import shutil
|
||||
from colorama import Fore, Style, init
|
||||
import subprocess
|
||||
from config import get_config
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -31,10 +33,13 @@ class AutoUpdateDisabler:
|
||||
if config:
|
||||
if self.system == "Windows":
|
||||
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":
|
||||
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":
|
||||
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:
|
||||
# If configuration loading fails, use default paths
|
||||
self.updater_paths = {
|
||||
@@ -43,6 +48,52 @@ class AutoUpdateDisabler:
|
||||
"Linux": os.path.expanduser("~/.config/cursor-updater")
|
||||
}
|
||||
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 _change_main_js(self):
|
||||
"""Change main.js"""
|
||||
try:
|
||||
main_path = get_config(self.translator).get('main_js_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/main.js"))
|
||||
original_stat = os.stat(main_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
|
||||
with open(main_path, "r", encoding="utf-8") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
patterns = {
|
||||
r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(main_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
os.unlink(tmp_path)
|
||||
return False
|
||||
|
||||
def _kill_cursor_processes(self):
|
||||
"""End all Cursor processes"""
|
||||
@@ -81,27 +132,70 @@ class AutoUpdateDisabler:
|
||||
|
||||
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}")
|
||||
# 即使删除失败,也返回 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
|
||||
|
||||
def _create_blocking_file(self):
|
||||
"""Create blocking file"""
|
||||
"""Create blocking files"""
|
||||
try:
|
||||
# 检查 updater_path
|
||||
updater_path = self.updater_path
|
||||
if not updater_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.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()
|
||||
|
||||
# Set read-only attribute
|
||||
# 设置 updater_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{updater_path}"')
|
||||
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
|
||||
|
||||
except Exception as e:
|
||||
@@ -117,14 +211,21 @@ class AutoUpdateDisabler:
|
||||
if not self._kill_cursor_processes():
|
||||
return False
|
||||
|
||||
# 2. Delete directory
|
||||
if not self._remove_updater_directory():
|
||||
# 2. Delete directory - 即使失败也继续执行
|
||||
self._remove_updater_directory()
|
||||
|
||||
# 3. Clear update.yml file
|
||||
if not self._clear_update_yml_file():
|
||||
return False
|
||||
|
||||
# 3. Create blocking file
|
||||
# 4. Create blocking file
|
||||
if not self._create_blocking_file():
|
||||
return False
|
||||
|
||||
# 5. Change main.js
|
||||
if not self._change_main_js():
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"register_manual": "Регистрирай се със свой имейл по избор",
|
||||
"quit": "Затвори приложението Курсор",
|
||||
"select_language": "Избери език",
|
||||
"es": "Spanish",
|
||||
"input_choice": "Моля, въведете своя избор ({choices})",
|
||||
"invalid_choice": "Невалиден избор. Моля, въведете избор от {choices}.",
|
||||
"program_terminated": "Програмата беше затворена от вас",
|
||||
@@ -32,7 +33,8 @@
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"bg": "Bulgarian"
|
||||
"bg": "Bulgarian",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Започни излизането от Курсор",
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"de": "Deutsch",
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ru": "Russisch"
|
||||
"ru": "Russisch",
|
||||
"es": "Spanisch"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Beginne Cursor zu Beenden",
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
"temp_github_register": "Temporary GitHub Register",
|
||||
"admin_required": "Running as executable, administrator privileges required.",
|
||||
"admin_required_continue": "Continuing without administrator privileges.",
|
||||
"coming_soon": "Coming Soon"
|
||||
"coming_soon": "Coming Soon",
|
||||
"fixed_soon": "Fixed Soon",
|
||||
"contribute": "Contribute to the Project",
|
||||
"config": "Show Config"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -34,7 +37,8 @@
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"bg": "Bulgarian"
|
||||
"bg": "Bulgarian",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start Quitting Cursor",
|
||||
@@ -101,8 +105,13 @@
|
||||
"package_not_found": "Package.json Not Found: {path}",
|
||||
"check_version_failed": "Check Version Failed: {error}",
|
||||
"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}",
|
||||
"path_not_found": "Path Not Found: {path}",
|
||||
"modify_file_failed": "Modify File Failed: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine ID Updated Successfully",
|
||||
"update_windows_machine_id_failed": "Update Windows Machine ID Failed: {error}",
|
||||
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registration Tool",
|
||||
@@ -284,7 +293,14 @@
|
||||
"removing_directory": "Removing Directory",
|
||||
"directory_removed": "Directory Removed",
|
||||
"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": {
|
||||
"checking": "Checking for updates...",
|
||||
@@ -297,7 +313,8 @@
|
||||
"update_skipped": "Skipping update.",
|
||||
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
|
||||
"development_version": "Development Version {current} > {latest}",
|
||||
"changelog_title": "Changelog"
|
||||
"changelog_title": "Changelog",
|
||||
"rate_limit_exceeded": "GitHub API rate limit exceeded. Skipping update check."
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Totally Reset Cursor",
|
||||
@@ -437,5 +454,66 @@
|
||||
"completed_successfully": "GitHub + Cursor registration completed successfully!",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"usage_not_found": "Usage not found",
|
||||
"lifetime_access_enabled": "Lifetime Access Enabled"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "Configuration not available",
|
||||
"configuration": "Configuration",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"config_directory": "Config Directory",
|
||||
"neither_cursor_nor_cursor_directory_found": "Neither Cursor nor Cursor directory found in {config_base}",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Please make sure Cursor is installed and has been run at least once",
|
||||
"storage_directory_not_found": "Storage directory not found: {storage_dir}",
|
||||
"storage_file_found": "Storage file found: {storage_path}",
|
||||
"file_size": "File size: {size}",
|
||||
"file_permissions": "File permissions: {permissions}",
|
||||
"file_owner": "File owner: {owner}",
|
||||
"file_group": "File group: {group}",
|
||||
"error_getting_file_stats": "Error getting file stats: {error}",
|
||||
"permission_denied": "Permission denied: {storage_path}",
|
||||
"try_running": "Try running 'cursor --help' to check if it's installed",
|
||||
"and": "and",
|
||||
"storage_file_is_empty": "Storage file is empty: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
|
||||
"storage_file_not_found": "Storage file not found: {storage_path}",
|
||||
"error_checking_linux_paths": "Error checking Linux paths: {error}",
|
||||
"config_option_added": "Config option added: {option}",
|
||||
"config_updated": "Config updated",
|
||||
"config_created": "Config created: {config_file}",
|
||||
"config_setup_error": "Error setting up config: {error}"
|
||||
|
||||
}
|
||||
}
|
||||
443
locales/es.json
Normal file
443
locales/es.json
Normal file
@@ -0,0 +1,443 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Opciones Disponibles",
|
||||
"exit": "Salir del Programa",
|
||||
"reset": "Restablecer ID de Máquina",
|
||||
"register": "Registrar Nueva Cuenta de Cursor",
|
||||
"register_google": "Registrarse con Cuenta de Google",
|
||||
"register_github": "Registrarse con Cuenta de GitHub",
|
||||
"register_manual": "Registrar Cursor con Correo Personalizado",
|
||||
"quit": "Cerrar Aplicación Cursor",
|
||||
"select_language": "Cambiar Idioma",
|
||||
"input_choice": "Por favor, ingrese su elección ({choices})",
|
||||
"invalid_choice": "Selección inválida. Por favor ingrese un número de {choices}",
|
||||
"program_terminated": "El programa fue terminado por el usuario",
|
||||
"error_occurred": "Ocurrió un error: {error}. Por favor intente de nuevo",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"disable_auto_update": "Deshabilitar Actualización Automática de Cursor",
|
||||
"lifetime_access_enabled": "ACCESO DE POR VIDA ACTIVADO",
|
||||
"totally_reset": "Restablecer Cursor Completamente",
|
||||
"outdate": "Desactualizado",
|
||||
"temp_github_register": "Registro Temporal de GitHub",
|
||||
"admin_required": "Ejecutando como ejecutable, se requieren privilegios de administrador.",
|
||||
"admin_required_continue": "Continuando sin privilegios de administrador.",
|
||||
"coming_soon": "Próximamente",
|
||||
"fixed_soon": "Arreglado Pronto"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Inglés",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamita",
|
||||
"nl": "Holandés",
|
||||
"de": "Alemán",
|
||||
"fr": "Francés",
|
||||
"pt": "Portugués",
|
||||
"ru": "Ruso",
|
||||
"tr": "Turco",
|
||||
"bg": "Búlgaro",
|
||||
"es": "Español"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Comenzando a Cerrar Cursor",
|
||||
"no_process": "No Hay Procesos de Cursor en Ejecución",
|
||||
"terminating": "Terminando Proceso {pid}",
|
||||
"waiting": "Esperando que el Proceso Termine",
|
||||
"success": "Todos los Procesos de Cursor Cerrados",
|
||||
"timeout": "Tiempo de Espera Agotado: {pids}",
|
||||
"error": "Ocurrió un Error: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Herramienta de Restablecimiento de ID de Máquina de Cursor",
|
||||
"checking": "Verificando Archivo de Configuración",
|
||||
"not_found": "Archivo de Configuración No Encontrado",
|
||||
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
|
||||
"reading": "Leyendo Configuración Actual",
|
||||
"creating_backup": "Creando Copia de Seguridad de la Configuración",
|
||||
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
|
||||
"generating": "Generando Nuevo ID de Máquina",
|
||||
"saving_json": "Guardando Nueva Configuración en JSON",
|
||||
"success": "ID de Máquina Restablecido Exitosamente",
|
||||
"new_id": "Nuevo ID de Máquina",
|
||||
"permission_error": "Error de Permisos: {error}",
|
||||
"run_as_admin": "Por Favor Intente Ejecutar Este Programa como Administrador",
|
||||
"process_error": "Error en el Proceso de Restablecimiento: {error}",
|
||||
"updating_sqlite": "Actualizando Base de Datos SQLite",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"sqlite_success": "Base de Datos SQLite Actualizada Exitosamente",
|
||||
"sqlite_error": "Falló la Actualización de la Base de Datos SQLite: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"unsupported_os": "Sistema Operativo No Soportado: {os}",
|
||||
"linux_path_not_found": "Ruta de Linux No Encontrada",
|
||||
"updating_system_ids": "Actualizando IDs del Sistema",
|
||||
"system_ids_updated": "IDs del Sistema Actualizados Exitosamente",
|
||||
"system_ids_update_failed": "Falló la Actualización de IDs del Sistema: {error}",
|
||||
"windows_guid_updated": "GUID de Windows Actualizado Exitosamente",
|
||||
"windows_permission_denied": "Permisos de Windows Denegados",
|
||||
"windows_guid_update_failed": "Falló la Actualización del GUID de Windows",
|
||||
"macos_uuid_updated": "UUID de macOS Actualizado Exitosamente",
|
||||
"plutil_command_failed": "Falló el Comando plutil",
|
||||
"start_patching": "Iniciando Parcheo de getMachineId",
|
||||
"macos_uuid_update_failed": "Falló la Actualización del UUID de macOS",
|
||||
"current_version": "Versión Actual de Cursor: {version}",
|
||||
"patch_completed": "Parcheo de getMachineId Completado",
|
||||
"patch_failed": "Falló el Parcheo de getMachineId: {error}",
|
||||
"version_check_passed": "Verificación de Versión de Cursor Exitosa",
|
||||
"file_modified": "Archivo Modificado",
|
||||
"version_less_than_0_45": "Versión de Cursor < 0.45.0, Omitiendo Parcheo de getMachineId",
|
||||
"detecting_version": "Detectando Versión de Cursor",
|
||||
"patching_getmachineid": "Parcheando getMachineId",
|
||||
"version_greater_than_0_45": "Versión de Cursor >= 0.45.0, Parcheando getMachineId",
|
||||
"permission_denied": "Permiso Denegado: {error}",
|
||||
"backup_created": "Copia de Seguridad Creada",
|
||||
"update_success": "Actualización Exitosa",
|
||||
"update_failed": "Falló la Actualización: {error}",
|
||||
"windows_machine_guid_updated": "GUID de Máquina Windows Actualizado Exitosamente",
|
||||
"reading_package_json": "Leyendo package.json {path}",
|
||||
"invalid_json_object": "Objeto JSON Inválido",
|
||||
"no_version_field": "No se Encontró el Campo de Versión en package.json",
|
||||
"version_field_empty": "El Campo de Versión está Vacío",
|
||||
"invalid_version_format": "Formato de Versión Inválido: {version}",
|
||||
"found_version": "Versión Encontrada: {version}",
|
||||
"version_parse_error": "Error al Analizar Versión: {error}",
|
||||
"package_not_found": "Package.json No Encontrado: {path}",
|
||||
"check_version_failed": "Falló la Verificación de Versión: {error}",
|
||||
"stack_trace": "Traza de la Pila",
|
||||
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0"
|
||||
|
||||
},
|
||||
"register": {
|
||||
"title": "Herramienta de Registro de Cursor",
|
||||
"start": "Iniciando proceso de registro...",
|
||||
"handling_turnstile": "Procesando verificación de seguridad...",
|
||||
"retry_verification": "Reintentando verificación...",
|
||||
"detect_turnstile": "Comprobando verificación de seguridad...",
|
||||
"verification_success": "Verificación de seguridad exitosa",
|
||||
"starting_browser": "Abriendo navegador...",
|
||||
"form_success": "Formulario enviado exitosamente",
|
||||
"browser_started": "Navegador abierto exitosamente",
|
||||
"waiting_for_second_verification": "Esperando verificación por correo electrónico...",
|
||||
"waiting_for_verification_code": "Esperando código de verificación...",
|
||||
"password_success": "Contraseña establecida exitosamente",
|
||||
"password_error": "No se pudo establecer la contraseña: {error}. Por favor intente de nuevo",
|
||||
"waiting_for_page_load": "Cargando página...",
|
||||
"first_verification_passed": "Verificación inicial exitosa",
|
||||
"mailbox": "Acceso exitoso a la bandeja de entrada",
|
||||
"register_start": "Iniciar Registro",
|
||||
"form_submitted": "Formulario Enviado, Iniciando Verificación...",
|
||||
"filling_form": "Rellenando Formulario",
|
||||
"visiting_url": "Visitando URL",
|
||||
"basic_info": "Información Básica Enviada",
|
||||
"handle_turnstile": "Manejar Turnstile",
|
||||
"no_turnstile": "No se Detectó Turnstile",
|
||||
"turnstile_passed": "Turnstile Superado",
|
||||
"verification_start": "Comenzar a Obtener Código de Verificación",
|
||||
"verification_timeout": "Tiempo de Espera Agotado para Código de Verificación",
|
||||
"verification_not_found": "No se Encontró Código de Verificación",
|
||||
"try_get_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {time}s",
|
||||
"get_account": "Obteniendo Información de la Cuenta",
|
||||
"get_token": "Obtener Token de Sesión de Cursor",
|
||||
"token_success": "Token Obtenido Exitosamente",
|
||||
"token_attempt": "Intento | {attempt} veces para obtener Token | Se reintentará en {time}s",
|
||||
"token_max_attempts": "Alcanzado Máximo de Intentos ({max}) | No se pudo obtener Token",
|
||||
"token_failed": "Falló al Obtener Token: {error}",
|
||||
"account_error": "Falló al Obtener Información de la Cuenta: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"browser_start": "Iniciando Navegador",
|
||||
"open_mailbox": "Abriendo Página de Correo",
|
||||
"email_error": "Falló al Obtener Dirección de Correo",
|
||||
"setup_error": "Error de Configuración de Correo: {error}",
|
||||
"start_getting_verification_code": "Comenzando a Obtener Código de Verificación, Se Intentará en 60s",
|
||||
"get_verification_code_timeout": "Tiempo de Espera Agotado para Código de Verificación",
|
||||
"get_verification_code_success": "Código de Verificación Obtenido Exitosamente",
|
||||
"try_get_verification_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {remaining_time}s",
|
||||
"verification_code_filled": "Código de Verificación Completado",
|
||||
"login_success_and_jump_to_settings_page": "Inicio de Sesión Exitoso y Salto a Página de Configuración",
|
||||
"detect_login_page": "Página de Inicio de Sesión Detectada, Iniciando Sesión...",
|
||||
"cursor_registration_completed": "¡Registro de Cursor Completado!",
|
||||
"set_password": "Establecer Contraseña",
|
||||
"basic_info_submitted": "Información Básica Enviada",
|
||||
"cursor_auth_info_updated": "Información de Autenticación de Cursor Actualizada",
|
||||
"cursor_auth_info_update_failed": "Falló la Actualización de Información de Autenticación de Cursor",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"account_info_saved": "Información de Cuenta Guardada",
|
||||
"save_account_info_failed": "Falló al Guardar Información de Cuenta",
|
||||
"get_email_address": "Obtener Dirección de Correo",
|
||||
"update_cursor_auth_info": "Actualizar Información de Autenticación de Cursor",
|
||||
"register_process_error": "Error en el Proceso de Registro: {error}",
|
||||
"setting_password": "Estableciendo Contraseña",
|
||||
"manual_code_input": "Entrada Manual de Código",
|
||||
"manual_email_input": "Entrada Manual de Correo",
|
||||
"password": "Contraseña",
|
||||
"first_name": "Nombre",
|
||||
"last_name": "Apellido",
|
||||
"exit_signal": "Señal de Salida",
|
||||
"email_address": "Dirección de Correo",
|
||||
"config_created": "Configuración Creada",
|
||||
"verification_failed": "Verificación Fallida",
|
||||
"verification_error": "Error de Verificación: {error}",
|
||||
"config_option_added": "Opción de Configuración Añadida: {option}",
|
||||
"config_updated": "Configuración Actualizada",
|
||||
"password_submitted": "Contraseña Enviada",
|
||||
"total_usage": "Uso Total: {usage}",
|
||||
"setting_on_password": "Estableciendo Contraseña",
|
||||
"getting_code": "Obteniendo Código de Verificación, Se Intentará en 60s",
|
||||
"human_verify_error": "No se puede verificar que el usuario es humano. Reintentando...",
|
||||
"max_retries_reached": "Se alcanzó el máximo de intentos. Registro fallido."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Administrador de Autenticación de Cursor",
|
||||
"checking_auth": "Verificando Archivo de Autenticación",
|
||||
"auth_not_found": "Archivo de Autenticación No Encontrado",
|
||||
"auth_file_error": "Error en Archivo de Autenticación: {error}",
|
||||
"reading_auth": "Leyendo Archivo de Autenticación",
|
||||
"updating_auth": "Actualizando Información de Autenticación",
|
||||
"auth_updated": "Información de Autenticación Actualizada Exitosamente",
|
||||
"auth_update_failed": "Falló la Actualización de Información de Autenticación: {error}",
|
||||
"auth_file_created": "Archivo de Autenticación Creado",
|
||||
"auth_file_create_failed": "Falló la Creación del Archivo de Autenticación: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"connected_to_database": "Conectado a la Base de Datos",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
|
||||
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
|
||||
"db_connection_error": "Falló la conexión a la base de datos: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Generando Nuevo Correo",
|
||||
"blocked_domain": "Dominio Bloqueado",
|
||||
"select_domain": "Seleccionando Dominio Aleatorio",
|
||||
"copy_email": "Copiando Dirección de Correo",
|
||||
"enter_mailbox": "Entrando al Buzón de Correo",
|
||||
"refresh_mailbox": "Actualizando Buzón de Correo",
|
||||
"check_verification": "Verificando Código de Verificación",
|
||||
"verification_found": "Código de Verificación Encontrado",
|
||||
"verification_not_found": "No se Encontró Código de Verificación",
|
||||
"browser_error": "Error de Control del Navegador: {error}",
|
||||
"navigation_error": "Error de Navegación: {error}",
|
||||
"email_copy_error": "Error al Copiar Correo: {error}",
|
||||
"mailbox_error": "Error en el Buzón de Correo: {error}",
|
||||
"token_saved_to_file": "Token Guardado en cursor_tokens.txt",
|
||||
"navigate_to": "Navegando a {url}",
|
||||
"generate_email_success": "Generación de Correo Exitosa",
|
||||
"select_email_domain": "Seleccionar Dominio de Correo",
|
||||
"select_email_domain_success": "Selección de Dominio de Correo Exitosa",
|
||||
"get_email_name": "Obtener Nombre de Correo",
|
||||
"get_email_name_success": "Nombre de Correo Obtenido Exitosamente",
|
||||
"get_email_address": "Obtener Dirección de Correo",
|
||||
"get_email_address_success": "Dirección de Correo Obtenida Exitosamente",
|
||||
"enter_mailbox_success": "Entrada al Buzón de Correo Exitosa",
|
||||
"found_verification_code": "Código de Verificación Encontrado",
|
||||
"get_cursor_session_token": "Obtener Token de Sesión de Cursor",
|
||||
"get_cursor_session_token_success": "Token de Sesión de Cursor Obtenido Exitosamente",
|
||||
"get_cursor_session_token_failed": "Falló al Obtener Token de Sesión de Cursor",
|
||||
"save_token_failed": "Falló al Guardar Token",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"no_valid_verification_code": "No Hay Código de Verificación Válido"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Iniciando Navegador",
|
||||
"visiting_site": "Visitando dominios de correo",
|
||||
"create_success": "Correo Creado Exitosamente",
|
||||
"create_failed": "Falló al Crear Correo",
|
||||
"create_error": "Error en la Creación del Correo: {error}",
|
||||
"refreshing": "Actualizando Correo",
|
||||
"refresh_success": "Correo Actualizado Exitosamente",
|
||||
"refresh_error": "Error al Actualizar Correo: {error}",
|
||||
"refresh_button_not_found": "Botón de Actualización No Encontrado",
|
||||
"verification_found": "Verificación Encontrada",
|
||||
"verification_not_found": "Verificación No Encontrada",
|
||||
"verification_error": "Error de Verificación: {error}",
|
||||
"verification_code_found": "Código de Verificación Encontrado",
|
||||
"verification_code_not_found": "Código de Verificación No Encontrado",
|
||||
"verification_code_error": "Error en el Código de Verificación: {error}",
|
||||
"address": "Dirección de Correo",
|
||||
"all_domains_blocked": "Todos los Dominios Bloqueados, Cambiando Servicio",
|
||||
"no_available_domains_after_filtering": "No Hay Dominios Disponibles Después del Filtrado",
|
||||
"switching_service": "Cambiando al Servicio {service}",
|
||||
"domains_list_error": "Falló al Obtener Lista de Dominios: {error}",
|
||||
"failed_to_get_available_domains": "Falló al Obtener Dominios Disponibles",
|
||||
"domains_excluded": "Dominios Excluidos: {domains}",
|
||||
"failed_to_create_account": "Falló al Crear Cuenta",
|
||||
"account_creation_error": "Error en la Creación de la Cuenta: {error}",
|
||||
"blocked_domains": "Dominios Bloqueados: {domains}",
|
||||
"blocked_domains_loaded": "Dominios Bloqueados Cargados: {count}",
|
||||
"blocked_domains_loaded_error": "Error al Cargar Dominios Bloqueados: {error}",
|
||||
"blocked_domains_loaded_success": "Dominios Bloqueados Cargados Exitosamente",
|
||||
"blocked_domains_loaded_timeout": "Tiempo de Espera Agotado para Cargar Dominios Bloqueados: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Error de Tiempo de Espera al Cargar Dominios Bloqueados: {error}",
|
||||
"available_domains_loaded": "Dominios Disponibles Cargados: {count}",
|
||||
"domains_filtered": "Dominios Filtrados: {count}",
|
||||
"trying_to_create_email": "Intentando crear correo: {email}",
|
||||
"domain_blocked": "Dominio Bloqueado: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Deshabilitar Actualización Automática de Cursor",
|
||||
"disable_success": "Actualización Automática Deshabilitada Exitosamente",
|
||||
"disable_failed": "Falló al Deshabilitar Actualización Automática: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"start_disable": "Comenzar a Deshabilitar Actualización Automática",
|
||||
"killing_processes": "Terminando Procesos",
|
||||
"processes_killed": "Procesos Terminados",
|
||||
"removing_directory": "Eliminando Directorio",
|
||||
"directory_removed": "Directorio Eliminado",
|
||||
"creating_block_file": "Creando Archivo de Bloqueo",
|
||||
"block_file_created": "Archivo de Bloqueo Creado"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Buscando actualizaciones...",
|
||||
"new_version_available": "¡Nueva versión disponible! (Actual: {current}, Última: {latest})",
|
||||
"updating": "Actualizando a la última versión. El programa se reiniciará automáticamente.",
|
||||
"up_to_date": "Está utilizando la última versión.",
|
||||
"check_failed": "Falló al verificar actualizaciones: {error}",
|
||||
"continue_anyway": "Continuando con la versión actual...",
|
||||
"update_confirm": "¿Desea actualizar a la última versión? (Y/n)",
|
||||
"update_skipped": "Omitiendo actualización.",
|
||||
"invalid_choice": "Elección inválida. Por favor ingrese 'Y' o 'n'.",
|
||||
"development_version": "Versión de Desarrollo {current} > {latest}",
|
||||
"changelog_title": "Registro de Cambios"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Restablecer Cursor Completamente",
|
||||
"checking_config": "Verificando Archivo de Configuración",
|
||||
"config_not_found": "Archivo de Configuración No Encontrado",
|
||||
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
|
||||
"reading_config": "Leyendo Configuración Actual",
|
||||
"creating_backup": "Creando Copia de Seguridad de la Configuración",
|
||||
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
|
||||
"generating_new_machine_id": "Generando Nuevo ID de Máquina",
|
||||
"saving_new_config": "Guardando Nueva Configuración en JSON",
|
||||
"success": "Cursor Restablecido Exitosamente",
|
||||
"error": "Falló el Restablecimiento de Cursor: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"connected_to_database": "Conectado a la Base de Datos",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
|
||||
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
|
||||
"db_connection_error": "Falló la conexión a la base de datos: {error}",
|
||||
"feature_title": "CARACTERÍSTICAS",
|
||||
"feature_1": "Eliminación completa de configuraciones y ajustes de Cursor AI",
|
||||
"feature_2": "Limpia todos los datos en caché incluyendo historial de IA y peticiones",
|
||||
"feature_3": "Restablece el ID de máquina para evitar la detección de prueba",
|
||||
"feature_4": "Crea nuevos identificadores de máquina aleatorios",
|
||||
"feature_5": "Elimina extensiones personalizadas y preferencias",
|
||||
"feature_6": "Restablece información de prueba y datos de activación",
|
||||
"feature_7": "Escaneo profundo de archivos ocultos relacionados con licencias y pruebas",
|
||||
"feature_8": "Preserva de forma segura archivos y aplicaciones que no son de Cursor",
|
||||
"feature_9": "Compatible con Windows, macOS y Linux",
|
||||
"disclaimer_title": "AVISO IMPORTANTE",
|
||||
"disclaimer_1": "Esta herramienta eliminará permanentemente todas las configuraciones de Cursor AI,",
|
||||
"disclaimer_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"disclaimer_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"disclaimer_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"disclaimer_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"disclaimer_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"disclaimer_7": "Use bajo su propio riesgo",
|
||||
"confirm_title": "¿Está seguro de que desea continuar?",
|
||||
"confirm_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
|
||||
"confirm_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"confirm_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"confirm_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"confirm_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"confirm_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"confirm_7": "Use bajo su propio riesgo",
|
||||
"invalid_choice": "Por favor ingrese 'Y' o 'n'",
|
||||
"skipped_for_safety": "Omitido por seguridad (no relacionado con Cursor): {path}",
|
||||
"deleted": "Eliminado: {path}",
|
||||
"error_deleting": "Error al eliminar {path}: {error}",
|
||||
"not_found": "Archivo no encontrado: {path}",
|
||||
"resetting_machine_id": "Restableciendo identificadores de máquina para evitar la detección de prueba...",
|
||||
"created_machine_id": "Creado nuevo ID de máquina: {path}",
|
||||
"error_creating_machine_id": "Error al crear archivo de ID de máquina {path}: {error}",
|
||||
"error_searching": "Error al buscar archivos en {path}: {error}",
|
||||
"created_extended_trial_info": "Creada nueva información de prueba extendida: {path}",
|
||||
"error_creating_trial_info": "Error al crear archivo de información de prueba {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Restableciendo Editor Cursor AI... Por favor espere.",
|
||||
"reset_cancelled": "Restablecimiento cancelado. Saliendo sin realizar cambios.",
|
||||
"windows_machine_id_modification_skipped": "Modificación de ID de máquina de Windows omitida: {error}",
|
||||
"linux_machine_id_modification_skipped": "Modificación de machine-id de Linux omitida: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Nota: El restablecimiento completo del ID de máquina puede requerir ejecutar como administrador",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Nota: El restablecimiento completo del machine-id del sistema puede requerir privilegios sudo",
|
||||
"windows_registry_instructions": "📝 NOTA: Para un restablecimiento completo en Windows, es posible que también deba limpiar entradas del registro.",
|
||||
"windows_registry_instructions_2": " Ejecute 'regedit' y busque claves que contengan 'Cursor' o 'CursorAI' bajo HKEY_CURRENT_USER\\Software\\ y elimínelas.",
|
||||
"reset_log_1": "¡Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
|
||||
"reset_log_2": "Por favor reinicie su sistema para que los cambios surtan efecto.",
|
||||
"reset_log_3": "Necesitará reinstalar Cursor AI y ahora debería tener un nuevo período de prueba.",
|
||||
"reset_log_4": "Para mejores resultados, considere también:",
|
||||
"reset_log_5": "Usar una dirección de correo diferente al registrarse para una nueva prueba",
|
||||
"reset_log_6": "Si está disponible, usar una VPN para cambiar su dirección IP",
|
||||
"reset_log_7": "Limpiar las cookies y caché de su navegador antes de visitar el sitio web de Cursor AI",
|
||||
"reset_log_8": "Si los problemas persisten, intente instalar Cursor AI en una ubicación diferente",
|
||||
"reset_log_9": "Si encuentra algún problema, vaya al Rastreador de Problemas de Github y cree un problema en https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Ocurrió un error inesperado: {error}",
|
||||
"report_issue": "Por favor reporte este problema al Rastreador de Problemas de Github en https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Proceso interrumpido por el usuario. Saliendo...",
|
||||
"return_to_main_menu": "Volviendo al menú principal...",
|
||||
"process_interrupted": "Proceso interrumpido. Saliendo...",
|
||||
"press_enter_to_return_to_main_menu": "Presione Enter para volver al menú principal...",
|
||||
"removing_known": "Eliminando archivos conocidos de prueba/licencia",
|
||||
"performing_deep_scan": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
|
||||
"found_additional_potential_license_trial_files": "Se encontraron {count} archivos potenciales adicionales de licencia/prueba",
|
||||
"checking_for_electron_localstorage_files": "Verificando archivos de localStorage de Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "No se encontraron archivos adicionales de licencia/prueba en el escaneo profundo",
|
||||
"removing_electron_localstorage_files": "Eliminando archivos de localStorage de Electron",
|
||||
"electron_localstorage_files_removed": "Archivos de localStorage de Electron eliminados",
|
||||
"electron_localstorage_files_removal_error": "Error al eliminar archivos de localStorage de Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Eliminación de archivos de localStorage de Electron completada",
|
||||
"warning_title": "ADVERTENCIA",
|
||||
"warning_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
|
||||
"warning_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"warning_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"warning_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"warning_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"warning_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"warning_7": "Use bajo su propio riesgo",
|
||||
"removed": "Eliminado: {path}",
|
||||
"failed_to_reset_machine_guid": "Falló al restablecer GUID de máquina",
|
||||
"failed_to_remove": "Falló al eliminar: {path}",
|
||||
"failed_to_delete_file": "Falló al eliminar archivo: {path}",
|
||||
"failed_to_delete_directory": "Falló al eliminar directorio: {path}",
|
||||
"failed_to_delete_file_or_directory": "Falló al eliminar archivo o directorio: {path}",
|
||||
"deep_scanning": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
|
||||
"resetting_cursor": "Restableciendo Editor Cursor AI... Por favor espere.",
|
||||
"completed_in": "Completado en {time} segundos",
|
||||
"cursor_reset_completed": "¡El Editor Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
|
||||
"cursor_reset_failed": "Falló el restablecimiento del Editor Cursor AI: {error}",
|
||||
"cursor_reset_cancelled": "Restablecimiento del Editor Cursor AI cancelado. Saliendo sin realizar cambios.",
|
||||
"operation_cancelled": "Operación cancelada. Saliendo sin realizar cambios."
|
||||
},
|
||||
"github_register": {
|
||||
"title": "Automatización de Registro de GitHub + Cursor AI",
|
||||
"features_header": "Características",
|
||||
"feature1": "Genera un correo temporal usando 1secmail.",
|
||||
"feature2": "Registra una nueva cuenta de GitHub con credenciales aleatorias.",
|
||||
"feature3": "Verifica el correo de GitHub automáticamente.",
|
||||
"feature4": "Inicia sesión en Cursor AI usando autenticación de GitHub.",
|
||||
"feature5": "Restablece el ID de máquina para evitar la detección de prueba.",
|
||||
"feature6": "Guarda todas las credenciales en un archivo.",
|
||||
"warnings_header": "Advertencias",
|
||||
"warning1": "Este script automatiza la creación de cuentas, lo que puede violar los términos de servicio de GitHub/Cursor.",
|
||||
"warning2": "Requiere acceso a internet y privilegios administrativos.",
|
||||
"warning3": "CAPTCHA o verificación adicional pueden interrumpir la automatización.",
|
||||
"warning4": "Use responsablemente y bajo su propio riesgo.",
|
||||
"confirm": "¿Está seguro de que desea continuar?",
|
||||
"invalid_choice": "Elección inválida. Por favor ingrese 'yes' o 'no'",
|
||||
"cancelled": "Operación cancelada",
|
||||
"program_terminated": "Programa terminado por el usuario",
|
||||
"starting_automation": "Iniciando automatización...",
|
||||
"github_username": "Nombre de Usuario de GitHub",
|
||||
"github_password": "Contraseña de GitHub",
|
||||
"email_address": "Dirección de Correo",
|
||||
"credentials_saved": "Estas credenciales han sido guardadas en github_cursor_accounts.txt",
|
||||
"completed_successfully": "¡Registro de GitHub + Cursor completado exitosamente!",
|
||||
"registration_encountered_issues": "El registro de GitHub + Cursor encontró problemas.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Revise las ventanas del navegador para intervención manual o intente nuevamente más tarde."
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@
|
||||
"de": "Allemand",
|
||||
"fr": "Français",
|
||||
"pt": "Portugais",
|
||||
"ru": "Russe"
|
||||
"ru": "Russe",
|
||||
"es": "Espagnol"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Début de la Fermeture de Cursor",
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"de": "Duits",
|
||||
"fr": "Frans",
|
||||
"pt": "Portugees",
|
||||
"ru": "Russisch"
|
||||
"ru": "Russisch",
|
||||
"es": "Spaans"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start met afsluiten van Cursor",
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"de": "Alemão",
|
||||
"fr": "Francês",
|
||||
"pt": "Português do Brasil",
|
||||
"ru": "Russo"
|
||||
"ru": "Russo",
|
||||
"es": "Espanhol"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Iniciando fechamento do Cursor",
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"de": "Немецкий",
|
||||
"fr": "Французский",
|
||||
"pt": "Бразильский португальский",
|
||||
"ru": "Русский"
|
||||
"ru": "Русский",
|
||||
"es": "Испанский"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Начало закрытия Cursor",
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish"
|
||||
"tr": "Turkish",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Cursor'dan Çıkış Başlatılıyor",
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"de": "Tiếng Đức",
|
||||
"fr": "Tiếng Pháp",
|
||||
"pt": "Tiếng Bồ Đào Nha",
|
||||
"ru": "Tiếng Nga"
|
||||
"ru": "Tiếng Nga",
|
||||
"es": "Tiếng Tây Ban Nha"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Bắt Đầu Thoát Cursor",
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
"temp_github_register": "临时GitHub注册",
|
||||
"admin_required": "运行可执行文件,需要管理员权限",
|
||||
"admin_required_continue": "继续使用当前版本...",
|
||||
"coming_soon": "即将推出"
|
||||
"coming_soon": "即将推出",
|
||||
"fixed_soon": "即将修复",
|
||||
"contribute": "贡献项目",
|
||||
"config": "显示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英语",
|
||||
@@ -34,7 +37,8 @@
|
||||
"pt": "葡萄牙语",
|
||||
"ru": "俄语",
|
||||
"tr": "土耳其语",
|
||||
"bg": "保加利亚语"
|
||||
"bg": "保加利亚语",
|
||||
"es": "西班牙语"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "开始退出 Cursor",
|
||||
@@ -101,7 +105,13 @@
|
||||
"package_not_found": "package.json未找到: {path}",
|
||||
"check_version_failed": "检查版本失败: {error}",
|
||||
"stack_trace": "堆栈跟踪",
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||
"no_write_permission": "没有写入权限: {path}",
|
||||
"path_not_found": "路径未找到: {path}",
|
||||
"modify_file_failed": "修改文件失败: {error}",
|
||||
"windows_machine_id_updated": "Windows机器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor 注册工具",
|
||||
@@ -278,7 +288,14 @@
|
||||
"removing_directory": "删除目录",
|
||||
"directory_removed": "目录已删除",
|
||||
"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": {
|
||||
"checking": "检查更新...",
|
||||
@@ -291,7 +308,8 @@
|
||||
"update_skipped": "跳过更新。",
|
||||
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
|
||||
"development_version": "开发版本 {current} > {latest}",
|
||||
"changelog_title": "更新日志"
|
||||
"changelog_title": "更新日志",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
@@ -431,5 +449,66 @@
|
||||
"completed_successfully": "GitHub + Cursor 注册成功",
|
||||
"registration_encountered_issues": "GitHub + Cursor 注册遇到问题",
|
||||
"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": "基础使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久访问已启用"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
"configuration": "配置",
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"config_directory": "配置目录",
|
||||
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目录",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "请确保 Cursor 已安装并至少运行一次",
|
||||
"storage_directory_not_found": "未找到存储目录",
|
||||
"storage_file_found": "找到存储文件",
|
||||
"file_size": "文件大小",
|
||||
"file_permissions": "文件权限",
|
||||
"file_owner": "文件所有者",
|
||||
"file_group": "文件组",
|
||||
"error_getting_file_stats": "获取文件统计信息时出错",
|
||||
"permission_denied": "权限拒绝",
|
||||
"try_running": "尝试运行 'cursor --help' 检查是否已安装",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "存储文件为空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
|
||||
"storage_file_not_found": "未找到存储文件",
|
||||
"error_checking_linux_paths": "检查 Linux 路径时出错",
|
||||
"config_option_added": "添加配置选项",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已创建",
|
||||
"config_setup_error": "配置设置错误"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,10 @@
|
||||
"temp_github_register": "臨時GitHub註冊",
|
||||
"admin_required": "運行可執行文件,需要管理員權限",
|
||||
"admin_required_continue": "繼續使用當前版本...",
|
||||
"coming_soon": "即將推出"
|
||||
"coming_soon": "即將推出",
|
||||
"fixed_soon": "即將修復",
|
||||
"contribute": "貢獻項目",
|
||||
"config": "顯示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英文",
|
||||
@@ -32,7 +35,8 @@
|
||||
"pt": "葡萄牙文",
|
||||
"ru": "俄文",
|
||||
"tr": "土耳其文",
|
||||
"bg": "保加利亞文"
|
||||
"bg": "保加利亞文",
|
||||
"es": "西班牙文"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "開始退出 Cursor",
|
||||
@@ -99,7 +103,13 @@
|
||||
"package_not_found": "package.json未找到: {path}",
|
||||
"check_version_failed": "檢查版本失敗: {error}",
|
||||
"stack_trace": "堆疊跟踪",
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||
"no_write_permission": "沒有寫入權限: {path}",
|
||||
"path_not_found": "路徑未找到: {path}",
|
||||
"modify_file_failed": "修改文件失敗: {error}",
|
||||
"windows_machine_id_updated": "Windows機器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}"
|
||||
},
|
||||
|
||||
"register": {
|
||||
@@ -257,7 +267,14 @@
|
||||
"removing_directory": "刪除目錄",
|
||||
"directory_removed": "目錄已刪除",
|
||||
"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": {
|
||||
"checking": "檢查更新...",
|
||||
@@ -270,7 +287,8 @@
|
||||
"update_skipped": "跳過更新。",
|
||||
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
|
||||
"development_version": "開發版本 {current} > {latest}",
|
||||
"changelog_title": "更新日誌"
|
||||
"changelog_title": "更新日誌",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
@@ -410,5 +428,66 @@
|
||||
"completed_successfully": "GitHub + Cursor 註冊成功",
|
||||
"registration_encountered_issues": "GitHub + Cursor 註冊遇到問題",
|
||||
"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": "基礎使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久訪問已啟用"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
"configuration": "配置",
|
||||
"enabled": "已啟用",
|
||||
"disabled": "已禁用",
|
||||
"config_directory": "配置目錄",
|
||||
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目錄",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "請確保 Cursor 已安裝並至少運行一次",
|
||||
"storage_directory_not_found": "未找到儲存目錄",
|
||||
"storage_file_found": "找到儲存文件",
|
||||
"file_size": "文件大小",
|
||||
"file_permissions": "文件權限",
|
||||
"file_owner": "文件所有者",
|
||||
"file_group": "文件組",
|
||||
"error_getting_file_stats": "獲取文件統計信息時出錯",
|
||||
"permission_denied": "權限拒絕",
|
||||
"try_running": "嘗試運行 'cursor --help' 檢查是否已安裝",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "儲存文件為空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
|
||||
"storage_file_not_found": "未找到儲存文件",
|
||||
"error_checking_linux_paths": "檢查 Linux 路徑時出錯",
|
||||
"config_option_added": "添加配置選項",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已創建",
|
||||
"config_setup_error": "配置設置錯誤"
|
||||
|
||||
}
|
||||
}
|
||||
10
logo.py
10
logo.py
@@ -20,7 +20,7 @@ init()
|
||||
# get terminal width
|
||||
def get_terminal_width():
|
||||
try:
|
||||
columns, _ = shutil.get_terminal_size()
|
||||
columns, _ = shutil.get_terminal_size()/2
|
||||
return columns
|
||||
except:
|
||||
return 80 # default width
|
||||
@@ -34,7 +34,7 @@ def center_multiline_text(text, handle_chinese=False):
|
||||
for line in lines:
|
||||
# calculate actual display width (remove ANSI color codes)
|
||||
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, '')
|
||||
|
||||
# remove all ANSI escape sequences to get the actual length
|
||||
@@ -83,7 +83,7 @@ muhammedfurkan plamkatawe
|
||||
"""
|
||||
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
||||
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
|
||||
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
|
||||
@@ -94,8 +94,8 @@ CURSOR_OTHER_INFO = center_multiline_text(OTHER_INFO_TEXT, handle_chinese=True)
|
||||
def print_logo():
|
||||
print(CURSOR_LOGO)
|
||||
print(CURSOR_DESCRIPTION)
|
||||
print(CURSOR_CONTRIBUTORS)
|
||||
# print(CURSOR_CONTRIBUTORS)
|
||||
print(CURSOR_OTHER_INFO)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print_logo()
|
||||
print_logo()
|
||||
|
||||
155
main.py
155
main.py
@@ -9,12 +9,14 @@ import locale
|
||||
import platform
|
||||
import requests
|
||||
import subprocess
|
||||
from config import get_config
|
||||
from config import get_config
|
||||
import shutil
|
||||
import re
|
||||
|
||||
# Only import windll on Windows systems
|
||||
if platform.system() == 'Windows':
|
||||
import ctypes
|
||||
# 只在 Windows 上导入 windll
|
||||
# Only import windll on Windows systems
|
||||
from ctypes import windll
|
||||
|
||||
# Initialize colorama
|
||||
@@ -32,7 +34,13 @@ EMOJI = {
|
||||
"ARROW": "➜",
|
||||
"LANG": "🌐",
|
||||
"UPDATE": "🔄",
|
||||
"ADMIN": "🔐"
|
||||
"ADMIN": "🔐",
|
||||
"AIRDROP": "💰",
|
||||
"ROCKET": "🚀",
|
||||
"STAR": "⭐",
|
||||
"SUN": "🌟",
|
||||
"CONTRIBUTE": "🤝",
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
# Function to check if running as frozen executable
|
||||
@@ -242,22 +250,111 @@ translator = Translator()
|
||||
|
||||
def print_menu():
|
||||
"""Print menu options"""
|
||||
try:
|
||||
config = get_config()
|
||||
if config.getboolean('Utils', 'enabled_account_info'):
|
||||
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"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})")
|
||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. 🌟 {translator.get('menu.register_google')}")
|
||||
print(f"{Fore.YELLOW} ┗━━ 🔥 {translator.get('menu.lifetime_access_enabled')} 🔥{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}4{Style.RESET_ALL}. ⭐ {translator.get('menu.register_github')}")
|
||||
print(f"{Fore.YELLOW} ┗━━ 🚀 {translator.get('menu.lifetime_access_enabled')} 🚀{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
||||
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}")
|
||||
print(f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
||||
print(f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
||||
print(f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
|
||||
print(f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
|
||||
print(f"{Fore.YELLOW}{'─' * 70}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{'─' * 110}{Style.RESET_ALL}")
|
||||
|
||||
# Get terminal width
|
||||
try:
|
||||
terminal_width = shutil.get_terminal_size().columns
|
||||
except:
|
||||
terminal_width = 80 # Default width
|
||||
|
||||
# Define all menu items
|
||||
menu_items = {
|
||||
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
|
||||
1: f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}",
|
||||
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})",
|
||||
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
|
||||
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
|
||||
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
|
||||
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
|
||||
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
|
||||
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
|
||||
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
|
||||
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
|
||||
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}"
|
||||
}
|
||||
|
||||
# Automatically calculate the number of menu items in the left and right columns
|
||||
total_items = len(menu_items)
|
||||
left_column_count = (total_items + 1) // 2 # The number of options displayed on the left (rounded up)
|
||||
|
||||
# Build left and right columns of menus
|
||||
sorted_indices = sorted(menu_items.keys())
|
||||
left_menu = [menu_items[i] for i in sorted_indices[:left_column_count]]
|
||||
right_menu = [menu_items[i] for i in sorted_indices[left_column_count:]]
|
||||
|
||||
# Calculate the maximum display width of left menu items
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
|
||||
def get_display_width(s):
|
||||
"""Calculate the display width of a string, considering Chinese characters and emojis"""
|
||||
# Remove ANSI color codes
|
||||
clean_s = ansi_escape.sub('', s)
|
||||
width = 0
|
||||
for c in clean_s:
|
||||
# Chinese characters and some emojis occupy two character widths
|
||||
if ord(c) > 127:
|
||||
width += 2
|
||||
else:
|
||||
width += 1
|
||||
return width
|
||||
|
||||
max_left_width = 0
|
||||
for item in left_menu:
|
||||
width = get_display_width(item)
|
||||
max_left_width = max(max_left_width, width)
|
||||
|
||||
# Set the starting position of right menu
|
||||
fixed_spacing = 4 # Fixed spacing
|
||||
right_start = max_left_width + fixed_spacing
|
||||
|
||||
# Calculate the number of spaces needed for right menu items
|
||||
spaces_list = []
|
||||
for i in range(len(left_menu)):
|
||||
if i < len(left_menu):
|
||||
left_item = left_menu[i]
|
||||
left_width = get_display_width(left_item)
|
||||
spaces = right_start - left_width
|
||||
spaces_list.append(spaces)
|
||||
|
||||
# Print menu items
|
||||
max_rows = max(len(left_menu), len(right_menu))
|
||||
|
||||
for i in range(max_rows):
|
||||
# Print left menu items
|
||||
if i < len(left_menu):
|
||||
left_item = left_menu[i]
|
||||
print(left_item, end='')
|
||||
|
||||
# Use pre-calculated spaces
|
||||
spaces = spaces_list[i]
|
||||
else:
|
||||
# If left side has no items, print only spaces
|
||||
spaces = right_start
|
||||
print('', end='')
|
||||
|
||||
# Print right menu items
|
||||
if i < len(right_menu):
|
||||
print(' ' * spaces + right_menu[i])
|
||||
else:
|
||||
print() # Change line
|
||||
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
|
||||
print(f"{Fore.YELLOW}{'─' * 70}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{'─' * 110}{Style.RESET_ALL}")
|
||||
|
||||
def select_language():
|
||||
"""Language selection menu"""
|
||||
@@ -297,6 +394,11 @@ def check_latest_version():
|
||||
timeout=10
|
||||
)
|
||||
|
||||
# Check if rate limit exceeded
|
||||
if response.status_code == 403 and "rate limit exceeded" in response.text.lower():
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Skipping update check.')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
# Check if response is successful
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"GitHub API returned status code {response.status_code}")
|
||||
@@ -448,12 +550,14 @@ def main():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
check_latest_version() # Add version check before showing menu
|
||||
if config.getboolean('Utils', 'enabled_update_check'):
|
||||
check_latest_version() # Add version check before showing menu
|
||||
print_menu()
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-10')}: {Style.RESET_ALL}")
|
||||
choice_num = 12
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
|
||||
|
||||
if choice == "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
@@ -498,7 +602,16 @@ def main():
|
||||
print_menu()
|
||||
elif choice == "10":
|
||||
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_menu()
|
||||
elif choice == "11":
|
||||
import logo
|
||||
print(logo.CURSOR_CONTRIBUTORS)
|
||||
print_menu()
|
||||
elif choice == "12":
|
||||
from config import print_config
|
||||
print_config(get_config(), translator)
|
||||
print_menu()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
|
||||
@@ -203,9 +203,10 @@ def setup_driver(translator=None):
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
# Set random port
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Set random port
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
# Set random port
|
||||
co.auto_port()
|
||||
|
||||
|
||||
@@ -107,6 +107,10 @@ class NewTempEmail:
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ EMOJI = {
|
||||
}
|
||||
|
||||
class OAuthHandler:
|
||||
def __init__(self, translator=None):
|
||||
def __init__(self, translator=None, auth_type=None):
|
||||
self.translator = translator
|
||||
self.config = get_config(translator)
|
||||
self.auth_type = auth_type # make sure the auth_type is not None
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
|
||||
@@ -380,7 +381,7 @@ class OAuthHandler:
|
||||
if self._delete_current_account():
|
||||
# Start new authentication based on auth type
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
|
||||
if auth_type == "google":
|
||||
if self.auth_type == "google":
|
||||
return self.handle_google_auth()
|
||||
else: # github
|
||||
return self.handle_github_auth()
|
||||
@@ -839,7 +840,7 @@ def main(auth_type, translator=None):
|
||||
auth_type (str): Type of authentication ('google' or 'github')
|
||||
translator: Translator instance for internationalization
|
||||
"""
|
||||
handler = OAuthHandler(translator)
|
||||
handler = OAuthHandler(translator, auth_type)
|
||||
|
||||
if auth_type.lower() == 'google':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start')}{Style.RESET_ALL}")
|
||||
|
||||
@@ -25,7 +25,8 @@ EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"RESET": "<EFBFBD><EFBFBD>",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
@@ -37,10 +38,41 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
|
||||
# Default paths for different systems
|
||||
default_paths = {
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app")]
|
||||
}
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
if not config.has_section(section):
|
||||
config.add_section(section)
|
||||
|
||||
config.read(config_file, encoding='utf-8') # Specify encoding
|
||||
if system == "Darwin":
|
||||
config.set('MacPaths', 'cursor_path', default_paths["Darwin"])
|
||||
elif system == "Windows":
|
||||
config.set('WindowsPaths', 'cursor_path', default_paths["Windows"])
|
||||
elif system == "Linux":
|
||||
# For Linux, try to find the first existing path
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
config.set('LinuxPaths', 'cursor_path', path)
|
||||
break
|
||||
else:
|
||||
# If no path exists, use the first one as default
|
||||
config.set('LinuxPaths', 'cursor_path', default_paths["Linux"][0])
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
else:
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# Get path based on system
|
||||
if system == "Darwin":
|
||||
@@ -51,15 +83,26 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
section = 'LinuxPaths'
|
||||
else:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
|
||||
if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
|
||||
raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
|
||||
|
||||
|
||||
base_path = config.get(section, 'cursor_path')
|
||||
|
||||
# For Linux, try to find the first existing path if the configured one doesn't exist
|
||||
if system == "Linux" and not os.path.exists(base_path):
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
base_path = path
|
||||
# Update config with the found path
|
||||
config.set(section, 'cursor_path', path)
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
break
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
|
||||
|
||||
|
||||
pkg_path = os.path.join(base_path, "package.json")
|
||||
main_path = os.path.join(base_path, "out/main.js")
|
||||
|
||||
@@ -534,6 +577,7 @@ class MachineIDResetter:
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self._update_windows_machine_guid()
|
||||
self._update_windows_machine_id()
|
||||
elif sys.platform == "darwin":
|
||||
self._update_macos_platform_uuid(new_ids)
|
||||
|
||||
@@ -563,6 +607,45 @@ class MachineIDResetter:
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def _update_windows_machine_id(self):
|
||||
"""Update Windows MachineId in SQMClient registry"""
|
||||
try:
|
||||
import winreg
|
||||
# 1. Generate new GUID
|
||||
new_guid = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.new_machine_id')}: {new_guid}{Style.RESET_ALL}")
|
||||
|
||||
# 2. Open the registry key
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
except FileNotFoundError:
|
||||
# If the key does not exist, create it
|
||||
key = winreg.CreateKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient"
|
||||
)
|
||||
|
||||
# 3. Set MachineId value
|
||||
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_id_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
|
||||
def _update_macos_platform_uuid(self, new_ids):
|
||||
"""Update macOS Platform UUID"""
|
||||
@@ -621,13 +704,10 @@ class MachineIDResetter:
|
||||
self.update_system_ids(new_ids)
|
||||
|
||||
|
||||
### Remove In v1.7.02
|
||||
# Modify workbench.desktop.main.js
|
||||
|
||||
# workbench_path = get_workbench_cursor_path(self.translator)
|
||||
# modify_workbench_js(workbench_path, self.translator)
|
||||
workbench_path = get_workbench_cursor_path(self.translator)
|
||||
modify_workbench_js(workbench_path, self.translator)
|
||||
|
||||
### Remove In v1.7.02
|
||||
# Check Cursor version and perform corresponding actions
|
||||
|
||||
greater_than_0_45 = check_cursor_version(self.translator)
|
||||
|
||||
@@ -2,195 +2,508 @@ import os
|
||||
import shutil
|
||||
import platform
|
||||
import time
|
||||
import sys
|
||||
import glob
|
||||
import json
|
||||
import uuid
|
||||
import random
|
||||
import string
|
||||
import re
|
||||
from datetime import datetime
|
||||
import subprocess
|
||||
from colorama import Fore, Style, init
|
||||
from main import translator
|
||||
from main import EMOJI
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji and color constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"MENU": "📋",
|
||||
"ARROW": "➜",
|
||||
"LANG": "🌐",
|
||||
"UPDATE": "🔄",
|
||||
"ADMIN": "🔐",
|
||||
"STOP": "🛑",
|
||||
"DISCLAIMER": "⚠️",
|
||||
"WARNING": "⚠️"
|
||||
}
|
||||
|
||||
def delete_directory(path, translator=None):
|
||||
"""Deletes a directory and all its contents."""
|
||||
def display_banner():
|
||||
"""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):
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.removed', path=path)}{Style.RESET_ALL}")
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.deleted', path=path)} {Style.RESET_ALL}")
|
||||
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:
|
||||
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):
|
||||
"""Deletes a file if it exists."""
|
||||
def remove_file(path):
|
||||
"""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):
|
||||
try:
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
"""Resets the machine ID to a new UUID."""
|
||||
new_id = str(uuid.uuid4())
|
||||
if platform.system() == "Windows":
|
||||
try:
|
||||
subprocess.run(
|
||||
["reg", "add", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid", "/d", new_id, "/f"],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
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:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_guid', error=e)}{Style.RESET_ALL}")
|
||||
elif platform.system() == "Linux":
|
||||
machine_id_paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"]
|
||||
for path in machine_id_paths:
|
||||
if os.path.exists(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']:
|
||||
def is_cursor_related(path):
|
||||
"""
|
||||
Safety function to verify a path is related to Cursor before deletion.
|
||||
Returns True if the path appears to be related to Cursor AI.
|
||||
"""
|
||||
# Skip .vscode check as it's shared with VS Code
|
||||
if path.endswith(".vscode"):
|
||||
return False
|
||||
|
||||
# Check if path contains cursor-related terms
|
||||
cursor_terms = ["cursor", "cursorai", "cursor-electron"]
|
||||
|
||||
# Convert path to lowercase for case-insensitive matching
|
||||
lower_path = path.lower()
|
||||
|
||||
# Return True if any cursor term is present in the path
|
||||
for term in cursor_terms:
|
||||
if term in lower_path:
|
||||
return True
|
||||
elif response in ['no', 'n']:
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.RED}{translator.get('totally_reset.invalid_choice')}{Style.RESET_ALL}")
|
||||
|
||||
# Check specific known Cursor file patterns
|
||||
cursor_patterns = [
|
||||
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):
|
||||
print(f"\n{Fore.GREEN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor')}\n")
|
||||
def find_cursor_license_files(base_path, pattern):
|
||||
"""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
|
||||
paths = []
|
||||
if platform.system() == "Linux":
|
||||
paths = [
|
||||
os.path.expanduser("~/.cursor"),
|
||||
os.path.expanduser("~/.local/share/cursor"),
|
||||
os.path.expanduser("~/.config/cursor"),
|
||||
os.path.expanduser("~/.cache/cursor"),
|
||||
"/usr/local/bin/cursor",
|
||||
"/opt/cursor",
|
||||
"/usr/bin/cursor",
|
||||
os.path.expanduser("~/.cursor/machine-id.db"),
|
||||
os.path.expanduser("~/.local/share/Cursor"),
|
||||
os.path.expanduser("~/.config/Cursor"),
|
||||
os.path.expanduser("~/.cache/Cursor")
|
||||
def generate_new_machine_id():
|
||||
"""Generates a new random machine ID."""
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def create_fake_machine_id(path):
|
||||
"""Creates a new machine ID file with random ID."""
|
||||
if not is_cursor_related(path):
|
||||
return
|
||||
|
||||
try:
|
||||
new_id = 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:
|
||||
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
|
||||
paths = [
|
||||
os.path.expanduser("~/Library/Application Support/Cursor"),
|
||||
os.path.expanduser("~/Library/Caches/Cursor"),
|
||||
"/Applications/Cursor.app",
|
||||
os.path.expanduser("~/Library/Preferences/com.cursor.app.plist"),
|
||||
elif system == "Darwin": # macOS
|
||||
machine_id_paths = [
|
||||
os.path.join(home, "Library", "Application Support", "Cursor", "cursor-machine-id"),
|
||||
os.path.join(home, "Library", "Application Support", "cursor-electron", "cursor-machine-id"),
|
||||
os.path.join(home, ".cursor-machine-id"),
|
||||
]
|
||||
elif platform.system() == "Windows":
|
||||
paths = [
|
||||
os.path.expanduser("~\\AppData\\Local\\Cursor"),
|
||||
os.path.expanduser("~\\AppData\\Roaming\\Cursor"),
|
||||
os.path.expanduser("~\\.cursor"),
|
||||
os.path.expanduser("~\\.config\\Cursor"),
|
||||
os.path.expanduser("~\\.cache\\Cursor"),
|
||||
"C:\\Program Files\\Cursor",
|
||||
"C:\\Program Files (x86)\\Cursor",
|
||||
"C:\\Users\\%USERNAME%\\AppData\\Local\\Cursor",
|
||||
"C:\\Users\\%USERNAME%\\AppData\\Roaming\\Cursor",
|
||||
elif system == "Linux":
|
||||
machine_id_paths = [
|
||||
os.path.join(home, ".config", "Cursor", "cursor-machine-id"),
|
||||
os.path.join(home, ".config", "cursor-electron", "cursor-machine-id"),
|
||||
os.path.join(home, ".cursor-machine-id"),
|
||||
]
|
||||
|
||||
# First remove existing machine IDs
|
||||
for path in machine_id_paths:
|
||||
remove_file(path)
|
||||
|
||||
# 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
|
||||
for path in paths:
|
||||
delete_directory(path, translator)
|
||||
elif system == "Linux":
|
||||
cursor_paths = [
|
||||
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
|
||||
files = [
|
||||
os.path.expanduser("~/.cursor/machine-id.db"),
|
||||
os.path.expanduser("~/.local/share/cursor.db"),
|
||||
os.path.expanduser("~/.config/cursor/preferences.json"),
|
||||
os.path.expanduser("~/.cache/cursor.log"),
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')} {Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
# Remove main Cursor directories
|
||||
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:
|
||||
delete_file(file, translator)
|
||||
# Direct known trial file paths
|
||||
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)
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.deep_scanning')}")
|
||||
base_dirs = ["/tmp", "/var/tmp", os.path.expanduser("~")] # Linux and macOS
|
||||
if platform.system() == "Windows":
|
||||
base_dirs = ["C:\\Temp", "C:\\Windows\\Temp", os.path.expanduser("~")] # Windows
|
||||
# OS-specific known trial/license files
|
||||
if system == "Windows":
|
||||
cursor_trial_files.extend([
|
||||
os.path.join(home, "AppData", "Local", "Cursor", "trial_info.json"),
|
||||
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:
|
||||
for root, dirs, files in os.walk(base):
|
||||
for dir in dirs:
|
||||
if "cursor" in dir.lower():
|
||||
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)
|
||||
# Remove known trial/license files
|
||||
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_known')} {Style.RESET_ALL}")
|
||||
for path in cursor_trial_files:
|
||||
remove_file(path)
|
||||
|
||||
# Reset machine ID
|
||||
reset_machine_id(translator)
|
||||
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.cursor_reset_completed')}")
|
||||
|
||||
def main(translator=None):
|
||||
start_time = time.time()
|
||||
# Deep search for additional trial/license files
|
||||
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.performing_deep_scan')} {Style.RESET_ALL}")
|
||||
all_found_files = []
|
||||
for base_path in license_search_paths:
|
||||
if os.path.exists(base_path):
|
||||
found_files = find_cursor_license_files(base_path, file_patterns)
|
||||
all_found_files.extend(found_files)
|
||||
|
||||
# Display features and warnings
|
||||
display_features_and_warnings(translator)
|
||||
|
||||
# Get user confirmation
|
||||
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}")
|
||||
if all_found_files:
|
||||
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:
|
||||
remove_file(file_path)
|
||||
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__':
|
||||
from main import translator
|
||||
main(translator)
|
||||
# Check for and remove localStorage files that might contain settings
|
||||
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.checking_for_electron_localstorage_files')} {Style.RESET_ALL}")
|
||||
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