mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-27 14:10:38 +03:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d7c1c496 | ||
|
|
a56b978669 | ||
|
|
9793b91bc7 | ||
|
|
735dd8c1eb | ||
|
|
e5d192efcb | ||
|
|
a4d5b0e467 | ||
|
|
d7b056b339 | ||
|
|
bd152be4e8 | ||
|
|
172d9fb4bb | ||
|
|
e2d70c1738 | ||
|
|
650e2d33db | ||
|
|
2d1347c8c6 | ||
|
|
67d3554664 | ||
|
|
cb202e08e5 | ||
|
|
0c0ec47432 | ||
|
|
f00678ddcb | ||
|
|
dbc220053d | ||
|
|
d7ab362c4c | ||
|
|
4448a62458 | ||
|
|
50d09e5172 | ||
|
|
ffff3bdeb1 | ||
|
|
816a09d4de | ||
|
|
e5b7e5727c | ||
|
|
087e3ebf78 | ||
|
|
bd96107911 | ||
|
|
17799e0209 | ||
|
|
c15ea25cb3 | ||
|
|
350d781ce8 |
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.17'
|
||||
default: '1.8.04'
|
||||
|
||||
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
|
||||
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,5 +1,28 @@
|
||||
# Change Log
|
||||
|
||||
## v1.8.04
|
||||
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
|
||||
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
|
||||
3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## 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 | 修復禁用自動更新
|
||||
|
||||
21
README.md
21
README.md
@@ -13,16 +13,15 @@
|
||||
[](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 register accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes,reset and wipe Cursor data and hardware info.
|
||||
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 cache and cookies. If possible, user a VPN to create new accounts.
|
||||
|
||||
Always clean your browser's cache and cookies. If possible, use a VPN to create new accounts.
|
||||
|
||||
|
||||
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||
@@ -101,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 | 注意事項
|
||||
|
||||
@@ -116,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]
|
||||
@@ -159,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
|
||||
167
config.py
167
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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +68,9 @@ def setup_config(translator=None):
|
||||
'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")),
|
||||
@@ -61,17 +80,112 @@ def setup_config(translator=None):
|
||||
'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
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
current_user = sudo_user if sudo_user else (os.getenv('USER') or os.getenv('USERNAME'))
|
||||
|
||||
if not current_user:
|
||||
current_user = os.path.expanduser('~').split('/')[-1]
|
||||
|
||||
# Handle sudo case
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
root_home = "/root"
|
||||
else:
|
||||
actual_home = f"/home/{current_user}"
|
||||
root_home = None
|
||||
|
||||
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 in both user and root locations
|
||||
cursor_dir = None
|
||||
possible_paths = [
|
||||
os.path.join(config_base, "Cursor"),
|
||||
os.path.join(config_base, "cursor"),
|
||||
os.path.join(root_home, ".config", "Cursor") if root_home else None,
|
||||
os.path.join(root_home, ".config", "cursor") if root_home else None
|
||||
]
|
||||
|
||||
for path in possible_paths:
|
||||
if path and os.path.exists(path):
|
||||
cursor_dir = path
|
||||
break
|
||||
|
||||
if not cursor_dir:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base) if translator else f'Neither Cursor nor cursor directory found in {config_base}'}{Style.RESET_ALL}")
|
||||
if root_home:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.also_checked', path=f'{root_home}/.config') if translator else f'Also checked {root_home}/.config'}{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') if translator else '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) if translator else f'Storage directory not found: {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') if translator else '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) if translator else f'Storage file found: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', size=stat.st_size) if translator else f'File size: {stat.st_size} bytes'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', permissions=oct(stat.st_mode & 0o777)) if translator else f'File permissions: {oct(stat.st_mode & 0o777)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', owner=stat.st_uid) if translator else f'File owner: {stat.st_uid}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', group=stat.st_gid) if translator else f'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)) if translator else f'Error getting file stats: {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) if translator else f'Permission denied: {storage_path}'}{Style.RESET_ALL}")
|
||||
if sudo_user:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {sudo_user}:{sudo_user} {storage_path}') if translator else f'Try running: chown {sudo_user}:{sudo_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {current_user}:{current_user} {storage_path}') if translator else f'Try running: chown {current_user}:{current_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {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) if translator else f'Storage file is empty: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted, please reinstall Cursor'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data') if translator else '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)) if translator else f'Error reading storage file: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else '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) if translator else f'Storage file not found: {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') if translator else '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)) if translator else f'Error checking Linux paths: {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"),
|
||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||
'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
|
||||
@@ -88,13 +202,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('config.config_option_added', option=f'{section}.{option}') if translator else f'Config option added: {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('config.config_updated') if translator else 'Config updated'}{Style.RESET_ALL}")
|
||||
else:
|
||||
for section, options in default_config.items():
|
||||
config.add_section(section)
|
||||
@@ -104,16 +218,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('config.config_created', config_file=config_file) if translator else f'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('config.config_setup_error', error=str(e)) if translator else f'Error setting up config: {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') if translator else 'Configuration not available'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration') if translator else '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') if translator else 'Enabled'}{Style.RESET_ALL}"
|
||||
elif value.lower() in ('false', 'no', 'off', '0'):
|
||||
value_display = f"{Fore.RED}{translator.get('config.disabled') if translator else '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') if translator else 'Config Directory'}: {config_dir}{Style.RESET_ALL}")
|
||||
|
||||
print()
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
|
||||
@@ -64,12 +64,12 @@ class UsageManager:
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# 获取 GPT-4 使用量和限制
|
||||
# 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)
|
||||
|
||||
# 获取 GPT-3.5 使用量,但将限制设为 "No Limit"
|
||||
# get Basic usage, but set limit to "No Limit"
|
||||
gpt35_data = data.get("gpt-3.5-turbo", {})
|
||||
basic_usage = gpt35_data.get("numRequestsTotal", 0)
|
||||
|
||||
@@ -77,10 +77,15 @@ class UsageManager:
|
||||
'premium_usage': premium_usage,
|
||||
'max_premium_usage': max_premium_usage,
|
||||
'basic_usage': basic_usage,
|
||||
'max_basic_usage': "No Limit" # 将 GPT-3.5 的限制设为 "No Limit"
|
||||
'max_basic_usage': "No Limit" # set Basic limit to "No Limit"
|
||||
}
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"获取使用量失败: {str(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
|
||||
@@ -95,7 +100,7 @@ class UsageManager:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"获取订阅信息失败: {str(e)}")
|
||||
logger.error(f"Get subscription info failed: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_token_from_config():
|
||||
@@ -126,7 +131,7 @@ def get_token_from_config():
|
||||
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"获取配置路径失败: {str(e)}")
|
||||
logger.error(f"Get config path failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
@@ -339,9 +344,9 @@ def get_email_from_sqlite(sqlite_path):
|
||||
|
||||
def display_account_info(translator=None):
|
||||
"""display account info"""
|
||||
print(f"\n{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||
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}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
|
||||
# get token
|
||||
token = get_token()
|
||||
@@ -363,7 +368,11 @@ def display_account_info(translator=None):
|
||||
email = get_email_from_sqlite(paths['sqlite_path'])
|
||||
|
||||
# get subscription info
|
||||
subscription_info = UsageManager.get_stripe_profile(token)
|
||||
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:
|
||||
@@ -371,32 +380,43 @@ def display_account_info(translator=None):
|
||||
if 'customer' in subscription_info and 'email' in subscription_info['customer']:
|
||||
email = subscription_info['customer']['email']
|
||||
|
||||
# get usage info
|
||||
usage_info = UsageManager.get_usage(token)
|
||||
# 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
|
||||
|
||||
# display account info
|
||||
# Prepare left and right info
|
||||
left_info = []
|
||||
right_info = []
|
||||
|
||||
# Left side shows account info
|
||||
if email:
|
||||
print(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
|
||||
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
|
||||
|
||||
# display subscription type
|
||||
# Add an empty line
|
||||
# left_info.append("")
|
||||
|
||||
# Show subscription type
|
||||
if subscription_info:
|
||||
subscription_type = format_subscription_type(subscription_info)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
|
||||
|
||||
# display remaining trial days
|
||||
# Show remaining trial days
|
||||
days_remaining = subscription_info.get("daysRemainingOnTrial")
|
||||
if days_remaining is not None and days_remaining > 0:
|
||||
print(f"{Fore.GREEN}{EMOJI['TIME']} {translator.get('account_info.trial_remaining') if translator else 'Remaining Pro Trial'}: {Fore.WHITE}{days_remaining} {translator.get('account_info.days') if translator else 'days'}{Style.RESET_ALL}")
|
||||
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:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.subscription_not_found') if translator else 'Subscription information not found'}{Style.RESET_ALL}")
|
||||
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}")
|
||||
|
||||
# display usage info
|
||||
# Right side shows usage info - only if available
|
||||
if usage_info:
|
||||
print(f"\n{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
|
||||
right_info.append(f"{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
|
||||
|
||||
# GPT-4 usage
|
||||
# Premium usage
|
||||
premium_usage = usage_info.get('premium_usage', 0)
|
||||
max_premium_usage = usage_info.get('max_premium_usage', "No Limit")
|
||||
|
||||
@@ -425,7 +445,7 @@ def display_account_info(translator=None):
|
||||
|
||||
premium_display = f"{premium_usage}/{max_premium_usage} ({premium_percentage:.1f}%)"
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['PREMIUM']} {translator.get('account_info.premium_usage') if translator else 'Fast Response'}: {premium_color}{premium_display}{Style.RESET_ALL}")
|
||||
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)
|
||||
@@ -456,11 +476,70 @@ def display_account_info(translator=None):
|
||||
|
||||
basic_display = f"{basic_usage}/{max_basic_usage} ({basic_percentage:.1f}%)"
|
||||
|
||||
print(f"{Fore.BLUE}{EMOJI['BASIC']} {translator.get('account_info.basic_usage') if translator else 'Slow Response'}: {basic_color}{basic_display}{Style.RESET_ALL}")
|
||||
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:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.usage_not_found') if translator else 'Usage information not found'}{Style.RESET_ALL}")
|
||||
# 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
|
||||
|
||||
print(f"{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||
# 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"""
|
||||
|
||||
@@ -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()
|
||||
@@ -54,6 +56,45 @@ class AutoUpdateDisabler:
|
||||
}
|
||||
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"""
|
||||
try:
|
||||
@@ -181,6 +222,10 @@ class AutoUpdateDisabler:
|
||||
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
|
||||
|
||||
|
||||
114
locales/en.json
114
locales/en.json
@@ -22,7 +22,9 @@
|
||||
"admin_required": "Running as executable, administrator privileges required.",
|
||||
"admin_required_continue": "Continuing without administrator privileges.",
|
||||
"coming_soon": "Coming Soon",
|
||||
"fixed_soon": "Fixed Soon"
|
||||
"fixed_soon": "Fixed Soon",
|
||||
"contribute": "Contribute to the Project",
|
||||
"config": "Show Config"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -106,7 +108,10 @@
|
||||
"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}"
|
||||
"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",
|
||||
@@ -275,7 +280,13 @@
|
||||
"available_domains_loaded": "Available Domains Loaded: {count}",
|
||||
"domains_filtered": "Domains Filtered: {count}",
|
||||
"trying_to_create_email": "Trying to create email: {email}",
|
||||
"domain_blocked": "Domain Blocked: {domain}"
|
||||
"domain_blocked": "Domain Blocked: {domain}",
|
||||
"using_chrome_profile": "Using Chrome profile from: {user_data_dir}",
|
||||
"no_display_found": "No display found. Make sure X server is running.",
|
||||
"try_export_display": "Try: export DISPLAY=:0",
|
||||
"extension_load_error": "Extension Load Error: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "Make sure Chrome/Chromium is properly installed",
|
||||
"try_install_chromium": "Try: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "Disable Cursor Auto Update",
|
||||
@@ -308,7 +319,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",
|
||||
@@ -478,6 +490,96 @@
|
||||
"active": "Active",
|
||||
"inactive": "Inactive",
|
||||
"premium_usage": "Premium Usage",
|
||||
"basic_usage": "Basic 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} bytes",
|
||||
"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: {command}",
|
||||
"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}",
|
||||
"storage_file_is_valid_and_contains_data": "Storage file is valid and contains data",
|
||||
"error_reading_storage_file": "Error reading storage file: {error}",
|
||||
"also_checked": "Also checked {path}"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Authentication button not found",
|
||||
"authentication_failed": "Authentication failed: {error}",
|
||||
"found_cookies": "Found {count} cookies",
|
||||
"token_extraction_error": "Token extraction error: {error}",
|
||||
"authentication_successful": "Authentication successful - Email: {email}",
|
||||
"missing_authentication_data": "Missing authentication data: {data}",
|
||||
"failed_to_delete_account": "Failed to delete account: {error}",
|
||||
"invalid_authentication_type": "Invalid authentication type",
|
||||
"auth_update_success": "Auth update success",
|
||||
"browser_closed": "Browser closed",
|
||||
"auth_update_failed": "Auth update failed",
|
||||
"google_start": "Google start",
|
||||
"github_start": "Github start",
|
||||
"usage_count": "Usage count: {usage}",
|
||||
"account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
|
||||
"starting_new_authentication_process": "Starting new authentication process...",
|
||||
"failed_to_delete_expired_account": "Failed to delete expired account",
|
||||
"could_not_check_usage_count": "Could not check usage count: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"could_not_find_email": "Could not find email: {error}",
|
||||
"could_not_find_usage_count": "Could not find usage count: {error}",
|
||||
"already_on_settings_page": "Already on settings page!",
|
||||
"failed_to_extract_auth_info": "Failed to extract auth info: {error}",
|
||||
"no_chrome_profiles_found": "No Chrome profiles found, using Default",
|
||||
"found_default_chrome_profile": "Found Default Chrome profile",
|
||||
"using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
|
||||
"error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
|
||||
"initializing_browser_setup": "Initializing browser setup...",
|
||||
"detected_platform": "Detected platform: {platform}",
|
||||
"running_as_root_warning": "Running as root is not recommended for browser automation",
|
||||
"consider_running_without_sudo": "Consider running the script without sudo",
|
||||
"no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
|
||||
"supported_browsers": "Supported browsers for {platform}",
|
||||
"using_browser_profile": "Using browser profile: {profile}",
|
||||
"starting_browser": "Starting browser at: {path}",
|
||||
"browser_setup_completed": "Browser setup completed successfully",
|
||||
"browser_setup_failed": "Browser setup failed: {error}",
|
||||
"try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
|
||||
"redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Starting Google authentication...",
|
||||
"starting_github_authentication": "Starting GitHub authentication...",
|
||||
"waiting_for_authentication": "Waiting for authentication...",
|
||||
"page_changed_checking_auth": "Page changed, checking auth...",
|
||||
"status_check_error": "Status check error: {error}",
|
||||
"authentication_timeout": "Authentication timeout",
|
||||
"account_is_still_valid": "Account is still valid (Usage: {usage})",
|
||||
"starting_re_authentication_process": "Starting re-authentication process...",
|
||||
"starting_new_google_authentication": "Starting new Google authentication...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
|
||||
"navigating_to_authentication_page": "Navigating to authentication page...",
|
||||
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
|
||||
"found_browser_data_directory": "Found browser data directory: {path}",
|
||||
"authentication_successful_getting_account_info": "Authentication successful, getting account info...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@
|
||||
"admin_required": "运行可执行文件,需要管理员权限",
|
||||
"admin_required_continue": "继续使用当前版本...",
|
||||
"coming_soon": "即将推出",
|
||||
"fixed_soon": "即将修复"
|
||||
"fixed_soon": "即将修复",
|
||||
"contribute": "贡献项目",
|
||||
"config": "显示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英语",
|
||||
@@ -106,7 +108,10 @@
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||
"no_write_permission": "没有写入权限: {path}",
|
||||
"path_not_found": "路径未找到: {path}",
|
||||
"modify_file_failed": "修改文件失败: {error}"
|
||||
"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 注册工具",
|
||||
@@ -270,7 +275,13 @@
|
||||
"available_domains_loaded": "获取到 {count} 个可用域名",
|
||||
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
|
||||
"trying_to_create_email": "尝试创建邮箱: {email}",
|
||||
"domain_blocked": "域名被屏蔽: {domain}"
|
||||
"domain_blocked": "域名被屏蔽: {domain}",
|
||||
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
|
||||
"no_display_found": "未找到显示器。确保 X 服务器正在运行。",
|
||||
"try_export_display": "尝试: export DISPLAY=:0",
|
||||
"extension_load_error": "加载插件失败: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "确保 Chrome/Chromium 已正确安装",
|
||||
"try_install_chromium": "尝试: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "禁用 Cursor 自动更新",
|
||||
@@ -303,7 +314,8 @@
|
||||
"update_skipped": "跳过更新。",
|
||||
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
|
||||
"development_version": "开发版本 {current} > {latest}",
|
||||
"changelog_title": "更新日志"
|
||||
"changelog_title": "更新日志",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
@@ -473,7 +485,97 @@
|
||||
"active": "活跃",
|
||||
"inactive": "非活跃",
|
||||
"premium_usage": "高级使用量",
|
||||
"basic_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": "尝试运行: {command}",
|
||||
"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": "配置设置错误",
|
||||
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
|
||||
"error_reading_storage_file": "读取存储文件时出错",
|
||||
"also_checked": "也检查了 {path}"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到认证按钮",
|
||||
"authentication_failed": "认证失败: {error}",
|
||||
"found_cookies": "找到 {count} 个 Cookie",
|
||||
"token_extraction_error": "Token 提取错误: {error}",
|
||||
"authentication_successful": "认证成功 - 邮箱: {email}",
|
||||
"missing_authentication_data": "缺少认证数据: {data}",
|
||||
"failed_to_delete_account": "删除账户失败: {error}",
|
||||
"invalid_authentication_type": "无效的认证类型",
|
||||
"auth_update_success": "认证更新成功",
|
||||
"browser_closed": "浏览器已关闭",
|
||||
"auth_update_failed": "认证更新失败",
|
||||
"google_start": "Google 开始",
|
||||
"github_start": "Github 开始",
|
||||
"usage_count": "使用次数: {usage}",
|
||||
"account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "开始新的认证过程...",
|
||||
"failed_to_delete_expired_account": "删除过期账户失败",
|
||||
"could_not_check_usage_count": "无法检查使用次数: {error}",
|
||||
"found_email": "找到邮箱: {email}",
|
||||
"could_not_find_email": "未找到邮箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用次数: {error}",
|
||||
"already_on_settings_page": "已处于设置页面",
|
||||
"failed_to_extract_auth_info": "提取认证信息失败: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
|
||||
"found_default_chrome_profile": "找到默认 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化浏览器设置...",
|
||||
"detected_platform": "检测平台: {platform}",
|
||||
"running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
|
||||
"consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
|
||||
"no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的浏览器: {platform}",
|
||||
"using_browser_profile": "使用浏览器配置文件: {profile}",
|
||||
"starting_browser": "正在启动浏览器: {path}",
|
||||
"browser_setup_completed": "浏览器设置完成",
|
||||
"browser_setup_failed": "浏览器设置失败: {error}",
|
||||
"try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "开始 Google 认证...",
|
||||
"starting_github_authentication": "开始 Github 认证...",
|
||||
"waiting_for_authentication": "等待认证...",
|
||||
"page_changed_checking_auth": "页面改变, 检查认证...",
|
||||
"status_check_error": "状态检查错误: {error}",
|
||||
"authentication_timeout": "认证超时",
|
||||
"account_is_still_valid": "账户仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "开始重新认证过程...",
|
||||
"starting_new_google_authentication": "开始新的 Google 认证...",
|
||||
"failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
|
||||
"navigating_to_authentication_page": "正在导航到认证页面...",
|
||||
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
|
||||
"found_browser_data_directory": "找到浏览器数据目录: {path}",
|
||||
"authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,9 @@
|
||||
"admin_required": "運行可執行文件,需要管理員權限",
|
||||
"admin_required_continue": "繼續使用當前版本...",
|
||||
"coming_soon": "即將推出",
|
||||
"fixed_soon": "即將修復"
|
||||
"fixed_soon": "即將修復",
|
||||
"contribute": "貢獻項目",
|
||||
"config": "顯示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英文",
|
||||
@@ -104,7 +106,10 @@
|
||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||
"no_write_permission": "沒有寫入權限: {path}",
|
||||
"path_not_found": "路徑未找到: {path}",
|
||||
"modify_file_failed": "修改文件失敗: {error}"
|
||||
"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": {
|
||||
@@ -249,7 +254,14 @@
|
||||
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
|
||||
"available_domains_loaded": "獲取到 {count} 個可用域名",
|
||||
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
|
||||
"trying_to_create_email": "嘗試創建郵箱: {email}"
|
||||
"trying_to_create_email": "嘗試創建郵箱: {email}",
|
||||
"domain_blocked": "域名被屏蔽: {domain}",
|
||||
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
|
||||
"no_display_found": "未找到顯示器。確保 X 伺服器正在運行。",
|
||||
"try_export_display": "嘗試: export DISPLAY=:0",
|
||||
"extension_load_error": "加載插件失敗: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "確保 Chrome/Chromium 已正確安裝",
|
||||
"try_install_chromium": "嘗試: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "禁用 Cursor 自动更新",
|
||||
@@ -282,7 +294,8 @@
|
||||
"update_skipped": "跳過更新。",
|
||||
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
|
||||
"development_version": "開發版本 {current} > {latest}",
|
||||
"changelog_title": "更新日誌"
|
||||
"changelog_title": "更新日誌",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
@@ -452,6 +465,96 @@
|
||||
"active": "活躍",
|
||||
"inactive": "非活躍",
|
||||
"premium_usage": "高級使用量",
|
||||
"basic_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": "嘗試運行: {command}",
|
||||
"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": "配置設置錯誤",
|
||||
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
|
||||
"error_reading_storage_file": "讀取儲存文件時出錯",
|
||||
"also_checked": "也檢查了 {path}"
|
||||
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到認證按鈕",
|
||||
"authentication_failed": "認證失敗: {error}",
|
||||
"found_cookies": "找到 {count} 個 Cookie",
|
||||
"token_extraction_error": "Token 提取錯誤: {error}",
|
||||
"authentication_successful": "認證成功 - 郵箱: {email}",
|
||||
"missing_authentication_data": "缺少認證數據: {data}",
|
||||
"failed_to_delete_account": "刪除帳戶失敗: {error}",
|
||||
"invalid_authentication_type": "無效的認證類型",
|
||||
"auth_update_success": "認證更新成功",
|
||||
"browser_closed": "瀏覽器已關閉",
|
||||
"auth_update_failed": "認證更新失敗",
|
||||
"google_start": "Google 開始",
|
||||
"github_start": "Github 開始",
|
||||
"usage_count": "使用量: {usage}",
|
||||
"account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "開始新的認證過程...",
|
||||
"failed_to_delete_expired_account": "刪除過期帳戶失敗",
|
||||
"could_not_check_usage_count": "無法檢查使用量: {error}",
|
||||
"found_email": "找到郵箱: {email}",
|
||||
"could_not_find_email": "未找到郵箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用量: {erro r}",
|
||||
"already_on_settings_page": "已處於設置頁面",
|
||||
"failed_to_extract_auth_info": "提取認證信息失敗: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
|
||||
"found_default_chrome_profile": "找到默認 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化瀏覽器設置...",
|
||||
"detected_platform": "檢測平台: {platform}",
|
||||
"running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
|
||||
"consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
|
||||
"no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的瀏覽器: {platform}",
|
||||
"using_browser_profile": "使用瀏覽器配置文件: {profile}",
|
||||
"starting_browser": "正在啟動瀏覽器: {path}",
|
||||
"browser_setup_completed": "瀏覽器設置完成成功",
|
||||
"browser_setup_failed": "瀏覽器設置失敗: {error}",
|
||||
"try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_github_authentication": "開始 Github 認證...",
|
||||
"waiting_for_authentication": "等待認證...",
|
||||
"page_changed_checking_auth": "頁面改變, 檢查認證...",
|
||||
"status_check_error": "狀態檢查錯誤: {error}",
|
||||
"authentication_timeout": "認證超時",
|
||||
"account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "開始重新認證過程...",
|
||||
"starting_new_google_authentication": "開始新的 Google 認證...",
|
||||
"failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
|
||||
"navigating_to_authentication_page": "正在導航到認證頁面...",
|
||||
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
|
||||
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
|
||||
"authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
|
||||
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed": "瀏覽器啟動失敗: {error}"
|
||||
}
|
||||
}
|
||||
2
logo.py
2
logo.py
@@ -94,7 +94,7 @@ 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__":
|
||||
|
||||
150
main.py
150
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
|
||||
@@ -243,27 +251,110 @@ translator = Translator()
|
||||
def print_menu():
|
||||
"""Print menu options"""
|
||||
try:
|
||||
import cursor_acc_info
|
||||
cursor_acc_info.display_account_info(translator)
|
||||
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"""
|
||||
@@ -303,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}")
|
||||
@@ -454,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}")
|
||||
@@ -507,6 +605,14 @@ def main():
|
||||
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}")
|
||||
print_menu()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -106,9 +106,31 @@ class NewTempEmail:
|
||||
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
co.set_argument("--headless=new")
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
# Only use headless for non-OAuth operations
|
||||
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Check if DISPLAY is set when not in headless mode
|
||||
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
co.set_argument("--no-sandbox")
|
||||
co.set_argument("--disable-dev-shm-usage")
|
||||
co.set_argument("--disable-gpu")
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
@@ -130,6 +152,10 @@ class NewTempEmail:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform == "linux":
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def create_email(self):
|
||||
|
||||
249
oauth_auth.py
249
oauth_auth.py
@@ -21,13 +21,15 @@ EMOJI = {
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️'
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -41,12 +43,12 @@ class OAuthHandler:
|
||||
profiles.append(item)
|
||||
|
||||
if not profiles:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} No Chrome profiles found, using Default{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.no_chrome_profiles_found') if self.translator else 'No Chrome profiles found, using Default'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
# First check if Default profile exists
|
||||
if 'Default' in profiles:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found Default Chrome profile{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_default_chrome_profile') if self.translator else 'Found Default Chrome profile'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
# If no Default profile, check Local State for last used profile
|
||||
@@ -68,21 +70,35 @@ class OAuthHandler:
|
||||
return profile
|
||||
|
||||
# If no profile found in Local State, use the first available profile
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Using first available Chrome profile: {profiles[0]}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_first_available_chrome_profile', profile=profiles[0]) if self.translator else f'Using first available Chrome profile: {profiles[0]}'}{Style.RESET_ALL}")
|
||||
return profiles[0]
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Error finding Chrome profile, using Default: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.error_finding_chrome_profile', error=str(e)) if self.translator else f'Error finding Chrome profile, using Default: {str(e)}'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup browser for OAuth flow using active profile"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Initializing browser setup...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific initialization
|
||||
platform_name = platform.system().lower()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected platform: {platform_name}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
|
||||
|
||||
# Linux-specific checks
|
||||
if platform_name == 'linux':
|
||||
# Check if DISPLAY is set
|
||||
display = os.environ.get('DISPLAY')
|
||||
if not display:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check if running as root
|
||||
if os.geteuid() == 0:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.running_as_root_warning') if self.translator else 'Running as root is not recommended for browser automation'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.consider_running_without_sudo') if self.translator else 'Consider running the script without sudo'}{Style.RESET_ALL}")
|
||||
|
||||
# Kill existing browser processes
|
||||
self._kill_browser_processes()
|
||||
@@ -92,34 +108,64 @@ class OAuthHandler:
|
||||
chrome_path = self._get_browser_path()
|
||||
|
||||
if not chrome_path:
|
||||
raise Exception(f"No compatible browser found. Please install Google Chrome or Chromium.\nSupported browsers for {platform_name}:\n" +
|
||||
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
|
||||
"- Windows: Google Chrome, Chromium\n" +
|
||||
"- macOS: Google Chrome, Chromium\n" +
|
||||
"- Linux: Google Chrome, Chromium, chromium-browser")
|
||||
|
||||
# Get active profile
|
||||
active_profile = self._get_active_profile(user_data_dir)
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Using browser profile: {active_profile}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_browser_profile', profile=active_profile) if self.translator else f'Using browser profile: {active_profile}'}{Style.RESET_ALL}")
|
||||
|
||||
# Configure browser options
|
||||
co = self._configure_browser_options(chrome_path, user_data_dir, active_profile)
|
||||
co = ChromiumOptions()
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting browser at: {chrome_path}{Style.RESET_ALL}")
|
||||
# Never use headless mode for OAuth flows
|
||||
co.headless(False)
|
||||
|
||||
# Platform-specific options
|
||||
if os.name == 'linux':
|
||||
co.set_argument('--no-sandbox')
|
||||
co.set_argument('--disable-dev-shm-usage')
|
||||
co.set_argument('--disable-gpu')
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_chrome_profile_from', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
# Set paths and profile
|
||||
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||
co.set_argument(f'--profile-directory={active_profile}')
|
||||
|
||||
# Basic options
|
||||
co.set_argument('--no-first-run')
|
||||
co.set_argument('--no-default-browser-check')
|
||||
# co.auto_port()
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
|
||||
self.browser = ChromiumPage(co)
|
||||
|
||||
# Verify browser launched successfully
|
||||
if not self.browser:
|
||||
raise Exception("Failed to initialize browser instance")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Browser setup completed successfully{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Browser setup failed: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
if "DevToolsActivePort file doesn't exist" in str(e):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running with administrator/root privileges{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
|
||||
elif "Chrome failed to start" in str(e):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
if platform_name == 'linux':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _kill_browser_processes(self):
|
||||
@@ -136,7 +182,7 @@ class OAuthHandler:
|
||||
|
||||
time.sleep(1) # Wait for processes to close
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Warning: Could not kill existing browser processes: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
|
||||
|
||||
def _get_user_data_directory(self):
|
||||
"""Get the appropriate user data directory based on platform"""
|
||||
@@ -162,17 +208,17 @@ class OAuthHandler:
|
||||
# Try each possible path
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found browser data directory: {path}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=path) if self.translator else f'Found browser data directory: {path}'}{Style.RESET_ALL}")
|
||||
return path
|
||||
|
||||
# Create temporary profile if no existing profile found
|
||||
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Creating temporary profile at: {temp_profile}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
|
||||
os.makedirs(temp_profile, exist_ok=True)
|
||||
return temp_profile
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting user data directory: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def _get_browser_path(self):
|
||||
@@ -183,7 +229,7 @@ class OAuthHandler:
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
return chrome_path
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Searching for alternative browser installations...{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific paths
|
||||
if os.name == 'nt': # Windows
|
||||
@@ -215,13 +261,13 @@ class OAuthHandler:
|
||||
for path in alt_paths:
|
||||
expanded_path = os.path.expanduser(path)
|
||||
if os.path.exists(expanded_path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found browser at: {expanded_path}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
|
||||
return expanded_path
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error finding browser path: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
|
||||
@@ -250,22 +296,22 @@ class OAuthHandler:
|
||||
return co
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error configuring browser options: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def handle_google_auth(self):
|
||||
"""Handle Google OAuth authentication"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
|
||||
|
||||
# Setup browser
|
||||
if not self.setup_browser():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
# Navigate to auth URL
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
@@ -289,16 +335,17 @@ class OAuthHandler:
|
||||
raise Exception("Could not find Google authentication button")
|
||||
|
||||
# Click the button and wait for page load
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting Google authentication...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Check if we're on account selection page
|
||||
if "accounts.google.com" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account to continue...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js("""
|
||||
alert('Please select your Google account to continue with Cursor authentication');
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
@@ -306,14 +353,14 @@ class OAuthHandler:
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
if not auth_info:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
|
||||
return True, auth_info
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
try:
|
||||
@@ -333,7 +380,7 @@ class OAuthHandler:
|
||||
start_time = time.time()
|
||||
check_interval = 2 # Check every 2 seconds
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for authentication (timeout: 5 minutes)...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
|
||||
|
||||
while time.time() - start_time < max_wait:
|
||||
try:
|
||||
@@ -352,7 +399,7 @@ class OAuthHandler:
|
||||
|
||||
if token:
|
||||
# Get email from settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Authentication successful, getting account info...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
time.sleep(3)
|
||||
|
||||
@@ -361,7 +408,7 @@ class OAuthHandler:
|
||||
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||
if email_element:
|
||||
email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {email}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
|
||||
except:
|
||||
email = "user@cursor.sh" # Fallback email
|
||||
|
||||
@@ -370,42 +417,42 @@ class OAuthHandler:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, creating new account...{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', creating_new_account='creating new account') if self.translator else 'Account has reached maximum usage, creating new account...'}{Style.RESET_ALL}")
|
||||
|
||||
# Delete current account
|
||||
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":
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
return self.handle_google_auth()
|
||||
else: # github
|
||||
return self.handle_github_auth()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete expired account{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not check usage count: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
return {"email": email, "token": token}
|
||||
|
||||
# Also check URL as backup
|
||||
if "cursor.com/settings" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected successful login{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Waiting for authentication... ({str(e)}){Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
|
||||
|
||||
time.sleep(check_interval)
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error while waiting for authentication: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def handle_github_auth(self):
|
||||
@@ -420,7 +467,7 @@ class OAuthHandler:
|
||||
|
||||
# Navigate to auth URL
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
@@ -444,21 +491,21 @@ class OAuthHandler:
|
||||
raise Exception("Could not find GitHub authentication button")
|
||||
|
||||
# Click the button and wait for page load
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting GitHub authentication...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
if not auth_info:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
|
||||
return True, auth_info
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
try:
|
||||
@@ -520,23 +567,23 @@ class OAuthHandler:
|
||||
|
||||
# Check if we're on account selection page
|
||||
if auth_type == "google" and "accounts.google.com" in self.browser.url:
|
||||
alert_js = """
|
||||
alert('Please select your Google account manually to continue with Cursor authentication');
|
||||
"""
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(alert_js)
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Alert display failed: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account manually to continue with Cursor authentication...{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Waiting for authentication to complete...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for authentication to complete
|
||||
max_wait = 300 # 5 minutes
|
||||
start_time = time.time()
|
||||
last_url = self.browser.url
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Checking authentication status...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.checking_authentication_status') if self.translator else 'Checking authentication status...'}{Style.RESET_ALL}")
|
||||
|
||||
while time.time() - start_time < max_wait:
|
||||
try:
|
||||
@@ -552,9 +599,9 @@ class OAuthHandler:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful!{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
|
||||
# Navigate to settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to settings page...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
time.sleep(3) # Wait for settings page to load
|
||||
|
||||
@@ -563,9 +610,9 @@ class OAuthHandler:
|
||||
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||
if email_element:
|
||||
actual_email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
|
||||
actual_email = "user@cursor.sh"
|
||||
|
||||
# Check usage count
|
||||
@@ -573,11 +620,11 @@ class OAuthHandler:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
delete_js = """
|
||||
function deleteAccount() {
|
||||
@@ -609,23 +656,23 @@ class OAuthHandler:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page and repeat authentication
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
|
||||
# Explicitly navigate to the authentication page
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Call handle_google_auth again to repeat the entire process
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
|
||||
return self.handle_google_auth()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Remove the browser stay open prompt and input wait
|
||||
return True, {"email": actual_email, "token": token}
|
||||
@@ -633,7 +680,7 @@ class OAuthHandler:
|
||||
# Also check URL as backup
|
||||
current_url = self.browser.url
|
||||
if "cursor.com/settings" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Already on settings page!{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
cookies = self.browser.cookies()
|
||||
for cookie in cookies:
|
||||
@@ -649,9 +696,9 @@ class OAuthHandler:
|
||||
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||
if email_element:
|
||||
actual_email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
|
||||
actual_email = "user@cursor.sh"
|
||||
|
||||
# Check usage count
|
||||
@@ -659,11 +706,11 @@ class OAuthHandler:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
delete_js = """
|
||||
function deleteAccount() {
|
||||
@@ -695,44 +742,44 @@ class OAuthHandler:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page and repeat authentication
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
|
||||
# Explicitly navigate to the authentication page
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Call handle_google_auth again to repeat the entire process
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
|
||||
return self.handle_google_auth()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Remove the browser stay open prompt and input wait
|
||||
return True, {"email": actual_email, "token": token}
|
||||
elif current_url != last_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Page changed, checking auth...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
|
||||
last_url = current_url
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Status check error: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
continue
|
||||
time.sleep(1)
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication button not found{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication failed: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
if self.browser:
|
||||
@@ -755,7 +802,7 @@ class OAuthHandler:
|
||||
time.sleep(1)
|
||||
|
||||
# Debug cookie information
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found {len(cookies)} cookies{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
|
||||
|
||||
email = None
|
||||
token = None
|
||||
@@ -770,12 +817,12 @@ class OAuthHandler:
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Token extraction error: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
|
||||
elif name == "cursor_email":
|
||||
email = cookie.get("value")
|
||||
|
||||
if email and token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful - Email: {email}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
|
||||
return True, {"email": email, "token": token}
|
||||
else:
|
||||
missing = []
|
||||
@@ -783,11 +830,11 @@ class OAuthHandler:
|
||||
missing.append("email")
|
||||
if not token:
|
||||
missing.append("token")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Missing authentication data: {', '.join(missing)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to extract auth info: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def _delete_current_account(self):
|
||||
@@ -822,14 +869,14 @@ class OAuthHandler:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(auth_type, translator=None):
|
||||
@@ -839,16 +886,16 @@ 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}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
|
||||
success, auth_info = handler.handle_google_auth()
|
||||
elif auth_type.lower() == 'github':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
|
||||
success, auth_info = handler.handle_github_auth()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid authentication type{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if success and auth_info:
|
||||
@@ -859,13 +906,13 @@ def main(auth_type, translator=None):
|
||||
access_token=auth_info["token"],
|
||||
refresh_token=auth_info["token"]
|
||||
):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
|
||||
# Close the browser after successful authentication
|
||||
if handler.browser:
|
||||
handler.browser.quit()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Browser closed{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
|
||||
|
||||
return False
|
||||
@@ -25,7 +25,8 @@ EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"RESET": "<EFBFBD><EFBFBD>",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
@@ -576,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)
|
||||
|
||||
@@ -605,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"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user