Compare commits

..

8 Commits

Author SHA1 Message Date
yeongpin
4f6f3fe814 fix: Improve Cursor Version Check and Configuration Handling
- Enhance version checking in `reset_machine_manual.py` with more robust error handling
- Add detailed error messages and logging for version detection
- Update configuration paths in `new_signup.py` to include Cursor application path
- Add configuration initialization in `main.py`
- Update localization files with new error and version-related messages
2025-03-10 17:41:07 +08:00
yeongpin
54ecf2d752 feat: Add Linux ARM64 Support and Update Build Workflow
- Extend GitHub Actions workflow to build Linux x64 and ARM64 executables
- Update install script to detect Linux architecture (x64 or ARM64)
- Modify release process to include both Linux architecture artifacts
- Rename Linux build job to clarify x64 architecture
2025-03-10 16:28:26 +08:00
yeongpin
1f1231d1a9 remove br in readme 2025-03-10 14:43:35 +08:00
yeongpin
d10999a517 Update Readme Doc 2025-03-10 14:43:09 +08:00
yeongpin
fb4be6334a docs: Add Detailed Comments and Localization for Configuration Settings
- Add bilingual comments (English and Chinese) for OSPaths section
- Provide clear descriptions for timing-related configuration parameters
- Enhance readability of README.md configuration settings
- Improve documentation for storage, SQLite, and machine ID paths
2025-03-10 13:42:18 +08:00
Pin Studios
786eba5371 Update README.md 2025-03-10 13:41:05 +08:00
yeongpin
41ddbf519e hotfix: Optimize Verification Code Handling and Timing Configuration
- Refactor verification code retrieval with dynamic wait time generation
- Use `get_random_wait_time()` for more flexible retry intervals
- Update version to 1.7.04 across project files
- Minor improvements to timing and retry logic in signup process
2025-03-10 13:27:41 +08:00
yeongpin
ffd48201fd hotfix: Improve Signup Flow and Timing Configuration
- Add comprehensive timing configuration for signup process
- Refactor random wait time generation with more flexible config options
- Update form filling and verification code handling with configurable wait times
- Enhance localization support for new timing-related messages
- Update version to 1.7.03 across project files
2025-03-10 12:24:43 +08:00
12 changed files with 402 additions and 129 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.7.02
VERSION=1.7.02
version=1.7.05
VERSION=1.7.05

View File

@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.7.02'
default: '1.7.05'
permissions:
contents: write
@@ -98,7 +98,7 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux:
build-linux-x64:
needs: create-tag
runs-on: ubuntu-22.04
@@ -120,20 +120,55 @@ jobs:
pip install pyinstaller
pip install -r requirements.txt
- name: Build Linux executable
- name: Build Linux x64 executable
env:
VERSION: ${{ env.VERSION }}
run: |
pyinstaller build.spec
mv "dist/CursorFreeVIP_${{ env.VERSION }}_linux" "dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64"
echo "Contents of dist directory:"
ls -la dist/
- name: Upload Linux artifact
- name: Upload Linux x64 artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_linux
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux
name: CursorFreeVIP_${{ env.VERSION }}_linux_x64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
build-linux-arm64:
needs: create-tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Build in ARM64 Docker container
run: |
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
python -m PyInstaller build.spec
mv /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
"
echo "Contents of dist directory:"
ls -la dist/
- name: Upload Linux ARM64 artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_linux_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
build-macos-intel:
needs: create-tag
@@ -171,9 +206,8 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release:
needs: [build-windows, build-macos-arm64, build-linux, build-macos-intel]
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
runs-on: ubuntu-22.04
steps:
@@ -201,7 +235,8 @@ jobs:
files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP_${{ env.VERSION }}_linux
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
draft: false
prerelease: false

View File

@@ -1,5 +1,16 @@
# Change Log
## v1.7.05
1. Fix: Cursor Version Check | 修復Cursor版本檢查
2. Fix: Small Problem | 修復一些小問題
## v1.7.04
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.03
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑
2. Add: Config File | 增加配置文件

View File

@@ -12,7 +12,7 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p>
<h4>Support Latest 0.46.9 Version | 支持最新0.46.9本</h4>
<h4>Support Latest 0.46.10 Version | 支持最新0.46.10版本</h4>
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
@@ -88,6 +88,8 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
📝 Config | 文件配置
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
<details>
<summary><b>⭐ Config | 文件配置</b></summary>
```
[Chrome]
@@ -101,10 +103,44 @@ handle_turnstile_time = 2
handle_turnstile_random_time = 1-3
[OSPaths]
# Storage Path | 存儲路徑
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
# SQLite Path | SQLite路徑
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
# Machine ID Path | 機器ID路徑
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
[Timing]
# Min Random Time | 最小隨機時間
min_random_time = 0.1
# Max Random Time | 最大隨機時間
max_random_time = 0.8
# Page Load Wait | 頁面加載等待時間
page_load_wait = 0.1-0.8
# Input Wait | 輸入等待時間
input_wait = 0.3-0.8
# Submit Wait | 提交等待時間
submit_wait = 0.5-1.5
# Verification Code Input | 驗證碼輸入等待時間
verification_code_input = 0.1-0.3
# Verification Success Wait | 驗證成功等待時間
verification_success_wait = 2-3
# Verification Retry Wait | 驗證重試等待時間
verification_retry_wait = 2-3
# Email Check Initial Wait | 郵件檢查初始等待時間
email_check_initial_wait = 4-6
# Email Refresh Wait | 郵件刷新等待時間
email_refresh_wait = 2-4
# Settings Page Load Wait | 設置頁面加載等待時間
settings_page_load_wait = 1-2
# Failed Retry Time | 失敗重試時間
failed_retry_time = 0.5-1
# Retry Interval | 重試間隔
retry_interval = 8-12
# Max Timeout | 最大超時時間
max_timeout = 160
```
</details>
* Use administrator to run the script <br>請使用管理員身份運行腳本

View File

@@ -160,7 +160,7 @@ class CursorRegistration:
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.total_usage', usage=total_usage)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30
retry_interval = 2

View File

@@ -68,7 +68,24 @@
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
"detecting_version": "Detecting Cursor Version",
"patching_getmachineid": "Patching getMachineId",
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId"
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId",
"permission_denied": "Permission Denied: {error}",
"backup_created": "Backup Created",
"update_success": "Update Success",
"update_failed": "Update Failed: {error}",
"windows_machine_guid_updated": "Windows Machine GUID Updated Successfully",
"reading_package_json": "Reading package.json {path}",
"invalid_json_object": "Invalid JSON Object",
"no_version_field": "No Version Field Found in package.json",
"version_field_empty": "Version Field is Empty",
"invalid_version_format": "Invalid Version Format: {version}",
"found_version": "Found Version: {version}",
"version_parse_error": "Version Parse Error: {error}",
"package_not_found": "Package.json Not Found: {path}",
"check_version_failed": "Check Version Failed: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0"
},
"register": {
"title": "Cursor Registration Tool",
@@ -141,7 +158,9 @@
"verification_failed": "Verification Failed",
"verification_error": "Verification Error: {error}",
"config_option_added": "Config Option Added: {option}",
"config_updated": "Config Updated"
"config_updated": "Config Updated",
"password_submitted": "Password Submitted",
"total_usage": "Total Usage: {usage}"
},
"auth": {
"title": "Cursor Auth Manager",

View File

@@ -68,7 +68,23 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "检测Cursor版本",
"patching_getmachineid": "修补getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId"
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId",
"permission_denied": "权限拒绝: {error}",
"backup_created": "备份已创建",
"update_success": "更新成功",
"update_failed": "更新失败: {error}",
"windows_machine_guid_updated": "Windows机器GUID更新成功",
"reading_package_json": "读取package.json {path}",
"invalid_json_object": "JSON对象无效",
"no_version_field": "package.json中没有版本字段",
"version_field_empty": "版本字段为空",
"invalid_version_format": "版本格式无效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析错误: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "检查版本失败: {error}",
"stack_trace": "堆栈跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
},
"register": {
"title": "Cursor 注册工具",
@@ -141,7 +157,9 @@
"verification_failed": "验证失败",
"verification_error": "验证错误: {error}",
"config_option_added": "配置项已添加: {option}",
"config_updated": "配置已更新"
"config_updated": "配置已更新",
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}"
},
"auth": {
"title": "Cursor 认证管理器",

View File

@@ -68,8 +68,25 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "檢測Cursor版本",
"patching_getmachineid": "修補getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId"
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId",
"permission_denied": "權限拒絕: {error}",
"backup_created": "備份已創建",
"update_success": "更新成功",
"update_failed": "更新失敗: {error}",
"windows_machine_guid_updated": "Windows機器GUID更新成功",
"reading_package_json": "讀取package.json {path}",
"invalid_json_object": "JSON對象無效",
"no_version_field": "package.json中沒有版本字段",
"version_field_empty": "版本字段為空",
"invalid_version_format": "版本格式無效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析錯誤: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "檢查版本失敗: {error}",
"stack_trace": "堆疊跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
},
"register": {
"title": "Cursor 註冊工具",
"start": "正在啟動註冊流程...",
@@ -122,7 +139,9 @@
"verification_failed": "驗證失敗",
"verification_error": "驗證錯誤: {error}",
"config_option_added": "配置項已添加: {option}",
"config_updated": "配置已更新"
"config_updated": "配置已更新",
"password_submitted": "密碼已提交",
"total_usage": "總使用量: {usage}"
},
"auth": {
"title": "Cursor 認證管理器",

View File

@@ -285,6 +285,14 @@ def check_latest_version():
def main():
print_logo()
# 初始化配置
from new_signup import setup_config
config = setup_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
return
check_latest_version() # Add version check before showing menu
print_menu()

View File

@@ -37,37 +37,51 @@ def signal_handler(signum, frame):
cleanup_chrome_processes(_translator)
os._exit(0)
def simulate_human_input(page, url, translator=None):
def simulate_human_input(page, url, config, translator=None):
"""访问网址"""
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
else:
print("正在访问网址...")
# 先访问空白页面
page.get('about:blank')
time.sleep(random.uniform(1.0, 2.0))
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 访问目标页面
page.get(url)
time.sleep(random.uniform(2.0, 3.0)) # 等待页面加载
time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, translator=None):
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""填写注册表单"""
try:
if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else:
print("\n正在填写注册表单...")
# 填写名字
first_name_input = page.ele("@name=first_name")
if first_name_input:
first_name_input.input(first_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写姓氏
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写邮箱
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
# 构建带参数的URL
encoded_email = email.replace('@', '%40')
signup_url = f"https://authenticator.cursor.sh/sign-up/password?first_name={first_name}&last_name={last_name}&email={encoded_email}&redirect_uri=https%3A%2F%2Fcursor.com%2Fapi%2Fauth%2Fcallback"
# 直接访问URL
page.get(signup_url)
time.sleep(random.uniform(2.0, 3.0))
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
@@ -117,18 +131,35 @@ def get_user_documents_path():
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def parse_random_time(time_str):
"""解析随机时间范围配置"""
def get_random_wait_time(config, timing_type='page_load_wait'):
"""
Get random wait time from config
Args:
config: ConfigParser object
timing_type: Type of timing to get (page_load_wait, input_wait, submit_wait)
Returns:
float: Random wait time or fixed time
"""
try:
if '-' in time_str:
min_time, max_time = map(float, time_str.split('-'))
elif ',' in time_str:
min_time, max_time = map(float, time_str.split(','))
else:
min_time = max_time = float(time_str)
return min_time, max_time
if not config.has_section('Timing'):
return random.uniform(0.1, 0.8) # 默认值
if timing_type == 'random':
min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
max_time = float(config.get('Timing', 'max_random_time', fallback='0.8'))
return random.uniform(min_time, max_time)
time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
# 检查是否为固定时间值
if '-' not in time_value and ',' not in time_value:
return float(time_value) # 返回固定时间
# 处理范围时间
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
return random.uniform(min_time, max_time)
except:
return 1, 3 # 默认值
return random.uniform(0.1, 0.8) # 出错时返回默认值
def setup_config(translator=None):
"""Setup configuration file and return config object"""
@@ -151,6 +182,22 @@ def setup_config(translator=None):
'Turnstile': {
'handle_turnstile_time': '2',
'handle_turnstile_random_time': '1-3'
},
'Timing': {
'min_random_time': '0.1',
'max_random_time': '0.8',
'page_load_wait': '0.1-0.8',
'input_wait': '0.3-0.8',
'submit_wait': '0.5-1.5',
'verification_code_input': '0.1-0.3', # 验证码输入间隔
'verification_success_wait': '2-3', # 验证成功后等待
'verification_retry_wait': '2-3', # 验证重试等待
'email_check_initial_wait': '4-6', # 首次等待邮件时间
'email_refresh_wait': '2-4', # 邮箱刷新等待时间
'settings_page_load_wait': '1-2', # 设置页面加载等待
'failed_retry_time': '0.5-1', # 验证失败重试等待时间
'retry_interval': '8-12', # 重试间隔时间
'max_timeout': '160' # 最大超时时间
}
}
@@ -160,21 +207,25 @@ def setup_config(translator=None):
default_config['WindowsPaths'] = {
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(os.getenv("APPDATA"), "Cursor", "machineId")
'machine_id_path': os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"),
'cursor_path': os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app")
}
elif sys.platform == "darwin":
default_config['MacPaths'] = {
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId")
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app"
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId")
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId"),
'cursor_path': "/opt/Cursor/resources/app" # 默認路徑
}
if os.path.exists(config_file):
@@ -289,7 +340,12 @@ def handle_turnstile(page, config, translator=None):
# from config
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
min_random_time, max_random_time = parse_random_time(random_time_str)
# 解析随机时间范围
try:
min_time, max_time = map(float, random_time_str.split('-'))
except:
min_time, max_time = 1, 3 # 默认值
max_retries = 2
retry_count = 0
@@ -322,7 +378,7 @@ def handle_turnstile(page, config, translator=None):
print("检测到验证框...")
# from config
time.sleep(random.uniform(min_random_time, max_random_time))
time.sleep(random.uniform(min_time, max_time))
challenge_check.click()
time.sleep(turnstile_time) # from config
@@ -348,7 +404,7 @@ def handle_turnstile(page, config, translator=None):
print("验证通过!")
return True
time.sleep(random.uniform(min_random_time, max_random_time))
time.sleep(random.uniform(min_time, max_time))
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
@@ -393,7 +449,7 @@ def generate_password(length=12):
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, translator=None) -> bool:
def fill_password(page, password: str, config, translator=None) -> bool:
"""
填写密码表单
"""
@@ -413,22 +469,22 @@ def fill_password(page, password: str, translator=None) -> bool:
if password_input:
# 清除可能存在的旧值并输入新密码
password_input.click()
time.sleep(random.uniform(0.5, 1))
time.sleep(get_random_wait_time(config, 'input_wait'))
password_input.input(password)
time.sleep(random.uniform(1, 2))
time.sleep(get_random_wait_time(config, 'input_wait'))
# 查找并点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
time.sleep(random.uniform(2, 3))
time.sleep(get_random_wait_time(config, 'submit_wait'))
return True
else:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_submit') if translator else '未找到提交按钮,重试中...'}{Style.RESET_ALL}")
# 如果没找到密码框,等待后重试
time.sleep(2)
time.sleep(get_random_wait_time(config, 'failed_retry_time'))
if i < max_retries - 1: # 不是最后一次尝试时才打印
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_password', attempt=i+1) if translator else f'{i+1} 次尝试设置密码...'}{Style.RESET_ALL}")
@@ -439,7 +495,7 @@ def fill_password(page, password: str, translator=None) -> bool:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
return False
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None):
def handle_verification_code(browser_tab, email_tab, controller, email, password, config, translator=None):
"""处理验证码"""
try:
if translator:
@@ -452,21 +508,21 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
time.sleep(get_random_wait_time(config, 'verification_code_input'))
print(f"{translator.get('register.verification_success')}")
time.sleep(3)
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator):
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2)
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面
print(f"{Fore.CYAN} {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
return False, None
@@ -474,11 +530,11 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 自动获取验证码逻辑
elif email_tab:
print(f"{translator.get('register.waiting_for_verification_code')}")
time.sleep(5) # 等待验证码邮件
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# 使用已有的 email_tab 刷新邮箱
email_tab.refresh_inbox()
time.sleep(3)
time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
# 检查邮箱是否有验证码邮件
if email_tab.check_for_cursor_email():
@@ -487,22 +543,23 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(3)
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator):
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2)
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面
if translator:
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
else:
@@ -515,9 +572,9 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 获取验证码,设置超时
verification_code = None
max_attempts = 20
retry_interval = 10
retry_interval = get_random_wait_time(config, 'retry_interval') # 使用 get_random_wait_time
start_time = time.time()
timeout = 160
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # 這個可以保持不變因為是固定值
if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
@@ -541,29 +598,29 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 刷新邮箱
email_tab.refresh_inbox()
time.sleep(retry_interval)
time.sleep(retry_interval) # 使用 get_random_wait_time
if verification_code:
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(3)
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator):
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2)
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 直接访问设置页面
if translator:
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
# 直接返回成功,让 cursor_register.py 处理账户信息获取
return True, browser_tab
@@ -650,10 +707,10 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# 访问页面
simulate_human_input(page, url, translator)
simulate_human_input(page, url, config, translator)
if translator:
print(f"{Fore.CYAN}{translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(5)
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 如果没有提供账号信息,则生成随机信息
if not all([email, password, first_name, last_name]):
@@ -670,7 +727,7 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
f.write(f"{'='*50}\n")
# 填写表单
if fill_signup_form(page, first_name, last_name, email, translator):
if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
@@ -680,7 +737,7 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# 填写密码
if fill_password(page, password, translator):
if fill_password(page, password, config, translator):
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
time.sleep(2)
@@ -689,9 +746,9 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, email, password, translator):
if handle_verification_code(page, email_tab, controller, email, password, config, translator):
success = True
return True, page # 返回浏览器实例
return True, page
else:
print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
else:

View File

@@ -12,6 +12,7 @@ from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
# Initialize colorama
init()
@@ -29,42 +30,45 @@ EMOJI = {
def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
paths_map = {
"Darwin": {
"base": "/Applications/Cursor.app/Contents/Resources/app",
"package": "package.json",
"main": "out/main.js",
},
"Windows": {
"base": os.path.join(
os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"
),
"package": "package.json",
"main": "out/main.js",
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
"package": "package.json",
"main": "out/main.js",
},
}
if system not in paths_map:
# 讀取配置文件
config = configparser.ConfigParser()
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
if not os.path.exists(config_file):
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
config.read(config_file)
# 根據系統獲取路徑
if system == "Darwin":
section = 'MacPaths'
elif system == "Windows":
section = 'WindowsPaths'
elif system == "Linux":
section = 'LinuxPaths'
else:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
if system == "Linux":
for base in paths_map["Linux"]["bases"]:
pkg_path = os.path.join(base, paths_map["Linux"]["package"])
if os.path.exists(pkg_path):
return (pkg_path, os.path.join(base, paths_map["Linux"]["main"]))
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"]
return (
os.path.join(base_path, paths_map[system]["package"]),
os.path.join(base_path, paths_map[system]["main"]),
)
if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
base_path = config.get(section, 'cursor_path')
if not os.path.exists(base_path):
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
pkg_path = os.path.join(base_path, "package.json")
main_path = os.path.join(base_path, "out/main.js")
# 檢查文件是否存在
if not os.path.exists(pkg_path):
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
if not os.path.exists(main_path):
raise OSError(translator.get('reset.main_not_found', path=main_path) if translator else f"找不到 main.js: {main_path}")
return (pkg_path, main_path)
def get_cursor_machine_id_path(translator=None) -> str:
"""
@@ -176,11 +180,60 @@ def check_cursor_version(translator) -> bool:
"""Check Cursor version"""
try:
pkg_path, _ = get_cursor_paths(translator)
with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"]
return version_check(version, min_version="0.45.0", translator=translator)
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.reading_package_json', path=pkg_path)}{Style.RESET_ALL}")
try:
with open(pkg_path, "r", encoding="utf-8") as f:
data = json.load(f)
except UnicodeDecodeError:
# 如果 UTF-8 讀取失敗,嘗試其他編碼
with open(pkg_path, "r", encoding="latin-1") as f:
data = json.load(f)
if not isinstance(data, dict):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
if "version" not in data:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_version_field')}{Style.RESET_ALL}")
return False
version = str(data["version"]).strip()
if not version:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_field_empty')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
# 檢查版本格式
if not re.match(r"^\d+\.\d+\.\d+$", version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False
# 比較版本
try:
current = tuple(map(int, version.split(".")))
min_ver = (0, 45, 0) # 直接使用元組而不是字符串
if current >= min_ver:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.version_too_low', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return False
except ValueError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_parse_error', error=str(e))}{Style.RESET_ALL}")
return False
except FileNotFoundError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.package_not_found', path=pkg_path)}{Style.RESET_ALL}")
return False
except json.JSONDecodeError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.stack_trace')}: {traceback.format_exc()}{Style.RESET_ALL}")
return False
def modify_workbench_js(file_path: str, translator=None) -> bool:
@@ -576,12 +629,12 @@ class MachineIDResetter:
### Remove In v1.7.02
# 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}")
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}")

View File

@@ -63,8 +63,15 @@ detect_os() {
echo -e "${CYAN} Detected macOS Intel architecture${NC}"
fi
elif [[ "$(uname)" == "Linux" ]]; then
OS="linux"
echo -e "${CYAN} Detected Linux system${NC}"
# Detect Linux architecture
ARCH=$(uname -m)
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
OS="linux_arm64"
echo -e "${CYAN} Detected Linux ARM64 architecture${NC}"
else
OS="linux_x64"
echo -e "${CYAN} Detected Linux x64 architecture${NC}"
fi
else
# Assume Windows
OS="windows"
@@ -127,9 +134,19 @@ install_cursor_free_vip() {
echo -e "${RED}❌ New download link does not exist${NC}"
exit 1
fi
else
} elif [[ "$OS" == "linux_x64" || "$OS" == "linux_arm64" ]]; then
OS="linux"
binary_name="CursorFreeVIP_${VERSION}_${OS}"
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} New download link: ${download_url}${NC}"
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${RED}❌ New download link does not exist${NC}"
exit 1
fi
} else {
exit 1
fi
}
fi
# Download file