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.
366 lines
17 KiB
Python
366 lines
17 KiB
Python
import os
|
||
import requests
|
||
import time
|
||
import hashlib
|
||
import base64
|
||
import struct
|
||
import logging
|
||
from colorama import Fore, Style, init
|
||
from typing import Optional, Dict, Union, Any, Tuple
|
||
|
||
# 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 = {
|
||
"SUCCESS": "✅",
|
||
"ERROR": "❌",
|
||
"INFO": "ℹ️",
|
||
"WARNING": "⚠️",
|
||
"KEY": "🔑",
|
||
"CHECK": "🔍"
|
||
}
|
||
|
||
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
|
||
"""Generate a SHA-256 hash of input + salt and return as hex.
|
||
|
||
Args:
|
||
input_str: The input string to hash
|
||
salt: Optional salt to add to the input string
|
||
|
||
Returns:
|
||
str: Hexadecimal representation of the hash
|
||
"""
|
||
if not input_str:
|
||
logger.warning("Empty input string provided for hashing")
|
||
return ""
|
||
|
||
hash_obj = hashlib.sha256()
|
||
hash_obj.update((input_str + salt).encode('utf-8'))
|
||
return hash_obj.hexdigest()
|
||
|
||
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
|
||
"""Obfuscate bytes using the algorithm from utils.js.
|
||
|
||
Args:
|
||
byte_array: The byte array to obfuscate
|
||
|
||
Returns:
|
||
bytearray: The obfuscated byte array
|
||
"""
|
||
if not byte_array:
|
||
return bytearray()
|
||
|
||
t = 165
|
||
for r in range(len(byte_array)):
|
||
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
|
||
t = byte_array[r]
|
||
return byte_array
|
||
|
||
def generate_cursor_checksum(token: str, translator: Any = None) -> str:
|
||
"""Generate Cursor checksum from token using the algorithm.
|
||
|
||
Args:
|
||
token: The authentication token
|
||
translator: Optional translator for internationalization
|
||
|
||
Returns:
|
||
str: The generated checksum
|
||
"""
|
||
try:
|
||
# Validate input
|
||
if not token or not isinstance(token, str):
|
||
logger.error("Invalid token provided")
|
||
return ""
|
||
|
||
# Clean the token
|
||
clean_token = token.strip()
|
||
|
||
# Generate machineId and macMachineId
|
||
machine_id = generate_hashed64_hex(clean_token, 'machineId')
|
||
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
|
||
|
||
# Get timestamp and convert to byte array
|
||
timestamp = int(time.time() * 1000) // 1000000
|
||
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
|
||
|
||
# Obfuscate bytes and encode as base64
|
||
obfuscated_bytes = obfuscate_bytes(byte_array)
|
||
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
|
||
|
||
# Combine final checksum
|
||
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
|
||
except Exception as e:
|
||
logger.error(f"Error generating checksum: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
|
||
return ""
|
||
|
||
def check_user_authorized(token: str, translator: Any = None) -> bool:
|
||
"""
|
||
Check if the user is authorized with the given token.
|
||
|
||
Args:
|
||
token: The authorization token
|
||
translator: Optional translator for internationalization
|
||
|
||
Returns:
|
||
bool: True if authorized, False otherwise
|
||
"""
|
||
try:
|
||
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
|
||
|
||
# Validate input
|
||
if not token or not isinstance(token, str):
|
||
logger.error("Invalid token provided")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
# Clean the token
|
||
if token and '%3A%3A' in token:
|
||
token = token.split('%3A%3A')[1]
|
||
elif token and '::' in token:
|
||
token = token.split('::')[1]
|
||
|
||
# Remove any whitespace
|
||
token = token.strip()
|
||
|
||
if not token or len(token) < 10: # Add a basic validation for token length
|
||
logger.error("Token too short or empty after cleaning")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
|
||
|
||
# Try to get usage info using the DashboardService API
|
||
try:
|
||
# Generate checksum
|
||
checksum = generate_cursor_checksum(token, translator)
|
||
if not checksum:
|
||
logger.error("Failed to generate checksum")
|
||
return False
|
||
|
||
# Create request headers
|
||
headers = {
|
||
'accept-encoding': 'gzip',
|
||
'authorization': f'Bearer {token}',
|
||
'connect-protocol-version': '1',
|
||
'content-type': 'application/proto',
|
||
'user-agent': 'connect-es/1.6.1',
|
||
'x-cursor-checksum': checksum,
|
||
'x-cursor-client-version': '0.48.7',
|
||
'x-cursor-timezone': 'Asia/Shanghai',
|
||
'x-ghost-mode': 'false',
|
||
'Host': 'api2.cursor.sh'
|
||
}
|
||
|
||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
|
||
|
||
# Make the request with timeout and retry
|
||
max_retries = 3
|
||
for attempt in range(max_retries):
|
||
try:
|
||
usage_response = requests.post(
|
||
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
|
||
headers=headers,
|
||
data=b'', # Empty body
|
||
timeout=10
|
||
)
|
||
break
|
||
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
||
if attempt < max_retries - 1:
|
||
logger.warning(f"Request attempt {attempt + 1} failed: {e}. Retrying...")
|
||
time.sleep(2)
|
||
else:
|
||
raise
|
||
|
||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||
|
||
if usage_response.status_code == 200:
|
||
logger.info("User is authorized")
|
||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
|
||
return True
|
||
elif usage_response.status_code == 401 or usage_response.status_code == 403:
|
||
logger.warning("User is unauthorized")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
|
||
return False
|
||
else:
|
||
logger.warning(f"Unexpected status code: {usage_response.status_code}")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||
|
||
# If the token at least looks like a valid JWT, consider it valid
|
||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||
logger.info("Token appears to be in JWT format, but API check returned an unexpected status code")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
return False
|
||
except requests.exceptions.Timeout:
|
||
logger.error("Request timed out")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
|
||
|
||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||
logger.info("Token appears to be in JWT format, but request timed out")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check timed out. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
return False
|
||
except requests.exceptions.ConnectionError:
|
||
logger.error("Connection error")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
|
||
|
||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||
logger.info("Token appears to be in JWT format, but connection failed")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API connection failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
return False
|
||
except Exception as e:
|
||
logger.error(f"Error checking usage: {e}")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
|
||
|
||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||
logger.info("Token appears to be in JWT format, but API check failed")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||
return True
|
||
|
||
return False
|
||
|
||
except requests.exceptions.Timeout:
|
||
logger.error("Request timed out")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
|
||
return False
|
||
except requests.exceptions.ConnectionError:
|
||
logger.error("Connection error")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
|
||
return False
|
||
except Exception as e:
|
||
logger.error(f"Error checking authorization: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
def get_token_from_database(translator: Any = None) -> str:
|
||
"""
|
||
Get token from database using cursor_acc_info.py.
|
||
|
||
Args:
|
||
translator: Optional translator for internationalization
|
||
|
||
Returns:
|
||
str: The token if found, empty string otherwise
|
||
"""
|
||
try:
|
||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
|
||
|
||
# Import functions from cursor_acc_info.py
|
||
from cursor_acc_info import get_token
|
||
|
||
# Get token using the get_token function
|
||
token = get_token()
|
||
|
||
if token:
|
||
logger.info("Token found in database")
|
||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
|
||
return token
|
||
else:
|
||
logger.warning("Token not found in database")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
|
||
return ""
|
||
except ImportError:
|
||
logger.error("cursor_acc_info.py not found")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
|
||
return ""
|
||
except Exception as e:
|
||
logger.error(f"Error getting token from database: {e}")
|
||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
|
||
return ""
|
||
|
||
def run(translator: Any = None) -> bool:
|
||
"""Run function to be called from main.py.
|
||
|
||
Args:
|
||
translator: Optional translator for internationalization
|
||
|
||
Returns:
|
||
bool: True if authorization successful, False otherwise
|
||
"""
|
||
try:
|
||
# Ask user if they want to get token from database or input manually
|
||
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
|
||
|
||
token = None
|
||
|
||
# If user chooses database or default
|
||
if not choice or choice == 'd':
|
||
token = get_token_from_database(translator)
|
||
|
||
# If token not found in database or user chooses manual input
|
||
if not token:
|
||
# Try to get token from environment
|
||
token = os.environ.get('CURSOR_TOKEN')
|
||
|
||
# If not in environment, ask user to input
|
||
if not token:
|
||
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
|
||
|
||
# Check authorization
|
||
is_authorized = check_user_authorized(token, translator)
|
||
|
||
if is_authorized:
|
||
logger.info("Authorization successful")
|
||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
|
||
else:
|
||
logger.warning("Authorization failed")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
|
||
|
||
return is_authorized
|
||
except Exception as e:
|
||
logger.error(f"Error in run function: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
def main(translator: Any = None) -> bool:
|
||
"""Main function to be called when script is run directly.
|
||
|
||
Args:
|
||
translator: Optional translator for internationalization
|
||
|
||
Returns:
|
||
bool: True if authorization successful, False otherwise
|
||
"""
|
||
try:
|
||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.title') if translator else 'Check User Authorization'}{Style.RESET_ALL}")
|
||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||
|
||
result = run(translator)
|
||
|
||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||
input(f"{EMOJI['INFO']} {translator.get('auth_check.press_enter') if translator else 'Press Enter to continue...'}")
|
||
|
||
return result
|
||
except Exception as e:
|
||
logger.error(f"Error in main function: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")
|
||
return False
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
from main import translator as main_translator
|
||
main(main_translator)
|
||
except ImportError:
|
||
logger.warning("Failed to import translator from main.py, running without translation")
|
||
main(None)
|
||
except Exception as e:
|
||
logger.error(f"Error running check_user_authorized.py: {e}")
|
||
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}") |