Compare commits

...

19 Commits

Author SHA1 Message Date
Pin Studios
e2d70c1738 Update CHANGELOG.md 2025-03-31 01:03:13 +08:00
Pin Studios
650e2d33db Update .env 2025-03-31 01:01:02 +08:00
Pin Studios
2d1347c8c6 Merge pull request #430 from Lostata/main
Fixed grammar errors in README.md
2025-03-31 01:00:37 +08:00
Pin Studios
67d3554664 Merge pull request #426 from Nigel1992/fix/412-case-insensitive-cursor
Fix: improve Linux path handling and add case-insensitive Cursor directory detection
2025-03-31 00:59:50 +08:00
yeongpin
cb202e08e5 refactor: enhance configuration error messages with translation support and improve user feedback 2025-03-31 00:58:51 +08:00
Lostata
0c0ec47432 Fixed grammar errors in README.md 2025-03-30 09:05:46 +03:00
Nigel1992
f00678ddcb fix: add case-insensitive handling for Cursor/cursor directory 2025-03-29 23:59:04 +01:00
Nigel1992
dbc220053d fix: improve Linux path handling for config file detection 2025-03-29 23:53:45 +01:00
yeongpin
d7ab362c4c Update README.md to reflect support for the latest 0.48.x version and add new configuration options for checking updates and displaying account information. 2025-03-29 22:30:59 +08:00
yeongpin
4448a62458 Update CHANGELOG.md to reflect the reorganization of features and fixes for version 1.8.02, including the addition of new configuration options and enhancements to the temporary email functionality. 2025-03-29 22:29:27 +08:00
yeongpin
50d09e5172 Update CHANGELOG.md to reflect the addition of a new temporary email feature, and fixes for Linux Chrome issues and other minor problems. 2025-03-29 22:23:16 +08:00
yeongpin
ffff3bdeb1 Update block_domain.txt to include corhash.net and mailshou.com; modify new_signup.py and new_tempemail.py to conditionally set the --no-sandbox argument for Linux platforms. 2025-03-29 22:22:43 +08:00
yeongpin
816a09d4de Refactor NewTempEmail class to enhance email creation and verification processes, including improved browser setup and error handling. Add new_tempemail_api.py to .gitignore. 2025-03-29 22:18:11 +08:00
yeongpin
e5b7e5727c Enhance NewTempEmail functionality by integrating random service selection, improving email creation process with retries, and implementing API calls for domain and message handling. Add new_tempemail_smail.py to .gitignore. 2025-03-29 22:15:33 +08:00
yeongpin
087e3ebf78 Add solerbe.net to block_domain.txt 2025-03-29 21:59:06 +08:00
yeongpin
bd96107911 Format new GUID in reset_machine_manual.py to include braces for consistency. 2025-03-29 21:55:21 +08:00
yeongpin
17799e0209 Add dugmail.com to block_domain.txt 2025-03-29 21:52:51 +08:00
yeongpin
c15ea25cb3 Update version to 1.8.02 in .env and CHANGELOG.md, adding new features and fixes including disabling auto-update, configuration options, and contributors options. 2025-03-29 21:49:57 +08:00
yeongpin
350d781ce8 Update default version in build.yml to 1.8.02 2025-03-28 23:49:32 +08:00
18 changed files with 622 additions and 94 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.8.01
VERSION=1.8.01
version=1.8.03
VERSION=1.8.03

View File

@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.7.17'
default: '1.8.02'
permissions:
contents: write

2
.gitignore vendored
View File

@@ -14,6 +14,8 @@ build.py
build.sh
ENV/
test.py
new_tempemail_smail.py
new_tempemail_api.py
install.bat
run.bat

View File

@@ -1,5 +1,22 @@
# Change Log
## 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 | 修復禁用自動更新

View File

@@ -13,16 +13,15 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a1)](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>

View File

@@ -16,3 +16,7 @@ ikomail.com
mailpull.com
drewzen.com
begemail.com
dugmail.com
solerbe.net
corhash.net
mailshou.com

146
config.py
View File

@@ -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,91 @@ 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
current_user = os.getenv('USER') or os.getenv('USERNAME') or os.getenv('SUDO_USER')
if not current_user:
current_user = os.path.expanduser('~').split('/')[-1]
actual_home = f"/home/{current_user}"
if not os.path.exists(actual_home):
actual_home = os.path.expanduser("~")
# Define base config directory
config_base = os.path.join(actual_home, ".config")
# Try both "Cursor" and "cursor" directory names
cursor_dir = None
for dir_name in ["Cursor", "cursor"]:
test_dir = os.path.join(config_base, dir_name)
if os.path.exists(test_dir):
cursor_dir = test_dir
break
if not cursor_dir:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
# Define Linux paths using the found cursor directory
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
storage_dir = os.path.dirname(storage_path) if storage_path else ""
# Verify paths and permissions
try:
# Check storage directory
if storage_dir and not os.path.exists(storage_dir):
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
# Check storage.json with more detailed verification
if storage_path and os.path.exists(storage_path):
# Get file stats
try:
stat = os.stat(storage_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path)}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', stat.st_size)}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', oct(stat.st_mode & 0o777))}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', stat.st_uid)}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', stat.st_gid)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e))}{Style.RESET_ALL}")
# Check if file is readable and writable
if not os.access(storage_path, os.R_OK | os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', current_user=current_user, storage_path=storage_path)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and', storage_path=storage_path)}{Style.RESET_ALL}")
# Try to read the file to verify it's not corrupted
try:
with open(storage_path, 'r') as f:
content = f.read()
if not content.strip():
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor')}{Style.RESET_ALL}")
elif storage_path:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
except (OSError, IOError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e))}{Style.RESET_ALL}")
# Define all paths using the found cursor directory
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/cursor/machineid"),
'storage_path': storage_path,
'sqlite_path': os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/state.vscdb")) if cursor_dir else "",
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
'cursor_path': get_linux_cursor_path(),
'updater_path': os.path.expanduser("~/.config/cursor-updater"),
'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 +181,13 @@ def setup_config(translator=None):
config.set(section, option, str(value))
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN} {translator.get('register.config_updated')}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_updated')}{Style.RESET_ALL}")
else:
for section, options in default_config.items():
config.add_section(section)
@@ -104,16 +197,41 @@ def setup_config(translator=None):
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
print(f"{Fore.RED} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ Error setting up config: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
return None
def print_config(config, translator=None):
"""Print configuration in a readable format"""
if not config:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available')}{Style.RESET_ALL}")
return
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration')}:{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
for section in config.sections():
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
for key, value in config.items(section):
# 对布尔值进行特殊处理,使其显示为彩色
if value.lower() in ('true', 'yes', 'on', '1'):
value_display = f"{Fore.GREEN}{translator.get('config.enabled')}{Style.RESET_ALL}"
elif value.lower() in ('false', 'no', 'off', '0'):
value_display = f"{Fore.RED}{translator.get('config.disabled')}{Style.RESET_ALL}"
else:
value_display = value
print(f" {key} = {value_display}")
print(f"\n{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory')}: {config_dir}{Style.RESET_ALL}")
print()
def get_config(translator=None):
"""Get existing config or create new one"""

View File

@@ -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"""

View File

@@ -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

View File

@@ -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",
@@ -308,7 +313,8 @@
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
"development_version": "Development Version {current} > {latest}",
"changelog_title": "Changelog"
"changelog_title": "Changelog",
"rate_limit_exceeded": "GitHub API rate limit exceeded. Skipping update check."
},
"totally_reset": {
"title": "Totally Reset Cursor",
@@ -478,6 +484,36 @@
"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}",
"file_permissions": "File permissions: {permissions}",
"file_owner": "File owner: {owner}",
"file_group": "File group: {group}",
"error_getting_file_stats": "Error getting file stats: {error}",
"permission_denied": "Permission denied: {storage_path}",
"try_running": "Try running 'cursor --help' to check if it's installed",
"and": "and",
"storage_file_is_empty": "Storage file is empty: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
"storage_file_not_found": "Storage file not found: {storage_path}",
"error_checking_linux_paths": "Error checking Linux paths: {error}",
"config_option_added": "Config option added: {option}",
"config_updated": "Config updated",
"config_created": "Config created: {config_file}",
"config_setup_error": "Error setting up config: {error}"
}
}

View File

@@ -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 注册工具",
@@ -303,7 +308,8 @@
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
"development_version": "开发版本 {current} > {latest}",
"changelog_title": "更新日志"
"changelog_title": "更新日志",
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
},
"totally_reset": {
"title": "完全重置 Cursor",
@@ -473,7 +479,36 @@
"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": "尝试运行 'cursor --help' 检查是否已安装",
"and": "和",
"storage_file_is_empty": "存储文件为空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
"storage_file_not_found": "未找到存储文件",
"error_checking_linux_paths": "检查 Linux 路径时出错",
"config_option_added": "添加配置选项",
"config_updated": "配置更新",
"config_created": "配置已创建",
"config_setup_error": "配置设置错误"
}
}

View File

@@ -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": {
@@ -282,7 +287,8 @@
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
"development_version": "開發版本 {current} > {latest}",
"changelog_title": "更新日誌"
"changelog_title": "更新日誌",
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
},
"totally_reset": {
"title": "完全重置 Cursor",
@@ -452,6 +458,36 @@
"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": "嘗試運行 'cursor --help' 檢查是否已安裝",
"and": "和",
"storage_file_is_empty": "儲存文件為空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
"storage_file_not_found": "未找到儲存文件",
"error_checking_linux_paths": "檢查 Linux 路徑時出錯",
"config_option_added": "添加配置選項",
"config_updated": "配置更新",
"config_created": "配置已創建",
"config_setup_error": "配置設置錯誤"
}
}

View File

@@ -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
View File

@@ -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()

View File

@@ -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()

View File

@@ -107,7 +107,9 @@ class NewTempEmail:
# 创建浏览器选项
co = ChromiumOptions()
co.set_argument("--headless=new")
co.set_argument("--no-sandbox")
if sys.platform == "linux":
co.set_argument("--no-sandbox")
co.auto_port() # 自动设置端口

View File

@@ -25,9 +25,10 @@ EMOJI = {
}
class OAuthHandler:
def __init__(self, translator=None):
def __init__(self, translator=None, auth_type=None):
self.translator = translator
self.config = get_config(translator)
self.auth_type = auth_type # make sure the auth_type is not None
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
@@ -380,7 +381,7 @@ class OAuthHandler:
if self._delete_current_account():
# Start new authentication based on auth type
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
if auth_type == "google":
if self.auth_type == "google":
return self.handle_google_auth()
else: # github
return self.handle_github_auth()
@@ -839,7 +840,7 @@ def main(auth_type, translator=None):
auth_type (str): Type of authentication ('google' or 'github')
translator: Translator instance for internationalization
"""
handler = OAuthHandler(translator)
handler = OAuthHandler(translator, auth_type)
if auth_type.lower() == 'google':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start')}{Style.RESET_ALL}")

View File

@@ -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"""