Compare commits

...

5 Commits

Author SHA1 Message Date
yeongpin
cd4f36725c refactor: Optimize Signup Flow and Error Handling
- Implement direct URL-based signup with pre-filled parameters
- Enhance password setting process with improved error detection
- Add more robust retry mechanism for password input
- Improve error handling for email availability and submission
- Refactor signup and password setting methods for better reliability
2025-03-10 11:44:37 +08:00
yeongpin
dccb524bd7 Update Readme add some doc 2025-03-10 11:21:42 +08:00
yeongpin
90e9a5b287 feat: Enhance Configuration Management for Cursor Paths
- Add OS-specific path configurations for Cursor storage, SQLite, and machine ID files
- Integrate configuration file management in `new_signup.py` and `reset_machine_manual.py`
- Remove Workbench Cursor path and main.js modification steps
- Improve cross-platform path detection and configuration handling
- Update version to 1.7.02
2025-03-10 11:18:21 +08:00
Pin Studios
66a67fce8b Update README.md 2025-03-07 11:20:04 +08:00
Pin Studios
0981f00b9c Update build.yml 2025-03-07 11:19:49 +08:00
6 changed files with 182 additions and 101 deletions

4
.env
View File

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

View File

@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.5.03'
default: '1.7.02'
permissions:
contents: write

View File

@@ -1,5 +1,11 @@
# Change Log
## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑
2. Add: Config File | 增加配置文件
3. Remove: Workbench Cursor Path | 移除Workbench Cursor路徑
4. Remove: Cursor Main JS | 移除Cursor main.js
## v1.7.01
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
- Optimization: Improve code maintainability and make configuration management and browser settings more clear

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.8 Version | 支持最新0.46.8本</h4>
<h4>Support Latest 0.46.9 Version | 支持最新0.46.9本</h4>
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
@@ -99,6 +99,11 @@ chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
handle_turnstile_time = 2
# Handle Tuenstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
handle_turnstile_random_time = 1-3
[OSPaths]
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
```
* Use administrator to run the script <br>請使用管理員身份運行腳本

View File

@@ -59,31 +59,15 @@ def fill_signup_form(page, first_name, last_name, email, translator=None):
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(random.uniform(0.5, 1.0))
# 填写姓氏
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(random.uniform(0.5, 1.0))
# 填写邮箱
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(random.uniform(0.5, 1.0))
# 点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(random.uniform(2.0, 3.0))
# 构建带参数的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:
@@ -170,6 +154,29 @@ def setup_config(translator=None):
}
}
# Add OS-specific path configurations
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
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")
}
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")
}
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")
}
if os.path.exists(config_file):
config.read(config_file)
config_modified = False
@@ -393,44 +400,43 @@ def fill_password(page, password: str, translator=None) -> bool:
try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}")
# 等待密码框出现
# 等待密码框出现并尝试多次
max_retries = 5
for i in range(max_retries):
try:
# 使用 DrissionPage 的方式查找密码输入框
password_input = page.ele('@type=password', timeout=3)
if password_input:
break
time.sleep(2)
except:
if i == max_retries - 1:
print(f"{Fore.RED}{translator.get('register.password_field_not_found') if translator else '未找到密码输入框'}{Style.RESET_ALL}")
return False
continue
if password_input:
# 清除可能存在的旧值
password_input.click()
time.sleep(0.5)
password_input.input(password)
time.sleep(1)
# 查找并点击提交按钮
submit_button = page.ele('@type=submit')
if submit_button:
submit_button.click()
time.sleep(2)
return True
else:
print(f"{Fore.RED}{translator.get('register.continue_button_not_found') if translator else '未找到继续按钮'}{Style.RESET_ALL}")
# 检查是否出现错误信息
if page.ele("This email is not available."):
print(f"{Fore.RED}{translator.get('register.email_used') if translator else '注册失败:邮箱已被使用'}{Style.RESET_ALL}")
return False
else:
print(f"{Fore.RED}{translator.get('register.password_input_failed') if translator else '密码输入失败'}{Style.RESET_ALL}")
return False
# 查找密码输入框
password_input = page.ele("@name=password")
if password_input:
# 清除可能存在的旧值并输入新密码
password_input.click()
time.sleep(random.uniform(0.5, 1))
password_input.input(password)
time.sleep(random.uniform(1, 2))
# 查找并点击提交按钮
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))
return True
else:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_submit') if translator else '未找到提交按钮,重试中...'}{Style.RESET_ALL}")
# 如果没找到密码框,等待后重试
time.sleep(2)
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}")
print(f"{Fore.RED}{translator.get('register.password_set_failed') if translator else '密码设置失败:超过重试次数'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_setting_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
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):

View File

@@ -10,6 +10,8 @@ import re
import tempfile
from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
# Initialize colorama
init()
@@ -70,15 +72,42 @@ def get_cursor_machine_id_path(translator=None) -> str:
Returns:
str: Path to machineId file
"""
# 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 os.path.exists(config_file):
config.read(config_file)
if sys.platform == "win32": # Windows
return os.path.join(os.getenv("APPDATA"), "Cursor", "machineId")
if not config.has_section('WindowsPaths'):
config.add_section('WindowsPaths')
config.set('WindowsPaths', 'machine_id_path',
os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"))
return config.get('WindowsPaths', 'machine_id_path')
elif sys.platform == "linux": # Linux
return os.path.expanduser("~/.config/Cursor/machineId")
if not config.has_section('LinuxPaths'):
config.add_section('LinuxPaths')
config.set('LinuxPaths', 'machine_id_path',
os.path.expanduser("~/.config/Cursor/machineId"))
return config.get('LinuxPaths', 'machine_id_path')
elif sys.platform == "darwin": # macOS
return os.path.expanduser("~/Library/Application Support/Cursor/machineId")
if not config.has_section('MacPaths'):
config.add_section('MacPaths')
config.set('MacPaths', 'machine_id_path',
os.path.expanduser("~/Library/Application Support/Cursor/machineId"))
return config.get('MacPaths', 'machine_id_path')
else:
raise OSError(f"Unsupported operating system: {sys.platform}")
# Save any changes to config file
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
def get_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
@@ -318,43 +347,73 @@ 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)
# Check operating system
if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA")
if appdata is None:
raise EnvironmentError("APPDATA Environment Variable Not Set")
self.db_path = os.path.join(
appdata, "Cursor", "User", "globalStorage", "storage.json"
)
self.sqlite_path = os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
)
elif sys.platform == "darwin": # macOS
self.db_path = os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/storage.json"
))
self.sqlite_path = os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
))
elif sys.platform == "linux": # Linux
# 获取实际用户的主目录
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
actual_home = f"/home/{sudo_user}"
else:
actual_home = os.path.expanduser("~")
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 = os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/storage.json"
))
self.sqlite_path = os.path.abspath(os.path.join(
actual_home,
".config/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')
# 获取实际用户的主目录
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
@@ -507,17 +566,22 @@ class MachineIDResetter:
# 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)
### Remove In v1.7.02
# Modify workbench.desktop.main.js
# workbench_path = get_workbench_cursor_path(self.translator)
# modify_workbench_js(workbench_path, self.translator)
### 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}")