Pro Trial'
+ CBadge_new_pattern = r'
Pro'
+
+ CToast_old_pattern = r'notifications-toasts'
+ CToast_new_pattern = r'notifications-toasts hidden'
+
+ # Replace content
+ content = content.replace(CButton_old_pattern, CButton_new_pattern)
+ content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
+ content = content.replace(CToast_old_pattern, CToast_new_pattern)
+
+ # Write to temporary file
+ tmp_file.write(content)
+ tmp_path = tmp_file.name
+
+ # Backup original file
+ backup_path = file_path + ".backup"
+ if os.path.exists(backup_path):
+ os.remove(backup_path)
+ shutil.copy2(file_path, backup_path)
+
+ # Move temporary file to original position
+ if os.path.exists(file_path):
+ os.remove(file_path)
+ shutil.move(tmp_path, file_path)
+
+ # Restore original permissions
+ os.chmod(file_path, original_mode)
+ if os.name != "nt": # Not Windows
+ os.chown(file_path, original_uid, original_gid)
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
+ return True
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
+ if "tmp_path" in locals():
+ try:
+ os.unlink(tmp_path)
+ except:
+ pass
+ return False
+
+def modify_main_js(main_path: str, translator) -> bool:
+ """Modify main.js file"""
+ try:
+ original_stat = os.stat(main_path)
+ original_mode = original_stat.st_mode
+ original_uid = original_stat.st_uid
+ original_gid = original_stat.st_gid
+
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
+ with open(main_path, "r", encoding="utf-8") as main_file:
+ content = main_file.read()
+
+ patterns = {
+ r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}",
+ r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}",
+ }
+
+ for pattern, replacement in patterns.items():
+ content = re.sub(pattern, replacement, content)
+
+ tmp_file.write(content)
+ tmp_path = tmp_file.name
+
+ shutil.copy2(main_path, main_path + ".old")
+ shutil.move(tmp_path, main_path)
+
+ os.chmod(main_path, original_mode)
+ if os.name != "nt":
+ os.chown(main_path, original_uid, original_gid)
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
+ return True
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
+ if "tmp_path" in locals():
+ os.unlink(tmp_path)
+ return False
+
+def patch_cursor_get_machine_id(translator) -> bool:
+ """Patch Cursor getMachineId function"""
+ try:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
+
+ # Get paths
+ pkg_path, main_path = get_cursor_paths(translator)
+
+ # Check file permissions
+ for file_path in [pkg_path, main_path]:
+ if not os.path.isfile(file_path):
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
+ return False
+ if not os.access(file_path, os.W_OK):
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
+ return False
+
+ # Get version number
+ try:
+ with open(pkg_path, "r", encoding="utf-8") as f:
+ version = json.load(f)["version"]
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.current_version', version=version)}{Style.RESET_ALL}")
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+ # Check version
+ if not version_check(version, min_version="0.45.0", translator=translator):
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
+ return False
+
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
+
+ # Backup file
+ backup_path = main_path + ".bak"
+ if not os.path.exists(backup_path):
+ shutil.copy2(main_path, backup_path)
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
+
+ # Modify file
+ if not modify_main_js(main_path, translator):
+ return False
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.patch_completed')}{Style.RESET_ALL}")
+ return True
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.patch_failed', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+class MachineIDResetter:
+ def __init__(self, translator=None):
+ self.translator = translator
+
+ # 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 not os.path.exists(config_file):
+ raise FileNotFoundError(f"Config file not found: {config_file}")
+
+ config.read(config_file, encoding='utf-8')
+
+ # Check operating system
+ 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'):
+ config.add_section('WindowsPaths')
+ config.set('WindowsPaths', 'storage_path', os.path.join(
+ appdata, "Cursor", "User", "globalStorage", "storage.json"
+ ))
+ config.set('WindowsPaths', 'sqlite_path', os.path.join(
+ appdata, "Cursor", "User", "globalStorage", "state.vscdb"
+ ))
+
+ 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'):
+ config.add_section('MacPaths')
+ config.set('MacPaths', 'storage_path', os.path.abspath(os.path.expanduser(
+ "~/Library/Application Support/Cursor/User/globalStorage/storage.json"
+ )))
+ config.set('MacPaths', 'sqlite_path', os.path.abspath(os.path.expanduser(
+ "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
+ )))
+
+ 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'):
+ config.add_section('LinuxPaths')
+ # Get actual user's home directory
+ sudo_user = os.environ.get('SUDO_USER')
+ actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
+
+ config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
+ actual_home,
+ ".config/cursor/User/globalStorage/storage.json"
+ )))
+ config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
+ actual_home,
+ ".config/cursor/User/globalStorage/state.vscdb"
+ )))
+
+ 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}")
+
+ # Save any changes to config file
+ with open(config_file, 'w', encoding='utf-8') as f:
+ config.write(f)
+
+ def generate_new_ids(self):
+ """Generate new machine ID"""
+ # Generate new UUID
+ dev_device_id = str(uuid.uuid4())
+
+ # Generate new machineId (64 characters of hexadecimal)
+ machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
+
+ # Generate new macMachineId (128 characters of hexadecimal)
+ mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
+
+ # Generate new sqmId
+ sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
+
+ self.update_machine_id_file(dev_device_id)
+
+ return {
+ "telemetry.devDeviceId": dev_device_id,
+ "telemetry.macMachineId": mac_machine_id,
+ "telemetry.machineId": machine_id,
+ "telemetry.sqmId": sqm_id,
+ "storage.serviceMachineId": dev_device_id, # Add storage.serviceMachineId
+ }
+
+ def update_sqlite_db(self, new_ids):
+ """Update machine ID in SQLite database"""
+ try:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.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
+ )
+ """)
+
+ updates = [
+ (key, value) for key, value in new_ids.items()
+ ]
+
+ for key, value in updates:
+ cursor.execute("""
+ INSERT OR REPLACE INTO ItemTable (key, value)
+ VALUES (?, ?)
+ """, (key, value))
+ print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('reset.updating_pair')}: {key}{Style.RESET_ALL}")
+
+ conn.commit()
+ conn.close()
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.sqlite_success')}{Style.RESET_ALL}")
+ return True
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.sqlite_error', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+ def update_system_ids(self, new_ids):
+ """Update system-level IDs"""
+ try:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
+
+ if sys.platform.startswith("win"):
+ self._update_windows_machine_guid()
+ self._update_windows_machine_id()
+ elif sys.platform == "darwin":
+ self._update_macos_platform_uuid(new_ids)
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.system_ids_updated')}{Style.RESET_ALL}")
+ return True
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+ def _update_windows_machine_guid(self):
+ """Update Windows MachineGuid"""
+ try:
+ import winreg
+ key = winreg.OpenKey(
+ winreg.HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Cryptography",
+ 0,
+ winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
+ )
+ new_guid = str(uuid.uuid4())
+ winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
+ winreg.CloseKey(key)
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
+ except PermissionError:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
+ raise
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
+ raise
+
+ def _update_windows_machine_id(self):
+ """Update Windows MachineId in SQMClient registry"""
+ try:
+ import winreg
+ # 1. Generate new GUID
+ new_guid = "{" + str(uuid.uuid4()).upper() + "}"
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.new_machine_id')}: {new_guid}{Style.RESET_ALL}")
+
+ # 2. Open the registry key
+ try:
+ key = winreg.OpenKey(
+ winreg.HKEY_LOCAL_MACHINE,
+ r"SOFTWARE\Microsoft\SQMClient",
+ 0,
+ winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
+ )
+ except FileNotFoundError:
+ # If the key does not exist, create it
+ key = winreg.CreateKey(
+ winreg.HKEY_LOCAL_MACHINE,
+ r"SOFTWARE\Microsoft\SQMClient"
+ )
+
+ # 3. Set MachineId value
+ winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, new_guid)
+ winreg.CloseKey(key)
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_id_updated')}{Style.RESET_ALL}")
+ return True
+
+ except PermissionError:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
+ return False
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+
+ def _update_macos_platform_uuid(self, new_ids):
+ """Update macOS Platform UUID"""
+ try:
+ uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
+ if os.path.exists(uuid_file):
+ # Use sudo to execute plutil command
+ cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
+ result = os.system(cmd)
+ if result == 0:
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
+ else:
+ raise Exception(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_macos_platform_uuid_failed', error=str(e))}{Style.RESET_ALL}")
+ raise
+
+ def reset_machine_ids(self):
+ """Reset machine ID and backup original file"""
+ try:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
+
+ if not os.path.exists(self.db_path):
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.not_found')}: {self.db_path}{Style.RESET_ALL}")
+ return False
+
+ if not os.access(self.db_path, os.R_OK | os.W_OK):
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.no_permission')}{Style.RESET_ALL}")
+ return False
+
+ print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.reading')}...{Style.RESET_ALL}")
+ with open(self.db_path, "r", encoding="utf-8") as f:
+ config = json.load(f)
+
+ backup_path = self.db_path + ".bak"
+ if not os.path.exists(backup_path):
+ print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
+ shutil.copy2(self.db_path, backup_path)
+ else:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
+
+ print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
+ new_ids = self.generate_new_ids()
+
+ # Update configuration file
+ config.update(new_ids)
+
+ print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
+ with open(self.db_path, "w", encoding="utf-8") as f:
+ json.dump(config, f, indent=4)
+
+ # Update SQLite database
+ self.update_sqlite_db(new_ids)
+
+ # Update system IDs
+ self.update_system_ids(new_ids)
+
+
+ # Modify workbench.desktop.main.js
+ workbench_path = get_workbench_cursor_path(self.translator)
+ modify_workbench_js(workbench_path, self.translator)
+
+ # Check Cursor version and perform corresponding actions
+
+ greater_than_0_45 = check_cursor_version(self.translator)
+ if greater_than_0_45:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0,{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
+ patch_cursor_get_machine_id(self.translator)
+ else:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}")
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
+ print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
+ for key, value in new_ids.items():
+ print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
+
+ return True
+
+ except PermissionError as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_error', error=str(e))}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
+ return False
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}")
+ return False
+
+ def update_machine_id_file(self, machine_id: str) -> bool:
+ """
+ Update machineId file with new machine_id
+ Args:
+ machine_id (str): New machine ID to write
+ Returns:
+ bool: True if successful, False otherwise
+ """
+ try:
+ # Get the machineId file path
+ machine_id_path = get_cursor_machine_id_path()
+
+ # Create directory if it doesn't exist
+ os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
+
+ # Create backup if file exists
+ if os.path.exists(machine_id_path):
+ backup_path = machine_id_path + ".backup"
+ try:
+ shutil.copy2(machine_id_path, backup_path)
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
+ except Exception as e:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_creation_failed', error=str(e)) if self.translator else f'Could not create backup: {str(e)}'}{Style.RESET_ALL}")
+
+ # Write new machine ID to file
+ with open(machine_id_path, "w", encoding="utf-8") as f:
+ f.write(machine_id)
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.update_success') if self.translator else 'Successfully updated machineId file'}{Style.RESET_ALL}")
+ return True
+
+ except Exception as e:
+ error_msg = f"Failed to update machineId file: {str(e)}"
+ if self.translator:
+ error_msg = self.translator.get('reset.update_failed', error=str(e))
+ print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
+ return False
+
+def run(translator=None):
+ 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('reset.title')}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
+
+ resetter = MachineIDResetter(translator) # Correctly pass translator
+ resetter.reset_machine_ids()
+
+ print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
+ input(f"{EMOJI['INFO']} {translator.get('reset.press_enter')}...")
if __name__ == "__main__":
- try:
- reset_cursor()
- except KeyboardInterrupt:
- print(f"\n\n{Fore.RED}{EMOJI['WARNING']} Process interrupted.{Style.RESET_ALL}")
- sys.exit(1)
- except Exception as e:
- print(f"\n{Fore.RED}{EMOJI['ERROR']} Unexpected error: {str(e)}{Style.RESET_ALL}")
- sys.exit(1)
+ from main import translator as main_translator
+ run(main_translator)
From 735dd8c1eb15c82790dc85e1615e790bc346981f Mon Sep 17 00:00:00 2001
From: yeongpin
Date: Mon, 31 Mar 2025 11:58:54 +0800
Subject: [PATCH 40/47] refactor: enhance user feedback and error messages with
translation support across multiple modules
---
config.py | 66 +++++++-------
locales/en.json | 77 +++++++++++++++--
locales/zh_cn.json | 70 ++++++++++++++-
locales/zh_tw.json | 70 ++++++++++++++-
new_tempemail.py | 10 +--
oauth_auth.py | 209 +++++++++++++++++++++++----------------------
6 files changed, 347 insertions(+), 155 deletions(-)
diff --git a/config.py b/config.py
index 1d8e6bf..cf481c0 100644
--- a/config.py
+++ b/config.py
@@ -120,10 +120,10 @@ def setup_config(translator=None):
break
if not cursor_dir:
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} Neither Cursor nor cursor directory found in {config_base}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base) if translator else f'Neither Cursor nor cursor directory found in {config_base}'}{Style.RESET_ALL}")
if root_home:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Also checked {root_home}/.config{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Please make sure Cursor is installed and has been run at least once{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.also_checked', path=f'{root_home}/.config') if translator else f'Also checked {root_home}/.config'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
# Define Linux paths using the found cursor directory
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
@@ -133,50 +133,50 @@ def setup_config(translator=None):
try:
# Check storage directory
if storage_dir and not os.path.exists(storage_dir):
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} Storage directory not found: {storage_dir}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Please make sure Cursor is installed and has been run at least once{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir) if translator else f'Storage directory not found: {storage_dir}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
# Check storage.json with more detailed verification
if storage_path and os.path.exists(storage_path):
# Get file stats
try:
stat = os.stat(storage_path)
- print(f"{Fore.GREEN}{EMOJI['INFO']} Storage file found: {storage_path}{Style.RESET_ALL}")
- print(f"{Fore.GREEN}{EMOJI['INFO']} File size: {stat.st_size} bytes{Style.RESET_ALL}")
- print(f"{Fore.GREEN}{EMOJI['INFO']} File permissions: {oct(stat.st_mode & 0o777)}{Style.RESET_ALL}")
- print(f"{Fore.GREEN}{EMOJI['INFO']} File owner: {stat.st_uid}{Style.RESET_ALL}")
- print(f"{Fore.GREEN}{EMOJI['INFO']} File group: {stat.st_gid}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path) if translator else f'Storage file found: {storage_path}'}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', size=stat.st_size) if translator else f'File size: {stat.st_size} bytes'}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', permissions=oct(stat.st_mode & 0o777)) if translator else f'File permissions: {oct(stat.st_mode & 0o777)}'}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', owner=stat.st_uid) if translator else f'File owner: {stat.st_uid}'}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', group=stat.st_gid) if translator else f'File group: {stat.st_gid}'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error getting file stats: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e)) if translator else f'Error getting file stats: {str(e)}'}{Style.RESET_ALL}")
# Check if file is readable and writable
if not os.access(storage_path, os.R_OK | os.W_OK):
- print(f"{Fore.RED}{EMOJI['ERROR']} Permission denied: {storage_path}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path) if translator else f'Permission denied: {storage_path}'}{Style.RESET_ALL}")
if sudo_user:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running: chown {sudo_user}:{sudo_user} {storage_path}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} And: chmod 644 {storage_path}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {sudo_user}:{sudo_user} {storage_path}') if translator else f'Try running: chown {sudo_user}:{sudo_user} {storage_path}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
else:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running: chown {current_user}:{current_user} {storage_path}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} And: chmod 644 {storage_path}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {current_user}:{current_user} {storage_path}') if translator else f'Try running: chown {current_user}:{current_user} {storage_path}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
# Try to read the file to verify it's not corrupted
try:
with open(storage_path, 'r') as f:
content = f.read()
if not content.strip():
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} Storage file is empty: {storage_path}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Please make sure Cursor is installed and has been run at least once{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path) if translator else f'Storage file is empty: {storage_path}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted, please reinstall Cursor'}{Style.RESET_ALL}")
else:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Storage file is valid and contains data{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data') if translator else 'Storage file is valid and contains data'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error reading storage file: {str(e)}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} The file might be corrupted. Please reinstall Cursor{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e)) if translator else f'Error reading storage file: {str(e)}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted. Please reinstall Cursor'}{Style.RESET_ALL}")
elif storage_path:
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} Storage file not found: {storage_path}{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Please make sure Cursor is installed and has been run at least once{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path) if translator else f'Storage file not found: {storage_path}'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
except (OSError, IOError) as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error checking Linux paths: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e)) if translator else f'Error checking Linux paths: {str(e)}'}{Style.RESET_ALL}")
# Define all paths using the found cursor directory
default_config['LinuxPaths'] = {
@@ -202,13 +202,13 @@ def setup_config(translator=None):
config.set(section, option, str(value))
config_modified = True
if translator:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.config_option_added', option=f'{section}.{option}') if translator else f'Config option added: {section}.{option}'}{Style.RESET_ALL}")
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_updated')}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_updated') if translator else 'Config updated'}{Style.RESET_ALL}")
else:
for section, options in default_config.items():
config.add_section(section)
@@ -218,31 +218,31 @@ def setup_config(translator=None):
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_created', config_file=config_file) if translator else f'Config created: {config_file}'}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
- print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.config_setup_error', error=str(e)) if translator else f'Error setting up config: {str(e)}'}{Style.RESET_ALL}")
return None
def print_config(config, translator=None):
"""Print configuration in a readable format"""
if not config:
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available')}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available') if translator else 'Configuration not available'}{Style.RESET_ALL}")
return
- print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration')}:{Style.RESET_ALL}")
+ print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration') if translator else 'Configuration'}:{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
for section in config.sections():
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
for key, value in config.items(section):
# 对布尔值进行特殊处理,使其显示为彩色
if value.lower() in ('true', 'yes', 'on', '1'):
- value_display = f"{Fore.GREEN}{translator.get('config.enabled')}{Style.RESET_ALL}"
+ value_display = f"{Fore.GREEN}{translator.get('config.enabled') if translator else 'Enabled'}{Style.RESET_ALL}"
elif value.lower() in ('false', 'no', 'off', '0'):
- value_display = f"{Fore.RED}{translator.get('config.disabled')}{Style.RESET_ALL}"
+ value_display = f"{Fore.RED}{translator.get('config.disabled') if translator else 'Disabled'}{Style.RESET_ALL}"
else:
value_display = value
@@ -250,7 +250,7 @@ def print_config(config, translator=None):
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory')}: {config_dir}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory') if translator else 'Config Directory'}: {config_dir}{Style.RESET_ALL}")
print()
diff --git a/locales/en.json b/locales/en.json
index c3bef05..05a4877 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -280,7 +280,13 @@
"available_domains_loaded": "Available Domains Loaded: {count}",
"domains_filtered": "Domains Filtered: {count}",
"trying_to_create_email": "Trying to create email: {email}",
- "domain_blocked": "Domain Blocked: {domain}"
+ "domain_blocked": "Domain Blocked: {domain}",
+ "using_chrome_profile": "Using Chrome profile from: {user_data_dir}",
+ "no_display_found": "No display found. Make sure X server is running.",
+ "try_export_display": "Try: export DISPLAY=:0",
+ "extension_load_error": "Extension Load Error: {error}",
+ "make_sure_chrome_chromium_is_properly_installed": "Make sure Chrome/Chromium is properly installed",
+ "try_install_chromium": "Try: sudo apt install chromium-browser"
},
"update": {
"title": "Disable Cursor Auto Update",
@@ -498,14 +504,14 @@
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Please make sure Cursor is installed and has been run at least once",
"storage_directory_not_found": "Storage directory not found: {storage_dir}",
"storage_file_found": "Storage file found: {storage_path}",
- "file_size": "File size: {size}",
+ "file_size": "File size: {size} bytes",
"file_permissions": "File permissions: {permissions}",
"file_owner": "File owner: {owner}",
"file_group": "File group: {group}",
"error_getting_file_stats": "Error getting file stats: {error}",
"permission_denied": "Permission denied: {storage_path}",
- "try_running": "Try running 'cursor --help' to check if it's installed",
- "and": "and",
+ "try_running": "Try running: {command}",
+ "and": "And",
"storage_file_is_empty": "Storage file is empty: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
"storage_file_not_found": "Storage file not found: {storage_path}",
@@ -513,7 +519,64 @@
"config_option_added": "Config option added: {option}",
"config_updated": "Config updated",
"config_created": "Config created: {config_file}",
- "config_setup_error": "Error setting up config: {error}"
-
+ "config_setup_error": "Error setting up config: {error}",
+ "storage_file_is_valid_and_contains_data": "Storage file is valid and contains data",
+ "error_reading_storage_file": "Error reading storage file: {error}",
+ "also_checked": "Also checked {path}"
+ },
+ "oauth": {
+ "authentication_button_not_found": "Authentication button not found",
+ "authentication_failed": "Authentication failed: {error}",
+ "found_cookies": "Found {count} cookies",
+ "token_extraction_error": "Token extraction error: {error}",
+ "authentication_successful": "Authentication successful - Email: {email}",
+ "missing_authentication_data": "Missing authentication data: {data}",
+ "failed_to_delete_account": "Failed to delete account: {error}",
+ "invalid_authentication_type": "Invalid authentication type",
+ "auth_update_success": "Auth update success",
+ "browser_closed": "Browser closed",
+ "auth_update_failed": "Auth update failed",
+ "google_start": "Google start",
+ "github_start": "Github start",
+ "usage_count": "Usage count: {usage}",
+ "account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
+ "starting_new_authentication_process": "Starting new authentication process...",
+ "failed_to_delete_expired_account": "Failed to delete expired account",
+ "could_not_check_usage_count": "Could not check usage count: {error}",
+ "found_email": "Found email: {email}",
+ "could_not_find_email": "Could not find email: {error}",
+ "could_not_find_usage_count": "Could not find usage count: {error}",
+ "already_on_settings_page": "Already on settings page!",
+ "failed_to_extract_auth_info": "Failed to extract auth info: {error}",
+ "no_chrome_profiles_found": "No Chrome profiles found, using Default",
+ "found_default_chrome_profile": "Found Default Chrome profile",
+ "using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
+ "error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
+ "initializing_browser_setup": "Initializing browser setup...",
+ "detected_platform": "Detected platform: {platform}",
+ "running_as_root_warning": "Running as root is not recommended for browser automation",
+ "consider_running_without_sudo": "Consider running the script without sudo",
+ "no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
+ "supported_browsers": "Supported browsers for {platform}",
+ "using_browser_profile": "Using browser profile: {profile}",
+ "starting_browser": "Starting browser at: {path}",
+ "browser_setup_completed": "Browser setup completed successfully",
+ "browser_setup_failed": "Browser setup failed: {error}",
+ "try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
+ "redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
+ "starting_google_authentication": "Starting Google authentication...",
+ "starting_github_authentication": "Starting GitHub authentication...",
+ "waiting_for_authentication": "Waiting for authentication...",
+ "page_changed_checking_auth": "Page changed, checking auth...",
+ "status_check_error": "Status check error: {error}",
+ "authentication_timeout": "Authentication timeout",
+ "account_is_still_valid": "Account is still valid (Usage: {usage})",
+ "starting_re_authentication_process": "Starting re-authentication process...",
+ "starting_new_google_authentication": "Starting new Google authentication...",
+ "failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
+ "navigating_to_authentication_page": "Navigating to authentication page...",
+ "please_select_your_google_account_to_continue": "Please select your Google account to continue...",
+ "found_browser_data_directory": "Found browser data directory: {path}",
+ "authentication_successful_getting_account_info": "Authentication successful, getting account info..."
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/locales/zh_cn.json b/locales/zh_cn.json
index 05710fe..4bf37ed 100644
--- a/locales/zh_cn.json
+++ b/locales/zh_cn.json
@@ -275,7 +275,13 @@
"available_domains_loaded": "获取到 {count} 个可用域名",
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
"trying_to_create_email": "尝试创建邮箱: {email}",
- "domain_blocked": "域名被屏蔽: {domain}"
+ "domain_blocked": "域名被屏蔽: {domain}",
+ "using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
+ "no_display_found": "未找到显示器。确保 X 服务器正在运行。",
+ "try_export_display": "尝试: export DISPLAY=:0",
+ "extension_load_error": "加载插件失败: {error}",
+ "make_sure_chrome_chromium_is_properly_installed": "确保 Chrome/Chromium 已正确安装",
+ "try_install_chromium": "尝试: sudo apt install chromium-browser"
},
"update": {
"title": "禁用 Cursor 自动更新",
@@ -499,7 +505,7 @@
"file_group": "文件组",
"error_getting_file_stats": "获取文件统计信息时出错",
"permission_denied": "权限拒绝",
- "try_running": "尝试运行 'cursor --help' 检查是否已安装",
+ "try_running": "尝试运行: {command}",
"and": "和",
"storage_file_is_empty": "存储文件为空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
@@ -508,7 +514,65 @@
"config_option_added": "添加配置选项",
"config_updated": "配置更新",
"config_created": "配置已创建",
- "config_setup_error": "配置设置错误"
+ "config_setup_error": "配置设置错误",
+ "storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
+ "error_reading_storage_file": "读取存储文件时出错",
+ "also_checked": "也检查了 {path}"
+ },
+ "oauth": {
+ "authentication_button_not_found": "未找到认证按钮",
+ "authentication_failed": "认证失败: {error}",
+ "found_cookies": "找到 {count} 个 Cookie",
+ "token_extraction_error": "Token 提取错误: {error}",
+ "authentication_successful": "认证成功 - 邮箱: {email}",
+ "missing_authentication_data": "缺少认证数据: {data}",
+ "failed_to_delete_account": "删除账户失败: {error}",
+ "invalid_authentication_type": "无效的认证类型",
+ "auth_update_success": "认证更新成功",
+ "browser_closed": "浏览器已关闭",
+ "auth_update_failed": "认证更新失败",
+ "google_start": "Google 开始",
+ "github_start": "Github 开始",
+ "usage_count": "使用次数: {usage}",
+ "account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
+ "starting_new_authentication_process": "开始新的认证过程...",
+ "failed_to_delete_expired_account": "删除过期账户失败",
+ "could_not_check_usage_count": "无法检查使用次数: {error}",
+ "found_email": "找到邮箱: {email}",
+ "could_not_find_email": "未找到邮箱: {error}",
+ "could_not_find_usage_count": "未找到使用次数: {error}",
+ "already_on_settings_page": "已处于设置页面",
+ "failed_to_extract_auth_info": "提取认证信息失败: {error}",
+ "no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
+ "found_default_chrome_profile": "找到默认 Chrome 配置文件",
+ "using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
+ "error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
+ "initializing_browser_setup": "初始化浏览器设置...",
+ "detected_platform": "检测平台: {platform}",
+ "running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
+ "consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
+ "no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
+ "supported_browsers": "支持的浏览器: {platform}",
+ "using_browser_profile": "使用浏览器配置文件: {profile}",
+ "starting_browser": "正在启动浏览器: {path}",
+ "browser_setup_completed": "浏览器设置完成",
+ "browser_setup_failed": "浏览器设置失败: {error}",
+ "try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
+ "redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
+ "starting_google_authentication": "开始 Google 认证...",
+ "starting_github_authentication": "开始 Github 认证...",
+ "waiting_for_authentication": "等待认证...",
+ "page_changed_checking_auth": "页面改变, 检查认证...",
+ "status_check_error": "状态检查错误: {error}",
+ "authentication_timeout": "认证超时",
+ "account_is_still_valid": "账户仍然有效 (使用量: {usage})",
+ "starting_re_authentication_process": "开始重新认证过程...",
+ "starting_new_google_authentication": "开始新的 Google 认证...",
+ "failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
+ "navigating_to_authentication_page": "正在导航到认证页面...",
+ "please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
+ "found_browser_data_directory": "找到浏览器数据目录: {path}",
+ "authentication_successful_getting_account_info": "认证成功, 获取账户信息..."
}
}
\ No newline at end of file
diff --git a/locales/zh_tw.json b/locales/zh_tw.json
index c88ae6e..bca1a83 100644
--- a/locales/zh_tw.json
+++ b/locales/zh_tw.json
@@ -254,7 +254,14 @@
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
"available_domains_loaded": "獲取到 {count} 個可用域名",
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
- "trying_to_create_email": "嘗試創建郵箱: {email}"
+ "trying_to_create_email": "嘗試創建郵箱: {email}",
+ "domain_blocked": "域名被屏蔽: {domain}",
+ "using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
+ "no_display_found": "未找到顯示器。確保 X 伺服器正在運行。",
+ "try_export_display": "嘗試: export DISPLAY=:0",
+ "extension_load_error": "加載插件失敗: {error}",
+ "make_sure_chrome_chromium_is_properly_installed": "確保 Chrome/Chromium 已正確安裝",
+ "try_install_chromium": "嘗試: sudo apt install chromium-browser"
},
"update": {
"title": "禁用 Cursor 自动更新",
@@ -478,7 +485,7 @@
"file_group": "文件組",
"error_getting_file_stats": "獲取文件統計信息時出錯",
"permission_denied": "權限拒絕",
- "try_running": "嘗試運行 'cursor --help' 檢查是否已安裝",
+ "try_running": "嘗試運行: {command}",
"and": "和",
"storage_file_is_empty": "儲存文件為空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
@@ -487,7 +494,64 @@
"config_option_added": "添加配置選項",
"config_updated": "配置更新",
"config_created": "配置已創建",
- "config_setup_error": "配置設置錯誤"
+ "config_setup_error": "配置設置錯誤",
+ "storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
+ "error_reading_storage_file": "讀取儲存文件時出錯",
+ "also_checked": "也檢查了 {path}"
+ },
+ "oauth": {
+ "authentication_button_not_found": "未找到認證按鈕",
+ "authentication_failed": "認證失敗: {error}",
+ "found_cookies": "找到 {count} 個 Cookie",
+ "token_extraction_error": "Token 提取錯誤: {error}",
+ "authentication_successful": "認證成功 - 郵箱: {email}",
+ "missing_authentication_data": "缺少認證數據: {data}",
+ "failed_to_delete_account": "刪除帳戶失敗: {error}",
+ "invalid_authentication_type": "無效的認證類型",
+ "auth_update_success": "認證更新成功",
+ "browser_closed": "瀏覽器已關閉",
+ "auth_update_failed": "認證更新失敗",
+ "google_start": "Google 開始",
+ "github_start": "Github 開始",
+ "usage_count": "使用量: {usage}",
+ "account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
+ "starting_new_authentication_process": "開始新的認證過程...",
+ "failed_to_delete_expired_account": "刪除過期帳戶失敗",
+ "could_not_check_usage_count": "無法檢查使用量: {error}",
+ "found_email": "找到郵箱: {email}",
+ "could_not_find_email": "未找到郵箱: {error}",
+ "could_not_find_usage_count": "未找到使用量: {erro r}",
+ "already_on_settings_page": "已處於設置頁面",
+ "failed_to_extract_auth_info": "提取認證信息失敗: {error}",
+ "no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
+ "found_default_chrome_profile": "找到默認 Chrome 配置文件",
+ "using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
+ "error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
+ "initializing_browser_setup": "初始化瀏覽器設置...",
+ "detected_platform": "檢測平台: {platform}",
+ "running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
+ "consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
+ "no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
+ "supported_browsers": "支持的瀏覽器: {platform}",
+ "using_browser_profile": "使用瀏覽器配置文件: {profile}",
+ "starting_browser": "正在啟動瀏覽器: {path}",
+ "browser_setup_completed": "瀏覽器設置完成成功",
+ "browser_setup_failed": "瀏覽器設置失敗: {error}",
+ "try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
+ "redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
+ "starting_github_authentication": "開始 Github 認證...",
+ "waiting_for_authentication": "等待認證...",
+ "page_changed_checking_auth": "頁面改變, 檢查認證...",
+ "status_check_error": "狀態檢查錯誤: {error}",
+ "authentication_timeout": "認證超時",
+ "account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
+ "starting_re_authentication_process": "開始重新認證過程...",
+ "starting_new_google_authentication": "開始新的 Google 認證...",
+ "failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
+ "navigating_to_authentication_page": "正在導航到認證頁面...",
+ "please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
+ "found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
+ "authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息..."
}
}
\ No newline at end of file
diff --git a/new_tempemail.py b/new_tempemail.py
index dfb96b3..178f24f 100644
--- a/new_tempemail.py
+++ b/new_tempemail.py
@@ -114,8 +114,8 @@ class NewTempEmail:
if sys.platform == "linux":
# Check if DISPLAY is set when not in headless mode
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
- print(f"{Fore.RED}❌ No display found. Make sure X server is running.{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}ℹ️ Try: export DISPLAY=:0{Style.RESET_ALL}")
+ print(f"{Fore.RED}❌ {self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
return False
co.set_argument("--no-sandbox")
@@ -129,7 +129,7 @@ class NewTempEmail:
actual_home = f"/home/{sudo_user}"
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
if os.path.exists(user_data_dir):
- print(f"{Fore.CYAN}ℹ️ Using Chrome profile from: {user_data_dir}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
co.set_argument(f"--user-data-dir={user_data_dir}")
co.auto_port() # 自动设置端口
@@ -154,8 +154,8 @@ class NewTempEmail:
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
if sys.platform == "linux":
- print(f"{Fore.YELLOW}ℹ️ Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}ℹ️ Try: sudo apt install chromium-browser{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def create_email(self):
diff --git a/oauth_auth.py b/oauth_auth.py
index 374bec6..6dc4c71 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -43,12 +43,12 @@ class OAuthHandler:
profiles.append(item)
if not profiles:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} No Chrome profiles found, using Default{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.no_chrome_profiles_found') if self.translator else 'No Chrome profiles found, using Default'}{Style.RESET_ALL}")
return 'Default'
# First check if Default profile exists
if 'Default' in profiles:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found Default Chrome profile{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_default_chrome_profile') if self.translator else 'Found Default Chrome profile'}{Style.RESET_ALL}")
return 'Default'
# If no Default profile, check Local State for last used profile
@@ -70,35 +70,35 @@ class OAuthHandler:
return profile
# If no profile found in Local State, use the first available profile
- print(f"{Fore.CYAN}{EMOJI['INFO']} Using first available Chrome profile: {profiles[0]}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_first_available_chrome_profile', profile=profiles[0]) if self.translator else f'Using first available Chrome profile: {profiles[0]}'}{Style.RESET_ALL}")
return profiles[0]
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Error finding Chrome profile, using Default: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.error_finding_chrome_profile', error=str(e)) if self.translator else f'Error finding Chrome profile, using Default: {str(e)}'}{Style.RESET_ALL}")
return 'Default'
def setup_browser(self):
"""Setup browser for OAuth flow using active profile"""
try:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Initializing browser setup...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
# Platform-specific initialization
platform_name = platform.system().lower()
- print(f"{Fore.CYAN}{EMOJI['INFO']} Detected platform: {platform_name}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
# Linux-specific checks
if platform_name == 'linux':
# Check if DISPLAY is set
display = os.environ.get('DISPLAY')
if not display:
- print(f"{Fore.RED}{EMOJI['ERROR']} No display found. Make sure X server is running.{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Try: export DISPLAY=:0{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
return False
# Check if running as root
if os.geteuid() == 0:
- print(f"{Fore.YELLOW}{EMOJI['WARNING']} Running as root is not recommended for browser automation{Style.RESET_ALL}")
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Consider running the script without sudo{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.running_as_root_warning') if self.translator else 'Running as root is not recommended for browser automation'}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.consider_running_without_sudo') if self.translator else 'Consider running the script without sudo'}{Style.RESET_ALL}")
# Kill existing browser processes
self._kill_browser_processes()
@@ -108,14 +108,14 @@ class OAuthHandler:
chrome_path = self._get_browser_path()
if not chrome_path:
- raise Exception(f"No compatible browser found. Please install Google Chrome or Chromium.\nSupported browsers for {platform_name}:\n" +
+ raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, chromium-browser")
# Get active profile
active_profile = self._get_active_profile(user_data_dir)
- print(f"{Fore.CYAN}{EMOJI['INFO']} Using browser profile: {active_profile}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_browser_profile', profile=active_profile) if self.translator else f'Using browser profile: {active_profile}'}{Style.RESET_ALL}")
# Configure browser options
co = ChromiumOptions()
@@ -124,7 +124,7 @@ class OAuthHandler:
co.headless(False)
# Platform-specific options
- if platform_name == 'linux':
+ if os.name == 'linux':
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--disable-gpu')
@@ -136,7 +136,7 @@ class OAuthHandler:
actual_home = f"/home/{sudo_user}"
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
if os.path.exists(user_data_dir):
- print(f"{Fore.CYAN}{EMOJI['INFO']} Using Chrome profile from: {user_data_dir}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_chrome_profile_from', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
co.set_argument(f"--user-data-dir={user_data_dir}")
# Set paths and profile
@@ -146,26 +146,26 @@ class OAuthHandler:
# Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
- co.auto_port()
+ # co.auto_port()
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting browser at: {chrome_path}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
# Verify browser launched successfully
if not self.browser:
raise Exception("Failed to initialize browser instance")
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Browser setup completed successfully{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Browser setup failed: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running without sudo/administrator privileges{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
elif "Chrome failed to start" in str(e):
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
if platform_name == 'linux':
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Try: sudo apt install chromium-browser{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def _kill_browser_processes(self):
@@ -182,7 +182,7 @@ class OAuthHandler:
time.sleep(1) # Wait for processes to close
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Warning: Could not kill existing browser processes: {e}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the appropriate user data directory based on platform"""
@@ -208,17 +208,17 @@ class OAuthHandler:
# Try each possible path
for path in possible_paths:
if os.path.exists(path):
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found browser data directory: {path}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=path) if self.translator else f'Found browser data directory: {path}'}{Style.RESET_ALL}")
return path
# Create temporary profile if no existing profile found
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Creating temporary profile at: {temp_profile}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
os.makedirs(temp_profile, exist_ok=True)
return temp_profile
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error getting user data directory: {e}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
raise
def _get_browser_path(self):
@@ -229,7 +229,7 @@ class OAuthHandler:
if chrome_path and os.path.exists(chrome_path):
return chrome_path
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Searching for alternative browser installations...{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
# Platform-specific paths
if os.name == 'nt': # Windows
@@ -261,13 +261,13 @@ class OAuthHandler:
for path in alt_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found browser at: {expanded_path}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
return expanded_path
return None
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error finding browser path: {e}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
return None
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
@@ -296,22 +296,22 @@ class OAuthHandler:
return co
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error configuring browser options: {e}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start')}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -335,16 +335,17 @@ class OAuthHandler:
raise Exception("Could not find Google authentication button")
# Click the button and wait for page load
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting Google authentication...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account to continue...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
+ alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
- self.browser.run_js("""
- alert('Please select your Google account to continue with Cursor authentication');
+ self.browser.run_js(f"""
+ alert('{alert_message}');
""")
except:
pass # Alert is optional
@@ -352,14 +353,14 @@ class OAuthHandler:
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
@@ -379,7 +380,7 @@ class OAuthHandler:
start_time = time.time()
check_interval = 2 # Check every 2 seconds
- print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for authentication (timeout: 5 minutes)...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
@@ -398,7 +399,7 @@ class OAuthHandler:
if token:
# Get email from settings page
- print(f"{Fore.CYAN}{EMOJI['INFO']} Authentication successful, getting account info...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
@@ -407,7 +408,7 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {email}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
email = "user@cursor.sh" # Fallback email
@@ -416,42 +417,42 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, creating new account...{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', creating_new_account='creating new account') if self.translator else 'Account has reached maximum usage, creating new account...'}{Style.RESET_ALL}")
# Delete current account
if self._delete_current_account():
# Start new authentication based on auth type
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else: # github
return self.handle_github_auth()
else:
- print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete expired account{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not check usage count: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
# Also check URL as backup
if "cursor.com/settings" in self.browser.url:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Detected successful login{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Waiting for authentication... ({str(e)}){Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Error while waiting for authentication: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
@@ -466,7 +467,7 @@ class OAuthHandler:
# Navigate to auth URL
try:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -490,21 +491,21 @@ class OAuthHandler:
raise Exception("Could not find GitHub authentication button")
# Click the button and wait for page load
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting GitHub authentication...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
@@ -566,23 +567,23 @@ class OAuthHandler:
# Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
- alert_js = """
- alert('Please select your Google account manually to continue with Cursor authentication');
- """
+ alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
- self.browser.run_js(alert_js)
+ self.browser.run_js(f"""
+ alert('{alert_message}');
+ """)
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Alert display failed: {str(e)}{Style.RESET_ALL}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account manually to continue with Cursor authentication...{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} Waiting for authentication to complete...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
# Wait for authentication to complete
max_wait = 300 # 5 minutes
start_time = time.time()
last_url = self.browser.url
- print(f"{Fore.CYAN}{EMOJI['WAIT']} Checking authentication status...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.checking_authentication_status') if self.translator else 'Checking authentication status...'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
@@ -598,9 +599,9 @@ class OAuthHandler:
token = value.split("%3A%3A")[-1]
if token:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful!{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
# Navigate to settings page
- print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to settings page...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3) # Wait for settings page to load
@@ -609,9 +610,9 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
@@ -619,11 +620,11 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
@@ -655,23 +656,23 @@ class OAuthHandler:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
else:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
@@ -679,7 +680,7 @@ class OAuthHandler:
# Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Already on settings page!{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
for cookie in cookies:
@@ -695,9 +696,9 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
@@ -705,11 +706,11 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
@@ -741,44 +742,44 @@ class OAuthHandler:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
- print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
else:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
- print(f"{Fore.CYAN}{EMOJI['INFO']} Page changed, checking auth...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Status check error: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication button not found{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Authentication failed: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
@@ -801,7 +802,7 @@ class OAuthHandler:
time.sleep(1)
# Debug cookie information
- print(f"{Fore.CYAN}{EMOJI['INFO']} Found {len(cookies)} cookies{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
token = None
@@ -816,12 +817,12 @@ class OAuthHandler:
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} Token extraction error: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
if email and token:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful - Email: {email}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
missing = []
@@ -829,11 +830,11 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
- print(f"{Fore.RED}{EMOJI['ERROR']} Missing authentication data: {', '.join(missing)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
return False, None
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Failed to extract auth info: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
return False, None
def _delete_current_account(self):
@@ -868,14 +869,14 @@ class OAuthHandler:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page
- print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
return True
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account: {str(e)}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
return False
def main(auth_type, translator=None):
@@ -888,13 +889,13 @@ def main(auth_type, translator=None):
handler = OAuthHandler(translator, auth_type)
if auth_type.lower() == 'google':
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start')}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_google_auth()
elif auth_type.lower() == 'github':
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start')}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_github_auth()
else:
- print(f"{Fore.RED}{EMOJI['ERROR']} Invalid authentication type{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
return False
if success and auth_info:
@@ -905,13 +906,13 @@ def main(auth_type, translator=None):
access_token=auth_info["token"],
refresh_token=auth_info["token"]
):
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success')}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
# Close the browser after successful authentication
if handler.browser:
handler.browser.quit()
- print(f"{Fore.CYAN}{EMOJI['INFO']} Browser closed{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
return True
else:
- print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed')}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
return False
\ No newline at end of file
From a56b97866942034df3a6ad0d5468fdf972bcdbc4 Mon Sep 17 00:00:00 2001
From: yeongpin
Date: Mon, 31 Mar 2025 12:33:25 +0800
Subject: [PATCH 41/47] feat: add new error messages for browser process
handling in English, Simplified Chinese, and Traditional Chinese locales
---
locales/en.json | 5 ++++-
locales/zh_cn.json | 5 ++++-
locales/zh_tw.json | 5 ++++-
3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/locales/en.json b/locales/en.json
index 05a4877..f5e84a8 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -577,6 +577,9 @@
"navigating_to_authentication_page": "Navigating to authentication page...",
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
"found_browser_data_directory": "Found browser data directory: {path}",
- "authentication_successful_getting_account_info": "Authentication successful, getting account info..."
+ "authentication_successful_getting_account_info": "Authentication successful, getting account info...",
+ "warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
+ "browser_failed_to_start": "Browser failed to start: {error}",
+ "browser_failed": "Browser failed to start: {error}"
}
}
\ No newline at end of file
diff --git a/locales/zh_cn.json b/locales/zh_cn.json
index 4bf37ed..1a4d304 100644
--- a/locales/zh_cn.json
+++ b/locales/zh_cn.json
@@ -572,7 +572,10 @@
"navigating_to_authentication_page": "正在导航到认证页面...",
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
"found_browser_data_directory": "找到浏览器数据目录: {path}",
- "authentication_successful_getting_account_info": "认证成功, 获取账户信息..."
+ "authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
+ "warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
+ "browser_failed_to_start": "浏览器启动失败: {error}",
+ "browser_failed": "浏览器启动失败: {error}"
}
}
\ No newline at end of file
diff --git a/locales/zh_tw.json b/locales/zh_tw.json
index bca1a83..ab97eed 100644
--- a/locales/zh_tw.json
+++ b/locales/zh_tw.json
@@ -552,6 +552,9 @@
"navigating_to_authentication_page": "正在導航到認證頁面...",
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
- "authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息..."
+ "authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
+ "warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
+ "browser_failed_to_start": "瀏覽器啟動失敗: {error}",
+ "browser_failed": "瀏覽器啟動失敗: {error}"
}
}
\ No newline at end of file
From e2d7c1c496c5834eb230882dea5afa7c3ddefee7 Mon Sep 17 00:00:00 2001
From: yeongpin
Date: Mon, 31 Mar 2025 12:35:35 +0800
Subject: [PATCH 42/47] chore: update version to 1.8.04 and enhance CHANGELOG
with new features and fixes
---
.env | 4 ++--
.github/workflows/build.yml | 2 +-
CHANGELOG.md | 6 ++++++
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/.env b/.env
index 5b2edca..e485b0b 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
-version=1.8.03
-VERSION=1.8.03
+version=1.8.04
+VERSION=1.8.04
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3b01720..b27e0d4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
- default: '1.8.02'
+ default: '1.8.04'
permissions:
contents: write
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0615c1d..62d76b8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## v1.8.04
+1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
+2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
+3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
+4. Fix: Some Issues | 修復一些問題
+
## v1.8.03
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
2. Fix: Some Issues | 修復一些問題
From 6e6180e3316c6bd2939baf13bbe4bbdb745b67d7 Mon Sep 17 00:00:00 2001
From: yeongpin
Date: Mon, 31 Mar 2025 17:49:35 +0800
Subject: [PATCH 43/47] fix: update reset emoji and correct button patterns for
cross-platform compatibility
---
reset_machine_manual.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/reset_machine_manual.py b/reset_machine_manual.py
index 3be1e4d..f9359be 100644
--- a/reset_machine_manual.py
+++ b/reset_machine_manual.py
@@ -25,7 +25,7 @@ EMOJI = {
"SUCCESS": "✅",
"ERROR": "❌",
"INFO": "ℹ️",
- "RESET": "��",
+ "RESET": "🔄",
"WARNING": "⚠️",
}
@@ -299,11 +299,11 @@ def modify_workbench_js(file_path: str, translator=None) -> bool:
if sys.platform == "win32":
# Define replacement patterns
- CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
- CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
+ CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
+ CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "linux":
- CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
- CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
+ CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
+ CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "darwin":
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
From 51cef9c2b25236622da1df625e1dc174f5a3361f Mon Sep 17 00:00:00 2001
From: Lucas
Date: Tue, 1 Apr 2025 15:42:05 -0300
Subject: [PATCH 44/47] fix: improve account usage limit detection
- Add support for detecting both 150/150 and 50/50 usage limits
- Improve usage parsing and validation
- Add better error handling for usage detection
- Add more descriptive log messages
---
oauth_auth.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index 6dc4c71..5da5b16 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -419,8 +419,8 @@ class OAuthHandler:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
- # Check if account is expired
- if usage_text.strip() == "150 / 150":
+ # Check if account is expired (both 150/150 and 50/50 cases)
+ if usage_text.strip() == "150 / 150" or usage_text.strip() == "50 / 50":
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', creating_new_account='creating new account') if self.translator else 'Account has reached maximum usage, creating new account...'}{Style.RESET_ALL}")
# Delete current account
@@ -622,8 +622,8 @@ class OAuthHandler:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
- # Check if account is expired
- if usage_text.strip() == "150 / 150":
+ # Check if account is expired (both 150/150 and 50/50 cases)
+ if usage_text.strip() == "150 / 150" or usage_text.strip() == "50 / 50":
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
delete_js = """
@@ -708,8 +708,8 @@ class OAuthHandler:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
- # Check if account is expired
- if usage_text.strip() == "150 / 150":
+ # Check if account is expired (both 150/150 and 50/50 cases)
+ if usage_text.strip() == "150 / 150" or usage_text.strip() == "50 / 50":
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
delete_js = """
From d91485ec7522ec179fd92d560847d9ccf83182f6 Mon Sep 17 00:00:00 2001
From: Valentin Guerdin <70648608+blobs0@users.noreply.github.com>
Date: Wed, 2 Apr 2025 14:32:27 +0200
Subject: [PATCH 45/47] Patch Linux Path Not Found
add folder /usr/lib/cursor/app/
---
reset_machine_manual.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/reset_machine_manual.py b/reset_machine_manual.py
index f9359be..a10f862 100644
--- a/reset_machine_manual.py
+++ b/reset_machine_manual.py
@@ -46,7 +46,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
default_paths = {
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
- "Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app")]
+ "Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app"), "/usr/lib/cursor/app/"]
}
# If config doesn't exist, create it with default paths
@@ -170,7 +170,7 @@ def get_workbench_cursor_path(translator=None) -> str:
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Linux": {
- "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
+ "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
"main": "out/vs/workbench/workbench.desktop.main.js"
}
}
@@ -786,4 +786,4 @@ def run(translator=None):
if __name__ == "__main__":
from main import translator as main_translator
- run(main_translator)
\ No newline at end of file
+ run(main_translator)
From fdc831738076bed4e0fac395f889984c559dc0e9 Mon Sep 17 00:00:00 2001
From: Pin Studios
Date: Thu, 3 Apr 2025 02:07:49 +0800
Subject: [PATCH 46/47] Update .env
---
.env | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.env b/.env
index e485b0b..1199388 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
-version=1.8.04
-VERSION=1.8.04
+version=1.8.05
+VERSION=1.8.05
From ea44218a8a8e4cc0fc8652d79ab72c10e6c1fb02 Mon Sep 17 00:00:00 2001
From: Pin Studios
Date: Thu, 3 Apr 2025 02:09:29 +0800
Subject: [PATCH 47/47] Update CHANGELOG.md
---
CHANGELOG.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 62d76b8..3f9bfcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Change Log
+## v1.8.05
+1. Fix: Linux Path Not Found | 修復linuxpath問題
+2. Add: support for detecting both 150/150 and 50/50 usage limits | 添加偵測50 或者150的使用量
+3. Improve: usage parsing and validation | 檢測使用量
+
## v1.8.04
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理