Compare commits

...

21 Commits

Author SHA1 Message Date
yeongpin
a83851d441 update new build.yml 2025-02-08 22:54:02 +08:00
yeongpin
1e290d0417 build.yml update 2025-02-08 22:50:05 +08:00
yeongpin
53ab15604e update build 2025-02-08 22:45:32 +08:00
yeongpin
638916e5ef update github 2025-02-08 22:38:11 +08:00
yeongpin
9500ce1249 update new build 2025-02-08 22:22:19 +08:00
yeongpin
721c13cb2f update build 2025-02-08 22:16:02 +08:00
yeongpin
a04eed2c6b update build.yml 2025-02-08 22:12:17 +08:00
yeongpin
8107bede63 update remake 2025-02-08 22:10:00 +08:00
yeongpin
4166bc5135 update reissue 2025-02-08 22:04:14 +08:00
yeongpin
69994d2486 update v4 2025-02-08 22:02:01 +08:00
yeongpin
ffcb8ec140 update 2025-02-08 22:00:19 +08:00
yeongpin
b61b61c607 add workflow 2025-02-08 21:57:44 +08:00
yeongpin
7326d0eeb0 newest reset machine 2025-02-08 21:52:16 +08:00
yeongpin
371f00645d update readme 2025-02-05 19:33:42 +08:00
yeongpin
a873291481 update env 2025-02-05 19:22:01 +08:00
yeongpin
7710ea4bb3 Update support 0.45 2025-02-05 19:21:34 +08:00
yeongpin
13483eed77 update auth message & reset message 2025-01-17 10:12:18 +08:00
yeongpin
eb69f933af Update & Fix reset machine problem 2025-01-15 17:14:05 +08:00
yeongpin
54cd8cf323 Update 107 2025-01-15 13:54:38 +08:00
yeongpin
4e0289c86c Update New107 images 2025-01-15 13:46:54 +08:00
yeongpin
af4a1c4065 Update Readme 2025-01-15 13:44:22 +08:00
16 changed files with 757 additions and 90 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.0.7
VERSION=1.0.7
version=1.0.9
VERSION=1.0.9

187
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,187 @@
name: Build Executables
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.0.9'
permissions:
contents: write
actions: write
packages: write
jobs:
create-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Create Tag
run: |
git tag v${{ github.event.inputs.version }}
git push origin v${{ github.event.inputs.version }}
build-windows:
needs: create-tag
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
- name: Build EXE
run: |
pyinstaller build.spec
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
path: dist/*
build-macos-arm64:
needs: create-tag
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
- name: Build MacOS ARM executable
run: |
pyinstaller build.spec
- name: Upload MacOS ARM artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
path: dist/*
build-linux:
needs: create-tag
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
- name: Build Linux executable
run: |
pyinstaller build.spec
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_linux
path: dist/*
build-macos-intel:
needs: create-tag
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
arch -x86_64 pip3 install --upgrade pip
arch -x86_64 pip3 install pyinstaller
arch -x86_64 pip3 install -r requirements.txt
- name: Build MacOS Intel executable
env:
TARGET_ARCH: 'x86_64'
run: |
arch -x86_64 python3 -m PyInstaller build.spec
- name: Upload MacOS Intel artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
path: dist/*
create-release:
needs: [build-windows, build-macos-arm64, build-linux, build-macos-intel]
runs-on: ubuntu-22.04
steps:
- name: Get version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: List files
run: ls -R artifacts/
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ env.VERSION }}
files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -11,13 +11,14 @@
[![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers)
</p>
<h4>Support Latest 0.45.11 Version | 支持最新0.45.11版本</h4>
This is a tool to automatically register (except for Google verification code), support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
這是一個自動化工具自動註冊除了Google驗證碼),支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
<p align="center">
<img src="./images/pro_2025-01-14_14-40-37.png" alt="new" width="400"/><br>
<img src="./images/new107_2025-01-15_13-53-56.png" alt="new" width="400"/><br>
</p>
<br>
@@ -37,6 +38,34 @@ This is a tool to automatically register (except for Google verification code),
## 🔄 更新日志
<details open>
<summary>v1.0.9</summary>
<p align="center">
<img src="./images/pass_2025-02-08_21-48-36.png" alt="free" width="400"/><br>
</p>
1. Fixed New 0.45.x Version Reset Machine | 修復新0.45版本重置機器
2. Fix Locale Language | 修復多語言
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
4. Add Remake main.js | 重做main.js
</details>
<details>
<summary>Other Version Change Log</summary>
<details>
<summary>v1.0.8</summary>
1. Fix New 0.45 Version Reset Machine | 修復新0.45版本重置機器
2. Fix Locale Language | 修復多語言
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
</details>
<details>
<summary>v1.0.7 - HotFix</summary>
1. Fix Reset Machine | 修復重置機器
2. Fix Locale Language | 修復多語言
</details>
<details>
<summary>v1.0.7</summary>
1. Add Locale Language Support | 增加多語言支持
@@ -130,6 +159,7 @@ This is a tool to automatically register (except for Google verification code),
<img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br>
</p>
</details>
</details>
## ✨ Features | 功能特點

View File

@@ -2,6 +2,7 @@ from DrissionPage import ChromiumOptions, ChromiumPage
import sys
import os
import logging
import random
class BrowserManager:
@@ -36,17 +37,58 @@ class BrowserManager:
except FileNotFoundError as e:
logging.warning(f"警告: {e}")
# 设置更真实的用户代理
co.set_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
# 基本设置
co.set_pref("credentials_enable_service", False)
co.set_pref("profile.password_manager_enabled", False)
# 禁用自动化标志
co.set_pref("useAutomationExtension", False)
co.set_pref("excludeSwitches", ["enable-automation"])
# WebGL 和 GPU 设置
co.set_pref("webgl.disabled", False)
co.set_pref("webgl.enable_webgl2", True)
# 设置语言和地区
co.set_pref("intl.accept_languages", "en-US,en")
# 基本命令行参数
co.set_argument("--disable-blink-features=AutomationControlled")
co.set_argument("--hide-crash-restore-bubble")
co.set_argument("--no-first-run")
co.set_argument("--no-default-browser-check")
co.set_argument("--disable-popup-blocking")
# 性能和稳定性参数
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-gpu")
co.set_argument("--no-sandbox")
co.set_argument("--ignore-certificate-errors")
# WebGL 相关参数
co.set_argument("--use-gl=swiftshader")
co.set_argument("--enable-webgl")
# 随机端口
co.auto_port()
# Mac 系统特殊处理
if sys.platform == "darwin":
co.set_argument("--no-sandbox")
# 系统特定设置
if sys.platform == "darwin": # macOS
co.set_argument("--disable-gpu")
co.set_argument("--no-sandbox")
elif sys.platform == "win32": # Windows
co.set_argument("--disable-software-rasterizer")
# 设置窗口大小
window_width = random.randint(1024, 1920)
window_height = random.randint(768, 1080)
co.set_argument(f"--window-size={window_width},{window_height}")
return co

View File

@@ -31,7 +31,8 @@ a = Analysis(
('reset_machine_manual.py', '.'),
('cursor_register.py', '.'),
('browser.py', '.'),
('control.py', '.')
('control.py', '.'),
('.env', '.')
],
hiddenimports=[
'cursor_auth',

View File

@@ -77,7 +77,7 @@ class BrowserControl:
return False
def select_email_domain(self, domain_index=None):
"""选择邮箱域名如果不指定index则随机选择"""
"""选择邮箱域名如果不指定index则随机选择。避免选择fr.nf域名"""
try:
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('control.select_email_domain')}...{Style.RESET_ALL}")
# 找到下拉框
@@ -95,21 +95,38 @@ class BrowserControl:
all_options.extend(other_options)
if all_options:
# 如果没有指定索引,随机选择一个
if domain_index is None:
domain_index = random.randint(0, len(all_options) - 1)
max_attempts = 5 # 最大尝试次数
attempt = 0
if domain_index < len(all_options):
# 获取选中选项的文本
selected_domain = all_options[domain_index].text
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('control.select_email_domain')}: {selected_domain}{Style.RESET_ALL}")
while attempt < max_attempts:
# 如果没有指定索引,随机选择一个
if domain_index is None:
domain_index = random.randint(0, len(all_options) - 1)
# 点击选择
all_options[domain_index].click()
time.sleep(1)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.select_email_domain_success')}{Style.RESET_ALL}")
return True
if domain_index < len(all_options):
# 获取选中选项的文本
selected_domain = all_options[domain_index].text
# 检查是否为fr.nf域名
if "fr.nf" in selected_domain.lower():
print(f"{Fore.YELLOW}{EMOJI['INFO']} 检测到fr.nf域名重新选择...{Style.RESET_ALL}")
domain_index = None # 重置索引以便重新随机选择
attempt += 1
continue
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('control.select_email_domain')}: {selected_domain}{Style.RESET_ALL}")
# 点击选择
all_options[domain_index].click()
time.sleep(1)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.select_email_domain_success')}{Style.RESET_ALL}")
return True
attempt += 1
print(f"{Fore.RED}{EMOJI['ERROR']} 无法找到合适的非fr.nf域名{Style.RESET_ALL}")
return False
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.no_available_domain_options', count=len(all_options))}{Style.RESET_ALL}")
return False
else:

View File

@@ -56,7 +56,7 @@ class CursorAuth:
# 重新连接数据库
conn = sqlite3.connect(self.db_path)
print(f"{EMOJI['INFO']} {Fore.GREEN}{self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
print(f"{EMOJI['INFO']} {Fore.GREEN} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
cursor = conn.cursor()
# 增加超时和其他优化设置
@@ -90,7 +90,7 @@ class CursorAuth:
UPDATE ItemTable SET value = ?
WHERE key = ?
""", (value, key))
print(f"{EMOJI['INFO']} {Fore.CYAN}Updating {key.split('/')[-1]}...{Style.RESET_ALL}")
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('auth.updating_pair')} {key.split('/')[-1]}...{Style.RESET_ALL}")
cursor.execute("COMMIT")
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}{self.translator.get('auth.database_updated_successfully')}{Style.RESET_ALL}")
@@ -101,14 +101,14 @@ class CursorAuth:
raise e
except sqlite3.Error as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED}{self.translator.get('auth.database_error', error=str(e))}{Style.RESET_ALL}")
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.database_error', error=str(e))}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED}{self.translator.get('auth.an_error_occurred', error=str(e))}{Style.RESET_ALL}")
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.an_error_occurred', error=str(e))}{Style.RESET_ALL}")
return False
finally:
if conn:
conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN}{self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

View File

@@ -102,7 +102,7 @@ class CursorRegistration:
# 创建新的浏览器实例用于注册
from browser import BrowserManager
signup_browser_manager = BrowserManager(noheader=True)
signup_browser_manager = BrowserManager(noheader=False)
self.signup_tab = signup_browser_manager.init_browser()
# 访问注册页面
@@ -144,10 +144,10 @@ class CursorRegistration:
# 获取验证码设置60秒超时
verification_code = None
max_attempts = 10 # 增加到10次尝试
retry_interval = 5 # 每5秒重试一次
max_attempts = 20 # 增加到10次尝试
retry_interval = 10 # 每5秒重试一次
start_time = time.time()
timeout = 60 # 60秒超时
timeout = 160 # 60秒超时
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.start_getting_verification_code')}...{Style.RESET_ALL}")
@@ -246,56 +246,87 @@ class CursorRegistration:
def _handle_turnstile(self):
"""处理 Turnstile 验证"""
print(f"{Fore.YELLOW}{EMOJI['VERIFY']} {self.translator.get('register.handle_turnstile')}...{Style.RESET_ALL}")
# 设置最大等待时间(秒)
max_wait_time = 10 # 增加等待时间
start_time = time.time()
while True:
try:
# 检查是否超时
if time.time() - start_time > max_wait_time:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.no_turnstile')}...{Style.RESET_ALL}")
time.sleep(2)
break
try:
print(f"{Fore.YELLOW}{EMOJI['VERIFY']} {self.translator.get('register.handle_turnstile')}...{Style.RESET_ALL}")
max_retries = 2
retry_interval = (1, 2)
retry_count = 0
while retry_count < max_retries:
retry_count += 1
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.turnstile_attempt', attempt=retry_count)}...{Style.RESET_ALL}")
try:
challengeCheck = (
self.signup_tab.ele("@id=cf-turnstile", timeout=1)
# 尝试重置 turnstile
self.signup_tab.run_js("try { turnstile.reset() } catch(e) { }")
time.sleep(2)
# 定位验证框元素
challenge_check = (
self.signup_tab.ele("@id=cf-turnstile", timeout=2)
.child()
.shadow_root.ele("tag:iframe")
.ele("tag:body")
.sr("tag:input")
)
if challengeCheck:
challengeCheck.click()
time.sleep(3)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}")
if challenge_check:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.turnstile_detected')}...{Style.RESET_ALL}")
# 随机延时后点击验证
time.sleep(random.uniform(1, 3))
challenge_check.click()
time.sleep(2)
break
except:
pass
try:
if (self.signup_tab.ele("@name=password", timeout=0.5) or
self.signup_tab.ele("@name=email", timeout=0.5) or
self.signup_tab.ele("@data-index=0", timeout=0.5)):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}")
time.sleep(2)
break
except:
pass
time.sleep(1)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.error', error=str(e))}{Style.RESET_ALL}")
time.sleep(2)
break
time.sleep(2)
# 检查验证结果
if self._check_verification_success():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('register.turnstile_attempt_failed', error=str(e))}{Style.RESET_ALL}")
# 检查是否已经验证成功
if self._check_verification_success():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}")
return True
# 随机延时后继续下一次尝试
time.sleep(random.uniform(*retry_interval))
# 超出最大重试次数
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.turnstile_max_retries', max=max_retries)}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.turnstile_error', error=str(e))}{Style.RESET_ALL}")
return False
def _check_verification_success(self):
"""检查验证是否成功"""
try:
# 检查是否存在后续表单元素,这表示验证已通过
if (self.signup_tab.ele("@name=password", timeout=0.5) or
self.signup_tab.ele("@name=email", timeout=0.5) or
self.signup_tab.ele("@data-index=0", timeout=0.5) or
self.signup_tab.ele("Account Settings", timeout=0.5)):
return True
# 检查是否出现错误消息
error_messages = [
'xpath://div[contains(text(), "Can\'t verify the user is human")]',
'xpath://div[contains(text(), "Error: 600010")]',
'xpath://div[contains(text(), "Please try again")]'
]
for error_xpath in error_messages:
if self.signup_tab.ele(error_xpath):
return False
return False
except:
return False
def _get_account_info(self):
"""获取账户信息和 Token"""

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -45,10 +45,29 @@
"updating_pair": "Updating Key-Value Pair",
"sqlite_success": "SQLite Database Updated Successfully",
"sqlite_error": "SQLite Database Update Failed: {error}",
"press_enter": "Press Enter to Exit"
"press_enter": "Press Enter to Exit",
"unsupported_os": "Unsupported OS: {os}",
"linux_path_not_found": "Linux Path Not Found",
"updating_system_ids": "Updating System IDs",
"system_ids_updated": "System IDs Updated Successfully",
"system_ids_update_failed": "System IDs Update Failed: {error}",
"windows_guid_updated": "Windows GUID Updated Successfully",
"windows_permission_denied": "Windows Permission Denied",
"windows_guid_update_failed": "Windows GUID Update Failed",
"macos_uuid_updated": "macOS UUID Updated Successfully",
"plutil_command_failed": "plutil Command Failed",
"start_patching": "Starting Patching getMachineId",
"macos_uuid_update_failed": "macOS UUID Update Failed",
"current_version": "Current Cursor Version: {version}",
"patch_completed": "Patching getMachineId Completed",
"patch_failed": "Patching getMachineId Failed: {error}",
"version_check_passed": "Cursor Version Check Passed",
"file_modified": "File Modified"
},
"register": {
"title": "Cursor Registration Tool",
"start": "Starting Registration Process",
"mailbox": "Successfully Entered Mailbox",
"register_start": "Start Register",
@@ -107,7 +126,8 @@
"reset_machine_id": "Reset Machine ID",
"database_connection_closed": "Database Connection Closed",
"database_updated_successfully": "Database Updated Successfully",
"connected_to_database": "Connected to Database"
"connected_to_database": "Connected to Database",
"updating_pair": "Updating Key-Value Pair"
},
"control": {
"generate_email": "Generating New Email",
@@ -136,6 +156,8 @@
"get_cursor_session_token": "Get Cursor Session Token",
"get_cursor_session_token_success": "Get Cursor Session Token Success",
"get_cursor_session_token_failed": "Get Cursor Session Token Failed",
"save_token_failed": "Save Token Failed"
"save_token_failed": "Save Token Failed",
"database_updated_successfully": "Database Updated Successfully",
"database_connection_closed": "Database Connection Closed"
}
}

View File

@@ -26,6 +26,44 @@
"timeout": "以下进程未能在规定时间内关闭: {pids}",
"error": "关闭 Cursor 进程时发生错误: {error}"
},
"reset": {
"title": "Cursor 机器标识重置工具",
"checking": "检查配置文件",
"not_found": "配置文件未找到",
"no_permission": "无法读取或写入配置文件,请检查文件权限",
"reading": "读取当前配置",
"creating_backup": "创建配置备份",
"backup_exists": "备份文件已存在,跳过备份步骤",
"generating": "生成新机器标识",
"saving_json": "保存新配置到JSON",
"success": "机器标识重置成功",
"new_id": "新机器标识",
"permission_error": "权限错误: {error}",
"run_as_admin": "请尝试以管理员身份运行此程序",
"process_error": "重置进程错误: {error}",
"updating_sqlite": "更新SQLite数据库",
"updating_pair": "更新键值对",
"sqlite_success": "SQLite数据库更新成功",
"sqlite_error": "SQLite数据库更新失败: {error}",
"press_enter": "按回车键退出",
"updating_system_ids": "更新系统ID",
"system_ids_updated": "系统ID更新成功",
"system_ids_update_failed": "系统ID更新失败: {error}",
"unsupported_os": "不支持的操作系统: {os}",
"linux_path_not_found": "Linux路径未找到",
"windows_guid_updated": "Windows GUID更新成功",
"windows_permission_denied": "Windows权限拒绝",
"windows_guid_update_failed": "Windows GUID更新失败",
"macos_uuid_updated": "macOS UUID更新成功",
"plutil_command_failed": "plutil命令失败",
"macos_uuid_update_failed": "macOS UUID更新失败",
"start_patching": "开始修补getMachineId",
"current_version": "当前Cursor版本: {version}",
"patch_completed": "getMachineId修补完成",
"patch_failed": "getMachineId修补失败: {error}",
"version_check_passed": "Cursor版本检查通过",
"file_modified": "文件已修改"
},
"register": {
"title": "Cursor 注册工具",
"start": "开始注册流程",
@@ -68,7 +106,8 @@
"account_info_saved": "账户信息已保存",
"save_account_info_failed": "保存账户信息失败",
"get_email_address": "获取邮箱地址",
"register_process_error": "注册流程错误: {error}"
"register_process_error": "注册流程错误: {error}",
"update_cursor_auth_info": "更新Cursor认证信息"
},
"auth": {
"title": "Cursor 认证管理器",
@@ -81,7 +120,11 @@
"auth_update_failed": "认证信息更新失败: {error}",
"auth_file_created": "认证文件已创建",
"auth_file_create_failed": "认证文件创建失败: {error}",
"press_enter": "按回车键退出"
"press_enter": "按回车键退出",
"connected_to_database": "已连接到数据库",
"database_updated_successfully": "数据库更新成功",
"database_connection_closed": "数据库连接已关闭",
"updating_pair": "更新键值对"
},
"control": {
"generate_email": "生成新邮箱",

View File

@@ -26,6 +26,48 @@
"timeout": "以下進程未能在規定時間內關閉: {pids}",
"error": "關閉 Cursor 進程時發生錯誤: {error}"
},
"reset": {
"title": "Cursor 機器標識重置工具",
"checking": "檢查配置文件",
"not_found": "配置文件未找到",
"no_permission": "無法讀取或寫入配置文件,請檢查文件權限",
"reading": "讀取當前配置",
"creating_backup": "創建配置備份",
"backup_exists": "備份文件已存在,跳過備份步驟",
"generating": "生成新機器標識",
"saving_json": "保存新配置到JSON",
"success": "機器標識重置成功",
"new_id": "新機器標識",
"permission_error": "權限錯誤: {error}",
"run_as_admin": "請嘗試以管理員身份運行此程序",
"process_error": "重置進程錯誤: {error}",
"updating_sqlite": "更新SQLite數據庫",
"updating_pair": "更新鍵值對",
"sqlite_success": "SQLite數據庫更新成功",
"sqlite_error": "SQLite數據庫更新失敗: {error}",
"press_enter": "按回車鍵退出",
"updating_system_ids": "更新系統ID",
"system_ids_updated": "系統ID更新成功",
"system_ids_update_failed": "系統ID更新失敗: {error}",
"unsupported_os": "不支持的操作系統: {os}",
"linux_path_not_found": "Linux路徑未找到",
"windows_guid_updated": "Windows GUID更新成功",
"windows_permission_denied": "Windows權限拒絕",
"windows_guid_update_failed": "Windows GUID更新失敗",
"macos_uuid_updated": "macOS UUID更新成功",
"plutil_command_failed": "plutil命令失敗",
"macos_uuid_update_failed": "macOS UUID更新失敗",
"start_patching": "開始修補getMachineId",
"current_version": "當前Cursor版本: {version}",
"patch_completed": "getMachineId修補完成",
"patch_failed": "getMachineId修補失敗: {error}",
"version_check_passed": "Cursor版本檢查通過",
"file_modified": "文件已修改"
},
"register": {
"title": "Cursor 註冊工具",
"start": "開始註冊流程",
@@ -68,7 +110,8 @@
"account_info_saved": "賬戶信息已保存",
"save_account_info_failed": "保存賬戶信息失敗",
"get_email_address": "獲取郵箱地址",
"register_process_error": "註冊流程錯誤: {error}"
"register_process_error": "註冊流程錯誤: {error}",
"update_cursor_auth_info": "更新Cursor認證信息"
},
"auth": {
"title": "Cursor 認證管理器",
@@ -81,7 +124,11 @@
"auth_update_failed": "認證信息更新失敗: {error}",
"auth_file_created": "認證文件已創建",
"auth_file_create_failed": "認證文件創建失敗: {error}",
"press_enter": "按回車鍵退出"
"press_enter": "按回車鍵退出",
"connected_to_database": "已連接到數據庫",
"database_updated_successfully": "數據庫更新成功",
"database_connection_closed": "數據庫連接已關閉",
"updating_pair": "更新鍵值對"
},
"control": {
"generate_email": "生成新郵箱",

11
logo.py
View File

@@ -2,8 +2,14 @@ from colorama import Fore, Style, init
from dotenv import load_dotenv
import os
# 加載環境變量獲取版本號
load_dotenv()
# 獲取當前腳本所在目錄
current_dir = os.path.dirname(os.path.abspath(__file__))
# 構建.env文件的完整路徑
env_path = os.path.join(current_dir, '.env')
# 加載環境變量,指定.env文件路徑
load_dotenv(env_path)
# 獲取版本號,如果未找到則使用默認值
version = os.getenv('VERSION', '1.0.0')
# 初始化 colorama
@@ -29,6 +35,5 @@ CURSOR_LOGO = f"""
def print_logo():
print(CURSOR_LOGO)
if __name__ == "__main__":
print_logo()

16
main.py
View File

@@ -42,11 +42,17 @@ class Translator:
def get(self, key, **kwargs):
"""获取翻译文本"""
keys = key.split('.')
value = self.translations.get(self.current_language, {})
for k in keys:
value = value.get(k, key)
return value.format(**kwargs) if kwargs else value
try:
keys = key.split('.')
value = self.translations.get(self.current_language, {})
for k in keys:
if isinstance(value, dict):
value = value.get(k, key)
else:
return key # 如果中間值不是字典返回原始key
return value.format(**kwargs) if kwargs else value
except Exception:
return key # 出現任何錯誤時返回原始key
def set_language(self, lang_code):
"""设置当前语言"""

View File

@@ -5,7 +5,11 @@ import uuid
import hashlib
import shutil
import sqlite3
import platform
import re
import tempfile
from colorama import Fore, Style, init
from typing import Tuple
# 初始化colorama
init()
@@ -20,6 +24,173 @@ EMOJI = {
"RESET": "🔄",
}
def get_cursor_paths(translator=None) -> Tuple[str, str]:
"""根据不同操作系统获取 Cursor 相关路径"""
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:
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"]),
)
def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
"""版本号检查"""
version_pattern = r"^\d+\.\d+\.\d+$"
try:
if not re.match(version_pattern, version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False
def parse_version(ver: str) -> Tuple[int, ...]:
return tuple(map(int, ver.split(".")))
current = parse_version(version)
if min_version and current < parse_version(min_version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_low', version=version, min_version=min_version)}{Style.RESET_ALL}")
return False
if max_version and current > parse_version(max_version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_high', version=version, max_version=max_version)}{Style.RESET_ALL}")
return False
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_check_error', error=str(e))}{Style.RESET_ALL}")
return False
def check_cursor_version(translator) -> bool:
"""检查 Cursor 版本"""
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)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
return False
def modify_main_js(main_path: str, translator) -> bool:
"""修改 main.js 文件"""
try:
original_stat = os.stat(main_path)
original_mode = original_stat.st_mode
original_uid = original_stat.st_uid
original_gid = original_stat.st_gid
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
with open(main_path, "r", encoding="utf-8") as main_file:
content = main_file.read()
patterns = {
r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}",
r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}",
}
for pattern, replacement in patterns.items():
content = re.sub(pattern, replacement, content)
tmp_file.write(content)
tmp_path = tmp_file.name
shutil.copy2(main_path, main_path + ".old")
shutil.move(tmp_path, main_path)
os.chmod(main_path, original_mode)
if os.name != "nt":
os.chown(main_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
os.unlink(tmp_path)
return False
def patch_cursor_get_machine_id(translator) -> bool:
"""修补 Cursor getMachineId 函数"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
# 获取路径
pkg_path, main_path = get_cursor_paths(translator)
# 检查文件权限
for file_path in [pkg_path, main_path]:
if not os.path.isfile(file_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
return False
if not os.access(file_path, os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
return False
# 获取版本号
try:
with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"]
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.current_version', version=version)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
return False
# 检查版本
if not version_check(version, min_version="0.45.0", translator=translator):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
# 备份文件
backup_path = main_path + ".bak"
if not os.path.exists(backup_path):
shutil.copy2(main_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
# 修改文件
if not modify_main_js(main_path, translator):
return False
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.patch_completed')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.patch_failed', error=str(e))}{Style.RESET_ALL}")
return False
class MachineIDResetter:
def __init__(self, translator=None):
self.translator = translator
@@ -98,7 +269,7 @@ class MachineIDResetter:
INSERT OR REPLACE INTO ItemTable (key, value)
VALUES (?, ?)
""", (key, value))
print(f"{EMOJI['INFO']} {Fore.CYAN}{self.translator.get('reset.updating_pair')}: {key}{Style.RESET_ALL}")
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('reset.updating_pair')}: {key}{Style.RESET_ALL}")
conn.commit()
conn.close()
@@ -109,6 +280,59 @@ class MachineIDResetter:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.sqlite_error', error=str(e))}{Style.RESET_ALL}")
return False
def update_system_ids(self, new_ids):
"""更新系统级别的ID"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
if sys.platform.startswith("win"):
self._update_windows_machine_guid()
elif sys.platform == "darwin":
self._update_macos_platform_uuid(new_ids)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.system_ids_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def _update_windows_machine_guid(self):
"""更新Windows MachineGuid"""
try:
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Cryptography",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
new_guid = str(uuid.uuid4())
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key)
print("Windows MachineGuid updated successfully")
except PermissionError:
print("Permission denied: Run as administrator to update Windows MachineGuid")
raise
except Exception as e:
print(f"Failed to update Windows MachineGuid: {e}")
raise
def _update_macos_platform_uuid(self, new_ids):
"""更新macOS Platform UUID"""
try:
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
if os.path.exists(uuid_file):
# 使用sudo来执行plutil命令
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
result = os.system(cmd)
if result == 0:
print("macOS Platform UUID updated successfully")
else:
raise Exception("Failed to execute plutil command")
except Exception as e:
print(f"Failed to update macOS Platform UUID: {e}")
raise
def reset_machine_ids(self):
"""重置机器ID并备份原文件"""
try:
@@ -136,14 +360,27 @@ class MachineIDResetter:
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
new_ids = self.generate_new_ids()
# 更新配置文件
config.update(new_ids)
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
with open(self.db_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=4)
# 更新SQLite数据库
self.update_sqlite_db(new_ids)
# 更新系统ID
self.update_system_ids(new_ids)
# 检查 Cursor 版本并执行相应的操作
greater_than_0_45 = check_cursor_version(self.translator)
if greater_than_0_45:
print(f"{Fore.CYAN}{EMOJI['INFO']} 检测到 Cursor 版本 >= 0.45.0,正在修补 getMachineId...{Style.RESET_ALL}")
patch_cursor_get_machine_id(self.translator)
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Cursor 版本 < 0.45.0,跳过 getMachineId 修补{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}")
for key, value in new_ids.items():
@@ -160,18 +397,17 @@ class MachineIDResetter:
return False
def run(translator=None):
"""Main function to be called from main.py"""
"""便捷函数,用于直接调用重置功能"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
resetter = MachineIDResetter(translator)
resetter = MachineIDResetter(translator) # 正確傳遞 translator
resetter.reset_machine_ids()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('reset.press_enter')}...")
if __name__ == "__main__":
# 如果直接运行,使用默认翻译器
from main import translator as main_translator
run(main_translator)