Compare commits

...

42 Commits

Author SHA1 Message Date
Pin Studios
809dac091d Update CHANGELOG.md to include mandatory update notice for version 1.11.01, enhancing user awareness of the new features and improvements. 2025-04-24 11:21:12 +08:00
Pin Studios
da5bff5994 Update version to 1.11.01 and enhance localization for OAuth profile selection. Added new features including Arabic language support, machine ID restoration from backup, and improved error handling in the update process. Updated menu options and fixed various issues for a better user experience. 2025-04-24 11:20:21 +08:00
Pin Studios
296a69bf73 Add CursorGoogleAccountDeleter class for Google account deletion via OAuth. Implemented robust authentication flow, including dynamic waits, error handling, and user feedback. This tool enhances user experience by providing a streamlined process for account deletion. 2025-04-24 10:51:09 +08:00
Pin Studios
5ca6c97d96 Add OAuthHandler class for Google and GitHub authentication, including profile selection and browser setup. Implemented error handling and user feedback for various authentication states. This new functionality enhances the user experience by streamlining the OAuth process. 2025-04-24 10:50:21 +08:00
Pin Studios
fb52888f9c Merge pull request #756 from ppanphper/main
增加备份的机器ID还原功能
2025-04-24 10:47:52 +08:00
Pin Studios
8e6df1d1f8 Merge branch 'main' into main 2025-04-24 10:47:28 +08:00
Pin Studios
be5a17c861 Update localization files to add "restore_machine_id" entry in English, Vietnamese, Simplified Chinese, and Traditional Chinese, enhancing user feedback consistency across languages. 2025-04-24 10:45:44 +08:00
Pin Studios
ca496ea53f Remove "restore_machine_id" entry from English localization file to streamline user feedback messages. 2025-04-24 10:45:00 +08:00
Pin Studios
52eaecd040 Update localization files to add new language configuration messages for English, Vietnamese, Simplified Chinese, and Traditional Chinese. Added "language_config_saved" and "lang_invalid_choice" entries to enhance user feedback. 2025-04-24 10:43:01 +08:00
Pin Studios
5fac4d6e45 Merge pull request #751 from TranMC/main
Language configuration saved setting
2025-04-24 10:39:06 +08:00
ppanphper
271d5d9db9 Merge branch 'yeongpin:main' into main 2025-04-23 16:40:44 +08:00
ppanphper
c2af657c88 添加恢复机器ID功能,新增restore_machine_id.py文件并更新主菜单以支持该功能。更新多语言支持,包含相关提示信息和错误处理。 2025-04-23 16:31:53 +08:00
TranMC
cf55c91117 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:58:27 +07:00
TranMC
9abf4f4899 Change something in language file 2025-04-23 12:58:24 +07:00
TranMC
a78b1160c2 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:56:22 +07:00
TranMC
c5453a4374 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:49:06 +07:00
TranMC
8c497d7639 Commit vui vui 2025-04-23 12:48:58 +07:00
TranMC
c0c2cd6120 Merge branch 'main' into main 2025-04-23 12:46:52 +07:00
Pin Studios
e36f6d986e Merge pull request #745 from Ahmed-Nagi1/add-arabic-translation
Add Arabic language
2025-04-23 13:13:05 +08:00
TranMC
0b547c0542 Fix nofitication 2025-04-23 12:09:02 +07:00
TranMC
ef17ba8803 Small language change 2025-04-23 12:04:03 +07:00
TranMC
31bf75e4de Update language configuration 2025-04-23 11:38:17 +07:00
TranMC
63e4e72ec7 Add language config saved 2025-04-23 10:57:50 +07:00
Ahmed Nagi
b51d9c7a74 Add Arabic language option to all locale files and support proper Arabic text display in terminal
- Added "ar" (Arabic) to the languages section in all translation JSON files in the locales directory.
- Implemented Arabic text reshaping and bidi support in main.py using arabic_reshaper and python-bidi for correct RTL display in terminal when Arabic is selected.
2025-04-22 10:57:51 +02:00
yeongpin
4aba849cf1 Refactor GitHub Actions workflow to adjust job dependencies for build processes, ensuring that version determination occurs before tag creation for Linux and macOS builds. 2025-04-22 11:15:15 +08:00
yeongpin
1489357328 Update GitHub Actions workflow to ensure build jobs depend on version determination. Simplify ARM64 Docker build process by directly renaming output files without pre-checks for existence. 2025-04-22 11:13:40 +08:00
yeongpin
243c47adb4 Update CHANGELOG for v1.10.05 to clarify the use of GitHub Actions for builds and include a fix for various issues. 2025-04-22 11:04:11 +08:00
yeongpin
b571356fbf Enhance GitHub Actions workflow to include checks for the existence of output files before renaming them in the ARM64 Docker build process. This ensures proper handling of file naming and provides error feedback if expected files are not found. 2025-04-22 11:03:08 +08:00
yeongpin
615c3ea2db Enhance GitHub Actions workflow to check for existing tags before creating new ones. Update tag creation step to only execute if the tag does not already exist. Remove deprecated steps related to Windows SDK installation and signing processes, which are currently disabled. 2025-04-22 10:40:07 +08:00
yeongpin
cffde7066e Refactor GitHub Actions workflow to replace deprecated set-output command with new output syntax. Update Windows SDK installation step and add fallback for SignTool download in case of failure. 2025-04-22 10:30:43 +08:00
yeongpin
73a8b23257 Update version to 1.10.05, remove block_domain.txt from build specification, and enhance GitHub Actions workflow to dynamically determine version from .env file or manual input. Update CHANGELOG for new version release. 2025-04-22 10:27:08 +08:00
Pin Studios
6d182fda55 Update CHANGELOG for v1.10.04 with hotfix for 'main_path' access issue on Windows and macOS, and general bug fixes. Modify issue templates to clarify version placeholder for bug reports. 2025-04-21 14:05:15 +08:00
Pin Studios
c243e9f2f6 Add main path retrieval for non-Linux systems in get_workbench_cursor_path function 2025-04-21 14:02:59 +08:00
yeongpin
caf996864f Update version to 1.10.03 in build workflow configuration. 2025-04-21 11:03:55 +08:00
yeongpin
ce9411dcda Update version to 1.10.03, enhance CHANGELOG with new features including manual registration, email support, and various bug fixes. Add new cursor_auth and cursor_register_manual scripts for improved user registration process. 2025-04-21 11:03:17 +08:00
Pin Studios
1c10750c2b Merge pull request #708 from DaydreamCoding/patch-for-cursor_path
fix use cursor_path
2025-04-21 10:55:48 +08:00
Pin Studios
5aa8dbb614 Merge pull request #709 from DaydreamCoding/patch-macOS-bypass_version-use-config_file
fix macOS 'bypass_version.py' get product_json_path from config_file
2025-04-21 10:55:30 +08:00
QTom
30df4d9ad1 fix macOS 'bypass_version.py' get product_json_path from config_file 2025-04-19 16:58:01 +08:00
QTom
4a533436eb fix use cursor_path 2025-04-19 16:47:06 +08:00
Pin Studios
b271166247 Merge pull request #684 from swechencheng/fix-win-totally-reset-cursor
fix: Port 80aab8741f
2025-04-17 17:54:33 +08:00
Charles Zhang
75825fe3fe fix: Port 80aab8741f
This applies the same fix in 80aab8741f to totally_reset_cursor,
which was missing.
2025-04-17 11:42:55 +02:00
yeongpin
56f9a86e7a Update version to 1.10.02, enhance CHANGELOG with new project guidelines, and implement a function to retrieve user Documents folder path in reset_machine_manual.py and totally_reset_cursor.py. 2025-04-16 11:33:17 +08:00
33 changed files with 4747 additions and 128 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.10.01
VERSION=1.10.01
version=1.11.01
VERSION=1.11.01

View File

@@ -43,7 +43,7 @@ body:
attributes:
label: 版本
description: 您正在运行的 Cursor Free Vip 版本是什么?
placeholder: 例如 v1.0.0
placeholder: 例如 v1.0.0 ( 不是 Cursor AI 版本 )
validations:
required: true

View File

@@ -43,7 +43,7 @@ body:
attributes:
label: Version
description: What version of Cursor Free Vip are you running?
placeholder: For example v1.0.0
placeholder: For example v1.0.0 ( Not Cursor AI Version )
validations:
required: true

View File

@@ -3,10 +3,14 @@ name: Build Executables
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g. 1.0.9)'
use_env_version:
description: 'Use version from .env file (yes/no)'
required: true
default: '1.9.02'
default: 'yes'
version:
description: 'Version number (only used if not using .env version)'
required: false
default: ''
permissions:
contents: write
@@ -14,27 +18,65 @@ permissions:
packages: write
jobs:
determine-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- uses: actions/checkout@v2
- name: Get version from .env file
id: env-version
if: ${{ github.event.inputs.use_env_version == 'yes' }}
run: |
VERSION=$(grep "^version=" .env | cut -d'=' -f2)
echo "ENV_VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using version from .env file: $VERSION"
- name: Use manual version
id: manual-version
if: ${{ github.event.inputs.use_env_version != 'yes' }}
run: |
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "Using manually entered version: ${{ github.event.inputs.version }}"
- name: Set final version
id: set-version
run: |
if [ "${{ github.event.inputs.use_env_version }}" == "yes" ]; then
echo "version=${{ env.ENV_VERSION }}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
fi
create-tag:
needs: determine-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # 获取所有标签
- name: Delete existing tag if exists
- name: Check if tag exists
id: check_tag
run: |
if git ls-remote --tags origin | grep -q "refs/tags/v${{ github.event.inputs.version }}"; then
git push origin --delete "v${{ github.event.inputs.version }}" || true
git tag -d "v${{ github.event.inputs.version }}" || true
if git ls-remote --tags origin | grep -q "refs/tags/v${{ needs.determine-version.outputs.version }}"; then
echo "Tag v${{ needs.determine-version.outputs.version }} already exists, will use existing tag"
echo "tag_exists=true" >> $GITHUB_OUTPUT
else
echo "Tag v${{ needs.determine-version.outputs.version }} does not exist, will create new tag"
echo "tag_exists=false" >> $GITHUB_OUTPUT
fi
- name: Create Tag
- name: Create Tag if not exists
if: steps.check_tag.outputs.tag_exists == 'false'
run: |
git tag "v${{ github.event.inputs.version }}"
git push origin "v${{ github.event.inputs.version }}"
git tag "v${{ needs.determine-version.outputs.version }}"
git push origin "v${{ needs.determine-version.outputs.version }}"
build-windows:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: windows-latest
steps:
@@ -47,7 +89,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -66,7 +108,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
build-macos-arm64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: macos-latest
steps:
@@ -79,7 +121,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -99,7 +141,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux-x64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: ubuntu-22.04
steps:
@@ -112,7 +154,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -136,7 +178,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
build-linux-arm64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: ubuntu-latest
steps:
@@ -149,7 +191,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Build in ARM64 Docker container
run: |
@@ -171,7 +213,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
build-macos-intel:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: macos-latest
steps:
@@ -184,7 +226,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -207,7 +249,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release:
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
needs: [determine-version, build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
runs-on: ubuntu-22.04
steps:
@@ -216,7 +258,7 @@ jobs:
- name: Get version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Download all artifacts
uses: actions/download-artifact@v4
@@ -279,7 +321,7 @@ jobs:
cat release_notes.md
- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ env.VERSION }}
body_path: release_notes.md

View File

@@ -1,5 +1,45 @@
# Change Log
## v1.11.01
0. Must Update to this version to get full experience | 必須更新到此版本以獲取完整體驗
1. Restore: Some Main Code | 恢復一些主程式碼
2. Add: Arabic language | 增加阿拉伯語
3. Add: Language configuration saved setting | 增加語言配置保存設定
4. Add: Restore Machine ID from Backup | 增加從備份恢復機器ID
5. Add: Owned Website Check Version | 增加擁有網站檢查版本
6. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
7. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
8. Fix: Some Issues | 修復一些問題
## v1.10.05
1. Remove block_domain.txt | 移除 block_domain.txt
2. Original Code In Github , If u afraid of virus, please clone the code and run locally | 原始碼在 Github 上,如果怕病毒,請複製原始碼並在本機運行
3. All Action using github workflow , not build myself , so i cant place virus in the file | 所有 Action 使用 github workflow ,不是我自己 build 的,所以我不會在文件中放置病毒
4. Fix: Some Issues | 修復一些問題
## v1.10.04
1. Hotfix: Reset Process Error: cannot access local variable 'main_path' where it is not associated with a value on windows & macos | 修復在 Windows 和 macOS 上無法訪問局部變量 'main_path' 的問題
2. Fix: Some Issues | 修復一些問題
## v1.10.03
1. Add: Manual Registration | 增加手動註冊
2. Only support your own Email | 只支持自己的Email 請勿使用Temp Email 註冊 註冊假賬號.
3. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
4. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
5. Fix: Some Issues | 修復一些問題
## v1.10.02
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
6. Use & Cherish | 切用且珍惜
7. Same as v1.10.01 | 與 v1.10.01 相同
8. Fix: reset machine ID no module name 'new_signup' | 修復機器 ID 重置 no module name 'new_signup'
9. Fix: Some Issues | 修復一些問題
## v1.10.01
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service

View File

@@ -26,8 +26,7 @@ a = Analysis(
('locales', 'locales'),
('quit_cursor.py', '.'),
('utils.py', '.'),
('.env', '.'),
('block_domain.txt', '.')
('.env', '.')
],
hiddenimports=[
'quit_cursor',

View File

@@ -83,10 +83,14 @@ def get_workbench_cursor_path(translator=None) -> str:
base_path = config.get('WindowsPaths', 'cursor_path')
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
main_path = os.path.join(base_path, paths_map[system]["main"])

View File

@@ -50,6 +50,8 @@ def get_product_json_path(translator=None):
elif system == "Darwin": # macOS
product_json_path = "/Applications/Cursor.app/Contents/Resources/app/product.json"
if config.has_section('MacPaths') and config.has_option('MacPaths', 'product_json_path'):
product_json_path = config.get('MacPaths', 'product_json_path')
elif system == "Linux":
# Try multiple common paths

View File

@@ -105,6 +105,12 @@ def setup_config(translator=None):
'Token': {
'refresh_server': 'https://token.cursorpro.com.cn',
'enable_refresh': True
},
'Language': {
'current_language': '', # Set by local system detection if empty
'fallback_language': 'en',
'auto_update_languages': 'True',
'language_cache_dir': os.path.join(config_dir, "language_cache")
}
}
@@ -364,4 +370,4 @@ def get_config(translator=None):
global _config_cache
if _config_cache is None:
_config_cache = setup_config(translator)
return _config_cache
return _config_cache

159
cursor_auth.py Normal file
View File

@@ -0,0 +1,159 @@
import sqlite3
import os
import sys
from colorama import Fore, Style, init
from config import get_config
# Initialize colorama
init()
# Define emoji and color constants
EMOJI = {
'DB': '🗄️',
'UPDATE': '🔄',
'SUCCESS': '',
'ERROR': '',
'WARN': '⚠️',
'INFO': '',
'FILE': '📄',
'KEY': '🔐'
}
class CursorAuth:
def __init__(self, translator=None):
self.translator = translator
# Get configuration
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
sys.exit(1)
# Get path based on operating system
try:
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
raise ValueError("Windows paths not configured")
self.db_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == 'linux': # Linux
if not config.has_section('LinuxPaths'):
raise ValueError("Linux paths not configured")
self.db_path = config.get('LinuxPaths', 'sqlite_path')
elif sys.platform == 'darwin': # macOS
if not config.has_section('MacPaths'):
raise ValueError("macOS paths not configured")
self.db_path = config.get('MacPaths', 'sqlite_path')
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
sys.exit(1)
# Verify if the path exists
if not os.path.exists(os.path.dirname(self.db_path)):
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
sys.exit(1)
# Check if the database file exists
if not os.path.exists(self.db_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path)}{Style.RESET_ALL}")
return
# Check file permissions
if not os.access(self.db_path, os.R_OK | os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_permission_error')}{Style.RESET_ALL}")
return
try:
self.conn = sqlite3.connect(self.db_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
except sqlite3.Error as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_connection_error', error=str(e))}{Style.RESET_ALL}")
return
def update_auth(self, email=None, access_token=None, refresh_token=None):
conn = None
try:
# Ensure the directory exists and set the correct permissions
db_dir = os.path.dirname(self.db_path)
if not os.path.exists(db_dir):
os.makedirs(db_dir, mode=0o755, exist_ok=True)
# If the database file does not exist, create a new one
if not os.path.exists(self.db_path):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS ItemTable (
key TEXT PRIMARY KEY,
value TEXT
)
''')
conn.commit()
if sys.platform != "win32":
os.chmod(self.db_path, 0o644)
conn.close()
# Reconnect to the database
conn = sqlite3.connect(self.db_path)
print(f"{EMOJI['INFO']} {Fore.GREEN} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
cursor = conn.cursor()
# Add timeout and other optimization settings
conn.execute("PRAGMA busy_timeout = 5000")
conn.execute("PRAGMA journal_mode = WAL")
conn.execute("PRAGMA synchronous = NORMAL")
# Set the key-value pairs to update
updates = []
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
if email is not None:
updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token))
# Use transactions to ensure data integrity
cursor.execute("BEGIN TRANSACTION")
try:
for key, value in updates:
# Check if the key exists
cursor.execute("SELECT COUNT(*) FROM ItemTable WHERE key = ?", (key,))
if cursor.fetchone()[0] == 0:
cursor.execute("""
INSERT INTO ItemTable (key, value)
VALUES (?, ?)
""", (key, value))
else:
cursor.execute("""
UPDATE ItemTable SET value = ?
WHERE key = ?
""", (value, key))
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('auth.updating_pair')} {key.split('/')[-1]}...{Style.RESET_ALL}")
cursor.execute("COMMIT")
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}{self.translator.get('auth.database_updated_successfully')}{Style.RESET_ALL}")
return True
except Exception as e:
cursor.execute("ROLLBACK")
raise e
except sqlite3.Error as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.database_error', error=str(e))}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.an_error_occurred', error=str(e))}{Style.RESET_ALL}")
return False
finally:
if conn:
conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

271
cursor_register_manual.py Normal file
View File

@@ -0,0 +1,271 @@
import os
from colorama import Fore, Style, init
import time
import random
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
from get_user_token import get_token_from_cookie
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': ''
}
class CursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.settings_url = "https://www.cursor.com/settings"
self.email_address = None
self.signup_tab = None
self.email_tab = None
# Generate account information
self.password = self._generate_password()
# Generate first name and last name separately
first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12):
"""Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def setup_email(self):
"""Setup Email"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}")
self.email_address = input().strip()
if '@' not in self.email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""Manually Get Verification Code"""
try:
print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}")
code = input().strip()
if not code.isdigit() or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}")
return None
return code
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}")
return None
def register_cursor(self):
"""Register Cursor"""
browser_tab = None
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# Use new_signup.py directly for registration
from new_signup import main as new_signup_main
# Execute new registration process, passing translator
result, browser_tab = new_signup_main(
email=self.email_address,
password=self.password,
first_name=self.first_name,
last_name=self.last_name,
email_tab=None, # No email tab needed
controller=self, # Pass self instead of self.controller
translator=self.translator
)
if result:
# Use the returned browser instance to get account information
self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info()
# Close browser after getting information
if browser_tab:
try:
browser_tab.quit()
except:
pass
return success
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False
finally:
# Ensure browser is closed in any case
if browser_tab:
try:
browser_tab.quit()
except:
pass
def _get_account_info(self):
"""Get Account Information and Token"""
try:
self.signup_tab.get(self.settings_url)
time.sleep(2)
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.signup_tab.ele(usage_selector)
total_usage = "未知"
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30
retry_interval = 2
attempts = 0
while attempts < max_attempts:
try:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = get_token_from_cookie(cookie["value"], self.translator)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
return False
def _save_account_info(self, token, total_usage):
"""Save Account Information to File"""
try:
# Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # Create instance with translator
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
return False
def start(self):
"""Start Registration Process"""
try:
if self.setup_email():
if self.register_cursor():
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
return True
return False
finally:
# Close email tab
if hasattr(self, 'temp_email'):
try:
self.temp_email.close()
except:
pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""Convenient function to update Cursor authentication information"""
auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token)
def main(translator=None):
"""Main function to be called from main.py"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
registration = CursorRegistration(translator)
registration.start()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
main(main_translator)

386
delete_cursor_google.py Normal file
View File

@@ -0,0 +1,386 @@
from oauth_auth import OAuthHandler
import time
from colorama import Fore, Style, init
import sys
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'DELETE': '🗑️',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
class CursorGoogleAccountDeleter(OAuthHandler):
def __init__(self, translator=None):
super().__init__(translator, auth_type='google')
def delete_google_account(self):
"""Delete Cursor account using Google OAuth"""
try:
# Setup browser and select profile
if not self.setup_browser():
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.starting_process') if self.translator else 'Starting account deletion process...'}{Style.RESET_ALL}")
# Navigate to Cursor auth page - using the same URL as in registration
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(2)
# Click Google auth button using same selectors as in registration
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn:
break
except:
continue
if not auth_btn:
raise Exception(self.translator.get('account_delete.google_button_not_found') if self.translator else "Google login button not found")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.logging_in') if self.translator else 'Logging in with Google...'}{Style.RESET_ALL}")
auth_btn.click()
# Wait for authentication to complete using a more robust method
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('account_delete.waiting_for_auth', fallback='Waiting for Google authentication...')}{Style.RESET_ALL}")
# Dynamic wait for authentication
max_wait_time = 120 # Increase maximum wait time to 120 seconds
start_time = time.time()
check_interval = 3 # Check every 3 seconds
google_account_alert_shown = False # Track if we've shown the alert already
while time.time() - start_time < max_wait_time:
current_url = self.browser.url
# If we're already on the settings or dashboard page, we're successful
if "/dashboard" in current_url or "/settings" in current_url or "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.login_successful') if self.translator else 'Login successful'}{Style.RESET_ALL}")
break
# If we're on Google accounts page or accounts.google.com, wait for user selection
if "accounts.google.com" in current_url:
# Only show the alert once to avoid spamming
if not google_account_alert_shown:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.select_google_account', fallback='Please select your Google account...')}{Style.RESET_ALL}")
# Alert to indicate user action needed
try:
self.browser.run_js("""
alert('Please select your Google account to continue with Cursor authentication');
""")
google_account_alert_shown = True # Mark that we've shown the alert
except:
pass # Alert is optional
# Sleep before checking again
time.sleep(check_interval)
else:
# If the loop completed without breaking, it means we hit the timeout
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.auth_timeout', fallback='Authentication timeout, continuing anyway...')}{Style.RESET_ALL}")
# Check current URL to determine next steps
current_url = self.browser.url
# If we're already on the settings page, no need to navigate
if "/settings" in current_url and "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.already_on_settings', fallback='Already on settings page')}{Style.RESET_ALL}")
# If we're on the dashboard or any Cursor page but not settings, navigate to settings
elif "cursor.com" in current_url or "authenticator.cursor.sh" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.navigating_to_settings', fallback='Navigating to settings page...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# If we're still on Google auth or somewhere else, try directly going to settings
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.login_redirect_failed', fallback='Login redirection failed, trying direct navigation...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# Wait for the settings page to load
time.sleep(3) # Reduced from 5 seconds
# First look for the email element to confirm we're logged in
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.found_email', email=email, fallback=f'Found email: {email}')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.email_not_found', error=str(e), fallback=f'Email not found: {str(e)}')}{Style.RESET_ALL}")
# Click on "Advanced" tab or dropdown - keep only the successful approach
advanced_found = False
# Direct JavaScript querySelector approach that worked according to logs
try:
advanced_element_js = self.browser.run_js("""
// Try to find the Advanced dropdown using querySelector with the exact classes
let advancedElement = document.querySelector('div.mb-0.flex.cursor-pointer.items-center.text-xs:not([style*="display: none"])');
// If not found, try a more general approach
if (!advancedElement) {
const allDivs = document.querySelectorAll('div');
for (const div of allDivs) {
if (div.textContent.includes('Advanced') &&
div.className.includes('mb-0') &&
div.className.includes('flex') &&
div.className.includes('cursor-pointer')) {
advancedElement = div;
break;
}
}
}
// Click the element if found
if (advancedElement) {
advancedElement.click();
return true;
}
return false;
""")
if advanced_element_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.advanced_tab_clicked', fallback='Found and clicked Advanced using direct JavaScript selector')}{Style.RESET_ALL}")
advanced_found = True
time.sleep(1) # Reduced from 2 seconds
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.advanced_tab_error', error=str(e), fallback='JavaScript querySelector approach failed: {str(e)}')}{Style.RESET_ALL}")
if not advanced_found:
# Fallback to direct URL navigation which is faster and more reliable
try:
self.browser.get("https://www.cursor.com/settings?tab=advanced")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('account_delete.direct_advanced_navigation', fallback='Trying direct navigation to advanced tab')}{Style.RESET_ALL}")
advanced_found = True
except:
raise Exception(self.translator.get('account_delete.advanced_tab_not_found') if self.translator else "Advanced option not found after multiple attempts")
# Wait for dropdown/tab content to load
time.sleep(2) # Reduced from 4 seconds
# Find and click the "Delete Account" button
delete_button_found = False
# Simplified approach for delete button based on what worked
delete_button_selectors = [
'xpath://button[contains(., "Delete Account")]',
'xpath://button[text()="Delete Account"]',
'xpath://div[contains(text(), "Delete Account")]',
'xpath://button[contains(text(), "Delete") and contains(text(), "Account")]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.delete_button_clicked') if self.translator else 'Clicked on Delete Account button'}{Style.RESET_ALL}")
delete_button_found = True
break
except:
continue
if not delete_button_found:
raise Exception(self.translator.get('account_delete.delete_button_not_found') if self.translator else "Delete Account button not found")
# Wait for confirmation dialog to appear
time.sleep(2)
# Check if we need to input "Delete" at all - some modals might not require it
input_required = True
try:
# Try detecting if the DELETE button is already enabled
delete_button_enabled = self.browser.run_js("""
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButtons = buttons.filter(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButtons.length > 0) {
return !deleteButtons.some(btn => btn.disabled);
}
return false;
""")
if delete_button_enabled:
print(f"{Fore.CYAN}{EMOJI['INFO']} DELETE button appears to be enabled already. Input may not be required.{Style.RESET_ALL}")
input_required = False
except:
pass
# Type "Delete" in the confirmation input - only if required
delete_input_found = False
if input_required:
# Try common selectors for the input field
delete_input_selectors = [
'xpath://input[@placeholder="Delete"]',
'xpath://div[contains(@class, "modal")]//input',
'xpath://input',
'css:input'
]
for selector in delete_input_selectors:
try:
delete_input = self.browser.ele(selector, timeout=3)
if delete_input:
delete_input.clear()
delete_input.input("Delete")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete', fallback='Typed \"Delete\" in confirmation box')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
# Try direct JavaScript input as fallback
try:
self.browser.run_js(r"""
arguments[0].value = "Delete";
const event = new Event('input', { bubbles: true });
arguments[0].dispatchEvent(event);
const changeEvent = new Event('change', { bubbles: true });
arguments[0].dispatchEvent(changeEvent);
""", delete_input)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
continue
if not delete_input_found:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.delete_input_not_found', fallback='Delete confirmation input not found, continuing anyway')}{Style.RESET_ALL}")
time.sleep(2)
# Wait before clicking the final DELETE button
time.sleep(2)
# Click on the final DELETE button
confirm_button_found = False
# Use JavaScript approach for the DELETE button
try:
delete_button_js = self.browser.run_js("""
// Try to find the DELETE button by exact text content
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButton = buttons.find(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButton) {
console.log("Found DELETE button with JavaScript");
deleteButton.click();
return true;
}
// If not found by text, try to find right-most button in the modal
const modalButtons = Array.from(document.querySelectorAll('.relative button, [role="dialog"] button, .modal button, [aria-modal="true"] button'));
if (modalButtons.length > 1) {
modalButtons.sort((a, b) => {
const rectA = a.getBoundingClientRect();
const rectB = b.getBoundingClientRect();
return rectB.right - rectA.right;
});
console.log("Clicking right-most button in modal");
modalButtons[0].click();
return true;
} else if (modalButtons.length === 1) {
console.log("Clicking single button found in modal");
modalButtons[0].click();
return true;
}
return false;
""")
if delete_button_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Clicked DELETE button')}{Style.RESET_ALL}")
confirm_button_found = True
except:
pass
if not confirm_button_found:
# Fallback to simple selectors
delete_button_selectors = [
'xpath://button[text()="DELETE"]',
'xpath://div[contains(@class, "modal")]//button[last()]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Account deleted successfully!')}{Style.RESET_ALL}")
confirm_button_found = True
break
except:
continue
if not confirm_button_found:
raise Exception(self.translator.get('account_delete.confirm_button_not_found') if self.translator else "Confirm button not found")
# Wait a moment to see the confirmation
time.sleep(2)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('account_delete.error', error=str(e)) if self.translator else f'Error during account deletion: {str(e)}'}{Style.RESET_ALL}")
return False
finally:
# Clean up browser
if self.browser:
try:
self.browser.quit()
except:
pass
def main(translator=None):
"""Main function to handle Google account deletion"""
print(f"\n{Fore.CYAN}{EMOJI['START']} {translator.get('account_delete.title') if translator else 'Cursor Google Account Deletion Tool'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
deleter = CursorGoogleAccountDeleter(translator)
try:
# Ask for confirmation
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('account_delete.warning') if translator else 'WARNING: This will permanently delete your Cursor account. This action cannot be undone.'}{Style.RESET_ALL}")
confirm = input(f"{Fore.RED} {translator.get('account_delete.confirm_prompt') if translator else 'Are you sure you want to proceed? (y/N): '}{Style.RESET_ALL}").lower()
if confirm != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.cancelled') if translator else 'Account deletion cancelled.'}{Style.RESET_ALL}")
return
success = deleter.delete_google_account()
if success:
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('account_delete.success') if translator else 'Your Cursor account has been successfully deleted!'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.failed') if translator else 'Account deletion process failed or was cancelled.'}{Style.RESET_ALL}")
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.interrupted') if translator else 'Account deletion process interrupted by user.'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
finally:
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
if __name__ == "__main__":
main()

769
locales/ar.json Normal file
View File

@@ -0,0 +1,769 @@
{
"menu": {
"title": "الخيارات المتاحة",
"exit": "خروج من البرنامج",
"reset": "إعادة تعيين معرف الجهاز",
"register": "تسجيل حساب Cursor جديد",
"register_google": "تسجيل باستخدام حساب جوجل",
"register_github": "تسجيل باستخدام حساب GitHub",
"register_manual": "تسجيل Cursor باستخدام بريد إلكتروني مخصص",
"quit": "إغلاق تطبيق Cursor",
"select_language": "تغيير اللغة",
"select_chrome_profile": "اختيار ملف تعريف Chrome",
"input_choice": "الرجاء إدخال اختيارك ({choices})",
"invalid_choice": "اختيار غير صالح. الرجاء إدخال رقم من {choices}",
"program_terminated": "تم إنهاء البرنامج بواسطة المستخدم",
"error_occurred": "حدث خطأ: {error}. يرجى المحاولة مرة أخرى",
"press_enter": "اضغط Enter للخروج",
"disable_auto_update": "تعطيل التحديث التلقائي لـ Cursor",
"lifetime_access_enabled": "تم تفعيل الوصول مدى الحياة",
"totally_reset": "إعادة تعيين Cursor بالكامل",
"outdate": "قديم",
"temp_github_register": "تسجيل GitHub مؤقت",
"admin_required": "يجب تشغيل البرنامج كمسؤول عند التنفيذ.",
"admin_required_continue": "المتابعة بدون صلاحيات المسؤول.",
"coming_soon": "قريباً",
"fixed_soon": "سيتم إصلاحه قريباً",
"contribute": "المساهمة في المشروع",
"config": "عرض الإعدادات",
"delete_google_account": "حذف حساب Cursor المرتبط بجوجل",
"continue_prompt": "المتابعة؟ (y/N): ",
"operation_cancelled_by_user": "تم إلغاء العملية بواسطة المستخدم",
"exiting": "جاري الخروج ……",
"bypass_version_check": "تجاوز فحص إصدار Cursor",
"check_user_authorized": "فحص صلاحية المستخدم",
"bypass_token_limit": "تجاوز حد الرمز المميز (Token)"
},
"languages": {
"en": "الإنجليزية",
"zh_cn": "الصينية المبسطة",
"zh_tw": "الصينية التقليدية",
"vi": "الفيتنامية",
"nl": "الهولندية",
"de": "الألمانية",
"fr": "الفرنسية",
"pt": "البرتغالية",
"ru": "الروسية",
"tr": "التركية",
"bg": "البلغارية",
"es": "الإسبانية",
"ar": "العربية"
},
"quit_cursor": {
"start": "بدء إغلاق Cursor",
"no_process": "لا توجد عمليات Cursor قيد التشغيل",
"terminating": "إنهاء العملية {pid}",
"waiting": "في انتظار إنهاء العملية",
"success": "تم إغلاق جميع عمليات Cursor",
"timeout": "انتهت مهلة العملية: {pids}",
"error": "حدث خطأ: {error}"
},
"reset": {
"title": "أداة إعادة تعيين معرف جهاز Cursor",
"checking": "جارٍ فحص ملف الإعدادات",
"not_found": "لم يتم العثور على ملف الإعدادات",
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
"reading": "جارٍ قراءة الإعدادات الحالية",
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
"generating": "جارٍ إنشاء معرف جهاز جديد",
"saving_json": "جارٍ حفظ الإعدادات الجديدة في JSON",
"success": "تم إعادة تعيين معرف الجهاز بنجاح",
"new_id": "معرف الجهاز الجديد",
"permission_error": "خطأ في الصلاحيات: {error}",
"run_as_admin": "يرجى محاولة تشغيل هذا البرنامج كمسؤول",
"process_error": "خطأ في عملية الإعادة: {error}",
"updating_sqlite": "جارٍ تحديث قاعدة بيانات SQLite",
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
"sqlite_success": "تم تحديث قاعدة بيانات SQLite بنجاح",
"sqlite_error": "فشل تحديث قاعدة بيانات SQLite: {error}",
"press_enter": "اضغط Enter للخروج",
"unsupported_os": "نظام تشغيل غير مدعوم: {os}",
"linux_path_not_found": "لم يتم العثور على مسار Linux",
"updating_system_ids": "جارٍ تحديث معرفات النظام",
"system_ids_updated": "تم تحديث معرفات النظام بنجاح",
"system_ids_update_failed": "فشل تحديث معرفات النظام: {error}",
"windows_guid_updated": "تم تحديث Windows GUID بنجاح",
"windows_permission_denied": "تم رفض الصلاحية في Windows",
"windows_guid_update_failed": "فشل تحديث Windows GUID",
"macos_uuid_updated": "تم تحديث macOS UUID بنجاح",
"plutil_command_failed": "فشل أمر plutil",
"start_patching": "بدء تصحيح getMachineId",
"macos_uuid_update_failed": "فشل تحديث macOS UUID",
"current_version": "إصدار Cursor الحالي: {version}",
"patch_completed": "تم تصحيح getMachineId بنجاح",
"patch_failed": "فشل تصحيح getMachineId: {error}",
"version_check_passed": "تم اجتياز فحص إصدار Cursor",
"file_modified": "تم تعديل الملف",
"version_less_than_0_45": "إصدار Cursor < 0.45.0، تخطي تصحيح getMachineId",
"detecting_version": "جارٍ اكتشاف إصدار Cursor",
"patching_getmachineid": "جارٍ تصحيح getMachineId",
"version_greater_than_0_45": "إصدار Cursor >= 0.45.0، جارٍ تصحيح getMachineId",
"permission_denied": "تم رفض الصلاحية: {error}",
"backup_created": "تم إنشاء نسخة احتياطية",
"update_success": "تم التحديث بنجاح",
"update_failed": "فشل التحديث: {error}",
"windows_machine_guid_updated": "تم تحديث Windows Machine GUID بنجاح",
"reading_package_json": "جارٍ قراءة package.json {path}",
"invalid_json_object": "كائن JSON غير صالح",
"no_version_field": "لم يتم العثور على حقل الإصدار في package.json",
"version_field_empty": "حقل الإصدار فارغ",
"invalid_version_format": "تنسيق إصدار غير صالح: {version}",
"found_version": "تم العثور على الإصدار: {version}",
"version_parse_error": "خطأ في تحليل الإصدار: {error}",
"package_not_found": "لم يتم العثور على package.json: {path}",
"check_version_failed": "فشل فحص الإصدار: {error}",
"stack_trace": "تتبع المكدس",
"version_too_low": "إصدار Cursor منخفض جداً: {version} < 0.45.0",
"no_write_permission": "لا توجد صلاحيات كتابة: {path}",
"path_not_found": "لم يتم العثور على المسار: {path}",
"modify_file_failed": "فشل تعديل الملف: {error}",
"windows_machine_id_updated": "تم تحديث Windows Machine ID بنجاح",
"update_windows_machine_id_failed": "فشل تحديث Windows Machine ID: {error}",
"update_windows_machine_guid_failed": "فشل تحديث Windows Machine GUID: {error}",
"file_not_found": "لم يتم العثور على الملف: {path}"
},
"register": {
"title": "أداة تسجيل Cursor",
"start": "بدء عملية التسجيل...",
"handling_turnstile": "جارٍ معالجة التحقق الأمني...",
"retry_verification": "إعادة محاولة التحقق...",
"detect_turnstile": "جارٍ التحقق من الأمان...",
"verification_success": "تم التحقق الأمني بنجاح",
"starting_browser": "جارٍ فتح المتصفح...",
"form_success": "تم إرسال النموذج بنجاح",
"browser_started": "تم فتح المتصفح بنجاح",
"waiting_for_second_verification": "في انتظار التحقق عبر البريد الإلكتروني...",
"waiting_for_verification_code": "في انتظار رمز التحقق...",
"password_success": "تم تعيين كلمة المرور بنجاح",
"password_error": "تعذر تعيين كلمة المرور: {error}. يرجى المحاولة مرة أخرى",
"waiting_for_page_load": "جارٍ تحميل الصفحة...",
"first_verification_passed": "تم اجتياز التحقق الأولي بنجاح",
"mailbox": "تم الوصول إلى صندوق البريد بنجاح",
"register_start": "بدء التسجيل",
"form_submitted": "تم إرسال النموذج، بدء التحقق...",
"filling_form": "تعبئة النموذج",
"visiting_url": "جارٍ زيارة الرابط",
"basic_info": "تم إرسال المعلومات الأساسية",
"handle_turnstile": "معالجة Turnstile",
"no_turnstile": "لم يتم اكتشاف Turnstile",
"turnstile_passed": "تم اجتياز Turnstile",
"verification_start": "بدء الحصول على رمز التحقق",
"verification_timeout": "انتهت مهلة الحصول على رمز التحقق",
"verification_not_found": "لم يتم العثور على رمز تحقق",
"try_get_code": "محاولة | {attempt} الحصول على رمز التحقق | الوقت المتبقي: {time}ثانية",
"get_account": "جارٍ الحصول على معلومات الحساب",
"get_token": "الحصول على رمز جلسة Cursor",
"token_success": "تم الحصول على الرمز بنجاح",
"token_attempt": "محاولة | {attempt} مرات للحصول على الرمز | سيتم إعادة المحاولة بعد {time}ثانية",
"token_max_attempts": "تم الوصول إلى الحد الأقصى للمحاولات ({max}) | فشل الحصول على الرمز",
"token_failed": "فشل الحصول على الرمز: {error}",
"account_error": "فشل الحصول على معلومات الحساب: {error}",
"press_enter": "اضغط Enter للخروج",
"browser_start": "بدء تشغيل المتصفح",
"open_mailbox": "جارٍ فتح صندوق البريد",
"email_error": "فشل الحصول على عنوان البريد الإلكتروني",
"setup_error": "خطأ في إعداد البريد الإلكتروني: {error}",
"start_getting_verification_code": "بدء الحصول على رمز التحقق، سيتم المحاولة خلال 60 ثانية",
"get_verification_code_timeout": "انتهت مهلة الحصول على رمز التحقق",
"get_verification_code_success": "تم الحصول على رمز التحقق بنجاح",
"try_get_verification_code": "محاولة | {attempt} الحصول على رمز التحقق | الوقت المتبقي: {remaining_time}ثانية",
"verification_code_filled": "تم تعبئة رمز التحقق",
"login_success_and_jump_to_settings_page": "تم تسجيل الدخول بنجاح والانتقال إلى صفحة الإعدادات",
"detect_login_page": "تم اكتشاف صفحة تسجيل الدخول، بدء التسجيل...",
"cursor_registration_completed": "تم تسجيل Cursor بنجاح!",
"set_password": "تعيين كلمة المرور",
"basic_info_submitted": "تم إرسال المعلومات الأساسية",
"cursor_auth_info_updated": "تم تحديث معلومات مصادقة Cursor",
"cursor_auth_info_update_failed": "فشل تحديث معلومات مصادقة Cursor",
"reset_machine_id": "إعادة تعيين معرف الجهاز",
"account_info_saved": "تم حفظ معلومات الحساب",
"save_account_info_failed": "فشل حفظ معلومات الحساب",
"get_email_address": "الحصول على عنوان البريد الإلكتروني",
"update_cursor_auth_info": "تحديث معلومات مصادقة Cursor",
"register_process_error": "خطأ في عملية التسجيل: {error}",
"setting_password": "جارٍ تعيين كلمة المرور",
"manual_code_input": "إدخال الرمز يدوياً",
"manual_email_input": "إدخال البريد الإلكتروني يدوياً",
"password": "كلمة المرور",
"first_name": "الاسم الأول",
"last_name": "الاسم الأخير",
"exit_signal": "إشارة خروج",
"email_address": "عنوان البريد الإلكتروني",
"config_created": "تم إنشاء الإعدادات",
"verification_failed": "فشل التحقق",
"verification_error": "خطأ في التحقق: {error}",
"config_option_added": "تمت إضافة خيار الإعدادات: {option}",
"config_updated": "تم تحديث الإعدادات",
"password_submitted": "تم إرسال كلمة المرور",
"total_usage": "إجمالي الاستخدام: {usage}",
"setting_on_password": "جارٍ تعيين كلمة المرور",
"getting_code": "جارٍ الحصول على رمز التحقق، سيتم المحاولة خلال 60 ثانية",
"human_verify_error": "تعذر التحقق من أن المستخدم بشري. جارٍ إعادة المحاولة...",
"max_retries_reached": "تم الوصول إلى الحد الأقصى للمحاولات. فشل التسجيل.",
"browser_path_invalid": "مسار {browser} غير صالح، جارٍ استخدام المسار الافتراضي",
"using_browser": "جارٍ استخدام متصفح {browser}: {path}",
"using_browser_profile": "جارٍ استخدام ملف تعريف {browser} من: {user_data_dir}",
"make_sure_browser_is_properly_installed": "تأكد من تثبيت {browser} بشكل صحيح",
"try_install_browser": "حاول تثبيت المتصفح باستخدام مدير الحزم الخاص بك",
"tracking_processes": "جارٍ تتبع {count} عمليات {browser}",
"no_new_processes_detected": "لم يتم اكتشاف عمليات {browser} جديدة للتتبع",
"could_not_track_processes": "تعذر تتبع عمليات {browser}: {error}"
},
"auth": {
"title": "مدير مصادقة Cursor",
"checking_auth": "جارٍ فحص ملف المصادقة",
"auth_not_found": "لم يتم العثور على ملف المصادقة",
"auth_file_error": "خطأ في ملف المصادقة: {error}",
"reading_auth": "جارٍ قراءة ملف المصادقة",
"updating_auth": "جارٍ تحديث معلومات المصادقة",
"auth_updated": "تم تحديث معلومات المصادقة بنجاح",
"auth_update_failed": "فشل تحديث معلومات المصادقة: {error}",
"auth_file_created": "تم إنشاء ملف المصادقة",
"auth_file_create_failed": "فشل إنشاء ملف المصادقة: {error}",
"press_enter": "اضغط Enter للخروج",
"reset_machine_id": "إعادة تعيين معرف الجهاز",
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
"connected_to_database": "تم الاتصال بقاعدة البيانات",
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
"db_not_found": "لم يتم العثور على ملف قاعدة البيانات في: {path}",
"db_permission_error": "لا يمكن الوصول إلى ملف قاعدة البيانات. يرجى التحقق من الصلاحيات",
"db_connection_error": "فشل الاتصال بقاعدة البيانات: {error}"
},
"control": {
"generate_email": "جارٍ إنشاء بريد إلكتروني جديد",
"blocked_domain": "نطاق محظور",
"select_domain": "جارٍ اختيار نطاق عشوائي",
"copy_email": "جارٍ نسخ عنوان البريد الإلكتروني",
"enter_mailbox": "جارٍ الدخول إلى صندوق البريد",
"refresh_mailbox": "جارٍ تحديث صندوق البريد",
"check_verification": "جارٍ التحقق من رمز التحقق",
"verification_found": "تم العثور على رمز التحقق",
"verification_not_found": "لم يتم العثور على رمز تحقق",
"browser_error": "خطأ في التحكم بالمتصفح: {error}",
"navigation_error": "خطأ في التنقل: {error}",
"email_copy_error": "خطأ في نسخ البريد الإلكتروني: {error}",
"mailbox_error": "خطأ في صندوق البريد: {error}",
"token_saved_to_file": "تم حفظ الرمز في ملف cursor_tokens.txt",
"navigate_to": "جارٍ الانتقال إلى {url}",
"generate_email_success": "تم إنشاء البريد الإلكتروني بنجاح",
"select_email_domain": "اختيار نطاق البريد الإلكتروني",
"select_email_domain_success": "تم اختيار نطاق البريد الإلكتروني بنجاح",
"get_email_name": "الحصول على اسم البريد الإلكتروني",
"get_email_name_success": "تم الحصول على اسم البريد الإلكتروني بنجاح",
"get_email_address": "الحصول على عنوان البريد الإلكتروني",
"get_email_address_success": "تم الحصول على عنوان البريد الإلكتروني بنجاح",
"enter_mailbox_success": "تم الدخول إلى صندوق البريد بنجاح",
"found_verification_code": "تم العثور على رمز التحقق",
"get_cursor_session_token": "الحصول على رمز جلسة Cursor",
"get_cursor_session_token_success": "تم الحصول على رمز جلسة Cursor بنجاح",
"get_cursor_session_token_failed": "فشل الحصول على رمز جلسة Cursor",
"save_token_failed": "فشل حفظ الرمز",
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
"no_valid_verification_code": "لا يوجد رمز تحقق صالح"
},
"email": {
"starting_browser": "جارٍ بدء تشغيل المتصفح",
"visiting_site": "جارٍ زيارة نطاقات البريد",
"create_success": "تم إنشاء البريد الإلكتروني بنجاح",
"create_failed": "فشل إنشاء البريد الإلكتروني",
"create_error": "خطأ في إنشاء البريد الإلكتروني: {error}",
"refreshing": "جارٍ تحديث البريد الإلكتروني",
"refresh_success": "تم تحديث البريد الإلكتروني بنجاح",
"refresh_error": "خطأ في تحديث البريد الإلكتروني: {error}",
"refresh_button_not_found": "لم يتم العثور على زر التحديث",
"verification_found": "تم العثور على التحقق",
"verification_not_found": "لم يتم العثور على التحقق",
"verification_error": "خطأ في التحقق: {error}",
"verification_code_found": "تم العثور على رمز التحقق",
"verification_code_not_found": "لم يتم العثور على رمز تحقق",
"verification_code_error": "خطأ في رمز التحقق: {error}",
"address": "عنوان البريد الإلكتروني",
"all_domains_blocked": "جميع النطاقات محظورة، جارٍ التحويل إلى خدمة أخرى",
"no_available_domains_after_filtering": "لا توجد نطاقات متاحة بعد التصفية",
"switching_service": "جارٍ التحويل إلى خدمة {service}",
"domains_list_error": "فشل الحصول على قائمة النطاقات: {error}",
"failed_to_get_available_domains": "فشل الحصول على نطاقات متاحة",
"domains_excluded": "النطاقات المستثناة: {domains}",
"failed_to_create_account": "فشل إنشاء الحساب",
"account_creation_error": "خطأ في إنشاء الحساب: {error}",
"blocked_domains": "النطاقات المحظورة: {domains}",
"blocked_domains_loaded": "تم تحميل النطاقات المحظورة: {count}",
"blocked_domains_loaded_error": "خطأ في تحميل النطاقات المحظورة: {error}",
"blocked_domains_loaded_success": "تم تحميل النطاقات المحظورة بنجاح",
"blocked_domains_loaded_timeout": "انتهت مهلة تحميل النطاقات المحظورة: {timeout}ثانية",
"blocked_domains_loaded_timeout_error": "خطأ في مهلة تحميل النطاقات المحظورة: {error}",
"available_domains_loaded": "تم تحميل النطاقات المتاحة: {count}",
"domains_filtered": "تم تصفية النطاقات: {count}",
"trying_to_create_email": "جارٍ محاولة إنشاء بريد إلكتروني: {email}",
"domain_blocked": "النطاق محظور: {domain}",
"using_chrome_profile": "جارٍ استخدام ملف تعريف Chrome من: {user_data_dir}",
"no_display_found": "لم يتم العثور على شاشة. تأكد من تشغيل خادم X.",
"try_export_display": "حاول: export DISPLAY=:0",
"extension_load_error": "خطأ في تحميل الامتداد: {error}",
"make_sure_chrome_chromium_is_properly_installed": "تأكد من تثبيت Chrome/Chromium بشكل صحيح",
"try_install_chromium": "حاول: sudo apt install chromium-browser"
},
"update": {
"title": "تعطيل التحديث التلقائي لـ Cursor",
"disable_success": "تم تعطيل التحديث التلقائي بنجاح",
"disable_failed": "فشل تعطيل التحديث التلقائي: {error}",
"press_enter": "اضغط Enter للخروج",
"start_disable": "بدء تعطيل التحديث التلقائي",
"killing_processes": "جارٍ إنهاء العمليات",
"processes_killed": "تم إنهاء العمليات",
"removing_directory": "جارٍ إزالة الدليل",
"directory_removed": "تمت إزالة الدليل",
"creating_block_file": "جارٍ إنشاء ملف حظر",
"block_file_created": "تم إنشاء ملف الحظر",
"clearing_update_yml": "جارٍ مسح ملف update.yml",
"update_yml_cleared": "تم مسح ملف update.yml",
"update_yml_not_found": "لم يتم العثور على ملف update.yml",
"clear_update_yml_failed": "فشل مسح ملف update.yml: {error}",
"unsupported_os": "نظام تشغيل غير مدعوم: {system}",
"remove_directory_failed": "فشل إزالة الدليل: {error}",
"create_block_file_failed": "فشل إنشاء ملف الحظر: {error}",
"directory_locked": "الدليل مقفل: {path}",
"yml_locked": "ملف update.yml مقفل",
"block_file_locked": "ملف الحظر مقفل",
"yml_already_locked": "ملف update.yml مقفل بالفعل",
"block_file_already_locked": "ملف الحظر مقفل بالفعل",
"block_file_locked_error": "خطأ في قفل ملف الحظر: {error}",
"yml_locked_error": "خطأ في قفل ملف update.yml: {error}",
"block_file_already_locked_error": "خطأ في قفل ملف الحظر الموجود بالفعل: {error}",
"yml_already_locked_error": "خطأ في قفل ملف update.yml الموجود بالفعل: {error}"
},
"updater": {
"checking": "جارٍ التحقق من التحديثات...",
"new_version_available": "يتوفر إصدار جديد! (الحالي: {current}, الأحدث: {latest})",
"updating": "جارٍ التحديث إلى أحدث إصدار. سيعيد البرنامج التشغيل تلقائياً.",
"up_to_date": "أنت تستخدم أحدث إصدار.",
"check_failed": "فشل التحقق من التحديثات: {error}",
"continue_anyway": "المتابعة باستخدام الإصدار الحالي...",
"update_confirm": "هل تريد التحديث إلى أحدث إصدار؟ (Y/n)",
"update_skipped": "تخطي التحديث.",
"invalid_choice": "اختيار غير صالح. الرجاء إدخال 'Y' أو 'n'.",
"development_version": "إصدار التطوير {current} > {latest}",
"changelog_title": "سجل التغييرات",
"rate_limit_exceeded": "تم تجاوز حد معدل GitHub API. تخطي فحص التحديث."
},
"totally_reset": {
"title": "إعادة تعيين Cursor بالكامل",
"checking_config": "جارٍ فحص ملف الإعدادات",
"config_not_found": "لم يتم العثور على ملف الإعدادات",
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
"reading_config": "جارٍ قراءة الإعدادات الحالية",
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
"generating_new_machine_id": "جارٍ إنشاء معرف جهاز جديد",
"saving_new_config": "جارٍ حفظ الإعدادات الجديدة في JSON",
"success": "تم إعادة تعيين Cursor بنجاح",
"error": "فشل إعادة تعيين Cursor: {error}",
"press_enter": "اضغط Enter للخروج",
"reset_machine_id": "إعادة تعيين معرف الجهاز",
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
"connected_to_database": "تم الاتصال بقاعدة البيانات",
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
"db_not_found": "لم يتم العثور على ملف قاعدة البيانات في: {path}",
"db_permission_error": "لا يمكن الوصول إلى ملف قاعدة البيانات. يرجى التحقق من الصلاحيات",
"db_connection_error": "فشل الاتصال بقاعدة البيانات: {error}",
"feature_title": "الميزات",
"feature_1": "إزالة كاملة لإعدادات وتكوينات Cursor AI",
"feature_2": "مسح جميع البيانات المخزنة مؤقتاً بما في ذلك سجل الذكاء الاصطناعي",
"feature_3": "إعادة تعيين معرف الجهاز لتجاوز كشف الفترة التجريبية",
"feature_4": "إنشاء معرفات أجهزة جديدة عشوائية",
"feature_5": "إزالة الامتدادات المخصصة والتفضيلات",
"feature_6": "إعادة تعيين معلومات الفترة التجريبية وبيانات التفعيل",
"feature_7": "فحص عميق لملفات الرخصة والملفات المتعلقة بالفترة التجريبية المخفية",
"feature_8": "الحفاظ على الملفات والتطبيقات غير المتعلقة بـ Cursor بأمان",
"feature_9": "متوافق مع Windows وmacOS وLinux",
"disclaimer_title": "تنبيه",
"disclaimer_1": "ستقوم هذه الأداة بحذف جميع إعدادات Cursor AI،",
"disclaimer_2": "والتكوينات والبيانات المخزنة مؤقتاً بشكل دائم. لا يمكن التراجع عن هذا الإجراء.",
"disclaimer_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
"disclaimer_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
"disclaimer_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
"disclaimer_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
"disclaimer_7": "استخدمها على مسؤوليتك الخاصة",
"confirm_title": "هل أنت متأكد أنك تريد المتابعة؟",
"confirm_1": "سيؤدي هذا الإجراء إلى حذف جميع إعدادات Cursor AI،",
"confirm_2": "والتكوينات والبيانات المخزنة مؤقتاً. لا يمكن التراجع عن هذا الإجراء.",
"confirm_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
"confirm_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
"confirm_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
"confirm_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
"confirm_7": "استخدمها على مسؤوليتك الخاصة",
"invalid_choice": "الرجاء إدخال 'Y' أو 'n'",
"skipped_for_safety": "تم تخطيه لأسباب أمنية (غير متعلق بـ Cursor): {path}",
"deleted": "تم الحذف: {path}",
"error_deleting": "خطأ في حذف {path}: {error}",
"not_found": "لم يتم العثور على الملف: {path}",
"resetting_machine_id": "جارٍ إعادة تعيين معرفات الجهاز لتجاوز كشف الفترة التجريبية...",
"created_machine_id": "تم إنشاء معرف جهاز جديد: {path}",
"error_creating_machine_id": "خطأ في إنشاء ملف معرف الجهاز {path}: {error}",
"error_searching": "خطأ في البحث عن الملفات في {path}: {error}",
"created_extended_trial_info": "تم إنشاء معلومات الفترة التجريبية الممتدة: {path}",
"error_creating_trial_info": "خطأ في إنشاء ملف معلومات الفترة التجريبية {path}: {error}",
"resetting_cursor_ai_editor": "جارٍ إعادة تعيين محرر Cursor AI... يرجى الانتظار.",
"reset_cancelled": "تم إلغاء الإعادة. الخروج دون إجراء أي تغييرات.",
"windows_machine_id_modification_skipped": "تم تخطي تعديل معرف جهاز Windows: {error}",
"linux_machine_id_modification_skipped": "تم تخطي تعديل machine-id في Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل تشغيل البرنامج كمسؤول",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل صلاحيات sudo",
"windows_registry_instructions": "📝 ملاحظة: للإعادة الكاملة على Windows، قد تحتاج أيضاً إلى تنظيف إدخالات التسجيل.",
"windows_registry_instructions_2": " قم بتشغيل 'regedit' وابحث عن المفاتيح التي تحتوي على 'Cursor' أو 'CursorAI' تحت HKEY_CURRENT_USER\\Software\\ واحذفها.\n",
"reset_log_1": "تمت إعادة تعيين Cursor AI بالكامل وتجاوز كشف الفترة التجريبية!",
"reset_log_2": "يرجى إعادة تشغيل النظام لتفعيل التغييرات.",
"reset_log_3": "ستحتاج إلى إعادة تثبيت Cursor AI ويجب أن تحصل الآن على فترة تجريبية جديدة.",
"reset_log_4": "للحصول على أفضل النتائج، ضع في الاعتبار أيضاً:",
"reset_log_5": "استخدم عنوان بريد إلكتروني مختلف عند التسجيل للحصول على فترة تجريبية جديدة",
"reset_log_6": "استخدم شبكة VPN لتغيير عنوان IP الخاص بك إذا كان ذلك متاحًا",
"reset_log_7": "قم بمسح ملفات تعريف الارتباط وذاكرة التخزين المؤقت للمتصفح قبل زيارة موقع Cursor AI",
"reset_log_8": "إذا استمرت المشكلات، حاول تثبيت Cursor AI في موقع مختلف",
"reset_log_9": "إذا واجهتك أي مشكلات، انتقل إلى متتبع المشكلات على Github وقم بإنشاء مشكلة جديدة على https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "حدث خطأ غير متوقع: {error}",
"report_issue": "يرجى الإبلاغ عن هذه المشكلة على متتبع المشكلات في Github على https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "تمت مقاطعة العملية بواسطة المستخدم. جارٍ الخروج...",
"return_to_main_menu": "جارٍ العودة إلى القائمة الرئيسية...",
"process_interrupted": "تمت مقاطعة العملية. جارٍ الخروج...",
"press_enter_to_return_to_main_menu": "اضغط على Enter للعودة إلى القائمة الرئيسية...",
"removing_known": "جارٍ إزالة ملفات الفترة التجريبية/الترخيص المعروفة",
"performing_deep_scan": "جارٍ إجراء فحص عميق للبحث عن ملفات ترخيص/تجريبية إضافية",
"found_additional_potential_license_trial_files": "تم العثور على {count} ملفات محتملة إضافية للترخيص/الفترة التجريبية",
"checking_for_electron_localstorage_files": "جارٍ التحقق من وجود ملفات التخزين المحلي لـ Electron",
"no_additional_license_trial_files_found_in_deep_scan": "لم يتم العثور على ملفات ترخيص/تجريبية إضافية في الفحص العميق",
"removing_electron_localstorage_files": "جارٍ إزالة ملفات التخزين المحلي لـ Electron",
"electron_localstorage_files_removed": "تمت إزالة ملفات التخزين المحلي لـ Electron",
"electron_localstorage_files_removal_error": "خطأ في إزالة ملفات التخزين المحلي لـ Electron: {error}",
"removing_electron_localstorage_files_completed": "اكتملت إزالة ملفات التخزين المحلي لـ Electron",
"warning_title": "تحذير",
"warning_1": "هذا الإجراء سيحذف جميع إعدادات Cursor AI،",
"warning_2": "والتكوينات والبيانات المخزنة مؤقتاً. لا يمكن التراجع عن هذا الإجراء.",
"warning_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
"warning_4": "لاستهداف ملفات محرر Cursor AI وآليات اكتشاف الفترة التجريبية فقط.",
"warning_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
"warning_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
"warning_7": "استخدم على مسؤوليتك الخاصة",
"removed": "تمت الإزالة: {path}",
"failed_to_reset_machine_guid": "فشل إعادة تعيين معرّف الجهاز",
"failed_to_remove": "فشل في الإزالة: {path}",
"failed_to_delete_file": "فشل في حذف الملف: {path}",
"failed_to_delete_directory": "فشل في حذف المجلد: {path}",
"failed_to_delete_file_or_directory": "فشل في حذف الملف أو المجلد: {path}",
"deep_scanning": "جارٍ إجراء فحص عميق للبحث عن ملفات ترخيص/تجريبية إضافية",
"resetting_cursor": "جارٍ إعادة تعيين محرر Cursor AI... يرجى الانتظار.",
"completed_in": "اكتمل في {time} ثانية",
"cursor_reset_completed": "تمت إعادة تعيين محرر Cursor AI بالكامل وتجاوز اكتشاف الفترة التجريبية!",
"cursor_reset_failed": "فشلت إعادة تعيين محرر Cursor AI: {error}",
"cursor_reset_cancelled": "تم إلغاء إعادة تعيين محرر Cursor AI. جارٍ الخروج دون إجراء أي تغييرات.",
"operation_cancelled": "تم إلغاء العملية. جارٍ الخروج دون إجراء أي تغييرات.",
"navigating_to_settings": "جارٍ الانتقال إلى صفحة الإعدادات...",
"already_on_settings": "أنت بالفعل في صفحة الإعدادات",
"login_redirect_failed": "فشلت إعادة توجيه تسجيل الدخول، جارٍ محاولة التنقل المباشر...",
"advanced_tab_not_found": "لم يتم العثور على علامة التبويب المتقدمة بعد عدة محاولات",
"advanced_tab_retry": "لم يتم العثور على علامة التبويب المتقدمة، المحاولة {attempt}/{max_attempts}",
"advanced_tab_error": "خطأ في العثور على علامة التبويب المتقدمة: {error}",
"advanced_tab_clicked": "تم النقر على علامة التبويب المتقدمة",
"direct_advanced_navigation": "جارٍ محاولة التنقل المباشر إلى علامة التبويب المتقدمة",
"delete_button_not_found": "لم يتم العثور على زر حذف الحساب بعد عدة محاولات",
"delete_button_retry": "لم يتم العثور على زر الحذف، المحاولة {attempt}/{max_attempts}",
"delete_button_error": "خطأ في العثور على زر الحذف: {error}",
"delete_button_clicked": "تم النقر على زر حذف الحساب",
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
"delete_input_not_found": "لم يتم العثور على حقل تأكيد الحذف بعد عدة محاولات",
"delete_input_retry": "لم يتم العثور على حقل الحذف، المحاولة {attempt}/{max_attempts}",
"delete_input_error": "خطأ في العثور على حقل الحذف: {error}",
"delete_input_not_found_continuing": "لم يتم العثور على حقل تأكيد الحذف، جارٍ محاولة المتابعة على أي حال"
},
"github_register": {
"title": "أتمتة تسجيل GitHub و Cursor AI",
"features_header": "الميزات",
"feature1": "ينشئ بريدًا إلكترونيًا مؤقتًا باستخدام 1secmail.",
"feature2": "يسجل حساب GitHub جديد ببيانات اعتماد عشوائية.",
"feature3": "يتحقق من بريد GitHub تلقائيًا.",
"feature4": "يسجل الدخول إلى Cursor AI باستخدام مصادقة GitHub.",
"feature5": "يعيد تعيين معرف الجهاز لتجاوز اكتشاف الفترة التجريبية.",
"feature6": "يحفظ جميع بيانات الاعتماد في ملف.",
"warnings_header": "تحذيرات",
"warning1": "تقوم هذه الأداة بأتمتة إنشاء الحساب، مما قد ينتهك شروط خدمة GitHub/Cursor.",
"warning2": "تتطلب وصولاً إلى الإنترنت وامتيازات المسؤول.",
"warning3": "قد تتعارض CAPTCHA أو عمليات التحقق الإضافية مع الأتمتة.",
"warning4": "استخدمها بمسؤولية وعلى مسؤوليتك الخاصة.",
"confirm": "هل أنت متأكد أنك تريد المتابعة؟",
"invalid_choice": "اختيار غير صالح. الرجاء إدخال 'yes' أو 'no'",
"cancelled": "تم إلغاء العملية",
"program_terminated": "تم إنهاء البرنامج بواسطة المستخدم",
"starting_automation": "جارٍ بدء الأتمتة...",
"github_username": "اسم مستخدم GitHub",
"github_password": "كلمة مرور GitHub",
"email_address": "عنوان البريد الإلكتروني",
"credentials_saved": "تم حفظ بيانات الاعتماد هذه في ملف github_cursor_accounts.txt",
"completed_successfully": "اكتمل تسجيل GitHub و Cursor بنجاح!",
"registration_encountered_issues": "واجه تسجيل GitHub و Cursor مشكلات.",
"check_browser_windows_for_manual_intervention_or_try_again_later": "تحقق من نوافذ المتصفح للتدخل اليدوي أو حاول مرة أخرى لاحقًا."
},
"account_info": {
"subscription": "الاشتراك",
"trial_remaining": "المتبقي من الفترة التجريبية Pro",
"days": "أيام",
"subscription_not_found": "لم يتم العثور على معلومات الاشتراك",
"email_not_found": "لم يتم العثور على البريد الإلكتروني",
"failed_to_get_account": "فشل في الحصول على معلومات الحساب",
"config_not_found": "لم يتم العثور على الإعدادات.",
"failed_to_get_usage": "فشل في الحصول على معلومات الاستخدام",
"failed_to_get_subscription": "فشل في الحصول على معلومات الاشتراك",
"failed_to_get_email": "فشل في الحصول على عنوان البريد الإلكتروني",
"failed_to_get_token": "فشل في الحصول على الرمز",
"failed_to_get_account_info": "فشل في الحصول على معلومات الحساب",
"title": "معلومات الحساب",
"email": "البريد الإلكتروني",
"token": "الرمز",
"usage": "الاستخدام",
"subscription_type": "نوع الاشتراك",
"remaining_trial": "الفترة التجريبية المتبقية",
"days_remaining": "الأيام المتبقية",
"premium": "مميز",
"pro": "محترف",
"pro_trial": "تجريبي محترف",
"team": "فريق",
"enterprise": "مؤسسة",
"free": "مجاني",
"active": "نشط",
"inactive": "غير نشط",
"premium_usage": "استخدام مميز",
"basic_usage": "استخدام أساسي",
"usage_not_found": "لم يتم العثور على الاستخدام",
"lifetime_access_enabled": "تم تمكين الوصول مدى الحياة",
"token_not_found": "لم يتم العثور على الرمز"
},
"config": {
"config_not_available": "الإعدادات غير متاحة",
"configuration": "الإعدادات",
"enabled": "ممكّن",
"disabled": "معطّل",
"config_directory": "دليل الإعدادات",
"neither_cursor_nor_cursor_directory_found": "لم يتم العثور على Cursor أو دليل Cursor في {config_base}",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "يرجى التأكد من تثبيت Cursor وتشغيله مرة واحدة على الأقل",
"storage_directory_not_found": "لم يتم العثور على دليل التخزين: {storage_dir}",
"storage_file_found": "تم العثور على ملف التخزين: {storage_path}",
"file_size": "حجم الملف: {size} بايت",
"file_permissions": "صلاحيات الملف: {permissions}",
"file_owner": "مالك الملف: {owner}",
"file_group": "مجموعة الملف: {group}",
"error_getting_file_stats": "خطأ في الحصول على إحصائيات الملف: {error}",
"permission_denied": "تم رفض الإذن: {storage_path}",
"try_running": "حاول تشغيل: {command}",
"and": "و",
"storage_file_is_empty": "ملف التخزين فارغ: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "قد يكون الملف تالفًا، يرجى إعادة تثبيت Cursor",
"storage_file_not_found": "لم يتم العثور على ملف التخزين: {storage_path}",
"error_checking_linux_paths": "خطأ في فحص مسارات Linux: {error}",
"config_option_added": "تمت إضافة خيار الإعدادات: {option}",
"config_updated": "تم تحديث الإعدادات",
"config_created": "تم إنشاء الإعدادات: {config_file}",
"config_setup_error": "خطأ في إعداد الإعدادات: {error}",
"storage_file_is_valid_and_contains_data": "ملف التخزين صالح ويحتوي على بيانات",
"error_reading_storage_file": "خطأ في قراءة ملف التخزين: {error}",
"also_checked": "تم فحص {path} أيضًا",
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
"config_removed": "تمت إزالة ملف الإعدادات للتحديث القسري",
"backup_failed": "فشل نسخ الإعدادات احتياطيًا: {error}",
"force_update_failed": "فشل تحديث الإعدادات القسري: {error}",
"config_force_update_disabled": "تم تعطيل التحديث القسري لملف الإعدادات، جارٍ تخطي التحديث القسري",
"config_force_update_enabled": "تم تمكين التحديث القسري لملف الإعدادات، جارٍ إجراء التحديث القسري",
"documents_path_not_found": "لم يتم العثور على مسار المستندات، جارٍ استخدام الدليل الحالي",
"config_dir_created": "تم إنشاء دليل الإعدادات: {path}",
"using_temp_dir": "جارٍ استخدام دليل مؤقت بسبب خطأ: {path} (الخطأ: {error})"
},
"oauth": {
"authentication_button_not_found": "لم يتم العثور على زر المصادقة",
"authentication_failed": "فشلت المصادقة: {error}",
"found_cookies": "تم العثور على {count} من ملفات تعريف الارتباط",
"token_extraction_error": "خطأ في استخراج الرمز: {error}",
"authentication_successful": "تمت المصادقة بنجاح - البريد الإلكتروني: {email}",
"missing_authentication_data": "بيانات المصادقة مفقودة: {data}",
"failed_to_delete_account": "فشل حذف الحساب: {error}",
"invalid_authentication_type": "نوع المصادقة غير صالح",
"auth_update_success": "تم تحديث المصادقة بنجاح",
"browser_closed": "تم إغلاق المتصفح",
"auth_update_failed": "فشل تحديث المصادقة",
"google_start": "بدء Google",
"github_start": "بدء Github",
"usage_count": "عدد مرات الاستخدام: {usage}",
"account_has_reached_maximum_usage": "وصل الحساب إلى الحد الأقصى للاستخدام، {deleting}",
"starting_new_authentication_process": "جارٍ بدء عملية مصادقة جديدة...",
"failed_to_delete_expired_account": "فشل حذف الحساب المنتهي",
"could_not_check_usage_count": "تعذر التحقق من عدد مرات الاستخدام: {error}",
"found_email": "تم العثور على البريد الإلكتروني: {email}",
"could_not_find_email": "تعذر العثور على البريد الإلكتروني: {error}",
"could_not_find_usage_count": "تعذر العثور على عدد مرات الاستخدام: {error}",
"already_on_settings_page": "أنت بالفعل في صفحة الإعدادات!",
"failed_to_extract_auth_info": "فشل استخراج معلومات المصادقة: {error}",
"no_chrome_profiles_found": "لم يتم العثور على ملفات تعريف Chrome، جارٍ استخدام الملف الافتراضي",
"found_default_chrome_profile": "تم العثور على ملف تعريف Chrome الافتراضي",
"using_first_available_chrome_profile": "جارٍ استخدام أول ملف تعريف Chrome متاح: {profile}",
"error_finding_chrome_profile": "خطأ في العثور على ملف تعريف Chrome، جارٍ استخدام الملف الافتراضي: {error}",
"initializing_browser_setup": "جارٍ تهيئة إعداد المتصفح...",
"detected_platform": "تم اكتشاف النظام: {platform}",
"running_as_root_warning": "التشغيل كمستخدم جذر غير مستحسن لأتمتة المتصفح",
"consider_running_without_sudo": "فكر في تشغيل البرنامج بدون sudo",
"no_compatible_browser_found": "لم يتم العثور على متصفح متوافق. يرجى تثبيت Google Chrome أو Chromium.",
"supported_browsers": "المتصفحات المدعومة لـ {platform}",
"using_browser_profile": "جارٍ استخدام ملف تعريف المتصفح: {profile}",
"starting_browser": "جارٍ بدء المتصفح في: {path}",
"browser_setup_completed": "تم إكمال إعداد المتصفح بنجاح",
"browser_setup_failed": "فشل إعداد المتصفح: {error}",
"try_running_without_sudo_admin": "حاول التشغيل بدون امتيازات sudo/المسؤول",
"redirecting_to_authenticator_cursor_sh": "جارٍ إعادة التوجيه إلى authenticator.cursor.sh...",
"starting_google_authentication": "جارٍ بدء مصادقة Google...",
"starting_github_authentication": "جارٍ بدء مصادقة GitHub...",
"waiting_for_authentication": "في انتظار المصادقة...",
"page_changed_checking_auth": "تغيرت الصفحة، جارٍ التحقق من المصادقة...",
"status_check_error": "خطأ في فحص الحالة: {error}",
"authentication_timeout": "انتهت مهلة المصادقة",
"account_is_still_valid": "الحساب لا يزال صالحًا (الاستخدام: {usage})",
"starting_re_authentication_process": "جارٍ بدء عملية إعادة المصادقة...",
"starting_new_google_authentication": "جارٍ بدء مصادقة Google جديدة...",
"failed_to_delete_account_or_re_authenticate": "فشل حذف الحساب أو إعادة المصادقة: {error}",
"navigating_to_authentication_page": "جارٍ الانتقال إلى صفحة المصادقة...",
"please_select_your_google_account_to_continue": "يرجى اختيار حساب Google الخاص بك للمتابعة...",
"found_browser_data_directory": "تم العثور على دليل بيانات المتصفح: {path}",
"authentication_successful_getting_account_info": "تمت المصادقة بنجاح، جارٍ الحصول على معلومات الحساب...",
"warning_could_not_kill_existing_browser_processes": "تحذير: تعذر إنهاء عمليات المتصفح الحالية: {error}",
"browser_failed_to_start": "فشل بدء تشغيل المتصفح: {error}",
"browser_failed": "فشل بدء تشغيل المتصفح: {error}",
"browser_failed_to_start_fallback": "فشل بدء تشغيل المتصفح: {error}",
"user_data_dir_not_found": "لم يتم العثور على دليل بيانات مستخدم {browser} في {path}، سيتم تجربة Chrome بدلاً من ذلك",
"error_getting_user_data_directory": "خطأ في الحصول على دليل بيانات المستخدم: {error}",
"warning_browser_close": "تحذير: سيؤدي هذا إلى إغلاق جميع عمليات {browser} قيد التشغيل",
"killing_browser_processes": "جارٍ إنهاء عمليات {browser}...",
"profile_selection_error": "خطأ أثناء اختيار الملف الشخصي: {error}",
"using_configured_browser_path": "جارٍ استخدام مسار {browser} المُكوّن: {path}",
"browser_not_found_trying_chrome": "تعذر العثور على {browser}، جارٍ تجربة Chrome بدلاً من ذلك",
"found_chrome_at": "تم العثور على Chrome في: {path}",
"found_browser_user_data_dir": "تم العثور على دليل بيانات مستخدم {browser}: {path}"
},
"browser_profile": {
"title": "اختيار ملف تعريف المتصفح",
"select_profile": "اختر ملف تعريف {browser} للاستخدام:",
"profile_list": "ملفات تعريف {browser} المتاحة:",
"default_profile": "ملف التعريف الافتراضي",
"profile": "ملف التعريف {number}",
"no_profiles": "لم يتم العثور على ملفات تعريف {browser}",
"error_loading": "خطأ في تحميل ملفات تعريف {browser}: {error}",
"profile_selected": "ملف التعريف المحدد: {profile}",
"invalid_selection": "اختيار غير صالح. يرجى المحاولة مرة أخرى."
},
"account_delete": {
"title": "أداة حذف حساب Cursor المرتبط بـ Google",
"warning": "تحذير: سيؤدي هذا إلى حذف حساب Cursor الخاص بك بشكل دائم. لا يمكن التراجع عن هذا الإجراء.",
"cancelled": "تم إلغاء حذف الحساب.",
"starting_process": "جارٍ بدء عملية حذف الحساب...",
"google_button_not_found": "لم يتم العثور على زر تسجيل الدخول بـ Google",
"logging_in": "جارٍ تسجيل الدخول باستخدام Google...",
"waiting_for_auth": "في انتظار مصادقة Google...",
"login_successful": "تم تسجيل الدخول بنجاح",
"unexpected_page": "صفحة غير متوقعة بعد تسجيل الدخول: {url}",
"trying_settings": "جارٍ محاولة الانتقال إلى صفحة الإعدادات...",
"select_google_account": "يرجى اختيار حساب Google الخاص بك...",
"auth_timeout": "انتهت مهلة المصادقة، جارٍ المتابعة على أي حال...",
"navigating_to_settings": "جارٍ الانتقال إلى صفحة الإعدادات...",
"already_on_settings": "أنت بالفعل في صفحة الإعدادات",
"login_redirect_failed": "فشلت إعادة توجيه تسجيل الدخول، جارٍ محاولة التنقل المباشر...",
"advanced_tab_not_found": "لم يتم العثور على علامة التبويب المتقدمة بعد عدة محاولات",
"advanced_tab_retry": "لم يتم العثور على علامة التبويب المتقدمة، المحاولة {attempt}/{max_attempts}",
"advanced_tab_error": "خطأ في العثور على علامة التبويب المتقدمة: {error}",
"advanced_tab_clicked": "تم النقر على علامة التبويب المتقدمة",
"direct_advanced_navigation": "جارٍ محاولة التنقل المباشر إلى علامة التبويب المتقدمة",
"delete_button_not_found": "لم يتم العثور على زر حذف الحساب بعد عدة محاولات",
"delete_button_retry": "لم يتم العثور على زر الحذف، المحاولة {attempt}/{max_attempts}",
"delete_button_error": "خطأ في العثور على زر الحذف: {error}",
"delete_button_clicked": "تم النقر على زر حذف الحساب",
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
"delete_input_not_found": "لم يتم العثور على حقل تأكيد الحذف بعد عدة محاولات",
"delete_input_retry": "لم يتم العثور على حقل الحذف، المحاولة {attempt}/{max_attempts}",
"delete_input_error": "خطأ في العثور على حقل الحذف: {error}",
"delete_input_not_found_continuing": "لم يتم العثور على حقل تأكيد الحذف، جارٍ محاولة المتابعة على أي حال",
"typed_delete": "تم كتابة \"Delete\" في مربع التأكيد",
"confirm_button_not_found": "لم يتم العثور على زر التأكيد بعد عدة محاولات",
"confirm_button_retry": "لم يتم العثور على زر التأكيد، المحاولة {attempt}/{max_attempts}",
"confirm_button_error": "خطأ في العثور على زر التأكيد: {error}",
"account_deleted": "تم حذف الحساب بنجاح!",
"error": "خطأ أثناء حذف الحساب: {error}",
"success": "تم حذف حساب Cursor الخاص بك بنجاح!",
"failed": "فشلت عملية حذف الحساب أو تم إلغاؤها.",
"interrupted": "تمت مقاطعة عملية حذف الحساب بواسطة المستخدم.",
"unexpected_error": "خطأ غير متوقع: {error}",
"found_email": "تم العثور على البريد الإلكتروني: {email}",
"email_not_found": "لم يتم العثور على البريد الإلكتروني: {error}",
"confirm_prompt": "هل أنت متأكد أنك تريد المتابعة؟ (y/N): "
},
"bypass": {
"starting": "جارٍ بدء تجاوز إصدار Cursor...",
"found_product_json": "تم العثور على product.json: {path}",
"no_write_permission": "لا توجد صلاحية كتابة للملف: {path}",
"read_failed": "فشلت قراءة product.json: {error}",
"current_version": "الإصدار الحالي: {version}",
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
"version_updated": "تم تحديث الإصدار من {old} إلى {new}",
"write_failed": "فشلت كتابة product.json: {error}",
"no_update_needed": "لا يلزم التحديث. الإصدار الحالي {version} بالفعل >= 0.46.0",
"bypass_failed": "فشل تجاوز الإصدار: {error}",
"stack_trace": "تتبع المكدس",
"localappdata_not_found": "لم يتم العثور على متغير بيئة LOCALAPPDATA",
"product_json_not_found": "لم يتم العثور على product.json في مسارات Linux الشائعة",
"unsupported_os": "نظام تشغيل غير مدعوم: {system}",
"file_not_found": "لم يتم العثور على الملف: {path}",
"title": "أداة تجاوز إصدار Cursor",
"description": "تقوم هذه الأداة بتعديل ملف product.json الخاص بـ Cursor لتجاوز قيود الإصدار",
"menu_option": "تجاوز فحص إصدار Cursor"
},
"auth_check": {
"checking_authorization": "جارٍ التحقق من التصريح...",
"token_source": "الحصول على الرمز من قاعدة البيانات أو إدخاله يدوياً؟ (d/m، الافتراضي: d)",
"getting_token_from_db": "جارٍ الحصول على الرمز من قاعدة البيانات...",
"token_found_in_db": "تم العثور على الرمز في قاعدة البيانات",
"token_not_found_in_db": "لم يتم العثور على الرمز في قاعدة البيانات",
"cursor_acc_info_not_found": "لم يتم العثور على cursor_acc_info.py",
"error_getting_token_from_db": "خطأ في الحصول على الرمز من قاعدة البيانات: {error}",
"enter_token": "أدخل رمز Cursor الخاص بك: ",
"token_length": "طول الرمز: {length} حرفاً",
"usage_response_status": "حالة استجابة الاستخدام: {response}",
"unexpected_status_code": "رمز حالة غير متوقع: {code}",
"jwt_token_warning": "يبدو أن الرمز بتنسيق JWT، لكن فحص API أعاد رمز حالة غير متوقع. قد يكون الرمز صالحاً ولكن الوصول إلى API مقيد.",
"invalid_token": "رمز غير صالح",
"user_authorized": "المستخدم مصرح له",
"user_unauthorized": "المستخدم غير مصرح له",
"request_timeout": "انتهت مهلة الطلب",
"connection_error": "خطأ في الاتصال",
"check_error": "خطأ في التحقق من التصريح: {error}",
"authorization_successful": "تم التصريح بنجاح!",
"authorization_failed": "فشل التصريح!",
"operation_cancelled": "تم إلغاء العملية بواسطة المستخدم",
"unexpected_error": "خطأ غير متوقع: {error}",
"error_generating_checksum": "خطأ في إنشاء المجموع الاختباري: {error}",
"checking_usage_information": "جارٍ التحقق من معلومات الاستخدام...",
"check_usage_response": "استجابة فحص الاستخدام: {response}",
"usage_response": "استجابة الاستخدام: {response}"
},
"bypass_token_limit": {
"title": "أداة تجاوز حد الرمز",
"description": "تقوم هذه الأداة بتعديل ملف workbench.desktop.main.js لتجاوز حد الرمز",
"press_enter": "اضغط Enter للمتابعة..."
},
"token": {
"refreshing": "جارٍ تحديث الرمز...",
"refresh_success": "تم تحديث الرمز بنجاح! صالح لمدة {days} يوماً (تاريخ انتهاء الصلاحية: {expire})",
"no_access_token": "لا يوجد رمز وصول في الاستجابة",
"refresh_failed": "فشل تحديث الرمز: {error}",
"invalid_response": "استجابة JSON غير صالحة من خادم التحديث",
"server_error": "خطأ في خادم التحديث: HTTP {status}",
"request_timeout": "انتهت مهلة طلب خادم التحديث",
"connection_error": "خطأ في الاتصال بخادم التحديث",
"unexpected_error": "خطأ غير متوقع أثناء تحديث الرمز: {error}",
"extraction_error": "خطأ في استخراج الرمز: {error}"
}
}

View File

@@ -30,9 +30,12 @@
"exiting": "Излизане ......",
"bypass_version_check": "Пропусни проверката на версията на Cursor",
"check_user_authorized": "Провери оторизацията на потребителя",
"select_chrome_profile": "Избери Chrome профил"
"select_chrome_profile": "Избери Chrome профил",
"bypass_token_limit": "Заобикаляне на ограничението на токените",
"restore_machine_id": "Възстановяване на машинен идентификатор от резервно копие"
},
"languages": {
"ar": "Арабски",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -410,5 +413,48 @@
"profile_selected": "Избран профил: {profile}",
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
},
"restore": {
"title": "Възстановяване на машинен идентификатор от резервно копие",
"starting": "Стартиране на процеса на възстановяване на машинния идентификатор",
"no_backups_found": "Не са намерени резервни копия",
"available_backups": "Налични резервни копия",
"select_backup": "Изберете резервно копие за възстановяване",
"to_cancel": "за отказ",
"operation_cancelled": "Операцията е отменена",
"invalid_selection": "Невалиден избор",
"please_enter_number": "Моля, въведете валиден номер",
"missing_id": "Липсващ идентификатор: {id}",
"read_backup_failed": "Неуспешно четене на резервното копие: {error}",
"current_file_not_found": "Текущият файл за съхранение не е намерен",
"current_backup_created": "Създадено е резервно копие на текущия файл за съхранение",
"storage_updated": "Файлът за съхранение е успешно актуализиран",
"update_failed": "Неуспешно актуализиране на файла за съхранение: {error}",
"sqlite_not_found": "SQLite базата данни не е намерена",
"updating_sqlite": "Актуализиране на SQLite базата данни",
"updating_pair": "Актуализиране на двойката ключ-стойност",
"sqlite_updated": "SQLite базата данни е успешно актуализирана",
"sqlite_update_failed": "Неуспешно актуализиране на SQLite базата данни: {error}",
"machine_id_backup_created": "Създадено е резервно копие на файла с машинния идентификатор",
"backup_creation_failed": "Неуспешно създаване на резервно копие: {error}",
"machine_id_updated": "Файлът с машинния идентификатор е успешно актуализиран",
"machine_id_update_failed": "Неуспешно актуализиране на файла с машинния идентификатор: {error}",
"updating_system_ids": "Актуализиране на системните идентификатори",
"system_ids_update_failed": "Неуспешно актуализиране на системните идентификатори: {error}",
"permission_denied": "Достъпът е отказан. Опитайте да стартирате като администратор",
"windows_machine_guid_updated": "Windows машинният GUID е успешно актуализиран",
"update_windows_machine_guid_failed": "Неуспешно актуализиране на Windows машинния GUID: {error}",
"windows_machine_id_updated": "Windows машинният идентификатор е успешно актуализиран",
"update_windows_machine_id_failed": "Неуспешно актуализиране на Windows машинния идентификатор: {error}",
"sqm_client_key_not_found": "Регистърният ключ SQMClient не е намерен",
"update_windows_system_ids_failed": "Неуспешно актуализиране на Windows системните идентификатори: {error}",
"macos_platform_uuid_updated": "macOS платформеният UUID е успешно актуализиран",
"failed_to_execute_plutil_command": "Неуспешно изпълнение на plutil командата",
"update_macos_system_ids_failed": "Неуспешно актуализиране на macOS системните идентификатори: {error}",
"ids_to_restore": "Машинни идентификатори за възстановяване",
"confirm": "Сигурни ли сте, че искате да възстановите тези идентификатори?",
"success": "Машинният идентификатор е успешно възстановен",
"process_error": "Грешка при процеса на възстановяване: {error}",
"press_enter": "Натиснете Enter, за да продължите"
}
}

View File

@@ -31,9 +31,12 @@
"operation_cancelled_by_user": "Vorgang vom Benutzer abgebrochen",
"exiting": "Wird beendet ……",
"bypass_version_check": "Cursor Versionsprüfung Überspringen",
"check_user_authorized": "Benutzerautorisierung Prüfen"
"check_user_authorized": "Benutzerautorisierung Prüfen",
"bypass_token_limit": "Token-Limit umgehen",
"restore_machine_id": "Geräte-ID aus Backup wiederherstellen"
},
"languages": {
"ar": "Arabisch",
"en": "Englisch",
"zh_cn": "Vereinfachtes Chinesisch",
"zh_tw": "Traditionelles Chinesisch",
@@ -407,5 +410,48 @@
"profile_selected": "Ausgewähltes Profil: {profile}",
"invalid_selection": "Ungültige Auswahl. Bitte versuchen Sie es erneut",
"warning_chrome_close": "Warnung: Dies wird alle laufenden Chrome-Prozesse beenden"
},
"restore": {
"title": "Geräte-ID aus Backup wiederherstellen",
"starting": "Starte Wiederherstellungsprozess für Geräte-ID",
"no_backups_found": "Keine Backup-Dateien gefunden",
"available_backups": "Verfügbare Backup-Dateien",
"select_backup": "Wählen Sie ein Backup zur Wiederherstellung aus",
"to_cancel": "zum Abbrechen",
"operation_cancelled": "Vorgang abgebrochen",
"invalid_selection": "Ungültige Auswahl",
"please_enter_number": "Bitte geben Sie eine gültige Zahl ein",
"missing_id": "Fehlende ID: {id}",
"read_backup_failed": "Backup-Datei konnte nicht gelesen werden: {error}",
"current_file_not_found": "Aktuelle Speicherdatei nicht gefunden",
"current_backup_created": "Backup der aktuellen Speicherdatei erstellt",
"storage_updated": "Speicherdatei erfolgreich aktualisiert",
"update_failed": "Aktualisierung der Speicherdatei fehlgeschlagen: {error}",
"sqlite_not_found": "SQLite-Datenbank nicht gefunden",
"updating_sqlite": "Aktualisiere SQLite-Datenbank",
"updating_pair": "Aktualisiere Schlüssel-Wert-Paar",
"sqlite_updated": "SQLite-Datenbank erfolgreich aktualisiert",
"sqlite_update_failed": "Aktualisierung der SQLite-Datenbank fehlgeschlagen: {error}",
"machine_id_backup_created": "Backup der machineId-Datei erstellt",
"backup_creation_failed": "Backup-Erstellung fehlgeschlagen: {error}",
"machine_id_updated": "machineId-Datei erfolgreich aktualisiert",
"machine_id_update_failed": "Aktualisierung der machineId-Datei fehlgeschlagen: {error}",
"updating_system_ids": "Aktualisiere System-IDs",
"system_ids_update_failed": "Aktualisierung der System-IDs fehlgeschlagen: {error}",
"permission_denied": "Zugriff verweigert. Bitte versuchen Sie es mit Administratorrechten",
"windows_machine_guid_updated": "Windows-Geräte-GUID erfolgreich aktualisiert",
"update_windows_machine_guid_failed": "Aktualisierung des Windows-Geräte-GUID fehlgeschlagen: {error}",
"windows_machine_id_updated": "Windows-Geräte-ID erfolgreich aktualisiert",
"update_windows_machine_id_failed": "Aktualisierung der Windows-Geräte-ID fehlgeschlagen: {error}",
"sqm_client_key_not_found": "SQMClient-Registrierungsschlüssel nicht gefunden",
"update_windows_system_ids_failed": "Aktualisierung der Windows-System-IDs fehlgeschlagen: {error}",
"macos_platform_uuid_updated": "macOS-Plattform-UUID erfolgreich aktualisiert",
"failed_to_execute_plutil_command": "Ausführung des plutil-Befehls fehlgeschlagen",
"update_macos_system_ids_failed": "Aktualisierung der macOS-System-IDs fehlgeschlagen: {error}",
"ids_to_restore": "Wiederherzustellende Geräte-IDs",
"confirm": "Sind Sie sicher, dass Sie diese IDs wiederherstellen möchten?",
"success": "Geräte-ID erfolgreich wiederhergestellt",
"process_error": "Fehler beim Wiederherstellungsprozess: {error}",
"press_enter": "Drücken Sie Enter, um fortzufahren"
}
}

View File

@@ -4,8 +4,8 @@
"exit": "Exit Program",
"reset": "Reset Machine ID",
"register": "Register New Cursor Account",
"register_google": "Register with Google Account",
"register_github": "Register with GitHub Account",
"register_google": "Register with Self Google Account",
"register_github": "Register with Self GitHub Account",
"register_manual": "Register Cursor with Custom Email",
"quit": "Close Cursor Application",
"select_language": "Change Language",
@@ -32,9 +32,13 @@
"exiting": "Exiting ……",
"bypass_version_check": "Bypass Cursor Version Check",
"check_user_authorized": "Check User Authorized",
"bypass_token_limit": "Bypass Token Limit"
"bypass_token_limit": "Bypass Token Limit",
"language_config_saved": "Language configuration saved successfully",
"lang_invalid_choice": "Invalid choice. Please enter one of the following options: ({lang_choices})",
"restore_machine_id": "Restore Machine ID from Backup"
},
"languages": {
"ar": "Arabic",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -764,5 +768,48 @@
"connection_error": "Connection error to refresh server",
"unexpected_error": "Unexpected error during token refresh: {error}",
"extraction_error": "Error extracting token: {error}"
},
"restore": {
"title": "Restore Machine ID from Backup",
"starting": "Starting Machine ID Restore Process",
"no_backups_found": "No backup files found",
"available_backups": "Available backup files",
"select_backup": "Select backup to restore",
"to_cancel": "to cancel",
"operation_cancelled": "Operation cancelled",
"invalid_selection": "Invalid selection",
"please_enter_number": "Please enter a valid number",
"missing_id": "Missing ID: {id}",
"read_backup_failed": "Failed to read backup file: {error}",
"current_file_not_found": "Current storage file not found",
"current_backup_created": "Created backup of current storage file",
"storage_updated": "Storage file updated successfully",
"update_failed": "Failed to update storage file: {error}",
"sqlite_not_found": "SQLite database not found",
"updating_sqlite": "Updating SQLite database",
"updating_pair": "Updating key-value pair",
"sqlite_updated": "SQLite database updated successfully",
"sqlite_update_failed": "Failed to update SQLite database: {error}",
"machine_id_backup_created": "Created backup of machineId file",
"backup_creation_failed": "Failed to create backup: {error}",
"machine_id_updated": "machineId file updated successfully",
"machine_id_update_failed": "Failed to update machineId file: {error}",
"updating_system_ids": "Updating system IDs",
"system_ids_update_failed": "Failed to update system IDs: {error}",
"permission_denied": "Permission denied. Please try running as administrator",
"windows_machine_guid_updated": "Windows Machine GUID updated successfully",
"update_windows_machine_guid_failed": "Failed to update Windows Machine GUID: {error}",
"windows_machine_id_updated": "Windows Machine ID updated successfully",
"update_windows_machine_id_failed": "Failed to update Windows Machine ID: {error}",
"sqm_client_key_not_found": "SQMClient registry key not found",
"update_windows_system_ids_failed": "Failed to update Windows system IDs: {error}",
"macos_platform_uuid_updated": "macOS Platform UUID updated successfully",
"failed_to_execute_plutil_command": "Failed to execute plutil command",
"update_macos_system_ids_failed": "Failed to update macOS system IDs: {error}",
"ids_to_restore": "Machine IDs to restore",
"confirm": "Are you sure you want to restore these IDs?",
"success": "Machine ID restored successfully",
"process_error": "Restore process error: {error}",
"press_enter": "Press Enter to continue"
}
}
}

View File

@@ -30,9 +30,12 @@
"operation_cancelled_by_user": "Operación cancelada por el usuario",
"exiting": "Saliendo ……",
"bypass_version_check": "Omitir Verificación de Versión de Cursor",
"check_user_authorized": "Verificar Usuario Autorizado"
"check_user_authorized": "Verificar Usuario Autorizado",
"bypass_token_limit": "Omitir límite de tokens",
"restore_machine_id": "Restaurar ID de máquina desde copia de seguridad"
},
"languages": {
"ar": "Árabe",
"en": "Inglés",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -465,5 +468,48 @@
"profile_selected": "Perfil seleccionado: {profile}",
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
},
"restore": {
"title": "Restaurar ID de máquina desde copia de seguridad",
"starting": "Iniciando proceso de restauración de ID de máquina",
"no_backups_found": "No se encontraron copias de seguridad",
"available_backups": "Copias de seguridad disponibles",
"select_backup": "Seleccione una copia de seguridad para restaurar",
"to_cancel": "para cancelar",
"operation_cancelled": "Operación cancelada",
"invalid_selection": "Selección inválida",
"please_enter_number": "Por favor, introduzca un número válido",
"missing_id": "ID faltante: {id}",
"read_backup_failed": "Error al leer el archivo de copia de seguridad: {error}",
"current_file_not_found": "No se encontró el archivo de almacenamiento actual",
"current_backup_created": "Se creó una copia de seguridad del archivo de almacenamiento actual",
"storage_updated": "Archivo de almacenamiento actualizado con éxito",
"update_failed": "Error al actualizar el archivo de almacenamiento: {error}",
"sqlite_not_found": "No se encontró la base de datos SQLite",
"updating_sqlite": "Actualizando base de datos SQLite",
"updating_pair": "Actualizando par clave-valor",
"sqlite_updated": "Base de datos SQLite actualizada con éxito",
"sqlite_update_failed": "Error al actualizar la base de datos SQLite: {error}",
"machine_id_backup_created": "Se creó una copia de seguridad del archivo machineId",
"backup_creation_failed": "Error al crear la copia de seguridad: {error}",
"machine_id_updated": "Archivo machineId actualizado con éxito",
"machine_id_update_failed": "Error al actualizar el archivo machineId: {error}",
"updating_system_ids": "Actualizando IDs del sistema",
"system_ids_update_failed": "Error al actualizar los IDs del sistema: {error}",
"permission_denied": "Permiso denegado. Intente ejecutar como administrador",
"windows_machine_guid_updated": "GUID de máquina Windows actualizado con éxito",
"update_windows_machine_guid_failed": "Error al actualizar el GUID de máquina Windows: {error}",
"windows_machine_id_updated": "ID de máquina Windows actualizado con éxito",
"update_windows_machine_id_failed": "Error al actualizar el ID de máquina Windows: {error}",
"sqm_client_key_not_found": "No se encontró la clave de registro SQMClient",
"update_windows_system_ids_failed": "Error al actualizar los IDs del sistema Windows: {error}",
"macos_platform_uuid_updated": "UUID de plataforma macOS actualizado con éxito",
"failed_to_execute_plutil_command": "Error al ejecutar el comando plutil",
"update_macos_system_ids_failed": "Error al actualizar los IDs del sistema macOS: {error}",
"ids_to_restore": "IDs de máquina a restaurar",
"confirm": "¿Está seguro de que desea restaurar estos IDs?",
"success": "ID de máquina restaurado con éxito",
"process_error": "Error en el proceso de restauración: {error}",
"press_enter": "Presione Enter para continuar"
}
}
}

View File

@@ -28,9 +28,12 @@
"operation_cancelled_by_user": "Opération annulée par l'utilisateur",
"exiting": "Fermeture ……",
"bypass_version_check": "Ignorer la Vérification de Version de Cursor",
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur"
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur",
"bypass_token_limit": "Contourner la limite de tokens",
"restore_machine_id": "Restaurer l'ID de machine depuis une sauvegarde"
},
"languages": {
"ar": "Arabe",
"en": "Anglais",
"zh_cn": "Chinois simplifié",
"zh_tw": "Chinois traditionnel",
@@ -404,5 +407,48 @@
"profile_selected": "Profil sélectionné : {profile}",
"invalid_selection": "Sélection invalide. Veuillez réessayer",
"warning_chrome_close": "Attention : Cela fermera tous les processus Chrome en cours d'exécution"
},
"restore": {
"title": "Restaurer l'ID de machine depuis une sauvegarde",
"starting": "Démarrage du processus de restauration de l'ID de machine",
"no_backups_found": "Aucune sauvegarde trouvée",
"available_backups": "Sauvegardes disponibles",
"select_backup": "Sélectionnez une sauvegarde à restaurer",
"to_cancel": "pour annuler",
"operation_cancelled": "Opération annulée",
"invalid_selection": "Sélection invalide",
"please_enter_number": "Veuillez entrer un numéro valide",
"missing_id": "ID manquant : {id}",
"read_backup_failed": "Échec de lecture du fichier de sauvegarde : {error}",
"current_file_not_found": "Fichier de stockage actuel introuvable",
"current_backup_created": "Sauvegarde du fichier de stockage actuel créée",
"storage_updated": "Fichier de stockage mis à jour avec succès",
"update_failed": "Échec de la mise à jour du fichier de stockage : {error}",
"sqlite_not_found": "Base de données SQLite introuvable",
"updating_sqlite": "Mise à jour de la base de données SQLite",
"updating_pair": "Mise à jour de la paire clé-valeur",
"sqlite_updated": "Base de données SQLite mise à jour avec succès",
"sqlite_update_failed": "Échec de la mise à jour de la base de données SQLite : {error}",
"machine_id_backup_created": "Sauvegarde du fichier machineId créée",
"backup_creation_failed": "Échec de création de la sauvegarde : {error}",
"machine_id_updated": "Fichier machineId mis à jour avec succès",
"machine_id_update_failed": "Échec de la mise à jour du fichier machineId : {error}",
"updating_system_ids": "Mise à jour des ID système",
"system_ids_update_failed": "Échec de la mise à jour des ID système : {error}",
"permission_denied": "Permission refusée. Veuillez essayer d'exécuter en tant qu'administrateur",
"windows_machine_guid_updated": "GUID de machine Windows mis à jour avec succès",
"update_windows_machine_guid_failed": "Échec de la mise à jour du GUID de machine Windows : {error}",
"windows_machine_id_updated": "ID de machine Windows mis à jour avec succès",
"update_windows_machine_id_failed": "Échec de la mise à jour de l'ID de machine Windows : {error}",
"sqm_client_key_not_found": "Clé de registre SQMClient introuvable",
"update_windows_system_ids_failed": "Échec de la mise à jour des ID système Windows : {error}",
"macos_platform_uuid_updated": "UUID de plateforme macOS mis à jour avec succès",
"failed_to_execute_plutil_command": "Échec d'exécution de la commande plutil",
"update_macos_system_ids_failed": "Échec de la mise à jour des ID système macOS : {error}",
"ids_to_restore": "ID de machine à restaurer",
"confirm": "Êtes-vous sûr de vouloir restaurer ces ID ?",
"success": "ID de machine restauré avec succès",
"process_error": "Erreur du processus de restauration : {error}",
"press_enter": "Appuyez sur Entrée pour continuer"
}
}

View File

@@ -29,9 +29,12 @@
"operation_cancelled_by_user": "Operatie geannuleerd door gebruiker",
"exiting": "Afsluiten ……",
"bypass_version_check": "Cursor Versiecontrole Overslaan",
"check_user_authorized": "Gebruikersautorisatie Controleren"
"check_user_authorized": "Gebruikersautorisatie Controleren",
"bypass_token_limit": "Token-limiet omzeilen",
"restore_machine_id": "Machine-ID herstellen vanaf backup"
},
"languages": {
"ar": "Arabisch",
"en": "Engels",
"zh_cn": "Vereenvoudigd Chinees",
"zh_tw": "Traditioneel Chinees",
@@ -403,5 +406,48 @@
"profile_selected": "Geselecteerd profiel: {profile}",
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
},
"restore": {
"title": "Machine-ID herstellen vanaf backup",
"starting": "Herstelproces van machine-ID starten",
"no_backups_found": "Geen backups gevonden",
"available_backups": "Beschikbare backups",
"select_backup": "Selecteer een backup om te herstellen",
"to_cancel": "om te annuleren",
"operation_cancelled": "Bewerking geannuleerd",
"invalid_selection": "Ongeldige selectie",
"please_enter_number": "Voer een geldig nummer in",
"missing_id": "Ontbrekende ID: {id}",
"read_backup_failed": "Lezen van backupbestand mislukt: {error}",
"current_file_not_found": "Huidig opslagbestand niet gevonden",
"current_backup_created": "Backup van huidig opslagbestand gemaakt",
"storage_updated": "Opslagbestand succesvol bijgewerkt",
"update_failed": "Bijwerken van opslagbestand mislukt: {error}",
"sqlite_not_found": "SQLite-database niet gevonden",
"updating_sqlite": "SQLite-database bijwerken",
"updating_pair": "Sleutel-waardepaar bijwerken",
"sqlite_updated": "SQLite-database succesvol bijgewerkt",
"sqlite_update_failed": "Bijwerken van SQLite-database mislukt: {error}",
"machine_id_backup_created": "Backup van machineId-bestand gemaakt",
"backup_creation_failed": "Maken van backup mislukt: {error}",
"machine_id_updated": "MachineId-bestand succesvol bijgewerkt",
"machine_id_update_failed": "Bijwerken van machineId-bestand mislukt: {error}",
"updating_system_ids": "Systeem-ID's bijwerken",
"system_ids_update_failed": "Bijwerken van systeem-ID's mislukt: {error}",
"permission_denied": "Toegang geweigerd. Probeer als administrator uit te voeren",
"windows_machine_guid_updated": "Windows machine-GUID succesvol bijgewerkt",
"update_windows_machine_guid_failed": "Bijwerken van Windows machine-GUID mislukt: {error}",
"windows_machine_id_updated": "Windows machine-ID succesvol bijgewerkt",
"update_windows_machine_id_failed": "Bijwerken van Windows machine-ID mislukt: {error}",
"sqm_client_key_not_found": "SQMClient registersleutel niet gevonden",
"update_windows_system_ids_failed": "Bijwerken van Windows systeem-ID's mislukt: {error}",
"macos_platform_uuid_updated": "macOS platform-UUID succesvol bijgewerkt",
"failed_to_execute_plutil_command": "Uitvoeren van plutil opdracht mislukt",
"update_macos_system_ids_failed": "Bijwerken van macOS systeem-ID's mislukt: {error}",
"ids_to_restore": "Te herstellen machine-ID's",
"confirm": "Weet u zeker dat u deze ID's wilt herstellen?",
"success": "Machine-ID succesvol hersteld",
"process_error": "Fout bij herstelproces: {error}",
"press_enter": "Druk op Enter om door te gaan"
}
}
}

View File

@@ -28,9 +28,12 @@
"operation_cancelled_by_user": "Operação cancelada pelo usuário",
"exiting": "Saindo ......",
"bypass_version_check": "Ignorar Verificação de Versão do Cursor",
"check_user_authorized": "Verificar Autorização do Usuário"
"check_user_authorized": "Verificar Autorização do Usuário",
"bypass_token_limit": "Contornar Limite de Tokens",
"restore_machine_id": "Restaurar ID da Máquina do Backup"
},
"languages": {
"ar": "Árabe",
"en": "Inglês",
"zh_cn": "Chinês Simplificado",
"zh_tw": "Chinês Tradicional",
@@ -404,5 +407,48 @@
"profile_selected": "Perfil selecionado: {profile}",
"invalid_selection": "Seleção inválida. Por favor, tente novamente",
"warning_chrome_close": "Aviso: Isso fechará todos os processos do Chrome em execução"
},
"restore": {
"title": "Restaurar ID da Máquina do Backup",
"starting": "Iniciando processo de restauração de ID da máquina",
"no_backups_found": "Nenhum backup encontrado",
"available_backups": "Backups disponíveis",
"select_backup": "Selecione um backup para restaurar",
"to_cancel": "para cancelar",
"operation_cancelled": "Operação cancelada",
"invalid_selection": "Seleção inválida",
"please_enter_number": "Por favor, insira um número válido",
"missing_id": "ID ausente: {id}",
"read_backup_failed": "Falha ao ler arquivo de backup: {error}",
"current_file_not_found": "Arquivo de armazenamento atual não encontrado",
"current_backup_created": "Backup do arquivo de armazenamento atual criado",
"storage_updated": "Arquivo de armazenamento atualizado com sucesso",
"update_failed": "Falha ao atualizar arquivo de armazenamento: {error}",
"sqlite_not_found": "Banco de dados SQLite não encontrado",
"updating_sqlite": "Atualizando banco de dados SQLite",
"updating_pair": "Atualizando par chave-valor",
"sqlite_updated": "Banco de dados SQLite atualizado com sucesso",
"sqlite_update_failed": "Falha ao atualizar banco de dados SQLite: {error}",
"machine_id_backup_created": "Backup do arquivo machineId criado",
"backup_creation_failed": "Falha ao criar backup: {error}",
"machine_id_updated": "Arquivo machineId atualizado com sucesso",
"machine_id_update_failed": "Falha ao atualizar arquivo machineId: {error}",
"updating_system_ids": "Atualizando IDs do sistema",
"system_ids_update_failed": "Falha ao atualizar IDs do sistema: {error}",
"permission_denied": "Permissão negada. Tente executar como administrador",
"windows_machine_guid_updated": "GUID da máquina Windows atualizado com sucesso",
"update_windows_machine_guid_failed": "Falha ao atualizar GUID da máquina Windows: {error}",
"windows_machine_id_updated": "ID da máquina Windows atualizado com sucesso",
"update_windows_machine_id_failed": "Falha ao atualizar ID da máquina Windows: {error}",
"sqm_client_key_not_found": "Chave de registro SQMClient não encontrada",
"update_windows_system_ids_failed": "Falha ao atualizar IDs do sistema Windows: {error}",
"macos_platform_uuid_updated": "UUID da plataforma macOS atualizado com sucesso",
"failed_to_execute_plutil_command": "Falha ao executar comando plutil",
"update_macos_system_ids_failed": "Falha ao atualizar IDs do sistema macOS: {error}",
"ids_to_restore": "IDs da máquina para restaurar",
"confirm": "Tem certeza que deseja restaurar esses IDs?",
"success": "ID da máquina restaurado com sucesso",
"process_error": "Erro no processo de restauração: {error}",
"press_enter": "Pressione Enter para continuar"
}
}
}

View File

@@ -29,9 +29,12 @@
"exiting": "Выход ......",
"bypass_version_check": "Пропустить проверку версии Cursor",
"check_user_authorized": "Проверить авторизацию пользователя",
"select_chrome_profile": "Выбрать профиль Chrome"
"select_chrome_profile": "Выбрать профиль Chrome",
"bypass_token_limit": "Обход ограничения токенов",
"restore_machine_id": "Восстановить ID устройства из резервной копии"
},
"languages": {
"ar": "Арабский",
"en": "Английский",
"zh_cn": "Упрощенный китайский",
"zh_tw": "Традиционный китайский",
@@ -405,5 +408,48 @@
"profile_selected": "Выбран профиль: {profile}",
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
},
"restore": {
"title": "Восстановить ID устройства из резервной копии",
"starting": "Запуск процесса восстановления ID устройства",
"no_backups_found": "Резервные копии не найдены",
"available_backups": "Доступные резервные копии",
"select_backup": "Выберите резервную копию для восстановления",
"to_cancel": "для отмены",
"operation_cancelled": "Операция отменена",
"invalid_selection": "Неверный выбор",
"please_enter_number": "Пожалуйста, введите корректный номер",
"missing_id": "Отсутствует ID: {id}",
"read_backup_failed": "Не удалось прочитать файл резервной копии: {error}",
"current_file_not_found": "Текущий файл хранилища не найден",
"current_backup_created": "Создана резервная копия текущего файла хранилища",
"storage_updated": "Файл хранилища успешно обновлен",
"update_failed": "Не удалось обновить файл хранилища: {error}",
"sqlite_not_found": "База данных SQLite не найдена",
"updating_sqlite": "Обновление базы данных SQLite",
"updating_pair": "Обновление пары ключ-значение",
"sqlite_updated": "База данных SQLite успешно обновлена",
"sqlite_update_failed": "Не удалось обновить базу данных SQLite: {error}",
"machine_id_backup_created": "Создана резервная копия файла machineId",
"backup_creation_failed": "Не удалось создать резервную копию: {error}",
"machine_id_updated": "Файл machineId успешно обновлен",
"machine_id_update_failed": "Не удалось обновить файл machineId: {error}",
"updating_system_ids": "Обновление системных ID",
"system_ids_update_failed": "Не удалось обновить системные ID: {error}",
"permission_denied": "Доступ запрещен. Попробуйте запустить с правами администратора",
"windows_machine_guid_updated": "GUID устройства Windows успешно обновлен",
"update_windows_machine_guid_failed": "Не удалось обновить GUID устройства Windows: {error}",
"windows_machine_id_updated": "ID устройства Windows успешно обновлен",
"update_windows_machine_id_failed": "Не удалось обновить ID устройства Windows: {error}",
"sqm_client_key_not_found": "Ключ реестра SQMClient не найден",
"update_windows_system_ids_failed": "Не удалось обновить системные ID Windows: {error}",
"macos_platform_uuid_updated": "UUID платформы macOS успешно обновлен",
"failed_to_execute_plutil_command": "Не удалось выполнить команду plutil",
"update_macos_system_ids_failed": "Не удалось обновить системные ID macOS: {error}",
"ids_to_restore": "ID устройства для восстановления",
"confirm": "Вы уверены, что хотите восстановить эти ID?",
"success": "ID устройства успешно восстановлен",
"process_error": "Ошибка процесса восстановления: {error}",
"press_enter": "Нажмите Enter для продолжения"
}
}

View File

@@ -31,9 +31,12 @@
"operation_cancelled_by_user": "İşlem kullanıcı tarafından iptal edildi",
"exiting": ıkılıyor ......",
"bypass_version_check": "Cursor Sürüm Kontrolünü Atla",
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et"
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et",
"bypass_token_limit": "Token Limitini Atla",
"restore_machine_id": "Makine Kimliğini Yedekten Geri Yükle"
},
"languages": {
"ar": "Arapça",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -410,5 +413,48 @@
"profile_selected": "Seçilen profil: {profile}",
"invalid_selection": "Geçersiz seçim. Lütfen tekrar deneyin",
"warning_chrome_close": "Uyarı: Bu işlem tüm çalışan Chrome işlemlerini kapatacaktır"
},
"restore": {
"title": "Makine Kimliğini Yedekten Geri Yükle",
"starting": "Makine kimliği geri yükleme işlemi başlatılıyor",
"no_backups_found": "Yedek bulunamadı",
"available_backups": "Mevcut yedekler",
"select_backup": "Geri yüklemek için bir yedek seçin",
"to_cancel": "iptal etmek için",
"operation_cancelled": "İşlem iptal edildi",
"invalid_selection": "Geçersiz seçim",
"please_enter_number": "Lütfen geçerli bir numara girin",
"missing_id": "Eksik kimlik: {id}",
"read_backup_failed": "Yedek dosyası okunamadı: {error}",
"current_file_not_found": "Mevcut depolama dosyası bulunamadı",
"current_backup_created": "Mevcut depolama dosyasının yedeği oluşturuldu",
"storage_updated": "Depolama dosyası başarıyla güncellendi",
"update_failed": "Depolama dosyası güncellenemedi: {error}",
"sqlite_not_found": "SQLite veritabanı bulunamadı",
"updating_sqlite": "SQLite veritabanı güncelleniyor",
"updating_pair": "Anahtar-değer çifti güncelleniyor",
"sqlite_updated": "SQLite veritabanı başarıyla güncellendi",
"sqlite_update_failed": "SQLite veritabanı güncellenemedi: {error}",
"machine_id_backup_created": "MachineId dosyasının yedeği oluşturuldu",
"backup_creation_failed": "Yedek oluşturulamadı: {error}",
"machine_id_updated": "MachineId dosyası başarıyla güncellendi",
"machine_id_update_failed": "MachineId dosyası güncellenemedi: {error}",
"updating_system_ids": "Sistem kimlikleri güncelleniyor",
"system_ids_update_failed": "Sistem kimlikleri güncellenemedi: {error}",
"permission_denied": "İzin reddedildi. Yönetici olarak çalıştırmayı deneyin",
"windows_machine_guid_updated": "Windows makine GUID'i başarıyla güncellendi",
"update_windows_machine_guid_failed": "Windows makine GUID'i güncellenemedi: {error}",
"windows_machine_id_updated": "Windows makine kimliği başarıyla güncellendi",
"update_windows_machine_id_failed": "Windows makine kimliği güncellenemedi: {error}",
"sqm_client_key_not_found": "SQMClient kayıt anahtarı bulunamadı",
"update_windows_system_ids_failed": "Windows sistem kimlikleri güncellenemedi: {error}",
"macos_platform_uuid_updated": "macOS platform UUID'si başarıyla güncellendi",
"failed_to_execute_plutil_command": "plutil komutu yürütülemedi",
"update_macos_system_ids_failed": "macOS sistem kimlikleri güncellenemedi: {error}",
"ids_to_restore": "Geri yüklenecek makine kimlikleri",
"confirm": "Bu kimlikleri geri yüklemek istediğinizden emin misiniz?",
"success": "Makine kimliği başarıyla geri yüklendi",
"process_error": "Geri yükleme işlemi hatası: {error}",
"press_enter": "Devam etmek için Enter tuşuna basın"
}
}
}

View File

@@ -31,9 +31,14 @@
"operation_cancelled_by_user": "Thao tác đã bị người dùng hủy",
"exiting": "Đang thoát ……",
"bypass_version_check": "Bỏ qua Kiểm tra Phiên bản Cursor",
"check_user_authorized": "Kiểm tra Quyền Người dùng"
"check_user_authorized": "Kiểm tra Quyền Người dùng",
"bypass_token_limit": "Bỏ qua giới hạn Token",
"language_config_saved": "Đổi ngôn ngữ thành công",
"lang_invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ ({lang_choices})",
"restore_machine_id": "Khôi phục ID máy từ bản sao lưu"
},
"languages": {
"ar": "Tiếng Ả Rập",
"en": "Tiếng Anh",
"zh_cn": "Tiếng Trung Giản Thể",
"zh_tw": "Tiếng Trung Phồn Thể",
@@ -727,5 +732,48 @@
"checking_usage_information": "Đang kiểm tra thông tin sử dụng...",
"check_usage_response": "Phản hồi kiểm tra sử dụng: {response}",
"usage_response": "Phản hồi sử dụng: {response}"
},
"restore": {
"title": "Khôi phục ID máy từ bản sao lưu",
"starting": "Bắt đầu quá trình khôi phục ID máy",
"no_backups_found": "Không tìm thấy bản sao lưu nào",
"available_backups": "Các bản sao lưu có sẵn",
"select_backup": "Chọn bản sao lưu để khôi phục",
"to_cancel": "để hủy",
"operation_cancelled": "Đã hủy thao tác",
"invalid_selection": "Lựa chọn không hợp lệ",
"please_enter_number": "Vui lòng nhập một số hợp lệ",
"missing_id": "Thiếu ID: {id}",
"read_backup_failed": "Không thể đọc tệp sao lưu: {error}",
"current_file_not_found": "Không tìm thấy tệp lưu trữ hiện tại",
"current_backup_created": "Đã tạo bản sao lưu của tệp lưu trữ hiện tại",
"storage_updated": "Tệp lưu trữ đã được cập nhật thành công",
"update_failed": "Không thể cập nhật tệp lưu trữ: {error}",
"sqlite_not_found": "Không tìm thấy cơ sở dữ liệu SQLite",
"updating_sqlite": "Đang cập nhật cơ sở dữ liệu SQLite",
"updating_pair": "Đang cập nhật cặp khóa-giá trị",
"sqlite_updated": "Cơ sở dữ liệu SQLite đã được cập nhật thành công",
"sqlite_update_failed": "Không thể cập nhật cơ sở dữ liệu SQLite: {error}",
"machine_id_backup_created": "Đã tạo bản sao lưu của tệp machineId",
"backup_creation_failed": "Không thể tạo bản sao lưu: {error}",
"machine_id_updated": "Tệp machineId đã được cập nhật thành công",
"machine_id_update_failed": "Không thể cập nhật tệp machineId: {error}",
"updating_system_ids": "Đang cập nhật ID hệ thống",
"system_ids_update_failed": "Không thể cập nhật ID hệ thống: {error}",
"permission_denied": "Quyền truy cập bị từ chối. Vui lòng thử chạy với quyền quản trị",
"windows_machine_guid_updated": "GUID máy Windows đã được cập nhật thành công",
"update_windows_machine_guid_failed": "Không thể cập nhật GUID máy Windows: {error}",
"windows_machine_id_updated": "ID máy Windows đã được cập nhật thành công",
"update_windows_machine_id_failed": "Không thể cập nhật ID máy Windows: {error}",
"sqm_client_key_not_found": "Không tìm thấy khóa đăng ký SQMClient",
"update_windows_system_ids_failed": "Không thể cập nhật ID hệ thống Windows: {error}",
"macos_platform_uuid_updated": "UUID nền tảng macOS đã được cập nhật thành công",
"failed_to_execute_plutil_command": "Không thể thực thi lệnh plutil",
"update_macos_system_ids_failed": "Không thể cập nhật ID hệ thống macOS: {error}",
"ids_to_restore": "ID máy cần khôi phục",
"confirm": "Bạn có chắc chắn muốn khôi phục những ID này không?",
"success": "ID máy đã được khôi phục thành công",
"process_error": "Lỗi quá trình khôi phục: {error}",
"press_enter": "Nhấn Enter để tiếp tục"
}
}

View File

@@ -4,8 +4,8 @@
"exit": "退出程序",
"reset": "重置机器ID",
"register": "注册新的Cursor账户",
"register_google": "使用Google账户注册",
"register_github": "使用GitHub账户注册",
"register_google": "使用自己的Google账户注册",
"register_github": "使用自己的GitHub账户注册",
"register_manual": "使用自定义邮箱注册Cursor",
"quit": "关闭Cursor应用",
"select_language": "更改语言",
@@ -32,9 +32,13 @@
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权",
"bypass_token_limit": "绕过 Token 限制"
"bypass_token_limit": "绕过 Token 限制",
"language_config_saved": "语言配置保存成功",
"lang_invalid_choice": "选择无效。请输入以下选项之一:({lang_choices})",
"restore_machine_id": "从备份恢复机器ID"
},
"languages": {
"ar": "阿拉伯语",
"en": "英语",
"zh_cn": "简体中文",
"zh_tw": "繁体中文",
@@ -623,7 +627,9 @@
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
"found_chrome_at": "找到 Chrome: {path}",
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}"
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}",
"select_profile": "选择要使用的 {browser} 配置文件:",
"profile_list": "可用 {browser} 配置文件:"
},
"browser_profile": {
"title": "浏览器配置文件选择",
@@ -744,5 +750,48 @@
"connection_error": "连接刷新服务器错误",
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
"extraction_error": "提取令牌时出错: {error}"
},
"restore": {
"title": "从备份恢复机器ID",
"starting": "正在启动机器ID恢复进程",
"no_backups_found": "未找到备份文件",
"available_backups": "可用的备份文件",
"select_backup": "选择要恢复的备份",
"to_cancel": "取消操作",
"operation_cancelled": "操作已取消",
"invalid_selection": "选择无效",
"please_enter_number": "请输入有效的数字",
"missing_id": "缺少ID: {id}",
"read_backup_failed": "读取备份文件失败: {error}",
"current_file_not_found": "未找到当前存储文件",
"current_backup_created": "已创建当前存储文件的备份",
"storage_updated": "存储文件已成功更新",
"update_failed": "更新存储文件失败: {error}",
"sqlite_not_found": "未找到SQLite数据库",
"updating_sqlite": "正在更新SQLite数据库",
"updating_pair": "正在更新键值对",
"sqlite_updated": "SQLite数据库已成功更新",
"sqlite_update_failed": "更新SQLite数据库失败: {error}",
"machine_id_backup_created": "已创建machineId文件的备份",
"backup_creation_failed": "创建备份失败: {error}",
"machine_id_updated": "machineId文件已成功更新",
"machine_id_update_failed": "更新machineId文件失败: {error}",
"updating_system_ids": "正在更新系统ID",
"system_ids_update_failed": "更新系统ID失败: {error}",
"permission_denied": "权限被拒绝。请尝试以管理员身份运行",
"windows_machine_guid_updated": "Windows机器GUID已成功更新",
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}",
"windows_machine_id_updated": "Windows机器ID已成功更新",
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
"sqm_client_key_not_found": "未找到SQMClient注册表项",
"update_windows_system_ids_failed": "更新Windows系统ID失败: {error}",
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
"failed_to_execute_plutil_command": "执行plutil命令失败",
"update_macos_system_ids_failed": "更新macOS系统ID失败: {error}",
"ids_to_restore": "要恢复的机器ID",
"confirm": "您确定要恢复这些ID吗",
"success": "机器ID已成功恢复",
"process_error": "恢复过程错误: {error}",
"press_enter": "按Enter键继续"
}
}

View File

@@ -4,8 +4,8 @@
"exit": "退出程式",
"reset": "重置機器ID",
"register": "註冊新的Cursor帳戶",
"register_google": "使用Google帳戶註冊",
"register_github": "使用GitHub帳戶註冊",
"register_google": "使用自己的Google帳戶註冊",
"register_github": "使用自己的GitHub帳戶註冊",
"register_manual": "使用自定義郵箱註冊Cursor",
"quit": "關閉Cursor應用",
"select_language": "更改語言",
@@ -32,9 +32,13 @@
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權",
"bypass_token_limit": "繞過 Token 限制"
"bypass_token_limit": "繞過 Token 限制",
"language_config_saved": "語言配置保存成功",
"lang_invalid_choice": "選擇無效。請輸入以下選項之一:({lang_choices})",
"restore_machine_id": "從備份恢復機器ID"
},
"languages": {
"ar": "阿拉伯語",
"en": "英文",
"zh_cn": "簡體中文",
"zh_tw": "繁體中文",
@@ -122,7 +126,6 @@
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
"file_not_found": "文件未找到: {path}"
},
"register": {
"title": "Cursor 註冊工具",
"start": "正在啟動註冊流程...",
@@ -598,7 +601,18 @@
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
"browser_failed": "瀏覽器啟動失敗: {error}",
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}"
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}",
"using_configured_browser_path": "使用配置的 {browser} 路徑: {path}",
"found_browser_user_data_dir": "找到 {browser} 用戶數據目錄: {path}",
"warning_browser_close": "警告:這將關閉所有正在執行的 {browser} 進程",
"killing_browser_processes": "正在關閉 {browser} 進程...",
"profile_selection_error": "配置文件選擇過程中出錯: {error}",
"select_profile": "選擇要使用的 {browser} 配置文件:",
"profile_list": "可用 {browser} 配置文件:",
"no_profiles": "未找到 {browser} 配置文件",
"error_loading": "載入 {browser} 配置文件時出錯:{error}",
"profile_selected": "已選擇配置文件:{profile}",
"invalid_selection": "選擇無效。請重試"
},
"chrome_profile": {
"title": "Chrome配置檔案選擇",
@@ -709,5 +723,48 @@
"title": "繞過 Token 限制工具",
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
"press_enter": "按回車鍵繼續..."
},
"restore": {
"title": "從備份恢復機器ID",
"starting": "正在啟動機器ID恢復進程",
"no_backups_found": "未找到備份文件",
"available_backups": "可用的備份文件",
"select_backup": "選擇要恢復的備份",
"to_cancel": "取消操作",
"operation_cancelled": "操作已取消",
"invalid_selection": "選擇無效",
"please_enter_number": "請輸入有效的數字",
"missing_id": "缺少ID: {id}",
"read_backup_failed": "讀取備份文件失敗: {error}",
"current_file_not_found": "未找到當前存儲文件",
"current_backup_created": "已創建當前存儲文件的備份",
"storage_updated": "存儲文件已成功更新",
"update_failed": "更新存儲文件失敗: {error}",
"sqlite_not_found": "未找到SQLite數據庫",
"updating_sqlite": "正在更新SQLite數據庫",
"updating_pair": "正在更新鍵值對",
"sqlite_updated": "SQLite數據庫已成功更新",
"sqlite_update_failed": "更新SQLite數據庫失敗: {error}",
"machine_id_backup_created": "已創建machineId文件的備份",
"backup_creation_failed": "創建備份失敗: {error}",
"machine_id_updated": "machineId文件已成功更新",
"machine_id_update_failed": "更新machineId文件失敗: {error}",
"updating_system_ids": "正在更新系統ID",
"system_ids_update_failed": "更新系統ID失敗: {error}",
"permission_denied": "權限被拒絕。請嘗試以管理員身份運行",
"windows_machine_guid_updated": "Windows機器GUID已成功更新",
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
"windows_machine_id_updated": "Windows機器ID已成功更新",
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
"sqm_client_key_not_found": "未找到SQMClient註冊表項",
"update_windows_system_ids_failed": "更新Windows系統ID失敗: {error}",
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
"failed_to_execute_plutil_command": "執行plutil命令失敗",
"update_macos_system_ids_failed": "更新macOS系統ID失敗: {error}",
"ids_to_restore": "要恢復的機器ID",
"confirm": "您確定要恢復這些ID嗎",
"success": "機器ID已成功恢復",
"process_error": "恢復過程錯誤: {error}",
"press_enter": "按Enter鍵繼續"
}
}

View File

@@ -83,7 +83,7 @@ muhammedfurkan plamkatawe Lucaszmv
"""
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
Press 4 to change language | 按下 4 键切换语言{Style.RESET_ALL}"""
# center display LOGO and DESCRIPTION
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)

300
main.py
View File

@@ -12,6 +12,15 @@ import subprocess
from config import get_config, force_update_config
import shutil
import re
from utils import get_user_documents_path
# Add these imports for Arabic support
try:
import arabic_reshaper
from bidi.algorithm import get_display
except ImportError:
arabic_reshaper = None
get_display = None
# Only import windll on Windows systems
if platform.system() == 'Windows':
@@ -79,8 +88,37 @@ def run_as_admin():
class Translator:
def __init__(self):
self.translations = {}
self.current_language = self.detect_system_language() # Use correct method name
self.fallback_language = 'en' # Fallback language if translation is missing
self.config = get_config()
# Create language cache directory if it doesn't exist
if self.config and self.config.has_section('Language'):
self.language_cache_dir = self.config.get('Language', 'language_cache_dir')
os.makedirs(self.language_cache_dir, exist_ok=True)
else:
self.language_cache_dir = None
# Set fallback language from config if available
self.fallback_language = 'en'
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'fallback_language'):
self.fallback_language = self.config.get('Language', 'fallback_language')
# Load saved language from config if available, otherwise detect system language
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'current_language'):
saved_language = self.config.get('Language', 'current_language')
if saved_language and saved_language.strip():
self.current_language = saved_language
else:
self.current_language = self.detect_system_language()
# Save detected language to config
if self.config.has_section('Language'):
self.config.set('Language', 'current_language', self.current_language)
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
with open(config_file, 'w', encoding='utf-8') as f:
self.config.write(f)
else:
self.current_language = self.detect_system_language()
self.load_translations()
def detect_system_language(self):
@@ -126,6 +164,8 @@ class Translator:
return 'tr' # Turkish
case 0x0402:
return 'bg' # Bulgarian
case 0x0401:
return 'ar' # Arabic
case _:
return 'en' # Default to English
except:
@@ -166,6 +206,8 @@ class Translator:
return 'tr'
case s if s.startswith('bg'):
return 'bg'
case s if s.startswith('ar'):
return 'ar'
case _:
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
@@ -190,34 +232,66 @@ class Translator:
return 'tr'
case s if 'bg' in s:
return 'bg'
case s if 'ar' in s:
return 'ar'
case _:
return 'en'
except:
return 'en'
def load_translations(self):
"""Load all available translations"""
try:
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
if hasattr(sys, '_MEIPASS'):
locales_dir = os.path.join(sys._MEIPASS, 'locales')
def download_language_file(self, lang_code):
"""Method kept for compatibility but now returns False as language files are integrated"""
print(f"{Fore.YELLOW}{EMOJI['INFO']} Languages are now integrated into the package, no need to download.{Style.RESET_ALL}")
return False
if not os.path.exists(locales_dir):
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
return
def load_translations(self):
"""Load all available translations from the integrated package"""
try:
# Collection of languages we've successfully loaded
loaded_languages = set()
locales_paths = []
# Check for PyInstaller bundle first
if hasattr(sys, '_MEIPASS'):
locales_paths.append(os.path.join(sys._MEIPASS, 'locales'))
# Check script directory next
script_dir = os.path.dirname(os.path.abspath(__file__))
locales_paths.append(os.path.join(script_dir, 'locales'))
# Also check current working directory
locales_paths.append(os.path.join(os.getcwd(), 'locales'))
for locales_dir in locales_paths:
if os.path.exists(locales_dir) and os.path.isdir(locales_dir):
for file in os.listdir(locales_dir):
if file.endswith('.json'):
lang_code = file[:-5] # Remove .json
try:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
loaded_languages.add(lang_code)
loaded_any = True
except (json.JSONDecodeError, UnicodeDecodeError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
continue
for file in os.listdir(locales_dir):
if file.endswith('.json'):
lang_code = file[:-5] # Remove .json
try:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
except (json.JSONDecodeError, UnicodeDecodeError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
continue
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to load translations: {e}{Style.RESET_ALL}")
# Create at least minimal English translations for basic functionality
self.translations['en'] = {"menu": {"title": "Menu", "exit": "Exit", "invalid_choice": "Invalid choice"}}
def fix_arabic(self, text):
if self.current_language == 'ar' and arabic_reshaper and get_display:
try:
reshaped_text = arabic_reshaper.reshape(text)
bidi_text = get_display(reshaped_text)
return bidi_text
except Exception:
return text
return text
def get(self, key, **kwargs):
"""Get translated text with fallback support"""
try:
@@ -226,7 +300,8 @@ class Translator:
if result == key and self.current_language != self.fallback_language:
# Try fallback language if translation not found
result = self._get_translation(self.fallback_language, key)
return result.format(**kwargs) if kwargs else result
formatted = result.format(**kwargs) if kwargs else result
return self.fix_arabic(formatted)
except Exception:
return key
@@ -253,7 +328,11 @@ class Translator:
def get_available_languages(self):
"""Get list of available languages"""
return list(self.translations.keys())
# Get currently loaded languages
available_languages = list(self.translations.keys())
# Sort languages alphabetically for better display
return sorted(available_languages)
# Create translator instance
translator = Translator()
@@ -284,15 +363,21 @@ def print_menu():
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['ERROR']} {translator.get('menu.quit')}",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit', fallback='Bypass Token Limit')}"
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check')}",
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized')}",
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit')}",
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['BACKUP']} {translator.get('menu.restore_machine_id')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}"
}
# Automatically calculate the number of menu items in the left and right columns
@@ -369,21 +454,45 @@ def select_language():
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
# Get available languages either from local directory or GitHub
languages = translator.get_available_languages()
languages_count = len(languages)
# Display all available languages with proper indices
for i, lang in enumerate(languages):
lang_name = translator.get(f"languages.{lang}")
lang_name = translator.get(f"languages.{lang}", fallback=lang)
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_name}")
try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{len(languages)-1}')}: {Style.RESET_ALL}")
if choice.isdigit() and 0 <= int(choice) < len(languages):
translator.set_language(languages[int(choice)])
# Use the actual number of languages in the prompt
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{languages_count-1}')}: {Style.RESET_ALL}")
if choice.isdigit() and 0 <= int(choice) < languages_count:
selected_language = languages[int(choice)]
translator.set_language(selected_language)
# Save selected language to config
config = get_config()
if config and config.has_section('Language'):
config.set('Language', 'current_language', selected_language)
# Get config path from user documents
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
# Write updated config
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('menu.language_config_saved', language=translator.get(f'languages.{selected_language}', fallback=selected_language))}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
# Show invalid choice message with the correct range
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
return False
except (ValueError, IndexError):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
except (ValueError, IndexError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
return False
def check_latest_version():
@@ -391,31 +500,67 @@ def check_latest_version():
try:
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
# Get latest version from GitHub API with timeout and proper headers
# First try GitHub API
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'CursorFreeVIP-Updater'
}
response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
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
latest_version = None
github_error = None
# Check if response is successful
if response.status_code != 200:
raise Exception(f"GitHub API returned status code {response.status_code}")
# Try GitHub API first
try:
github_response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
timeout=10
)
response_data = response.json()
if "tag_name" not in response_data:
raise Exception("No version tag found in GitHub response")
# Check if rate limit exceeded
if github_response.status_code == 403 and "rate limit exceeded" in github_response.text.lower():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Trying backup API...')}{Style.RESET_ALL}")
raise Exception("Rate limit exceeded")
# Check if response is successful
if github_response.status_code != 200:
raise Exception(f"GitHub API returned status code {github_response.status_code}")
github_data = github_response.json()
if "tag_name" not in github_data:
raise Exception("No version tag found in GitHub response")
latest_version = github_data["tag_name"].lstrip('v')
latest_version = response_data["tag_name"].lstrip('v')
except Exception as e:
github_error = str(e)
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.github_api_failed', fallback='GitHub API failed, trying backup API...')}{Style.RESET_ALL}")
# If GitHub API fails, try backup API
try:
backup_headers = {
'Accept': 'application/json',
'User-Agent': 'CursorFreeVIP-Updater'
}
backup_response = requests.get(
"https://pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip",
headers=backup_headers,
timeout=10
)
# Check if response is successful
if backup_response.status_code != 200:
raise Exception(f"Backup API returned status code {backup_response.status_code}")
backup_data = backup_response.json()
if "message" not in backup_data:
raise Exception("No version tag found in backup API response")
latest_version = backup_data["message"].lstrip('v')
except Exception as backup_e:
# If both APIs fail, raise the original GitHub error
raise Exception(f"Both APIs failed. GitHub error: {github_error}, Backup error: {str(backup_e)}")
# Validate version format
if not latest_version:
@@ -565,7 +710,7 @@ def main():
while True:
try:
choice_num = 10
choice_num = 16
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
match choice:
@@ -576,44 +721,69 @@ def main():
case "1":
import reset_machine_manual
reset_machine_manual.run(translator)
print_menu()
print_menu()
case "2":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
case "3":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
case "3":
case "4":
if select_language():
print_menu()
continue
case "4":
case "5":
from oauth_auth import main as oauth_main
oauth_main('google',translator)
print_menu()
case "6":
from oauth_auth import main as oauth_main
oauth_main('github',translator)
print_menu()
case "7":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
case "5":
case "8":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
case "6":
case "9":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
case "7":
case "10":
from config import print_config
print_config(get_config(), translator)
print_menu()
case "8":
case "11":
import bypass_version
bypass_version.main(translator)
print_menu()
case "9":
case "12":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
case "10":
case "13":
import bypass_token_limit
bypass_token_limit.run(translator)
print_menu()
case "14":
import restore_machine_id
restore_machine_id.run(translator)
print_menu()
case "15":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
case "16":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
case _:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()

692
new_signup.py Normal file
View File

@@ -0,0 +1,692 @@
from DrissionPage import ChromiumOptions, ChromiumPage
import time
import os
import signal
import random
from colorama import Fore, Style
import configparser
from pathlib import Path
import sys
from config import get_config
from utils import get_default_browser_path as utils_get_default_browser_path
# Add global variable at the beginning of the file
_translator = None
# Add global variable to track our Chrome processes
_chrome_process_ids = []
def cleanup_chrome_processes(translator=None):
"""Clean only Chrome processes launched by this script"""
global _chrome_process_ids
if not _chrome_process_ids:
print("\nNo Chrome processes to clean...")
return
print("\nCleaning Chrome processes launched by this script...")
try:
if os.name == 'nt':
for pid in _chrome_process_ids:
try:
os.system(f'taskkill /F /PID {pid} /T 2>nul')
except:
pass
else:
for pid in _chrome_process_ids:
try:
os.kill(pid, signal.SIGTERM)
except:
pass
_chrome_process_ids = [] # Reset the list after cleanup
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.cleanup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"清理进程时出错: {e}")
def signal_handler(signum, frame):
"""Handle Ctrl+C signal"""
global _translator
if _translator:
print(f"{Fore.CYAN}{_translator.get('register.exit_signal')}{Style.RESET_ALL}")
else:
print("\n接收到退出信号,正在关闭...")
cleanup_chrome_processes(_translator)
os._exit(0)
def simulate_human_input(page, url, config, translator=None):
"""Visit URL"""
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# First visit blank page
page.get('about:blank')
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# Visit target page
page.get(url)
time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""Fill signup form"""
try:
if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else:
print("\n正在填写注册表单...")
# Fill first name
first_name_input = page.ele("@name=first_name")
if first_name_input:
first_name_input.input(first_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Fill last name
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Fill email
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
print("Form filled successfully")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error filling form: {e}")
return False
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_random_wait_time(config, timing_type='page_load_wait'):
"""
Get random wait time from config
Args:
config: ConfigParser object
timing_type: Type of timing to get (page_load_wait, input_wait, submit_wait)
Returns:
float: Random wait time or fixed time
"""
try:
if not config.has_section('Timing'):
return random.uniform(0.1, 0.8) # Default value
if timing_type == 'random':
min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
max_time = float(config.get('Timing', 'max_random_time', fallback='0.8'))
return random.uniform(min_time, max_time)
time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
# Check if it's a fixed time value
if '-' not in time_value and ',' not in time_value:
return float(time_value) # Return fixed time
# Process range time
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
return random.uniform(min_time, max_time)
except:
return random.uniform(0.1, 0.8) # Return default value when error
def setup_driver(translator=None):
"""Setup browser driver"""
global _chrome_process_ids
try:
# Get config
config = get_config(translator)
# Get browser type and path
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
if not browser_path or not os.path.exists(browser_path):
if translator:
print(f"{Fore.YELLOW}⚠️ {browser_type} {translator.get('register.browser_path_invalid')}{Style.RESET_ALL}")
browser_path = utils_get_default_browser_path(browser_type)
# For backward compatibility, also check Chrome path
if browser_type == 'chrome':
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
if chrome_path and os.path.exists(chrome_path):
browser_path = chrome_path
# Set browser options
co = ChromiumOptions()
# Set browser path
co.set_browser_path(browser_path)
# Use incognito mode
co.set_argument("--incognito")
if sys.platform == "linux":
# Set Linux specific options
co.set_argument("--no-sandbox")
# Set random port
co.auto_port()
# Use headless mode (must be set to False, simulate human operation)
co.headless(False)
# Log browser info
if translator:
print(f"{Fore.CYAN}🌐 {translator.get('register.using_browser', browser=browser_type, path=browser_path)}{Style.RESET_ALL}")
try:
# Load extension
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
if os.path.exists(extension_path):
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error loading extension: {e}")
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
else:
print("Starting browser...")
# Record Chrome processes before launching
before_pids = []
try:
import psutil
browser_process_names = {
'chrome': ['chrome', 'chromium'],
'edge': ['msedge', 'edge'],
'firefox': ['firefox'],
'brave': ['brave', 'brave-browser']
}
process_names = browser_process_names.get(browser_type, ['chrome'])
before_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
except:
pass
# Launch browser
page = ChromiumPage(co)
# Wait a moment for browser to fully launch
time.sleep(1)
# Record browser processes after launching and find new ones
try:
import psutil
process_names = browser_process_names.get(browser_type, ['chrome'])
after_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
# Find new browser processes
new_pids = [pid for pid in after_pids if pid not in before_pids]
_chrome_process_ids.extend(new_pids)
if _chrome_process_ids:
print(f"{translator.get('register.tracking_processes', count=len(_chrome_process_ids), browser=browser_type)}")
else:
print(f"{Fore.YELLOW}Warning: {translator.get('register.no_new_processes_detected', browser=browser_type)}{Style.RESET_ALL}")
except Exception as e:
print(f"{translator.get('register.could_not_track_processes', browser=browser_type, error=str(e))}")
return config, page
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error setting up browser: {e}")
raise
def handle_turnstile(page, config, translator=None):
"""Handle Turnstile verification"""
try:
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.handling_turnstile')}{Style.RESET_ALL}")
else:
print("\nHandling Turnstile verification...")
# from config
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
# Parse random time range
try:
min_time, max_time = map(float, random_time_str.split('-'))
except:
min_time, max_time = 1, 3 # Default value
max_retries = 2
retry_count = 0
while retry_count < max_retries:
retry_count += 1
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.retry_verification', attempt=retry_count)}{Style.RESET_ALL}")
else:
print(f"Attempt {retry_count} of verification...")
try:
# Try to reset turnstile
page.run_js("try { turnstile.reset() } catch(e) { }")
time.sleep(turnstile_time) # from config
# Locate verification box element
challenge_check = (
page.ele("@id=cf-turnstile", timeout=2)
.child()
.shadow_root.ele("tag:iframe")
.ele("tag:body")
.sr("tag:input")
)
if challenge_check:
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.detect_turnstile')}{Style.RESET_ALL}")
else:
print("Detected verification box...")
# from config
time.sleep(random.uniform(min_time, max_time))
challenge_check.click()
time.sleep(turnstile_time) # from config
# check verification result
if check_verification_success(page, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("Verification successful!")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print(f"Verification attempt failed: {e}")
# Check if verification has been successful
if check_verification_success(page, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("Verification successful!")
return True
time.sleep(random.uniform(min_time, max_time))
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("Exceeded maximum retry attempts")
return False
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error in verification process: {e}")
return False
def check_verification_success(page, translator=None):
"""Check if verification is successful"""
try:
# Check if there is a subsequent form element, indicating verification has passed
if (page.ele("@name=password", timeout=0.5) or
page.ele("@name=email", timeout=0.5) or
page.ele("@data-index=0", timeout=0.5) or
page.ele("Account Settings", timeout=0.5)):
return True
# Check if there is an error message
error_messages = [
'xpath://div[contains(text(), "Can\'t verify the user is human")]',
'xpath://div[contains(text(), "Error: 600010")]',
'xpath://div[contains(text(), "Please try again")]'
]
for error_xpath in error_messages:
if page.ele(error_xpath):
return False
return False
except:
return False
def generate_password(length=12):
"""Generate random password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, config, translator=None):
"""
Fill password form
"""
try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else 'Setting password'}{Style.RESET_ALL}")
# Fill password
password_input = page.ele("@name=password")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
if password_input:
password_input.input(password)
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else 'Password submitted'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'Error setting password: {str(e)}'}{Style.RESET_ALL}")
return False
def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
"""Handle verification code"""
try:
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
# Check if using manual input verification code
if hasattr(controller, 'get_verification_code') and email_tab is None: # Manual mode
verification_code = controller.get_verification_code()
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
print(f"{translator.get('register.verification_success')}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
return False, None
# Automatic verification code logic
elif email_tab:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# Use existing email_tab to refresh email
email_tab.refresh_inbox()
time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
# Check if there is a verification code email
if email_tab.check_for_cursor_email():
verification_code = email_tab.get_verification_code()
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
if translator:
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("最后一次验证失败")
return False, None
# Get verification code, set timeout
verification_code = None
max_attempts = 20
retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
start_time = time.time()
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # This can be kept unchanged because it is a fixed value
if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
for attempt in range(max_attempts):
# Check if timeout
if time.time() - start_time > timeout:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
break
verification_code = controller.get_verification_code()
if verification_code:
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
break
remaining_time = int(timeout - (time.time() - start_time))
if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
# Refresh email
email_tab.refresh_inbox()
time.sleep(retry_interval) # Use get_random_wait_time
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
if translator:
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
# Return success directly, let cursor_register.py handle account information acquisition
return True, browser_tab
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
return False, None
return False, None
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
return False, None
def handle_sign_in(browser_tab, email, password, translator=None):
"""Handle login process"""
try:
# Check if on login page
sign_in_header = browser_tab.ele('xpath://h1[contains(text(), "Sign in")]')
if not sign_in_header:
return True # If not on login page, it means login is successful
print(f"{Fore.CYAN}检测到登录页面,开始登录...{Style.RESET_ALL}")
# Fill email
email_input = browser_tab.ele('@name=email')
if email_input:
email_input.input(email)
time.sleep(1)
# Click Continue
continue_button = browser_tab.ele('xpath://button[contains(@class, "BrandedButton") and text()="Continue"]')
if continue_button:
continue_button.click()
time.sleep(2)
# Handle Turnstile verification
if handle_turnstile(browser_tab, translator):
# Fill password
password_input = browser_tab.ele('@name=password')
if password_input:
password_input.input(password)
time.sleep(1)
# Click Sign in
sign_in_button = browser_tab.ele('xpath://button[@name="intent" and @value="password"]')
if sign_in_button:
sign_in_button.click()
time.sleep(2)
# Handle last Turnstile verification
if handle_turnstile(browser_tab, translator):
print(f"{Fore.GREEN}Login successful!{Style.RESET_ALL}")
time.sleep(3)
return True
print(f"{Fore.RED}Login failed{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}Login process error: {str(e)}{Style.RESET_ALL}")
return False
def main(email=None, password=None, first_name=None, last_name=None, email_tab=None, controller=None, translator=None):
"""Main function, can receive account information, email tab, and translator"""
global _translator
global _chrome_process_ids
_translator = translator # Save to global variable
_chrome_process_ids = [] # Reset the process IDs list
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
page = None
success = False
try:
config, page = setup_driver(translator)
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
# Visit registration page
url = "https://authenticator.cursor.sh/sign-up"
# Visit page
simulate_human_input(page, url, config, translator)
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# If account information is not provided, generate random information
if not all([email, password, first_name, last_name]):
first_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
last_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
email = f"{first_name.lower()}{random.randint(100,999)}@example.com"
password = generate_password()
# Save account information
with open('test_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {email}\n")
f.write(f"Password: {password}\n")
f.write(f"{'='*50}\n")
# Fill form
if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
# Handle first Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# Fill password
if fill_password(page, password, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
# Handle second Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, config, translator):
success = True
return True, page
else:
print(f"\n{Fore.RED}{translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.first_verification_failed') if translator else 'First verification failed'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"发生错误: {e}")
return False, None
finally:
if page and not success: # Only clean up when failed
try:
page.quit()
except:
pass
cleanup_chrome_processes(translator)
if __name__ == "__main__":
main() # Run without parameters, use randomly generated information

1052
oauth_auth.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,3 +8,5 @@ pyinstaller
DrissionPage>=4.0.0
selenium
webdriver_manager
arabic-reshaper
python-bidi

View File

@@ -12,7 +12,6 @@ import glob
from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
from datetime import datetime
@@ -31,6 +30,20 @@ EMOJI = {
"WARNING": "⚠️",
}
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
@@ -223,10 +236,14 @@ def get_workbench_cursor_path(translator=None) -> str:
base_path = config.get('WindowsPaths', 'cursor_path')
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
main_path = os.path.join(base_path, paths_map[system]["main"])

406
restore_machine_id.py Normal file
View File

@@ -0,0 +1,406 @@
import os
import sys
import json
import uuid
import hashlib
import shutil
import sqlite3
import platform
import re
import glob
import tempfile
from colorama import Fore, Style, init
from typing import Tuple
import configparser
import traceback
from config import get_config
from datetime import datetime
# 导入共享函数
from reset_machine_manual import get_cursor_machine_id_path, get_user_documents_path
# 初始化 colorama
init()
# 定义表情符号常量
EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "🔄",
"WARNING": "⚠️",
}
class ConfigError(Exception):
"""配置错误异常"""
pass
class MachineIDRestorer:
def __init__(self, translator=None):
self.translator = translator
# 读取配置
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file, encoding='utf-8')
# 根据操作系统获取路径
if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA")
if appdata is None:
raise EnvironmentError("APPDATA Environment Variable Not Set")
if not config.has_section('WindowsPaths'):
raise ConfigError("WindowsPaths section not found in config")
self.db_path = config.get('WindowsPaths', 'storage_path')
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == "darwin": # macOS
if not config.has_section('MacPaths'):
raise ConfigError("MacPaths section not found in config")
self.db_path = config.get('MacPaths', 'storage_path')
self.sqlite_path = config.get('MacPaths', 'sqlite_path')
elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'):
raise ConfigError("LinuxPaths section not found in config")
self.db_path = config.get('LinuxPaths', 'storage_path')
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
def find_backups(self):
"""查找可用的备份文件"""
db_dir = os.path.dirname(self.db_path)
db_name = os.path.basename(self.db_path)
# 查找格式为 {db_name}.bak.{timestamp} 的文件
backup_pattern = f"{db_name}.bak.*"
backups = glob.glob(os.path.join(db_dir, backup_pattern))
# 按创建时间排序(最新的在前)
backups.sort(key=os.path.getctime, reverse=True)
return backups
def list_backups(self):
"""列出所有可用备份"""
backups = self.find_backups()
if not backups:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.no_backups_found')}{Style.RESET_ALL}")
return None
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.available_backups')}:{Style.RESET_ALL}")
for i, backup in enumerate(backups, 1):
# 获取备份文件信息
timestamp_str = backup.split('.')[-1]
try:
# 尝试解析时间戳(如果格式为 YYYYmmdd_HHMMSS
timestamp = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
date_str = timestamp.strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
date_str = "未知日期"
# 获取文件大小
size = os.path.getsize(backup)
size_str = f"{size / 1024:.1f} KB"
print(f"{i}. {Fore.GREEN}{os.path.basename(backup)}{Style.RESET_ALL} ({date_str}, {size_str})")
return backups
def select_backup(self):
"""让用户选择要恢复的备份"""
backups = self.list_backups()
if not backups:
return None
while True:
try:
choice = input(f"{EMOJI['INFO']} {self.translator.get('restore.select_backup')} (1-{len(backups)}, 0 {self.translator.get('restore.to_cancel')}): ")
if choice.strip() == '0':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
return None
index = int(choice) - 1
if 0 <= index < len(backups):
return backups[index]
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.invalid_selection')}{Style.RESET_ALL}")
except ValueError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.please_enter_number')}{Style.RESET_ALL}")
def extract_ids_from_backup(self, backup_path):
"""从备份文件中提取机器ID"""
try:
with open(backup_path, "r", encoding="utf-8") as f:
backup_data = json.load(f)
# 提取需要恢复的ID
ids = {
"telemetry.devDeviceId": backup_data.get("telemetry.devDeviceId", ""),
"telemetry.macMachineId": backup_data.get("telemetry.macMachineId", ""),
"telemetry.machineId": backup_data.get("telemetry.machineId", ""),
"telemetry.sqmId": backup_data.get("telemetry.sqmId", ""),
"storage.serviceMachineId": backup_data.get("storage.serviceMachineId",
backup_data.get("telemetry.devDeviceId", ""))
}
# 确保所有ID都存在
for key, value in ids.items():
if not value:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.missing_id', id=key)}{Style.RESET_ALL}")
return ids
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.read_backup_failed', error=str(e))}{Style.RESET_ALL}")
return None
def update_current_file(self, ids):
"""更新当前的storage.json文件"""
try:
if not os.path.exists(self.db_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.current_file_not_found')}: {self.db_path}{Style.RESET_ALL}")
return False
# 读取当前文件
with open(self.db_path, "r", encoding="utf-8") as f:
current_data = json.load(f)
# 创建当前文件的备份
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.db_path}.restore_bak.{timestamp}"
shutil.copy2(self.db_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['BACKUP']} {self.translator.get('restore.current_backup_created')}: {backup_path}{Style.RESET_ALL}")
# 更新ID
current_data.update(ids)
# 保存更新后的文件
with open(self.db_path, "w", encoding="utf-8") as f:
json.dump(current_data, f, indent=4)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.storage_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_sqlite_db(self, ids):
"""更新SQLite数据库中的ID"""
try:
if not os.path.exists(self.sqlite_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_not_found')}: {self.sqlite_path}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_sqlite')}...{Style.RESET_ALL}")
conn = sqlite3.connect(self.sqlite_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS ItemTable (
key TEXT PRIMARY KEY,
value TEXT
)
""")
for key, value in ids.items():
cursor.execute("""
INSERT OR REPLACE INTO ItemTable (key, value)
VALUES (?, ?)
""", (key, value))
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('restore.updating_pair')}: {key}{Style.RESET_ALL}")
conn.commit()
conn.close()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.sqlite_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_machine_id_file(self, dev_device_id):
"""更新machineId文件"""
try:
machine_id_path = get_cursor_machine_id_path(self.translator)
# 创建目录(如果不存在)
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
# 备份当前文件(如果存在)
if os.path.exists(machine_id_path):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{machine_id_path}.restore_bak.{timestamp}"
try:
shutil.copy2(machine_id_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('restore.machine_id_backup_created')}: {backup_path}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.backup_creation_failed', error=str(e))}{Style.RESET_ALL}")
# 写入新的ID
with open(machine_id_path, "w", encoding="utf-8") as f:
f.write(dev_device_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.machine_id_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.machine_id_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_system_ids(self, ids):
"""更新系统级ID特定于操作系统"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_system_ids')}...{Style.RESET_ALL}")
if sys.platform.startswith("win"):
self._update_windows_system_ids(ids)
elif sys.platform == "darwin":
self._update_macos_system_ids(ids)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def _update_windows_system_ids(self, ids):
"""更新Windows系统ID"""
try:
import winreg
# 更新MachineGuid
guid = ids.get("telemetry.devDeviceId", "")
if guid:
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Cryptography",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, guid)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_guid_updated')}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
# 更新SQMClient MachineId
sqm_id = ids.get("telemetry.sqmId", "")
if sqm_id:
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\SQMClient",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, sqm_id)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_id_updated')}{Style.RESET_ALL}")
except FileNotFoundError:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.sqm_client_key_not_found')}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
def _update_macos_system_ids(self, ids):
"""更新macOS系统ID"""
try:
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
if os.path.exists(uuid_file):
mac_id = ids.get("telemetry.macMachineId", "")
if mac_id:
cmd = f'sudo plutil -replace "UUID" -string "{mac_id}" "{uuid_file}"'
result = os.system(cmd)
if result == 0:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.macos_platform_uuid_updated')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_macos_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
def restore_machine_ids(self):
"""恢复之前备份的机器ID"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.starting')}...{Style.RESET_ALL}")
# 选择要恢复的备份
backup_path = self.select_backup()
if not backup_path:
return False
# 从备份中提取ID
ids = self.extract_ids_from_backup(backup_path)
if not ids:
return False
# 显示将要恢复的ID
print(f"\n{Fore.CYAN}{self.translator.get('restore.ids_to_restore')}:{Style.RESET_ALL}")
for key, value in ids.items():
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
# 确认恢复
confirm = input(f"\n{EMOJI['WARNING']} {self.translator.get('restore.confirm')} (y/n): ")
if confirm.lower() != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
return False
# 更新当前文件
if not self.update_current_file(ids):
return False
# 更新SQLite数据库
self.update_sqlite_db(ids)
# 更新machineId文件
self.update_machine_id_file(ids.get("telemetry.devDeviceId", ""))
# 更新系统ID
self.update_system_ids(ids)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.success')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.process_error', error=str(e))}{Style.RESET_ALL}")
return False
def run(translator=None):
"""恢复机器ID的主函数"""
config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('restore.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
restorer = MachineIDRestorer(translator)
restorer.restore_machine_ids()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('restore.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)

View File

@@ -11,7 +11,6 @@ import tempfile
from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
import glob
@@ -30,6 +29,20 @@ EMOJI = {
"WARNING": "⚠️",
}
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
@@ -178,15 +191,22 @@ def get_cursor_machine_id_path(translator=None) -> str:
def get_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
# Read configuration
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if os.path.exists(config_file):
config.read(config_file)
paths_map = {
"Darwin": { # macOS
"base": "/Applications/Cursor.app/Contents/Resources/app",
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Windows": {
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
"main": "out/vs/workbench/workbench.desktop.main.js"
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
@@ -210,7 +230,20 @@ def get_workbench_cursor_path(translator=None) -> str:
return main_path
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"]
if system == "Windows":
base_path = config.get('WindowsPaths', 'cursor_path')
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
# Get the main path for non-Linux systems or if Linux path wasn't found in the loop
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):