Compare commits

...

129 Commits

Author SHA1 Message Date
Pin Studios
74be8a0a77 refactor: Simplify profile selection process in OAuthHandler
- Removed the user data directory retrieval in main.py for cleaner profile selection.
- Updated profile selection logic to include an exit option in oauth_auth.py.
- Enhanced user prompts in multiple languages to improve user experience during profile selection.
2025-04-05 19:10:21 +08:00
Pin Studios
bce205b252 chore: Update README image size for better visibility
- Increased the width of the image in README.md from 400px to 800px to enhance visual clarity and presentation.
2025-04-05 18:49:05 +08:00
Pin Studios
8fcf3b62cf chore: Update README with new image and improve visual content
- Replaced the old image with a new one in the README.md to enhance visual appeal.
- Added the new image file 'pro_2025-04-05_18-47-56.png' to the images directory.
2025-04-05 18:48:28 +08:00
Pin Studios
eeaee073cf feat: Add multilingual feature request templates for improved user engagement
- Introduced new feature request templates in both English and Chinese to enhance user experience and encourage suggestions.
- Removed the old single-language feature request template to streamline the process.
2025-04-05 18:40:12 +08:00
Pin Studios
2f353da381 Fix: Address issues with custom Chrome installations in CHANGELOG.md 2025-04-05 18:37:29 +08:00
Pin Studios
efd1417407 Merge pull request #480 from handwerk2016/main
[Windows] Fix for custom Chrome Installations
2025-04-05 18:36:25 +08:00
Pin Studios
2cc7cce283 Refactor OAuthHandler for improved Chrome profile management
- Introduced methods to retrieve and select available Chrome profiles.
- Enhanced error handling for profile loading and selection.
- Removed unnecessary Linux-specific checks and subprocess calls.
- Streamlined browser setup process to utilize selected profiles.
- Improved account usage limit detection with a dedicated function.
- Cleaned up code by removing deprecated methods and comments.
2025-04-05 18:36:00 +08:00
Pin Studios
4c30afcd7f chore: update version to 1.8.06 in .env file 2025-04-05 18:31:33 +08:00
Pin Studios
bfb2f5e95a Merge pull request #494 from saadtahir995/main
feat: Added Google OAuth Acc Deletion Tool
2025-04-05 18:30:57 +08:00
Pin Studios
35e01edf9c feat: Enhance account deletion process with improved translations and error handling
- Added multilingual support for account deletion messages in delete_cursor_google.py and oauth_auth.py
- Updated CHANGELOG.md to reflect new features and fixes
- Improved user prompts and error messages for better clarity and user experience
2025-04-05 18:30:44 +08:00
Pin Studios
df9e88131b Merge pull request #469 from Nigel1992/feat/feature-request-template-new
feat: add comprehensive feature request issue template [EN only]
2025-04-05 18:16:20 +08:00
Pin Studios
8a4d22103e Merge pull request #492 from Lucaszmv/fix/usage-limits-check
Fix: Improve usage limits check and tuple index error
2025-04-05 18:16:02 +08:00
Pin Studios
1244d86994 Merge pull request #489 from Hexoso/main
Fix: bug in disable cursor auto update
2025-04-05 18:14:56 +08:00
Pin Studios
a2833fcbd4 fix: handle permission errors in auto-update disabling process and enhance localization messages 2025-04-05 18:14:42 +08:00
Saad
b3e7c101d3 feat: Added Google OAuth Acc Deletion Tool 2025-04-05 12:16:02 +05:00
Sadegh Barzegar
0a779063bf chore: modify disable auto update & config 2025-04-05 05:01:46 +03:30
Lucas
c96433e446 Fix: Improve usage limits check and tuple index error 2025-04-04 17:23:27 -03:00
Sadegh Barzegar
5604f5073d fix: bug in disable cursor update 2025-04-04 12:19:14 +03:30
Alex
3cad68be36 Update oauth_auth.py
changed locale
2025-04-04 09:31:54 +03:00
Pin Studios
d8799cdf2e Merge pull request #485 from moelbanna-bot/main
fix-linux-appimage
2025-04-04 06:12:59 +08:00
Pin Studios
3e5c22b816 Merge pull request #479 from vauns/main
feat: Add support for custom Cursor installation paths on Windows
2025-04-04 06:12:10 +08:00
Pin Studios
893c3f8a52 Merge pull request #467 from Nigel1992/feat/chrome-profile-selection
feat(oauth): add Chrome profile selection feature
2025-04-04 06:10:07 +08:00
Mohamed Elbanna
edb74e21be Enhance Linux cursor path detection to include extracted AppImage directories and add debug output for found paths 2025-04-03 15:47:58 +02:00
Mohamed Elbanna
be52ce98a6 Improve Linux cursor path detection to include extracted AppImage with correct usr structure and add debug output for found paths 2025-04-03 15:20:38 +02:00
Mohamed Elbanna
163748fd96 Refactor Linux path detection to use extracted AppImage with correct usr structure 2025-04-03 14:55:56 +02:00
Mohamed Elbanna
f888b8c90c Enhance Linux path detection for Cursor AppImage installations 2025-04-03 14:35:07 +02:00
Alex
3c12561997 [Windows] Fix for Custom Chrome Installations
This commit fixes that error when you had an custom chrome installation. For example: D:\Chromium or something.
Now it uses windows PATH to start chrome and if there's no PATH installation script would check default location.

Here's demo for current bug: https://youtu.be/O8lPblnBIGQ
2025-04-03 10:55:45 +03:00
吕海龙
80aab8741f feat: Add support for custom Cursor installation paths on Windows
- Modify base_path detection to use custom paths from config
- Replace forward slashes with backslashes in Windows path definition
2025-04-03 15:42:10 +08:00
Nigel
2595c4c95d Merge branch 'yeongpin:main' into feat/feature-request-template-new 2025-04-03 01:11:38 +02:00
Nigel1992
f18e65f391 feat(template): add feature request issue template 2025-04-03 01:10:19 +02:00
Nigel1992
671307b53b fix(oauth): remove duplicate browser data directory message 2025-04-03 01:02:57 +02:00
Nigel1992
f58bb70d3a fix(oauth): fix translation keys in Chrome profile selection 2025-04-03 01:01:58 +02:00
Nigel1992
5bfe653a92 fix(oauth): remove duplicate Chrome warning message 2025-04-03 01:00:38 +02:00
Nigel1992
a66a0e5395 feat(oauth): add Chrome profile selection feature
- Add new menu option to select which Chrome profile to use

- Display actual profile names from Chrome's Local State

- Add safety warning and confirmation before closing Chrome

- Add translations for all supported languages
2025-04-03 00:51:04 +02:00
Pin Studios
ea44218a8a Update CHANGELOG.md 2025-04-03 02:09:29 +08:00
Pin Studios
fdc8317380 Update .env 2025-04-03 02:07:49 +08:00
Pin Studios
523daea54e Merge pull request #463 from blobs0/patch-1
Patch Linux Path Not Found
2025-04-03 02:06:52 +08:00
Pin Studios
5a4e32f3f4 Merge pull request #460 from Lucaszmv/fix/oauth-account-usage-limit
fix: improve account usage limit detection
2025-04-03 02:06:09 +08:00
Valentin Guerdin
d91485ec75 Patch Linux Path Not Found
add folder /usr/lib/cursor/app/
2025-04-02 14:32:27 +02:00
Lucas
51cef9c2b2 fix: improve account usage limit detection
- Add support for detecting both 150/150 and 50/50 usage limits
- Improve usage parsing and validation
- Add better error handling for usage detection
- Add more descriptive log messages
2025-04-01 15:42:05 -03:00
yeongpin
6e6180e331 fix: update reset emoji and correct button patterns for cross-platform compatibility 2025-03-31 17:49:35 +08:00
yeongpin
e2d7c1c496 chore: update version to 1.8.04 and enhance CHANGELOG with new features and fixes 2025-03-31 12:35:35 +08:00
yeongpin
a56b978669 feat: add new error messages for browser process handling in English, Simplified Chinese, and Traditional Chinese locales 2025-03-31 12:33:25 +08:00
Pin Studios
9793b91bc7 Merge pull request #437 from Nigel1992/fix/433-linux-chrome-visibility
fix: improve Linux Chrome visibility and root user handling
2025-03-31 12:00:11 +08:00
yeongpin
735dd8c1eb refactor: enhance user feedback and error messages with translation support across multiple modules 2025-03-31 11:58:54 +08:00
Pin Studios
e5d192efcb Merge pull request #434 from BasaiCorp/main
Update totally_reset_cursor.py face 1 wait for face 3
2025-03-31 11:23:52 +08:00
Pin Studios
a4d5b0e467 Update totally_reset_cursor.py
Fix & Add Locale
2025-03-31 11:23:31 +08:00
Nigel1992
d7b056b339 fix: improve Linux Chrome visibility and root user handling 2025-03-30 22:27:08 +02:00
Nigel1992
bd152be4e8 fix: improve Linux path handling and fix permission issues 2025-03-30 22:14:03 +02:00
Prathmesh Barot
172d9fb4bb Update totally_reset_cursor.py face 1 wait for face 3
1/3
2025-03-30 23:23:10 +05:30
Pin Studios
e2d70c1738 Update CHANGELOG.md 2025-03-31 01:03:13 +08:00
Pin Studios
650e2d33db Update .env 2025-03-31 01:01:02 +08:00
Pin Studios
2d1347c8c6 Merge pull request #430 from Lostata/main
Fixed grammar errors in README.md
2025-03-31 01:00:37 +08:00
Pin Studios
67d3554664 Merge pull request #426 from Nigel1992/fix/412-case-insensitive-cursor
Fix: improve Linux path handling and add case-insensitive Cursor directory detection
2025-03-31 00:59:50 +08:00
yeongpin
cb202e08e5 refactor: enhance configuration error messages with translation support and improve user feedback 2025-03-31 00:58:51 +08:00
Lostata
0c0ec47432 Fixed grammar errors in README.md 2025-03-30 09:05:46 +03:00
Nigel1992
f00678ddcb fix: add case-insensitive handling for Cursor/cursor directory 2025-03-29 23:59:04 +01:00
Nigel1992
dbc220053d fix: improve Linux path handling for config file detection 2025-03-29 23:53:45 +01:00
yeongpin
d7ab362c4c Update README.md to reflect support for the latest 0.48.x version and add new configuration options for checking updates and displaying account information. 2025-03-29 22:30:59 +08:00
yeongpin
4448a62458 Update CHANGELOG.md to reflect the reorganization of features and fixes for version 1.8.02, including the addition of new configuration options and enhancements to the temporary email functionality. 2025-03-29 22:29:27 +08:00
yeongpin
50d09e5172 Update CHANGELOG.md to reflect the addition of a new temporary email feature, and fixes for Linux Chrome issues and other minor problems. 2025-03-29 22:23:16 +08:00
yeongpin
ffff3bdeb1 Update block_domain.txt to include corhash.net and mailshou.com; modify new_signup.py and new_tempemail.py to conditionally set the --no-sandbox argument for Linux platforms. 2025-03-29 22:22:43 +08:00
yeongpin
816a09d4de Refactor NewTempEmail class to enhance email creation and verification processes, including improved browser setup and error handling. Add new_tempemail_api.py to .gitignore. 2025-03-29 22:18:11 +08:00
yeongpin
e5b7e5727c Enhance NewTempEmail functionality by integrating random service selection, improving email creation process with retries, and implementing API calls for domain and message handling. Add new_tempemail_smail.py to .gitignore. 2025-03-29 22:15:33 +08:00
yeongpin
087e3ebf78 Add solerbe.net to block_domain.txt 2025-03-29 21:59:06 +08:00
yeongpin
bd96107911 Format new GUID in reset_machine_manual.py to include braces for consistency. 2025-03-29 21:55:21 +08:00
yeongpin
17799e0209 Add dugmail.com to block_domain.txt 2025-03-29 21:52:51 +08:00
yeongpin
c15ea25cb3 Update version to 1.8.02 in .env and CHANGELOG.md, adding new features and fixes including disabling auto-update, configuration options, and contributors options. 2025-03-29 21:49:57 +08:00
yeongpin
350d781ce8 Update default version in build.yml to 1.8.02 2025-03-28 23:49:32 +08:00
yeongpin
4485cc5571 Add support for 'Free Trial' membership type in format_subscription_type function 2025-03-28 23:47:24 +08:00
yeongpin
df58b2e4ab Update version to 1.8.01 in .env and CHANGELOG.md, reflecting new features and fixes. 2025-03-28 23:42:28 +08:00
yeongpin
5f380ebe5e Refactor reset_machine_manual.py to remove deprecated comments and add new error message translations for file modification failures in English, Simplified Chinese, and Traditional Chinese locales. 2025-03-28 23:41:19 +08:00
yeongpin
c97bfd1475 Update CHANGELOG.md with specific Linux Chrome fix, add no-sandbox option in new_tempemail.py, and uncomment workbench path modification in reset_machine_manual.py. 2025-03-28 23:33:48 +08:00
yeongpin
28cd662e83 Update CHANGELOG.md, fix logo centering, and enhance totally reset cursor functionality. Revert cursor reset to beta, add new confirmation and display functions, and improve file handling for cursor-related paths. 2025-03-28 23:27:00 +08:00
Pin Studios
1b6ba5eab8 Merge pull request #409 from lulavc/main
updated the text in
2025-03-28 22:45:02 +08:00
Luiz Henrique
3e3cd40e3f Merge branch 'yeongpin:main' into main 2025-03-28 10:25:43 -03:00
Luiz Henrique
8f35f163c5 Update README.md 2025-03-28 10:24:49 -03:00
yeongpin
f6ffb18427 Update configuration path for update.yml to app-update.yml in setup_config function. 2025-03-28 19:29:57 +08:00
yeongpin
b6bf62f841 Update configuration paths for update.yml and add new translation key for 'create_block_file_failed' in English, Simplified Chinese, and Traditional Chinese locales. 2025-03-28 19:28:46 +08:00
yeongpin
1c1174fa6c Add new translation key for 'remove_directory_failed' in English, Simplified Chinese, and Traditional Chinese locales to enhance error messaging consistency. 2025-03-28 19:26:58 +08:00
yeongpin
4587fd9373 Update version to 1.7.19, add Cursor Account Info feature, and enhance configuration handling for updater paths. Update CHANGELOG.md with new features and fixes. 2025-03-28 19:24:55 +08:00
Luiz Henrique
51fcf83ebb Merge branch 'yeongpin:main' into main 2025-03-28 00:40:09 -03:00
Luiz Henrique
5cc7630ce6 Update README.md 2025-03-28 00:39:21 -03:00
Pin Studios
bd9e10056f Merge pull request #394 from distributedStorage/tiny-fix
fix: inconsistent UI message
2025-03-26 18:43:16 +08:00
Pin Studios
36f5356b4a Merge pull request #387 from AliAsgharRanjbar/main
fix: modified the change language option number
2025-03-26 18:43:01 +08:00
imbajin
18fc0532fd fix: inconsistent message 2025-03-26 18:24:10 +08:00
Ali Asghar Ranjbar
7405c61cc9 fix: modified the change language option number 2025-03-26 08:26:56 +03:30
Pin Studios
80e9946294 Update CHANGELOG.md 2025-03-25 16:12:04 +08:00
Pin Studios
4ef293da81 Update .env 2025-03-25 06:52:29 +08:00
Pin Studios
eb6ef6bb3b Update CHANGELOG.md 2025-03-25 06:51:28 +08:00
Pin Studios
3700239c99 Merge pull request #373 from Nigel1992/fix/linux-path-detection
fix: Improve Linux path detection and config handling
2025-03-25 06:48:28 +08:00
yeongpin
a8966de771 Add new translation key for 'path_not_found' in English and Chinese (Simplified and Traditional) locales, ensuring consistent error messaging across languages. 2025-03-25 06:37:20 +08:00
Nigel1992
8943266f6e fix: Improve Linux path detection and config handling 2025-03-24 23:01:24 +01:00
yeongpin
35ed9cb6f6 Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-23 21:13:11 +08:00
yeongpin
3d1450ced0 Update CHANGELOG for v1.7.18 with fixes for write permissions and other issues. Add new translation keys for error messages in English and Chinese (Simplified and Traditional) locales. 2025-03-23 21:13:08 +08:00
Pin Studios
892fc85efa Merge pull request #362 from lulavc/main
Added Spanish translation
2025-03-23 11:50:52 +08:00
Luiz Henrique
feaf9906ba Added Spanish translation 2025-03-22 23:14:23 -03:00
Luiz Henrique
568e38e70c Added spanish 2025-03-22 23:10:03 -03:00
Pin Studios
ab7281921e Update README.md 2025-03-23 08:48:29 +08:00
yeongpin
58b4b0dece Update version to 1.7.17, add 'fixed soon' translation key in English and Chinese (Simplified and Traditional), and modify CHANGELOG to reflect recent fixes and updates. 2025-03-23 08:34:50 +08:00
yeongpin
07bee3207d Add new display features and warnings to CHANGELOG, fix cursor reset message, and remake logo centering for improved clarity and user experience. 2025-03-22 21:33:23 +08:00
yeongpin
06a9d47dad Update warning message translation key for improved clarity in user feedback. 2025-03-22 21:22:15 +08:00
yeongpin
048a69be8f Update cursor reset messages for improved clarity and user feedback. Modify translation strings in English and Chinese (Simplified and Traditional) locales to reflect completed reset status and cancellation notifications. 2025-03-22 21:21:09 +08:00
yeongpin
0b2081175d Refactor cursor reset process to include translator support in file and directory deletion functions. Enhance user feedback with new messages for removal failures and deep scanning operations in English and Chinese (Simplified and Traditional) locales. 2025-03-22 21:18:29 +08:00
yeongpin
eb31209b6a Enhance logo formatting and add warning emoji to improve user interface. Update feature display and confirmation prompts for better clarity and user experience. 2025-03-22 20:39:25 +08:00
yeongpin
7a239d3348 Refactor cursor reset functionality to enhance user experience with improved prompts and error handling. Update translation support for various messages and warnings in English, Chinese (Simplified and Traditional). Change function calls in main.py to align with updated definitions in totally_reset_cursor.py. 2025-03-22 20:27:39 +08:00
yeongpin
a9eed9818c Update README to replace outdated image with a new screenshot and add the new image file for improved visual representation. 2025-03-22 19:54:35 +08:00
yeongpin
0260d6be04 Update version to 1.7.16, add Bulgarian language support, enhance GitHub + Cursor AI registration automation, and improve user prompts. Update CHANGELOG with new features and fixes. 2025-03-22 19:42:49 +08:00
BasaiCorp
cff7e05e4e Merge remote changes with local modifications 2025-03-22 15:26:24 +05:30
BasaiCorp
9fe6d1ae2c fix and added some things still in beta 2025-03-22 15:23:30 +05:30
yeongpin
b3f905acb6 Update input choice prompt to include options 0-10 for improved user interaction. 2025-03-22 17:17:57 +08:00
yeongpin
492482199a Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-22 16:55:44 +08:00
yeongpin
846a044704 Add webdriver_manager to requirements.txt for improved browser driver management 2025-03-22 16:55:16 +08:00
Pin Studios
b131281515 Update build.yml 2025-03-22 16:32:06 +08:00
yeongpin
b797d93db5 Update requirements.txt to include selenium package for enhanced browser automation capabilities. 2025-03-22 16:31:21 +08:00
yeongpin
e0a7afd835 Update version to 1.7.15 and enhance GitHub + Cursor AI registration automation. Added Turkish language support, improved user confirmation prompts, and optimized logging. Updated CHANGELOG with new features and fixes. 2025-03-22 16:14:27 +08:00
Pin Studios
902f6bd6f8 Merge pull request #351 from muhammedfurkan/patch-1
Create  Turkish lang
2025-03-22 15:21:50 +08:00
M.Furkan
56cdeaa116 Create tr.json 2025-03-22 10:19:29 +03:00
Pin Studios
225a24aad5 Merge pull request #348 from BasaiCorp/main
Comprehensive Enhancements to Cursor AI Reset and Registration Automation Scripts
2025-03-22 11:05:13 +08:00
Pin Studios
6464306958 Update README.md 2025-03-22 08:47:11 +08:00
BasaiCorp
5d70214a2f Update github_cursor_register.py Stable
Working and usable final
2025-03-21 22:07:40 +05:30
BasaiCorp
854e987927 Update totally_reset_cursor.py Stable
Working and Stable
2025-03-21 22:06:01 +05:30
Pin Studios
8f47801dad Merge pull request #347 from BasaiCorp/main
 Major Enhancements: Improved Cursor AI Reset & Automated GitHub + Cursor Registration
2025-03-21 23:44:59 +08:00
BasaiCorp
05e4d7faa9 Update github_cursor_register.py stable v1.0
stable and test both.
2025-03-21 20:49:09 +05:30
BasaiCorp
34f5c679a5 Update github_cursor_register.py test 0.1.2 2025-03-21 20:47:37 +05:30
BasaiCorp
f90a2916b1 Create github_cursor_register.py test 0.1.0
test now may be need time and this change and determinate all other tools and this tool will be favourite of all users
2025-03-21 20:46:26 +05:30
BasaiCorp
7b757c2d57 Update totally_reset_cursor.py test v0.2 2025-03-21 20:38:53 +05:30
BasaiCorp
209c58e3f8 Update totally_reset_cursor.py with new locations and test v0.1 2025-03-21 20:37:12 +05:30
Pin Studios
caa47fad03 Update CHANGELOG.md 2025-03-21 09:49:47 +08:00
38 changed files with 6001 additions and 1174 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.7.14
VERSION=1.7.14
version=1.8.06
VERSION=1.8.06

View File

@@ -0,0 +1,75 @@
name: 💡 功能建议
description: 为这个项目提出新的想法
title: "[功能建议]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
感谢您抽出时间提出新功能建议!
请确保填写以下所有部分。
- type: textarea
id: feature-description
attributes:
label: 功能描述
description: 清晰简洁地描述您想要实现的功能。
placeholder: "我希望能够..."
validations:
required: true
- type: textarea
id: problem-solution
attributes:
label: 问题和解决方案
description: 描述您试图解决的问题,以及这个功能将如何帮助解决。
placeholder: "目前,当我尝试..."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: 考虑过的替代方案
description: 描述您考虑过的任何替代解决方案或功能。
placeholder: "我考虑过..."
- type: dropdown
id: priority
attributes:
label: 优先级
description: 这个功能对您来说有多重要?
options:
- 高(对我的工作流程至关重要)
- 中(会很有帮助)
- 低(有更好)
validations:
required: true
- type: dropdown
id: impact
attributes:
label: 影响范围
description: 有多少用户会从这个功能中受益?
options:
- 所有用户
- 大多数用户
- 部分用户
- 仅我个人
validations:
required: true
- type: textarea
id: technical-details
attributes:
label: 技术细节
description: 您想分享的任何技术考虑或实现细节。
placeholder: "这个功能可以通过..."
- type: textarea
id: screenshots
attributes:
label: 截图
description: 如果适用,添加截图以帮助解释您的功能建议。
placeholder: "您可以在此处拖放截图..."
- type: checkboxes
id: terms
attributes:
label: 行为准则
description: 提交此问题即表示您同意遵守本项目的行为准则
options:
- label: 我同意遵守本项目的行为准则
required: true

View File

@@ -0,0 +1,75 @@
name: 💡 Feature Request
description: Suggest an idea for this project
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to suggest a new feature!
Please make sure to fill out all the sections below.
- type: textarea
id: feature-description
attributes:
label: Feature Description
description: A clear and concise description of what you want to happen.
placeholder: "I would like to..."
validations:
required: true
- type: textarea
id: problem-solution
attributes:
label: Problem & Solution
description: Describe the problem you're trying to solve and how this feature would help.
placeholder: "Currently, when I try to..."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Describe any alternative solutions or features you've considered.
placeholder: "I've considered..."
- type: dropdown
id: priority
attributes:
label: Priority
description: How important is this feature to you?
options:
- High (Critical for my workflow)
- Medium (Would be very helpful)
- Low (Nice to have)
validations:
required: true
- type: dropdown
id: impact
attributes:
label: Impact
description: How many users would benefit from this feature?
options:
- All users
- Most users
- Some users
- Just me
validations:
required: true
- type: textarea
id: technical-details
attributes:
label: Technical Details
description: Any technical considerations or implementation details you'd like to share.
placeholder: "The feature could be implemented by..."
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your feature.
placeholder: "You can drag and drop screenshots here..."
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow this project's Code of Conduct
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

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

2
.gitignore vendored
View File

@@ -14,6 +14,8 @@ build.py
build.sh
ENV/
test.py
new_tempemail_smail.py
new_tempemail_api.py
install.bat
run.bat

View File

@@ -1,5 +1,89 @@
# Change Log
## v1.8.06
1. Add: Google Account Deletion Feature | 添加 Google 账号删除功能
2. Update: Menu with new account deletion option | 更新菜单添加账号删除选项
3. Add: Multilanguage support for account deletion | 添加账号删除功能的多语言支持
4. Fix: Improve usage limits check and tuple index error | 修复使用限制检查和元组索引错误
5. Fix: bug in disable cursor auto update | 修复禁用 Cursor 自动更新的错误
6. Fix: Linux-appimage | 修复 Linux-appimage 问题
7. Add: Support for custom Cursor installation paths on Windows | 添加 Windows 系统下自定义 Cursor 安装路径支持
8. Add: Chrome profile selection feature | 添加 Chrome 配置文件选择功能
9. Fix: improve account usage limit detection | 修復賬號檢測
10. Fix: For custom Chrome Installations | 修復自定義chrome遊覽器安裝
## v1.8.05
1. Fix: Linux Path Not Found 修復linuxpath問題
2. Add: support for detecting both 150/150 and 50/50 usage limits 添加偵測50 或者150的使用量
3. Improve: usage parsing and validation 檢測使用量
## v1.8.04
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
4. Fix: Some Issues | 修復一些問題
## v1.8.03
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
2. Fix: Some Issues | 修復一些問題
## v1.8.02
1. Add: New Temp Email | 增加新臨時郵箱
2. Add: Config Options | 增加配置選項
3. Add: Update Windows Machine ID | 增加更新 Windows 機器 ID
4. Add: Contributors Options | 增加貢獻者選項
5. Add: Check update enable Options In config | 增加在 config 中檢查更新選項
6. Add: Show account info enabled options in config | 增加在 config 中顯示賬號信息選項
7. Optimize Row & Colume Options | 優化行與列選項
8. Fix: Too Many Free Trial On Some Machine | 修復某些機器上太多免費試用
9. Fix: Disable Auto Update | 修復禁用自動更新
10. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
11. Fix: Some Issues | 修復一些問題
## v1.8.01
1. Add: Cursor Account Info | 增加 Cursor 賬號信息
2. Fix: Disable Auto Update | 修復禁用自動更新
3. Add: 0.48.x Version Support | 增加 0.48.x 版本支持
4. Revert: Totally Reser Cursor to Beta | 恢復完全重置 Cursor 到 Beta
5. Reopen: Totally Reset Cursor | 重新開啟完全重置 Cursor
6. Fix: Logo.py Center | 修復 Logo.py 居中
7. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
8. Fix: Some Issues | 修復一些問題
## v1.7.18
1. Fix: No Write Permission | 修復沒有寫入權限
2. Fix: Improve Linux path detection and config handling 修正 linux 路徑和config寫入讀取
3. Fix: Locale path_no_exist missing 修正 path_no_exist 語言遺失
4. Fix: Some Issues | 修復一些問題
## v1.7.17
1. Fix: Remove 10 options Totally Reset Cursor | 修復完全重置 Cursor 選項
## v1.7.16
1. Add bulgarian language | 增加保加利亚语
2. Fix: Some Issues | 修復一些問題
3. Add: Contributors | 增加貢獻者
4. Fix: Total Reset Cursor | 修復完全重置 Cursor
5. Add: Display Features and Warnings | 增加顯示功能與警告
6. Fix: Totally Reset Cursor | 修復完全重置 Cursor
7. Remake: Logo.py Center | 重做 Logo.py 居中
## v1.7.15
1. Fix: Cant Verify the User is Human | 修復無法驗證用戶是否為人類
2. Added temporary email & GitHub + Cursor AI registration automation | 增加临时邮箱 & GitHub + Cursor AI 注册自动化
3. Added Turkish language support | 增加土耳其语支持
4. Removed outdated temporary option in Option 2 | 移除选项2中的过期临时添加项
5. Enhanced machine ID reset (Linux, Windows, macOS), bypasses Cursor free trial detection | 机器 ID 重置支持 Linux/Windows/macOS绕过 Cursor 免费试用检测
6. Expanded Cursor AI file detection, deep removal of leftover trial files | 扩展 Cursor AI 文件检测,深度清理残留试用文件
7. Optimized logging, replaced print with logging module, added verification steps | 日志优化,统一采用 logging 模块,增加验证步骤提示
8. Added retry mechanism for email verification | 增加邮箱验证重试机制
9. Automated GitHub OAuth login for Cursor AI | 自动 GitHub OAuth 登录 Cursor AI
10. Saved registered GitHub accounts and timestamps | 保存 GitHub 账户和注册时间戳
11. Added user confirmation before execution | 添加用户确认步骤,防止误操作
12. Displayed feature list & warnings before actions | 显示功能与风险警告
13. Improved Selenium flow and error handling | 增强 Selenium 流程与错误处理
14. Added Chrome process tracking and cleanup | 增加 Chrome 进程跟踪和清理
## v1.7.14
1. Added a Russian locale to program, fixed a typo in readme.md. Also translated other files
為程式新增了俄語語系,修正了 readme.md 的拼寫錯誤,並翻譯了其他文件。
@@ -21,8 +105,6 @@
6. These changes align with Linux filesystem conventions and fix issues with Chrome/Chromium integration
這些變更符合 Linux 文件系統的規範,並修復了與 Chrome/Chromium 整合的問題。
**#337**
7. This PR adds retry logic to handle the 'Can't verify the user is human' error during registration
此 PR 新增了重試機制,以處理註冊時的「無法驗證用戶是否為人類」錯誤。

View File

@@ -10,18 +10,24 @@
[![Release](https://img.shields.io/github/v/release/yeongpin/cursor-free-vip?style=flat-square&logo=github&color=blue)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
[![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![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)
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a1)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p>
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
This tool registers accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes, resets and wipes Cursor data and hardware info.
Supports Windows, macOS and Linux.
For optimal performance, run with privileges and always stay up to date.
Always clean your browser's cache and cookies. If possible, use a VPN to create new accounts.
This is a tool to automatically register, support Windows and macOS systems, complete Auth verification, and reset
Cursor's configuration.
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
<p align="center">
<img src="./images/new_2025-03-19_00-19-09.png" alt="new" width="400" style="border-radius: 6px;"/><br>
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
</p>
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
@@ -48,6 +54,8 @@ Cursor's configuration.
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
* Delete Cursor Google Account<br>删除 Cursor Google 账号<br>
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
## 💻 System Support | 系統支持
@@ -94,7 +102,7 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
</details>
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
## ❗ Note | 注意事項
@@ -109,9 +117,9 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
[Turnstile]
# Handle Tuenstile Wait Time | 等待人機驗證時間
# Handle Turnstile Wait Time | 等待人機驗證時間
handle_turnstile_time = 2
# Handle Tuenstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
# Handle Turnstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
handle_turnstile_random_time = 1-3
[OSPaths]
@@ -152,11 +160,17 @@ failed_retry_time = 0.5-1
retry_interval = 8-12
# Max Timeout | 最大超時時間
max_timeout = 160
[Utils]
# Check Update | 檢查更新
check_update = True
# Show Account Info | 顯示賬號信息
show_account_info = True
```
</details>
* Use administrator to run the script <br>請使用管理員身份運行腳本
* Use administrator privileges to run the script <br>請使用管理員身份運行腳本
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
@@ -176,7 +190,7 @@ max_timeout = 160
<a href="https://github.com/yeongpin/cursor-free-vip/graphs/contributors">
<img src="https://contrib.rocks/image?repo=yeongpin/cursor-free-vip" />
<img src="https://contrib.rocks/image?repo=yeongpin/cursor-free-vip&preview=true&max=&columns=" />
</a>
<br /><br />

View File

@@ -16,3 +16,7 @@ ikomail.com
mailpull.com
drewzen.com
begemail.com
dugmail.com
solerbe.net
corhash.net
mailshou.com

0
build.sh Normal file → Executable file
View File

View File

@@ -33,6 +33,8 @@ a = Analysis(
('new_tempemail.py', '.'),
('quit_cursor.py', '.'),
('cursor_register_manual.py', '.'),
('oauth_auth.py', '.'),
('utils.py', '.'),
('.env', '.'),
('block_domain.txt', '.')
],
@@ -42,7 +44,9 @@ a = Analysis(
'new_signup',
'new_tempemail',
'quit_cursor',
'cursor_register_manual'
'cursor_register_manual',
'oauth_auth',
'utils'
],
hookspath=[],
hooksconfig={},

175
config.py
View File

@@ -4,6 +4,18 @@ import configparser
from colorama import Fore, Style
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
EMOJI = {
"INFO": "",
"WARNING": "⚠️",
"ERROR": "",
"SUCCESS": "",
"ADMIN": "🔒",
"ARROW": "➡️",
"USER": "👤",
"KEY": "🔑",
"SETTINGS": "⚙️"
}
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
@@ -37,6 +49,10 @@ def setup_config(translator=None):
'failed_retry_time': '0.5-1',
'retry_interval': '8-12',
'max_timeout': '160'
},
'Utils': {
'enabled_update_check': 'True',
'enabled_account_info': 'True'
}
}
@@ -49,26 +65,130 @@ def setup_config(translator=None):
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
'updater_path': os.path.join(localappdata, "cursor-updater")
'updater_path': os.path.join(localappdata, "cursor-updater"),
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml"),
'product_json_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json")
}
# Create storage directory
os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True)
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"),
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater")
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"),
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml",
'product_json_path': "/Applications/Cursor.app/Contents/Resources/app/product.json"
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
# Create storage directory
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
elif sys.platform == "linux":
# Get the actual user's home directory, handling both sudo and normal cases
sudo_user = os.environ.get('SUDO_USER')
current_user = sudo_user if sudo_user else (os.getenv('USER') or os.getenv('USERNAME'))
if not current_user:
current_user = os.path.expanduser('~').split('/')[-1]
# Handle sudo case
if sudo_user:
actual_home = f"/home/{sudo_user}"
root_home = "/root"
else:
actual_home = f"/home/{current_user}"
root_home = None
if not os.path.exists(actual_home):
actual_home = os.path.expanduser("~")
# Define base config directory
config_base = os.path.join(actual_home, ".config")
# Try both "Cursor" and "cursor" directory names in both user and root locations
cursor_dir = None
possible_paths = [
os.path.join(config_base, "Cursor"),
os.path.join(config_base, "cursor"),
os.path.join(root_home, ".config", "Cursor") if root_home else None,
os.path.join(root_home, ".config", "cursor") if root_home else None
]
for path in possible_paths:
if path and os.path.exists(path):
cursor_dir = path
break
if not cursor_dir:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base) if translator else f'Neither Cursor nor cursor directory found in {config_base}'}{Style.RESET_ALL}")
if root_home:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.also_checked', path=f'{root_home}/.config') if translator else f'Also checked {root_home}/.config'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
# Define Linux paths using the found cursor directory
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
storage_dir = os.path.dirname(storage_path) if storage_path else ""
# Verify paths and permissions
try:
# Check storage directory
if storage_dir and not os.path.exists(storage_dir):
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir) if translator else f'Storage directory not found: {storage_dir}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
# Check storage.json with more detailed verification
if storage_path and os.path.exists(storage_path):
# Get file stats
try:
stat = os.stat(storage_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path) if translator else f'Storage file found: {storage_path}'}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', size=stat.st_size) if translator else f'File size: {stat.st_size} bytes'}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', permissions=oct(stat.st_mode & 0o777)) if translator else f'File permissions: {oct(stat.st_mode & 0o777)}'}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', owner=stat.st_uid) if translator else f'File owner: {stat.st_uid}'}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', group=stat.st_gid) if translator else f'File group: {stat.st_gid}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e)) if translator else f'Error getting file stats: {str(e)}'}{Style.RESET_ALL}")
# Check if file is readable and writable
if not os.access(storage_path, os.R_OK | os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path) if translator else f'Permission denied: {storage_path}'}{Style.RESET_ALL}")
if sudo_user:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {sudo_user}:{sudo_user} {storage_path}') if translator else f'Try running: chown {sudo_user}:{sudo_user} {storage_path}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {current_user}:{current_user} {storage_path}') if translator else f'Try running: chown {current_user}:{current_user} {storage_path}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
# Try to read the file to verify it's not corrupted
try:
with open(storage_path, 'r') as f:
content = f.read()
if not content.strip():
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path) if translator else f'Storage file is empty: {storage_path}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted, please reinstall Cursor'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data') if translator else 'Storage file is valid and contains data'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e)) if translator else f'Error reading storage file: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted. Please reinstall Cursor'}{Style.RESET_ALL}")
elif storage_path:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path) if translator else f'Storage file not found: {storage_path}'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
except (OSError, IOError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e)) if translator else f'Error checking Linux paths: {str(e)}'}{Style.RESET_ALL}")
# Define all paths using the found cursor directory
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"),
'storage_path': storage_path,
'sqlite_path': os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/state.vscdb")) if cursor_dir else "",
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
'cursor_path': get_linux_cursor_path(),
'updater_path': os.path.expanduser("~/.config/cursor-updater")
'updater_path': os.path.join(config_base, "cursor-updater"),
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else "",
'product_json_path': os.path.join(cursor_dir, "resources/app/product.json") if cursor_dir else ""
}
# Read existing configuration and merge
@@ -85,13 +205,13 @@ def setup_config(translator=None):
config.set(section, option, str(value))
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.config_option_added', option=f'{section}.{option}') if translator else f'Config option added: {section}.{option}'}{Style.RESET_ALL}")
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN} {translator.get('register.config_updated')}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_updated') if translator else 'Config updated'}{Style.RESET_ALL}")
else:
for section, options in default_config.items():
config.add_section(section)
@@ -101,16 +221,41 @@ def setup_config(translator=None):
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_created', config_file=config_file) if translator else f'Config created: {config_file}'}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
print(f"{Fore.RED} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ Error setting up config: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.config_setup_error', error=str(e)) if translator else f'Error setting up config: {str(e)}'}{Style.RESET_ALL}")
return None
def print_config(config, translator=None):
"""Print configuration in a readable format"""
if not config:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available') if translator else 'Configuration not available'}{Style.RESET_ALL}")
return
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration') if translator else 'Configuration'}:{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
for section in config.sections():
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
for key, value in config.items(section):
# 对布尔值进行特殊处理,使其显示为彩色
if value.lower() in ('true', 'yes', 'on', '1'):
value_display = f"{Fore.GREEN}{translator.get('config.enabled') if translator else 'Enabled'}{Style.RESET_ALL}"
elif value.lower() in ('false', 'no', 'off', '0'):
value_display = f"{Fore.RED}{translator.get('config.disabled') if translator else 'Disabled'}{Style.RESET_ALL}"
else:
value_display = value
print(f" {key} = {value_display}")
print(f"\n{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory') if translator else 'Config Directory'}: {config_dir}{Style.RESET_ALL}")
print()
def get_config(translator=None):
"""Get existing config or create new one"""

552
cursor_acc_info.py Normal file
View File

@@ -0,0 +1,552 @@
import os
import sys
import json
import requests
import sqlite3
from typing import Dict, Optional
import platform
from colorama import Fore, Style, init
import logging
import re
# Initialize colorama
init()
# Setup logger
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Define emoji constants
EMOJI = {
"USER": "👤",
"USAGE": "📊",
"PREMIUM": "",
"BASIC": "📝",
"SUBSCRIPTION": "💳",
"INFO": "",
"ERROR": "",
"SUCCESS": "",
"WARNING": "⚠️",
"TIME": "🕒"
}
class Config:
"""Config"""
NAME_LOWER = "cursor"
NAME_CAPITALIZE = "Cursor"
BASE_HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Accept": "application/json",
"Content-Type": "application/json"
}
class UsageManager:
"""Usage Manager"""
@staticmethod
def get_proxy():
"""get proxy"""
# from config import get_config
proxy = os.environ.get("HTTP_PROXY") or os.environ.get("HTTPS_PROXY")
if proxy:
return {"http": proxy, "https": proxy}
return None
@staticmethod
def get_usage(token: str) -> Optional[Dict]:
"""get usage"""
url = f"https://www.{Config.NAME_LOWER}.com/api/usage"
headers = Config.BASE_HEADERS.copy()
headers.update({"Cookie": f"Workos{Config.NAME_CAPITALIZE}SessionToken=user_01OOOOOOOOOOOOOOOOOOOOOOOO%3A%3A{token}"})
try:
proxies = UsageManager.get_proxy()
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
response.raise_for_status()
data = response.json()
# get Premium usage and limit
gpt4_data = data.get("gpt-4", {})
premium_usage = gpt4_data.get("numRequestsTotal", 0)
max_premium_usage = gpt4_data.get("maxRequestUsage", 999)
# get Basic usage, but set limit to "No Limit"
gpt35_data = data.get("gpt-3.5-turbo", {})
basic_usage = gpt35_data.get("numRequestsTotal", 0)
return {
'premium_usage': premium_usage,
'max_premium_usage': max_premium_usage,
'basic_usage': basic_usage,
'max_basic_usage': "No Limit" # set Basic limit to "No Limit"
}
except requests.RequestException as e:
# only log error
logger.error(f"Get usage info failed: {str(e)}")
return None
except Exception as e:
# catch all other exceptions
logger.error(f"Get usage info failed: {str(e)}")
return None
@staticmethod
def get_stripe_profile(token: str) -> Optional[Dict]:
"""get user subscription info"""
url = f"https://api2.{Config.NAME_LOWER}.sh/auth/full_stripe_profile"
headers = Config.BASE_HEADERS.copy()
headers.update({"Authorization": f"Bearer {token}"})
try:
proxies = UsageManager.get_proxy()
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
logger.error(f"Get subscription info failed: {str(e)}")
return None
def get_token_from_config():
"""get path info from config"""
try:
from config import get_config
config = get_config()
if not config:
return None
system = platform.system()
if system == "Windows" and config.has_section('WindowsPaths'):
return {
'storage_path': config.get('WindowsPaths', 'storage_path'),
'sqlite_path': config.get('WindowsPaths', 'sqlite_path'),
'session_path': os.path.join(os.getenv("APPDATA"), "Cursor", "Session Storage")
}
elif system == "Darwin" and config.has_section('MacPaths'): # macOS
return {
'storage_path': config.get('MacPaths', 'storage_path'),
'sqlite_path': config.get('MacPaths', 'sqlite_path'),
'session_path': os.path.expanduser("~/Library/Application Support/Cursor/Session Storage")
}
elif system == "Linux" and config.has_section('LinuxPaths'):
return {
'storage_path': config.get('LinuxPaths', 'storage_path'),
'sqlite_path': config.get('LinuxPaths', 'sqlite_path'),
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
}
except Exception as e:
logger.error(f"Get config path failed: {str(e)}")
return None
def get_token_from_storage(storage_path):
"""get token from storage.json"""
if not os.path.exists(storage_path):
return None
try:
with open(storage_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# try to get accessToken
if 'cursorAuth/accessToken' in data:
return data['cursorAuth/accessToken']
# try other possible keys
for key in data:
if 'token' in key.lower() and isinstance(data[key], str) and len(data[key]) > 20:
return data[key]
except Exception as e:
logger.error(f"get token from storage.json failed: {str(e)}")
return None
def get_token_from_sqlite(sqlite_path):
"""get token from sqlite"""
if not os.path.exists(sqlite_path):
return None
try:
conn = sqlite3.connect(sqlite_path)
cursor = conn.cursor()
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%token%'")
rows = cursor.fetchall()
conn.close()
for row in rows:
try:
value = row[0]
if isinstance(value, str) and len(value) > 20:
return value
# try to parse JSON
data = json.loads(value)
if isinstance(data, dict) and 'token' in data:
return data['token']
except:
continue
except Exception as e:
logger.error(f"get token from sqlite failed: {str(e)}")
return None
def get_token_from_session(session_path):
"""get token from session"""
if not os.path.exists(session_path):
return None
try:
# try to find all possible session files
for file in os.listdir(session_path):
if file.endswith('.log'):
file_path = os.path.join(session_path, file)
try:
with open(file_path, 'rb') as f:
content = f.read().decode('utf-8', errors='ignore')
# find token pattern
token_match = re.search(r'"token":"([^"]+)"', content)
if token_match:
return token_match.group(1)
except:
continue
except Exception as e:
logger.error(f"get token from session failed: {str(e)}")
return None
def get_token():
"""get Cursor token"""
# get path from config
paths = get_token_from_config()
if not paths:
return None
# try to get token from different locations
token = get_token_from_storage(paths['storage_path'])
if token:
return token
token = get_token_from_sqlite(paths['sqlite_path'])
if token:
return token
token = get_token_from_session(paths['session_path'])
if token:
return token
return None
def format_subscription_type(subscription_data: Dict) -> str:
"""format subscription type"""
if not subscription_data:
return "Free"
# handle new API response format
if "membershipType" in subscription_data:
membership_type = subscription_data.get("membershipType", "").lower()
subscription_status = subscription_data.get("subscriptionStatus", "").lower()
if subscription_status == "active":
if membership_type == "pro":
return "Pro"
elif membership_type == "free_trial":
return "Free Trial"
elif membership_type == "pro_trial":
return "Pro Trial"
elif membership_type == "team":
return "Team"
elif membership_type == "enterprise":
return "Enterprise"
elif membership_type:
return membership_type.capitalize()
else:
return "Active Subscription"
elif subscription_status:
return f"{membership_type.capitalize()} ({subscription_status})"
# compatible with old API response format
subscription = subscription_data.get("subscription")
if subscription:
plan = subscription.get("plan", {}).get("nickname", "Unknown")
status = subscription.get("status", "unknown")
if status == "active":
if "pro" in plan.lower():
return "Pro"
elif "pro_trial" in plan.lower():
return "Pro Trial"
elif "free_trial" in plan.lower():
return "Free Trial"
elif "team" in plan.lower():
return "Team"
elif "enterprise" in plan.lower():
return "Enterprise"
else:
return plan
else:
return f"{plan} ({status})"
return "Free"
def get_email_from_storage(storage_path):
"""get email from storage.json"""
if not os.path.exists(storage_path):
return None
try:
with open(storage_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# try to get email
if 'cursorAuth/cachedEmail' in data:
return data['cursorAuth/cachedEmail']
# try other possible keys
for key in data:
if 'email' in key.lower() and isinstance(data[key], str) and '@' in data[key]:
return data[key]
except Exception as e:
logger.error(f"get email from storage.json failed: {str(e)}")
return None
def get_email_from_sqlite(sqlite_path):
"""get email from sqlite"""
if not os.path.exists(sqlite_path):
return None
try:
conn = sqlite3.connect(sqlite_path)
cursor = conn.cursor()
# try to query records containing email
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%email%' OR key LIKE '%cursorAuth%'")
rows = cursor.fetchall()
conn.close()
for row in rows:
try:
value = row[0]
# if it's a string and contains @, it might be an email
if isinstance(value, str) and '@' in value:
return value
# try to parse JSON
try:
data = json.loads(value)
if isinstance(data, dict):
# check if there's an email field
if 'email' in data:
return data['email']
# check if there's a cachedEmail field
if 'cachedEmail' in data:
return data['cachedEmail']
except:
pass
except:
continue
except Exception as e:
logger.error(f"get email from sqlite failed: {str(e)}")
return None
def display_account_info(translator=None):
"""display account info"""
print(f"\n{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['USER']} {translator.get('account_info.title') if translator else 'Cursor Account Information'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
# get token
token = get_token()
if not token:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.token_not_found') if translator else 'Token not found. Please login to Cursor first.'}{Style.RESET_ALL}")
return
# get path info
paths = get_token_from_config()
if not paths:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.config_not_found') if translator else 'Configuration not found.'}{Style.RESET_ALL}")
return
# get email info - try multiple sources
email = get_email_from_storage(paths['storage_path'])
# if not found in storage, try from sqlite
if not email:
email = get_email_from_sqlite(paths['sqlite_path'])
# get subscription info
try:
subscription_info = UsageManager.get_stripe_profile(token)
except Exception as e:
logger.error(f"Get subscription info failed: {str(e)}")
subscription_info = None
# if not found in storage and sqlite, try from subscription info
if not email and subscription_info:
# try to get email from subscription info
if 'customer' in subscription_info and 'email' in subscription_info['customer']:
email = subscription_info['customer']['email']
# get usage info - silently handle errors
try:
usage_info = UsageManager.get_usage(token)
except Exception as e:
logger.error(f"Get usage info failed: {str(e)}")
usage_info = None
# Prepare left and right info
left_info = []
right_info = []
# Left side shows account info
if email:
left_info.append(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
else:
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
# Add an empty line
# left_info.append("")
# Show subscription type
if subscription_info:
subscription_type = format_subscription_type(subscription_info)
left_info.append(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
# Show remaining trial days
days_remaining = subscription_info.get("daysRemainingOnTrial")
if days_remaining is not None and days_remaining > 0:
left_info.append(f"{Fore.GREEN}{EMOJI['TIME']} {translator.get('account_info.trial_remaining') if translator else 'Remaining Pro Trial'}: {Fore.WHITE}{days_remaining} {translator.get('account_info.days') if translator else 'days'}{Style.RESET_ALL}")
else:
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.subscription_not_found') if translator else 'Subscription information not found'}{Style.RESET_ALL}")
# Right side shows usage info - only if available
if usage_info:
right_info.append(f"{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
# Premium usage
premium_usage = usage_info.get('premium_usage', 0)
max_premium_usage = usage_info.get('max_premium_usage', "No Limit")
# make sure the value is not None
if premium_usage is None:
premium_usage = 0
# handle "No Limit" case
if isinstance(max_premium_usage, str) and max_premium_usage == "No Limit":
premium_color = Fore.GREEN # when there is no limit, use green
premium_display = f"{premium_usage}/{max_premium_usage}"
else:
# calculate percentage when the value is a number
if max_premium_usage is None or max_premium_usage == 0:
max_premium_usage = 999
premium_percentage = 0
else:
premium_percentage = (premium_usage / max_premium_usage) * 100
# select color based on usage percentage
premium_color = Fore.GREEN
if premium_percentage > 70:
premium_color = Fore.YELLOW
if premium_percentage > 90:
premium_color = Fore.RED
premium_display = f"{premium_usage}/{max_premium_usage} ({premium_percentage:.1f}%)"
right_info.append(f"{Fore.YELLOW}{EMOJI['PREMIUM']} {translator.get('account_info.premium_usage') if translator else 'Fast Response'}: {premium_color}{premium_display}{Style.RESET_ALL}")
# Slow Response
basic_usage = usage_info.get('basic_usage', 0)
max_basic_usage = usage_info.get('max_basic_usage', "No Limit")
# make sure the value is not None
if basic_usage is None:
basic_usage = 0
# handle "No Limit" case
if isinstance(max_basic_usage, str) and max_basic_usage == "No Limit":
basic_color = Fore.GREEN # when there is no limit, use green
basic_display = f"{basic_usage}/{max_basic_usage}"
else:
# calculate percentage when the value is a number
if max_basic_usage is None or max_basic_usage == 0:
max_basic_usage = 999
basic_percentage = 0
else:
basic_percentage = (basic_usage / max_basic_usage) * 100
# select color based on usage percentage
basic_color = Fore.GREEN
if basic_percentage > 70:
basic_color = Fore.YELLOW
if basic_percentage > 90:
basic_color = Fore.RED
basic_display = f"{basic_usage}/{max_basic_usage} ({basic_percentage:.1f}%)"
right_info.append(f"{Fore.BLUE}{EMOJI['BASIC']} {translator.get('account_info.basic_usage') if translator else 'Slow Response'}: {basic_color}{basic_display}{Style.RESET_ALL}")
else:
# if get usage info failed, only log in log, not show in interface
# you can choose to not show any usage info, or show a simple prompt
# right_info.append(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_info.usage_unavailable') if translator else 'Usage information unavailable'}{Style.RESET_ALL}")
pass # not show any usage info
# Calculate the maximum display width of left info
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
def get_display_width(s):
"""Calculate the display width of a string, considering Chinese characters and emojis"""
# Remove ANSI color codes
clean_s = ansi_escape.sub('', s)
width = 0
for c in clean_s:
# Chinese characters and some emojis occupy two character widths
if ord(c) > 127:
width += 2
else:
width += 1
return width
max_left_width = 0
for item in left_info:
width = get_display_width(item)
max_left_width = max(max_left_width, width)
# Set the starting position of right info
fixed_spacing = 4 # Fixed spacing
right_start = max_left_width + fixed_spacing
# Calculate the number of spaces needed for right info
spaces_list = []
for i in range(len(left_info)):
if i < len(left_info):
left_item = left_info[i]
left_width = get_display_width(left_item)
spaces = right_start - left_width
spaces_list.append(spaces)
# Print info
max_rows = max(len(left_info), len(right_info))
for i in range(max_rows):
# Print left info
if i < len(left_info):
left_item = left_info[i]
print(left_item, end='')
# Use pre-calculated spaces
spaces = spaces_list[i]
else:
# If left side has no items, print only spaces
spaces = right_start
print('', end='')
# Print right info
if i < len(right_info):
print(' ' * spaces + right_info[i])
else:
print() # Change line
print(f"{Fore.CYAN}{'' * 70}{Style.RESET_ALL}")
def main(translator=None):
"""main function"""
try:
display_account_info(translator)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.error') if translator else 'Error'}: {str(e)}{Style.RESET_ALL}")
if __name__ == "__main__":
main()

386
delete_cursor_google.py Normal file
View File

@@ -0,0 +1,386 @@
from oauth_auth import OAuthHandler
import time
from colorama import Fore, Style, init
import sys
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'DELETE': '🗑️',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
class CursorGoogleAccountDeleter(OAuthHandler):
def __init__(self, translator=None):
super().__init__(translator, auth_type='google')
def delete_google_account(self):
"""Delete Cursor account using Google OAuth"""
try:
# Setup browser and select profile
if not self.setup_browser():
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.starting_process') if self.translator else 'Starting account deletion process...'}{Style.RESET_ALL}")
# Navigate to Cursor auth page - using the same URL as in registration
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(2)
# Click Google auth button using same selectors as in registration
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn:
break
except:
continue
if not auth_btn:
raise Exception(self.translator.get('account_delete.google_button_not_found') if self.translator else "Google login button not found")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.logging_in') if self.translator else 'Logging in with Google...'}{Style.RESET_ALL}")
auth_btn.click()
# Wait for authentication to complete using a more robust method
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('account_delete.waiting_for_auth', fallback='Waiting for Google authentication...')}{Style.RESET_ALL}")
# Dynamic wait for authentication
max_wait_time = 120 # Increase maximum wait time to 120 seconds
start_time = time.time()
check_interval = 3 # Check every 3 seconds
google_account_alert_shown = False # Track if we've shown the alert already
while time.time() - start_time < max_wait_time:
current_url = self.browser.url
# If we're already on the settings or dashboard page, we're successful
if "/dashboard" in current_url or "/settings" in current_url or "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.login_successful') if self.translator else 'Login successful'}{Style.RESET_ALL}")
break
# If we're on Google accounts page or accounts.google.com, wait for user selection
if "accounts.google.com" in current_url:
# Only show the alert once to avoid spamming
if not google_account_alert_shown:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.select_google_account', fallback='Please select your Google account...')}{Style.RESET_ALL}")
# Alert to indicate user action needed
try:
self.browser.run_js("""
alert('Please select your Google account to continue with Cursor authentication');
""")
google_account_alert_shown = True # Mark that we've shown the alert
except:
pass # Alert is optional
# Sleep before checking again
time.sleep(check_interval)
else:
# If the loop completed without breaking, it means we hit the timeout
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.auth_timeout', fallback='Authentication timeout, continuing anyway...')}{Style.RESET_ALL}")
# Check current URL to determine next steps
current_url = self.browser.url
# If we're already on the settings page, no need to navigate
if "/settings" in current_url and "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.already_on_settings', fallback='Already on settings page')}{Style.RESET_ALL}")
# If we're on the dashboard or any Cursor page but not settings, navigate to settings
elif "cursor.com" in current_url or "authenticator.cursor.sh" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.navigating_to_settings', fallback='Navigating to settings page...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# If we're still on Google auth or somewhere else, try directly going to settings
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.login_redirect_failed', fallback='Login redirection failed, trying direct navigation...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# Wait for the settings page to load
time.sleep(3) # Reduced from 5 seconds
# First look for the email element to confirm we're logged in
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.found_email', email=email, fallback=f'Found email: {email}')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.email_not_found', error=str(e), fallback=f'Email not found: {str(e)}')}{Style.RESET_ALL}")
# Click on "Advanced" tab or dropdown - keep only the successful approach
advanced_found = False
# Direct JavaScript querySelector approach that worked according to logs
try:
advanced_element_js = self.browser.run_js("""
// Try to find the Advanced dropdown using querySelector with the exact classes
let advancedElement = document.querySelector('div.mb-0.flex.cursor-pointer.items-center.text-xs:not([style*="display: none"])');
// If not found, try a more general approach
if (!advancedElement) {
const allDivs = document.querySelectorAll('div');
for (const div of allDivs) {
if (div.textContent.includes('Advanced') &&
div.className.includes('mb-0') &&
div.className.includes('flex') &&
div.className.includes('cursor-pointer')) {
advancedElement = div;
break;
}
}
}
// Click the element if found
if (advancedElement) {
advancedElement.click();
return true;
}
return false;
""")
if advanced_element_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.advanced_tab_clicked', fallback='Found and clicked Advanced using direct JavaScript selector')}{Style.RESET_ALL}")
advanced_found = True
time.sleep(1) # Reduced from 2 seconds
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.advanced_tab_error', error=str(e), fallback='JavaScript querySelector approach failed: {str(e)}')}{Style.RESET_ALL}")
if not advanced_found:
# Fallback to direct URL navigation which is faster and more reliable
try:
self.browser.get("https://www.cursor.com/settings?tab=advanced")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('account_delete.direct_advanced_navigation', fallback='Trying direct navigation to advanced tab')}{Style.RESET_ALL}")
advanced_found = True
except:
raise Exception(self.translator.get('account_delete.advanced_tab_not_found') if self.translator else "Advanced option not found after multiple attempts")
# Wait for dropdown/tab content to load
time.sleep(2) # Reduced from 4 seconds
# Find and click the "Delete Account" button
delete_button_found = False
# Simplified approach for delete button based on what worked
delete_button_selectors = [
'xpath://button[contains(., "Delete Account")]',
'xpath://button[text()="Delete Account"]',
'xpath://div[contains(text(), "Delete Account")]',
'xpath://button[contains(text(), "Delete") and contains(text(), "Account")]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.delete_button_clicked') if self.translator else 'Clicked on Delete Account button'}{Style.RESET_ALL}")
delete_button_found = True
break
except:
continue
if not delete_button_found:
raise Exception(self.translator.get('account_delete.delete_button_not_found') if self.translator else "Delete Account button not found")
# Wait for confirmation dialog to appear
time.sleep(2)
# Check if we need to input "Delete" at all - some modals might not require it
input_required = True
try:
# Try detecting if the DELETE button is already enabled
delete_button_enabled = self.browser.run_js("""
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButtons = buttons.filter(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButtons.length > 0) {
return !deleteButtons.some(btn => btn.disabled);
}
return false;
""")
if delete_button_enabled:
print(f"{Fore.CYAN}{EMOJI['INFO']} DELETE button appears to be enabled already. Input may not be required.{Style.RESET_ALL}")
input_required = False
except:
pass
# Type "Delete" in the confirmation input - only if required
delete_input_found = False
if input_required:
# Try common selectors for the input field
delete_input_selectors = [
'xpath://input[@placeholder="Delete"]',
'xpath://div[contains(@class, "modal")]//input',
'xpath://input',
'css:input'
]
for selector in delete_input_selectors:
try:
delete_input = self.browser.ele(selector, timeout=3)
if delete_input:
delete_input.clear()
delete_input.input("Delete")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete', fallback='Typed \"Delete\" in confirmation box')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
# Try direct JavaScript input as fallback
try:
self.browser.run_js(f"""
arguments[0].value = "Delete";
const event = new Event('input', {{ bubbles: true }});
arguments[0].dispatchEvent(event);
const changeEvent = new Event('change', {{ bubbles: true }});
arguments[0].dispatchEvent(changeEvent);
""", delete_input)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
continue
if not delete_input_found:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.delete_input_not_found', fallback='Delete confirmation input not found, continuing anyway')}{Style.RESET_ALL}")
time.sleep(2)
# Wait before clicking the final DELETE button
time.sleep(2)
# Click on the final DELETE button
confirm_button_found = False
# Use JavaScript approach for the DELETE button
try:
delete_button_js = self.browser.run_js("""
// Try to find the DELETE button by exact text content
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButton = buttons.find(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButton) {
console.log("Found DELETE button with JavaScript");
deleteButton.click();
return true;
}
// If not found by text, try to find right-most button in the modal
const modalButtons = Array.from(document.querySelectorAll('.relative button, [role="dialog"] button, .modal button, [aria-modal="true"] button'));
if (modalButtons.length > 1) {
modalButtons.sort((a, b) => {
const rectA = a.getBoundingClientRect();
const rectB = b.getBoundingClientRect();
return rectB.right - rectA.right;
});
console.log("Clicking right-most button in modal");
modalButtons[0].click();
return true;
} else if (modalButtons.length === 1) {
console.log("Clicking single button found in modal");
modalButtons[0].click();
return true;
}
return false;
""")
if delete_button_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Clicked DELETE button')}{Style.RESET_ALL}")
confirm_button_found = True
except:
pass
if not confirm_button_found:
# Fallback to simple selectors
delete_button_selectors = [
'xpath://button[text()="DELETE"]',
'xpath://div[contains(@class, "modal")]//button[last()]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Account deleted successfully!')}{Style.RESET_ALL}")
confirm_button_found = True
break
except:
continue
if not confirm_button_found:
raise Exception(self.translator.get('account_delete.confirm_button_not_found') if self.translator else "Confirm button not found")
# Wait a moment to see the confirmation
time.sleep(2)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('account_delete.error', error=str(e)) if self.translator else f'Error during account deletion: {str(e)}'}{Style.RESET_ALL}")
return False
finally:
# Clean up browser
if self.browser:
try:
self.browser.quit()
except:
pass
def main(translator=None):
"""Main function to handle Google account deletion"""
print(f"\n{Fore.CYAN}{EMOJI['START']} {translator.get('account_delete.title') if translator else 'Cursor Google Account Deletion Tool'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
deleter = CursorGoogleAccountDeleter(translator)
try:
# Ask for confirmation
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('account_delete.warning') if translator else 'WARNING: This will permanently delete your Cursor account. This action cannot be undone.'}{Style.RESET_ALL}")
confirm = input(f"{Fore.RED} {translator.get('account_delete.confirm_prompt') if translator else 'Are you sure you want to proceed? (y/N): '}{Style.RESET_ALL}").lower()
if confirm != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.cancelled') if translator else 'Account deletion cancelled.'}{Style.RESET_ALL}")
return
success = deleter.delete_google_account()
if success:
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('account_delete.success') if translator else 'Your Cursor account has been successfully deleted!'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.failed') if translator else 'Account deletion process failed or was cancelled.'}{Style.RESET_ALL}")
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.interrupted') if translator else 'Account deletion process interrupted by user.'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
finally:
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
if __name__ == "__main__":
main()

View File

@@ -5,6 +5,8 @@ import shutil
from colorama import Fore, Style, init
import subprocess
from config import get_config
import re
import tempfile
# Initialize colorama
init()
@@ -31,10 +33,16 @@ class AutoUpdateDisabler:
if config:
if self.system == "Windows":
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"))
self.product_json_path = config.get('WindowsPaths', 'product_json_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"))
elif self.system == "Darwin":
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml")
self.product_json_path = config.get('MacPaths', 'product_json_path', fallback="/Applications/Cursor.app/Contents/Resources/app/product.json")
elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml"))
self.product_json_path = config.get('LinuxPaths', 'product_json_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/product.json"))
else:
# If configuration loading fails, use default paths
self.updater_paths = {
@@ -43,6 +51,60 @@ class AutoUpdateDisabler:
"Linux": os.path.expanduser("~/.config/cursor-updater")
}
self.updater_path = self.updater_paths.get(self.system)
self.update_yml_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"),
"Darwin": "/Applications/Cursor.app/Contents/Resources/app-update.yml",
"Linux": os.path.expanduser("~/.config/cursor/resources/app-update.yml")
}
self.update_yml_path = self.update_yml_paths.get(self.system)
self.product_json_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"),
"Darwin": "/Applications/Cursor.app/Contents/Resources/app/product.json",
"Linux": os.path.expanduser("~/.config/cursor/resources/app/product.json")
}
self.product_json_path = self.product_json_paths.get(self.system)
def _remove_update_url(self):
"""Remove update URL"""
try:
original_stat = os.stat(self.product_json_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(self.product_json_path, "r", encoding="utf-8") as product_json_file:
content = product_json_file.read()
patterns = {
r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
r"https://api2.cursor.sh/updates": r"",
r"http://cursorapi.com/updates": r"",
}
for pattern, replacement in patterns.items():
content = re.sub(pattern, replacement, content)
tmp_file.write(content)
tmp_path = tmp_file.name
shutil.copy2(self.product_json_path, self.product_json_path + ".old")
shutil.move(tmp_path, self.product_json_path)
os.chmod(self.product_json_path, original_mode)
if os.name != "nt":
os.chown(self.product_json_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
os.unlink(tmp_path)
return False
def _kill_cursor_processes(self):
"""End all Cursor processes"""
@@ -71,42 +133,92 @@ class AutoUpdateDisabler:
print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}")
if os.path.exists(updater_path):
if os.path.isdir(updater_path):
shutil.rmtree(updater_path)
else:
os.remove(updater_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
try:
if os.path.isdir(updater_path):
shutil.rmtree(updater_path)
else:
os.remove(updater_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.directory_locked', path=updater_path) if self.translator else f'更新程序目录已被锁定,跳过删除: {updater_path}'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
return True
def _clear_update_yml_file(self):
"""Clear update.yml file"""
try:
update_yml_path = self.update_yml_path
if not update_yml_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}")
if os.path.exists(update_yml_path):
try:
with open(update_yml_path, 'w') as f:
f.write('')
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已被锁定,跳过清空'}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}")
return False
def _create_blocking_file(self):
"""Create blocking file"""
"""Create blocking files"""
try:
# 检查 updater_path
updater_path = self.updater_path
if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
# Create empty file
open(updater_path, 'w').close()
# Set read-only attribute
if self.system == "Windows":
os.system(f'attrib +r "{updater_path}"')
else:
os.chmod(updater_path, 0o444) # Set to read-only
# 创建 updater_path 阻止文件
try:
os.makedirs(os.path.dirname(updater_path), exist_ok=True)
open(updater_path, 'w').close()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}{Style.RESET_ALL}")
# 设置 updater_path 为只读
if self.system == "Windows":
os.system(f'attrib +r "{updater_path}"')
else:
os.chmod(updater_path, 0o444) # 设置为只读
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.block_file_locked') if self.translator else '阻止文件已被锁定,跳过创建'}{Style.RESET_ALL}")
# 检查 update_yml_path
update_yml_path = self.update_yml_path
if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)):
try:
# 创建 update_yml_path 阻止文件
with open(update_yml_path, 'w') as f:
f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
# 设置 update_yml_path 为只读
if self.system == "Windows":
os.system(f'attrib +r "{update_yml_path}"')
else:
os.chmod(update_yml_path, 0o444) # 设置为只读
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_already_locked') if self.translator else '更新配置文件已被锁定,跳过修改'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
return False
return True # 返回 True 以继续执行后续步骤
def disable_auto_update(self):
"""Disable auto update"""
@@ -117,14 +229,21 @@ class AutoUpdateDisabler:
if not self._kill_cursor_processes():
return False
# 2. Delete directory
if not self._remove_updater_directory():
# 2. Delete directory - 即使失败也继续执行
self._remove_updater_directory()
# 3. Clear update.yml file
if not self._clear_update_yml_file():
return False
# 3. Create blocking file
# 4. Create blocking file
if not self._create_blocking_file():
return False
# 5. Remove update URL from product.json
if not self._remove_update_url():
return False
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
return True

701
github_cursor_register.py Normal file
View File

@@ -0,0 +1,701 @@
import os
import time
import uuid
import json
import random
import string
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import logging
import platform
from colorama import Fore, Style, init
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
import shutil
# Initialize colorama
init()
# Set up logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': '',
'EMAIL': '📧',
'REFRESH': '🔄',
'LINK': '🔗',
'WARNING': '⚠️'
}
class GitHubCursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set browser to visible mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.email_address = None
# Generate random credentials
self.github_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
self.github_password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=16))
def setup_browser(self):
"""Setup and configure the web browser"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} Setting up browser...{Style.RESET_ALL}")
options = Options()
options.add_argument('--incognito')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--window-size=1920,1080')
options.add_argument('--disable-notifications')
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36')
self.browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
self.browser.set_page_load_timeout(30)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to setup browser: {str(e)}{Style.RESET_ALL}")
return False
def get_temp_email(self):
"""Get a temporary email address using YOPmail"""
try:
if not self.browser:
if not self.setup_browser():
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} Generating temporary email address...{Style.RESET_ALL}")
self.browser.get("https://yopmail.com/")
time.sleep(2)
# Generate a realistic username
first_names = ["john", "sara", "michael", "emma", "david", "jennifer", "robert", "lisa"]
last_names = ["smith", "johnson", "williams", "brown", "jones", "miller", "davis", "garcia"]
random_first = random.choice(first_names)
random_last = random.choice(last_names)
random_num = random.randint(100, 999)
username = f"{random_first}.{random_last}{random_num}"
# Enter the username and check inbox
email_field = self.browser.find_element(By.XPATH, "//input[@id='login']")
if email_field:
email_field.clear()
email_field.send_keys(username)
time.sleep(1)
# Click the check button
check_button = self.browser.find_element(By.XPATH, "//button[@title='Check Inbox' or @class='sbut' or contains(@onclick, 'ver')]")
if check_button:
check_button.click()
time.sleep(2)
self.email_address = f"{username}@yopmail.com"
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Temp email created: {self.email_address}{Style.RESET_ALL}")
return True
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to create YOPmail address{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting temporary email: {str(e)}{Style.RESET_ALL}")
return False
def register_github(self):
"""Register a new GitHub account"""
if not self.email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} No email address available{Style.RESET_ALL}")
return False
if not self.browser:
if not self.setup_browser():
return False
try:
print(f"{Fore.CYAN}{EMOJI['FORM']} Registering GitHub account...{Style.RESET_ALL}")
self.browser.get("https://github.com/join")
time.sleep(3)
# Fill in the registration form
WebDriverWait(self.browser, 15).until(EC.visibility_of_element_located((By.ID, "user_login")))
self.browser.find_element(By.ID, "user_login").send_keys(self.github_username)
self.browser.find_element(By.ID, "user_email").send_keys(self.email_address)
self.browser.find_element(By.ID, "user_password").send_keys(self.github_password)
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub password: {self.github_password}{Style.RESET_ALL}")
# Check for any notice or popup and handle it
try:
signup_button = self.browser.find_element(By.ID, "signup_button")
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking sign up button...{Style.RESET_ALL}")
signup_button.click()
except NoSuchElementException:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Signup button not found, trying alternative selector{Style.RESET_ALL}")
buttons = self.browser.find_elements(By.TAG_NAME, "button")
for button in buttons:
if "Sign up" in button.text:
button.click()
break
# Wait for page transition and check for CAPTCHA
time.sleep(5)
# Check if registration was successful or if CAPTCHA appeared
current_url = self.browser.current_url
# Look for CAPTCHA in URL or on page
if "captcha" in current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.YELLOW}{EMOJI['WAIT']} CAPTCHA detected, please complete it manually{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You have 60 seconds to solve the CAPTCHA...{Style.RESET_ALL}")
# Wait for user to solve CAPTCHA (60 seconds max)
for i in range(60):
current_url = self.browser.current_url
if "captcha" not in current_url.lower() and "are you a robot" not in self.browser.page_source.lower():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} CAPTCHA completed successfully{Style.RESET_ALL}")
break
time.sleep(1)
if i % 10 == 0 and i > 0:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Still waiting for CAPTCHA completion... {60-i} seconds remaining{Style.RESET_ALL}")
# Check if CAPTCHA was solved after waiting
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA not solved within time limit{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want more time to solve the CAPTCHA? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've completed the CAPTCHA...{Style.RESET_ALL}")
input()
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA still not solved{Style.RESET_ALL}")
return False
else:
return False
# Wait for registration to complete
time.sleep(5)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub account registered{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register GitHub account: {str(e)}{Style.RESET_ALL}")
return False
def check_email_verification(self):
"""Check for GitHub verification email and click the verification link"""
if not self.email_address or not self.browser:
print(f"{Fore.RED}{EMOJI['ERROR']} Email or browser not available{Style.RESET_ALL}")
return False
try:
print(f"{Fore.CYAN}{EMOJI['EMAIL']} Checking for verification email...{Style.RESET_ALL}")
# Extract username from email for YOPmail
username = self.email_address.split('@')[0]
max_attempts = 10
for attempt in range(1, max_attempts + 1):
print(f"{Fore.CYAN}{EMOJI['REFRESH']} Checking YOPmail inbox (attempt {attempt}/{max_attempts})...{Style.RESET_ALL}")
# Go to YOPmail inbox
self.browser.get(f"https://yopmail.com/en/wm")
time.sleep(2)
# Enter email address
try:
email_input = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "login"))
)
email_input.clear()
email_input.send_keys(username)
# Click the check inbox button
check_button = self.browser.find_element(By.CSS_SELECTOR, "button[onclick='verif()']")
check_button.click()
time.sleep(3)
# Switch to inbox frame
iframe = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "ifinbox"))
)
self.browser.switch_to.frame(iframe)
# Look for GitHub email
emails = self.browser.find_elements(By.CSS_SELECTOR, "div.m")
github_email = None
for email in emails:
if "github" in email.text.lower():
github_email = email
break
if github_email:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub verification email found{Style.RESET_ALL}")
github_email.click()
time.sleep(2)
# Switch back to default content
self.browser.switch_to.default_content()
# Switch to email content frame
iframe = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "ifmail"))
)
self.browser.switch_to.frame(iframe)
# Find verification link
try:
# Look for the verification button or link
verification_elements = self.browser.find_elements(By.XPATH, "//a[contains(text(), 'Verify') or contains(text(), 'verify') or contains(@href, 'verify')]")
if verification_elements:
verification_link = verification_elements[0].get_attribute('href')
print(f"{Fore.CYAN}{EMOJI['LINK']} Found verification link{Style.RESET_ALL}")
# Open the verification link in the same window
self.browser.get(verification_link)
time.sleep(5)
# Check if verification was successful
if "verified" in self.browser.page_source.lower() or "successful" in self.browser.page_source.lower():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Email verified successfully{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Email verification page loaded but success not confirmed{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check if verification was successful manually and press Enter to continue...{Style.RESET_ALL}")
input()
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} No verification link found in email{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error extracting verification link: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} No GitHub verification email yet, waiting... ({attempt}/{max_attempts}){Style.RESET_ALL}")
time.sleep(15) # Wait before checking again
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error checking email: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} No verification email received after {max_attempts} attempts{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to check manually? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check your YOPmail inbox manually at: https://yopmail.com/en/wm")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Username: {username}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've verified the email...{Style.RESET_ALL}")
input()
return True
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to check verification email: {str(e)}{Style.RESET_ALL}")
return False
def register_cursor(self):
"""Register with Cursor using GitHub"""
if not self.browser:
if not self.setup_browser():
return False
try:
print(f"{Fore.CYAN}{EMOJI['KEY']} Registering with Cursor using GitHub...{Style.RESET_ALL}")
# Navigate to Cursor login page
self.browser.get("https://cursor.sh/login")
time.sleep(3)
try:
# Look for GitHub login button
github_buttons = WebDriverWait(self.browser, 15).until(
EC.presence_of_all_elements_located((By.XPATH, "//button[contains(., 'GitHub') or contains(@class, 'github')]"))
)
if not github_buttons:
print(f"{Fore.RED}{EMOJI['ERROR']} GitHub login button not found{Style.RESET_ALL}")
return False
# Click the first GitHub button
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking GitHub login button...{Style.RESET_ALL}")
github_buttons[0].click()
time.sleep(5)
# Check if we're redirected to GitHub login
current_url = self.browser.current_url
if "github.com" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirected to GitHub login{Style.RESET_ALL}")
# Check if we need to log in to GitHub
if "login" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Logging into GitHub...{Style.RESET_ALL}")
try:
# Enter GitHub credentials
username_field = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "login_field"))
)
username_field.send_keys(self.github_username)
password_field = self.browser.find_element(By.ID, "password")
password_field.send_keys(self.github_password)
# Click sign in
signin_button = self.browser.find_element(By.CSS_SELECTOR, "input[type='submit']")
signin_button.click()
time.sleep(5)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub login: {str(e)}{Style.RESET_ALL}")
return False
# Check if we're on the authorization page
if "authorize" in self.browser.current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Authorizing Cursor app...{Style.RESET_ALL}")
try:
# Look for authorization button
auth_buttons = self.browser.find_elements(By.XPATH, "//button[contains(., 'Authorize') or contains(@class, 'btn-primary')]")
if auth_buttons:
auth_buttons[0].click()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor authorized with GitHub{Style.RESET_ALL}")
time.sleep(5)
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No authorization button found, GitHub may be already authorized{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub authorization: {str(e)}{Style.RESET_ALL}")
# Wait for Cursor dashboard to load
timeout = 30
start_time = time.time()
while time.time() - start_time < timeout:
if "cursor.sh" in self.browser.current_url and not "login" in self.browser.current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Successfully logged into Cursor{Style.RESET_ALL}")
break
time.sleep(1)
if "login" in self.browser.current_url:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to log into Cursor after {timeout} seconds{Style.RESET_ALL}")
return False
# Wait for dashboard elements to load
time.sleep(3)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor registered with GitHub successfully{Style.RESET_ALL}")
# Now reset the machine ID
return self.reset_machine_id()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during Cursor registration: {str(e)}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register with Cursor: {str(e)}{Style.RESET_ALL}")
return False
def reset_machine_id(self):
"""Reset the Cursor machine ID to bypass limitations"""
try:
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Resetting Cursor machine ID...{Style.RESET_ALL}")
# Find Cursor app data location based on platform
cursor_data_dir = None
if platform.system() == "Windows":
appdata = os.getenv('APPDATA')
if appdata:
cursor_data_dir = os.path.join(appdata, "cursor", "Local Storage", "leveldb")
elif platform.system() == "Darwin": # macOS
home = os.path.expanduser("~")
cursor_data_dir = os.path.join(home, "Library", "Application Support", "cursor", "Local Storage", "leveldb")
elif platform.system() == "Linux":
home = os.path.expanduser("~")
cursor_data_dir = os.path.join(home, ".config", "cursor", "Local Storage", "leveldb")
if not cursor_data_dir or not os.path.exists(cursor_data_dir):
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found at: {cursor_data_dir}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reset the machine ID manually{Style.RESET_ALL}")
# Try to find the Cursor data directory
if platform.system() == "Linux":
possible_paths = [
os.path.join(os.path.expanduser("~"), ".config", "cursor"),
os.path.join(os.path.expanduser("~"), ".cursor")
]
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor directory at: {path}{Style.RESET_ALL}")
# Look for Local Storage subfolder
for root, dirs, files in os.walk(path):
if "Local Storage" in dirs:
cursor_data_dir = os.path.join(root, "Local Storage", "leveldb")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor data directory at: {cursor_data_dir}{Style.RESET_ALL}")
break
break
if cursor_data_dir and os.path.exists(cursor_data_dir):
# Generate a new UUID
new_machine_id = str(uuid.uuid4())
print(f"{Fore.CYAN}{EMOJI['KEY']} New machine ID: {new_machine_id}{Style.RESET_ALL}")
# Ask for permission to modify files
print(f"{Fore.YELLOW}{EMOJI['WARNING']} This operation will modify Cursor app data files{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to continue? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response not in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Machine ID reset aborted{Style.RESET_ALL}")
return False
# Backup the directory
backup_dir = cursor_data_dir + "_backup_" + time.strftime("%Y%m%d%H%M%S")
print(f"{Fore.CYAN}{EMOJI['INFO']} Creating backup of data directory to: {backup_dir}{Style.RESET_ALL}")
try:
shutil.copytree(cursor_data_dir, backup_dir)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Backup created successfully{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Failed to create backup: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without backup...{Style.RESET_ALL}")
# Find and modify files containing the machine ID
modified = False
for filename in os.listdir(cursor_data_dir):
if filename.endswith(".log") or filename.endswith(".ldb"):
file_path = os.path.join(cursor_data_dir, filename)
try:
with open(file_path, "rb") as f:
content = f.read()
# Look for patterns that might contain machine ID
if b"machineId" in content:
print(f"{Fore.CYAN}{EMOJI['INFO']} Found machineId reference in: {filename}{Style.RESET_ALL}")
modified = True
# For safety, don't modify the binary files directly
# Instead, instruct user to uninstall and reinstall Cursor
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Binary files found that may contain machine ID{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} For best results, please:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 1. Close Cursor if it's running{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 2. Uninstall Cursor completely{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 3. Reinstall Cursor{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 4. Login with your new GitHub account{Style.RESET_ALL}")
break
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error processing file {filename}: {str(e)}{Style.RESET_ALL}")
if not modified:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No machine ID references found in data files{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reinstall Cursor for a complete reset{Style.RESET_ALL}")
# Save credentials before returning
self.save_credentials()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID reset process completed{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to manually reset the machine ID by reinstalling Cursor{Style.RESET_ALL}")
# Still save credentials
self.save_credentials()
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to reset machine ID: {str(e)}{Style.RESET_ALL}")
# Still save credentials even if machine ID reset fails
self.save_credentials()
return False
def save_credentials(self):
"""Save the generated credentials to a file"""
try:
if not self.email_address or not self.github_username or not self.github_password:
print(f"{Fore.RED}{EMOJI['ERROR']} No credentials to save{Style.RESET_ALL}")
return False
output_file = "github_cursor_accounts.txt"
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
credentials = {
"timestamp": timestamp,
"github_username": self.github_username,
"github_password": self.github_password,
"email": self.email_address
}
credentials_json = json.dumps(credentials)
# Check if file exists and create if not
file_exists = os.path.exists(output_file)
with open(output_file, "a") as f:
if not file_exists:
f.write("# GitHub + Cursor AI Accounts\n")
f.write("# Format: JSON with timestamp, github_username, github_password, email\n\n")
f.write(credentials_json + "\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Credentials saved to: {output_file}{Style.RESET_ALL}")
# Print a summary
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} Registration Summary:{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Saved to: {output_file}{Style.RESET_ALL}\n")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to save credentials: {str(e)}{Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} Make sure to copy these credentials manually:{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}\n")
return False
def cleanup(self):
"""Clean up resources"""
if self.browser:
try:
self.browser.quit()
except:
pass
def start_registration(self):
"""Start the GitHub Cursor registration process"""
try:
# Step 1: Get temporary email
if not self.get_temp_email():
return False
# Step 2: Register GitHub account
if not self.register_github():
return False
# Step 3: Check and verify email
if not self.check_email_verification():
return False
# Step 4: Register Cursor with GitHub
if not self.register_cursor():
return False
# Step 5: Reset machine ID
self.reset_machine_id()
return True
finally:
self.cleanup()
def display_features_and_warnings(translator=None):
"""Display features and warnings before proceeding"""
if translator:
print(f"\n🚀 {translator.get('github_register.title')}")
print("=====================================")
print(f"{translator.get('github_register.features_header')}:")
print(f" - {translator.get('github_register.feature1')}")
print(f" - {translator.get('github_register.feature2')}")
print(f" - {translator.get('github_register.feature3')}")
print(f" - {translator.get('github_register.feature4')}")
print(f" - {translator.get('github_register.feature5')}")
print(f" - {translator.get('github_register.feature6')}")
print(f"\n⚠️ {translator.get('github_register.warnings_header')}:")
print(f" - {translator.get('github_register.warning1')}")
print(f" - {translator.get('github_register.warning2')}")
print(f" - {translator.get('github_register.warning3')}")
print(f" - {translator.get('github_register.warning4')}")
print("=====================================\n")
else:
print("\n🚀 GitHub + Cursor AI Registration Automation")
print("=====================================")
print("Features:")
print(" - Creates a temporary email using YOPmail")
print(" - Registers a new GitHub account with random credentials")
print(" - Verifies the GitHub email automatically")
print(" - Logs into Cursor AI using GitHub authentication")
print(" - Resets the machine ID to bypass trial detection")
print(" - Saves all credentials to a file")
print("\n⚠️ Warnings:")
print(" - This script automates account creation, which may violate GitHub/Cursor terms of service")
print(" - Requires internet access and administrative privileges")
print(" - CAPTCHA or additional verification may interrupt automation")
print(" - Use responsibly and at your own risk")
print("=====================================\n")
def get_user_confirmation(translator=None):
"""Prompt the user for confirmation to proceed"""
while True:
if translator:
response = input(f"{translator.get('github_register.confirm')} (yes/no): ").lower().strip()
else:
response = input("Do you want to proceed with GitHub + Cursor AI registration? (yes/no): ").lower().strip()
if response in ['yes', 'y']:
return True
elif response in ['no', 'n']:
if translator:
print(f"{translator.get('github_register.cancelled')}")
else:
print("❌ Operation cancelled.")
return False
else:
if translator:
print(f"{translator.get('github_register.invalid_choice')}")
else:
print("Please enter 'yes' or 'no'.")
def main(translator=None):
"""Main function to run the GitHub Cursor registration process"""
logging.info(f"{Fore.CYAN} {translator.get('github_register.starting_automation')}{Style.RESET_ALL}")
# Display features and warnings
display_features_and_warnings(translator)
# Get user confirmation
if not get_user_confirmation(translator):
return
# Start registration process
registration = GitHubCursorRegistration(translator)
success = registration.start_registration()
# Display final message
if success:
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {translator.get('github_register.completed_successfully')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_username')}: {registration.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_password')}: {registration.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.email')}: {registration.email_address}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.credentials_saved')}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('github_register.registration_encountered_issues')}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('github_register.check_browser_windows_for_manual_intervention_or_try_again_later')}{Style.RESET_ALL}")
# Wait for user acknowledgment
if translator:
input(f"\n{EMOJI['INFO']} {translator.get('register.press_enter')}...")
else:
input(f"\n{EMOJI['INFO']} Press Enter to continue...")
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

404
locales/bg.json Normal file
View File

@@ -0,0 +1,404 @@
{
"menu": {
"title": "Възможни избори",
"exit": "Затвори програмата",
"reset": "Нулирай ид-то на компютъра",
"register": "Регистрирай нов Курсор акаунт",
"register_google": "Регистрирай се с Google акаунт",
"register_github": "Регистрирай се с GitHub акаунт",
"register_manual": "Регистрирай се със свой имейл по избор",
"quit": "Затвори приложението Курсор",
"select_language": "Избери език",
"es": "Spanish",
"input_choice": "Моля, въведете своя избор ({choices})",
"invalid_choice": "Невалиден избор. Моля, въведете избор от {choices}.",
"program_terminated": "Програмата беше затворена от вас",
"error_occurred": "Възникна грешка: {error}. Опитайте отново",
"press_enter": "Натиснете Enter, за да излезете",
"disable_auto_update": "Спрете автоматичните ъпдейти на Курсор",
"lifetime_access_enabled": "ДОЖИВОТЕН ДОСТЪП Е АКТИВИРАН",
"totally_reset": "Нулирайте изцяло Курсор",
"outdate": "Изтекъл срок",
"temp_github_register": "Временно регистриране с GitHub",
"coming_soon": "Очаквайте скоро"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
"vi": "Vietnamese",
"nl": "Dutch",
"de": "German",
"fr": "French",
"pt": "Portuguese",
"ru": "Russian",
"tr": "Turkish",
"bg": "Bulgarian",
"es": "Spanish"
},
"quit_cursor": {
"start": "Започни излизането от Курсор",
"no_process": "Няма съществуващ процес, засягащ Курсор",
"terminating": "Прекратяване на процес {pid}",
"waiting": "Изчакване на процеса да приключи",
"success": "Всички процеси, свързани с Курсор, бяха прекратени",
"timeout": "Таймаут на процеса: {pids}",
"error": "Възникна грешка: {error}"
},
"reset": {
"title": "",
"checking": "Проверка на конфигурационния файл",
"not_found": "Конфигурационният файл не беше намерен",
"no_permission": "Конфигурационният файл не може да бъде прочетен или записан. Моля, проверете разрешенията на файла",
"reading": "Четене на текущата конфигурация",
"creating_backup": "Създаване на резервно копие на конфигурацията",
"backup_exists": "Резервното копие вече съществува. Стъпката за резервно копие се пропуска",
"generating": "Генериране на ново машинно ID",
"saving_json": "Запазване на новата конфигурация в JSON",
"success": "Машинното ID беше успешно нулирано",
"new_id": "Ново машинно ID",
"permission_error": "Грешка в разрешенията: {error}",
"run_as_admin": "Моля, опитайте да стартирате тази програма като администратор",
"process_error": "Грешка при нулиране: {error}",
"updating_sqlite": "Актуализиране на SQLite базата данни",
"updating_pair": "Актуализиране на двойката ключ-стойност",
"sqlite_success": "SQLite базата данни беше успешно актуализирана",
"sqlite_error": "Грешка при актуализиране на SQLite базата данни: {error}",
"press_enter": "Натиснете Enter, за да излезете",
"unsupported_os": "Неподдържана операционна система: {os}",
"linux_path_not_found": "Linux пътят не беше намерен",
"updating_system_ids": "Актуализиране на системните ID",
"system_ids_updated": "Системните ID бяха успешно актуализирани",
"system_ids_update_failed": "Грешка при актуализиране на системните ID: {error}",
"windows_guid_updated": "Windows GUID беше успешно актуализиран",
"windows_permission_denied": "Достъпът до Windows беше отказан",
"windows_guid_update_failed": "Грешка при актуализиране на Windows GUID",
"macos_uuid_updated": "macOS UUID беше успешно актуализиран",
"plutil_command_failed": "Командата plutil не беше успешна",
"start_patching": "Започване на прилагане на корекция за getMachineId",
"macos_uuid_update_failed": "Грешка при актуализиране на macOS UUID",
"current_version": "Текуща версия на Курсор: {version}",
"patch_completed": "Корекцията на getMachineId беше успешно завършена",
"patch_failed": "Грешка при прилагане на корекция за getMachineId: {error}",
"version_check_passed": "Проверката на версията на Курсор беше успешна",
"file_modified": "Файлът беше променен",
"version_less_than_0_45": "Версията на Курсор е < 0.45.0, корекцията на getMachineId се пропуска",
"detecting_version": "Откриване на версията на Курсор",
"patching_getmachineid": "Прилагане на корекция за getMachineId",
"version_greater_than_0_45": "Версията на Курсор е >= 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": "Версията на Курсор е твърде ниска: {version} < 0.45.0"
},
"register": {
"title": "Инструмент за регистрация в Курсор",
"start": "Започване на процеса на регистрация...",
"handling_turnstile": "Обработка на проверка за сигурност...",
"retry_verification": "Опит за повторна проверка...",
"detect_turnstile": "Проверка на защитната врата...",
"verification_success": "Проверката за сигурност беше успешна",
"starting_browser": "Стартиране на браузъра...",
"form_success": "Формата беше успешно изпратена",
"browser_started": "Браузърът беше успешно стартиран",
"waiting_for_second_verification": "Изчакване на втората проверка по имейл...",
"waiting_for_verification_code": "Изчакване на код за потвърждение...",
"password_success": "Паролата беше успешно зададена",
"password_error": "Грешка при задаване на парола: {error}. Моля, опитайте отново",
"waiting_for_page_load": "Зареждане на страницата...",
"first_verification_passed": "Първата проверка беше успешна",
"mailbox": "Успешен достъп до пощенската кутия",
"register_start": "Начало на регистрацията",
"form_submitted": "Формата беше изпратена, започване на проверка...",
"filling_form": "Попълване на формуляра",
"visiting_url": "Посещение на URL",
"basic_info": "Основните данни бяха изпратени",
"handle_turnstile": "Обработка на защитната врата",
"no_turnstile": "Няма защитна врата",
"turnstile_passed": "Защитната врата беше премината",
"verification_start": "Започване на получаване на код за потвърждение",
"verification_timeout": "Времето за получаване на код за потвърждение изтече",
"verification_not_found": "Кодът за потвърждение не беше намерен",
"try_get_code": "Опит | {attempt} Получаване на код за потвърждение | Оставащо време: {time}s",
"get_account": "Получаване на информация за акаунта",
"get_token": "Получаване на токен за сесия на Курсор",
"token_success": "Токенът беше успешно получен",
"token_attempt": "Опит | {attempt} опита за получаване на токен | Ще бъде опит отново след {time}s",
"token_max_attempts": "Достигнат максимален брой опити ({max}) | Неуспешно получаване на токен",
"token_failed": "Грешка при получаване на токен: {error}",
"account_error": "Грешка при получаване на информация за акаунта: {error}",
"press_enter": "Натиснете Enter, за да излезете",
"browser_start": "Стартиране на браузъра",
"open_mailbox": "Отваряне на страницата на пощенската кутия",
"email_error": "Грешка при получаване на имейл адрес",
"setup_error": "Грешка при настройка на имейл: {error}",
"start_getting_verification_code": "Започване на получаване на код за потвърждение, ще бъде опит след 60 секунди",
"get_verification_code_timeout": "Времето за получаване на код за потвърждение изтече",
"get_verification_code_success": "Кодът за потвърждение беше успешно получен",
"try_get_verification_code": "Опит | {attempt} Получаване на код за потвърждение | Оставащо време: {remaining_time}s",
"verification_code_filled": "Кодът за потвърждение беше попълнен",
"login_success_and_jump_to_settings_page": "Успешен вход и преход към страницата с настройки",
"detect_login_page": "Открита е страница за вход, започване на влизане...",
"cursor_registration_completed": "Регистрацията в Курсор беше завършена!",
"set_password": "Задаване на парола",
"basic_info_submitted": "Основните данни бяха изпратени",
"cursor_auth_info_updated": "Информацията за удостоверяване на Курсор беше актуализирана",
"cursor_auth_info_update_failed": "Грешка при актуализиране на информацията за удостоверяване на Курсор",
"reset_machine_id": "Нулиране на машинното ID",
"account_info_saved": "Информацията за акаунта беше запазена",
"save_account_info_failed": "Грешка при запазване на информацията за акаунта",
"get_email_address": "Получаване на имейл адрес",
"update_cursor_auth_info": "Актуализиране на информацията за удостоверяване на Курсор",
"register_process_error": "Грешка в процеса на регистрация: {error}",
"setting_password": "Задаване на парола",
"manual_code_input": "Ръчно въвеждане на код",
"manual_email_input": "Ръчно въвеждане на имейл",
"password": "Парола",
"first_name": "Име",
"last_name": "Фамилия",
"exit_signal": "Сигнал за изход",
"email_address": "Имейл адрес",
"config_created": "Конфигурацията беше създадена",
"verification_failed": "Проверката беше неуспешна",
"verification_error": "Грешка при проверка: {error}",
"config_option_added": "Добавена е опция за конфигурация: {option}",
"config_updated": "Конфигурацията беше актуализирана",
"password_submitted": "Паролата беше изпратена",
"total_usage": "Общо използване: {usage}",
"setting_on_password": "Задаване на парола",
"getting_code": "Получаване на код за потвърждение, ще бъде опит след 60 секунди",
"human_verify_error": "Не може да се потвърди, че потребителят е човек. Опитва се отново...",
"max_retries_reached": "Достигнат максимален брой опити. Регистрацията беше неуспешна."
},
"auth": {
"title": "Управление на удостоверяването на Курсор",
"checking_auth": "Проверка на файла за удостоверяване",
"auth_not_found": "Файлът за удостоверяване не беше намерен",
"auth_file_error": "Грешка във файла за удостоверяване: {error}",
"reading_auth": "Четене на файла за удостоверяване",
"updating_auth": "Актуализиране на информацията за удостоверяване",
"auth_updated": "Информацията за удостоверяване беше успешно актуализирана",
"auth_update_failed": "Грешка при актуализиране на информацията за удостоверяване: {error}",
"auth_file_created": "Файлът за удостоверяване беше създаден",
"auth_file_create_failed": "Грешка при създаване на файла за удостоверяване: {error}",
"press_enter": "Натиснете Enter, за да излезете",
"reset_machine_id": "Нулиране на машинното ID",
"database_connection_closed": "Връзката с базата беше затворена",
"database_updated_successfully": "Базата данни беше успешно актуализирана",
"connected_to_database": "Успешно свързване с базата данни",
"updating_pair": "Актуализиране на двойката ключ-стойност",
"db_not_found": "Файлът на базата данни не беше намерен: {path}",
"db_permission_error": "Няма достъп до файла на базата данни. Моля, проверете разрешенията",
"db_connection_error": "Грешка при свързване с базата данни: {error}"
},
"control": {
"generate_email": "Създаване на нов имейл",
"blocked_domain": "Блокиран домейн",
"select_domain": "Избиране на случаен домейн",
"copy_email": "Копиране на имейл адрес",
"enter_mailbox": "Влизане в пощенската кутия",
"refresh_mailbox": "Опресняване на пощенската кутия",
"check_verification": "Проверка на кода за потвърждение",
"verification_found": "Кодът за потвърждение беше намерен",
"verification_not_found": "Кодът за потвърждение не беше намерен",
"browser_error": "Грешка при контрол на браузъра: {error}",
"navigation_error": "Грешка при навигация: {error}",
"email_copy_error": "Грешка при копиране на имейл: {error}",
"mailbox_error": "Грешка в пощенската кутия: {error}",
"token_saved_to_file": "Токенът беше запазен във файла cursor_tokens.txt",
"navigate_to": "Навигиране към {url}",
"generate_email_success": "Имейлът беше успешно създаден",
"select_email_domain": "Избор на домейн за имейл",
"select_email_domain_success": "Успешен избор на домейн за имейл",
"get_email_name": "Получаване на име на имейл",
"get_email_name_success": "Успешно получаване на име на имейл",
"get_email_address": "Получаване на имейл адрес",
"get_email_address_success": "Успешно получаване на имейл адрес",
"enter_mailbox_success": "Успешно влизане в пощенската кутия",
"found_verification_code": "Кодът за потвърждение беше намерен",
"get_cursor_session_token": "Получаване на токен за сесия на Курсор",
"get_cursor_session_token_success": "Успешно получаване на токен за сесия на Курсор",
"get_cursor_session_token_failed": "Грешка при получаване на токен за сесия на Курсор",
"save_token_failed": "Грешка при запазване на токен",
"database_updated_successfully": "Базата данни беше успешно актуализирана",
"database_connection_closed": "Връзката с базата данни беше затворена",
"no_valid_verification_code": "Няма валиден код за потвърждение"
},
"email": {
"starting_browser": "Стартиране на браузъра",
"visiting_site": "Посещение на сайтове за имейл домейни",
"create_success": "Имейлът беше успешно създаден",
"create_failed": "Грешка при създаване на имейл",
"create_error": "Грешка при създаване на имейл: {error}",
"refreshing": "Опресняване на имейл",
"refresh_success": "Имейлът беше успешно опреснен",
"refresh_error": "Грешка при опресняване на имейл: {error}",
"refresh_button_not_found": "Бутонът за опресняване не беше намерен",
"verification_found": "Потвърждението беше намерено",
"verification_not_found": "Потвърждението не беше намерено",
"verification_error": "Грешка при потвърждение: {error}",
"verification_code_found": "Кодът за потвърждение беше намерен",
"verification_code_not_found": "Кодът за потвърждение не беше намерен",
"verification_code_error": "Грешка при код за потвърждение: {error}",
"address": "Имейл адрес",
"all_domains_blocked": "Всички домейни са блокирани, смяна на услуга",
"no_available_domains_after_filtering": "Няма налични домейни след филтриране",
"switching_service": "Смяна на услугата {service}",
"domains_list_error": "Грешка при получаване на списък с домейни: {error}",
"failed_to_get_available_domains": "Неуспешно получаване на налични домейни",
"domains_excluded": "Изключени домейни: {domains}",
"failed_to_create_account": "Неуспешно създаване на акаунт",
"account_creation_error": "Грешка при създаване на акаунт: {error}",
"blocked_domains": "Блокирани домейни: {domains}",
"blocked_domains_loaded": "Блокирани домейни са заредени: {count}",
"blocked_domains_loaded_error": "Грешка при зареждане на блокирани домейни: {error}",
"blocked_domains_loaded_success": "Блокираните домейни бяха успешно заредени",
"blocked_domains_loaded_timeout": "Време за зареждане на блокирани домейни: {timeout}s",
"blocked_domains_loaded_timeout_error": "Грешка при време за зареждане на блокирани домейни: {error}",
"available_domains_loaded": "Налични домейни са заредени: {count}",
"domains_filtered": "Филтрирани домейни: {count}",
"trying_to_create_email": "Опит за създаване на имейл: {email}",
"domain_blocked": "Домейнът е блокиран: {domain}"
},
"update": {
"title": "Деактивиране на автоматичните актуализации на Курсор",
"disable_success": "Автоматичните актуализации бяха успешно деактивирани",
"disable_failed": "Грешка при деактивиране на автоматичните актуализации: {error}",
"press_enter": "Натиснете Enter, за да излезете",
"start_disable": "Започване на деактивиране на автоматичните актуализации",
"killing_processes": "Прекратяване на процеси",
"processes_killed": "Процесите бяха прекратени",
"removing_directory": "Премахване на директория",
"directory_removed": "Директорията беше премахната",
"creating_block_file": "Създаване на блокиращ файл",
"block_file_created": "Блокиращият файл беше създаден"
},
"updater": {
"checking": "Проверка за актуализации...",
"new_version_available": "Налична е нова версия! (Текуща: {current}, Последна: {latest})",
"updating": "Актуализиране до последната версия. Програмата ще се рестартира автоматично.",
"up_to_date": "Използвате последната версия.",
"check_failed": "Грешка при проверка за актуализации: {error}",
"continue_anyway": "Продължаване с текущата версия...",
"update_confirm": "Искате ли да актуализирате до последната версия? (Y/n)",
"update_skipped": "Актуализацията беше пропусната.",
"invalid_choice": "Невалиден избор. Моля, въведете 'Y' или 'n'.",
"development_version": "Версия за разработка {current} > {latest}",
"changelog_title": "Списък с промени"
},
"totally_reset": {
"title": "Пълно нулиране на Курсор",
"checking_config": "Проверка на конфигурационния файл",
"config_not_found": "Конфигурационният файл не беше намерен",
"no_permission": "Конфигурационният файл не може да бъде прочетен или записан. Моля, проверете разрешенията на файла",
"reading_config": "Четене на текущата конфигурация",
"creating_backup": "Създаване на резервно копие на конфигурацията",
"backup_exists": "Резервното копие вече съществува. Стъпката за резервно копие се пропуска",
"generating_new_machine_id": "Генериране на ново машинно ID",
"saving_new_config": "Запазване на новата конфигурация в JSON",
"success": "Курсор беше успешно нулиран",
"error": "Грешка при нулиране на Курсор: {error}",
"press_enter": "Натиснете Enter, за да излезете",
"reset_machine_id": "Нулиране на машинното ID",
"database_connection_closed": "Връзката с базата данни беше затворена",
"database_updated_successfully": "Базата данни беше успешно актуализирана",
"connected_to_database": "Успешно свързване с базата данни",
"updating_pair": "Актуализиране на двойката ключ-стойност",
"db_not_found": "Файлът на базата данни не беше намерен: {path}",
"db_permission_error": "Няма достъп до файла на базата данни. Моля, проверете разрешенията",
"db_connection_error": "Грешка при свързване с базата данни: {error}",
"feature_title": "ФУНКЦИИ",
"feature_1": "Пълно премахване на настройките и конфигурациите на Курсор AI",
"feature_2": "Изчистване на всички кеширани данни, включително историята и командите на AI",
"feature_3": "Нулиране на машинното ID за заобикаляне на ограниченията за пробен период",
"feature_4": "Генериране на нови случайни машинни идентификатори",
"feature_5": "Премахване на персонализирани разширения и предпочитания",
"feature_6": "Нулиране на информацията за пробния период и активиране",
"feature_7": "Дълбоко сканиране за скрити файлове, свързани с лицензи и пробен период",
"feature_8": "Безопасно запазване на файлове и приложения, които не са свързани с Курсор",
"feature_9": "Съвместимост с Windows, macOS и Linux",
"disclaimer_title": "ПРАВНО ИЗВЕСТВИЕ",
"disclaimer_1": "Този инструмент ще изтрие за постоянно всички настройки на Курсор AI,",
"disclaimer_2": "конфигурации и кеширани данни. Това действие е необратимо.",
"disclaimer_3": "Вашите кодови файлове НЯМА да бъдат засегнати и този инструмент",
"disclaimer_4": "е проектиран специално да се фокусира върху файловете на редактора на Курсор AI и механизмите за откриване на пробен период.",
"disclaimer_5": "Другите приложения на вашата система няма да бъдат засегнати.",
"disclaimer_6": "След като използвате този инструмент, ще трябва да преинсталирате Курсор AI.",
"disclaimer_7": "Вие носите отговорността за използването му",
"confirm_title": "Сигурни ли сте, че искате да продължите?",
"confirm_1": "Това действие ще изтрие всички настройки на Курсор AI,",
"confirm_2": "конфигурации и кеширани данни. Това действие е необратимо.",
"confirm_3": "Вашите кодови файлове НЯМА да бъдат засегнати и този инструмент",
"confirm_4": "е проектиран специално да се фокусира върху файловете на редактора на Курсор AI и механизмите за откриване на пробен период.",
"confirm_5": "Другите приложения на вашата система няма да бъдат засегнати.",
"confirm_6": "След като използвате този инструмент, ще трябва да преинсталирате Курсор AI.",
"confirm_7": "Вие носите отговорността за използването му",
"invalid_choice": "Моля, въведете 'Y' или 'n'",
"skipped_for_safety": "Пропуснато за безопасност (не е свързано с Курсор): {path}",
"deleted": "Изтрито: {path}",
"error_deleting": "Грешка при изтриване на {path}: {error}",
"not_found": "Файлът не беше намерен: {path}",
"resetting_machine_id": "Нулиране на машинните идентификатори за заобикаляне на ограниченията за пробен период...",
"created_machine_id": "Създадено е ново машинно ID: {path}",
"error_creating_machine_id": "Грешка при създаване на файл за машинно ID {path}: {error}",
"error_searching": "Грешка при търсене на файлове в {path}: {error}",
"created_extended_trial_info": "Създадена е нова информация за удължен пробен период: {path}",
"error_creating_trial_info": "Грешка при създаване на файл за информация за пробен период {path}: {error}",
"resetting_cursor_ai_editor": "Нулиране на редактора на Курсор AI... Моля, изчакайте.",
"reset_cancelled": "Нулирането беше отменено. Излизане без промени.",
"windows_machine_id_modification_skipped": "Промяната на машинното ID на Windows беше пропусната: {error}",
"linux_machine_id_modification_skipped": "Промяната на machine-id на Linux беше пропусната: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Забележка: Пълното нулиране на машинното ID може да изисква работа като администратор",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Забележка: Пълното нулиране на системното machine-id може да изисква sudo права",
"windows_registry_instructions": "📝 ЗАБЕЛЕЖКА: За пълно нулиране на Windows може да се наложи да изчистите и записите в регистъра.",
"windows_registry_instructions_2": " Стартирайте 'regedit' и потърсете и изтрийте ключове, съдържащи 'Cursor' или 'CursorAI' в HKEY_CURRENT_USER\\Software\\.\n",
"reset_log_1": "Курсор AI беше напълно нулиран и ограниченията за пробен период бяха заобиколени!",
"reset_log_2": "Моля, рестартирайте системата си, за да влязат в сила промените.",
"reset_log_3": "Ще трябва да преинсталирате Курсор AI и сега трябва да имате нов пробен период.",
"reset_log_4": "За най-добри резултати, помислете за следното:",
"reset_log_5": "Използвайте различен имейл адрес при регистрация за нов пробен период",
"reset_log_6": "Ако е възможно, използвайте VPN за промяна на IP адреса си",
"reset_log_7": "Изчистете бисквитките и кеша на браузъра си, преди да посетите уебсайта на Курсор AI",
"reset_log_8": "Ако проблемите продължават, опитайте да инсталирате Курсор AI на друго място",
"reset_log_9": "Ако срещнете проблеми, посетете Github Issue Tracker и създайте проблем на https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Възникна неочаквана грешка: {error}",
"report_issue": "Моля, докладвайте този проблем в Github Issue Tracker на https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Процесът беше прекратен от потребителя. Излизане...",
"return_to_main_menu": "Връщане към главното меню...",
"process_interrupted": "Процесът беше прекратен. Излизане...",
"press_enter_to_return_to_main_menu": "Натиснете Enter, за да се върнете към главното меню...",
"removing_known": "Премахване на известни файлове за пробен период/лиценз",
"performing_deep_scan": "Извършване на дълбоко сканиране за допълнителни файлове за пробен период/лиценз",
"found_additional_potential_license_trial_files": "Намерени са {count} допълнителни потенциални файлове за лиценз/пробен период",
"checking_for_electron_localstorage_files": "Проверка на Electron localStorage файлове",
"no_additional_license_trial_files_found_in_deep_scan": "Няма намерени допълнителни файлове за лиценз/пробен период при дълбоко сканиране",
"removing_electron_localstorage_files": "Премахване на Electron localStorage файлове",
"electron_localstorage_files_removed": "Electron localStorage файлове бяха премахнати",
"electron_localstorage_files_removal_error": "Грешка при премахване на Electron localStorage файлове: {error}",
"removing_electron_localstorage_files_completed": "Премахването на Electron localStorage файлове беше завършено"
},
"chrome_profile": {
"title": "Избор на Chrome Профил",
"select_profile": "Изберете Chrome профил за използване:",
"profile_list": "Налични профили:",
"default_profile": "Профил по Подразбиране",
"profile": "Профил {number}",
"no_profiles": "Не са намерени Chrome профили",
"error_loading": "Грешка при зареждане на Chrome профили: {error}",
"profile_selected": "Избран профил: {profile}",
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
}
}

View File

@@ -6,17 +6,23 @@
"register": "Neues Cursor-Konto Registrieren",
"register_google": "Mit Google-Konto Registrieren",
"register_github": "Mit GitHub-Konto Registrieren",
"register_manual": "Cursor mit Benutzerdefinierter E-Mail Registrieren",
"register_manual": "Cursor Mit Benutzerdefinierter E-Mail Registrieren",
"quit": "Cursor-Anwendung Schließen",
"select_language": "Sprache Ändern",
"input_choice": "Bitte Auswahl eingeben ({choices})",
"select_chrome_profile": "Chrome-Profil Auswählen",
"input_choice": "Bitte geben Sie Ihre Auswahl ein ({choices})",
"invalid_choice": "Ungültige Auswahl. Bitte eine Nummer von {choices} eingeben",
"program_terminated": "Programm wurde vom Benutzer beendet",
"error_occurred": "Ein Fehler ist aufgetreten: {error}. Bitte erneut versuchen",
"press_enter": "Drücken Sie Enter zum Beenden",
"disable_auto_update": "Cursor Auto-Update Deaktivieren",
"lifetime_access_enabled": "LEBENSLANGER ZUGRIFF AKTIVIERT",
"totally_reset": "Cursor Vollständig Zurücksetzen"
"totally_reset": "Cursor Vollständig Zurücksetzen",
"outdate": "Veraltet",
"temp_github_register": "Temporäre GitHub-Registrierung",
"admin_required": "Ausführen als ausführbare Datei, Administratorrechte erforderlich.",
"admin_required_continue": "Mit der aktuellen Version fortfahren...",
"coming_soon": "Bald verfügbar"
},
"languages": {
"en": "Englisch",
@@ -27,7 +33,8 @@
"de": "Deutsch",
"fr": "Französisch",
"pt": "Portugiesisch",
"ru": "Russisch"
"ru": "Russisch",
"es": "Spanisch"
},
"quit_cursor": {
"start": "Beginne Cursor zu Beenden",
@@ -370,5 +377,17 @@
"electron_localstorage_files_removed": "Electron localStorage-Dateien entfernt",
"electron_localstorage_files_removal_error": "Fehler beim Entfernen von Electron localStorage-Dateien: {error}",
"removing_electron_localstorage_files_completed": "Entfernen von Electron localStorage-Dateien abgeschlossen"
},
"chrome_profile": {
"title": "Chrome-Profil Auswahl",
"select_profile": "Wählen Sie ein Chrome-Profil zum Verwenden:",
"profile_list": "Verfügbare Profile:",
"default_profile": "Standard-Profil",
"profile": "Profil {number}",
"no_profiles": "Keine Chrome-Profile gefunden",
"error_loading": "Fehler beim Laden der Chrome-Profile: {error}",
"profile_selected": "Ausgewähltes Profil: {profile}",
"invalid_selection": "Ungültige Auswahl. Bitte versuchen Sie es erneut",
"warning_chrome_close": "Warnung: Dies wird alle laufenden Chrome-Prozesse beenden"
}
}

View File

@@ -9,6 +9,7 @@
"register_manual": "Register Cursor with Custom Email",
"quit": "Close Cursor Application",
"select_language": "Change Language",
"select_chrome_profile": "Select Chrome Profile",
"input_choice": "Please enter your choice ({choices})",
"invalid_choice": "Invalid selection. Please enter a number from {choices}",
"program_terminated": "Program was terminated by user",
@@ -16,7 +17,19 @@
"press_enter": "Press Enter to Exit",
"disable_auto_update": "Disable Cursor Auto-Update",
"lifetime_access_enabled": "LIFETIME ACCESS ENABLED",
"totally_reset": "Totally Reset Cursor"
"totally_reset": "Totally Reset Cursor",
"outdate": "Outdated",
"temp_github_register": "Temporary GitHub Register",
"admin_required": "Running as executable, administrator privileges required.",
"admin_required_continue": "Continuing without administrator privileges.",
"coming_soon": "Coming Soon",
"fixed_soon": "Fixed Soon",
"contribute": "Contribute to the Project",
"config": "Show Config",
"delete_google_account": "Delete Cursor Google Account",
"continue_prompt": "Continue? (y/N): ",
"operation_cancelled_by_user": "Operation cancelled by user",
"exiting": "Exiting ……"
},
"languages": {
"en": "English",
@@ -27,7 +40,10 @@
"de": "German",
"fr": "French",
"pt": "Portuguese",
"ru": "Russian"
"ru": "Russian",
"tr": "Turkish",
"bg": "Bulgarian",
"es": "Spanish"
},
"quit_cursor": {
"start": "Start Quitting Cursor",
@@ -94,8 +110,13 @@
"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"
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
"no_write_permission": "No Write Permission: {path}",
"path_not_found": "Path Not Found: {path}",
"modify_file_failed": "Modify File Failed: {error}",
"windows_machine_id_updated": "Windows Machine ID Updated Successfully",
"update_windows_machine_id_failed": "Update Windows Machine ID Failed: {error}",
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}"
},
"register": {
"title": "Cursor Registration Tool",
@@ -264,7 +285,13 @@
"available_domains_loaded": "Available Domains Loaded: {count}",
"domains_filtered": "Domains Filtered: {count}",
"trying_to_create_email": "Trying to create email: {email}",
"domain_blocked": "Domain Blocked: {domain}"
"domain_blocked": "Domain Blocked: {domain}",
"using_chrome_profile": "Using Chrome profile from: {user_data_dir}",
"no_display_found": "No display found. Make sure X server is running.",
"try_export_display": "Try: export DISPLAY=:0",
"extension_load_error": "Extension Load Error: {error}",
"make_sure_chrome_chromium_is_properly_installed": "Make sure Chrome/Chromium is properly installed",
"try_install_chromium": "Try: sudo apt install chromium-browser"
},
"update": {
"title": "Disable Cursor Auto Update",
@@ -277,7 +304,23 @@
"removing_directory": "Removing Directory",
"directory_removed": "Directory Removed",
"creating_block_file": "Creating Block File",
"block_file_created": "Block File Created"
"block_file_created": "Block File Created",
"clearing_update_yml": "Clearing update.yml file",
"update_yml_cleared": "update.yml file cleared",
"update_yml_not_found": "update.yml file not found",
"clear_update_yml_failed": "Failed to clear update.yml file: {error}",
"unsupported_os": "Unsupported OS: {system}",
"remove_directory_failed": "Failed to remove directory: {error}",
"create_block_file_failed": "Failed to create block file: {error}",
"directory_locked": "Directory is locked: {path}",
"yml_locked": "update.yml file is locked",
"block_file_locked": "block file is locked",
"yml_already_locked": "update.yml file is already locked",
"block_file_already_locked": "block file is already locked",
"block_file_locked_error": "Block file locked error: {error}",
"yml_locked_error": "update.yml file locked error: {error}",
"block_file_already_locked_error": "Block file already locked error: {error}",
"yml_already_locked_error": "update.yml file already locked error: {error}"
},
"updater": {
"checking": "Checking for updates...",
@@ -290,7 +333,8 @@
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
"development_version": "Development Version {current} > {latest}",
"changelog_title": "Changelog"
"changelog_title": "Changelog",
"rate_limit_exceeded": "GitHub API rate limit exceeded. Skipping update check."
},
"totally_reset": {
"title": "Totally Reset Cursor",
@@ -381,6 +425,249 @@
"removing_electron_localstorage_files": "Removing Electron localStorage files",
"electron_localstorage_files_removed": "Electron localStorage files removed",
"electron_localstorage_files_removal_error": "Error removing Electron localStorage files: {error}",
"removing_electron_localstorage_files_completed": "Electron localStorage files removal completed"
"removing_electron_localstorage_files_completed": "Electron localStorage files removal completed",
"warning_title": "WARNING",
"warning_1": "This action will delete all Cursor AI settings,",
"warning_2": "configurations, and cached data. This action cannot be undone.",
"warning_3": "Your code files will NOT be affected, and the tool is designed",
"warning_4": "to only target Cursor AI editor files and trial detection mechanisms.",
"warning_5": "Other applications on your system will not be affected.",
"warning_6": "You will need to set up Cursor AI again after running this tool.",
"warning_7": "Use at your own risk",
"removed": "Removed: {path}",
"failed_to_reset_machine_guid": "Failed to reset machine GUID",
"failed_to_remove": "Failed to remove: {path}",
"failed_to_delete_file": "Failed to delete file: {path}",
"failed_to_delete_directory": "Failed to delete directory: {path}",
"failed_to_delete_file_or_directory": "Failed to delete file or directory: {path}",
"deep_scanning": "Performing deep scan for additional trial/license files",
"resetting_cursor": "Resetting Cursor AI Editor... Please wait.",
"completed_in": "Completed in {time} seconds",
"cursor_reset_completed": "Cursor AI Editor has been fully reset and trial detection bypassed!",
"cursor_reset_failed": "Cursor AI Editor reset failed: {error}",
"cursor_reset_cancelled": "Cursor AI Editor reset cancelled. Exiting without making any changes.",
"operation_cancelled": "Operation cancelled. Exiting without making any changes.",
"navigating_to_settings": "Navigating to settings page...",
"already_on_settings": "Already on settings page",
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
"advanced_tab_error": "Error finding Advanced tab: {error}",
"advanced_tab_clicked": "Clicked on Advanced tab",
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
"delete_button_not_found": "Delete Account button not found after multiple attempts",
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
"delete_button_error": "Error finding Delete button: {error}",
"delete_button_clicked": "Clicked on Delete Account button",
"found_danger_zone": "Found Danger Zone section",
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
"delete_input_error": "Error finding Delete input: {error}",
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway"
},
"github_register": {
"title": "GitHub + Cursor AI Registration Automation",
"features_header": "Features",
"feature1": "Generates a temporary email using 1secmail.",
"feature2": "Registers a new GitHub account with random credentials.",
"feature3": "Verifies the GitHub email automatically.",
"feature4": "Logs into Cursor AI using GitHub authentication.",
"feature5": "Resets the machine ID to bypass trial detection.",
"feature6": "Saves all credentials to a file.",
"warnings_header": "Warnings",
"warning1": "This script automates account creation, which may violate GitHub/Cursor terms of service.",
"warning2": "Requires internet access and administrative privileges.",
"warning3": "CAPTCHA or additional verification may interrupt automation.",
"warning4": "Use responsibly and at your own risk.",
"confirm": "Are you sure you want to proceed?",
"invalid_choice": "Invalid choice. Please enter 'yes' or 'no'",
"cancelled": "Operation cancelled",
"program_terminated": "Program terminated by user",
"starting_automation": "Starting automation...",
"github_username": "GitHub Username",
"github_password": "GitHub Password",
"email_address": "Email Address",
"credentials_saved": "These credentials have been saved to github_cursor_accounts.txt",
"completed_successfully": "GitHub + Cursor registration completed successfully!",
"registration_encountered_issues": "GitHub + Cursor registration encountered issues.",
"check_browser_windows_for_manual_intervention_or_try_again_later": "Check browser windows for manual intervention or try again later."
},
"account_info": {
"subscription": "Subscription",
"trial_remaining": "Remaining Pro Trial",
"days": "days",
"subscription_not_found": "Subscription information not found",
"email_not_found": "Email not found",
"failed_to_get_account": "Failed to get account information",
"config_not_found": "Configuration not found.",
"failed_to_get_usage": "Failed to get usage information",
"failed_to_get_subscription": "Failed to get subscription information",
"failed_to_get_email": "Failed to get email address",
"failed_to_get_token": "Failed to get token",
"failed_to_get_account_info": "Failed to get account information",
"title": "Account Information",
"email": "Email",
"token": "Token",
"usage": "Usage",
"subscription_type": "Subscription Type",
"remaining_trial": "Remaining Trial",
"days_remaining": "Days Remaining",
"premium": "Premium",
"pro": "Pro",
"pro_trial": "Pro Trial",
"team": "Team",
"enterprise": "Enterprise",
"free": "Free",
"active": "Active",
"inactive": "Inactive",
"premium_usage": "Premium Usage",
"basic_usage": "Basic Usage",
"usage_not_found": "Usage not found",
"lifetime_access_enabled": "Lifetime Access Enabled"
},
"config": {
"config_not_available": "Configuration not available",
"configuration": "Configuration",
"enabled": "Enabled",
"disabled": "Disabled",
"config_directory": "Config Directory",
"neither_cursor_nor_cursor_directory_found": "Neither Cursor nor Cursor directory found in {config_base}",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Please make sure Cursor is installed and has been run at least once",
"storage_directory_not_found": "Storage directory not found: {storage_dir}",
"storage_file_found": "Storage file found: {storage_path}",
"file_size": "File size: {size} bytes",
"file_permissions": "File permissions: {permissions}",
"file_owner": "File owner: {owner}",
"file_group": "File group: {group}",
"error_getting_file_stats": "Error getting file stats: {error}",
"permission_denied": "Permission denied: {storage_path}",
"try_running": "Try running: {command}",
"and": "And",
"storage_file_is_empty": "Storage file is empty: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
"storage_file_not_found": "Storage file not found: {storage_path}",
"error_checking_linux_paths": "Error checking Linux paths: {error}",
"config_option_added": "Config option added: {option}",
"config_updated": "Config updated",
"config_created": "Config created: {config_file}",
"config_setup_error": "Error setting up config: {error}",
"storage_file_is_valid_and_contains_data": "Storage file is valid and contains data",
"error_reading_storage_file": "Error reading storage file: {error}",
"also_checked": "Also checked {path}"
},
"oauth": {
"authentication_button_not_found": "Authentication button not found",
"authentication_failed": "Authentication failed: {error}",
"found_cookies": "Found {count} cookies",
"token_extraction_error": "Token extraction error: {error}",
"authentication_successful": "Authentication successful - Email: {email}",
"missing_authentication_data": "Missing authentication data: {data}",
"failed_to_delete_account": "Failed to delete account: {error}",
"invalid_authentication_type": "Invalid authentication type",
"auth_update_success": "Auth update success",
"browser_closed": "Browser closed",
"auth_update_failed": "Auth update failed",
"google_start": "Google start",
"github_start": "Github start",
"usage_count": "Usage count: {usage}",
"account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
"starting_new_authentication_process": "Starting new authentication process...",
"failed_to_delete_expired_account": "Failed to delete expired account",
"could_not_check_usage_count": "Could not check usage count: {error}",
"found_email": "Found email: {email}",
"could_not_find_email": "Could not find email: {error}",
"could_not_find_usage_count": "Could not find usage count: {error}",
"already_on_settings_page": "Already on settings page!",
"failed_to_extract_auth_info": "Failed to extract auth info: {error}",
"no_chrome_profiles_found": "No Chrome profiles found, using Default",
"found_default_chrome_profile": "Found Default Chrome profile",
"using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
"error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
"initializing_browser_setup": "Initializing browser setup...",
"detected_platform": "Detected platform: {platform}",
"running_as_root_warning": "Running as root is not recommended for browser automation",
"consider_running_without_sudo": "Consider running the script without sudo",
"no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
"supported_browsers": "Supported browsers for {platform}",
"using_browser_profile": "Using browser profile: {profile}",
"starting_browser": "Starting browser at: {path}",
"browser_setup_completed": "Browser setup completed successfully",
"browser_setup_failed": "Browser setup failed: {error}",
"try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
"redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
"starting_google_authentication": "Starting Google authentication...",
"starting_github_authentication": "Starting GitHub authentication...",
"waiting_for_authentication": "Waiting for authentication...",
"page_changed_checking_auth": "Page changed, checking auth...",
"status_check_error": "Status check error: {error}",
"authentication_timeout": "Authentication timeout",
"account_is_still_valid": "Account is still valid (Usage: {usage})",
"starting_re_authentication_process": "Starting re-authentication process...",
"starting_new_google_authentication": "Starting new Google authentication...",
"failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
"navigating_to_authentication_page": "Navigating to authentication page...",
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
"found_browser_data_directory": "Found browser data directory: {path}",
"authentication_successful_getting_account_info": "Authentication successful, getting account info...",
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
"browser_failed_to_start": "Browser failed to start: {error}",
"browser_failed": "Browser failed to start: {error}",
"browser_failed_to_start_fallback": "Browser failed to start: {error}"
},
"chrome_profile": {
"title": "Chrome Profile Selection",
"select_profile": "Select a Chrome profile to use:",
"profile_list": "Available profiles:",
"default_profile": "Default Profile",
"profile": "Profile {number}",
"no_profiles": "No Chrome profiles found",
"error_loading": "Error loading Chrome profiles: {error}",
"profile_selected": "Selected profile: {profile}",
"invalid_selection": "Invalid selection. Please try again",
"warning_chrome_close": "Warning: This will close all running Chrome processes"
},
"account_delete": {
"title": "Cursor Google Account Deletion Tool",
"warning": "WARNING: This will permanently delete your Cursor account. This action cannot be undone.",
"cancelled": "Account deletion cancelled.",
"starting_process": "Starting account deletion process...",
"google_button_not_found": "Google login button not found",
"logging_in": "Logging in with Google...",
"waiting_for_auth": "Waiting for Google authentication...",
"login_successful": "Login successful",
"unexpected_page": "Unexpected page after login: {url}",
"trying_settings": "Trying to navigate to settings page...",
"select_google_account": "Please select your Google account...",
"auth_timeout": "Authentication timeout, continuing anyway...",
"navigating_to_settings": "Navigating to settings page...",
"already_on_settings": "Already on settings page",
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
"advanced_tab_error": "Error finding Advanced tab: {error}",
"advanced_tab_clicked": "Clicked on Advanced tab",
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
"delete_button_not_found": "Delete Account button not found after multiple attempts",
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
"delete_button_error": "Error finding Delete button: {error}",
"delete_button_clicked": "Clicked on Delete Account button",
"found_danger_zone": "Found Danger Zone section",
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
"delete_input_error": "Error finding Delete input: {error}",
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway",
"typed_delete": "Typed \"Delete\" in confirmation box",
"confirm_button_not_found": "Confirm button not found after multiple attempts",
"confirm_button_retry": "Confirm button not found, attempt {attempt}/{max_attempts}",
"confirm_button_error": "Error finding Confirm button: {error}",
"account_deleted": "Account deleted successfully!",
"error": "Error during account deletion: {error}",
"success": "Your Cursor account has been successfully deleted!",
"failed": "Account deletion process failed or was cancelled.",
"interrupted": "Account deletion process interrupted by user.",
"unexpected_error": "Unexpected error: {error}",
"found_email": "Found email: {email}",
"email_not_found": "Email not found: {error}",
"confirm_prompt": "Are you sure you want to proceed? (y/N): "
}
}
}

455
locales/es.json Normal file
View File

@@ -0,0 +1,455 @@
{
"menu": {
"title": "Opciones Disponibles",
"exit": "Salir del Programa",
"reset": "Restablecer ID de Máquina",
"register": "Registrar Nueva Cuenta de Cursor",
"register_google": "Registrarse con Cuenta de Google",
"register_github": "Registrarse con Cuenta de GitHub",
"register_manual": "Registrar Cursor con Correo Personalizado",
"quit": "Cerrar Aplicación Cursor",
"select_language": "Cambiar Idioma",
"input_choice": "Por favor, ingrese su elección ({choices})",
"invalid_choice": "Selección inválida. Por favor ingrese un número de {choices}",
"program_terminated": "El programa fue terminado por el usuario",
"error_occurred": "Ocurrió un error: {error}. Por favor intente de nuevo",
"press_enter": "Presione Enter para Salir",
"disable_auto_update": "Deshabilitar Actualización Automática de Cursor",
"lifetime_access_enabled": "ACCESO DE POR VIDA ACTIVADO",
"totally_reset": "Restablecer Cursor Completamente",
"outdate": "Desactualizado",
"temp_github_register": "Registro Temporal de GitHub",
"admin_required": "Ejecutando como ejecutable, se requieren privilegios de administrador.",
"admin_required_continue": "Continuando sin privilegios de administrador.",
"coming_soon": "Próximamente",
"fixed_soon": "Arreglado Pronto"
},
"languages": {
"en": "Inglés",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
"vi": "Vietnamita",
"nl": "Holandés",
"de": "Alemán",
"fr": "Francés",
"pt": "Portugués",
"ru": "Ruso",
"tr": "Turco",
"bg": "Búlgaro",
"es": "Español"
},
"quit_cursor": {
"start": "Comenzando a Cerrar Cursor",
"no_process": "No Hay Procesos de Cursor en Ejecución",
"terminating": "Terminando Proceso {pid}",
"waiting": "Esperando que el Proceso Termine",
"success": "Todos los Procesos de Cursor Cerrados",
"timeout": "Tiempo de Espera Agotado: {pids}",
"error": "Ocurrió un Error: {error}"
},
"reset": {
"title": "Herramienta de Restablecimiento de ID de Máquina de Cursor",
"checking": "Verificando Archivo de Configuración",
"not_found": "Archivo de Configuración No Encontrado",
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
"reading": "Leyendo Configuración Actual",
"creating_backup": "Creando Copia de Seguridad de la Configuración",
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
"generating": "Generando Nuevo ID de Máquina",
"saving_json": "Guardando Nueva Configuración en JSON",
"success": "ID de Máquina Restablecido Exitosamente",
"new_id": "Nuevo ID de Máquina",
"permission_error": "Error de Permisos: {error}",
"run_as_admin": "Por Favor Intente Ejecutar Este Programa como Administrador",
"process_error": "Error en el Proceso de Restablecimiento: {error}",
"updating_sqlite": "Actualizando Base de Datos SQLite",
"updating_pair": "Actualizando Par Clave-Valor",
"sqlite_success": "Base de Datos SQLite Actualizada Exitosamente",
"sqlite_error": "Falló la Actualización de la Base de Datos SQLite: {error}",
"press_enter": "Presione Enter para Salir",
"unsupported_os": "Sistema Operativo No Soportado: {os}",
"linux_path_not_found": "Ruta de Linux No Encontrada",
"updating_system_ids": "Actualizando IDs del Sistema",
"system_ids_updated": "IDs del Sistema Actualizados Exitosamente",
"system_ids_update_failed": "Falló la Actualización de IDs del Sistema: {error}",
"windows_guid_updated": "GUID de Windows Actualizado Exitosamente",
"windows_permission_denied": "Permisos de Windows Denegados",
"windows_guid_update_failed": "Falló la Actualización del GUID de Windows",
"macos_uuid_updated": "UUID de macOS Actualizado Exitosamente",
"plutil_command_failed": "Falló el Comando plutil",
"start_patching": "Iniciando Parcheo de getMachineId",
"macos_uuid_update_failed": "Falló la Actualización del UUID de macOS",
"current_version": "Versión Actual de Cursor: {version}",
"patch_completed": "Parcheo de getMachineId Completado",
"patch_failed": "Falló el Parcheo de getMachineId: {error}",
"version_check_passed": "Verificación de Versión de Cursor Exitosa",
"file_modified": "Archivo Modificado",
"version_less_than_0_45": "Versión de Cursor < 0.45.0, Omitiendo Parcheo de getMachineId",
"detecting_version": "Detectando Versión de Cursor",
"patching_getmachineid": "Parcheando getMachineId",
"version_greater_than_0_45": "Versión de Cursor >= 0.45.0, Parcheando getMachineId",
"permission_denied": "Permiso Denegado: {error}",
"backup_created": "Copia de Seguridad Creada",
"update_success": "Actualización Exitosa",
"update_failed": "Falló la Actualización: {error}",
"windows_machine_guid_updated": "GUID de Máquina Windows Actualizado Exitosamente",
"reading_package_json": "Leyendo package.json {path}",
"invalid_json_object": "Objeto JSON Inválido",
"no_version_field": "No se Encontró el Campo de Versión en package.json",
"version_field_empty": "El Campo de Versión está Vacío",
"invalid_version_format": "Formato de Versión Inválido: {version}",
"found_version": "Versión Encontrada: {version}",
"version_parse_error": "Error al Analizar Versión: {error}",
"package_not_found": "Package.json No Encontrado: {path}",
"check_version_failed": "Falló la Verificación de Versión: {error}",
"stack_trace": "Traza de la Pila",
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0"
},
"register": {
"title": "Herramienta de Registro de Cursor",
"start": "Iniciando proceso de registro...",
"handling_turnstile": "Procesando verificación de seguridad...",
"retry_verification": "Reintentando verificación...",
"detect_turnstile": "Comprobando verificación de seguridad...",
"verification_success": "Verificación de seguridad exitosa",
"starting_browser": "Abriendo navegador...",
"form_success": "Formulario enviado exitosamente",
"browser_started": "Navegador abierto exitosamente",
"waiting_for_second_verification": "Esperando verificación por correo electrónico...",
"waiting_for_verification_code": "Esperando código de verificación...",
"password_success": "Contraseña establecida exitosamente",
"password_error": "No se pudo establecer la contraseña: {error}. Por favor intente de nuevo",
"waiting_for_page_load": "Cargando página...",
"first_verification_passed": "Verificación inicial exitosa",
"mailbox": "Acceso exitoso a la bandeja de entrada",
"register_start": "Iniciar Registro",
"form_submitted": "Formulario Enviado, Iniciando Verificación...",
"filling_form": "Rellenando Formulario",
"visiting_url": "Visitando URL",
"basic_info": "Información Básica Enviada",
"handle_turnstile": "Manejar Turnstile",
"no_turnstile": "No se Detectó Turnstile",
"turnstile_passed": "Turnstile Superado",
"verification_start": "Comenzar a Obtener Código de Verificación",
"verification_timeout": "Tiempo de Espera Agotado para Código de Verificación",
"verification_not_found": "No se Encontró Código de Verificación",
"try_get_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {time}s",
"get_account": "Obteniendo Información de la Cuenta",
"get_token": "Obtener Token de Sesión de Cursor",
"token_success": "Token Obtenido Exitosamente",
"token_attempt": "Intento | {attempt} veces para obtener Token | Se reintentará en {time}s",
"token_max_attempts": "Alcanzado Máximo de Intentos ({max}) | No se pudo obtener Token",
"token_failed": "Falló al Obtener Token: {error}",
"account_error": "Falló al Obtener Información de la Cuenta: {error}",
"press_enter": "Presione Enter para Salir",
"browser_start": "Iniciando Navegador",
"open_mailbox": "Abriendo Página de Correo",
"email_error": "Falló al Obtener Dirección de Correo",
"setup_error": "Error de Configuración de Correo: {error}",
"start_getting_verification_code": "Comenzando a Obtener Código de Verificación, Se Intentará en 60s",
"get_verification_code_timeout": "Tiempo de Espera Agotado para Código de Verificación",
"get_verification_code_success": "Código de Verificación Obtenido Exitosamente",
"try_get_verification_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {remaining_time}s",
"verification_code_filled": "Código de Verificación Completado",
"login_success_and_jump_to_settings_page": "Inicio de Sesión Exitoso y Salto a Página de Configuración",
"detect_login_page": "Página de Inicio de Sesión Detectada, Iniciando Sesión...",
"cursor_registration_completed": "¡Registro de Cursor Completado!",
"set_password": "Establecer Contraseña",
"basic_info_submitted": "Información Básica Enviada",
"cursor_auth_info_updated": "Información de Autenticación de Cursor Actualizada",
"cursor_auth_info_update_failed": "Falló la Actualización de Información de Autenticación de Cursor",
"reset_machine_id": "Restablecer ID de Máquina",
"account_info_saved": "Información de Cuenta Guardada",
"save_account_info_failed": "Falló al Guardar Información de Cuenta",
"get_email_address": "Obtener Dirección de Correo",
"update_cursor_auth_info": "Actualizar Información de Autenticación de Cursor",
"register_process_error": "Error en el Proceso de Registro: {error}",
"setting_password": "Estableciendo Contraseña",
"manual_code_input": "Entrada Manual de Código",
"manual_email_input": "Entrada Manual de Correo",
"password": "Contraseña",
"first_name": "Nombre",
"last_name": "Apellido",
"exit_signal": "Señal de Salida",
"email_address": "Dirección de Correo",
"config_created": "Configuración Creada",
"verification_failed": "Verificación Fallida",
"verification_error": "Error de Verificación: {error}",
"config_option_added": "Opción de Configuración Añadida: {option}",
"config_updated": "Configuración Actualizada",
"password_submitted": "Contraseña Enviada",
"total_usage": "Uso Total: {usage}",
"setting_on_password": "Estableciendo Contraseña",
"getting_code": "Obteniendo Código de Verificación, Se Intentará en 60s",
"human_verify_error": "No se puede verificar que el usuario es humano. Reintentando...",
"max_retries_reached": "Se alcanzó el máximo de intentos. Registro fallido."
},
"auth": {
"title": "Administrador de Autenticación de Cursor",
"checking_auth": "Verificando Archivo de Autenticación",
"auth_not_found": "Archivo de Autenticación No Encontrado",
"auth_file_error": "Error en Archivo de Autenticación: {error}",
"reading_auth": "Leyendo Archivo de Autenticación",
"updating_auth": "Actualizando Información de Autenticación",
"auth_updated": "Información de Autenticación Actualizada Exitosamente",
"auth_update_failed": "Falló la Actualización de Información de Autenticación: {error}",
"auth_file_created": "Archivo de Autenticación Creado",
"auth_file_create_failed": "Falló la Creación del Archivo de Autenticación: {error}",
"press_enter": "Presione Enter para Salir",
"reset_machine_id": "Restablecer ID de Máquina",
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
"connected_to_database": "Conectado a la Base de Datos",
"updating_pair": "Actualizando Par Clave-Valor",
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
"db_connection_error": "Falló la conexión a la base de datos: {error}"
},
"control": {
"generate_email": "Generando Nuevo Correo",
"blocked_domain": "Dominio Bloqueado",
"select_domain": "Seleccionando Dominio Aleatorio",
"copy_email": "Copiando Dirección de Correo",
"enter_mailbox": "Entrando al Buzón de Correo",
"refresh_mailbox": "Actualizando Buzón de Correo",
"check_verification": "Verificando Código de Verificación",
"verification_found": "Código de Verificación Encontrado",
"verification_not_found": "No se Encontró Código de Verificación",
"browser_error": "Error de Control del Navegador: {error}",
"navigation_error": "Error de Navegación: {error}",
"email_copy_error": "Error al Copiar Correo: {error}",
"mailbox_error": "Error en el Buzón de Correo: {error}",
"token_saved_to_file": "Token Guardado en cursor_tokens.txt",
"navigate_to": "Navegando a {url}",
"generate_email_success": "Generación de Correo Exitosa",
"select_email_domain": "Seleccionar Dominio de Correo",
"select_email_domain_success": "Selección de Dominio de Correo Exitosa",
"get_email_name": "Obtener Nombre de Correo",
"get_email_name_success": "Nombre de Correo Obtenido Exitosamente",
"get_email_address": "Obtener Dirección de Correo",
"get_email_address_success": "Dirección de Correo Obtenida Exitosamente",
"enter_mailbox_success": "Entrada al Buzón de Correo Exitosa",
"found_verification_code": "Código de Verificación Encontrado",
"get_cursor_session_token": "Obtener Token de Sesión de Cursor",
"get_cursor_session_token_success": "Token de Sesión de Cursor Obtenido Exitosamente",
"get_cursor_session_token_failed": "Falló al Obtener Token de Sesión de Cursor",
"save_token_failed": "Falló al Guardar Token",
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
"no_valid_verification_code": "No Hay Código de Verificación Válido"
},
"email": {
"starting_browser": "Iniciando Navegador",
"visiting_site": "Visitando dominios de correo",
"create_success": "Correo Creado Exitosamente",
"create_failed": "Falló al Crear Correo",
"create_error": "Error en la Creación del Correo: {error}",
"refreshing": "Actualizando Correo",
"refresh_success": "Correo Actualizado Exitosamente",
"refresh_error": "Error al Actualizar Correo: {error}",
"refresh_button_not_found": "Botón de Actualización No Encontrado",
"verification_found": "Verificación Encontrada",
"verification_not_found": "Verificación No Encontrada",
"verification_error": "Error de Verificación: {error}",
"verification_code_found": "Código de Verificación Encontrado",
"verification_code_not_found": "Código de Verificación No Encontrado",
"verification_code_error": "Error en el Código de Verificación: {error}",
"address": "Dirección de Correo",
"all_domains_blocked": "Todos los Dominios Bloqueados, Cambiando Servicio",
"no_available_domains_after_filtering": "No Hay Dominios Disponibles Después del Filtrado",
"switching_service": "Cambiando al Servicio {service}",
"domains_list_error": "Falló al Obtener Lista de Dominios: {error}",
"failed_to_get_available_domains": "Falló al Obtener Dominios Disponibles",
"domains_excluded": "Dominios Excluidos: {domains}",
"failed_to_create_account": "Falló al Crear Cuenta",
"account_creation_error": "Error en la Creación de la Cuenta: {error}",
"blocked_domains": "Dominios Bloqueados: {domains}",
"blocked_domains_loaded": "Dominios Bloqueados Cargados: {count}",
"blocked_domains_loaded_error": "Error al Cargar Dominios Bloqueados: {error}",
"blocked_domains_loaded_success": "Dominios Bloqueados Cargados Exitosamente",
"blocked_domains_loaded_timeout": "Tiempo de Espera Agotado para Cargar Dominios Bloqueados: {timeout}s",
"blocked_domains_loaded_timeout_error": "Error de Tiempo de Espera al Cargar Dominios Bloqueados: {error}",
"available_domains_loaded": "Dominios Disponibles Cargados: {count}",
"domains_filtered": "Dominios Filtrados: {count}",
"trying_to_create_email": "Intentando crear correo: {email}",
"domain_blocked": "Dominio Bloqueado: {domain}"
},
"update": {
"title": "Deshabilitar Actualización Automática de Cursor",
"disable_success": "Actualización Automática Deshabilitada Exitosamente",
"disable_failed": "Falló al Deshabilitar Actualización Automática: {error}",
"press_enter": "Presione Enter para Salir",
"start_disable": "Comenzar a Deshabilitar Actualización Automática",
"killing_processes": "Terminando Procesos",
"processes_killed": "Procesos Terminados",
"removing_directory": "Eliminando Directorio",
"directory_removed": "Directorio Eliminado",
"creating_block_file": "Creando Archivo de Bloqueo",
"block_file_created": "Archivo de Bloqueo Creado"
},
"updater": {
"checking": "Buscando actualizaciones...",
"new_version_available": "¡Nueva versión disponible! (Actual: {current}, Última: {latest})",
"updating": "Actualizando a la última versión. El programa se reiniciará automáticamente.",
"up_to_date": "Está utilizando la última versión.",
"check_failed": "Falló al verificar actualizaciones: {error}",
"continue_anyway": "Continuando con la versión actual...",
"update_confirm": "¿Desea actualizar a la última versión? (Y/n)",
"update_skipped": "Omitiendo actualización.",
"invalid_choice": "Elección inválida. Por favor ingrese 'Y' o 'n'.",
"development_version": "Versión de Desarrollo {current} > {latest}",
"changelog_title": "Registro de Cambios"
},
"totally_reset": {
"title": "Restablecer Cursor Completamente",
"checking_config": "Verificando Archivo de Configuración",
"config_not_found": "Archivo de Configuración No Encontrado",
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
"reading_config": "Leyendo Configuración Actual",
"creating_backup": "Creando Copia de Seguridad de la Configuración",
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
"generating_new_machine_id": "Generando Nuevo ID de Máquina",
"saving_new_config": "Guardando Nueva Configuración en JSON",
"success": "Cursor Restablecido Exitosamente",
"error": "Falló el Restablecimiento de Cursor: {error}",
"press_enter": "Presione Enter para Salir",
"reset_machine_id": "Restablecer ID de Máquina",
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
"connected_to_database": "Conectado a la Base de Datos",
"updating_pair": "Actualizando Par Clave-Valor",
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
"db_connection_error": "Falló la conexión a la base de datos: {error}",
"feature_title": "CARACTERÍSTICAS",
"feature_1": "Eliminación completa de configuraciones y ajustes de Cursor AI",
"feature_2": "Limpia todos los datos en caché incluyendo historial de IA y peticiones",
"feature_3": "Restablece el ID de máquina para evitar la detección de prueba",
"feature_4": "Crea nuevos identificadores de máquina aleatorios",
"feature_5": "Elimina extensiones personalizadas y preferencias",
"feature_6": "Restablece información de prueba y datos de activación",
"feature_7": "Escaneo profundo de archivos ocultos relacionados con licencias y pruebas",
"feature_8": "Preserva de forma segura archivos y aplicaciones que no son de Cursor",
"feature_9": "Compatible con Windows, macOS y Linux",
"disclaimer_title": "AVISO IMPORTANTE",
"disclaimer_1": "Esta herramienta eliminará permanentemente todas las configuraciones de Cursor AI,",
"disclaimer_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
"disclaimer_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
"disclaimer_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
"disclaimer_5": "Otras aplicaciones en su sistema no se verán afectadas.",
"disclaimer_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
"disclaimer_7": "Use bajo su propio riesgo",
"confirm_title": "¿Está seguro de que desea continuar?",
"confirm_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
"confirm_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
"confirm_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
"confirm_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
"confirm_5": "Otras aplicaciones en su sistema no se verán afectadas.",
"confirm_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
"confirm_7": "Use bajo su propio riesgo",
"invalid_choice": "Por favor ingrese 'Y' o 'n'",
"skipped_for_safety": "Omitido por seguridad (no relacionado con Cursor): {path}",
"deleted": "Eliminado: {path}",
"error_deleting": "Error al eliminar {path}: {error}",
"not_found": "Archivo no encontrado: {path}",
"resetting_machine_id": "Restableciendo identificadores de máquina para evitar la detección de prueba...",
"created_machine_id": "Creado nuevo ID de máquina: {path}",
"error_creating_machine_id": "Error al crear archivo de ID de máquina {path}: {error}",
"error_searching": "Error al buscar archivos en {path}: {error}",
"created_extended_trial_info": "Creada nueva información de prueba extendida: {path}",
"error_creating_trial_info": "Error al crear archivo de información de prueba {path}: {error}",
"resetting_cursor_ai_editor": "Restableciendo Editor Cursor AI... Por favor espere.",
"reset_cancelled": "Restablecimiento cancelado. Saliendo sin realizar cambios.",
"windows_machine_id_modification_skipped": "Modificación de ID de máquina de Windows omitida: {error}",
"linux_machine_id_modification_skipped": "Modificación de machine-id de Linux omitida: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Nota: El restablecimiento completo del ID de máquina puede requerir ejecutar como administrador",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Nota: El restablecimiento completo del machine-id del sistema puede requerir privilegios sudo",
"windows_registry_instructions": "📝 NOTA: Para un restablecimiento completo en Windows, es posible que también deba limpiar entradas del registro.",
"windows_registry_instructions_2": " Ejecute 'regedit' y busque claves que contengan 'Cursor' o 'CursorAI' bajo HKEY_CURRENT_USER\\Software\\ y elimínelas.",
"reset_log_1": "¡Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
"reset_log_2": "Por favor reinicie su sistema para que los cambios surtan efecto.",
"reset_log_3": "Necesitará reinstalar Cursor AI y ahora debería tener un nuevo período de prueba.",
"reset_log_4": "Para mejores resultados, considere también:",
"reset_log_5": "Usar una dirección de correo diferente al registrarse para una nueva prueba",
"reset_log_6": "Si está disponible, usar una VPN para cambiar su dirección IP",
"reset_log_7": "Limpiar las cookies y caché de su navegador antes de visitar el sitio web de Cursor AI",
"reset_log_8": "Si los problemas persisten, intente instalar Cursor AI en una ubicación diferente",
"reset_log_9": "Si encuentra algún problema, vaya al Rastreador de Problemas de Github y cree un problema en https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Ocurrió un error inesperado: {error}",
"report_issue": "Por favor reporte este problema al Rastreador de Problemas de Github en https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Proceso interrumpido por el usuario. Saliendo...",
"return_to_main_menu": "Volviendo al menú principal...",
"process_interrupted": "Proceso interrumpido. Saliendo...",
"press_enter_to_return_to_main_menu": "Presione Enter para volver al menú principal...",
"removing_known": "Eliminando archivos conocidos de prueba/licencia",
"performing_deep_scan": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
"found_additional_potential_license_trial_files": "Se encontraron {count} archivos potenciales adicionales de licencia/prueba",
"checking_for_electron_localstorage_files": "Verificando archivos de localStorage de Electron",
"no_additional_license_trial_files_found_in_deep_scan": "No se encontraron archivos adicionales de licencia/prueba en el escaneo profundo",
"removing_electron_localstorage_files": "Eliminando archivos de localStorage de Electron",
"electron_localstorage_files_removed": "Archivos de localStorage de Electron eliminados",
"electron_localstorage_files_removal_error": "Error al eliminar archivos de localStorage de Electron: {error}",
"removing_electron_localstorage_files_completed": "Eliminación de archivos de localStorage de Electron completada",
"warning_title": "ADVERTENCIA",
"warning_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
"warning_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
"warning_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
"warning_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
"warning_5": "Otras aplicaciones en su sistema no se verán afectadas.",
"warning_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
"warning_7": "Use bajo su propio riesgo",
"removed": "Eliminado: {path}",
"failed_to_reset_machine_guid": "Falló al restablecer GUID de máquina",
"failed_to_remove": "Falló al eliminar: {path}",
"failed_to_delete_file": "Falló al eliminar archivo: {path}",
"failed_to_delete_directory": "Falló al eliminar directorio: {path}",
"failed_to_delete_file_or_directory": "Falló al eliminar archivo o directorio: {path}",
"deep_scanning": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
"resetting_cursor": "Restableciendo Editor Cursor AI... Por favor espere.",
"completed_in": "Completado en {time} segundos",
"cursor_reset_completed": "¡El Editor Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
"cursor_reset_failed": "Falló el restablecimiento del Editor Cursor AI: {error}",
"cursor_reset_cancelled": "Restablecimiento del Editor Cursor AI cancelado. Saliendo sin realizar cambios.",
"operation_cancelled": "Operación cancelada. Saliendo sin realizar cambios."
},
"github_register": {
"title": "Automatización de Registro de GitHub + Cursor AI",
"features_header": "Características",
"feature1": "Genera un correo temporal usando 1secmail.",
"feature2": "Registra una nueva cuenta de GitHub con credenciales aleatorias.",
"feature3": "Verifica el correo de GitHub automáticamente.",
"feature4": "Inicia sesión en Cursor AI usando autenticación de GitHub.",
"feature5": "Restablece el ID de máquina para evitar la detección de prueba.",
"feature6": "Guarda todas las credenciales en un archivo.",
"warnings_header": "Advertencias",
"warning1": "Este script automatiza la creación de cuentas, lo que puede violar los términos de servicio de GitHub/Cursor.",
"warning2": "Requiere acceso a internet y privilegios administrativos.",
"warning3": "CAPTCHA o verificación adicional pueden interrumpir la automatización.",
"warning4": "Use responsablemente y bajo su propio riesgo.",
"confirm": "¿Está seguro de que desea continuar?",
"invalid_choice": "Elección inválida. Por favor ingrese 'yes' o 'no'",
"cancelled": "Operación cancelada",
"program_terminated": "Programa terminado por el usuario",
"starting_automation": "Iniciando automatización...",
"github_username": "Nombre de Usuario de GitHub",
"github_password": "Contraseña de GitHub",
"email_address": "Dirección de Correo",
"credentials_saved": "Estas credenciales han sido guardadas en github_cursor_accounts.txt",
"completed_successfully": "¡Registro de GitHub + Cursor completado exitosamente!",
"registration_encountered_issues": "El registro de GitHub + Cursor encontró problemas.",
"check_browser_windows_for_manual_intervention_or_try_again_later": "Revise las ventanas del navegador para intervención manual o intente nuevamente más tarde."
},
"chrome_profile": {
"title": "Selección de Perfil de Chrome",
"select_profile": "Seleccione un perfil de Chrome para usar:",
"profile_list": "Perfiles disponibles:",
"default_profile": "Perfil Predeterminado",
"profile": "Perfil {number}",
"no_profiles": "No se encontraron perfiles de Chrome",
"error_loading": "Error al cargar perfiles de Chrome: {error}",
"profile_selected": "Perfil seleccionado: {profile}",
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
}
}

View File

@@ -16,7 +16,10 @@
"press_enter": "Appuyez sur Entrée pour quitter",
"disable_auto_update": "Désactiver la Mise à Jour Automatique de Cursor",
"lifetime_access_enabled": "ACCÈS À VIE ACTIVÉ",
"totally_reset": "Réinitialisation Complète de Cursor"
"totally_reset": "Réinitialisation Complète de Cursor",
"outdate": "Obsolete",
"temp_github_register": "Inscription GitHub temporaire",
"coming_soon": "Bientôt"
},
"languages": {
"en": "Anglais",
@@ -27,7 +30,8 @@
"de": "Allemand",
"fr": "Français",
"pt": "Portugais",
"ru": "Russe"
"ru": "Russe",
"es": "Espagnol"
},
"quit_cursor": {
"start": "Début de la Fermeture de Cursor",
@@ -370,5 +374,17 @@
"electron_localstorage_files_removed": "Fichiers localStorage Electron supprimés",
"electron_localstorage_files_removal_error": "Erreur de suppression des fichiers localStorage Electron: {error}",
"removing_electron_localstorage_files_completed": "Suppression des fichiers localStorage Electron terminée"
},
"chrome_profile": {
"title": "Sélection du Profil Chrome",
"select_profile": "Sélectionnez un profil Chrome à utiliser :",
"profile_list": "Profils disponibles :",
"default_profile": "Profil par Défaut",
"profile": "Profil {number}",
"no_profiles": "Aucun profil Chrome trouvé",
"error_loading": "Erreur lors du chargement des profils Chrome : {error}",
"profile_selected": "Profil sélectionné : {profile}",
"invalid_selection": "Sélection invalide. Veuillez réessayer",
"warning_chrome_close": "Attention : Cela fermera tous les processus Chrome en cours d'exécution"
}
}

View File

@@ -1,22 +1,26 @@
{
"menu": {
"title": "Beschikbare opties",
"exit": "Programma afsluiten",
"reset": "Machine-ID resetten",
"register": "Nieuw Cursor-account registreren",
"register_google": "Registreren met Google-account",
"register_github": "Registreren met GitHub-account",
"register_manual": "Cursor registreren met aangepast e-mailadres",
"quit": "Cursor-applicatie sluiten",
"select_language": "Taal wijzigen",
"input_choice": "Voer uw keuze in: {choices}",
"title": "Beschikbare Opties",
"exit": "Programma Afsluiten",
"reset": "Machine ID Resetten",
"register": "Nieuw Cursor Account Registreren",
"register_google": "Registreren met Google Account",
"register_github": "Registreren met GitHub Account",
"register_manual": "Cursor Registreren met Aangepaste E-mail",
"quit": "Cursor Toepassing Sluiten",
"select_language": "Taal Wijzigen",
"select_chrome_profile": "Chrome Profiel Selecteren",
"input_choice": "Voer uw keuze in ({choices})",
"invalid_choice": "Ongeldige selectie. Voer een nummer in uit {choices}.",
"program_terminated": "Programma is beëindigd door de gebruiker",
"error_occurred": "Er is een fout opgetreden: {error}. Probeer het opnieuw.",
"press_enter": "Druk op Enter om door te gaan.",
"disable_auto_update": "Cursor automatische updates uitschakelen",
"lifetime_access_enabled": "Levenslange toegang ingeschakeld",
"totally_reset": "Cursor volledig resetten"
"totally_reset": "Cursor volledig resetten",
"outdate": "Verouderd",
"temp_github_register": "Tijdelijke GitHub-registratie",
"coming_soon": "Binnenkort"
},
"languages": {
"en": "Engels",
@@ -27,7 +31,8 @@
"de": "Duits",
"fr": "Frans",
"pt": "Portugees",
"ru": "Russisch"
"ru": "Russisch",
"es": "Spaans"
},
"quit_cursor": {
"start": "Start met afsluiten van Cursor",
@@ -278,7 +283,7 @@
"update_skipped": "Update overgeslagen.",
"invalid_choice": "Ongeldige keuze. Voer 'Y' of 'n' in.",
"development_version": "Ontwikkelversie {current} > {latest}",
"changelog_title": "Changelog"
"changelog_title": "Wijzigingslogboek"
},
"totally_reset": {
"title": "Cursor volledig herstellen",
@@ -370,5 +375,17 @@
"electron_localstorage_files_removed": "Electron localStorage-bestanden verwijderd",
"electron_localstorage_files_removal_error": "Fout bij het verwijderen van Electron localStorage-bestanden: {error}",
"removing_electron_localstorage_files_completed": "Electron localStorage-bestanden verwijderd"
},
"chrome_profile": {
"title": "Chrome Profiel Selectie",
"select_profile": "Selecteer een Chrome profiel om te gebruiken:",
"profile_list": "Beschikbare profielen:",
"default_profile": "Standaard Profiel",
"profile": "Profiel {number}",
"no_profiles": "Geen Chrome profielen gevonden",
"error_loading": "Fout bij laden van Chrome profielen: {error}",
"profile_selected": "Geselecteerd profiel: {profile}",
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
}
}

View File

@@ -16,7 +16,10 @@
"press_enter": "Pressione Enter para Sair",
"disable_auto_update": "Desativar Atualização Automática do Cursor",
"lifetime_access_enabled": "ACESSO VITALÍCIO HABILITADO",
"totally_reset": "Redefinir Cursor Completamente"
"totally_reset": "Redefinir Cursor Completamente",
"outdate": "Obsoleto",
"temp_github_register": "Registro temporário do GitHub",
"coming_soon": "Em breve"
},
"languages": {
"en": "Inglês",
@@ -27,7 +30,8 @@
"de": "Alemão",
"fr": "Francês",
"pt": "Português do Brasil",
"ru": "Russo"
"ru": "Russo",
"es": "Espanhol"
},
"quit_cursor": {
"start": "Iniciando fechamento do Cursor",
@@ -287,7 +291,7 @@
"update_skipped": "Atualização ignorada.",
"invalid_choice": "Escolha inválida. Por favor, digite 'Y' ou 'n'.",
"development_version": "Versão de desenvolvimento {current} > {latest}",
"changelog_title": "Registro de mudanças"
"changelog_title": "Registro de alterações"
},
"totally_reset": {
"title": "Redefinir Cursor Completamente",
@@ -379,5 +383,17 @@
"electron_localstorage_files_removed": "Arquivos localStorage do Electron removidos",
"electron_localstorage_files_removal_error": "Erro ao remover arquivos localStorage do Electron: {error}",
"removing_electron_localstorage_files_completed": "Remoção dos arquivos localStorage do Electron concluída"
},
"chrome_profile": {
"title": "Seleção de Perfil do Chrome",
"select_profile": "Selecione um perfil do Chrome para usar:",
"profile_list": "Perfis disponíveis:",
"default_profile": "Perfil Padrão",
"profile": "Perfil {number}",
"no_profiles": "Nenhum perfil do Chrome encontrado",
"error_loading": "Erro ao carregar perfis do Chrome: {error}",
"profile_selected": "Perfil selecionado: {profile}",
"invalid_selection": "Seleção inválida. Por favor, tente novamente",
"warning_chrome_close": "Aviso: Isso fechará todos os processos do Chrome em execução"
}
}

View File

@@ -16,7 +16,10 @@
"press_enter": "Нажмите Enter для выхода",
"disable_auto_update": "Отключить автоматическое обновление Cursor",
"lifetime_access_enabled": "ВКЛЮЧЕН ПОЖИЗНЕННЫЙ ДОСТУП",
"totally_reset": "Полностью сбросить Cursor"
"totally_reset": "Полностью сбросить Cursor",
"outdate": "Устаревший",
"temp_github_register": "Временная регистрация GitHub",
"coming_soon": "Скоро"
},
"languages": {
"en": "Английский",
@@ -27,7 +30,8 @@
"de": "Немецкий",
"fr": "Французский",
"pt": "Бразильский португальский",
"ru": "Русский"
"ru": "Русский",
"es": "Испанский"
},
"quit_cursor": {
"start": "Начало закрытия Cursor",
@@ -287,7 +291,7 @@
"update_skipped": "Обновление пропущено.",
"invalid_choice": "Неверный выбор. Пожалуйста, введите 'Y' или 'n'.",
"development_version": "Версия разработки {current} > {latest}",
"changelog_title": "Журнал изменений"
"changelog_title": "Список изменений"
},
"totally_reset": {
"title": "Полный сброс Cursor",
@@ -379,5 +383,17 @@
"electron_localstorage_files_removed": "Файлы localStorage Electron удалены",
"electron_localstorage_files_removal_error": "Ошибка удаления файлов localStorage Electron: {error}",
"removing_electron_localstorage_files_completed": "Удаление файлов localStorage Electron завершено"
},
"chrome_profile": {
"title": "Выбор Профиля Chrome",
"select_profile": "Выберите профиль Chrome для использования:",
"profile_list": "Доступные профили:",
"default_profile": "Профиль по умолчанию",
"profile": "Профиль {number}",
"no_profiles": "Профили Chrome не найдены",
"error_loading": "Ошибка загрузки профилей Chrome: {error}",
"profile_selected": "Выбран профиль: {profile}",
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
}
}

402
locales/tr.json Normal file
View File

@@ -0,0 +1,402 @@
{
"menu": {
"title": "Mevcut Seçenekler",
"exit": "Programdan Çık",
"reset": "Makine Kimliğini Sıfırla",
"register": "Yeni Cursor Hesabı Kaydet",
"register_google": "Google Hesabı ile Kayıt Ol",
"register_github": "GitHub Hesabı ile Kayıt Ol",
"register_manual": "Cursor'ı Özel E-posta ile Kaydet",
"quit": "Cursor Uygulamasını Kapat",
"select_language": "Dili Değiştir",
"input_choice": "Lütfen seçiminizi girin ({choices})",
"invalid_choice": "Geçersiz seçim. Lütfen {choices} arasından bir sayı girin",
"program_terminated": "Program kullanıcı tarafından sonlandırıldı",
"error_occurred": "Bir hata oluştu: {error}. Lütfen tekrar deneyin",
"press_enter": ıkmak için Enter'a Basın",
"disable_auto_update": "Cursor Otomatik Güncellemeyi Devre Dışı Bırak",
"lifetime_access_enabled": "ÖMÜR BOYU ERİŞİM ETKİNLEŞTİRİLDİ",
"totally_reset": "Cursor'ı Tamamen Sıfırla",
"outdate": "güncel değil",
"temp_github_register": "Geçici GitHub Kaydı",
"coming_soon": "Yakında"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
"vi": "Vietnamese",
"nl": "Dutch",
"de": "German",
"fr": "French",
"pt": "Portuguese",
"ru": "Russian",
"tr": "Turkish",
"es": "Spanish"
},
"quit_cursor": {
"start": "Cursor'dan Çıkış Başlatılıyor",
"no_process": "Çalışan Cursor İşlemi Yok",
"terminating": "İşlem Sonlandırılıyor {pid}",
"waiting": "İşlemin Çıkması Bekleniyor",
"success": "Tüm Cursor İşlemleri Kapatıldı",
"timeout": "İşlem Zaman Aşımı: {pids}",
"error": "Hata Oluştu: {error}"
},
"reset": {
"title": "Cursor Makine Kimliği Sıfırlama Aracı",
"checking": "Yapılandırma Dosyası Kontrol Ediliyor",
"not_found": "Yapılandırma Dosyası Bulunamadı",
"no_permission": "Yapılandırma Dosyası Okunamıyor veya Yazılamıyor, Lütfen Dosya İzinlerini Kontrol Edin",
"reading": "Mevcut Yapılandırma Okunuyor",
"creating_backup": "Yapılandırma Yedeği Oluşturuluyor",
"backup_exists": "Yedek Dosya Zaten Mevcut, Yedekleme Adımı Atlanıyor",
"generating": "Yeni Makine Kimliği Oluşturuluyor",
"saving_json": "Yeni Yapılandırma JSON'a Kaydediliyor",
"success": "Makine Kimliği Başarıyla Sıfırlandı",
"new_id": "Yeni Makine Kimliği",
"permission_error": "İzin Hatası: {error}",
"run_as_admin": "Lütfen Bu Programı Yönetici Olarak Çalıştırmayı Deneyin",
"process_error": "Sıfırlama İşlemi Hatası: {error}",
"updating_sqlite": "SQLite Veritabanı Güncelleniyor",
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
"sqlite_success": "SQLite Veritabanı Başarıyla Güncellendi",
"sqlite_error": "SQLite Veritabanı Güncellemesi Başarısız: {error}",
"press_enter": ıkmak için Enter'a Basın",
"unsupported_os": "Desteklenmeyen İşletim Sistemi: {os}",
"linux_path_not_found": "Linux Yolu Bulunamadı",
"updating_system_ids": "Sistem Kimlikleri Güncelleniyor",
"system_ids_updated": "Sistem Kimlikleri Başarıyla Güncellendi",
"system_ids_update_failed": "Sistem Kimlikleri Güncellemesi Başarısız: {error}",
"windows_guid_updated": "Windows GUID Başarıyla Güncellendi",
"windows_permission_denied": "Windows İzni Reddedildi",
"windows_guid_update_failed": "Windows GUID Güncellemesi Başarısız",
"macos_uuid_updated": "macOS UUID Başarıyla Güncellendi",
"plutil_command_failed": "plutil Komutu Başarısız",
"start_patching": "getMachineId Yamalanması Başlatılıyor",
"macos_uuid_update_failed": "macOS UUID Güncellemesi Başarısız",
"current_version": "Mevcut Cursor Sürümü: {version}",
"patch_completed": "getMachineId Yamalama Tamamlandı",
"patch_failed": "getMachineId Yamalama Başarısız: {error}",
"version_check_passed": "Cursor Sürüm Kontrolü Geçildi",
"file_modified": "Dosya Değiştirildi",
"version_less_than_0_45": "Cursor Sürümü < 0.45.0, getMachineId Yamalama Atlanıyor",
"detecting_version": "Cursor Sürümü Tespit Ediliyor",
"patching_getmachineid": "getMachineId Yamalanıyor",
"version_greater_than_0_45": "Cursor Sürümü >= 0.45.0, getMachineId Yamalanıyor",
"permission_denied": "İzin Reddedildi: {error}",
"backup_created": "Yedek Oluşturuldu",
"update_success": "Güncelleme Başarılı",
"update_failed": "Güncelleme Başarısız: {error}",
"windows_machine_guid_updated": "Windows Makine GUID'si Başarıyla Güncellendi",
"reading_package_json": "package.json Okunuyor {path}",
"invalid_json_object": "Geçersiz JSON Nesnesi",
"no_version_field": "package.json İçinde Sürüm Alanı Bulunamadı",
"version_field_empty": "Sürüm Alanı Boş",
"invalid_version_format": "Geçersiz Sürüm Formatı: {version}",
"found_version": "Sürüm Bulundu: {version}",
"version_parse_error": "Sürüm Ayrıştırma Hatası: {error}",
"package_not_found": "Package.json Bulunamadı: {path}",
"check_version_failed": "Sürüm Kontrolü Başarısız: {error}",
"stack_trace": "Yığın İzleme",
"version_too_low": "Cursor Sürümü Çok Düşük: {version} < 0.45.0"
},
"register": {
"title": "Cursor Kayıt Aracı",
"start": "Kayıt işlemi başlatılıyor...",
"handling_turnstile": "Güvenlik doğrulaması işleniyor...",
"retry_verification": "Doğrulama tekrar deneniyor...",
"detect_turnstile": "Güvenlik doğrulaması kontrol ediliyor...",
"verification_success": "Güvenlik doğrulaması başarılı",
"starting_browser": "Tarayıcıılıyor...",
"form_success": "Form başarıyla gönderildi",
"browser_started": "Tarayıcı başarıyla açıldı",
"waiting_for_second_verification": "E-posta doğrulaması bekleniyor...",
"waiting_for_verification_code": "Doğrulama kodu bekleniyor...",
"password_success": "Şifre başarıyla ayarlandı",
"password_error": "Şifre ayarlanamadı: {error}. Lütfen tekrar deneyin",
"waiting_for_page_load": "Sayfa yükleniyor...",
"first_verification_passed": "İlk doğrulama başarılı",
"mailbox": "E-posta gelen kutusuna başarıyla erişildi",
"register_start": "Kayıt Başlat",
"form_submitted": "Form Gönderildi, Doğrulama Başlatılıyor...",
"filling_form": "Form Dolduruluyor",
"visiting_url": "URL Ziyaret Ediliyor",
"basic_info": "Temel Bilgiler Gönderildi",
"handle_turnstile": "Turnstile İşleniyor",
"no_turnstile": "Turnstile Algılanmadı",
"turnstile_passed": "Turnstile Geçildi",
"verification_start": "Doğrulama Kodu Alma Başlatılıyor",
"verification_timeout": "Doğrulama Kodu Alma Zaman Aşımı",
"verification_not_found": "Doğrulama Kodu Bulunamadı",
"try_get_code": "Deneme | {attempt} Doğrulama Kodu Al | Kalan Süre: {time}s",
"get_account": "Hesap Bilgileri Alınıyor",
"get_token": "Cursor Oturum Jetonu Alınıyor",
"token_success": "Jeton Başarıyla Alındı",
"token_attempt": "Deneme | {attempt} kez Jeton alma denemesi | {time}s içinde tekrar denenecek",
"token_max_attempts": "Maksimum Deneme Sayısına Ulaşıldı ({max}) | Jeton alınamadı",
"token_failed": "Jeton Alma Başarısız: {error}",
"account_error": "Hesap Bilgisi Alma Başarısız: {error}",
"press_enter": ıkmak için Enter'a Basın",
"browser_start": "Tarayıcı Başlatılıyor",
"open_mailbox": "Posta Kutusu Sayfasıılıyor",
"email_error": "E-posta Adresi Alınamadı",
"setup_error": "E-posta Kurulum Hatası: {error}",
"start_getting_verification_code": "Doğrulama Kodu Alma Başlatılıyor, 60 saniye içinde denenecek",
"get_verification_code_timeout": "Doğrulama Kodu Alma Zaman Aşımı",
"get_verification_code_success": "Doğrulama Kodu Alma Başarılı",
"try_get_verification_code": "Deneme | {attempt} Doğrulama Kodu Al | Kalan Süre: {remaining_time}s",
"verification_code_filled": "Doğrulama Kodu Dolduruldu",
"login_success_and_jump_to_settings_page": "Giriş Başarılı ve Ayarlar Sayfasına Yönlendiriliyor",
"detect_login_page": "Giriş Sayfası Algılandı, Giriş Başlatılıyor...",
"cursor_registration_completed": "Cursor Kaydı Tamamlandı!",
"set_password": "Şifre Belirle",
"basic_info_submitted": "Temel Bilgiler Gönderildi",
"cursor_auth_info_updated": "Cursor Kimlik Bilgileri Güncellendi",
"cursor_auth_info_update_failed": "Cursor Kimlik Bilgileri Güncellemesi Başarısız",
"reset_machine_id": "Makine Kimliğini Sıfırla",
"account_info_saved": "Hesap Bilgileri Kaydedildi",
"save_account_info_failed": "Hesap Bilgilerini Kaydetme Başarısız",
"get_email_address": "E-posta Adresi Al",
"update_cursor_auth_info": "Cursor Kimlik Bilgilerini Güncelle",
"register_process_error": "Kayıt İşlemi Hatası: {error}",
"setting_password": "Şifre Ayarlanıyor",
"manual_code_input": "Manuel Kod Girişi",
"manual_email_input": "Manuel E-posta Girişi",
"password": "Şifre",
"first_name": "Ad",
"last_name": "Soyad",
"exit_signal": ıkış Sinyali",
"email_address": "E-posta Adresi",
"config_created": "Yapılandırma Oluşturuldu",
"verification_failed": "Doğrulama Başarısız",
"verification_error": "Doğrulama Hatası: {error}",
"config_option_added": "Yapılandırma Seçeneği Eklendi: {option}",
"config_updated": "Yapılandırma Güncellendi",
"password_submitted": "Şifre Gönderildi",
"total_usage": "Toplam Kullanım: {usage}",
"setting_on_password": "Şifre Ayarlanıyor",
"getting_code": "Doğrulama Kodu Alınıyor, 60 saniye içinde denenecek",
"human_verify_error": "Kullanıcının insan olduğu doğrulanamıyor. Tekrar deneniyor...",
"max_retries_reached": "Maksimum deneme sayısına ulaşıldı. Kayıt başarısız."
},
"auth": {
"title": "Cursor Kimlik Yöneticisi",
"checking_auth": "Kimlik Dosyası Kontrol Ediliyor",
"auth_not_found": "Kimlik Dosyası Bulunamadı",
"auth_file_error": "Kimlik Dosyası Hatası: {error}",
"reading_auth": "Kimlik Dosyası Okunuyor",
"updating_auth": "Kimlik Bilgileri Güncelleniyor",
"auth_updated": "Kimlik Bilgileri Başarıyla Güncellendi",
"auth_update_failed": "Kimlik Bilgileri Güncellemesi Başarısız: {error}",
"auth_file_created": "Kimlik Dosyası Oluşturuldu",
"auth_file_create_failed": "Kimlik Dosyası Oluşturma Başarısız: {error}",
"press_enter": ıkmak için Enter'a Basın",
"reset_machine_id": "Makine Kimliğini Sıfırla",
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
"connected_to_database": "Veritabanına Bağlanıldı",
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
"db_not_found": "Veritabanı dosyası bulunamadı: {path}",
"db_permission_error": "Veritabanı dosyasına erişilemiyor. Lütfen izinleri kontrol edin",
"db_connection_error": "Veritabanına bağlantı başarısız: {error}"
},
"control": {
"generate_email": "Yeni E-posta Oluşturuluyor",
"blocked_domain": "Engellenmiş Alan Adı",
"select_domain": "Rastgele Alan Adı Seçiliyor",
"copy_email": "E-posta Adresi Kopyalanıyor",
"enter_mailbox": "Posta Kutusuna Giriliyor",
"refresh_mailbox": "Posta Kutusu Yenileniyor",
"check_verification": "Doğrulama Kodu Kontrol Ediliyor",
"verification_found": "Doğrulama Kodu Bulundu",
"verification_not_found": "Doğrulama Kodu Bulunamadı",
"browser_error": "Tarayıcı Kontrol Hatası: {error}",
"navigation_error": "Gezinme Hatası: {error}",
"email_copy_error": "E-posta Kopyalama Hatası: {error}",
"mailbox_error": "Posta Kutusu Hatası: {error}",
"token_saved_to_file": "Jeton cursor_tokens.txt dosyasına kaydedildi",
"navigate_to": "{url} adresine gidiliyor",
"generate_email_success": "E-posta Oluşturma Başarılı",
"select_email_domain": "E-posta Alan Adı Seç",
"select_email_domain_success": "E-posta Alan Adı Seçimi Başarılı",
"get_email_name": "E-posta Adı Al",
"get_email_name_success": "E-posta Adı Alma Başarılı",
"get_email_address": "E-posta Adresi Al",
"get_email_address_success": "E-posta Adresi Alma Başarılı",
"enter_mailbox_success": "Posta Kutusuna Giriş Başarılı",
"found_verification_code": "Doğrulama Kodu Bulundu",
"get_cursor_session_token": "Cursor Oturum Jetonu Al",
"get_cursor_session_token_success": "Cursor Oturum Jetonu Alma Başarılı",
"get_cursor_session_token_failed": "Cursor Oturum Jetonu Alma Başarısız",
"save_token_failed": "Jeton Kaydetme Başarısız",
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
"no_valid_verification_code": "Geçerli Doğrulama Kodu Yok"
},
"email": {
"starting_browser": "Tarayıcı Başlatılıyor",
"visiting_site": "E-posta alan adları ziyaret ediliyor",
"create_success": "E-posta Başarıyla Oluşturuldu",
"create_failed": "E-posta Oluşturma Başarısız",
"create_error": "E-posta Oluşturma Hatası: {error}",
"refreshing": "E-posta Yenileniyor",
"refresh_success": "E-posta Başarıyla Yenilendi",
"refresh_error": "E-posta Yenileme Hatası: {error}",
"refresh_button_not_found": "Yenileme Düğmesi Bulunamadı",
"verification_found": "Doğrulama Bulundu",
"verification_not_found": "Doğrulama Bulunamadı",
"verification_error": "Doğrulama Hatası: {error}",
"verification_code_found": "Doğrulama Kodu Bulundu",
"verification_code_not_found": "Doğrulama Kodu Bulunamadı",
"verification_code_error": "Doğrulama Kodu Hatası: {error}",
"address": "E-posta Adresi",
"all_domains_blocked": "Tüm Alan Adları Engellendi, Servis Değiştiriliyor",
"no_available_domains_after_filtering": "Filtrelemeden Sonra Kullanılabilir Alan Adı Yok",
"switching_service": "{service} Servisine Geçiliyor",
"domains_list_error": "Alan Adları Listesi Alınamadı: {error}",
"failed_to_get_available_domains": "Kullanılabilir Alan Adları Alınamadı",
"domains_excluded": "Hariç Tutulan Alan Adları: {domains}",
"failed_to_create_account": "Hesap Oluşturma Başarısız",
"account_creation_error": "Hesap Oluşturma Hatası: {error}",
"blocked_domains": "Engellenen Alan Adları: {domains}",
"blocked_domains_loaded": "Engellenen Alan Adları Yüklendi: {count}",
"blocked_domains_loaded_error": "Engellenen Alan Adları Yükleme Hatası: {error}",
"blocked_domains_loaded_success": "Engellenen Alan Adları Başarıyla Yüklendi",
"blocked_domains_loaded_timeout": "Engellenen Alan Adları Yükleme Zaman Aşımı: {timeout}s",
"blocked_domains_loaded_timeout_error": "Engellenen Alan Adları Yükleme Zaman Aşımı Hatası: {error}",
"available_domains_loaded": "Kullanılabilir Alan Adları Yüklendi: {count}",
"domains_filtered": "Filtrelenen Alan Adları: {count}",
"trying_to_create_email": "E-posta oluşturulmaya çalışılıyor: {email}",
"domain_blocked": "Alan Adı Engellendi: {domain}"
},
"update": {
"title": "Cursor Otomatik Güncellemeyi Devre Dışı Bırak",
"disable_success": "Otomatik Güncelleme Başarıyla Devre Dışı Bırakıldı",
"disable_failed": "Otomatik Güncellemeyi Devre Dışı Bırakma Başarısız: {error}",
"press_enter": ıkmak için Enter'a Basın",
"start_disable": "Otomatik Güncellemeyi Devre Dışı Bırakma Başlatılıyor",
"killing_processes": "İşlemler Sonlandırılıyor",
"processes_killed": "İşlemler Sonlandırıldı",
"removing_directory": "Dizin Kaldırılıyor",
"directory_removed": "Dizin Kaldırıldı",
"creating_block_file": "Engelleme Dosyası Oluşturuluyor",
"block_file_created": "Engelleme Dosyası Oluşturuldu"
},
"updater": {
"checking": "Güncellemeler kontrol ediliyor...",
"new_version_available": "Yeni sürüm mevcut! (Mevcut: {current}, En son: {latest})",
"updating": "En son sürüme güncelleniyor. Program otomatik olarak yeniden başlatılacak.",
"up_to_date": "En son sürümü kullanıyorsunuz.",
"check_failed": "Güncellemeler kontrol edilemedi: {error}",
"continue_anyway": "Mevcut sürümle devam ediliyor...",
"update_confirm": "En son sürüme güncellemek istiyor musunuz? (Y/n)",
"update_skipped": "Güncelleme atlanıyor.",
"invalid_choice": "Geçersiz seçim. Lütfen 'Y' veya 'n' girin.",
"development_version": "Geliştirme Sürümü {current} > {latest}",
"changelog_title": "Değişiklik günlüğü"
},
"totally_reset": {
"title": "Cursor'ı Tamamen Sıfırla",
"checking_config": "Yapılandırma Dosyası Kontrol Ediliyor",
"config_not_found": "Yapılandırma Dosyası Bulunamadı",
"no_permission": "Yapılandırma Dosyası Okunamıyor veya Yazılamıyor, Lütfen Dosya İzinlerini Kontrol Edin",
"reading_config": "Mevcut Yapılandırma Okunuyor",
"creating_backup": "Yapılandırma Yedeği Oluşturuluyor",
"backup_exists": "Yedek Dosya Zaten Mevcut, Yedekleme Adımı Atlanıyor",
"generating_new_machine_id": "Yeni Makine Kimliği Oluşturuluyor",
"saving_new_config": "Yeni Yapılandırma JSON'a Kaydediliyor",
"success": "Cursor Başarıyla Sıfırlandı",
"error": "Cursor Sıfırlama Başarısız: {error}",
"press_enter": ıkmak için Enter'a Basın",
"reset_machine_id": "Makine Kimliğini Sıfırla",
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
"connected_to_database": "Veritabanına Bağlanıldı",
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
"db_not_found": "Veritabanı dosyası bulunamadı: {path}",
"db_permission_error": "Veritabanı dosyasına erişilemiyor. Lütfen izinleri kontrol edin",
"db_connection_error": "Veritabanına bağlantı başarısız: {error}",
"feature_title": "ÖZELLİKLER",
"feature_1": "Cursor AI ayarları ve yapılandırmalarının tamamen kaldırılması",
"feature_2": "AI geçmişi ve komutları dahil tüm önbelleğe alınmış verileri temizler",
"feature_3": "Deneme süresini aşma tespitini atlatmak için makine kimliğini sıfırlar",
"feature_4": "Rastgele yeni makine tanımlayıcıları oluşturur",
"feature_5": "Özel uzantıları ve tercihleri kaldırır",
"feature_6": "Deneme süresi bilgilerini ve aktivasyon verilerini sıfırlar",
"feature_7": "Gizli lisans ve deneme süresiyle ilgili dosyalar için derin tarama",
"feature_8": "Cursor olmayan dosyaları ve uygulamaları güvenle korur",
"feature_9": "Windows, macOS ve Linux ile uyumludur",
"disclaimer_title": "YASAL UYARI",
"disclaimer_1": "Bu araç, tüm Cursor AI ayarlarını,",
"disclaimer_2": "yapılandırmalarını ve önbelleğe alınmış verileri kalıcı olarak silecektir. Bu işlem geri alınamaz.",
"disclaimer_3": "Kod dosyalarınız ETKİLENMEYECEK ve bu araç",
"disclaimer_4": "yalnızca Cursor AI editör dosyalarını ve deneme süresi algılama mekanizmalarını hedef almak için tasarlanmıştır.",
"disclaimer_5": "Sisteminizde bulunan diğer uygulamalar etkilenmeyecektir.",
"disclaimer_6": "Bu aracı çalıştırdıktan sonra Cursor AI'yi yeniden kurmanız gerekecektir.",
"disclaimer_7": "Kullanım sorumluluğu size aittir",
"confirm_title": "Devam etmek istediğinizden emin misiniz?",
"confirm_1": "Bu işlem, tüm Cursor AI ayarlarını,",
"confirm_2": "yapılandırmalarını ve önbelleğe alınmış verileri silecektir. Bu işlem geri alınamaz.",
"confirm_3": "Kod dosyalarınız ETKİLENMEYECEK ve bu araç",
"confirm_4": "yalnızca Cursor AI editör dosyalarını ve deneme süresi algılama mekanizmalarını hedef almak için tasarlanmıştır.",
"confirm_5": "Sisteminizde bulunan diğer uygulamalar etkilenmeyecektir.",
"confirm_6": "Bu aracı çalıştırdıktan sonra Cursor AI'yi yeniden kurmanız gerekecektir.",
"confirm_7": "Kullanım sorumluluğu size aittir",
"invalid_choice": "Lütfen 'Y' veya 'n' girin",
"skipped_for_safety": "Güvenlik için atlandı (Cursor ile ilgili değil): {path}",
"deleted": "Silindi: {path}",
"error_deleting": "{path} silinirken hata: {error}",
"not_found": "Dosya bulunamadı: {path}",
"resetting_machine_id": "Deneme süresi algılamasını atlatmak için makine tanımlayıcıları sıfırlanıyor...",
"created_machine_id": "Yeni makine kimliği oluşturuldu: {path}",
"error_creating_machine_id": "Makine kimliği dosyası oluşturulurken hata {path}: {error}",
"error_searching": "{path} içindeki dosyalar aranırken hata: {error}",
"created_extended_trial_info": "Yeni genişletilmiş deneme bilgisi oluşturuldu: {path}",
"error_creating_trial_info": "Deneme bilgisi dosyası oluşturulurken hata {path}: {error}",
"resetting_cursor_ai_editor": "Cursor AI Editor sıfırlanıyor... Lütfen bekleyin.",
"reset_cancelled": "Sıfırlama iptal edildi. Herhangi bir değişiklik yapmadan çıkılıyor.",
"windows_machine_id_modification_skipped": "Windows makine kimliği değişikliği atlandı: {error}",
"linux_machine_id_modification_skipped": "Linux machine-id değişikliği atlandı: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Not: Tam makine kimliği sıfırlaması yönetici olarak çalıştırmayı gerektirebilir",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Not: Tam sistem machine-id sıfırlaması sudo ayrıcalıkları gerektirebilir",
"windows_registry_instructions": "📝 NOT: Windows'ta tam sıfırlama için kayıt defteri girdilerini de temizlemeniz gerekebilir.",
"windows_registry_instructions_2": " 'regedit' çalıştırın ve HKEY_CURRENT_USER\\Software\\ altında 'Cursor' veya 'CursorAI' içeren anahtarları arayıp silin.\n",
"reset_log_1": "Cursor AI tamamen sıfırlandı ve deneme süresi algılaması atlatıldı!",
"reset_log_2": "Değişikliklerin etkili olması için lütfen sisteminizi yeniden başlatın.",
"reset_log_3": "Cursor AI'yi yeniden kurmanız gerekecek ve şimdi yeni bir deneme süreniz olmalı.",
"reset_log_4": "En iyi sonuçlar için şunları da düşünün:",
"reset_log_5": "Yeni bir deneme süresi için kaydolurken farklı bir e-posta adresi kullanın",
"reset_log_6": "Mümkünse, IP adresinizi değiştirmek için VPN kullanın",
"reset_log_7": "Cursor AI'nin web sitesini ziyaret etmeden önce tarayıcı çerezlerinizi ve önbelleği temizleyin",
"reset_log_8": "Sorunlar devam ederse, Cursor AI'yi farklı bir konuma kurmayı deneyin",
"reset_log_9": "Herhangi bir sorunla karşılaşırsanız, Github Sorun Takibine gidin ve https://github.com/yeongpin/cursor-free-vip/issues adresinde bir sorun oluşturun",
"unexpected_error": "Beklenmeyen bir hata oluştu: {error}",
"report_issue": "Lütfen bu sorunu https://github.com/yeongpin/cursor-free-vip/issues adresindeki Github Sorun Takibine bildirin",
"keyboard_interrupt": "İşlem kullanıcı tarafından kesildi. Çıkılıyor...",
"return_to_main_menu": "Ana menüye dönülüyor...",
"process_interrupted": "İşlem kesildi. Çıkılıyor...",
"press_enter_to_return_to_main_menu": "Ana menüye dönmek için Enter'a basın...",
"removing_known": "Bilinen deneme/lisans dosyaları kaldırılıyor",
"performing_deep_scan": "Ek deneme/lisans dosyaları için derin tarama yapılıyor",
"found_additional_potential_license_trial_files": "{count} ek potansiyel lisans/deneme dosyası bulundu",
"checking_for_electron_localstorage_files": "Electron localStorage dosyaları kontrol ediliyor",
"no_additional_license_trial_files_found_in_deep_scan": "Derin taramada ek lisans/deneme dosyası bulunamadı",
"removing_electron_localstorage_files": "Electron localStorage dosyaları kaldırılıyor",
"electron_localstorage_files_removed": "Electron localStorage dosyaları kaldırıldı",
"electron_localstorage_files_removal_error": "Electron localStorage dosyaları kaldırılırken hata: {error}",
"removing_electron_localstorage_files_completed": "Electron localStorage dosyaları kaldırma işlemi tamamlandı"
},
"chrome_profile": {
"title": "Chrome Profil Seçimi",
"select_profile": "Kullanılacak Chrome profilini seçin:",
"profile_list": "Mevcut profiller:",
"default_profile": "Varsayılan Profil",
"profile": "Profil {number}",
"no_profiles": "Chrome profili bulunamadı",
"error_loading": "Chrome profilleri yüklenirken hata: {error}",
"profile_selected": "Seçilen profil: {profile}",
"invalid_selection": "Geçersiz seçim. Lütfen tekrar deneyin",
"warning_chrome_close": "Uyarı: Bu işlem tüm çalışan Chrome işlemlerini kapatacaktır"
}
}

View File

@@ -1,12 +1,15 @@
{
"menu": {
"title": "Các Tùy Chọn Khả Dụng",
"title": "Các Tùy Chọn",
"exit": "Thoát Chương Trình",
"reset": "Đặt Lại ID Máy",
"register": "Đăng Ký Tài Khoản Cursor Mới",
"register_google": "Đăng Ký Bằng Tài Khoản Google",
"register_github": "Đăng Ký Bằng Tài Khoản GitHub",
"register_manual": "Đăng Ký Cursor Với Email Tùy Chỉnh",
"quit": "Đóng Ứng Dụng Cursor",
"select_language": "Thay Đổi Ngôn Ngữ",
"select_chrome_profile": "Chọn Hồ Sơ Chrome",
"input_choice": "Vui lòng nhập lựa chọn của bạn ({choices})",
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ {choices}",
"program_terminated": "Chương trình đã bị người dùng chấm dứt",
@@ -14,7 +17,10 @@
"press_enter": "Nhấn Enter để Thoát",
"disable_auto_update": "Tắt tự động cập nhật Cursor",
"lifetime_access_enabled": "ĐÃ BẬT TRUY CẬP TRỌN ĐỜI",
"totally_reset": "Đặt lại hoàn toàn Cursor"
"totally_reset": "Đặt lại hoàn toàn Cursor",
"outdate": "Quá cũ",
"temp_github_register": "Đăng ký GitHub tạm thời",
"coming_soon": "Sắp ra mắt"
},
"languages": {
"en": "Tiếng Anh",
@@ -25,7 +31,8 @@
"de": "Tiếng Đức",
"fr": "Tiếng Pháp",
"pt": "Tiếng Bồ Đào Nha",
"ru": "Tiếng Nga"
"ru": "Tiếng Nga",
"es": "Tiếng Tây Ban Nha"
},
"quit_cursor": {
"start": "Bắt Đầu Thoát Cursor",
@@ -93,7 +100,6 @@
"check_version_failed": "Kiểm Tra Phiên Bản Thất Bại: {error}",
"stack_trace": "Dấu Vết Ngăn Xếp",
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0"
},
"register": {
"title": "Công Cụ Đăng Ký Cursor",
@@ -286,7 +292,7 @@
"update_skipped": "Bỏ Qua Cập Nhật.",
"invalid_choice": "Lựa Chọn Không Hợp Lệ. Vui Lòng Nhập 'Y' Hoặc 'n'.",
"development_version": "Phiên Bản Phát Triển {current} > {latest}",
"changelog_title": "Changelog"
"changelog_title": "Nhật ký thay đổi"
},
"totally_reset": {
"title": "Đặt lại hoàn toàn Cursor",
@@ -378,5 +384,17 @@
"electron_localstorage_files_removed": "Đã xóa các tệp Electron localStorage",
"electron_localstorage_files_removal_error": "Lỗi khi xóa các tệp Electron localStorage: {error}",
"removing_electron_localstorage_files_completed": "Đã hoàn tất việc xóa các tệp Electron localStorage"
},
"chrome_profile": {
"title": "Chọn Hồ Sơ Chrome",
"select_profile": "Chọn hồ sơ Chrome để sử dụng:",
"profile_list": "Các hồ sơ có sẵn:",
"default_profile": "Hồ Sơ Mặc Định",
"profile": "Hồ Sơ {number}",
"no_profiles": "Không tìm thấy hồ sơ Chrome",
"error_loading": "Lỗi khi tải hồ sơ Chrome: {error}",
"profile_selected": "Đã chọn hồ sơ: {profile}",
"invalid_selection": "Lựa chọn không hợp lệ. Vui lòng thử lại",
"warning_chrome_close": "Cảnh báo: Điều này sẽ đóng tất cả các tiến trình Chrome đang chạy"
}
}

View File

@@ -2,13 +2,14 @@
"menu": {
"title": "可用选项",
"exit": "退出程序",
"reset": "重置机器标识",
"register": "注册新 Cursor 账号",
"register_google": "使用 Google 账号注册",
"register_github": "使用 GitHub 账号注册",
"register_manual": "使用自定义邮箱注册",
"quit": "关闭 Cursor 应用",
"reset": "重置机器ID",
"register": "注册新Cursor账户",
"register_google": "使用Google账户注册",
"register_github": "使用GitHub账户注册",
"register_manual": "使用自定义邮箱注册Cursor",
"quit": "关闭Cursor应用",
"select_language": "更改语言",
"select_chrome_profile": "选择Chrome配置文件",
"input_choice": "请输入您的选择 ({choices})",
"invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
"program_terminated": "程序已被用户终止",
@@ -16,7 +17,19 @@
"press_enter": "按回车键退出",
"disable_auto_update": "禁用 Cursor 自动更新",
"lifetime_access_enabled": "永久订阅",
"totally_reset": "完全重置 Cursor"
"totally_reset": "完全重置 Cursor",
"outdate": "过时",
"temp_github_register": "临时GitHub注册",
"admin_required": "运行可执行文件,需要管理员权限",
"admin_required_continue": "继续使用当前版本...",
"coming_soon": "即将推出",
"fixed_soon": "即将修复",
"contribute": "贡献项目",
"config": "显示配置",
"delete_google_account": "删除 Cursor Google 账号",
"continue_prompt": "继续?(y/N): ",
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……"
},
"languages": {
"en": "英语",
@@ -27,7 +40,10 @@
"de": "德语",
"fr": "法语",
"pt": "葡萄牙语",
"ru": "俄语"
"ru": "俄语",
"tr": "土耳其语",
"bg": "保加利亚语",
"es": "西班牙语"
},
"quit_cursor": {
"start": "开始退出 Cursor",
@@ -94,7 +110,13 @@
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "检查版本失败: {error}",
"stack_trace": "堆栈跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
"no_write_permission": "没有写入权限: {path}",
"path_not_found": "路径未找到: {path}",
"modify_file_failed": "修改文件失败: {error}",
"windows_machine_id_updated": "Windows机器ID更新成功",
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}"
},
"register": {
"title": "Cursor 注册工具",
@@ -258,7 +280,13 @@
"available_domains_loaded": "获取到 {count} 个可用域名",
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
"trying_to_create_email": "尝试创建邮箱: {email}",
"domain_blocked": "域名被屏蔽: {domain}"
"domain_blocked": "域名被屏蔽: {domain}",
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
"no_display_found": "未找到显示器。确保 X 服务器正在运行。",
"try_export_display": "尝试: export DISPLAY=:0",
"extension_load_error": "加载插件失败: {error}",
"make_sure_chrome_chromium_is_properly_installed": "确保 Chrome/Chromium 已正确安装",
"try_install_chromium": "尝试: sudo apt install chromium-browser"
},
"update": {
"title": "禁用 Cursor 自动更新",
@@ -271,7 +299,23 @@
"removing_directory": "删除目录",
"directory_removed": "目录已删除",
"creating_block_file": "创建阻止文件",
"block_file_created": "阻止文件已创建"
"block_file_created": "阻止文件已创建",
"clearing_update_yml": "清空 update.yml 文件",
"update_yml_cleared": "update.yml 文件已清空",
"update_yml_not_found": "update.yml 文件未找到",
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
"unsupported_os": "不支持的操作系统: {system}",
"remove_directory_failed": "删除目录失败: {error}",
"create_block_file_failed": "创建阻止文件失败: {error}",
"directory_locked": "目录被锁定: {path}",
"yml_locked": "update.yml 文件被锁定",
"block_file_locked": "阻止文件被锁定",
"yml_already_locked": "update.yml 文件已锁定",
"block_file_already_locked": "阻止文件已锁定",
"block_file_locked_error": "阻止文件锁定错误: {error}",
"yml_locked_error": "update.yml 文件锁定错误: {error}",
"block_file_already_locked_error": "阻止文件已锁定错误: {error}",
"yml_already_locked_error": "update.yml 文件已锁定错误: {error}"
},
"updater": {
"checking": "检查更新...",
@@ -284,7 +328,8 @@
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
"development_version": "开发版本 {current} > {latest}",
"changelog_title": "更新日志"
"changelog_title": "更新日志",
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
},
"totally_reset": {
"title": "完全重置 Cursor",
@@ -375,6 +420,232 @@
"removing_electron_localstorage_files": "正在移除 Electron localStorage 文件",
"electron_localstorage_files_removed": "Electron localStorage 文件已移除",
"electron_localstorage_files_removal_error": "移除 Electron localStorage 文件时出错:{error}",
"removing_electron_localstorage_files_completed": "Electron localStorage 文件移除完成"
"removing_electron_localstorage_files_completed": "Electron localStorage 文件移除完成",
"warning_title": "警告",
"warning_1": "此操作将删除所有 Cursor AI 设置、",
"warning_2": "配置和缓存数据。此操作无法撤销。",
"warning_3": "您的代码文件 **不会** 受到影响,工具设计为",
"warning_4": "仅针对 Cursor AI 编辑器文件和试用检测机制。",
"warning_5": "系统中的其他应用程序不会受到影响。",
"warning_6": "运行此工具后,您需要重新设置 Cursor AI。",
"warning_7": "请自行承担风险",
"removed": "已删除:{path}",
"failed_to_reset_machine_guid": "无法重置机器 GUID",
"failed_to_remove": "无法删除:{path}",
"failed_to_delete_file": "无法删除文件:{path}",
"failed_to_delete_directory": "无法删除目录:{path}",
"failed_to_delete_file_or_directory": "无法删除文件或目录:{path}",
"deep_scanning": "正在进行深度扫描以查找其他试用/授权文件",
"resetting_cursor": "正在重置 Cursor AI 编辑器... 请稍候。",
"completed_in": "完成时间:{time} 秒",
"cursor_reset_completed": "Cursor AI 编辑器已完全重置且绕过试用检测!",
"cursor_reset_failed": "Cursor AI 编辑器重置失败:{error}",
"cursor_reset_cancelled": "Cursor AI 编辑器重置已取消,未进行任何更改。",
"operation_cancelled": "操作已取消,未进行任何更改。"
},
"github_register": {
"title": "GitHub + Cursor AI 注册自动化",
"features_header": "功能",
"feature1": "使用 1secmail 生成临时邮箱",
"feature2": "使用随机凭证注册新的 GitHub 账户",
"feature3": "自动验证 GitHub 邮箱",
"feature4": "使用 GitHub 认证登录 Cursor AI",
"feature5": "重置机器 ID 以绕过试用检测",
"feature6": "保存所有凭证到文件",
"warnings_header": "警告",
"warning1": "此脚本自动化账户创建,可能违反 GitHub/Cursor 服务条款",
"warning2": "需要互联网访问和管理员权限",
"warning3": "CAPTCHA 或额外验证可能会中断自动化",
"warning4": "请负责任地使用,风险自负",
"confirm": "您确定要继续吗?",
"invalid_choice": "无效选择。请输入 'yes' 或 'no'",
"cancelled": "操作已取消",
"program_terminated": "程序已由用户终止",
"starting_automation": "开始自动化...",
"github_username": "GitHub 用户名",
"github_password": "GitHub 密码",
"email_address": "邮箱地址",
"credentials_saved": "这些凭证已保存到 github_cursor_accounts.txt",
"completed_successfully": "GitHub + Cursor 注册成功",
"registration_encountered_issues": "GitHub + Cursor 注册遇到问题",
"check_browser_windows_for_manual_intervention_or_try_again_later": "检查浏览器窗口进行手动干预或稍后再试"
},
"account_info": {
"subscription": "订阅",
"trial_remaining": "剩余试用",
"days": "天",
"subscription_not_found": "订阅信息未找到",
"email_not_found": "邮箱未找到",
"failed_to_get_account": "获取账户信息失败",
"config_not_found": "配置未找到。",
"failed_to_get_usage": "获取使用信息失败",
"failed_to_get_subscription": "获取订阅信息失败",
"failed_to_get_email": "获取邮箱地址失败",
"failed_to_get_token": "获取 token 失败",
"failed_to_get_account_info": "获取账户信息失败",
"title": "账户信息",
"email": "邮箱",
"token": "Token",
"usage": "使用量",
"subscription_type": "订阅类型",
"remaining_trial": "剩余试用",
"days_remaining": "剩余天数",
"premium": "高级",
"pro": "专业",
"pro_trial": "专业试用",
"team": "团队",
"enterprise": "企业",
"free": "免费",
"active": "活跃",
"inactive": "非活跃",
"premium_usage": "高级使用量",
"basic_usage": "基础使用量",
"usage_not_found": "使用量未找到",
"lifetime_access_enabled": "永久访问已启用"
},
"config": {
"config_not_available": "配置未找到。",
"configuration": "配置",
"enabled": "已启用",
"disabled": "已禁用",
"config_directory": "配置目录",
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目录",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "请确保 Cursor 已安装并至少运行一次",
"storage_directory_not_found": "未找到存储目录",
"storage_file_found": "找到存储文件",
"file_size": "文件大小",
"file_permissions": "文件权限",
"file_owner": "文件所有者",
"file_group": "文件组",
"error_getting_file_stats": "获取文件统计信息时出错",
"permission_denied": "权限拒绝",
"try_running": "尝试运行: {command}",
"and": "和",
"storage_file_is_empty": "存储文件为空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
"storage_file_not_found": "未找到存储文件",
"error_checking_linux_paths": "检查 Linux 路径时出错",
"config_option_added": "添加配置选项",
"config_updated": "配置更新",
"config_created": "配置已创建",
"config_setup_error": "配置设置错误",
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
"error_reading_storage_file": "读取存储文件时出错",
"also_checked": "也检查了 {path}"
},
"oauth": {
"authentication_button_not_found": "未找到认证按钮",
"authentication_failed": "认证失败: {error}",
"found_cookies": "找到 {count} 个 Cookie",
"token_extraction_error": "Token 提取错误: {error}",
"authentication_successful": "认证成功 - 邮箱: {email}",
"missing_authentication_data": "缺少认证数据: {data}",
"failed_to_delete_account": "删除账户失败: {error}",
"invalid_authentication_type": "无效的认证类型",
"auth_update_success": "认证更新成功",
"browser_closed": "浏览器已关闭",
"auth_update_failed": "认证更新失败",
"google_start": "Google 开始",
"github_start": "Github 开始",
"usage_count": "使用次数: {usage}",
"account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
"starting_new_authentication_process": "开始新的认证过程...",
"failed_to_delete_expired_account": "删除过期账户失败",
"could_not_check_usage_count": "无法检查使用次数: {error}",
"found_email": "找到邮箱: {email}",
"could_not_find_email": "未找到邮箱: {error}",
"could_not_find_usage_count": "未找到使用次数: {error}",
"already_on_settings_page": "已处于设置页面",
"failed_to_extract_auth_info": "提取认证信息失败: {error}",
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
"found_default_chrome_profile": "找到默认 Chrome 配置文件",
"using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
"initializing_browser_setup": "初始化浏览器设置...",
"detected_platform": "检测平台: {platform}",
"running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
"consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
"no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
"supported_browsers": "支持的浏览器: {platform}",
"using_browser_profile": "使用浏览器配置文件: {profile}",
"starting_browser": "正在启动浏览器: {path}",
"browser_setup_completed": "浏览器设置完成",
"browser_setup_failed": "浏览器设置失败: {error}",
"try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
"starting_google_authentication": "开始 Google 认证...",
"starting_github_authentication": "开始 Github 认证...",
"waiting_for_authentication": "等待认证...",
"page_changed_checking_auth": "页面改变, 检查认证...",
"status_check_error": "状态检查错误: {error}",
"authentication_timeout": "认证超时",
"account_is_still_valid": "账户仍然有效 (使用量: {usage})",
"starting_re_authentication_process": "开始重新认证过程...",
"starting_new_google_authentication": "开始新的 Google 认证...",
"failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
"navigating_to_authentication_page": "正在导航到认证页面...",
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
"found_browser_data_directory": "找到浏览器数据目录: {path}",
"authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
"browser_failed_to_start": "浏览器启动失败: {error}",
"browser_failed": "浏览器启动失败: {error}",
"browser_failed_to_start_fallback": "浏览器启动失败: {error}"
},
"chrome_profile": {
"title": "Chrome配置文件选择",
"select_profile": "选择要使用的Chrome配置文件",
"profile_list": "可用配置文件:",
"default_profile": "默认配置文件",
"profile": "配置文件 {number}",
"no_profiles": "未找到Chrome配置文件",
"error_loading": "加载Chrome配置文件时出错{error}",
"profile_selected": "已选择配置文件:{profile}",
"invalid_selection": "选择无效。请重试",
"warning_chrome_close": "警告这将关闭所有正在运行的Chrome进程"
},
"account_delete": {
"title": "Cursor Google 账号删除工具",
"warning": "警告:这将永久删除您的 Cursor 账号。此操作无法撤销。",
"cancelled": "账号删除已取消。",
"starting_process": "开始账号删除过程...",
"google_button_not_found": "未找到 Google 登录按钮",
"logging_in": "正在使用 Google 登录...",
"waiting_for_auth": "等待 Google 验证...",
"login_successful": "登录成功",
"unexpected_page": "登录后页面异常:{url}",
"trying_settings": "尝试导航到设置页面...",
"select_google_account": "请选择您的 Google 账号...",
"auth_timeout": "认证超时,继续执行...",
"navigating_to_settings": "正在导航到设置页面...",
"already_on_settings": "已在设置页面",
"login_redirect_failed": "登录重定向失败,尝试直接导航...",
"advanced_tab_not_found": "多次尝试后未找到高级选项卡",
"advanced_tab_retry": "未找到高级选项卡,尝试 {attempt}/{max_attempts}",
"advanced_tab_error": "查找高级选项卡时出错:{error}",
"advanced_tab_clicked": "已点击高级选项卡",
"direct_advanced_navigation": "尝试直接导航到高级选项卡",
"delete_button_not_found": "多次尝试后未找到删除账号按钮",
"delete_button_retry": "未找到删除按钮,尝试 {attempt}/{max_attempts}",
"delete_button_error": "查找删除按钮时出错:{error}",
"delete_button_clicked": "已点击删除账号按钮",
"delete_input_not_found": "多次尝试后未找到删除确认输入框",
"delete_input_retry": "未找到删除输入框,尝试 {attempt}/{max_attempts}",
"delete_input_error": "查找删除输入框时出错:{error}",
"delete_input_not_found_continuing": "未找到删除确认输入框,尝试继续执行...",
"typed_delete": "已在确认框中输入\"Delete\"",
"confirm_button_not_found": "多次尝试后未找到确认按钮",
"confirm_button_retry": "未找到确认按钮,尝试 {attempt}/{max_attempts}",
"confirm_button_error": "查找确认按钮时出错:{error}",
"account_deleted": "账号删除成功!",
"error": "账号删除过程中出错:{error}",
"success": "您的 Cursor 账号已成功删除!",
"failed": "账号删除过程失败或已取消。",
"interrupted": "账号删除过程被用户中断。",
"unexpected_error": "意外错误:{error}",
"found_email": "找到邮箱:{email}",
"email_not_found": "未找到邮箱: {error}",
"found_danger_zone": "已找到危险区域部分",
"confirm_prompt": "您确定要继续吗?(y/N): "
}
}

View File

@@ -2,11 +2,14 @@
"menu": {
"title": "可用選項",
"exit": "退出程式",
"reset": "重置機器識別碼",
"register": "註冊新 Cursor 帳號",
"register_manual": "使用自訂郵箱註冊",
"quit": "關閉 Cursor 應用程式",
"select_language": "變更語言",
"reset": "重置機器ID",
"register": "註冊新Cursor帳戶",
"register_google": "使用Google帳戶註冊",
"register_github": "使用GitHub帳戶註冊",
"register_manual": "使用自定義郵箱註冊Cursor",
"quit": "關閉Cursor應用",
"select_language": "更改語言",
"select_chrome_profile": "選擇Chrome配置檔案",
"input_choice": "請輸入您的選擇 ({choices})",
"invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
"program_terminated": "程式已被使用者終止",
@@ -14,7 +17,19 @@
"press_enter": "按返回鍵退出",
"disable_auto_update": "禁用 Cursor 自動更新",
"lifetime_access_enabled": "終身訪問已啟用",
"totally_reset": "完全重置 Cursor"
"totally_reset": "完全重置 Cursor",
"outdate": "過時",
"temp_github_register": "臨時GitHub註冊",
"admin_required": "運行可執行文件,需要管理員權限",
"admin_required_continue": "繼續使用當前版本...",
"coming_soon": "即將推出",
"fixed_soon": "即將修復",
"contribute": "貢獻項目",
"config": "顯示配置",
"delete_google_account": "刪除 Cursor Google 帳號",
"continue_prompt": "繼續?(y/N): ",
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……"
},
"languages": {
"en": "英文",
@@ -25,7 +40,10 @@
"de": "德文",
"fr": "法文",
"pt": "葡萄牙文",
"ru": "俄文"
"ru": "俄文",
"tr": "土耳其文",
"bg": "保加利亞文",
"es": "西班牙文"
},
"quit_cursor": {
"start": "開始退出 Cursor",
@@ -92,7 +110,13 @@
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "檢查版本失敗: {error}",
"stack_trace": "堆疊跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
"version_too_low": "Cursor版本太低: {version} < 0.45.0",
"no_write_permission": "沒有寫入權限: {path}",
"path_not_found": "路徑未找到: {path}",
"modify_file_failed": "修改文件失敗: {error}",
"windows_machine_id_updated": "Windows機器ID更新成功",
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}"
},
"register": {
@@ -237,7 +261,14 @@
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
"available_domains_loaded": "獲取到 {count} 個可用域名",
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
"trying_to_create_email": "嘗試創建郵箱: {email}"
"trying_to_create_email": "嘗試創建郵箱: {email}",
"domain_blocked": "域名被屏蔽: {domain}",
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
"no_display_found": "未找到顯示器。確保 X 伺服器正在運行。",
"try_export_display": "嘗試: export DISPLAY=:0",
"extension_load_error": "加載插件失敗: {error}",
"make_sure_chrome_chromium_is_properly_installed": "確保 Chrome/Chromium 已正確安裝",
"try_install_chromium": "嘗試: sudo apt install chromium-browser"
},
"update": {
"title": "禁用 Cursor 自动更新",
@@ -250,7 +281,23 @@
"removing_directory": "刪除目錄",
"directory_removed": "目錄已刪除",
"creating_block_file": "創建阻止文件",
"block_file_created": "阻止文件已創建"
"block_file_created": "阻止文件已創建",
"clearing_update_yml": "清空 update.yml 文件",
"update_yml_cleared": "update.yml 文件已清空",
"update_yml_not_found": "update.yml 文件未找到",
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
"unsupported_os": "不支持的操作系统: {system}",
"remove_directory_failed": "刪除目錄失败: {error}",
"create_block_file_failed": "創建阻止文件失败: {error}",
"directory_locked": "目錄被鎖定: {path}",
"yml_locked": "update.yml 文件被鎖定",
"block_file_locked": "阻止文件被鎖定",
"yml_already_locked": "update.yml 文件已鎖定",
"block_file_already_locked": "阻止文件已鎖定",
"block_file_locked_error": "阻止文件锁定错误: {error}",
"yml_locked_error": "update.yml 文件锁定错误: {error}",
"block_file_already_locked_error": "阻止文件已锁定错误: {error}",
"yml_already_locked_error": "update.yml 文件已锁定错误: {error}"
},
"updater": {
"checking": "檢查更新...",
@@ -263,7 +310,8 @@
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
"development_version": "開發版本 {current} > {latest}",
"changelog_title": "更新日志"
"changelog_title": "更新日誌",
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
},
"totally_reset": {
"title": "完全重置 Cursor",
@@ -354,6 +402,232 @@
"removing_electron_localstorage_files": "正在移除 Electron localStorage 檔案",
"electron_localstorage_files_removed": "已移除 Electron localStorage 檔案",
"electron_localstorage_files_removal_error": "移除 Electron localStorage 檔案時出錯:{error}",
"removing_electron_localstorage_files_completed": "Electron localStorage 檔案移除完成"
}
"removing_electron_localstorage_files_completed": "Electron localStorage 檔案移除完成",
"warning_title": "警告",
"warning_1": "此操作將刪除所有 Cursor AI 設定、",
"warning_2": "配置與快取資料。此操作無法還原。",
"warning_3": "您的程式碼檔案將 **不會** 受到影響,且此工具僅針對",
"warning_4": "Cursor AI 編輯器檔案與試用偵測機制。",
"warning_5": "系統中的其他應用程式不會受到影響。",
"warning_6": "執行此工具後,您需要重新設定 Cursor AI。",
"warning_7": "請自行承擔風險",
"removed": "已刪除:{path}",
"failed_to_reset_machine_guid": "無法重置機器 GUID",
"failed_to_remove": "無法刪除:{path}",
"failed_to_delete_file": "無法刪除檔案:{path}",
"failed_to_delete_directory": "無法刪除目錄:{path}",
"failed_to_delete_file_or_directory": "無法刪除檔案或目錄:{path}",
"deep_scanning": "正在進行深度掃描以查找其他試用/授權檔案",
"resetting_cursor": "正在重置 Cursor AI 編輯器... 請稍候。",
"completed_in": "完成時間:{time} 秒",
"cursor_reset_completed": "Cursor AI 編輯器已完全重置且繞過試用偵測!",
"cursor_reset_failed": "Cursor AI 編輯器重置失敗:{error}",
"cursor_reset_cancelled": "Cursor AI 編輯器重置已取消,未進行任何更改。",
"operation_cancelled": "操作已取消,未進行任何更改。"
},
"github_register": {
"title": "GitHub + Cursor AI 注册自动化",
"features_header": "功能",
"feature1": "使用 1secmail 生成临时邮箱",
"feature2": "使用随机凭证注册新的 GitHub 账户",
"feature3": "自动验证 GitHub 邮箱",
"feature4": "使用 GitHub 认证登录 Cursor AI",
"feature5": "重置机器 ID 以绕过试用检测",
"feature6": "保存所有凭证到文件",
"warnings_header": "警告",
"warning1": "此脚本自动化账户创建,可能违反 GitHub/Cursor 服务条款",
"warning2": "需要互联网访问和管理员权限",
"warning3": "CAPTCHA 或额外验证可能会中断自动化",
"warning4": "请负责任地使用,风险自负",
"confirm": "您确定要继续吗?",
"invalid_choice": "无效选择。请输入 'yes' 或 'no'",
"cancelled": "操作已取消",
"program_terminated": "程序已由用户终止",
"starting_automation": "開始自動化...",
"github_username": "GitHub 用戶名",
"github_password": "GitHub 密碼",
"email_address": "郵箱地址",
"credentials_saved": "這些憑證已保存到 github_cursor_accounts.txt",
"completed_successfully": "GitHub + Cursor 註冊成功",
"registration_encountered_issues": "GitHub + Cursor 註冊遇到問題",
"check_browser_windows_for_manual_intervention_or_try_again_later": "檢查瀏覽器視窗進行手動干預或稍後再試"
},
"account_info": {
"subscription": "訂閱",
"trial_remaining": "剩餘試用",
"days": "天",
"subscription_not_found": "訂閱信息未找到",
"email_not_found": "郵箱未找到",
"failed_to_get_account": "獲取帳戶信息失敗",
"config_not_found": "配置未找到。",
"failed_to_get_usage": "獲取使用信息失敗",
"failed_to_get_subscription": "獲取訂閱信息失敗",
"failed_to_get_email": "獲取郵箱地址失敗",
"failed_to_get_token": "獲取 token 失敗",
"failed_to_get_account_info": "獲取帳戶信息失敗",
"title": "帳戶信息",
"email": "郵箱",
"token": "Token",
"usage": "使用量",
"subscription_type": "訂閱類型",
"remaining_trial": "剩餘試用",
"days_remaining": "剩餘天數",
"premium": "高級",
"pro": "專業",
"pro_trial": "專業試用",
"team": "團隊",
"enterprise": "企業",
"free": "免費",
"active": "活躍",
"inactive": "非活躍",
"premium_usage": "高級使用量",
"basic_usage": "基礎使用量",
"usage_not_found": "使用量未找到",
"lifetime_access_enabled": "永久訪問已啟用"
},
"config": {
"config_not_available": "配置未找到。",
"configuration": "配置",
"enabled": "已啟用",
"disabled": "已禁用",
"config_directory": "配置目錄",
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目錄",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "請確保 Cursor 已安裝並至少運行一次",
"storage_directory_not_found": "未找到儲存目錄",
"storage_file_found": "找到儲存文件",
"file_size": "文件大小",
"file_permissions": "文件權限",
"file_owner": "文件所有者",
"file_group": "文件組",
"error_getting_file_stats": "獲取文件統計信息時出錯",
"permission_denied": "權限拒絕",
"try_running": "嘗試運行: {command}",
"and": "和",
"storage_file_is_empty": "儲存文件為空",
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
"storage_file_not_found": "未找到儲存文件",
"error_checking_linux_paths": "檢查 Linux 路徑時出錯",
"config_option_added": "添加配置選項",
"config_updated": "配置更新",
"config_created": "配置已創建",
"config_setup_error": "配置設置錯誤",
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
"error_reading_storage_file": "讀取儲存文件時出錯",
"also_checked": "也檢查了 {path}"
},
"oauth": {
"authentication_button_not_found": "未找到認證按鈕",
"authentication_failed": "認證失敗: {error}",
"found_cookies": "找到 {count} 個 Cookie",
"token_extraction_error": "Token 提取錯誤: {error}",
"authentication_successful": "認證成功 - 郵箱: {email}",
"missing_authentication_data": "缺少認證數據: {data}",
"failed_to_delete_account": "刪除帳戶失敗: {error}",
"invalid_authentication_type": "無效的認證類型",
"auth_update_success": "認證更新成功",
"browser_closed": "瀏覽器已關閉",
"auth_update_failed": "認證更新失敗",
"google_start": "Google 開始",
"github_start": "Github 開始",
"usage_count": "使用量: {usage}",
"account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
"starting_new_authentication_process": "開始新的認證過程...",
"failed_to_delete_expired_account": "刪除過期帳戶失敗",
"could_not_check_usage_count": "無法檢查使用量: {error}",
"found_email": "找到郵箱: {email}",
"could_not_find_email": "未找到郵箱: {error}",
"could_not_find_usage_count": "未找到使用量: {erro r}",
"already_on_settings_page": "已處於設置頁面",
"failed_to_extract_auth_info": "提取認證信息失敗: {error}",
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
"found_default_chrome_profile": "找到默認 Chrome 配置文件",
"using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
"initializing_browser_setup": "初始化瀏覽器設置...",
"detected_platform": "檢測平台: {platform}",
"running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
"consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
"no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
"supported_browsers": "支持的瀏覽器: {platform}",
"using_browser_profile": "使用瀏覽器配置文件: {profile}",
"starting_browser": "正在啟動瀏覽器: {path}",
"browser_setup_completed": "瀏覽器設置完成成功",
"browser_setup_failed": "瀏覽器設置失敗: {error}",
"try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
"starting_github_authentication": "開始 Github 認證...",
"waiting_for_authentication": "等待認證...",
"page_changed_checking_auth": "頁面改變, 檢查認證...",
"status_check_error": "狀態檢查錯誤: {error}",
"authentication_timeout": "認證超時",
"account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
"starting_re_authentication_process": "開始重新認證過程...",
"starting_new_google_authentication": "開始新的 Google 認證...",
"failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
"navigating_to_authentication_page": "正在導航到認證頁面...",
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
"authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
"browser_failed": "瀏覽器啟動失敗: {error}",
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}"
},
"chrome_profile": {
"title": "Chrome配置檔案選擇",
"select_profile": "選擇要使用的Chrome配置檔案",
"profile_list": "可用配置檔案:",
"default_profile": "預設配置檔案",
"profile": "配置檔案 {number}",
"no_profiles": "未找到Chrome配置檔案",
"error_loading": "載入Chrome配置檔案時出錯{error}",
"profile_selected": "已選擇配置檔案:{profile}",
"invalid_selection": "選擇無效。請重試",
"warning_chrome_close": "警告這將關閉所有正在執行的Chrome程序"
},
"account_delete": {
"title": "Cursor Google 帳號刪除工具",
"warning": "警告:這將永久刪除您的 Cursor 帳號。此操作無法撤銷。",
"cancelled": "帳號刪除已取消。",
"starting_process": "開始帳號刪除過程...",
"google_button_not_found": "未找到 Google 登錄按鈕",
"logging_in": "正在使用 Google 登錄...",
"waiting_for_auth": "等待 Google 驗證...",
"login_successful": "登錄成功",
"unexpected_page": "登錄後頁面異常:{url}",
"trying_settings": "嘗試導航到設置頁面...",
"select_google_account": "請選擇您的 Google 帳號...",
"auth_timeout": "認證超時,繼續執行...",
"navigating_to_settings": "正在導航到設置頁面...",
"already_on_settings": "已在設置頁面",
"login_redirect_failed": "登錄重定向失敗,嘗試直接導航...",
"advanced_tab_not_found": "多次嘗試後未找到高級選項卡",
"advanced_tab_retry": "未找到高級選項卡,嘗試 {attempt}/{max_attempts}",
"advanced_tab_error": "查找高級選項卡時出錯:{error}",
"advanced_tab_clicked": "已點擊高級選項卡",
"direct_advanced_navigation": "嘗試直接導航到高級選項卡",
"delete_button_not_found": "多次嘗試後未找到刪除帳號按鈕",
"delete_button_retry": "未找到刪除按鈕,嘗試 {attempt}/{max_attempts}",
"delete_button_error": "查找刪除按鈕時出錯:{error}",
"delete_button_clicked": "已點擊刪除帳號按鈕",
"delete_input_not_found": "多次嘗試後未找到刪除確認輸入框",
"delete_input_retry": "未找到刪除輸入框,嘗試 {attempt}/{max_attempts}",
"delete_input_error": "查找刪除輸入框時出錯:{error}",
"delete_input_not_found_continuing": "未找到刪除確認輸入框,嘗試繼續執行...",
"typed_delete": "已在確認框中輸入\"Delete\"",
"confirm_button_not_found": "多次嘗試後未找到確認按鈕",
"confirm_button_retry": "未找到確認按鈕,嘗試 {attempt}/{max_attempts}",
"confirm_button_error": "查找確認按鈕時出錯:{error}",
"account_deleted": "帳號刪除成功!",
"error": "帳號刪除過程中出錯:{error}",
"success": "您的 Cursor 帳號已成功刪除!",
"failed": "帳號刪除過程失敗或已取消。",
"interrupted": "帳號刪除過程被用戶中斷。",
"unexpected_error": "意外錯誤:{error}",
"found_email": "找到郵箱:{email}",
"email_not_found": "未找到郵箱: {error}",
"found_danger_zone": "已找到危險區域部分",
"confirm_prompt": "您確定要繼續嗎?(y/N): ",
"typed_delete_js": "已使用 JavaScript 輸入\"Delete\""
}
}

84
logo.py
View File

@@ -1,6 +1,8 @@
from colorama import Fore, Style, init
from dotenv import load_dotenv
import os
import shutil
import re
# Get the current script directory
current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -15,27 +17,85 @@ version = os.getenv('VERSION', '1.0.0')
# Initialize colorama
init()
CURSOR_LOGO = f"""
{Fore.CYAN}
# get terminal width
def get_terminal_width():
try:
columns, _ = shutil.get_terminal_size()/2
return columns
except:
return 80 # default width
# center display text (not handling Chinese characters)
def center_multiline_text(text, handle_chinese=False):
width = get_terminal_width()
lines = text.split('\n')
centered_lines = []
for line in lines:
# calculate actual display width (remove ANSI color codes)
clean_line = line
for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Fore.BLUE, Style.RESET_ALL]:
clean_line = clean_line.replace(color, '')
# remove all ANSI escape sequences to get the actual length
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
clean_line = ansi_escape.sub('', clean_line)
# calculate display width
if handle_chinese:
# consider Chinese characters occupying two positions
display_width = 0
for char in clean_line:
if ord(char) > 127: # non-ASCII characters
display_width += 2
else:
display_width += 1
else:
# not handling Chinese characters
display_width = len(clean_line)
# calculate the number of spaces to add
padding = max(0, (width - display_width) // 2)
centered_lines.append(' ' * padding + line)
return '\n'.join(centered_lines)
# original LOGO text
LOGO_TEXT = f"""{Fore.CYAN}
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██████╔╝██████╔╝██║ ██║
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═══╝ ██╔══██╗██║ ██║
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██║ ██║╚██████╔╝
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
{Fore.YELLOW}
Pro Version Activator v{version}
{Fore.GREEN}
Author: Pin Studios | yeongpin
{Style.RESET_ALL}"""
Github: https://github.com/yeongpin/cursor-free-vip
{Fore.RED}
Press 7 to change language | 按下 7 键切换语言
{Style.RESET_ALL}
"""
DESCRIPTION_TEXT = f"""{Fore.YELLOW}
Pro Version Activator v{version}{Fore.GREEN}
Author: Pin Studios (yeongpin)"""
CONTRIBUTORS_TEXT = f"""{Fore.BLUE}
Contributors:
BasaiCorp aliensb handwerk2016 Nigel1992
UntaDotMy RenjiYuusei imbajin ahmed98Osama
bingoohuang mALIk-sHAHId MFaiqKhan httpmerak
muhammedfurkan plamkatawe Lucaszmv
"""
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
# center display LOGO and DESCRIPTION
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
CURSOR_DESCRIPTION = center_multiline_text(DESCRIPTION_TEXT, handle_chinese=False)
CURSOR_CONTRIBUTORS = center_multiline_text(CONTRIBUTORS_TEXT, handle_chinese=False)
CURSOR_OTHER_INFO = center_multiline_text(OTHER_INFO_TEXT, handle_chinese=True)
def print_logo():
print(CURSOR_LOGO)
print(CURSOR_DESCRIPTION)
# print(CURSOR_CONTRIBUTORS)
print(CURSOR_OTHER_INFO)
if __name__ == "__main__":
print_logo()
print_logo()

189
main.py
View File

@@ -9,12 +9,14 @@ import locale
import platform
import requests
import subprocess
from config import get_config
from config import get_config
import shutil
import re
# Only import windll on Windows systems
if platform.system() == 'Windows':
import ctypes
# 只在 Windows 上导入 windll
# Only import windll on Windows systems
from ctypes import windll
# Initialize colorama
@@ -32,7 +34,13 @@ EMOJI = {
"ARROW": "",
"LANG": "🌐",
"UPDATE": "🔄",
"ADMIN": "🔐"
"ADMIN": "🔐",
"AIRDROP": "💰",
"ROCKET": "🚀",
"STAR": "",
"SUN": "🌟",
"CONTRIBUTE": "🤝",
"SETTINGS": "⚙️"
}
# Function to check if running as frozen executable
@@ -109,6 +117,8 @@ class Translator:
0x0804: 'zh_cn', # Simplified Chinese
0x0422: 'vi', # Vietnamese
0x0419: 'ru', # Russian
0x0415: 'tr', # Turkish
0x0402: 'bg', # Bulgarian
}
return language_map.get(layout_id, 'en')
@@ -144,7 +154,10 @@ class Translator:
return 'pt'
elif system_locale.startswith('ru'):
return 'ru'
elif system_locale.startswith('tr'):
return 'tr'
elif system_locale.startswith('bg'):
return 'bg'
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
if 'tw' in env_lang or 'hk' in env_lang:
@@ -163,6 +176,10 @@ class Translator:
return 'pt'
elif 'ru' in env_lang:
return 'ru'
elif 'tr' in env_lang:
return 'tr'
elif 'bg' in env_lang:
return 'bg'
return 'en'
except:
@@ -233,21 +250,113 @@ translator = Translator()
def print_menu():
"""Print menu options"""
try:
config = get_config()
if config.getboolean('Utils', 'enabled_account_info'):
import cursor_acc_info
cursor_acc_info.display_account_info(translator)
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.account_info_error', error=str(e))}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
print(f"{Fore.GREEN}3{Style.RESET_ALL}. 🌟 {translator.get('menu.register_google')}")
print(f"{Fore.YELLOW} ┗━━ 🔥 {translator.get('menu.lifetime_access_enabled')} 🔥{Style.RESET_ALL}")
print(f"{Fore.GREEN}4{Style.RESET_ALL}. ⭐ {translator.get('menu.register_github')}")
print(f"{Fore.YELLOW} ┗━━ 🚀 {translator.get('menu.lifetime_access_enabled')} 🚀{Style.RESET_ALL}")
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
print(f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
print(f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
print(f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
print(f"{Fore.YELLOW}{'' * 70}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{'' * 110}{Style.RESET_ALL}")
# Get terminal width
try:
terminal_width = shutil.get_terminal_size().columns
except:
terminal_width = 80 # Default width
# Define all menu items
menu_items = {
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
1: f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}",
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
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')}"
}
# Automatically calculate the number of menu items in the left and right columns
total_items = len(menu_items)
left_column_count = (total_items + 1) // 2 # The number of options displayed on the left (rounded up)
# Build left and right columns of menus
sorted_indices = sorted(menu_items.keys())
left_menu = [menu_items[i] for i in sorted_indices[:left_column_count]]
right_menu = [menu_items[i] for i in sorted_indices[left_column_count:]]
# Calculate the maximum display width of left menu items
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
def get_display_width(s):
"""Calculate the display width of a string, considering Chinese characters and emojis"""
# Remove ANSI color codes
clean_s = ansi_escape.sub('', s)
width = 0
for c in clean_s:
# Chinese characters and some emojis occupy two character widths
if ord(c) > 127:
width += 2
else:
width += 1
return width
max_left_width = 0
for item in left_menu:
width = get_display_width(item)
max_left_width = max(max_left_width, width)
# Set the starting position of right menu
fixed_spacing = 4 # Fixed spacing
right_start = max_left_width + fixed_spacing
# Calculate the number of spaces needed for right menu items
spaces_list = []
for i in range(len(left_menu)):
if i < len(left_menu):
left_item = left_menu[i]
left_width = get_display_width(left_item)
spaces = right_start - left_width
spaces_list.append(spaces)
# Print menu items
max_rows = max(len(left_menu), len(right_menu))
for i in range(max_rows):
# Print left menu items
if i < len(left_menu):
left_item = left_menu[i]
print(left_item, end='')
# Use pre-calculated spaces
spaces = spaces_list[i]
else:
# If left side has no items, print only spaces
spaces = right_start
print('', end='')
# Print right menu items
if i < len(right_menu):
print(' ' * spaces + right_menu[i])
else:
print() # Change line
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
print(f"{Fore.YELLOW}{'' * 70}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{'' * 110}{Style.RESET_ALL}")
def select_language():
"""Language selection menu"""
@@ -287,6 +396,11 @@ def check_latest_version():
timeout=10
)
# Check if rate limit exceeded
if response.status_code == 403 and "rate limit exceeded" in response.text.lower():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Skipping update check.')}{Style.RESET_ALL}")
return
# Check if response is successful
if response.status_code != 200:
raise Exception(f"GitHub API returned status code {response.status_code}")
@@ -424,11 +538,11 @@ def check_latest_version():
def main():
# Check for admin privileges if running as executable on Windows only
if platform.system() == 'Windows' and is_frozen() and not is_admin():
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} Running as executable, administrator privileges required.{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} {translator.get('menu.admin_required')}{Style.RESET_ALL}")
if run_as_admin():
sys.exit(0) # Exit after requesting admin privileges
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without administrator privileges.{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.admin_required_continue')}{Style.RESET_ALL}")
print_logo()
@@ -438,12 +552,14 @@ def main():
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
if config.getboolean('Utils', 'enabled_update_check'):
check_latest_version() # Add version check before showing menu
print_menu()
while True:
try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-9')}: {Style.RESET_ALL}")
choice_num = 14
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
@@ -470,20 +586,43 @@ def main():
cursor_register_manual.main(translator)
print_menu()
elif choice == "6":
import github_cursor_register
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
# github_cursor_register.main(translator)
print_menu()
elif choice == "7":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
elif choice == "7":
elif choice == "8":
if select_language():
print_menu()
continue
elif choice == "8":
elif choice == "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
elif choice == "9":
elif choice == "10":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
elif choice == "11":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
elif choice == "12":
from config import print_config
print_config(get_config(), translator)
print_menu()
elif choice == "13":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
elif choice == "14":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")

View File

@@ -12,16 +12,32 @@ from config import get_config
# Add global variable at the beginning of the file
_translator = None
# Add global variable to track our Chrome processes
_chrome_process_ids = []
def cleanup_chrome_processes(translator=None):
"""Clean all Chrome related processes"""
print("\nCleaning Chrome processes...")
"""Clean only Chrome processes launched by this script"""
global _chrome_process_ids
if not _chrome_process_ids:
print("\nNo Chrome processes to clean...")
return
print("\nCleaning Chrome processes launched by this script...")
try:
if os.name == 'nt':
os.system('taskkill /F /IM chrome.exe /T 2>nul')
os.system('taskkill /F /IM chromedriver.exe /T 2>nul')
for pid in _chrome_process_ids:
try:
os.system(f'taskkill /F /PID {pid} /T 2>nul')
except:
pass
else:
os.system('pkill -f chrome')
os.system('pkill -f chromedriver')
for pid in _chrome_process_ids:
try:
os.kill(pid, signal.SIGTERM)
except:
pass
_chrome_process_ids = [] # Reset the list after cleanup
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.cleanup_error', error=str(e))}{Style.RESET_ALL}")
@@ -39,7 +55,7 @@ def signal_handler(signum, frame):
os._exit(0)
def simulate_human_input(page, url, config, translator=None):
"""Visit URL with human-like behavior"""
"""Visit URL"""
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
@@ -47,194 +63,53 @@ def simulate_human_input(page, url, config, translator=None):
page.get('about:blank')
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# Add random mouse movements before visiting target page
try:
page.run_js("""
function simulateMouseMovement() {
const points = [];
const numPoints = Math.floor(Math.random() * 10) + 5;
for (let i = 0; i < numPoints; i++) {
points.push({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight
});
}
points.forEach((point, i) => {
setTimeout(() => {
const event = new MouseEvent('mousemove', {
view: window,
bubbles: true,
cancelable: true,
clientX: point.x,
clientY: point.y
});
document.dispatchEvent(event);
}, i * (Math.random() * 200 + 50));
});
}
simulateMouseMovement();
""")
except:
pass # Ignore if JavaScript execution fails
# Visit target page
page.get(url)
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# Add some random scrolling
try:
page.run_js("""
function simulateHumanScroll() {
const maxScroll = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.body.clientHeight,
document.documentElement.clientHeight
);
const scrollPoints = [];
const numPoints = Math.floor(Math.random() * 3) + 2;
for (let i = 0; i < numPoints; i++) {
scrollPoints.push(Math.floor(Math.random() * maxScroll));
}
scrollPoints.sort((a, b) => a - b);
scrollPoints.forEach((point, i) => {
setTimeout(() => {
window.scrollTo({
top: point,
behavior: 'smooth'
});
}, i * (Math.random() * 1000 + 500));
});
}
simulateHumanScroll();
""")
except:
pass # Ignore if JavaScript execution fails
def simulate_human_typing(element, text, config):
"""Simulate human-like typing with random delays between keystrokes"""
for char in text:
element.input(char)
# Random delay between keystrokes (30-100ms)
time.sleep(random.uniform(0.03, 0.1))
time.sleep(get_random_wait_time(config, 'input_wait'))
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""Fill signup form with human-like behavior"""
max_retries = 5
retry_count = 0
while retry_count < max_retries:
try:
if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')} (Attempt {retry_count + 1}/{max_retries}){Style.RESET_ALL}")
else:
print(f"\n正在填写注册表单... (Attempt {retry_count + 1}/{max_retries})")
# Add random initial delay
time.sleep(random.uniform(0.5, 1.5))
# Fill first name with human-like typing
first_name_input = page.ele("@name=first_name")
if first_name_input:
simulate_human_typing(first_name_input, first_name, config)
# Add random pause between fields
time.sleep(random.uniform(0.3, 0.8))
# Fill last name with human-like typing
last_name_input = page.ele("@name=last_name")
if last_name_input:
simulate_human_typing(last_name_input, last_name, config)
# Add random pause between fields
time.sleep(random.uniform(0.3, 0.8))
# Fill email with human-like typing
email_input = page.ele("@name=email")
if email_input:
simulate_human_typing(email_input, email, config)
# Add random pause before submitting
time.sleep(random.uniform(0.5, 1.2))
# Move mouse to submit button with human-like movement
submit_button = page.ele("@type=submit")
if submit_button:
try:
# Simulate mouse movement to button
page.run_js("""
function moveToButton(button) {
const rect = button.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
// Create a curved path to the button
const startX = Math.random() * window.innerWidth;
const startY = Math.random() * window.innerHeight;
const controlX = (startX + centerX) / 2 + (Math.random() - 0.5) * 100;
const controlY = (startY + centerY) / 2 + (Math.random() - 0.5) * 100;
const steps = 20;
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const x = Math.pow(1-t, 2) * startX + 2 * (1-t) * t * controlX + Math.pow(t, 2) * centerX;
const y = Math.pow(1-t, 2) * startY + 2 * (1-t) * t * controlY + Math.pow(t, 2) * centerY;
setTimeout(() => {
const event = new MouseEvent('mousemove', {
view: window,
bubbles: true,
cancelable: true,
clientX: x,
clientY: y
});
document.dispatchEvent(event);
}, i * (Math.random() * 20 + 10));
}
}
moveToButton(document.querySelector('button[type="submit"]'));
""")
time.sleep(random.uniform(0.3, 0.6))
except:
pass # Ignore if JavaScript execution fails
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
# Check for human verification error
error_message = page.ele("text:Can't verify the user is human")
if error_message:
if translator:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.human_verify_error')} (Attempt {retry_count + 1}/{max_retries}){Style.RESET_ALL}")
else:
print(f"Can't verify the user is human. Retrying... (Attempt {retry_count + 1}/{max_retries})")
retry_count += 1
# Add longer random delay between retries
time.sleep(random.uniform(2, 4))
continue
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
print("Form filled successfully")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error filling form: {e}")
retry_count += 1
if retry_count < max_retries:
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
continue
return False
if retry_count >= max_retries:
"""Fill signup form"""
try:
if translator:
print(f"{Fore.RED} {translator.get('register.max_retries_reached')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else:
print("Maximum retry attempts reached. Registration failed.")
print("\n正在填写注册表单...")
# Fill first name
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'))
# Fill last name
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'))
# Fill email
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
print("Form filled successfully")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error filling form: {e}")
return False
def get_default_chrome_path():
@@ -304,7 +179,9 @@ def get_random_wait_time(config, timing_type='page_load_wait'):
return random.uniform(0.1, 0.8) # Return default value when error
def setup_driver(translator=None):
"""Setup browser driver with randomized fingerprint"""
"""Setup browser driver"""
global _chrome_process_ids
try:
# Get config
config = get_config(translator)
@@ -326,42 +203,11 @@ def setup_driver(translator=None):
# Use incognito mode
co.set_argument("--incognito")
# Randomize browser fingerprint
# Random screen resolution
resolutions = [
"1920,1080", "1366,768", "1536,864", "1440,900",
"1280,720", "1600,900", "1024,768", "1680,1050"
]
window_size = random.choice(resolutions)
co.set_argument(f"--window-size={window_size}")
# Random user agent
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
]
co.set_argument(f"--user-agent={random.choice(user_agents)}")
# Random color depth
color_depths = ["24", "30", "48"]
co.set_argument(f"--color-depth={random.choice(color_depths)}")
# Additional fingerprint randomization
co.set_argument("--disable-blink-features=AutomationControlled") # Hide automation
co.set_argument("--disable-features=IsolateOrigins,site-per-process")
# Random platform
platforms = ["Win32", "Win64", "MacIntel", "Linux x86_64"]
co.set_argument(f"--platform={random.choice(platforms)}")
# Random language
languages = ["en-US", "en-GB", "fr-FR", "de-DE", "es-ES", "it-IT"]
co.set_argument(f"--lang={random.choice(languages)}")
if sys.platform == "linux":
# Set random port
co.set_argument("--no-sandbox")
# Set random port
co.set_argument("--no-sandbox")
co.auto_port()
# Use headless mode (must be set to False, simulate human operation)
@@ -384,64 +230,35 @@ def setup_driver(translator=None):
else:
print("Starting browser...")
# Record Chrome processes before launching
before_pids = []
try:
import psutil
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
except:
pass
# Launch browser
page = ChromiumPage(co)
# Additional JavaScript-based fingerprint randomization
try:
page.run_js("""
// Override navigator properties
const originalNavigator = window.navigator;
const navigatorProxy = new Proxy(originalNavigator, {
get: function(target, key) {
switch (key) {
case 'webdriver':
return undefined;
case 'plugins':
return [
{ name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
{ name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
{ name: 'Native Client', filename: 'internal-nacl-plugin' }
];
case 'languages':
return ['en-US', 'en'];
default:
return target[key];
}
}
});
// Override permissions
const originalPermissions = window.Permissions;
window.Permissions = new Proxy(originalPermissions, {
get: function(target, key) {
if (key === 'prototype') {
return {
query: async () => ({ state: 'prompt' })
};
}
return target[key];
}
});
// Random canvas fingerprint
const originalGetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = function() {
const context = originalGetContext.apply(this, arguments);
if (context && arguments[0] === '2d') {
const originalFillText = context.fillText;
context.fillText = function() {
const noise = Math.random() * 0.1;
context.rotate(noise);
originalFillText.apply(this, arguments);
context.rotate(-noise);
};
}
return context;
};
""")
except:
pass # Ignore if JavaScript execution fails
# Wait a moment for Chrome to fully launch
time.sleep(1)
# Record Chrome processes after launching and find new ones
try:
import psutil
after_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
# Find new Chrome processes
new_pids = [pid for pid in after_pids if pid not in before_pids]
_chrome_process_ids.extend(new_pids)
if _chrome_process_ids:
print(f"Tracking {len(_chrome_process_ids)} Chrome processes")
else:
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
except Exception as e:
print(f"Warning: Could not track Chrome processes: {e}")
return config, page
except Exception as e:
@@ -793,7 +610,9 @@ def handle_sign_in(browser_tab, email, password, translator=None):
def main(email=None, password=None, first_name=None, last_name=None, email_tab=None, controller=None, translator=None):
"""Main function, can receive account information, email tab, and translator"""
global _translator
global _chrome_process_ids
_translator = translator # Save to global variable
_chrome_process_ids = [] # Reset the process IDs list
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

View File

@@ -106,7 +106,31 @@ class NewTempEmail:
# 创建浏览器选项
co = ChromiumOptions()
co.set_argument("--headless=new")
# Only use headless for non-OAuth operations
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
co.set_argument("--headless=new")
if sys.platform == "linux":
# Check if DISPLAY is set when not in headless mode
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
print(f"{Fore.RED}{self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW} {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
return False
co.set_argument("--no-sandbox")
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-gpu")
# If running as root, try to use actual user's Chrome profile
if os.geteuid() == 0:
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
actual_home = f"/home/{sudo_user}"
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
if os.path.exists(user_data_dir):
print(f"{Fore.CYAN} {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
co.set_argument(f"--user-data-dir={user_data_dir}")
co.auto_port() # 自动设置端口
@@ -128,48 +152,52 @@ class NewTempEmail:
print(f"{Fore.RED}{self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
if sys.platform == "linux":
print(f"{Fore.YELLOW} {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW} {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def create_email(self):
"""创建临时邮箱"""
"""create temporary email"""
try:
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在访问 smailpro.com...{Style.RESET_ALL}")
# 加载被屏蔽域名列表
# load blocked domains list
self.blocked_domains = self.get_blocked_domains()
# 访问网站
# visit website
self.page.get("https://smailpro.com/")
time.sleep(2)
# 点击创建邮箱按钮
# click create email button
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
if create_button:
create_button.click()
time.sleep(1)
# 点击弹窗中的 Create 按钮
# click Create button in popup
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]')
if modal_create_button:
modal_create_button.click()
time.sleep(2)
# 获取邮箱地址 - 修改选择器
# get email address - modify selector
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
if email_div:
email = email_div.text.strip()
if '@' in email: # 验证是否是有效的邮箱地址
# 检查域名是否被屏蔽
if '@' in email: # check if it's a valid email address
# check if domain is blocked
domain = email.split('@')[1]
if self.blocked_domains and domain in self.blocked_domains:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domain_blocked')}: {domain}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 域名已被屏蔽: {domain},尝试重新创建邮箱{Style.RESET_ALL}")
# 重新创建邮箱
# create email again
return self.create_email()
if self.translator:
@@ -191,23 +219,23 @@ class NewTempEmail:
return None
def close(self):
"""关闭浏览器"""
"""close browser"""
if self.page:
self.page.quit()
def refresh_inbox(self):
"""刷新邮箱"""
"""refresh inbox"""
try:
if self.translator:
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
# 点击刷新按钮
# click refresh button
refresh_button = self.page.ele('xpath://button[@id="refresh"]')
if refresh_button:
refresh_button.click()
time.sleep(2) # 等待刷新完成
time.sleep(2) # wait for refresh to complete
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
else:
@@ -230,16 +258,16 @@ class NewTempEmail:
def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件"""
try:
# 查找验证邮件 - 使用更精确的选择器
# find verification email - use more accurate selector
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]')
if email_div:
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
# 使用 JavaScript 点击元素
# use JavaScript to click element
self.page.run_js('arguments[0].click()', email_div)
time.sleep(2) # 等待邮件内容加载
time.sleep(2) # wait for email content to load
return True
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
@@ -257,7 +285,7 @@ class NewTempEmail:
def get_verification_code(self):
"""获取验证码"""
try:
# 查找验证码元素
# find verification code element
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]')
if code_element:
code = code_element.text.strip()
@@ -291,7 +319,7 @@ def main(translator=None):
else:
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
# 测试刷新功能
# test refresh function
while True:
if translator:
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()

View File

@@ -21,105 +21,140 @@ EMOJI = {
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': ''
'INFO': '',
'WARNING': '⚠️'
}
class OAuthHandler:
def __init__(self, translator=None):
def __init__(self, translator=None, auth_type=None):
self.translator = translator
self.config = get_config(translator)
self.auth_type = auth_type # make sure the auth_type is not None
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.selected_profile = None
def _get_active_profile(self, user_data_dir):
"""Find the existing default/active Chrome profile"""
def _get_available_profiles(self, user_data_dir):
"""Get list of available Chrome profiles with their names"""
try:
# List all profile directories
profiles = []
for item in os.listdir(user_data_dir):
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
profiles.append(item)
profile_names = {}
if not profiles:
print(f"{Fore.YELLOW}{EMOJI['INFO']} No Chrome profiles found, using Default{Style.RESET_ALL}")
return 'Default'
# First check if Default profile exists
if 'Default' in profiles:
print(f"{Fore.CYAN}{EMOJI['INFO']} Found Default Chrome profile{Style.RESET_ALL}")
return 'Default'
# If no Default profile, check Local State for last used profile
# Read Local State file to get profile names
local_state_path = os.path.join(user_data_dir, 'Local State')
if os.path.exists(local_state_path):
with open(local_state_path, 'r', encoding='utf-8') as f:
local_state = json.load(f)
# Get info about last used profile
profile_info = local_state.get('profile', {})
last_used = profile_info.get('last_used', '')
info_cache = profile_info.get('info_cache', {})
# Try to find an active profile
for profile in profiles:
profile_path = profile.replace('\\', '/')
if profile_path in info_cache:
#print(f"{Fore.CYAN}{EMOJI['INFO']} Using existing Chrome profile: {profile}{Style.RESET_ALL}")
return profile
# If no profile found in Local State, use the first available profile
print(f"{Fore.CYAN}{EMOJI['INFO']} Using first available Chrome profile: {profiles[0]}{Style.RESET_ALL}")
return profiles[0]
info_cache = local_state.get('profile', {}).get('info_cache', {})
for profile_dir, info in info_cache.items():
profile_dir = profile_dir.replace('\\', '/')
if profile_dir == 'Default':
profile_names['Default'] = info.get('name', 'Default')
elif profile_dir.startswith('Profile '):
profile_names[profile_dir] = info.get('name', profile_dir)
# Get list of profile directories
for item in os.listdir(user_data_dir):
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
profiles.append((item, profile_names.get(item, item)))
return sorted(profiles)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return []
def _select_profile(self):
"""Select a Chrome profile to use"""
try:
# Get available profiles
profiles = self._get_available_profiles(self._get_user_data_directory())
if not profiles:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('chrome_profile.no_profiles') if self.translator else 'No Chrome profiles found'}{Style.RESET_ALL}")
return False
# Display available profiles
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('chrome_profile.select_profile') if self.translator else 'Select a Chrome profile to use:'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('chrome_profile.profile_list') if self.translator else 'Available profiles:'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit') if self.translator else 'Exit'}{Style.RESET_ALL}")
for i, (dir_name, display_name) in enumerate(profiles, 1):
print(f"{Fore.CYAN}{i}. {display_name} ({dir_name}){Style.RESET_ALL}")
# Get user selection
while True:
try:
choice = int(input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{len(profiles)}') if self.translator else f'Please enter your choice (0-{len(profiles)}): '}{Style.RESET_ALL}"))
if choice == 0: # Add quit
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.exiting') if self.translator else 'Exiting profile selection...'}{Style.RESET_ALL}")
return False
elif 1 <= choice <= len(profiles):
self.selected_profile = profiles[choice - 1][0]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('chrome_profile.profile_selected', profile=self.selected_profile) if self.translator else f'Selected profile: {self.selected_profile}'}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
except ValueError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Error finding Chrome profile, using Default: {str(e)}{Style.RESET_ALL}")
return 'Default'
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return False
def setup_browser(self):
"""Setup browser for OAuth flow using active profile"""
"""Setup browser for OAuth flow using selected profile"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Initializing browser setup...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
# Platform-specific initialization
platform_name = platform.system().lower()
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected platform: {platform_name}{Style.RESET_ALL}")
# Kill existing browser processes
self._kill_browser_processes()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
# Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
chrome_path = self._get_browser_path()
if not chrome_path:
raise Exception(f"No compatible browser found. Please install Google Chrome or Chromium.\nSupported browsers for {platform_name}:\n" +
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, chromium-browser")
# Get active profile
active_profile = self._get_active_profile(user_data_dir)
print(f"{Fore.CYAN}{EMOJI['INFO']} Using browser profile: {active_profile}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
# Show warning about closing Chrome first
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('chrome_profile.warning_chrome_close') if self.translator else 'Warning: This will close all running Chrome processes'}{Style.RESET_ALL}")
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
if choice != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
# Kill existing browser processes
self._kill_browser_processes()
# Let user select a profile
if not self._select_profile():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
# Configure browser options
co = self._configure_browser_options(chrome_path, user_data_dir, active_profile)
co = self._configure_browser_options(chrome_path, user_data_dir, self.selected_profile)
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting browser at: {chrome_path}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
# Verify browser launched successfully
if not self.browser:
raise Exception("Failed to initialize browser instance")
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Browser setup completed successfully{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Browser setup failed: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running with administrator/root privileges{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
elif "Chrome failed to start" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
if platform_name == 'linux':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def _kill_browser_processes(self):
@@ -136,7 +171,7 @@ class OAuthHandler:
time.sleep(1) # Wait for processes to close
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Warning: Could not kill existing browser processes: {e}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the appropriate user data directory based on platform"""
@@ -162,17 +197,16 @@ class OAuthHandler:
# Try each possible path
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.CYAN}{EMOJI['INFO']} Found browser data directory: {path}{Style.RESET_ALL}")
return path
# Create temporary profile if no existing profile found
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
print(f"{Fore.YELLOW}{EMOJI['INFO']} Creating temporary profile at: {temp_profile}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
os.makedirs(temp_profile, exist_ok=True)
return temp_profile
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting user data directory: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
raise
def _get_browser_path(self):
@@ -183,7 +217,7 @@ class OAuthHandler:
if chrome_path and os.path.exists(chrome_path):
return chrome_path
print(f"{Fore.YELLOW}{EMOJI['INFO']} Searching for alternative browser installations...{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
# Platform-specific paths
if os.name == 'nt': # Windows
@@ -215,13 +249,13 @@ class OAuthHandler:
for path in alt_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found browser at: {expanded_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
return expanded_path
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error finding browser path: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
return None
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
@@ -250,22 +284,22 @@ class OAuthHandler:
return co
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error configuring browser options: {e}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -289,16 +323,17 @@ class OAuthHandler:
raise Exception("Could not find Google authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting Google authentication...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account to continue...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
self.browser.run_js("""
alert('Please select your Google account to continue with Cursor authentication');
self.browser.run_js(f"""
alert('{alert_message}');
""")
except:
pass # Alert is optional
@@ -306,14 +341,14 @@ class OAuthHandler:
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
@@ -333,7 +368,7 @@ class OAuthHandler:
start_time = time.time()
check_interval = 2 # Check every 2 seconds
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for authentication (timeout: 5 minutes)...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
@@ -352,7 +387,7 @@ class OAuthHandler:
if token:
# Get email from settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} Authentication successful, getting account info...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
@@ -361,7 +396,7 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {email}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
email = "user@cursor.sh" # Fallback email
@@ -370,42 +405,51 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, creating new account...{Style.RESET_ALL}")
def check_usage_limits(usage_str):
try:
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
# Delete current account
if self._delete_current_account():
# Start new authentication based on auth type
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
if auth_type == "google":
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else: # github
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete expired account{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not check usage count: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
# Also check URL as backup
if "cursor.com/settings" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected successful login{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Waiting for authentication... ({str(e)}){Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error while waiting for authentication: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
@@ -420,7 +464,7 @@ class OAuthHandler:
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -444,21 +488,21 @@ class OAuthHandler:
raise Exception("Could not find GitHub authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting GitHub authentication...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
@@ -520,23 +564,23 @@ class OAuthHandler:
# Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
alert_js = """
alert('Please select your Google account manually to continue with Cursor authentication');
"""
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
self.browser.run_js(alert_js)
self.browser.run_js(f"""
alert('{alert_message}');
""")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Alert display failed: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account manually to continue with Cursor authentication...{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Waiting for authentication to complete...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
# Wait for authentication to complete
max_wait = 300 # 5 minutes
start_time = time.time()
last_url = self.browser.url
print(f"{Fore.CYAN}{EMOJI['WAIT']} Checking authentication status...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.checking_authentication_status') if self.translator else 'Checking authentication status...'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
@@ -552,9 +596,9 @@ class OAuthHandler:
token = value.split("%3A%3A")[-1]
if token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful!{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
# Navigate to settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to settings page...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3) # Wait for settings page to load
@@ -563,9 +607,9 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
@@ -573,59 +617,34 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
return new Promise((resolve, reject) => {
fetch('https://www.cursor.com/api/dashboard/delete-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
})
.then(response => {
if (response.status === 200) {
resolve('Account deleted successfully');
} else {
reject('Failed to delete account: ' + response.status);
}
})
.catch(error => {
reject('Error: ' + error);
});
});
}
return deleteAccount();
"""
def check_usage_limits(usage_str):
try:
result = self.browser.run_js(delete_js)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
@@ -633,7 +652,7 @@ class OAuthHandler:
# Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Already on settings page!{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
for cookie in cookies:
@@ -649,9 +668,9 @@ class OAuthHandler:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
@@ -659,80 +678,55 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
return new Promise((resolve, reject) => {
fetch('https://www.cursor.com/api/dashboard/delete-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
})
.then(response => {
if (response.status === 200) {
resolve('Account deleted successfully');
} else {
reject('Failed to delete account: ' + response.status);
}
})
.catch(error => {
reject('Error: ' + error);
});
});
}
return deleteAccount();
"""
def check_usage_limits(usage_str):
try:
result = self.browser.run_js(delete_js)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Page changed, checking auth...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Status check error: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication button not found{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication failed: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
@@ -755,7 +749,7 @@ class OAuthHandler:
time.sleep(1)
# Debug cookie information
print(f"{Fore.CYAN}{EMOJI['INFO']} Found {len(cookies)} cookies{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
token = None
@@ -770,12 +764,12 @@ class OAuthHandler:
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Token extraction error: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
if email and token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful - Email: {email}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
missing = []
@@ -783,11 +777,11 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
print(f"{Fore.RED}{EMOJI['ERROR']} Missing authentication data: {', '.join(missing)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to extract auth info: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
return False, None
def _delete_current_account(self):
@@ -822,14 +816,14 @@ class OAuthHandler:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
return False
def main(auth_type, translator=None):
@@ -839,16 +833,16 @@ def main(auth_type, translator=None):
auth_type (str): Type of authentication ('google' or 'github')
translator: Translator instance for internationalization
"""
handler = OAuthHandler(translator)
handler = OAuthHandler(translator, auth_type)
if auth_type.lower() == 'google':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_google_auth()
elif auth_type.lower() == 'github':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid authentication type{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
return False
if success and auth_info:
@@ -859,13 +853,13 @@ def main(auth_type, translator=None):
access_token=auth_info["token"],
refresh_token=auth_info["token"]
):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success')}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
# Close the browser after successful authentication
if handler.browser:
handler.browser.quit()
print(f"{Fore.CYAN}{EMOJI['INFO']} Browser closed{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
return False

View File

@@ -5,4 +5,6 @@ requests
psutil>=5.8.0
pywin32; platform_system == "Windows"
pyinstaller
DrissionPage>=4.0.0
DrissionPage>=4.0.0
selenium
webdriver_manager

View File

@@ -8,6 +8,7 @@ import sqlite3
import platform
import re
import tempfile
import glob
from colorama import Fore, Style, init
from typing import Tuple
import configparser
@@ -26,6 +27,7 @@ EMOJI = {
"ERROR": "",
"INFO": "",
"RESET": "🔄",
"WARNING": "⚠️",
}
def get_cursor_paths(translator=None) -> Tuple[str, str]:
@@ -37,10 +39,60 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
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 "找不到配置文件")
# Create config directory if it doesn't exist
if not os.path.exists(config_dir):
os.makedirs(config_dir)
# Default paths for different systems
default_paths = {
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app"), "/usr/lib/cursor/app/"]
}
if system == "Linux":
# Look for extracted AppImage with correct usr structure
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
# Also check current directory for extraction without home path prefix
current_dir_paths = glob.glob("squashfs-root/usr/share/cursor/resources/app")
config.read(config_file, encoding='utf-8') # Specify encoding
# Add any found paths to the Linux paths list
default_paths["Linux"].extend(extracted_usr_paths)
default_paths["Linux"].extend(current_dir_paths)
# Print debug information
print(f"{Fore.CYAN}{EMOJI['INFO']} Available paths found:{Style.RESET_ALL}")
for path in default_paths["Linux"]:
if os.path.exists(path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {path} (exists){Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {path} (not found){Style.RESET_ALL}")
# If config doesn't exist, create it with default paths
if not os.path.exists(config_file):
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
if not config.has_section(section):
config.add_section(section)
if system == "Darwin":
config.set('MacPaths', 'cursor_path', default_paths["Darwin"])
elif system == "Windows":
config.set('WindowsPaths', 'cursor_path', default_paths["Windows"])
elif system == "Linux":
# For Linux, try to find the first existing path
for path in default_paths["Linux"]:
if os.path.exists(path):
config.set('LinuxPaths', 'cursor_path', path)
break
else:
# If no path exists, use the first one as default
config.set('LinuxPaths', 'cursor_path', default_paths["Linux"][0])
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
else:
config.read(config_file, encoding='utf-8')
# Get path based on system
if system == "Darwin":
@@ -51,15 +103,26 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
section = 'LinuxPaths'
else:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
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')
# For Linux, try to find the first existing path if the configured one doesn't exist
if system == "Linux" and not os.path.exists(base_path):
for path in default_paths["Linux"]:
if os.path.exists(path):
base_path = path
# Update config with the found path
config.set(section, 'cursor_path', path)
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
break
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")
@@ -116,6 +179,14 @@ def get_cursor_machine_id_path(translator=None) -> str:
def get_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
# 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)
paths_map = {
"Darwin": { # macOS
@@ -123,14 +194,19 @@ def get_workbench_cursor_path(translator=None) -> str:
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Windows": {
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
"main": "out/vs/workbench/workbench.desktop.main.js"
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
"main": "out/vs/workbench/workbench.desktop.main.js"
}
}
if system == "Linux":
# Add extracted AppImage with correct usr structure
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
if system not in paths_map:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
@@ -138,11 +214,15 @@ def get_workbench_cursor_path(translator=None) -> str:
if system == "Linux":
for base in paths_map["Linux"]["bases"]:
main_path = os.path.join(base, paths_map["Linux"]["main"])
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
if os.path.exists(main_path):
return main_path
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"]
if system == "Windows":
base_path = config.get('WindowsPaths', 'cursor_path')
else:
base_path = paths_map[system]["base"]
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):
@@ -256,11 +336,11 @@ def modify_workbench_js(file_path: str, translator=None) -> bool:
if sys.platform == "win32":
# Define replacement patterns
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "linux":
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "darwin":
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
@@ -534,6 +614,7 @@ class MachineIDResetter:
if sys.platform.startswith("win"):
self._update_windows_machine_guid()
self._update_windows_machine_id()
elif sys.platform == "darwin":
self._update_macos_platform_uuid(new_ids)
@@ -563,6 +644,45 @@ class MachineIDResetter:
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
raise
def _update_windows_machine_id(self):
"""Update Windows MachineId in SQMClient registry"""
try:
import winreg
# 1. Generate new GUID
new_guid = "{" + str(uuid.uuid4()).upper() + "}"
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.new_machine_id')}: {new_guid}{Style.RESET_ALL}")
# 2. Open the registry key
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\SQMClient",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
except FileNotFoundError:
# If the key does not exist, create it
key = winreg.CreateKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\SQMClient"
)
# 3. Set MachineId value
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_id_updated')}{Style.RESET_ALL}")
return True
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
return False
def _update_macos_platform_uuid(self, new_ids):
"""Update macOS Platform UUID"""
@@ -621,13 +741,10 @@ class MachineIDResetter:
self.update_system_ids(new_ids)
### 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)
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)
@@ -706,4 +823,4 @@ def run(translator=None):
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)
run(main_translator)

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,15 @@ def get_user_documents_path():
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
# Trying to find chrome in PATH
try:
import shutil
chrome_in_path = shutil.which("chrome")
if chrome_in_path:
return chrome_in_path
except:
pass
# Going to default path
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif sys.platform == "darwin":
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"