mirror of
https://github.com/psipher/cursor-free-vip-main.git
synced 2026-01-20 07:10:21 +00:00
## 🚀 Enhanced Features Implementation This PR introduces significant improvements to the Cursor Free VIP project with enhanced configuration management, error handling, and utility systems. ### ✨ New Features #### 🔧 Enhanced Configuration Management (`enhanced_config.py`) - **Multi-format support**: INI, JSON, YAML configuration formats - **Automatic validation**: Built-in configuration validation with detailed error reporting - **Backup system**: Automatic configuration backup and restore functionality - **Platform-specific paths**: Automatic detection and management of paths for Windows, macOS, and Linux - **Type safety**: Improved type checking and error handling #### 🛡️ Enhanced Error Handling (`enhanced_error_handler.py`) - **Automatic categorization**: Intelligent error classification (Network, File System, Process, etc.) - **Severity-based logging**: Critical, High, Medium, Low severity levels - **Recovery strategies**: Automatic retry logic with exponential backoff - **Error history**: Comprehensive error tracking and resolution management - **Custom callbacks**: Registerable error handlers for specific categories #### 🛠️ Enhanced Utility System (`enhanced_utils.py`) - **Advanced path management**: Cross-platform path detection and validation - **Multi-browser support**: Automatic detection of Chrome, Firefox, Edge, Brave, Opera - **Process monitoring**: Real-time process tracking and management - **System information**: Detailed system resource monitoring - **Network connectivity**: Automated network testing and validation ### 🔧 Technical Improvements - **Type safety**: Fixed all linter errors and type annotation issues - **Code robustness**: Improved error handling and exception management - **Maintainability**: Better code organization and documentation - **Cross-platform compatibility**: Enhanced support for Windows, macOS, and Linux ### �� Files Changed - `enhanced_config.py` - New enhanced configuration management system - `enhanced_utils.py` - New enhanced utility and system management - `enhanced_error_handler.py` - New enhanced error handling system - `CHANGELOG.md` - Updated with v1.11.04 release notes ### �� Testing - All new systems have been tested on Windows, macOS, and Linux - Type checking passes with no linter errors - Backward compatibility maintained with existing functionality ### 📝 Documentation - Comprehensive inline documentation for all new features - Updated CHANGELOG with detailed feature descriptions - Follows existing code style and conventions --- **Note**: These enhancements provide a solid foundation for future development while maintaining full backward compatibility with existing functionality.
239 lines
10 KiB
Python
239 lines
10 KiB
Python
import psutil
|
||
import time
|
||
import logging
|
||
import platform
|
||
import sys
|
||
import os
|
||
from colorama import Fore, Style, init
|
||
from typing import Optional, Dict, List, Any, Tuple, Union, Set
|
||
|
||
# Initialize colorama
|
||
init(autoreset=True)
|
||
|
||
# Configure logging
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||
datefmt="%Y-%m-%d %H:%M:%S"
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Define emoji constants
|
||
EMOJI = {
|
||
"PROCESS": "⚙️",
|
||
"SUCCESS": "✅",
|
||
"ERROR": "❌",
|
||
"INFO": "ℹ️",
|
||
"WAIT": "⏳",
|
||
"KILL": "🛑",
|
||
"SEARCH": "🔍"
|
||
}
|
||
|
||
class CursorQuitter:
|
||
"""Class to handle termination of Cursor processes."""
|
||
|
||
def __init__(self, timeout: int = 5, translator: Any = None):
|
||
"""Initialize CursorQuitter.
|
||
|
||
Args:
|
||
timeout: Maximum time to wait for processes to terminate naturally
|
||
translator: Optional translator for internationalization
|
||
"""
|
||
self.timeout = max(1, timeout) # Ensure timeout is at least 1 second
|
||
self.translator = translator
|
||
|
||
def _get_message(self, key: str, fallback: str, **kwargs) -> str:
|
||
"""Get translated message or fallback.
|
||
|
||
Args:
|
||
key: Translation key
|
||
fallback: Fallback message if translation not available
|
||
**kwargs: Format parameters for the message
|
||
|
||
Returns:
|
||
str: Translated or fallback message
|
||
"""
|
||
if self.translator:
|
||
return self.translator.get(key, **kwargs)
|
||
return fallback.format(**kwargs) if kwargs else fallback
|
||
|
||
def _find_cursor_processes(self) -> List[psutil.Process]:
|
||
"""Find all Cursor processes.
|
||
|
||
Returns:
|
||
List[psutil.Process]: List of Cursor processes
|
||
"""
|
||
cursor_processes = []
|
||
cursor_names = {
|
||
'windows': ['cursor.exe', 'cursor helper.exe', 'cursor crash handler.exe'],
|
||
'darwin': ['Cursor', 'Cursor Helper', 'Cursor Crash Handler'],
|
||
'linux': ['cursor', 'cursor-helper', 'cursor-crash-handler']
|
||
}
|
||
|
||
# Get platform-specific process names
|
||
system = platform.system().lower()
|
||
if system in cursor_names:
|
||
target_names = cursor_names[system]
|
||
else:
|
||
# Fallback to all possible names
|
||
target_names = [name for names in cursor_names.values() for name in names]
|
||
|
||
logger.info(f"Looking for Cursor processes with names: {target_names}")
|
||
print(f"{Fore.CYAN}{EMOJI['SEARCH']} {self._get_message('quit_cursor.searching', 'Searching for Cursor processes...')}{Style.RESET_ALL}")
|
||
|
||
# Collect all Cursor processes
|
||
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||
try:
|
||
proc_name = proc.info['name'].lower() if proc.info['name'] else ""
|
||
|
||
# Check process name
|
||
if any(target.lower() in proc_name for target in target_names):
|
||
cursor_processes.append(proc)
|
||
continue
|
||
|
||
# Check command line for additional detection
|
||
if proc.info['cmdline']:
|
||
cmdline = " ".join(proc.info['cmdline']).lower()
|
||
if 'cursor' in cmdline and ('electron' in cmdline or 'app' in cmdline):
|
||
cursor_processes.append(proc)
|
||
|
||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception) as e:
|
||
logger.warning(f"Error accessing process: {e}")
|
||
continue
|
||
|
||
return cursor_processes
|
||
|
||
def quit_cursor(self) -> bool:
|
||
"""Gently close Cursor processes.
|
||
|
||
Returns:
|
||
bool: True if all processes were terminated successfully, False otherwise
|
||
"""
|
||
try:
|
||
msg = self._get_message('quit_cursor.start', 'Attempting to close Cursor processes')
|
||
logger.info(msg)
|
||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {msg}...{Style.RESET_ALL}")
|
||
|
||
# Find Cursor processes
|
||
cursor_processes = self._find_cursor_processes()
|
||
|
||
if not cursor_processes:
|
||
msg = self._get_message('quit_cursor.no_process', 'No Cursor processes found')
|
||
logger.info(msg)
|
||
print(f"{Fore.GREEN}{EMOJI['INFO']} {msg}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
# Log found processes
|
||
logger.info(f"Found {len(cursor_processes)} Cursor processes")
|
||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self._get_message('quit_cursor.processes_found', 'Found {count} Cursor processes', count=len(cursor_processes))}{Style.RESET_ALL}")
|
||
|
||
# Gently request processes to terminate
|
||
for proc in cursor_processes:
|
||
try:
|
||
if proc.is_running():
|
||
msg = self._get_message('quit_cursor.terminating', 'Terminating process {pid}', pid=proc.pid)
|
||
logger.info(f"Terminating process {proc.pid}")
|
||
print(f"{Fore.YELLOW}{EMOJI['PROCESS']} {msg}...{Style.RESET_ALL}")
|
||
proc.terminate()
|
||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception) as e:
|
||
logger.warning(f"Error terminating process {proc.pid}: {e}")
|
||
continue
|
||
|
||
# Wait for processes to terminate naturally
|
||
msg = self._get_message('quit_cursor.waiting', f'Waiting up to {self.timeout} seconds for processes to close')
|
||
logger.info(f"Waiting up to {self.timeout} seconds for processes to close")
|
||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {msg}...{Style.RESET_ALL}")
|
||
|
||
start_time = time.time()
|
||
while time.time() - start_time < self.timeout:
|
||
still_running = []
|
||
for proc in cursor_processes:
|
||
try:
|
||
if proc.is_running():
|
||
still_running.append(proc)
|
||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception):
|
||
continue
|
||
|
||
if not still_running:
|
||
msg = self._get_message('quit_cursor.success', 'All Cursor processes have been closed successfully')
|
||
logger.info("All Cursor processes have been closed successfully")
|
||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {msg}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
time.sleep(0.5)
|
||
|
||
# If processes are still running after timeout, try to kill them
|
||
if still_running:
|
||
process_list = ", ".join([str(p.pid) for p in still_running])
|
||
msg = self._get_message('quit_cursor.timeout', 'Timeout reached. Some processes are still running: {pids}', pids=process_list)
|
||
logger.warning(f"Timeout reached. Still running: {process_list}")
|
||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {msg}{Style.RESET_ALL}")
|
||
|
||
# Try to kill remaining processes
|
||
print(f"{Fore.RED}{EMOJI['KILL']} {self._get_message('quit_cursor.force_kill', 'Attempting to force kill remaining processes')}{Style.RESET_ALL}")
|
||
for proc in still_running:
|
||
try:
|
||
if proc.is_running():
|
||
logger.info(f"Force killing process {proc.pid}")
|
||
proc.kill()
|
||
except (psutil.NoSuchProcess, psutil.AccessDenied, Exception) as e:
|
||
logger.error(f"Error killing process {proc.pid}: {e}")
|
||
continue
|
||
|
||
# Check if all processes are now killed
|
||
time.sleep(1)
|
||
final_check = [p for p in still_running if p.is_running()]
|
||
if not final_check:
|
||
msg = self._get_message('quit_cursor.force_success', 'All Cursor processes have been forcefully terminated')
|
||
logger.info("All Cursor processes have been forcefully terminated")
|
||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {msg}{Style.RESET_ALL}")
|
||
return True
|
||
else:
|
||
failed_list = ", ".join([str(p.pid) for p in final_check])
|
||
msg = self._get_message('quit_cursor.force_failed', 'Failed to terminate some processes: {pids}', pids=failed_list)
|
||
logger.error(f"Failed to terminate processes: {failed_list}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {msg}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error in quit_cursor: {e}")
|
||
msg = self._get_message('quit_cursor.error', 'An error occurred: {error}', error=str(e))
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {msg}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
def quit_cursor(translator: Any = None, timeout: int = 5) -> bool:
|
||
"""Convenient function for directly calling the quit function.
|
||
|
||
Args:
|
||
translator: Optional translator for internationalization
|
||
timeout: Maximum time to wait for processes to terminate naturally
|
||
|
||
Returns:
|
||
bool: True if all processes were terminated successfully, False otherwise
|
||
"""
|
||
try:
|
||
quitter = CursorQuitter(timeout, translator)
|
||
return quitter.quit_cursor()
|
||
except Exception as e:
|
||
logger.error(f"Error in quit_cursor function: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
# If run directly, try to use the default translator
|
||
try:
|
||
from main import translator as main_translator
|
||
result = quit_cursor(main_translator)
|
||
except ImportError:
|
||
logger.warning("Failed to import translator from main.py, running without translation")
|
||
result = quit_cursor()
|
||
|
||
# Exit with appropriate status code
|
||
sys.exit(0 if result else 1)
|
||
except Exception as e:
|
||
logger.critical(f"Critical error: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} Critical error: {str(e)}{Style.RESET_ALL}")
|
||
sys.exit(1) |