forked from mirrors/cursor-free-vip
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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:
|
version:
|
||||||
description: 'Version number (e.g. 1.0.9)'
|
description: 'Version number (e.g. 1.0.9)'
|
||||||
required: true
|
required: true
|
||||||
default: '1.7.17'
|
default: '1.8.02'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,6 +14,8 @@ build.py
|
|||||||
build.sh
|
build.sh
|
||||||
ENV/
|
ENV/
|
||||||
test.py
|
test.py
|
||||||
|
new_tempemail_smail.py
|
||||||
|
new_tempemail_api.py
|
||||||
|
|
||||||
install.bat
|
install.bat
|
||||||
run.bat
|
run.bat
|
||||||
|
|||||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
|||||||
# Change Log
|
# 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
|
## v1.8.01
|
||||||
1. Add: Cursor Account Info | 增加 Cursor 賬號信息
|
1. Add: Cursor Account Info | 增加 Cursor 賬號信息
|
||||||
2. Fix: Disable Auto Update | 修復禁用自動更新
|
2. Fix: Disable Auto Update | 修復禁用自動更新
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -13,16 +13,15 @@
|
|||||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||||
|
|
||||||
</p>
|
</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.
|
Supports Windows, macOS and Linux.
|
||||||
|
|
||||||
For optimal performance, run with privileges and always stay up to date.
|
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 的配置。
|
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||||
@@ -101,7 +100,7 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
|||||||
|
|
||||||
</details>
|
</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 | 注意事項
|
## ❗ 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
|
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
|
||||||
|
|
||||||
[Turnstile]
|
[Turnstile]
|
||||||
# Handle Tuenstile Wait Time | 等待人機驗證時間
|
# Handle Turnstile Wait Time | 等待人機驗證時間
|
||||||
handle_turnstile_time = 2
|
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
|
handle_turnstile_random_time = 1-3
|
||||||
|
|
||||||
[OSPaths]
|
[OSPaths]
|
||||||
@@ -159,11 +158,17 @@ failed_retry_time = 0.5-1
|
|||||||
retry_interval = 8-12
|
retry_interval = 8-12
|
||||||
# Max Timeout | 最大超時時間
|
# Max Timeout | 最大超時時間
|
||||||
max_timeout = 160
|
max_timeout = 160
|
||||||
|
|
||||||
|
[Utils]
|
||||||
|
# Check Update | 檢查更新
|
||||||
|
check_update = True
|
||||||
|
# Show Account Info | 顯示賬號信息
|
||||||
|
show_account_info = True
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</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>
|
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
||||||
|
|
||||||
|
|||||||
@@ -16,3 +16,7 @@ ikomail.com
|
|||||||
mailpull.com
|
mailpull.com
|
||||||
drewzen.com
|
drewzen.com
|
||||||
begemail.com
|
begemail.com
|
||||||
|
dugmail.com
|
||||||
|
solerbe.net
|
||||||
|
corhash.net
|
||||||
|
mailshou.com
|
||||||
146
config.py
146
config.py
@@ -4,6 +4,18 @@ import configparser
|
|||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
|
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):
|
def setup_config(translator=None):
|
||||||
"""Setup configuration file and return config object"""
|
"""Setup configuration file and return config object"""
|
||||||
try:
|
try:
|
||||||
@@ -37,6 +49,10 @@ def setup_config(translator=None):
|
|||||||
'failed_retry_time': '0.5-1',
|
'failed_retry_time': '0.5-1',
|
||||||
'retry_interval': '8-12',
|
'retry_interval': '8-12',
|
||||||
'max_timeout': '160'
|
'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"),
|
'updater_path': os.path.join(localappdata, "cursor-updater"),
|
||||||
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml")
|
'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":
|
elif sys.platform == "darwin":
|
||||||
default_config['MacPaths'] = {
|
default_config['MacPaths'] = {
|
||||||
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
|
'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"),
|
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||||
}
|
}
|
||||||
elif sys.platform == "linux":
|
# Create storage directory
|
||||||
sudo_user = os.environ.get('SUDO_USER')
|
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
|
||||||
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
|
|
||||||
|
|
||||||
|
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'] = {
|
default_config['LinuxPaths'] = {
|
||||||
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/storage.json")),
|
'storage_path': storage_path,
|
||||||
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/state.vscdb")),
|
'sqlite_path': os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/state.vscdb")) if cursor_dir else "",
|
||||||
'machine_id_path': os.path.expanduser("~/.config/cursor/machineid"),
|
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
|
||||||
'cursor_path': get_linux_cursor_path(),
|
'cursor_path': get_linux_cursor_path(),
|
||||||
'updater_path': os.path.expanduser("~/.config/cursor-updater"),
|
'updater_path': os.path.join(config_base, "cursor-updater"),
|
||||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Read existing configuration and merge
|
# Read existing configuration and merge
|
||||||
@@ -88,13 +181,13 @@ def setup_config(translator=None):
|
|||||||
config.set(section, option, str(value))
|
config.set(section, option, str(value))
|
||||||
config_modified = True
|
config_modified = True
|
||||||
if translator:
|
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:
|
if config_modified:
|
||||||
with open(config_file, 'w', encoding='utf-8') as f:
|
with open(config_file, 'w', encoding='utf-8') as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
if translator:
|
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:
|
else:
|
||||||
for section, options in default_config.items():
|
for section, options in default_config.items():
|
||||||
config.add_section(section)
|
config.add_section(section)
|
||||||
@@ -104,16 +197,41 @@ def setup_config(translator=None):
|
|||||||
with open(config_file, 'w', encoding='utf-8') as f:
|
with open(config_file, 'w', encoding='utf-8') as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
if translator:
|
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
|
return config
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.RED}❌ {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {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}")
|
|
||||||
return None
|
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):
|
def get_config(translator=None):
|
||||||
"""Get existing config or create new one"""
|
"""Get existing config or create new one"""
|
||||||
|
|||||||
@@ -64,12 +64,12 @@ class UsageManager:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
# 获取 GPT-4 使用量和限制
|
# get Premium usage and limit
|
||||||
gpt4_data = data.get("gpt-4", {})
|
gpt4_data = data.get("gpt-4", {})
|
||||||
premium_usage = gpt4_data.get("numRequestsTotal", 0)
|
premium_usage = gpt4_data.get("numRequestsTotal", 0)
|
||||||
max_premium_usage = gpt4_data.get("maxRequestUsage", 999)
|
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", {})
|
gpt35_data = data.get("gpt-3.5-turbo", {})
|
||||||
basic_usage = gpt35_data.get("numRequestsTotal", 0)
|
basic_usage = gpt35_data.get("numRequestsTotal", 0)
|
||||||
|
|
||||||
@@ -77,10 +77,15 @@ class UsageManager:
|
|||||||
'premium_usage': premium_usage,
|
'premium_usage': premium_usage,
|
||||||
'max_premium_usage': max_premium_usage,
|
'max_premium_usage': max_premium_usage,
|
||||||
'basic_usage': basic_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:
|
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
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -95,7 +100,7 @@ class UsageManager:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.json()
|
return response.json()
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
logger.error(f"获取订阅信息失败: {str(e)}")
|
logger.error(f"Get subscription info failed: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_token_from_config():
|
def get_token_from_config():
|
||||||
@@ -126,7 +131,7 @@ def get_token_from_config():
|
|||||||
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
|
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取配置路径失败: {str(e)}")
|
logger.error(f"Get config path failed: {str(e)}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -339,9 +344,9 @@ def get_email_from_sqlite(sqlite_path):
|
|||||||
|
|
||||||
def display_account_info(translator=None):
|
def display_account_info(translator=None):
|
||||||
"""display account info"""
|
"""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}{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
|
# get token
|
||||||
token = get_token()
|
token = get_token()
|
||||||
@@ -363,7 +368,11 @@ def display_account_info(translator=None):
|
|||||||
email = get_email_from_sqlite(paths['sqlite_path'])
|
email = get_email_from_sqlite(paths['sqlite_path'])
|
||||||
|
|
||||||
# get subscription info
|
# 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 found in storage and sqlite, try from subscription info
|
||||||
if not email and 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']:
|
if 'customer' in subscription_info and 'email' in subscription_info['customer']:
|
||||||
email = subscription_info['customer']['email']
|
email = subscription_info['customer']['email']
|
||||||
|
|
||||||
# get usage info
|
# get usage info - silently handle errors
|
||||||
usage_info = UsageManager.get_usage(token)
|
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:
|
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:
|
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:
|
if subscription_info:
|
||||||
subscription_type = format_subscription_type(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")
|
days_remaining = subscription_info.get("daysRemainingOnTrial")
|
||||||
if days_remaining is not None and days_remaining > 0:
|
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:
|
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:
|
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)
|
premium_usage = usage_info.get('premium_usage', 0)
|
||||||
max_premium_usage = usage_info.get('max_premium_usage', "No Limit")
|
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}%)"
|
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
|
# Slow Response
|
||||||
basic_usage = usage_info.get('basic_usage', 0)
|
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}%)"
|
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:
|
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):
|
def main(translator=None):
|
||||||
"""main function"""
|
"""main function"""
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import shutil
|
|||||||
from colorama import Fore, Style, init
|
from colorama import Fore, Style, init
|
||||||
import subprocess
|
import subprocess
|
||||||
from config import get_config
|
from config import get_config
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
|
|
||||||
# Initialize colorama
|
# Initialize colorama
|
||||||
init()
|
init()
|
||||||
@@ -54,6 +56,45 @@ class AutoUpdateDisabler:
|
|||||||
}
|
}
|
||||||
self.update_yml_path = self.update_yml_paths.get(self.system)
|
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):
|
def _kill_cursor_processes(self):
|
||||||
"""End all Cursor processes"""
|
"""End all Cursor processes"""
|
||||||
try:
|
try:
|
||||||
@@ -181,6 +222,10 @@ class AutoUpdateDisabler:
|
|||||||
if not self._create_blocking_file():
|
if not self._create_blocking_file():
|
||||||
return False
|
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}")
|
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
"admin_required": "Running as executable, administrator privileges required.",
|
"admin_required": "Running as executable, administrator privileges required.",
|
||||||
"admin_required_continue": "Continuing without administrator privileges.",
|
"admin_required_continue": "Continuing without administrator privileges.",
|
||||||
"coming_soon": "Coming Soon",
|
"coming_soon": "Coming Soon",
|
||||||
"fixed_soon": "Fixed Soon"
|
"fixed_soon": "Fixed Soon",
|
||||||
|
"contribute": "Contribute to the Project",
|
||||||
|
"config": "Show Config"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "English",
|
"en": "English",
|
||||||
@@ -106,7 +108,10 @@
|
|||||||
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
|
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
|
||||||
"no_write_permission": "No Write Permission: {path}",
|
"no_write_permission": "No Write Permission: {path}",
|
||||||
"path_not_found": "Path Not Found: {path}",
|
"path_not_found": "Path Not Found: {path}",
|
||||||
"modify_file_failed": "Modify File Failed: {error}"
|
"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": {
|
"register": {
|
||||||
"title": "Cursor Registration Tool",
|
"title": "Cursor Registration Tool",
|
||||||
@@ -308,7 +313,8 @@
|
|||||||
"update_skipped": "Skipping update.",
|
"update_skipped": "Skipping update.",
|
||||||
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
|
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
|
||||||
"development_version": "Development Version {current} > {latest}",
|
"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": {
|
"totally_reset": {
|
||||||
"title": "Totally Reset Cursor",
|
"title": "Totally Reset Cursor",
|
||||||
@@ -478,6 +484,36 @@
|
|||||||
"active": "Active",
|
"active": "Active",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"premium_usage": "Premium Usage",
|
"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}"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,9 @@
|
|||||||
"admin_required": "运行可执行文件,需要管理员权限",
|
"admin_required": "运行可执行文件,需要管理员权限",
|
||||||
"admin_required_continue": "继续使用当前版本...",
|
"admin_required_continue": "继续使用当前版本...",
|
||||||
"coming_soon": "即将推出",
|
"coming_soon": "即将推出",
|
||||||
"fixed_soon": "即将修复"
|
"fixed_soon": "即将修复",
|
||||||
|
"contribute": "贡献项目",
|
||||||
|
"config": "显示配置"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "英语",
|
"en": "英语",
|
||||||
@@ -106,7 +108,10 @@
|
|||||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||||
"no_write_permission": "没有写入权限: {path}",
|
"no_write_permission": "没有写入权限: {path}",
|
||||||
"path_not_found": "路径未找到: {path}",
|
"path_not_found": "路径未找到: {path}",
|
||||||
"modify_file_failed": "修改文件失败: {error}"
|
"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": {
|
"register": {
|
||||||
"title": "Cursor 注册工具",
|
"title": "Cursor 注册工具",
|
||||||
@@ -303,7 +308,8 @@
|
|||||||
"update_skipped": "跳过更新。",
|
"update_skipped": "跳过更新。",
|
||||||
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
|
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
|
||||||
"development_version": "开发版本 {current} > {latest}",
|
"development_version": "开发版本 {current} > {latest}",
|
||||||
"changelog_title": "更新日志"
|
"changelog_title": "更新日志",
|
||||||
|
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
|
||||||
},
|
},
|
||||||
"totally_reset": {
|
"totally_reset": {
|
||||||
"title": "完全重置 Cursor",
|
"title": "完全重置 Cursor",
|
||||||
@@ -473,7 +479,36 @@
|
|||||||
"active": "活跃",
|
"active": "活跃",
|
||||||
"inactive": "非活跃",
|
"inactive": "非活跃",
|
||||||
"premium_usage": "高级使用量",
|
"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": "配置设置错误"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,9 @@
|
|||||||
"admin_required": "運行可執行文件,需要管理員權限",
|
"admin_required": "運行可執行文件,需要管理員權限",
|
||||||
"admin_required_continue": "繼續使用當前版本...",
|
"admin_required_continue": "繼續使用當前版本...",
|
||||||
"coming_soon": "即將推出",
|
"coming_soon": "即將推出",
|
||||||
"fixed_soon": "即將修復"
|
"fixed_soon": "即將修復",
|
||||||
|
"contribute": "貢獻項目",
|
||||||
|
"config": "顯示配置"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "英文",
|
"en": "英文",
|
||||||
@@ -104,7 +106,10 @@
|
|||||||
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
|
||||||
"no_write_permission": "沒有寫入權限: {path}",
|
"no_write_permission": "沒有寫入權限: {path}",
|
||||||
"path_not_found": "路徑未找到: {path}",
|
"path_not_found": "路徑未找到: {path}",
|
||||||
"modify_file_failed": "修改文件失敗: {error}"
|
"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": {
|
"register": {
|
||||||
@@ -282,7 +287,8 @@
|
|||||||
"update_skipped": "跳過更新。",
|
"update_skipped": "跳過更新。",
|
||||||
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
|
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
|
||||||
"development_version": "開發版本 {current} > {latest}",
|
"development_version": "開發版本 {current} > {latest}",
|
||||||
"changelog_title": "更新日誌"
|
"changelog_title": "更新日誌",
|
||||||
|
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
|
||||||
},
|
},
|
||||||
"totally_reset": {
|
"totally_reset": {
|
||||||
"title": "完全重置 Cursor",
|
"title": "完全重置 Cursor",
|
||||||
@@ -452,6 +458,36 @@
|
|||||||
"active": "活躍",
|
"active": "活躍",
|
||||||
"inactive": "非活躍",
|
"inactive": "非活躍",
|
||||||
"premium_usage": "高級使用量",
|
"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": "配置設置錯誤"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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():
|
def print_logo():
|
||||||
print(CURSOR_LOGO)
|
print(CURSOR_LOGO)
|
||||||
print(CURSOR_DESCRIPTION)
|
print(CURSOR_DESCRIPTION)
|
||||||
print(CURSOR_CONTRIBUTORS)
|
# print(CURSOR_CONTRIBUTORS)
|
||||||
print(CURSOR_OTHER_INFO)
|
print(CURSOR_OTHER_INFO)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
150
main.py
150
main.py
@@ -9,12 +9,14 @@ import locale
|
|||||||
import platform
|
import platform
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
from config import get_config
|
from config import get_config
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
|
||||||
# Only import windll on Windows systems
|
# Only import windll on Windows systems
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
import ctypes
|
import ctypes
|
||||||
# 只在 Windows 上导入 windll
|
# Only import windll on Windows systems
|
||||||
from ctypes import windll
|
from ctypes import windll
|
||||||
|
|
||||||
# Initialize colorama
|
# Initialize colorama
|
||||||
@@ -32,7 +34,13 @@ EMOJI = {
|
|||||||
"ARROW": "➜",
|
"ARROW": "➜",
|
||||||
"LANG": "🌐",
|
"LANG": "🌐",
|
||||||
"UPDATE": "🔄",
|
"UPDATE": "🔄",
|
||||||
"ADMIN": "🔐"
|
"ADMIN": "🔐",
|
||||||
|
"AIRDROP": "💰",
|
||||||
|
"ROCKET": "🚀",
|
||||||
|
"STAR": "⭐",
|
||||||
|
"SUN": "🌟",
|
||||||
|
"CONTRIBUTE": "🤝",
|
||||||
|
"SETTINGS": "⚙️"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if running as frozen executable
|
# Function to check if running as frozen executable
|
||||||
@@ -243,27 +251,110 @@ translator = Translator()
|
|||||||
def print_menu():
|
def print_menu():
|
||||||
"""Print menu options"""
|
"""Print menu options"""
|
||||||
try:
|
try:
|
||||||
import cursor_acc_info
|
config = get_config()
|
||||||
cursor_acc_info.display_account_info(translator)
|
if config.getboolean('Utils', 'enabled_account_info'):
|
||||||
|
import cursor_acc_info
|
||||||
|
cursor_acc_info.display_account_info(translator)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.account_info_error', error=str(e))}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.account_info_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
|
||||||
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
|
||||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
print(f"{Fore.YELLOW}{'─' * 70}{Style.RESET_ALL}")
|
||||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
else:
|
||||||
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.YELLOW}{'─' * 110}{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}")
|
# Get terminal width
|
||||||
print(f"{Fore.GREEN}4{Style.RESET_ALL}. ⭐ {translator.get('menu.register_github')}")
|
try:
|
||||||
print(f"{Fore.YELLOW} ┗━━ 🚀 {translator.get('menu.lifetime_access_enabled')} 🚀{Style.RESET_ALL}")
|
terminal_width = shutil.get_terminal_size().columns
|
||||||
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
except:
|
||||||
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}")
|
terminal_width = 80 # Default width
|
||||||
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')}")
|
# Define all menu items
|
||||||
print(f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
|
menu_items = {
|
||||||
print(f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}")
|
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
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():
|
def select_language():
|
||||||
"""Language selection menu"""
|
"""Language selection menu"""
|
||||||
@@ -303,6 +394,11 @@ def check_latest_version():
|
|||||||
timeout=10
|
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
|
# Check if response is successful
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
raise Exception(f"GitHub API returned status code {response.status_code}")
|
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}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
|
||||||
return
|
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()
|
print_menu()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
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":
|
if choice == "0":
|
||||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
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)
|
totally_reset_cursor.run(translator)
|
||||||
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
||||||
print_menu()
|
print_menu()
|
||||||
|
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:
|
else:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||||
print_menu()
|
print_menu()
|
||||||
|
|||||||
@@ -203,9 +203,10 @@ def setup_driver(translator=None):
|
|||||||
# Use incognito mode
|
# Use incognito mode
|
||||||
co.set_argument("--incognito")
|
co.set_argument("--incognito")
|
||||||
|
|
||||||
# Set random port
|
if sys.platform == "linux":
|
||||||
co.set_argument("--no-sandbox")
|
# Set random port
|
||||||
|
co.set_argument("--no-sandbox")
|
||||||
|
|
||||||
# Set random port
|
# Set random port
|
||||||
co.auto_port()
|
co.auto_port()
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ class NewTempEmail:
|
|||||||
# 创建浏览器选项
|
# 创建浏览器选项
|
||||||
co = ChromiumOptions()
|
co = ChromiumOptions()
|
||||||
co.set_argument("--headless=new")
|
co.set_argument("--headless=new")
|
||||||
co.set_argument("--no-sandbox")
|
|
||||||
|
if sys.platform == "linux":
|
||||||
|
co.set_argument("--no-sandbox")
|
||||||
|
|
||||||
|
|
||||||
co.auto_port() # 自动设置端口
|
co.auto_port() # 自动设置端口
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ EMOJI = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OAuthHandler:
|
class OAuthHandler:
|
||||||
def __init__(self, translator=None):
|
def __init__(self, translator=None, auth_type=None):
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
self.config = get_config(translator)
|
self.config = get_config(translator)
|
||||||
|
self.auth_type = auth_type # make sure the auth_type is not None
|
||||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||||
self.browser = None
|
self.browser = None
|
||||||
|
|
||||||
@@ -380,7 +381,7 @@ class OAuthHandler:
|
|||||||
if self._delete_current_account():
|
if self._delete_current_account():
|
||||||
# Start new authentication based on auth type
|
# Start new authentication based on auth type
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
|
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()
|
return self.handle_google_auth()
|
||||||
else: # github
|
else: # github
|
||||||
return self.handle_github_auth()
|
return self.handle_github_auth()
|
||||||
@@ -839,7 +840,7 @@ def main(auth_type, translator=None):
|
|||||||
auth_type (str): Type of authentication ('google' or 'github')
|
auth_type (str): Type of authentication ('google' or 'github')
|
||||||
translator: Translator instance for internationalization
|
translator: Translator instance for internationalization
|
||||||
"""
|
"""
|
||||||
handler = OAuthHandler(translator)
|
handler = OAuthHandler(translator, auth_type)
|
||||||
|
|
||||||
if auth_type.lower() == 'google':
|
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')}{Style.RESET_ALL}")
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ EMOJI = {
|
|||||||
"SUCCESS": "✅",
|
"SUCCESS": "✅",
|
||||||
"ERROR": "❌",
|
"ERROR": "❌",
|
||||||
"INFO": "ℹ️",
|
"INFO": "ℹ️",
|
||||||
"RESET": "🔄",
|
"RESET": "<EFBFBD><EFBFBD>",
|
||||||
|
"WARNING": "⚠️",
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||||
@@ -576,6 +577,7 @@ class MachineIDResetter:
|
|||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
self._update_windows_machine_guid()
|
self._update_windows_machine_guid()
|
||||||
|
self._update_windows_machine_id()
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
self._update_macos_platform_uuid(new_ids)
|
self._update_macos_platform_uuid(new_ids)
|
||||||
|
|
||||||
@@ -605,6 +607,45 @@ class MachineIDResetter:
|
|||||||
except Exception as e:
|
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}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
raise
|
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):
|
def _update_macos_platform_uuid(self, new_ids):
|
||||||
"""Update macOS Platform UUID"""
|
"""Update macOS Platform UUID"""
|
||||||
|
|||||||
Reference in New Issue
Block a user