Compare commits

..

4 Commits

Author SHA1 Message Date
yeongpin
9f51ba8128 Update CHANGELOG.md for version 1.8.10, detailing fixes for Linux reset process errors and enhancements to cursor path retrieval for improved maintainability across operating systems. 2025-04-10 00:29:37 +08:00
yeongpin
9aa09c436e Enhance cursor path retrieval in reset_machine_manual.py for Linux systems
- Updated the get_workbench_cursor_path function to handle Linux systems more effectively.
- Added logic to use the first base path if no valid paths are found in the existing loop.
- Improved maintainability and clarity of the code by explicitly handling different operating systems.
2025-04-10 00:28:20 +08:00
yeongpin
1e3e9c99eb Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-09 20:49:43 +08:00
yeongpin
3f9cbc3d08 Update version to 1.8.10, add user authorization check feature, and enhance localization support
- Bumped version in .env file to 1.8.10.
- Introduced a new script for checking user authorization with detailed feedback and error handling.
- Updated CHANGELOG.md to include new entries for the user authorization feature and minor fixes.
- Added localization strings for user authorization checks in English, Simplified Chinese, and Traditional Chinese.
2025-04-09 20:49:39 +08:00
9 changed files with 327 additions and 9 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.8.09
VERSION=1.8.09
version=1.8.10
VERSION=1.8.10

View File

@@ -1,5 +1,13 @@
# Change Log
## v1.8.10
1. Add: Check User Authorized | 添加檢查用戶授權
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統
4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑
5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性
6. Fix: Some Issues | 修復一些問題
## v1.8.09
1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查
2. AddBypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5)

214
check_user_authorized.py Normal file
View File

@@ -0,0 +1,214 @@
import os
import requests
import time
import hashlib
import base64
import struct
from colorama import Fore, Style, init
# Initialize colorama
init()
# 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"""
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"""
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=None) -> str:
"""Generate Cursor checksum from token using the algorithm"""
try:
# 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:
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=None) -> bool:
"""
Check if the user is authorized with the given token
Args:
token (str): 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}")
# 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
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)
# 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 - this endpoint doesn't need a request body
usage_response = requests.post(
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
headers=headers,
data=b'', # Empty body
timeout=10
)
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:
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:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
return False
else:
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:
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 Exception as 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:
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:
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:
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:
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 run(translator=None):
"""Run function to be called from main.py"""
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':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
try:
# 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:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
else:
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}")
except ImportError:
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}")
except Exception as 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}")
# 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:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
return is_authorized
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
return False
def main(translator=None):
"""Main function to check user authorization"""
return run(translator)
if __name__ == "__main__":
main()

View File

@@ -54,7 +54,7 @@ def setup_config(translator=None):
},
'Utils': {
'enabled_update_check': 'True',
'enabled_force_update': 'True',
'enabled_force_update': 'False',
'enabled_account_info': 'True'
}
}

View File

@@ -30,7 +30,8 @@
"continue_prompt": "Continue? (y/N): ",
"operation_cancelled_by_user": "Operation cancelled by user",
"exiting": "Exiting ……",
"bypass_version_check": "Bypass Cursor Version Check"
"bypass_version_check": "Bypass Cursor Version Check",
"check_user_authorized": "Check User Authorized"
},
"languages": {
"en": "English",
@@ -698,5 +699,33 @@
"title": "Cursor Version Bypass Tool",
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
"menu_option": "Bypass Cursor Version Check"
},
"auth_check": {
"checking_authorization": "Checking authorization...",
"token_source": "Get token from database or input manually? (d/m, default: d)",
"getting_token_from_db": "Getting token from database...",
"token_found_in_db": "Token found in database",
"token_not_found_in_db": "Token not found in database",
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
"error_getting_token_from_db": "Error getting token from database: {error}",
"enter_token": "Enter your Cursor token: ",
"token_length": "Token length: {length} characters",
"usage_response_status": "Usage response status: {response}",
"unexpected_status_code": "Unexpected status code: {code}",
"jwt_token_warning": "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.",
"invalid_token": "Invalid token",
"user_authorized": "User is authorized",
"user_unauthorized": "User is unauthorized",
"request_timeout": "Request timed out",
"connection_error": "Connection error",
"check_error": "Error checking authorization: {error}",
"authorization_successful": "Authorization successful!",
"authorization_failed": "Authorization failed!",
"operation_cancelled": "Operation cancelled by user",
"unexpected_error": "Unexpected error: {error}",
"error_generating_checksum": "Error generating checksum: {error}",
"checking_usage_information": "Checking usage information...",
"check_usage_response": "Check usage response: {response}",
"usage_response": "Usage response: {response}"
}
}

View File

@@ -30,7 +30,8 @@
"continue_prompt": "继续?(y/N): ",
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查"
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权"
},
"languages": {
"en": "英语",
@@ -676,5 +677,33 @@
"title": "Cursor 版本绕过工具",
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
"menu_option": "绕过 Cursor 版本检查"
},
"auth_check": {
"checking_authorization": "检查授权...",
"token_source": "从数据库获取 token 或手动输入d/m, 默认: d",
"getting_token_from_db": "从数据库获取 token...",
"token_found_in_db": "在数据库中找到 token",
"token_not_found_in_db": "在数据库中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
"enter_token": "请输入您的 Cursor token: ",
"token_length": "token 长度: {length}",
"usage_response_status": "使用情况响应状态: {response}",
"unexpected_status_code": "意外状态码: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
"invalid_token": "无效的 token",
"user_authorized": "用户已授权",
"user_unauthorized": "用户未授权",
"request_timeout": "请求超时",
"connection_error": "连接错误",
"check_error": "检查授权时出错: {error}",
"authorization_successful": "授权成功",
"authorization_failed": "授权失败",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外错误: {error}",
"error_generating_checksum": "生成校验和时出错: {error}",
"checking_usage_information": "检查使用情况...",
"check_usage_response": "检查使用情况响应: {response}",
"usage_response": "使用情况响应: {response}"
}
}

View File

@@ -30,7 +30,8 @@
"continue_prompt": "繼續?(y/N): ",
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查"
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權"
},
"languages": {
"en": "英文",
@@ -658,5 +659,33 @@
"title": "Cursor 版本繞過工具",
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
"menu_option": "繞過 Cursor 版本檢查"
},
"auth_check": {
"checking_authorization": "檢查授權...",
"token_source": "從資料庫獲取 token 或手動輸入d/m, 預設: d",
"getting_token_from_db": "從資料庫獲取 token...",
"token_found_in_db": "在資料庫中找到 token",
"token_not_found_in_db": "在資料庫中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"usage_response_status": "使用情況響應狀態: {response}",
"unexpected_status_code": "意外狀態碼: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
"enter_token": "請輸入您的 Cursor token: ",
"token_length": "token 長度: {length}",
"invalid_token": "無效的 token",
"user_authorized": "用戶已授權",
"user_unauthorized": "用戶未授權",
"request_timeout": "請求超時",
"connection_error": "連接錯誤",
"check_error": "檢查授權時出錯: {error}",
"authorization_successful": "授權成功",
"authorization_failed": "授權失敗",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外錯誤: {error}",
"error_generating_checksum": "生成校驗和時出錯: {error}",
"checking_usage_information": "檢查使用情況...",
"check_usage_response": "檢查使用情況響應: {response}",
"usage_response": "使用情況響應: {response}"
}
}

View File

@@ -287,7 +287,8 @@ def print_menu():
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}"
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}"
}
# Automatically calculate the number of menu items in the left and right columns
@@ -560,7 +561,7 @@ def main():
while True:
try:
choice_num = 15
choice_num = 16
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0":
@@ -630,6 +631,10 @@ def main():
import bypass_version
bypass_version.main(translator)
print_menu()
elif choice == "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()

View File

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