Compare commits

...

149 Commits

Author SHA1 Message Date
Pin Studios
809dac091d Update CHANGELOG.md to include mandatory update notice for version 1.11.01, enhancing user awareness of the new features and improvements. 2025-04-24 11:21:12 +08:00
Pin Studios
da5bff5994 Update version to 1.11.01 and enhance localization for OAuth profile selection. Added new features including Arabic language support, machine ID restoration from backup, and improved error handling in the update process. Updated menu options and fixed various issues for a better user experience. 2025-04-24 11:20:21 +08:00
Pin Studios
296a69bf73 Add CursorGoogleAccountDeleter class for Google account deletion via OAuth. Implemented robust authentication flow, including dynamic waits, error handling, and user feedback. This tool enhances user experience by providing a streamlined process for account deletion. 2025-04-24 10:51:09 +08:00
Pin Studios
5ca6c97d96 Add OAuthHandler class for Google and GitHub authentication, including profile selection and browser setup. Implemented error handling and user feedback for various authentication states. This new functionality enhances the user experience by streamlining the OAuth process. 2025-04-24 10:50:21 +08:00
Pin Studios
fb52888f9c Merge pull request #756 from ppanphper/main
增加备份的机器ID还原功能
2025-04-24 10:47:52 +08:00
Pin Studios
8e6df1d1f8 Merge branch 'main' into main 2025-04-24 10:47:28 +08:00
Pin Studios
be5a17c861 Update localization files to add "restore_machine_id" entry in English, Vietnamese, Simplified Chinese, and Traditional Chinese, enhancing user feedback consistency across languages. 2025-04-24 10:45:44 +08:00
Pin Studios
ca496ea53f Remove "restore_machine_id" entry from English localization file to streamline user feedback messages. 2025-04-24 10:45:00 +08:00
Pin Studios
52eaecd040 Update localization files to add new language configuration messages for English, Vietnamese, Simplified Chinese, and Traditional Chinese. Added "language_config_saved" and "lang_invalid_choice" entries to enhance user feedback. 2025-04-24 10:43:01 +08:00
Pin Studios
5fac4d6e45 Merge pull request #751 from TranMC/main
Language configuration saved setting
2025-04-24 10:39:06 +08:00
ppanphper
271d5d9db9 Merge branch 'yeongpin:main' into main 2025-04-23 16:40:44 +08:00
ppanphper
c2af657c88 添加恢复机器ID功能,新增restore_machine_id.py文件并更新主菜单以支持该功能。更新多语言支持,包含相关提示信息和错误处理。 2025-04-23 16:31:53 +08:00
TranMC
cf55c91117 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:58:27 +07:00
TranMC
9abf4f4899 Change something in language file 2025-04-23 12:58:24 +07:00
TranMC
a78b1160c2 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:56:22 +07:00
TranMC
c5453a4374 Merge branch 'main' of https://github.com/TranMC/cursor-free-vip 2025-04-23 12:49:06 +07:00
TranMC
8c497d7639 Commit vui vui 2025-04-23 12:48:58 +07:00
TranMC
c0c2cd6120 Merge branch 'main' into main 2025-04-23 12:46:52 +07:00
Pin Studios
e36f6d986e Merge pull request #745 from Ahmed-Nagi1/add-arabic-translation
Add Arabic language
2025-04-23 13:13:05 +08:00
TranMC
0b547c0542 Fix nofitication 2025-04-23 12:09:02 +07:00
TranMC
ef17ba8803 Small language change 2025-04-23 12:04:03 +07:00
TranMC
31bf75e4de Update language configuration 2025-04-23 11:38:17 +07:00
TranMC
63e4e72ec7 Add language config saved 2025-04-23 10:57:50 +07:00
Ahmed Nagi
b51d9c7a74 Add Arabic language option to all locale files and support proper Arabic text display in terminal
- Added "ar" (Arabic) to the languages section in all translation JSON files in the locales directory.
- Implemented Arabic text reshaping and bidi support in main.py using arabic_reshaper and python-bidi for correct RTL display in terminal when Arabic is selected.
2025-04-22 10:57:51 +02:00
yeongpin
4aba849cf1 Refactor GitHub Actions workflow to adjust job dependencies for build processes, ensuring that version determination occurs before tag creation for Linux and macOS builds. 2025-04-22 11:15:15 +08:00
yeongpin
1489357328 Update GitHub Actions workflow to ensure build jobs depend on version determination. Simplify ARM64 Docker build process by directly renaming output files without pre-checks for existence. 2025-04-22 11:13:40 +08:00
yeongpin
243c47adb4 Update CHANGELOG for v1.10.05 to clarify the use of GitHub Actions for builds and include a fix for various issues. 2025-04-22 11:04:11 +08:00
yeongpin
b571356fbf Enhance GitHub Actions workflow to include checks for the existence of output files before renaming them in the ARM64 Docker build process. This ensures proper handling of file naming and provides error feedback if expected files are not found. 2025-04-22 11:03:08 +08:00
yeongpin
615c3ea2db Enhance GitHub Actions workflow to check for existing tags before creating new ones. Update tag creation step to only execute if the tag does not already exist. Remove deprecated steps related to Windows SDK installation and signing processes, which are currently disabled. 2025-04-22 10:40:07 +08:00
yeongpin
cffde7066e Refactor GitHub Actions workflow to replace deprecated set-output command with new output syntax. Update Windows SDK installation step and add fallback for SignTool download in case of failure. 2025-04-22 10:30:43 +08:00
yeongpin
73a8b23257 Update version to 1.10.05, remove block_domain.txt from build specification, and enhance GitHub Actions workflow to dynamically determine version from .env file or manual input. Update CHANGELOG for new version release. 2025-04-22 10:27:08 +08:00
Pin Studios
6d182fda55 Update CHANGELOG for v1.10.04 with hotfix for 'main_path' access issue on Windows and macOS, and general bug fixes. Modify issue templates to clarify version placeholder for bug reports. 2025-04-21 14:05:15 +08:00
Pin Studios
c243e9f2f6 Add main path retrieval for non-Linux systems in get_workbench_cursor_path function 2025-04-21 14:02:59 +08:00
yeongpin
caf996864f Update version to 1.10.03 in build workflow configuration. 2025-04-21 11:03:55 +08:00
yeongpin
ce9411dcda Update version to 1.10.03, enhance CHANGELOG with new features including manual registration, email support, and various bug fixes. Add new cursor_auth and cursor_register_manual scripts for improved user registration process. 2025-04-21 11:03:17 +08:00
Pin Studios
1c10750c2b Merge pull request #708 from DaydreamCoding/patch-for-cursor_path
fix use cursor_path
2025-04-21 10:55:48 +08:00
Pin Studios
5aa8dbb614 Merge pull request #709 from DaydreamCoding/patch-macOS-bypass_version-use-config_file
fix macOS 'bypass_version.py' get product_json_path from config_file
2025-04-21 10:55:30 +08:00
QTom
30df4d9ad1 fix macOS 'bypass_version.py' get product_json_path from config_file 2025-04-19 16:58:01 +08:00
QTom
4a533436eb fix use cursor_path 2025-04-19 16:47:06 +08:00
Pin Studios
b271166247 Merge pull request #684 from swechencheng/fix-win-totally-reset-cursor
fix: Port 80aab8741f
2025-04-17 17:54:33 +08:00
Charles Zhang
75825fe3fe fix: Port 80aab8741f
This applies the same fix in 80aab8741f to totally_reset_cursor,
which was missing.
2025-04-17 11:42:55 +02:00
yeongpin
56f9a86e7a Update version to 1.10.02, enhance CHANGELOG with new project guidelines, and implement a function to retrieve user Documents folder path in reset_machine_manual.py and totally_reset_cursor.py. 2025-04-16 11:33:17 +08:00
yeongpin
bdb7fa4ddf Update README.md to clarify tool's purpose and usage, remove outdated features, and add new product image. 2025-04-16 10:41:29 +08:00
yeongpin
51ac969f76 Update version to 1.10.01, add function to retrieve user Documents folder path, and enhance CHANGELOG with new project guidelines and fixes. 2025-04-16 10:34:53 +08:00
Pin Studios
da9d4a3648 Update version to 1.9.06 and remove deprecated cursor authentication and registration scripts 2025-04-16 10:29:12 +08:00
Pin Studios
33a497bf52 Merge pull request #648 from tking007/main
fix: disable UPX in build.spec to resolve GLIBC dependency error
2025-04-16 01:23:19 +08:00
Pin Studios
c392e287fe Merge pull request #652 from BronzonTech-Cloud/main
Update block_domain.txt
2025-04-16 01:19:10 +08:00
Pin Studios
ea4cfaa7ab Merge pull request #651 from canmi21/main
sync(aur): 1.9.05
2025-04-16 01:18:12 +08:00
BronzonTech-Cloud
24c1acc562 Update block_domain.txt to add and reorder disposable email domains for improved filtering 2025-04-15 12:49:17 +00:00
Canmi
43eea92f21 sync: 1.9.05 2025-04-15 20:30:55 +08:00
limu3
631f8be5e4 fix: 禁用 build.spec 中的 UPX,解决 [PYI-714723:ERROR] Failed to load Python shared library ...,修复 Bug #620 2025-04-15 19:03:07 +08:00
yeongpin
c8fa00589e Update button replacement logic in bypass_token_limit.py to link to the yeongpin GitHub page, refining patterns for Windows, Linux, and Mac environments. 2025-04-15 12:05:12 +08:00
yeongpin
a3dff87da9 Enhance GitHub Actions workflow by adding code checkout step, calculating SHA256 checksums for release files, and extracting release notes from CHANGELOG. The release notes now include checksums for better transparency. 2025-04-15 10:41:46 +08:00
yeongpin
861f258ce4 Update CHANGELOG.md to reflect new version 1.9.04, including multiple fixes for package issues, readme formatting, and exception handling. 2025-04-15 10:33:08 +08:00
Pin Studios
ab9202fe48 Merge pull request #631 from xbhel/main
Fix ChromiumOptions.arguments type error: list object has no attribute 'get'
2025-04-15 10:28:41 +08:00
Pin Studios
d60c46bac6 Merge pull request #630 from canmi21/main
update: aur release 1.9.04 & fix(linux): pkg name & fix(chrome): oauth error
2025-04-15 10:27:43 +08:00
Pin Studios
4757f9777a Merge branch 'main' of https://github.com/canmi21/cursor into pr/597 2025-04-15 10:24:08 +08:00
Pin Studios
caecbd4c8d fix(readme): format download links for browsers 2025-04-15 10:24:07 +08:00
Canmi
105b5d4517 fix: github ouath fail error 2025-04-15 00:00:14 +08:00
Canmi
f325690e32 add: more output 2025-04-14 23:48:25 +08:00
Canmi
3de2db74b2 fix: unexpected 2025-04-14 23:32:49 +08:00
Canmi
6153041607 fix: handle 2025-04-14 23:22:56 +08:00
Canmi
6eba95c055 fix: oauth logic, add more debug output 2025-04-14 23:05:10 +08:00
Canmi
21535104a6 fix: google-chrome-stable on archlinx(linux), killall, port, and debug 2025-04-14 23:04:43 +08:00
Canmi
b42b4b01b9 fix: fetch 2025-04-14 22:02:10 +08:00
Canmi
4b9c465dd5 fix: path 2025-04-14 21:49:54 +08:00
Canmi
b98059a476 fix: missspell 2025-04-14 21:41:16 +08:00
xbhel
56882f0663 Fix ChromiumOptions#arguments has no attribute 'get' 2025-04-14 21:16:26 +08:00
Canmi
0852472746 fix: conflict 2025-04-14 21:10:56 +08:00
Canmi
d867f5cfe9 update: SRCINFO 2025-04-14 21:02:50 +08:00
Canmi
e69e500e8d add: LICENSE install 2025-04-14 21:02:34 +08:00
Canmi
2f012b9dc5 fix: missing LICENSE 2025-04-14 21:01:47 +08:00
Canmi
d3c6bf227b sync: aur version 2025-04-14 21:00:58 +08:00
Canmi
f697b71755 Merge branch 'yeongpin:main' into main 2025-04-14 20:59:13 +08:00
yeongpin
5863891f4b Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-14 16:24:53 +08:00
yeongpin
7e0da4a0cb Update version to 1.9.05 and enhance CHANGELOG with new features, fixes, and Python version update in Docker container. 2025-04-14 16:24:50 +08:00
Pin Studios
1cdb543ea9 Merge pull request #613 from haroondilshad/fix/f-string-backslash-errors
fix:  An error occurred: f-string expression part cannot include a b…
2025-04-14 16:24:04 +08:00
Pin Studios
5dad4f35a6 Merge pull request #614 from wang93wei/main
refactor: 优化语言逻辑 + 升级 Linux arm64 Docker Python 版本至 3.10
2025-04-14 16:20:32 +08:00
Pin Studios
34a23a69a1 Update README.md 2025-04-14 15:24:40 +08:00
Pin Studios
3cca2e3b17 Update README.md 2025-04-14 15:01:06 +08:00
alanwang
ee287b91f2 ci: 更新 ARM64 Docker 容器中的 Python 版本至 3.10
将 ARM64 Docker 容器中的 Python 版本从 3.9 更新至 3.10,以使用最新的稳定版本并确保兼容性
2025-04-14 14:31:06 +08:00
Pin Studios
96704e9f38 Update README.md 2025-04-14 14:21:42 +08:00
haroondilshad
f541bc40b4 fix: An error occurred: f-string expression part cannot include a backslash (delete_cursor_google.py, line 243) 2025-04-14 08:20:55 +02:00
alanwang
a730b145a1 refactor: 使用match-case重构语言映射和菜单选择逻辑
将语言映射和菜单选择逻辑从if-elif重构为match-case,提高代码可读性和维护性
2025-04-14 14:18:58 +08:00
yeongpin
1e72e59985 Update version in .env file to 1.9.04 2025-04-14 13:49:02 +08:00
yeongpin
7d355f126c Update CHANGELOG for v1.9.04, adding Opera GX support and fixing various issues. Update config and OAuth handler to include paths for Opera GX. Enhance utils to retrieve default browser paths for Opera GX. 2025-04-14 13:08:32 +08:00
Pin Studios
abcc3a84fa Update README.md 2025-04-14 12:52:45 +08:00
Pin Studios
d8f1cbfc3b Merge pull request #611 from yeongpin/revert-609-main
Revert "Create generator-generic-ossf-slsa3-publish.yml"
2025-04-14 12:43:02 +08:00
Pin Studios
431550e4a9 Revert "Create generator-generic-ossf-slsa3-publish.yml" 2025-04-14 12:42:45 +08:00
Pin Studios
dde25cba02 Merge pull request #609 from Martyb166/main
Create generator-generic-ossf-slsa3-publish.yml
2025-04-14 12:41:06 +08:00
Pin Studios
0466421823 Update README.md 2025-04-14 12:38:52 +08:00
Pin Studios
aa4177d5ec Update README.md 2025-04-14 12:35:12 +08:00
Pin Studios
75b17bb515 Update README.md 2025-04-14 12:31:10 +08:00
Pin Studios
80a76b507a Update README.md 2025-04-14 12:28:00 +08:00
Marcin Tyburski
84b77e8b13 Create generator-generic-ossf-slsa3-publish.yml 2025-04-14 06:20:18 +02:00
Pin Studios
f471450e12 Update README.md
Unable to select next GitHub token from pool
2025-04-14 12:01:18 +08:00
Canmi
9f81f94957 rm: duplicate content 2025-04-14 11:15:31 +08:00
Pin Studios
b423779d04 Merge pull request #602 from canmi21/main
fix(readme): missing linux
2025-04-14 01:00:17 +08:00
Canmi
5203634f0a Merge branch 'yeongpin:main' into main 2025-04-14 00:03:58 +08:00
Canmi
1a73ec0a32 fix(readme): missing linux 2025-04-14 00:03:17 +08:00
Pin Studios
92263013f2 Merge pull request #597 from canmi21/main
update(readme):  add linux & add: aur
2025-04-13 20:57:50 +08:00
Pin Studios
3edb69e831 update(readme): change section title for Archlinux 2025-04-13 20:57:34 +08:00
Pin Studios
271fc818b1 refactor(Translator): enhance language detection and fallback logic
Reorganize language detection methods for Windows and Unix systems, simplifying the fallback mechanism to ensure English is returned when detection fails. Introduce a mapping for language codes based on keyboard layout and system locale, improving maintainability and clarity in the code. This update also removes redundant code and enhances the overall structure of the Translator class.
2025-04-13 20:54:10 +08:00
Pin Studios
61803031dc Merge pull request #551 from paulpham157/feat/locales
[WIP] Support more new languages
2025-04-13 20:50:29 +08:00
Canmi
9a56bc5f7e fix: title 2025-04-13 20:19:54 +08:00
Canmi
32fea2c82b update(readme): add aur 2025-04-13 20:17:10 +08:00
Canmi
45f10a6da8 add: pkg 2025-04-13 20:11:44 +08:00
Canmi
a4692d11dc update(readme): add linux 2025-04-13 19:37:53 +08:00
Pin Studios
43a58db339 Update README.md 2025-04-13 16:57:03 +08:00
Pin Studios
bc55000668 Update README.md 2025-04-13 15:01:39 +08:00
paulpham157
84358805fc feat(locales): add new translation keys and update existing ones
Add new translation keys for various languages including zh_cn, pt, bg, ru, zh_tw, tr, nl, es, fr, and de. These changes include new error messages, prompts, and additional UI text to improve user experience and localization. Also, reorder initialization code in main.py for better readability.
2025-04-13 10:55:40 +07:00
paulpham157
82e2625dfe refactor(Translator): simplify language detection logic
Consolidate language detection logic for Windows and Unix systems by using a default layout mapping and simplifying locale extraction. Fallback to English if detection fails. This improves maintainability and reduces redundancy in the code.
2025-04-13 10:54:26 +07:00
paulpham157
d5404e8f57 fix: main.py:132: DeprecationWarning: 'locale.getdefaultlocale' is deprecated and slated for removal in Python 3.15. Use setlocale(), getencoding() and getlocale() instead. 2025-04-13 10:54:26 +07:00
yeongpin
fb3e532058 Update version to 1.9.03 and enhance CHANGELOG
- Updated the version in the .env file to 1.9.03.
- Added new entries in CHANGELOG.md for version 1.9.03, detailing hotfixes and improvements, including bypassing Cursor JWT expiration issues and fixing automatic logout in the Cursor editor.
- Refactored token extraction logic in cursor registration files to utilize a new method for improved reliability.
2025-04-13 02:11:59 +08:00
yeongpin
a7c4631ea4 Update CHANGELOG.md for version 1.9.02 with new features and fixes
- Added support for more browsers including Opera, Brave, Edge, and Firefox.
- Introduced a manual browser path configuration option.
- Enhanced existing features by fixing browser profile selection and addressing the Cursor JWT expiration issue.
- Retained fixes for configuration file path, Windows user permissions, and other minor issues.
2025-04-12 17:36:56 +08:00
yeongpin
5b64e54e90 Update CHANGELOG.md for version 1.9.02 with new features and fixes
- Added entry for bypassing Cursor JWT expiration issue.
- Fixed redirect issue in Cursor editor that caused automatic logout.
- Retained existing fixes for configuration file path, Windows user permissions, and other issues.
2025-04-12 17:31:14 +08:00
yeongpin
f667da64b3 Add token refresh functionality and improve token extraction
- Introduced a new `get_user_token.py` file to handle token refresh logic using the Chinese server API.
- Updated `config.py` to include new token settings for refresh server URL and enable refresh option.
- Refactored `oauth_auth.py` to utilize the new token extraction method, enhancing error handling and user feedback.
- Added localization strings for token refresh messages in both English and Chinese to improve user experience.
2025-04-12 17:28:11 +08:00
yeongpin
26a8e8da28 Update CHANGELOG.md for version 1.9.02 and enhance config handling
- Added entries for version 1.9.02 in CHANGELOG.md, detailing fixes for configuration file path, Windows user permissions, and other issues.
- Improved `config.py` to handle document path retrieval more robustly, including fallback to a temporary directory if the documents path is not found.
- Updated localization files to include new strings for configuration messages in both English and Chinese.
2025-04-12 17:00:18 +08:00
yeongpin
3862176867 Refactor README.md for improved readability
- Adjusted formatting of browser download instructions to enhance clarity and presentation.
- Separated links for better visual structure in both English and Chinese sections.
2025-04-12 14:37:32 +08:00
yeongpin
5adc598661 Update README.md and configuration files for enhanced browser support
- Revised browser download instructions in README.md to include multiple options: Google Chrome, Opera, Edge, Firefox, and Brave.
- Added new Windows path configurations for browser executables and drivers in the configuration section, improving support for various browsers and their respective drivers.
- Introduced new settings for OAuth configuration, including alert display options and timeout settings.
2025-04-12 14:36:37 +08:00
yeongpin
5e6651bb32 Update CHANGELOG.md for version 1.9.01
- Reorganized entries for clarity, ensuring the new features and fixes are listed under the correct version.
- Added details on enhanced browser support, including Opera, Brave, Edge, and Firefox, along with fixes for browser profile selection and other issues.
2025-04-12 14:35:10 +08:00
yeongpin
bdc606ce2d Update default version in build workflow to 1.9.01 2025-04-12 14:29:54 +08:00
yeongpin
564e421288 Update version to 1.9.01 and enhance CHANGELOG
- Updated the version in the .env file to 1.9.01.
- Added new entries in CHANGELOG.md for version 1.9.01, detailing enhancements such as increased browser support for Opera, Brave, Edge, and Firefox, along with fixes for browser profile selection and other issues.
2025-04-12 14:28:56 +08:00
yeongpin
dce359dc33 Enhance OAuth and browser configuration
- Updated `config.py` to include paths for Opera browser and added new OAuth settings for alert display and timeout configurations.
- Modified `oauth_auth.py` to improve error handling and user data directory retrieval for Opera, along with conditional alert display based on configuration.
- Enhanced `utils.py` to support multiple Opera installation paths for better browser detection.
- Updated localization files to include new strings for user data directory messages.
2025-04-12 14:26:46 +08:00
yeongpin
ff79fae77b Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-12 14:11:24 +08:00
yeongpin
db3a2032dc Enhance browser configuration and path retrieval
- Updated `config.py` to include a comprehensive browser configuration section, allowing for dynamic retrieval of browser paths and drivers for Chrome, Edge, Firefox, and Brave.
- Refactored `new_signup.py`, `new_tempemail.py`, and `oauth_auth.py` to utilize the new browser configuration, improving flexibility and maintainability.
- Removed deprecated `get_default_chrome_path` function and replaced it with a more generalized `get_default_browser_path` function in `utils.py`.
- Updated localization files to include new strings related to browser path validation and selection.
2025-04-12 14:11:22 +08:00
Pin Studios
e2a33d178d Merge pull request #562 from eltociear/patch-1
docs: update CHANGELOG.md
2025-04-12 13:24:15 +08:00
Ikko Eltociear Ashimine
42d97cfa87 docs: update CHANGELOG.md
Faild -> Failed
2025-04-11 18:23:14 +09:00
yeongpin
c42d7d5422 Add option to bypass token limit in menu
- Updated the menu in `main.py` to include a new option for bypassing token limits.
- Adjusted the choice number in the main loop to accommodate the new functionality.
- Integrated the `bypass_token_limit` script to be executed when the new menu option is selected.
2025-04-11 10:54:29 +08:00
yeongpin
c7a84ca59f Update version to 1.8.11 in .env file 2025-04-11 10:53:49 +08:00
yeongpin
4746af7ce9 Add bypass token limit functionality
- Introduced a new script `bypass_token_limit.py` to modify the `workbench.desktop.main.js` file, allowing users to bypass token limits.
- Updated localization files to include new strings for the bypass token limit feature in English, Simplified Chinese, and Traditional Chinese.
- Enhanced CHANGELOG.md to reflect the addition of the bypass token limit feature and related fixes.
2025-04-11 10:53:06 +08:00
yeongpin
9f51ba8128 Update CHANGELOG.md for version 1.8.10, detailing fixes for Linux reset process errors and enhancements to cursor path retrieval for improved maintainability across operating systems. 2025-04-10 00:29:37 +08:00
yeongpin
9aa09c436e Enhance cursor path retrieval in reset_machine_manual.py for Linux systems
- Updated the get_workbench_cursor_path function to handle Linux systems more effectively.
- Added logic to use the first base path if no valid paths are found in the existing loop.
- Improved maintainability and clarity of the code by explicitly handling different operating systems.
2025-04-10 00:28:20 +08:00
yeongpin
1e3e9c99eb Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-09 20:49:43 +08:00
yeongpin
3f9cbc3d08 Update version to 1.8.10, add user authorization check feature, and enhance localization support
- Bumped version in .env file to 1.8.10.
- Introduced a new script for checking user authorization with detailed feedback and error handling.
- Updated CHANGELOG.md to include new entries for the user authorization feature and minor fixes.
- Added localization strings for user authorization checks in English, Simplified Chinese, and Traditional Chinese.
2025-04-09 20:49:39 +08:00
yeongpin
12d46d5f18 Update build.yml to bump version to 1.8.09 and remove changelog extraction step from release process 2025-04-09 16:26:43 +08:00
yeongpin
6cb3ad79af Update CHANGELOG.md for version 1.8.09, adding new entries for bypassing token limits and enhancing multilingual support, along with minor issue fixes. 2025-04-09 16:17:23 +08:00
yeongpin
6470c65f8b Update CHANGELOG.md for version 1.8.09, adding a fix for reset break and enhancing the button replacement logic in reset_machine_manual.py for improved maintainability and clarity. 2025-04-09 16:14:57 +08:00
yeongpin
491b227486 Bump version to 1.8.09 in .env file 2025-04-09 10:30:48 +08:00
Pin Studios
96c0cd5274 Merge pull request #528 from Cantue35/fix/disable-forced-update
Fix forced update config deletion when update check is disabled
2025-04-09 10:29:55 +08:00
yeongpin
60a438e618 Update CHANGELOG.md for version 1.8.09, adding new entries for force update configuration enhancements and multilingual support, along with minor issue fixes. 2025-04-09 10:29:43 +08:00
yeongpin
6a25871366 feat: Enhance force update configuration with new options and multilingual support
- Added 'enabled_force_update' option in the [Utils] section of the configuration.
- Updated the force_update_config() function to utilize the new option for determining update behavior.
- Improved user feedback messages for force update status in English, Simplified Chinese, and Traditional Chinese locale files.
- Ensured consistent messaging for enabling and disabling force updates.
2025-04-09 10:29:28 +08:00
Cantue35
b46a58bd23 Fix forced update config deletion when update check is disabled
- Added a conditional check in force_update_config() to verify that enabled_update_check in the [Utils] section is read as False.
- If disabled, the config file will not be deleted.
- This prevents loss of custom configuration and improves usability.
- Tested on macOS with our modifications.
2025-04-08 20:35:09 +03:00
yeongpin
fea2b88a8e Update CHANGELOG.md to reflect recent changes, including a new entry for bypassing Cursor version checks and documentation for header configuration. Adjusted existing entries for clarity. 2025-04-07 12:43:35 +08:00
yeongpin
63fe39f2c1 fix: Update backup timestamp format in force update configuration
- Changed the variable name from 'time' to 'current_time' for clarity.
- Updated the backup file naming to use the current timestamp correctly, ensuring accurate backup creation.
2025-04-07 10:53:06 +08:00
yeongpin
386ffa4568 chore: Bump version to 1.8.08 in .env file 2025-04-07 10:51:09 +08:00
yeongpin
9c66725caf feat: Introduce force update configuration feature with multilingual support
- Added a new function to force update the configuration file with the latest defaults, including backup and removal of the original file.
- Implemented multilingual support for force update messages in English, Simplified Chinese, and Traditional Chinese locale files.
- Updated CHANGELOG.md to reflect the new feature and fixes, bumping the version to 1.8.08 in the build workflow.
2025-04-07 10:51:01 +08:00
yeongpin
16b6ba95e2 fix: Update RESET emoji in totally_reset_cursor.py for improved clarity
- Changed the RESET emoji from an invalid character to a clear refresh symbol (🔄) for better user understanding.
2025-04-06 17:31:57 +08:00
yeongpin
3424f49a57 fix: Correct indentation and formatting issues in totally_reset_cursor.py
- Fixed indentation in the get_cursor_paths function for better readability.
- Adjusted the else statement in the MachineIDResetter class to maintain consistent formatting.
- Ensured proper newline at the end of the file for compliance with coding standards.
2025-04-06 17:31:10 +08:00
56 changed files with 4639 additions and 2301 deletions

21
.SRCINFO Normal file
View File

@@ -0,0 +1,21 @@
pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
pkgver = 1.9.05
pkgrel = 1
url = https://github.com/yeongpin/cursor-free-vip
arch = x86_64
license = MIT
license = Attribution-NonCommercial-NoDerivatives 4.0 International
makedepends = git
makedepends = python
makedepends = pyinstaller
makedepends = uv
depends = python
depends = cursor-bin
provides = cursor-free-vip
source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git
source = https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE
sha256sums = SKIP
sha256sums = SKIP
pkgname = cursor-free-vip-git

4
.env
View File

@@ -1,2 +1,2 @@
version=1.8.07
VERSION=1.8.07
version=1.11.01
VERSION=1.11.01

View File

@@ -43,7 +43,7 @@ body:
attributes:
label: 版本
description: 您正在运行的 Cursor Free Vip 版本是什么?
placeholder: 例如 v1.0.0
placeholder: 例如 v1.0.0 ( 不是 Cursor AI 版本 )
validations:
required: true

View File

@@ -43,7 +43,7 @@ body:
attributes:
label: Version
description: What version of Cursor Free Vip are you running?
placeholder: For example v1.0.0
placeholder: For example v1.0.0 ( Not Cursor AI Version )
validations:
required: true

View File

@@ -3,10 +3,14 @@ name: Build Executables
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g. 1.0.9)'
use_env_version:
description: 'Use version from .env file (yes/no)'
required: true
default: '1.8.07'
default: 'yes'
version:
description: 'Version number (only used if not using .env version)'
required: false
default: ''
permissions:
contents: write
@@ -14,27 +18,65 @@ permissions:
packages: write
jobs:
determine-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- uses: actions/checkout@v2
- name: Get version from .env file
id: env-version
if: ${{ github.event.inputs.use_env_version == 'yes' }}
run: |
VERSION=$(grep "^version=" .env | cut -d'=' -f2)
echo "ENV_VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using version from .env file: $VERSION"
- name: Use manual version
id: manual-version
if: ${{ github.event.inputs.use_env_version != 'yes' }}
run: |
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "Using manually entered version: ${{ github.event.inputs.version }}"
- name: Set final version
id: set-version
run: |
if [ "${{ github.event.inputs.use_env_version }}" == "yes" ]; then
echo "version=${{ env.ENV_VERSION }}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
fi
create-tag:
needs: determine-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # 获取所有标签
- name: Delete existing tag if exists
- name: Check if tag exists
id: check_tag
run: |
if git ls-remote --tags origin | grep -q "refs/tags/v${{ github.event.inputs.version }}"; then
git push origin --delete "v${{ github.event.inputs.version }}" || true
git tag -d "v${{ github.event.inputs.version }}" || true
if git ls-remote --tags origin | grep -q "refs/tags/v${{ needs.determine-version.outputs.version }}"; then
echo "Tag v${{ needs.determine-version.outputs.version }} already exists, will use existing tag"
echo "tag_exists=true" >> $GITHUB_OUTPUT
else
echo "Tag v${{ needs.determine-version.outputs.version }} does not exist, will create new tag"
echo "tag_exists=false" >> $GITHUB_OUTPUT
fi
- name: Create Tag
- name: Create Tag if not exists
if: steps.check_tag.outputs.tag_exists == 'false'
run: |
git tag "v${{ github.event.inputs.version }}"
git push origin "v${{ github.event.inputs.version }}"
git tag "v${{ needs.determine-version.outputs.version }}"
git push origin "v${{ needs.determine-version.outputs.version }}"
build-windows:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: windows-latest
steps:
@@ -47,7 +89,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -66,7 +108,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
build-macos-arm64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: macos-latest
steps:
@@ -79,7 +121,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -99,7 +141,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux-x64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: ubuntu-22.04
steps:
@@ -112,7 +154,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -136,7 +178,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
build-linux-arm64:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: ubuntu-latest
steps:
@@ -149,11 +191,11 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Build in ARM64 Docker container
run: |
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.10-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
@@ -171,7 +213,7 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
build-macos-intel:
needs: create-tag
needs: [determine-version, create-tag]
runs-on: macos-latest
steps:
@@ -184,7 +226,7 @@ jobs:
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Install dependencies
run: |
@@ -207,31 +249,82 @@ jobs:
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release:
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
needs: [determine-version, build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare release files
- name: Calculate SHA256 checksums
run: |
cd artifacts
echo "Contents of artifacts directory:"
ls -la
echo "Contents of subdirectories:"
ls -la */
mkdir -p checksums
for file in artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe \
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 \
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64 \
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64 \
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
do
if [ -f "$file" ]; then
filename=$(basename $file)
sha256sum "$file" | cut -d ' ' -f 1 > checksums/${filename}.sha256
echo "${filename}: $(cat checksums/${filename}.sha256)" >> checksums/all_checksums.txt
else
echo "Warning: File $file not found"
fi
done
cat checksums/all_checksums.txt
- name: Extract release notes from CHANGELOG
run: |
version_pattern="## v${{ env.VERSION }}"
next_version_pattern="## v"
# Find the start line number of the current version
start_line=$(grep -n "$version_pattern" CHANGELOG.md | head -1 | cut -d: -f1)
if [ -z "$start_line" ]; then
echo "Error: Version ${{ env.VERSION }} not found in CHANGELOG.md"
exit 1
fi
# Find the line number of the next version
next_version_line=$(tail -n +$((start_line + 1)) CHANGELOG.md | grep -n "$next_version_pattern" | head -1 | cut -d: -f1)
if [ -z "$next_version_line" ]; then
# If there's no next version, get to the end of the file
changelog_content=$(tail -n +$start_line CHANGELOG.md)
else
# Extract content between current version and next version
end_line=$((start_line + next_version_line - 1))
changelog_content=$(sed -n "${start_line},${end_line}p" CHANGELOG.md)
fi
# Create release notes file
{
echo "$changelog_content"
echo ""
echo "## SHA256 Checksums"
cat checksums/all_checksums.txt
} > release_notes.md
# Display release notes for debugging
cat release_notes.md
- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ env.VERSION }}
body_path: release_notes.md
files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
@@ -241,4 +334,4 @@ jobs:
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,128 @@
# Change Log
## v1.11.01
0. Must Update to this version to get full experience | 必須更新到此版本以獲取完整體驗
1. Restore: Some Main Code | 恢復一些主程式碼
2. Add: Arabic language | 增加阿拉伯語
3. Add: Language configuration saved setting | 增加語言配置保存設定
4. Add: Restore Machine ID from Backup | 增加從備份恢復機器ID
5. Add: Owned Website Check Version | 增加擁有網站檢查版本
6. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
7. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
8. Fix: Some Issues | 修復一些問題
## v1.10.05
1. Remove block_domain.txt | 移除 block_domain.txt
2. Original Code In Github , If u afraid of virus, please clone the code and run locally | 原始碼在 Github 上,如果怕病毒,請複製原始碼並在本機運行
3. All Action using github workflow , not build myself , so i cant place virus in the file | 所有 Action 使用 github workflow ,不是我自己 build 的,所以我不會在文件中放置病毒
4. Fix: Some Issues | 修復一些問題
## v1.10.04
1. Hotfix: Reset Process Error: cannot access local variable 'main_path' where it is not associated with a value on windows & macos | 修復在 Windows 和 macOS 上無法訪問局部變量 'main_path' 的問題
2. Fix: Some Issues | 修復一些問題
## v1.10.03
1. Add: Manual Registration | 增加手動註冊
2. Only support your own Email | 只支持自己的Email 請勿使用Temp Email 註冊 註冊假賬號.
3. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
4. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
5. Fix: Some Issues | 修復一些問題
## v1.10.02
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
6. Use & Cherish | 切用且珍惜
7. Same as v1.10.01 | 與 v1.10.01 相同
8. Fix: reset machine ID no module name 'new_signup' | 修復機器 ID 重置 no module name 'new_signup'
9. Fix: Some Issues | 修復一些問題
## v1.10.01
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
6. Use & Cherish | 切用且珍惜
7. Fix: Some Issues | 修復一些問題
## v1.9.05
1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。
2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。
3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤
4. Sync AUR new version 1.9.04 | 同步 AUR 新版本 1.9.04
5. Fix: missing license install on pkgbuild @michaeldavis246611119 mention here | 修復 pkgbuild 中缺少授權安裝 @michaeldavis246611119 提到這裡
6. Fix: readme table | 修復 readme 表格
7. Fix: google-chrome package name problem, add "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 | 修復 google-chrome 包名稱問題,添加 "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616
8. Fix: exception error log | 修復異常錯誤日誌
9. Fix: github oauth error [Bug]: #564 | 修復 github oauth 錯誤 [Bug]: #564
10. Fix: ChromiumOptions.arguments type error: list object has no attribute 'get' | 修復 ChromiumOptions.arguments 類型錯誤list 對象沒有屬性 'get'
11. Fix: Some Issues | 修復一些問題
## v1.9.04
1. Add: Opera GX Support | 添加 Opera GX 支持
2. Same as v1.9.03 | 與 v1.9.03 相同
3. Hotfix: Some Issues | 修復一些問題
4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
6. Fix: Some Issues | 修復一些問題
## v1.9.03[Skip & Merge to v1.9.04]
1. Hotfix: Some Issues | 修復一些問題
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
4. Fix: Some Issues | 修復一些問題
## v1.9.02
1. Add: Bypass Token Limit | 添加繞過 Token 限制
2. Add: More Browser Support | 添加更多瀏覽器支持
3. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
4. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
5. Add config manual browser path | 添加配置手動選擇遊覽器路徑
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
6. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
7. Fix: Config File Path | 修復配置文件路徑
8. Fix: window user permission | 修復 window 用戶權限
9. Fix: Some Issues | 修復一些問題
## v1.9.01
1. Add: Bypass Token Limit | 添加繞過 Token 限制
2. Add: More Browser Support | 添加更多瀏覽器支持
3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
4. Add config manual browser path | 添加配置手動選擇遊覽器路徑
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
6. Fix: Some Issues | 修復一些問題
## v1.8.10
1. Add: Check User Authorized | 添加檢查用戶授權
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統
4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑
5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性
6. Fix: Some Issues | 修復一些問題
## v1.8.09
1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查
2. AddBypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5)
3. Add: Force Update Config | 添加強制更新配置
4. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
5. Fix: Reset break | 修復重置中斷
4. Fix: Some Issues | 修復一些問題
## v1.8.08
1. Add: Force Update Config | 添加強制更新配置
2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
3. Fix: Google Auth & Github Auth JWT Problem | 修復 Google Auth & Github Auth JWT 問題
4. Fix: Totally reset import & import * raw options problem | 修復 totally reset import & import * raw 選項問題
5. Fix: reset.file_not_found problem | 修復 reset.file_not_found 問題
6. Outdated: Bypass Cursor Version Check | 過期:繞過 Cursor 版本檢查
7. Document: i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx"); | 文檔i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx");
8. Fix: Some Issues | 修復一些問題
## v1.8.07
1. Add: Bypass Cursor Version Check | 添加繞過 Cursor 版本檢查
2. Add: Multilanguage support for bypass | 添加繞過的多語言支持
@@ -245,7 +368,7 @@ These changes make the application more user-friendly by only requesting admin p
1. Fix: Cursor Auth | 修復 Cursor Auth
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
3. Fix: Cursor Auth Error | 修復 Cursor Auth 錯誤
4. Fix: Update Curl Faild | 修復更新 Curl 失敗
4. Fix: Update Curl Failed | 修復更新 Curl 失敗
## v1.5.03
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題

View File

@@ -1,5 +0,0 @@
// Import the contents of your other files
importScripts('default_filters.js', 'block.js');
// Your service worker initialization code can go here
console.log('Service worker initialized');

View File

@@ -1,129 +0,0 @@
/**
* All the actual functionality of the extension; loads as part of the background page.
*
* Active ingredient is enable(), which sets up the webRequest callbacks.
*
* */
let blockingEnabled = false;
let allFilters = null;
let webRTCPrivacy = null;
function setFilters(newFilters) {
allFilters = newFilters;
chrome.storage.local.set({"filters": newFilters});
if (blockingEnabled) {
refreshFilters();
}
}
// Convert URL patterns to declarativeNetRequest rule format
function createRules(filters) {
return filters.map((filter, index) => ({
id: index + 1,
priority: 1,
action: {
type: "block"
},
condition: {
urlFilter: filter.replace("*://", "*"),
resourceTypes: [
"main_frame", "sub_frame", "stylesheet", "script", "image",
"font", "object", "xmlhttprequest", "ping", "media", "websocket"
]
}
}));
}
async function enable(icon = true) {
if (blockingEnabled) return;
if (allFilters && allFilters.length > 0) {
const rules = createRules(allFilters);
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: rules.map(rule => rule.id),
addRules: rules
});
}
blockingEnabled = true;
if (icon) {
chrome.action.setIcon({
path: {
"16": "enabled16.png",
"48": "enabled48.png"
}
});
}
}
async function disable(icon = true) {
if (!blockingEnabled) return;
const rules = await chrome.declarativeNetRequest.getDynamicRules();
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: rules.map(rule => rule.id),
addRules: []
});
blockingEnabled = false;
if (icon) {
chrome.action.setIcon({
path: {
"16": "disabled.png",
"32": "disabled.png"
}
});
}
}
async function refreshFilters() {
await disable(false);
await enable(true);
}
async function toggleEnabled() {
if (blockingEnabled) {
await disable();
} else {
await enable();
}
}
function setWebRTCPrivacy(flag, store = true) {
webRTCPrivacy = flag;
const privacySetting = flag ? "default_public_interface_only" : "default";
chrome.privacy.network.webRTCIPHandlingPolicy.set({value: privacySetting});
if (store) {
chrome.storage.local.set({"webrtc_privacy": flag});
}
}
// Initialization
chrome.storage.local.get("filters",
function(result) {
if (result["filters"] == undefined) {
console.log("Initializing filters to defaults.");
setFilters(defaultFilters);
} else {
setFilters(result["filters"]);
allFilters = result["filters"];
}
// toggle blocking on-off via the extension icon
chrome.action.onClicked.addListener(toggleEnabled);
// initialize blocking
enable();
}
);
chrome.storage.local.get("webrtc_privacy",
function(result) {
if (result["webrtc_privacy"] == undefined) {
console.log("Initializing WebRTC privacy to default.");
setWebRTCPrivacy(false, true);
} else {
setWebRTCPrivacy(result["webrtc_privacy"], false);
}
}
);

View File

@@ -1,14 +0,0 @@
defaultFilters = [
// personally, I can't stand the like box
//"http://www.facebook.com/plugins/likebox.php?*",
"*://*.doubleclick.net/*",
"*://partner.googleadservices.com/*",
"*://*.googlesyndication.com/*",
"*://*.google-analytics.com/*",
"*://creative.ak.fbcdn.net/*",
"*://*.adbrite.com/*",
"*://*.exponential.com/*",
"*://*.quantserve.com/*",
"*://*.scorecardresearch.com/*",
"*://*.zedo.com/*",
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,29 +0,0 @@
{
"name": "PBlock",
"version": "0.1",
"manifest_version": 3,
"author": "yeongpin",
"url": "https://github.com/yeongpin/PBlock",
"description": "Just a simple blocker.",
"action": {
"default_icon": "enabled48.png"
},
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "enabled16.png",
"48": "enabled48.png"
},
"permissions": [
"declarativeNetRequest",
"storage",
"privacy"
],
"host_permissions": [
"http://*/*",
"https://*/*",
"ws://*/*",
"wss://*/*"
]
}

View File

@@ -1,5 +0,0 @@
{
"id": "ruleset_1",
"version": "1.0",
"rules": []
}

34
PKGBUILD Normal file
View File

@@ -0,0 +1,34 @@
# Maintainer: Canmi21 <9997200@qq.com>
# Contributor: Canmi (Canmi21)
pkgname=cursor-free-vip-git
pkgver=1.9.05
pkgrel=1
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64')
url="https://github.com/yeongpin/cursor-free-vip"
license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
depends=('python' 'cursor-bin')
makedepends=('git' 'python' 'pyinstaller' 'uv')
provides=('cursor-free-vip')
source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git" "https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE")
sha256sums=('SKIP' 'SKIP')
pkgver() {
cd "$srcdir/cursor-free-vip"
git describe --tags --always | sed 's/^v//;s/-/./g'
}
build() {
cd "$srcdir/cursor-free-vip"
uv venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt
pyinstaller --clean --noconfirm --onefile main.py --name cursor-free-vip
}
package() {
install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license"
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
}

View File

@@ -7,33 +7,42 @@
<p align="center">
[![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)
[![Release](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip)](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=52c41a1)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![Stars](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/stars/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![Downloads](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/downloads/yeongpin/cursor-free-vip/total)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
<a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a>
</p>
<a href="https://trendshift.io/repositories/13425" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13425" alt="yeongpin%2Fcursor-free-vip | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<a href="https://www.buymeacoffee.com/yeongpin" target="_blank">
<img src="https://img.buymeacoffee.com/button-api/?text=buy me a coffee&emoji=☕&slug=yeongpin&button_colour=ffda33&font_colour=000000&font_family=Bree&outline_colour=000000&coffee_colour=FFDD00&latest=2" width="160" height='55' alt="Buy Me a Coffee"/>
</a>
<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.
This tool is for educational purposes, currently the repo does not violate any laws. Please support the original project.
This tool will not generate any fake email accounts and OAuth access.
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.
這是一款用於學習和研究的工具,目前 repo 沒有違反任何法律。請支持原作者。
這款工具不會生成任何假的電子郵件帳戶和 OAuth 訪問。
支持 Windows、macOS 和 Linux。
對於最佳性能,請以管理員身份運行並始終保持最新。
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
<p align="center">
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
<img src="./images/product_2025-04-16_10-40-21.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/)
##### 如果沒有 Google Chrome可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
</div>
## 🔄 Change Log | 更新日志
@@ -42,42 +51,40 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
## ✨ Features | 功能特點
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
* Complete Auth verification<br>完成 Auth 驗證<br>
* Support Windows macOS and Linux systems<br>支持 Windows、macOS 和 Linux 系統<br>
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
* Delete Cursor Google Account<br>删除 Cursor Google 账号<br>
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
## 💻 System Support | 系統支持
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
| Linux | x64 | ✅ | Linux | x86 | ✅ |
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
| Operating System | Architecture | Supported |
|------------------|-------------------|-----------|
| Windows | x64, x86 | ✅ |
| macOS | Intel, Apple Silicon | ✅ |
| Linux | x64, x86, ARM64 | ✅ |
## 👀 How to use | 如何使用
<details open>
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
**Linux/macOS**
### **Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
```
**Windows**
### **Archlinux**
Install via [AUR](https://aur.archlinux.org/packages/cursor-free-vip-git)
```bash
yay -S cursor-free-vip-git
```
### **Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
@@ -88,13 +95,13 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/inst
<details>
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
**Linux/macOS**
### **Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
```
**Windows**
### **Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
@@ -166,6 +173,33 @@ max_timeout = 160
check_update = True
# Show Account Info | 顯示賬號信息
show_account_info = True
[WindowsPaths]
storage_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\storage.json
sqlite_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\state.vscdb
machine_id_path = C:\Users\yeongpin\AppData\Roaming\Cursor\machineId
cursor_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app
updater_path = C:\Users\yeongpin\AppData\Local\cursor-updater
update_yml_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app-update.yml
product_json_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app\product.json
[Browser]
default_browser = opera
chrome_path = C:\Program Files\Google\Chrome\Application\chrome.exe
edge_path = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
firefox_path = C:\Program Files\Mozilla Firefox\firefox.exe
brave_path = C:\Program Files\BraveSoftware/Brave-Browser/Application/brave.exe
chrome_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
edge_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\msedgedriver.exe
firefox_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\geckodriver.exe
brave_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
opera_path = C:\Users\yeongpin\AppData\Local\Programs\Opera\opera.exe
opera_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
[OAuth]
show_selection_alert = False
timeout = 120
max_attempts = 3
```
</details>

View File

@@ -1,83 +1,134 @@
oakon.com
famamail.com
2925.com
indigobook.com
teihu.com
raleigh-construction.com
pastryofistanbul.com
linshiyouxiang.net
Mohmal.com
pusmail.com
questtechsystems.com
ikomail.com
ofanda.com
pusmail.com
ikomail.com
mailpull.com
drewzen.com
begemail.com
dugmail.com
solerbe.net
corhash.net
mailshou.com
0-mail.com
10minemail.com
1secmail.com
20minutemail.com
2925.com
2prong.com
33mail.com
abusemail.de
afrobacon.com
anonbox.net
anonymbox.com
antichef.com
bareed.ws
begemail.com
boun.cr
brefmail.com
burnermail.io
byom.de
chammy.info
cloud-mail.top
cocovpn.com
cool.fr.nf
corhash.net
crazymailing.com
cuvox.de
dayrep.com
deadaddress.com
discard.email
dispostable.com
drewzen.com
dudmail.com
dugmail.com
emailondeck.com
emailtemporario.com.br
ephemail.net
fakeinbox.com
fakeinbox.org
fakemailgenerator.com
famamail.com
fastmailbox.net
filzmail.com
fizmail.com
getairmail.com
getnada.com
givmail.com
guerrillamail.com
gustr.com
harakirimail.com
hottempmail.com
ikomail.com
inboxbear.com
inboxkitten.com
incognitomail.org
indigobook.com
jetable.org
kaspop.com
letthemeatspam.com
linshiyouxiang.net
luxusmail.org
mail-temp.com
mail1a.de
mailbucket.org
mailcatch.com
maildrop.cc
mailexpire.com
mailhazard.com
mailimate.com
mailin8r.com
mailinator.com
mailme.lv
mailnesia.com
mailnull.com
mailpull.com
mailsac.com
mailshou.com
mailtemp.net
mailzilla.org
meltmail.com
mintemail.com
moakt.com
mohmal.com
my10minutemail.com
mycleaninbox.net
mytrashmail.com
no-spam.ws
nomail.pw
nospamfor.us
notmailinator.com
nowmymail.com
oakon.com
objectmail.com
ofanda.com
openmailbox.org
owlpic.com
pastryofistanbul.com
privacyroot.com
pusmail.com
questtechsystems.com
raleigh-construction.com
rcpt.at
safemail.link
sendspamhere.com
sharklasers.com
shortmail.net
solerbe.net
spam4.me
spamavert.com
spambog.com
spamdecoy.net
spamex.com
spamfree24.org
spamgourmet.com
spamhereplease.com
spaml.com
spamslicer.com
spamsphere.com
spamtroll.net
teihu.com
temp-mail.org
tempmail.net
tempmailaddress.com
temporaryemail.net
throwawayemail.com
tmail.ws
trash-mail.com
trash2009.com
trashdevil.com
trashmail.com
trashmail.de
trbvn.com
wegwerfadresse.org
yepmail.net
yopmail.com
zippymail.info
zippymail.info

View File

@@ -23,29 +23,13 @@ a = Analysis(
pathex=[],
binaries=[],
datas=[
('turnstilePatch', 'turnstilePatch'),
('PBlock', 'PBlock'),
('locales', 'locales'),
('cursor_auth.py', '.'),
('reset_machine_manual.py', '.'),
('cursor_register.py', '.'),
('new_signup.py', '.'),
('new_tempemail.py', '.'),
('quit_cursor.py', '.'),
('cursor_register_manual.py', '.'),
('oauth_auth.py', '.'),
('utils.py', '.'),
('.env', '.'),
('block_domain.txt', '.')
('.env', '.')
],
hiddenimports=[
'cursor_auth',
'reset_machine_manual',
'new_signup',
'new_tempemail',
'quit_cursor',
'cursor_register_manual',
'oauth_auth',
'utils'
],
hookspath=[],
@@ -69,7 +53,7 @@ exe = EXE(
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=True,

194
bypass_token_limit.py Normal file
View File

@@ -0,0 +1,194 @@
import os
import shutil
import platform
import tempfile
import glob
from colorama import Fore, Style, init
import configparser
import sys
from config import get_config
from datetime import datetime
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "🔄",
"WARNING": "⚠️",
}
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
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
"base": "/Applications/Cursor.app/Contents/Resources/app",
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Windows": {
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
},
"Linux": {
"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}")
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
if system == "Windows":
base_path = config.get('WindowsPaths', 'cursor_path')
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
return main_path
def modify_workbench_js(file_path: str, translator=None) -> bool:
"""
Modify file content
"""
try:
# Save original file permissions
original_stat = os.stat(file_path)
original_mode = original_stat.st_mode
original_uid = original_stat.st_uid
original_gid = original_stat.st_gid
# Create temporary file
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
# Read original content
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
content = main_file.read()
patterns = {
# 通用按钮替换模式
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
# Windows/Linux
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
# Mac 通用按钮替换模式
r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)': 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)',
# Badge 替换
r'<div>Pro Trial': r'<div>Pro',
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
#
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
# Pro
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
# Toast 替换
r'notifications-toasts': r'notifications-toasts hidden'
}
# 使用patterns进行替换
for old_pattern, new_pattern in patterns.items():
content = content.replace(old_pattern, new_pattern)
# Write to temporary file
tmp_file.write(content)
tmp_path = tmp_file.name
# Backup original file with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{file_path}.backup.{timestamp}"
shutil.copy2(file_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
# Move temporary file to original position
if os.path.exists(file_path):
os.remove(file_path)
shutil.move(tmp_path, file_path)
# Restore original permissions
os.chmod(file_path, original_mode)
if os.name != "nt": # Not Windows
os.chown(file_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
try:
os.unlink(tmp_path)
except:
pass
return False
def run(translator=None):
config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('bypass_token_limit.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
modify_workbench_js(get_workbench_cursor_path(translator), translator)
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('bypass_token_limit.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)

View File

@@ -50,6 +50,8 @@ def get_product_json_path(translator=None):
elif system == "Darwin": # macOS
product_json_path = "/Applications/Cursor.app/Contents/Resources/app/product.json"
if config.has_section('MacPaths') and config.has_option('MacPaths', 'product_json_path'):
product_json_path = config.get('MacPaths', 'product_json_path')
elif system == "Linux":
# Try multiple common paths

214
check_user_authorized.py Normal file
View File

@@ -0,0 +1,214 @@
import os
import requests
import time
import hashlib
import base64
import struct
from colorama import Fore, Style, init
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"WARNING": "⚠️",
"KEY": "🔑",
"CHECK": "🔍"
}
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
"""Generate a SHA-256 hash of input + salt and return as hex"""
hash_obj = hashlib.sha256()
hash_obj.update((input_str + salt).encode('utf-8'))
return hash_obj.hexdigest()
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
"""Obfuscate bytes using the algorithm from utils.js"""
t = 165
for r in range(len(byte_array)):
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
t = byte_array[r]
return byte_array
def generate_cursor_checksum(token: str, translator=None) -> str:
"""Generate Cursor checksum from token using the algorithm"""
try:
# Clean the token
clean_token = token.strip()
# Generate machineId and macMachineId
machine_id = generate_hashed64_hex(clean_token, 'machineId')
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
# Get timestamp and convert to byte array
timestamp = int(time.time() * 1000) // 1000000
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
# Obfuscate bytes and encode as base64
obfuscated_bytes = obfuscate_bytes(byte_array)
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
# Combine final checksum
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
return ""
def check_user_authorized(token: str, translator=None) -> bool:
"""
Check if the user is authorized with the given token
Args:
token (str): The authorization token
translator: Optional translator for internationalization
Returns:
bool: True if authorized, False otherwise
"""
try:
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
# Clean the token
if token and '%3A%3A' in token:
token = token.split('%3A%3A')[1]
elif token and '::' in token:
token = token.split('::')[1]
# Remove any whitespace
token = token.strip()
if not token or len(token) < 10: # Add a basic validation for token length
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
# Try to get usage info using the DashboardService API
try:
# Generate checksum
checksum = generate_cursor_checksum(token, translator)
# Create request headers
headers = {
'accept-encoding': 'gzip',
'authorization': f'Bearer {token}',
'connect-protocol-version': '1',
'content-type': 'application/proto',
'user-agent': 'connect-es/1.6.1',
'x-cursor-checksum': checksum,
'x-cursor-client-version': '0.48.7',
'x-cursor-timezone': 'Asia/Shanghai',
'x-ghost-mode': 'false',
'Host': 'api2.cursor.sh'
}
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
# Make the request - this endpoint doesn't need a request body
usage_response = requests.post(
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
headers=headers,
data=b'', # Empty body
timeout=10
)
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
if usage_response.status_code == 200:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
return True
elif usage_response.status_code == 401 or usage_response.status_code == 403:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
return False
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid
if token.startswith('eyJ') and '.' in token and len(token) > 100:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
if token.startswith('eyJ') and '.' in token and len(token) > 100:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except requests.exceptions.Timeout:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
return False
except requests.exceptions.ConnectionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
return False
def run(translator=None):
"""Run function to be called from main.py"""
try:
# Ask user if they want to get token from database or input manually
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
token = None
# If user chooses database or default
if not choice or choice == 'd':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
try:
# Import functions from cursor_acc_info.py
from cursor_acc_info import get_token
# Get token using the get_token function
token = get_token()
if token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
except ImportError:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
# If token not found in database or user chooses manual input
if not token:
# Try to get token from environment
token = os.environ.get('CURSOR_TOKEN')
# If not in environment, ask user to input
if not token:
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
# Check authorization
is_authorized = check_user_authorized(token, translator)
if is_authorized:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
return is_authorized
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
return False
def main(translator=None):
"""Main function to check user authorization"""
return run(translator)
if __name__ == "__main__":
main()

125
config.py
View File

@@ -2,7 +2,9 @@ import os
import sys
import configparser
from colorama import Fore, Style
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
from utils import get_user_documents_path, get_linux_cursor_path, get_default_driver_path, get_default_browser_path
import shutil
import datetime
EMOJI = {
"INFO": "",
@@ -16,19 +18,59 @@ EMOJI = {
"SETTINGS": "⚙️"
}
# global config cache
_config_cache = None
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
os.makedirs(config_dir, exist_ok=True)
# get documents path
docs_path = get_user_documents_path()
if not docs_path or not os.path.exists(docs_path):
# if documents path not found, use current directory
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.documents_path_not_found', fallback='Documents path not found, using current directory') if translator else 'Documents path not found, using current directory'}{Style.RESET_ALL}")
docs_path = os.path.abspath('.')
# normalize path
config_dir = os.path.normpath(os.path.join(docs_path, ".cursor-free-vip"))
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
# create config directory, only print message when directory not exists
dir_exists = os.path.exists(config_dir)
try:
os.makedirs(config_dir, exist_ok=True)
if not dir_exists: # only print message when directory not exists
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_dir_created', path=config_dir) if translator else f'Config directory created: {config_dir}'}{Style.RESET_ALL}")
except Exception as e:
# if cannot create directory, use temporary directory
import tempfile
temp_dir = os.path.normpath(os.path.join(tempfile.gettempdir(), ".cursor-free-vip"))
temp_exists = os.path.exists(temp_dir)
config_dir = temp_dir
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
os.makedirs(config_dir, exist_ok=True)
if not temp_exists: # only print message when temporary directory not exists
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.using_temp_dir', path=config_dir, error=str(e)) if translator else f'Using temporary directory due to error: {config_dir} (Error: {str(e)})'}{Style.RESET_ALL}")
# create config object
config = configparser.ConfigParser()
# Default configuration
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
'Browser': {
'default_browser': 'chrome',
'chrome_path': get_default_browser_path('chrome'),
'chrome_driver_path': get_default_driver_path('chrome'),
'edge_path': get_default_browser_path('edge'),
'edge_driver_path': get_default_driver_path('edge'),
'firefox_path': get_default_browser_path('firefox'),
'firefox_driver_path': get_default_driver_path('firefox'),
'brave_path': get_default_browser_path('brave'),
'brave_driver_path': get_default_driver_path('brave'),
'opera_path': get_default_browser_path('opera'),
'opera_driver_path': get_default_driver_path('opera'),
'operagx_path': get_default_browser_path('operagx'),
'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
},
'Turnstile': {
'handle_turnstile_time': '2',
@@ -52,7 +94,23 @@ def setup_config(translator=None):
},
'Utils': {
'enabled_update_check': 'True',
'enabled_force_update': 'False',
'enabled_account_info': 'True'
},
'OAuth': {
'show_selection_alert': False, # 默认不显示选择提示弹窗
'timeout': 120,
'max_attempts': 3
},
'Token': {
'refresh_server': 'https://token.cursorpro.com.cn',
'enable_refresh': True
},
'Language': {
'current_language': '', # Set by local system detection if empty
'fallback_language': 'en',
'auto_update_languages': 'True',
'language_cache_dir': os.path.join(config_dir, "language_cache")
}
}
@@ -257,6 +315,59 @@ def print_config(config, translator=None):
print()
def force_update_config(translator=None):
"""
Force update configuration file with latest defaults if update check is enabled.
Args:
translator: Translator instance
Returns:
ConfigParser instance or None if failed
"""
try:
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
current_time = datetime.datetime.now()
# If the config file exists, check if forced update is enabled
if os.path.exists(config_file):
# First, read the existing configuration
existing_config = configparser.ConfigParser()
existing_config.read(config_file, encoding='utf-8')
# Check if "enabled_update_check" is True
update_enabled = True # Default to True if not set
if existing_config.has_section('Utils') and existing_config.has_option('Utils', 'enabled_force_update'):
update_enabled = existing_config.get('Utils', 'enabled_force_update').strip().lower() in ('true', 'yes', '1', 'on')
if update_enabled:
try:
# Create a backup
backup_file = f"{config_file}.bak.{current_time.strftime('%Y%m%d_%H%M%S')}"
shutil.copy2(config_file, backup_file)
if translator:
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.backup_created', path=backup_file) if translator else f'Backup created: {backup_file}'}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_enabled') if translator else 'Config file force update enabled'}{Style.RESET_ALL}")
# Delete the original config file (forced update)
os.remove(config_file)
if translator:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_removed') if translator else 'Config file removed for forced update'}{Style.RESET_ALL}")
except Exception as e:
if translator:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.backup_failed', error=str(e)) if translator else f'Failed to backup config: {str(e)}'}{Style.RESET_ALL}")
else:
if translator:
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_disabled', fallback='Config file force update disabled by configuration. Keeping existing config file.') if translator else 'Config file force update disabled by configuration. Keeping existing config file.'}{Style.RESET_ALL}")
# Generate a new (or updated) configuration if needed
return setup_config(translator)
except Exception as e:
if translator:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.force_update_failed', error=str(e)) if translator else f'Force update config failed: {str(e)}'}{Style.RESET_ALL}")
return None
def get_config(translator=None):
"""Get existing config or create new one"""
return setup_config(translator)
global _config_cache
if _config_cache is None:
_config_cache = setup_config(translator)
return _config_cache

View File

@@ -156,4 +156,4 @@ class CursorAuth:
finally:
if conn:
conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

View File

@@ -1,263 +0,0 @@
import os
from colorama import Fore, Style, init
import time
import random
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': ''
}
class CursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.controller = None
self.mail_url = "https://yopmail.com/zh/email-generator"
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.settings_url = "https://www.cursor.com/settings"
self.email_address = None
self.signup_tab = None
self.email_tab = None
# Account information
self.password = self._generate_password()
# Generate first name and last name separately
first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12):
"""Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def setup_email(self):
"""Setup Email"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
# Create a temporary email using new_tempemail, passing translator
from new_tempemail import NewTempEmail
self.temp_email = NewTempEmail(self.translator) # Pass translator
# Create a temporary email
email_address = self.temp_email.create_email()
if not email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
return False
# Save email address
self.email_address = email_address
self.email_tab = self.temp_email # Pass NewTempEmail instance
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
return False
def register_cursor(self):
"""注册 Cursor"""
browser_tab = None
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# Directly use new_signup.py to sign up
from new_signup import main as new_signup_main
# Execute the new registration process, passing translator
result, browser_tab = new_signup_main(
email=self.email_address,
password=self.password,
first_name=self.first_name,
last_name=self.last_name,
email_tab=self.email_tab,
controller=self.controller,
translator=self.translator
)
if result:
# Use the returned browser instance to get account information
self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info()
# Close browser after getting information
if browser_tab:
try:
browser_tab.quit()
except:
pass
return success
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False
finally:
# Ensure browser is closed in any case
if browser_tab:
try:
browser_tab.quit()
except:
pass
def _get_account_info(self):
"""Get Account Information and Token"""
try:
self.signup_tab.get(self.settings_url)
time.sleep(2)
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.signup_tab.ele(usage_selector)
total_usage = "未知"
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.total_usage', usage=total_usage)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30
retry_interval = 2
attempts = 0
while attempts < max_attempts:
try:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
return False
def _save_account_info(self, token, total_usage):
"""Save Account Information to File"""
try:
# Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # Pass translator when creating instance
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
return False
def start(self):
"""Start Registration Process"""
try:
if self.setup_email():
if self.register_cursor():
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
return True
return False
finally:
# Close email tab
if hasattr(self, 'temp_email'):
try:
self.temp_email.close()
except:
pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""Update Cursor Auth Info"""
auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token)
def main(translator=None):
"""Main function to be called from main.py"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
registration = CursorRegistration(translator)
registration.start()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
main(main_translator)

View File

@@ -1,5 +0,0 @@
from oauth_auth import main as oauth_main
def main(translator=None):
"""Handle GitHub OAuth registration"""
oauth_main('github', translator)

View File

@@ -1,5 +0,0 @@
from oauth_auth import main as oauth_main
def main(translator=None):
"""Handle Google OAuth registration"""
oauth_main('google', translator)

View File

@@ -4,6 +4,7 @@ import time
import random
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
from get_user_token import get_token_from_cookie
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
@@ -78,7 +79,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}")
return True
except Exception as e:
@@ -175,7 +176,7 @@ class CursorRegistration:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
token = get_token_from_cookie(cookie["value"], self.translator)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True

View File

@@ -247,11 +247,11 @@ class CursorGoogleAccountDeleter(OAuthHandler):
except:
# Try direct JavaScript input as fallback
try:
self.browser.run_js(f"""
self.browser.run_js(r"""
arguments[0].value = "Delete";
const event = new Event('input', {{ bubbles: true }});
const event = new Event('input', { bubbles: true });
arguments[0].dispatchEvent(event);
const changeEvent = new Event('change', {{ bubbles: true }});
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}")

112
get_user_token.py Normal file
View File

@@ -0,0 +1,112 @@
import requests
import json
import time
from colorama import Fore, Style
import os
from config import get_config
# Define emoji constants
EMOJI = {
'START': '🚀',
'OAUTH': '🔑',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
def refresh_token(token, translator=None):
"""Refresh the token using the Chinese server API
Args:
token (str): The full WorkosCursorSessionToken cookie value
translator: Optional translator object
Returns:
str: The refreshed access token or original token if refresh fails
"""
try:
config = get_config(translator)
# Get refresh_server URL from config or use default
refresh_server = config.get('Token', 'refresh_server', fallback='https://token.cursorpro.com.cn')
# Ensure the token is URL encoded properly
if '%3A%3A' not in token and '::' in token:
# Replace :: with URL encoded version if needed
token = token.replace('::', '%3A%3A')
# Make the request to the refresh server
url = f"{refresh_server}/reftoken?token={token}"
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('token.refreshing') if translator else 'Refreshing token...'}{Style.RESET_ALL}")
response = requests.get(url, timeout=30)
if response.status_code == 200:
try:
data = response.json()
if data.get('code') == 0 and data.get('msg') == "获取成功":
access_token = data.get('data', {}).get('accessToken')
days_left = data.get('data', {}).get('days_left', 0)
expire_time = data.get('data', {}).get('expire_time', 'Unknown')
if access_token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('token.refresh_success', days=days_left, expire=expire_time) if translator else f'Token refreshed successfully! Valid for {days_left} days (expires: {expire_time})'}{Style.RESET_ALL}")
return access_token
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('token.no_access_token') if translator else 'No access token in response'}{Style.RESET_ALL}")
else:
error_msg = data.get('msg', 'Unknown error')
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.refresh_failed', error=error_msg) if translator else f'Token refresh failed: {error_msg}'}{Style.RESET_ALL}")
except json.JSONDecodeError:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.invalid_response') if translator else 'Invalid JSON response from refresh server'}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.server_error', status=response.status_code) if translator else f'Refresh server error: HTTP {response.status_code}'}{Style.RESET_ALL}")
except requests.exceptions.Timeout:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.request_timeout') if translator else 'Request to refresh server timed out'}{Style.RESET_ALL}")
except requests.exceptions.ConnectionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.connection_error') if translator else 'Connection error to refresh server'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.unexpected_error', error=str(e)) if translator else f'Unexpected error during token refresh: {str(e)}'}{Style.RESET_ALL}")
# Return original token if refresh fails
return token.split('%3A%3A')[-1] if '%3A%3A' in token else token.split('::')[-1] if '::' in token else token
def get_token_from_cookie(cookie_value, translator=None):
"""Extract and process token from cookie value
Args:
cookie_value (str): The WorkosCursorSessionToken cookie value
translator: Optional translator object
Returns:
str: The processed token
"""
try:
# Try to refresh the token with the API first
refreshed_token = refresh_token(cookie_value, translator)
# If refresh succeeded and returned a different token, use it
if refreshed_token and refreshed_token != cookie_value:
return refreshed_token
# If refresh failed or returned same token, use traditional extraction method
if '%3A%3A' in cookie_value:
return cookie_value.split('%3A%3A')[-1]
elif '::' in cookie_value:
return cookie_value.split('::')[-1]
else:
return cookie_value
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.extraction_error', error=str(e)) if translator else f'Error extracting token: {str(e)}'}{Style.RESET_ALL}")
# Fall back to original behavior
if '%3A%3A' in cookie_value:
return cookie_value.split('%3A%3A')[-1]
elif '::' in cookie_value:
return cookie_value.split('::')[-1]
else:
return cookie_value

View File

@@ -1,701 +0,0 @@
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: 170 KiB

769
locales/ar.json Normal file
View File

@@ -0,0 +1,769 @@
{
"menu": {
"title": "الخيارات المتاحة",
"exit": "خروج من البرنامج",
"reset": "إعادة تعيين معرف الجهاز",
"register": "تسجيل حساب Cursor جديد",
"register_google": "تسجيل باستخدام حساب جوجل",
"register_github": "تسجيل باستخدام حساب GitHub",
"register_manual": "تسجيل Cursor باستخدام بريد إلكتروني مخصص",
"quit": "إغلاق تطبيق Cursor",
"select_language": "تغيير اللغة",
"select_chrome_profile": "اختيار ملف تعريف Chrome",
"input_choice": "الرجاء إدخال اختيارك ({choices})",
"invalid_choice": "اختيار غير صالح. الرجاء إدخال رقم من {choices}",
"program_terminated": "تم إنهاء البرنامج بواسطة المستخدم",
"error_occurred": "حدث خطأ: {error}. يرجى المحاولة مرة أخرى",
"press_enter": "اضغط Enter للخروج",
"disable_auto_update": "تعطيل التحديث التلقائي لـ Cursor",
"lifetime_access_enabled": "تم تفعيل الوصول مدى الحياة",
"totally_reset": "إعادة تعيين Cursor بالكامل",
"outdate": "قديم",
"temp_github_register": "تسجيل GitHub مؤقت",
"admin_required": "يجب تشغيل البرنامج كمسؤول عند التنفيذ.",
"admin_required_continue": "المتابعة بدون صلاحيات المسؤول.",
"coming_soon": "قريباً",
"fixed_soon": "سيتم إصلاحه قريباً",
"contribute": "المساهمة في المشروع",
"config": "عرض الإعدادات",
"delete_google_account": "حذف حساب Cursor المرتبط بجوجل",
"continue_prompt": "المتابعة؟ (y/N): ",
"operation_cancelled_by_user": "تم إلغاء العملية بواسطة المستخدم",
"exiting": "جاري الخروج ……",
"bypass_version_check": "تجاوز فحص إصدار Cursor",
"check_user_authorized": "فحص صلاحية المستخدم",
"bypass_token_limit": "تجاوز حد الرمز المميز (Token)"
},
"languages": {
"en": "الإنجليزية",
"zh_cn": "الصينية المبسطة",
"zh_tw": "الصينية التقليدية",
"vi": "الفيتنامية",
"nl": "الهولندية",
"de": "الألمانية",
"fr": "الفرنسية",
"pt": "البرتغالية",
"ru": "الروسية",
"tr": "التركية",
"bg": "البلغارية",
"es": "الإسبانية",
"ar": "العربية"
},
"quit_cursor": {
"start": "بدء إغلاق Cursor",
"no_process": "لا توجد عمليات Cursor قيد التشغيل",
"terminating": "إنهاء العملية {pid}",
"waiting": "في انتظار إنهاء العملية",
"success": "تم إغلاق جميع عمليات Cursor",
"timeout": "انتهت مهلة العملية: {pids}",
"error": "حدث خطأ: {error}"
},
"reset": {
"title": "أداة إعادة تعيين معرف جهاز Cursor",
"checking": "جارٍ فحص ملف الإعدادات",
"not_found": "لم يتم العثور على ملف الإعدادات",
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
"reading": "جارٍ قراءة الإعدادات الحالية",
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
"generating": "جارٍ إنشاء معرف جهاز جديد",
"saving_json": "جارٍ حفظ الإعدادات الجديدة في JSON",
"success": "تم إعادة تعيين معرف الجهاز بنجاح",
"new_id": "معرف الجهاز الجديد",
"permission_error": "خطأ في الصلاحيات: {error}",
"run_as_admin": "يرجى محاولة تشغيل هذا البرنامج كمسؤول",
"process_error": "خطأ في عملية الإعادة: {error}",
"updating_sqlite": "جارٍ تحديث قاعدة بيانات SQLite",
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
"sqlite_success": "تم تحديث قاعدة بيانات SQLite بنجاح",
"sqlite_error": "فشل تحديث قاعدة بيانات SQLite: {error}",
"press_enter": "اضغط Enter للخروج",
"unsupported_os": "نظام تشغيل غير مدعوم: {os}",
"linux_path_not_found": "لم يتم العثور على مسار Linux",
"updating_system_ids": "جارٍ تحديث معرفات النظام",
"system_ids_updated": "تم تحديث معرفات النظام بنجاح",
"system_ids_update_failed": "فشل تحديث معرفات النظام: {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": "إصدار Cursor الحالي: {version}",
"patch_completed": "تم تصحيح getMachineId بنجاح",
"patch_failed": "فشل تصحيح getMachineId: {error}",
"version_check_passed": "تم اجتياز فحص إصدار Cursor",
"file_modified": "تم تعديل الملف",
"version_less_than_0_45": "إصدار Cursor < 0.45.0، تخطي تصحيح getMachineId",
"detecting_version": "جارٍ اكتشاف إصدار Cursor",
"patching_getmachineid": "جارٍ تصحيح getMachineId",
"version_greater_than_0_45": "إصدار Cursor >= 0.45.0، جارٍ تصحيح getMachineId",
"permission_denied": "تم رفض الصلاحية: {error}",
"backup_created": "تم إنشاء نسخة احتياطية",
"update_success": "تم التحديث بنجاح",
"update_failed": "فشل التحديث: {error}",
"windows_machine_guid_updated": "تم تحديث Windows Machine GUID بنجاح",
"reading_package_json": "جارٍ قراءة package.json {path}",
"invalid_json_object": "كائن JSON غير صالح",
"no_version_field": "لم يتم العثور على حقل الإصدار في package.json",
"version_field_empty": "حقل الإصدار فارغ",
"invalid_version_format": "تنسيق إصدار غير صالح: {version}",
"found_version": "تم العثور على الإصدار: {version}",
"version_parse_error": "خطأ في تحليل الإصدار: {error}",
"package_not_found": "لم يتم العثور على package.json: {path}",
"check_version_failed": "فشل فحص الإصدار: {error}",
"stack_trace": "تتبع المكدس",
"version_too_low": "إصدار Cursor منخفض جداً: {version} < 0.45.0",
"no_write_permission": "لا توجد صلاحيات كتابة: {path}",
"path_not_found": "لم يتم العثور على المسار: {path}",
"modify_file_failed": "فشل تعديل الملف: {error}",
"windows_machine_id_updated": "تم تحديث Windows Machine ID بنجاح",
"update_windows_machine_id_failed": "فشل تحديث Windows Machine ID: {error}",
"update_windows_machine_guid_failed": "فشل تحديث Windows Machine GUID: {error}",
"file_not_found": "لم يتم العثور على الملف: {path}"
},
"register": {
"title": "أداة تسجيل Cursor",
"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": "جارٍ زيارة الرابط",
"basic_info": "تم إرسال المعلومات الأساسية",
"handle_turnstile": "معالجة Turnstile",
"no_turnstile": "لم يتم اكتشاف Turnstile",
"turnstile_passed": "تم اجتياز Turnstile",
"verification_start": "بدء الحصول على رمز التحقق",
"verification_timeout": "انتهت مهلة الحصول على رمز التحقق",
"verification_not_found": "لم يتم العثور على رمز تحقق",
"try_get_code": "محاولة | {attempt} الحصول على رمز التحقق | الوقت المتبقي: {time}ثانية",
"get_account": "جارٍ الحصول على معلومات الحساب",
"get_token": "الحصول على رمز جلسة Cursor",
"token_success": "تم الحصول على الرمز بنجاح",
"token_attempt": "محاولة | {attempt} مرات للحصول على الرمز | سيتم إعادة المحاولة بعد {time}ثانية",
"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}ثانية",
"verification_code_filled": "تم تعبئة رمز التحقق",
"login_success_and_jump_to_settings_page": "تم تسجيل الدخول بنجاح والانتقال إلى صفحة الإعدادات",
"detect_login_page": "تم اكتشاف صفحة تسجيل الدخول، بدء التسجيل...",
"cursor_registration_completed": "تم تسجيل Cursor بنجاح!",
"set_password": "تعيين كلمة المرور",
"basic_info_submitted": "تم إرسال المعلومات الأساسية",
"cursor_auth_info_updated": "تم تحديث معلومات مصادقة Cursor",
"cursor_auth_info_update_failed": "فشل تحديث معلومات مصادقة Cursor",
"reset_machine_id": "إعادة تعيين معرف الجهاز",
"account_info_saved": "تم حفظ معلومات الحساب",
"save_account_info_failed": "فشل حفظ معلومات الحساب",
"get_email_address": "الحصول على عنوان البريد الإلكتروني",
"update_cursor_auth_info": "تحديث معلومات مصادقة Cursor",
"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": "تم الوصول إلى الحد الأقصى للمحاولات. فشل التسجيل.",
"browser_path_invalid": "مسار {browser} غير صالح، جارٍ استخدام المسار الافتراضي",
"using_browser": "جارٍ استخدام متصفح {browser}: {path}",
"using_browser_profile": "جارٍ استخدام ملف تعريف {browser} من: {user_data_dir}",
"make_sure_browser_is_properly_installed": "تأكد من تثبيت {browser} بشكل صحيح",
"try_install_browser": "حاول تثبيت المتصفح باستخدام مدير الحزم الخاص بك",
"tracking_processes": "جارٍ تتبع {count} عمليات {browser}",
"no_new_processes_detected": "لم يتم اكتشاف عمليات {browser} جديدة للتتبع",
"could_not_track_processes": "تعذر تتبع عمليات {browser}: {error}"
},
"auth": {
"title": "مدير مصادقة Cursor",
"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": "إعادة تعيين معرف الجهاز",
"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": "الحصول على رمز جلسة Cursor",
"get_cursor_session_token_success": "تم الحصول على رمز جلسة Cursor بنجاح",
"get_cursor_session_token_failed": "فشل الحصول على رمز جلسة Cursor",
"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}ثانية",
"blocked_domains_loaded_timeout_error": "خطأ في مهلة تحميل النطاقات المحظورة: {error}",
"available_domains_loaded": "تم تحميل النطاقات المتاحة: {count}",
"domains_filtered": "تم تصفية النطاقات: {count}",
"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",
"disable_success": "تم تعطيل التحديث التلقائي بنجاح",
"disable_failed": "فشل تعطيل التحديث التلقائي: {error}",
"press_enter": "اضغط Enter للخروج",
"start_disable": "بدء تعطيل التحديث التلقائي",
"killing_processes": "جارٍ إنهاء العمليات",
"processes_killed": "تم إنهاء العمليات",
"removing_directory": "جارٍ إزالة الدليل",
"directory_removed": "تمت إزالة الدليل",
"creating_block_file": "جارٍ إنشاء ملف حظر",
"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": "جارٍ التحقق من التحديثات...",
"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": "سجل التغييرات",
"rate_limit_exceeded": "تم تجاوز حد معدل GitHub API. تخطي فحص التحديث."
},
"totally_reset": {
"title": "إعادة تعيين Cursor بالكامل",
"checking_config": "جارٍ فحص ملف الإعدادات",
"config_not_found": "لم يتم العثور على ملف الإعدادات",
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
"reading_config": "جارٍ قراءة الإعدادات الحالية",
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
"generating_new_machine_id": "جارٍ إنشاء معرف جهاز جديد",
"saving_new_config": "جارٍ حفظ الإعدادات الجديدة في JSON",
"success": "تم إعادة تعيين Cursor بنجاح",
"error": "فشل إعادة تعيين Cursor: {error}",
"press_enter": "اضغط Enter للخروج",
"reset_machine_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": "إزالة كاملة لإعدادات وتكوينات Cursor AI",
"feature_2": "مسح جميع البيانات المخزنة مؤقتاً بما في ذلك سجل الذكاء الاصطناعي",
"feature_3": "إعادة تعيين معرف الجهاز لتجاوز كشف الفترة التجريبية",
"feature_4": "إنشاء معرفات أجهزة جديدة عشوائية",
"feature_5": "إزالة الامتدادات المخصصة والتفضيلات",
"feature_6": "إعادة تعيين معلومات الفترة التجريبية وبيانات التفعيل",
"feature_7": "فحص عميق لملفات الرخصة والملفات المتعلقة بالفترة التجريبية المخفية",
"feature_8": "الحفاظ على الملفات والتطبيقات غير المتعلقة بـ Cursor بأمان",
"feature_9": "متوافق مع Windows وmacOS وLinux",
"disclaimer_title": "تنبيه",
"disclaimer_1": "ستقوم هذه الأداة بحذف جميع إعدادات Cursor AI،",
"disclaimer_2": "والتكوينات والبيانات المخزنة مؤقتاً بشكل دائم. لا يمكن التراجع عن هذا الإجراء.",
"disclaimer_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
"disclaimer_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
"disclaimer_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
"disclaimer_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
"disclaimer_7": "استخدمها على مسؤوليتك الخاصة",
"confirm_title": "هل أنت متأكد أنك تريد المتابعة؟",
"confirm_1": "سيؤدي هذا الإجراء إلى حذف جميع إعدادات Cursor AI،",
"confirm_2": "والتكوينات والبيانات المخزنة مؤقتاً. لا يمكن التراجع عن هذا الإجراء.",
"confirm_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
"confirm_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
"confirm_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
"confirm_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
"confirm_7": "استخدمها على مسؤوليتك الخاصة",
"invalid_choice": "الرجاء إدخال 'Y' أو 'n'",
"skipped_for_safety": "تم تخطيه لأسباب أمنية (غير متعلق بـ Cursor): {path}",
"deleted": "تم الحذف: {path}",
"error_deleting": "خطأ في حذف {path}: {error}",
"not_found": "لم يتم العثور على الملف: {path}",
"resetting_machine_id": "جارٍ إعادة تعيين معرفات الجهاز لتجاوز كشف الفترة التجريبية...",
"created_machine_id": "تم إنشاء معرف جهاز جديد: {path}",
"error_creating_machine_id": "خطأ في إنشاء ملف معرف الجهاز {path}: {error}",
"error_searching": "خطأ في البحث عن الملفات في {path}: {error}",
"created_extended_trial_info": "تم إنشاء معلومات الفترة التجريبية الممتدة: {path}",
"error_creating_trial_info": "خطأ في إنشاء ملف معلومات الفترة التجريبية {path}: {error}",
"resetting_cursor_ai_editor": "جارٍ إعادة تعيين محرر Cursor AI... يرجى الانتظار.",
"reset_cancelled": "تم إلغاء الإعادة. الخروج دون إجراء أي تغييرات.",
"windows_machine_id_modification_skipped": "تم تخطي تعديل معرف جهاز Windows: {error}",
"linux_machine_id_modification_skipped": "تم تخطي تعديل machine-id في Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل تشغيل البرنامج كمسؤول",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل صلاحيات sudo",
"windows_registry_instructions": "📝 ملاحظة: للإعادة الكاملة على Windows، قد تحتاج أيضاً إلى تنظيف إدخالات التسجيل.",
"windows_registry_instructions_2": " قم بتشغيل 'regedit' وابحث عن المفاتيح التي تحتوي على 'Cursor' أو 'CursorAI' تحت HKEY_CURRENT_USER\\Software\\ واحذفها.\n",
"reset_log_1": "تمت إعادة تعيين Cursor AI بالكامل وتجاوز كشف الفترة التجريبية!",
"reset_log_2": "يرجى إعادة تشغيل النظام لتفعيل التغييرات.",
"reset_log_3": "ستحتاج إلى إعادة تثبيت Cursor AI ويجب أن تحصل الآن على فترة تجريبية جديدة.",
"reset_log_4": "للحصول على أفضل النتائج، ضع في الاعتبار أيضاً:",
"reset_log_5": "استخدم عنوان بريد إلكتروني مختلف عند التسجيل للحصول على فترة تجريبية جديدة",
"reset_log_6": "استخدم شبكة VPN لتغيير عنوان IP الخاص بك إذا كان ذلك متاحًا",
"reset_log_7": "قم بمسح ملفات تعريف الارتباط وذاكرة التخزين المؤقت للمتصفح قبل زيارة موقع Cursor AI",
"reset_log_8": "إذا استمرت المشكلات، حاول تثبيت Cursor AI في موقع مختلف",
"reset_log_9": "إذا واجهتك أي مشكلات، انتقل إلى متتبع المشكلات على Github وقم بإنشاء مشكلة جديدة على https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "حدث خطأ غير متوقع: {error}",
"report_issue": "يرجى الإبلاغ عن هذه المشكلة على متتبع المشكلات في Github على 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",
"no_additional_license_trial_files_found_in_deep_scan": "لم يتم العثور على ملفات ترخيص/تجريبية إضافية في الفحص العميق",
"removing_electron_localstorage_files": "جارٍ إزالة ملفات التخزين المحلي لـ Electron",
"electron_localstorage_files_removed": "تمت إزالة ملفات التخزين المحلي لـ Electron",
"electron_localstorage_files_removal_error": "خطأ في إزالة ملفات التخزين المحلي لـ Electron: {error}",
"removing_electron_localstorage_files_completed": "اكتملت إزالة ملفات التخزين المحلي لـ Electron",
"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": "فشل إعادة تعيين معرّف الجهاز",
"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": "تم إلغاء العملية. جارٍ الخروج دون إجراء أي تغييرات.",
"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": "تم النقر على زر حذف الحساب",
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
"delete_input_not_found": "لم يتم العثور على حقل تأكيد الحذف بعد عدة محاولات",
"delete_input_retry": "لم يتم العثور على حقل الحذف، المحاولة {attempt}/{max_attempts}",
"delete_input_error": "خطأ في العثور على حقل الحذف: {error}",
"delete_input_not_found_continuing": "لم يتم العثور على حقل تأكيد الحذف، جارٍ محاولة المتابعة على أي حال"
},
"github_register": {
"title": "أتمتة تسجيل GitHub و Cursor AI",
"features_header": "الميزات",
"feature1": "ينشئ بريدًا إلكترونيًا مؤقتًا باستخدام 1secmail.",
"feature2": "يسجل حساب GitHub جديد ببيانات اعتماد عشوائية.",
"feature3": "يتحقق من بريد GitHub تلقائيًا.",
"feature4": "يسجل الدخول إلى Cursor AI باستخدام مصادقة GitHub.",
"feature5": "يعيد تعيين معرف الجهاز لتجاوز اكتشاف الفترة التجريبية.",
"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": "المتبقي من الفترة التجريبية Pro",
"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": "فشل في الحصول على الرمز",
"failed_to_get_account_info": "فشل في الحصول على معلومات الحساب",
"title": "معلومات الحساب",
"email": "البريد الإلكتروني",
"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": "تم تمكين الوصول مدى الحياة",
"token_not_found": "لم يتم العثور على الرمز"
},
"config": {
"config_not_available": "الإعدادات غير متاحة",
"configuration": "الإعدادات",
"enabled": "ممكّن",
"disabled": "معطّل",
"config_directory": "دليل الإعدادات",
"neither_cursor_nor_cursor_directory_found": "لم يتم العثور على Cursor أو دليل Cursor في {config_base}",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "يرجى التأكد من تثبيت Cursor وتشغيله مرة واحدة على الأقل",
"storage_directory_not_found": "لم يتم العثور على دليل التخزين: {storage_dir}",
"storage_file_found": "تم العثور على ملف التخزين: {storage_path}",
"file_size": "حجم الملف: {size} بايت",
"file_permissions": "صلاحيات الملف: {permissions}",
"file_owner": "مالك الملف: {owner}",
"file_group": "مجموعة الملف: {group}",
"error_getting_file_stats": "خطأ في الحصول على إحصائيات الملف: {error}",
"permission_denied": "تم رفض الإذن: {storage_path}",
"try_running": "حاول تشغيل: {command}",
"and": "و",
"storage_file_is_empty": "ملف التخزين فارغ: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "قد يكون الملف تالفًا، يرجى إعادة تثبيت Cursor",
"storage_file_not_found": "لم يتم العثور على ملف التخزين: {storage_path}",
"error_checking_linux_paths": "خطأ في فحص مسارات Linux: {error}",
"config_option_added": "تمت إضافة خيار الإعدادات: {option}",
"config_updated": "تم تحديث الإعدادات",
"config_created": "تم إنشاء الإعدادات: {config_file}",
"config_setup_error": "خطأ في إعداد الإعدادات: {error}",
"storage_file_is_valid_and_contains_data": "ملف التخزين صالح ويحتوي على بيانات",
"error_reading_storage_file": "خطأ في قراءة ملف التخزين: {error}",
"also_checked": "تم فحص {path} أيضًا",
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
"config_removed": "تمت إزالة ملف الإعدادات للتحديث القسري",
"backup_failed": "فشل نسخ الإعدادات احتياطيًا: {error}",
"force_update_failed": "فشل تحديث الإعدادات القسري: {error}",
"config_force_update_disabled": "تم تعطيل التحديث القسري لملف الإعدادات، جارٍ تخطي التحديث القسري",
"config_force_update_enabled": "تم تمكين التحديث القسري لملف الإعدادات، جارٍ إجراء التحديث القسري",
"documents_path_not_found": "لم يتم العثور على مسار المستندات، جارٍ استخدام الدليل الحالي",
"config_dir_created": "تم إنشاء دليل الإعدادات: {path}",
"using_temp_dir": "جارٍ استخدام دليل مؤقت بسبب خطأ: {path} (الخطأ: {error})"
},
"oauth": {
"authentication_button_not_found": "لم يتم العثور على زر المصادقة",
"authentication_failed": "فشلت المصادقة: {error}",
"found_cookies": "تم العثور على {count} من ملفات تعريف الارتباط",
"token_extraction_error": "خطأ في استخراج الرمز: {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": "التشغيل كمستخدم جذر غير مستحسن لأتمتة المتصفح",
"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}",
"user_data_dir_not_found": "لم يتم العثور على دليل بيانات مستخدم {browser} في {path}، سيتم تجربة Chrome بدلاً من ذلك",
"error_getting_user_data_directory": "خطأ في الحصول على دليل بيانات المستخدم: {error}",
"warning_browser_close": "تحذير: سيؤدي هذا إلى إغلاق جميع عمليات {browser} قيد التشغيل",
"killing_browser_processes": "جارٍ إنهاء عمليات {browser}...",
"profile_selection_error": "خطأ أثناء اختيار الملف الشخصي: {error}",
"using_configured_browser_path": "جارٍ استخدام مسار {browser} المُكوّن: {path}",
"browser_not_found_trying_chrome": "تعذر العثور على {browser}، جارٍ تجربة Chrome بدلاً من ذلك",
"found_chrome_at": "تم العثور على Chrome في: {path}",
"found_browser_user_data_dir": "تم العثور على دليل بيانات مستخدم {browser}: {path}"
},
"browser_profile": {
"title": "اختيار ملف تعريف المتصفح",
"select_profile": "اختر ملف تعريف {browser} للاستخدام:",
"profile_list": "ملفات تعريف {browser} المتاحة:",
"default_profile": "ملف التعريف الافتراضي",
"profile": "ملف التعريف {number}",
"no_profiles": "لم يتم العثور على ملفات تعريف {browser}",
"error_loading": "خطأ في تحميل ملفات تعريف {browser}: {error}",
"profile_selected": "ملف التعريف المحدد: {profile}",
"invalid_selection": "اختيار غير صالح. يرجى المحاولة مرة أخرى."
},
"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": "تم النقر على زر حذف الحساب",
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
"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}",
"confirm_prompt": "هل أنت متأكد أنك تريد المتابعة؟ (y/N): "
},
"bypass": {
"starting": "جارٍ بدء تجاوز إصدار Cursor...",
"found_product_json": "تم العثور على product.json: {path}",
"no_write_permission": "لا توجد صلاحية كتابة للملف: {path}",
"read_failed": "فشلت قراءة product.json: {error}",
"current_version": "الإصدار الحالي: {version}",
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
"version_updated": "تم تحديث الإصدار من {old} إلى {new}",
"write_failed": "فشلت كتابة product.json: {error}",
"no_update_needed": "لا يلزم التحديث. الإصدار الحالي {version} بالفعل >= 0.46.0",
"bypass_failed": "فشل تجاوز الإصدار: {error}",
"stack_trace": "تتبع المكدس",
"localappdata_not_found": "لم يتم العثور على متغير بيئة LOCALAPPDATA",
"product_json_not_found": "لم يتم العثور على product.json في مسارات Linux الشائعة",
"unsupported_os": "نظام تشغيل غير مدعوم: {system}",
"file_not_found": "لم يتم العثور على الملف: {path}",
"title": "أداة تجاوز إصدار Cursor",
"description": "تقوم هذه الأداة بتعديل ملف product.json الخاص بـ Cursor لتجاوز قيود الإصدار",
"menu_option": "تجاوز فحص إصدار Cursor"
},
"auth_check": {
"checking_authorization": "جارٍ التحقق من التصريح...",
"token_source": "الحصول على الرمز من قاعدة البيانات أو إدخاله يدوياً؟ (d/m، الافتراضي: d)",
"getting_token_from_db": "جارٍ الحصول على الرمز من قاعدة البيانات...",
"token_found_in_db": "تم العثور على الرمز في قاعدة البيانات",
"token_not_found_in_db": "لم يتم العثور على الرمز في قاعدة البيانات",
"cursor_acc_info_not_found": "لم يتم العثور على cursor_acc_info.py",
"error_getting_token_from_db": "خطأ في الحصول على الرمز من قاعدة البيانات: {error}",
"enter_token": "أدخل رمز Cursor الخاص بك: ",
"token_length": "طول الرمز: {length} حرفاً",
"usage_response_status": "حالة استجابة الاستخدام: {response}",
"unexpected_status_code": "رمز حالة غير متوقع: {code}",
"jwt_token_warning": "يبدو أن الرمز بتنسيق JWT، لكن فحص API أعاد رمز حالة غير متوقع. قد يكون الرمز صالحاً ولكن الوصول إلى API مقيد.",
"invalid_token": "رمز غير صالح",
"user_authorized": "المستخدم مصرح له",
"user_unauthorized": "المستخدم غير مصرح له",
"request_timeout": "انتهت مهلة الطلب",
"connection_error": "خطأ في الاتصال",
"check_error": "خطأ في التحقق من التصريح: {error}",
"authorization_successful": "تم التصريح بنجاح!",
"authorization_failed": "فشل التصريح!",
"operation_cancelled": "تم إلغاء العملية بواسطة المستخدم",
"unexpected_error": "خطأ غير متوقع: {error}",
"error_generating_checksum": "خطأ في إنشاء المجموع الاختباري: {error}",
"checking_usage_information": "جارٍ التحقق من معلومات الاستخدام...",
"check_usage_response": "استجابة فحص الاستخدام: {response}",
"usage_response": "استجابة الاستخدام: {response}"
},
"bypass_token_limit": {
"title": "أداة تجاوز حد الرمز",
"description": "تقوم هذه الأداة بتعديل ملف workbench.desktop.main.js لتجاوز حد الرمز",
"press_enter": "اضغط Enter للمتابعة..."
},
"token": {
"refreshing": "جارٍ تحديث الرمز...",
"refresh_success": "تم تحديث الرمز بنجاح! صالح لمدة {days} يوماً (تاريخ انتهاء الصلاحية: {expire})",
"no_access_token": "لا يوجد رمز وصول في الاستجابة",
"refresh_failed": "فشل تحديث الرمز: {error}",
"invalid_response": "استجابة JSON غير صالحة من خادم التحديث",
"server_error": "خطأ في خادم التحديث: HTTP {status}",
"request_timeout": "انتهت مهلة طلب خادم التحديث",
"connection_error": "خطأ في الاتصال بخادم التحديث",
"unexpected_error": "خطأ غير متوقع أثناء تحديث الرمز: {error}",
"extraction_error": "خطأ في استخراج الرمز: {error}"
}
}

View File

@@ -20,9 +20,22 @@
"totally_reset": "Нулирайте изцяло Курсор",
"outdate": "Изтекъл срок",
"temp_github_register": "Временно регистриране с GitHub",
"coming_soon": "Очаквайте скоро"
"coming_soon": "Очаквайте скоро",
"fixed_soon": "Ще бъде поправено скоро",
"contribute": "Принос към проекта",
"config": "Покажи конфигурацията",
"delete_google_account": "Изтрий Google акаунта на Cursor",
"continue_prompt": "Продължи? (y/N): ",
"operation_cancelled_by_user": "Операцията е отменена от потребителя",
"exiting": "Излизане ......",
"bypass_version_check": "Пропусни проверката на версията на Cursor",
"check_user_authorized": "Провери оторизацията на потребителя",
"select_chrome_profile": "Избери Chrome профил",
"bypass_token_limit": "Заобикаляне на ограничението на токените",
"restore_machine_id": "Възстановяване на машинен идентификатор от резервно копие"
},
"languages": {
"ar": "Арабски",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -400,5 +413,48 @@
"profile_selected": "Избран профил: {profile}",
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
},
"restore": {
"title": "Възстановяване на машинен идентификатор от резервно копие",
"starting": "Стартиране на процеса на възстановяване на машинния идентификатор",
"no_backups_found": "Не са намерени резервни копия",
"available_backups": "Налични резервни копия",
"select_backup": "Изберете резервно копие за възстановяване",
"to_cancel": "за отказ",
"operation_cancelled": "Операцията е отменена",
"invalid_selection": "Невалиден избор",
"please_enter_number": "Моля, въведете валиден номер",
"missing_id": "Липсващ идентификатор: {id}",
"read_backup_failed": "Неуспешно четене на резервното копие: {error}",
"current_file_not_found": "Текущият файл за съхранение не е намерен",
"current_backup_created": "Създадено е резервно копие на текущия файл за съхранение",
"storage_updated": "Файлът за съхранение е успешно актуализиран",
"update_failed": "Неуспешно актуализиране на файла за съхранение: {error}",
"sqlite_not_found": "SQLite базата данни не е намерена",
"updating_sqlite": "Актуализиране на SQLite базата данни",
"updating_pair": "Актуализиране на двойката ключ-стойност",
"sqlite_updated": "SQLite базата данни е успешно актуализирана",
"sqlite_update_failed": "Неуспешно актуализиране на SQLite базата данни: {error}",
"machine_id_backup_created": "Създадено е резервно копие на файла с машинния идентификатор",
"backup_creation_failed": "Неуспешно създаване на резервно копие: {error}",
"machine_id_updated": "Файлът с машинния идентификатор е успешно актуализиран",
"machine_id_update_failed": "Неуспешно актуализиране на файла с машинния идентификатор: {error}",
"updating_system_ids": "Актуализиране на системните идентификатори",
"system_ids_update_failed": "Неуспешно актуализиране на системните идентификатори: {error}",
"permission_denied": "Достъпът е отказан. Опитайте да стартирате като администратор",
"windows_machine_guid_updated": "Windows машинният GUID е успешно актуализиран",
"update_windows_machine_guid_failed": "Неуспешно актуализиране на Windows машинния GUID: {error}",
"windows_machine_id_updated": "Windows машинният идентификатор е успешно актуализиран",
"update_windows_machine_id_failed": "Неуспешно актуализиране на Windows машинния идентификатор: {error}",
"sqm_client_key_not_found": "Регистърният ключ SQMClient не е намерен",
"update_windows_system_ids_failed": "Неуспешно актуализиране на Windows системните идентификатори: {error}",
"macos_platform_uuid_updated": "macOS платформеният UUID е успешно актуализиран",
"failed_to_execute_plutil_command": "Неуспешно изпълнение на plutil командата",
"update_macos_system_ids_failed": "Неуспешно актуализиране на macOS системните идентификатори: {error}",
"ids_to_restore": "Машинни идентификатори за възстановяване",
"confirm": "Сигурни ли сте, че искате да възстановите тези идентификатори?",
"success": "Машинният идентификатор е успешно възстановен",
"process_error": "Грешка при процеса на възстановяване: {error}",
"press_enter": "Натиснете Enter, за да продължите"
}
}

View File

@@ -22,9 +22,21 @@
"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"
"coming_soon": "Bald verfügbar",
"fixed_soon": "Bald Behoben",
"contribute": "Zum Projekt Beitragen",
"config": "Konfiguration Anzeigen",
"delete_google_account": "Cursor Google-Konto Löschen",
"continue_prompt": "Fortfahren? (y/N): ",
"operation_cancelled_by_user": "Vorgang vom Benutzer abgebrochen",
"exiting": "Wird beendet ……",
"bypass_version_check": "Cursor Versionsprüfung Überspringen",
"check_user_authorized": "Benutzerautorisierung Prüfen",
"bypass_token_limit": "Token-Limit umgehen",
"restore_machine_id": "Geräte-ID aus Backup wiederherstellen"
},
"languages": {
"ar": "Arabisch",
"en": "Englisch",
"zh_cn": "Vereinfachtes Chinesisch",
"zh_tw": "Traditionelles Chinesisch",
@@ -34,7 +46,9 @@
"fr": "Französisch",
"pt": "Portugiesisch",
"ru": "Russisch",
"es": "Spanisch"
"es": "Spanisch",
"tr": "Türkisch",
"bg": "Bulgarisch"
},
"quit_cursor": {
"start": "Beginne Cursor zu Beenden",
@@ -101,7 +115,14 @@
"package_not_found": "Package.json Nicht Gefunden: {path}",
"check_version_failed": "Versionsüberprüfung Fehlgeschlagen: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0"
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0",
"no_write_permission": "Keine Schreibberechtigung: {path}",
"path_not_found": "Pfad Nicht Gefunden: {path}",
"modify_file_failed": "Datei Ändern Fehlgeschlagen: {error}",
"windows_machine_id_updated": "Windows Maschinen-ID Erfolgreich Aktualisiert",
"update_windows_machine_id_failed": "Windows Maschinen-ID Aktualisierung Fehlgeschlagen: {error}",
"update_windows_machine_guid_failed": "Windows Maschinen-GUID Aktualisierung Fehlgeschlagen: {error}",
"file_not_found": "Datei Nicht Gefunden: {path}"
},
"register": {
"title": "Cursor Registrierungstool",
@@ -389,5 +410,48 @@
"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"
},
"restore": {
"title": "Geräte-ID aus Backup wiederherstellen",
"starting": "Starte Wiederherstellungsprozess für Geräte-ID",
"no_backups_found": "Keine Backup-Dateien gefunden",
"available_backups": "Verfügbare Backup-Dateien",
"select_backup": "Wählen Sie ein Backup zur Wiederherstellung aus",
"to_cancel": "zum Abbrechen",
"operation_cancelled": "Vorgang abgebrochen",
"invalid_selection": "Ungültige Auswahl",
"please_enter_number": "Bitte geben Sie eine gültige Zahl ein",
"missing_id": "Fehlende ID: {id}",
"read_backup_failed": "Backup-Datei konnte nicht gelesen werden: {error}",
"current_file_not_found": "Aktuelle Speicherdatei nicht gefunden",
"current_backup_created": "Backup der aktuellen Speicherdatei erstellt",
"storage_updated": "Speicherdatei erfolgreich aktualisiert",
"update_failed": "Aktualisierung der Speicherdatei fehlgeschlagen: {error}",
"sqlite_not_found": "SQLite-Datenbank nicht gefunden",
"updating_sqlite": "Aktualisiere SQLite-Datenbank",
"updating_pair": "Aktualisiere Schlüssel-Wert-Paar",
"sqlite_updated": "SQLite-Datenbank erfolgreich aktualisiert",
"sqlite_update_failed": "Aktualisierung der SQLite-Datenbank fehlgeschlagen: {error}",
"machine_id_backup_created": "Backup der machineId-Datei erstellt",
"backup_creation_failed": "Backup-Erstellung fehlgeschlagen: {error}",
"machine_id_updated": "machineId-Datei erfolgreich aktualisiert",
"machine_id_update_failed": "Aktualisierung der machineId-Datei fehlgeschlagen: {error}",
"updating_system_ids": "Aktualisiere System-IDs",
"system_ids_update_failed": "Aktualisierung der System-IDs fehlgeschlagen: {error}",
"permission_denied": "Zugriff verweigert. Bitte versuchen Sie es mit Administratorrechten",
"windows_machine_guid_updated": "Windows-Geräte-GUID erfolgreich aktualisiert",
"update_windows_machine_guid_failed": "Aktualisierung des Windows-Geräte-GUID fehlgeschlagen: {error}",
"windows_machine_id_updated": "Windows-Geräte-ID erfolgreich aktualisiert",
"update_windows_machine_id_failed": "Aktualisierung der Windows-Geräte-ID fehlgeschlagen: {error}",
"sqm_client_key_not_found": "SQMClient-Registrierungsschlüssel nicht gefunden",
"update_windows_system_ids_failed": "Aktualisierung der Windows-System-IDs fehlgeschlagen: {error}",
"macos_platform_uuid_updated": "macOS-Plattform-UUID erfolgreich aktualisiert",
"failed_to_execute_plutil_command": "Ausführung des plutil-Befehls fehlgeschlagen",
"update_macos_system_ids_failed": "Aktualisierung der macOS-System-IDs fehlgeschlagen: {error}",
"ids_to_restore": "Wiederherzustellende Geräte-IDs",
"confirm": "Sind Sie sicher, dass Sie diese IDs wiederherstellen möchten?",
"success": "Geräte-ID erfolgreich wiederhergestellt",
"process_error": "Fehler beim Wiederherstellungsprozess: {error}",
"press_enter": "Drücken Sie Enter, um fortzufahren"
}
}

View File

@@ -4,8 +4,8 @@
"exit": "Exit Program",
"reset": "Reset Machine ID",
"register": "Register New Cursor Account",
"register_google": "Register with Google Account",
"register_github": "Register with GitHub Account",
"register_google": "Register with Self Google Account",
"register_github": "Register with Self GitHub Account",
"register_manual": "Register Cursor with Custom Email",
"quit": "Close Cursor Application",
"select_language": "Change Language",
@@ -30,9 +30,15 @@
"continue_prompt": "Continue? (y/N): ",
"operation_cancelled_by_user": "Operation cancelled by user",
"exiting": "Exiting ……",
"bypass_version_check": "Bypass Cursor Version Check"
"bypass_version_check": "Bypass Cursor Version Check",
"check_user_authorized": "Check User Authorized",
"bypass_token_limit": "Bypass Token Limit",
"language_config_saved": "Language configuration saved successfully",
"lang_invalid_choice": "Invalid choice. Please enter one of the following options: ({lang_choices})",
"restore_machine_id": "Restore Machine ID from Backup"
},
"languages": {
"ar": "Arabic",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -117,7 +123,8 @@
"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}"
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}",
"file_not_found": "File Not Found: {path}"
},
"register": {
"title": "Cursor Registration Tool",
@@ -196,7 +203,15 @@
"setting_on_password": "Setting Password",
"getting_code": "Getting Verification Code, Will Try in 60s",
"human_verify_error": "Can't verify the user is human. Retrying...",
"max_retries_reached": "Maximum retry attempts reached. Registration failed."
"max_retries_reached": "Maximum retry attempts reached. Registration failed.",
"browser_path_invalid": "{browser} path is invalid, using default path",
"using_browser": "Using {browser} browser: {path}",
"using_browser_profile": "Using {browser} profile from: {user_data_dir}",
"make_sure_browser_is_properly_installed": "Make sure {browser} is properly installed",
"try_install_browser": "Try installing the browser with your package manager",
"tracking_processes": "Tracking {count} {browser} processes",
"no_new_processes_detected": "No new {browser} processes detected to track",
"could_not_track_processes": "Could not track {browser} processes: {error}"
},
"auth": {
"title": "Cursor Auth Manager",
@@ -555,7 +570,16 @@
"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}"
"also_checked": "Also checked {path}",
"backup_created": "Backup created: {path}",
"config_removed": "Config file removed for forced update",
"backup_failed": "Failed to backup config: {error}",
"force_update_failed": "Force update config failed: {error}",
"config_force_update_disabled": "Config file force update disabled , skipping forced update",
"config_force_update_enabled": "Config file force update enabled , performing forced update",
"documents_path_not_found": "Documents path not found, using current directory",
"config_dir_created": "Config directory created: {path}",
"using_temp_dir": "Using temporary directory due to error: {path} (Error: {error})"
},
"oauth": {
"authentication_button_not_found": "Authentication button not found",
@@ -614,19 +638,27 @@
"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}"
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
"user_data_dir_not_found": "{browser} user data directory not found at {path}, will try Chrome instead",
"error_getting_user_data_directory": "Error getting user data directory: {error}",
"warning_browser_close": "Warning: This will close all running {browser} processes",
"killing_browser_processes": "Killing {browser} processes...",
"profile_selection_error": "Error during profile selection: {error}",
"using_configured_browser_path": "Using configured {browser} path: {path}",
"browser_not_found_trying_chrome": "Could not find {browser}, trying Chrome instead",
"found_chrome_at": "Found Chrome at: {path}",
"found_browser_user_data_dir": "Found {browser} user data directory: {path}"
},
"chrome_profile": {
"title": "Chrome Profile Selection",
"select_profile": "Select a Chrome profile to use:",
"profile_list": "Available profiles:",
"default_profile": "Default Profile",
"browser_profile": {
"title": "Browser Profile Selection",
"select_profile": "Select {browser} profile to use:",
"profile_list": "Available {browser} profiles:",
"default_profile": "Default profile",
"profile": "Profile {number}",
"no_profiles": "No Chrome profiles found",
"error_loading": "Error loading Chrome profiles: {error}",
"no_profiles": "No {browser} profiles found",
"error_loading": "Error loading {browser} 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"
"invalid_selection": "Invalid selection. Please try again."
},
"account_delete": {
"title": "Cursor Google Account Deletion Tool",
@@ -691,5 +723,93 @@
"title": "Cursor Version Bypass Tool",
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
"menu_option": "Bypass Cursor Version Check"
},
"auth_check": {
"checking_authorization": "Checking authorization...",
"token_source": "Get token from database or input manually? (d/m, default: d)",
"getting_token_from_db": "Getting token from database...",
"token_found_in_db": "Token found in database",
"token_not_found_in_db": "Token not found in database",
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
"error_getting_token_from_db": "Error getting token from database: {error}",
"enter_token": "Enter your Cursor token: ",
"token_length": "Token length: {length} characters",
"usage_response_status": "Usage response status: {response}",
"unexpected_status_code": "Unexpected status code: {code}",
"jwt_token_warning": "Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.",
"invalid_token": "Invalid token",
"user_authorized": "User is authorized",
"user_unauthorized": "User is unauthorized",
"request_timeout": "Request timed out",
"connection_error": "Connection error",
"check_error": "Error checking authorization: {error}",
"authorization_successful": "Authorization successful!",
"authorization_failed": "Authorization failed!",
"operation_cancelled": "Operation cancelled by user",
"unexpected_error": "Unexpected error: {error}",
"error_generating_checksum": "Error generating checksum: {error}",
"checking_usage_information": "Checking usage information...",
"check_usage_response": "Check usage response: {response}",
"usage_response": "Usage response: {response}"
},
"bypass_token_limit": {
"title": "Bypass Token Limit Tool",
"description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit",
"press_enter": "Press Enter to continue..."
},
"token": {
"refreshing": "Refreshing token...",
"refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})",
"no_access_token": "No access token in response",
"refresh_failed": "Token refresh failed: {error}",
"invalid_response": "Invalid JSON response from refresh server",
"server_error": "Refresh server error: HTTP {status}",
"request_timeout": "Request to refresh server timed out",
"connection_error": "Connection error to refresh server",
"unexpected_error": "Unexpected error during token refresh: {error}",
"extraction_error": "Error extracting token: {error}"
},
"restore": {
"title": "Restore Machine ID from Backup",
"starting": "Starting Machine ID Restore Process",
"no_backups_found": "No backup files found",
"available_backups": "Available backup files",
"select_backup": "Select backup to restore",
"to_cancel": "to cancel",
"operation_cancelled": "Operation cancelled",
"invalid_selection": "Invalid selection",
"please_enter_number": "Please enter a valid number",
"missing_id": "Missing ID: {id}",
"read_backup_failed": "Failed to read backup file: {error}",
"current_file_not_found": "Current storage file not found",
"current_backup_created": "Created backup of current storage file",
"storage_updated": "Storage file updated successfully",
"update_failed": "Failed to update storage file: {error}",
"sqlite_not_found": "SQLite database not found",
"updating_sqlite": "Updating SQLite database",
"updating_pair": "Updating key-value pair",
"sqlite_updated": "SQLite database updated successfully",
"sqlite_update_failed": "Failed to update SQLite database: {error}",
"machine_id_backup_created": "Created backup of machineId file",
"backup_creation_failed": "Failed to create backup: {error}",
"machine_id_updated": "machineId file updated successfully",
"machine_id_update_failed": "Failed to update machineId file: {error}",
"updating_system_ids": "Updating system IDs",
"system_ids_update_failed": "Failed to update system IDs: {error}",
"permission_denied": "Permission denied. Please try running as administrator",
"windows_machine_guid_updated": "Windows Machine GUID updated successfully",
"update_windows_machine_guid_failed": "Failed to update Windows Machine GUID: {error}",
"windows_machine_id_updated": "Windows Machine ID updated successfully",
"update_windows_machine_id_failed": "Failed to update Windows Machine ID: {error}",
"sqm_client_key_not_found": "SQMClient registry key not found",
"update_windows_system_ids_failed": "Failed to update Windows system IDs: {error}",
"macos_platform_uuid_updated": "macOS Platform UUID updated successfully",
"failed_to_execute_plutil_command": "Failed to execute plutil command",
"update_macos_system_ids_failed": "Failed to update macOS system IDs: {error}",
"ids_to_restore": "Machine IDs to restore",
"confirm": "Are you sure you want to restore these IDs?",
"success": "Machine ID restored successfully",
"process_error": "Restore process error: {error}",
"press_enter": "Press Enter to continue"
}
}
}

View File

@@ -22,9 +22,20 @@
"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"
"fixed_soon": "Arreglado Pronto",
"contribute": "Contribuir al Proyecto",
"config": "Mostrar Configuración",
"delete_google_account": "Eliminar Cuenta Google de Cursor",
"continue_prompt": "¿Continuar? (y/N): ",
"operation_cancelled_by_user": "Operación cancelada por el usuario",
"exiting": "Saliendo ……",
"bypass_version_check": "Omitir Verificación de Versión de Cursor",
"check_user_authorized": "Verificar Usuario Autorizado",
"bypass_token_limit": "Omitir límite de tokens",
"restore_machine_id": "Restaurar ID de máquina desde copia de seguridad"
},
"languages": {
"ar": "Árabe",
"en": "Inglés",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -103,8 +114,14 @@
"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"
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0",
"no_write_permission": "Sin Permiso de Escritura: {path}",
"path_not_found": "Ruta No Encontrada: {path}",
"modify_file_failed": "Falló la Modificación del Archivo: {error}",
"windows_machine_id_updated": "ID de Máquina Windows Actualizado Exitosamente",
"update_windows_machine_id_failed": "Falló la Actualización del ID de Máquina Windows: {error}",
"update_windows_machine_guid_failed": "Falló la Actualización del GUID de Máquina Windows: {error}",
"file_not_found": "Archivo No Encontrado: {path}"
},
"register": {
"title": "Herramienta de Registro de Cursor",
@@ -451,5 +468,48 @@
"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"
},
"restore": {
"title": "Restaurar ID de máquina desde copia de seguridad",
"starting": "Iniciando proceso de restauración de ID de máquina",
"no_backups_found": "No se encontraron copias de seguridad",
"available_backups": "Copias de seguridad disponibles",
"select_backup": "Seleccione una copia de seguridad para restaurar",
"to_cancel": "para cancelar",
"operation_cancelled": "Operación cancelada",
"invalid_selection": "Selección inválida",
"please_enter_number": "Por favor, introduzca un número válido",
"missing_id": "ID faltante: {id}",
"read_backup_failed": "Error al leer el archivo de copia de seguridad: {error}",
"current_file_not_found": "No se encontró el archivo de almacenamiento actual",
"current_backup_created": "Se creó una copia de seguridad del archivo de almacenamiento actual",
"storage_updated": "Archivo de almacenamiento actualizado con éxito",
"update_failed": "Error al actualizar el archivo de almacenamiento: {error}",
"sqlite_not_found": "No se encontró la base de datos SQLite",
"updating_sqlite": "Actualizando base de datos SQLite",
"updating_pair": "Actualizando par clave-valor",
"sqlite_updated": "Base de datos SQLite actualizada con éxito",
"sqlite_update_failed": "Error al actualizar la base de datos SQLite: {error}",
"machine_id_backup_created": "Se creó una copia de seguridad del archivo machineId",
"backup_creation_failed": "Error al crear la copia de seguridad: {error}",
"machine_id_updated": "Archivo machineId actualizado con éxito",
"machine_id_update_failed": "Error al actualizar el archivo machineId: {error}",
"updating_system_ids": "Actualizando IDs del sistema",
"system_ids_update_failed": "Error al actualizar los IDs del sistema: {error}",
"permission_denied": "Permiso denegado. Intente ejecutar como administrador",
"windows_machine_guid_updated": "GUID de máquina Windows actualizado con éxito",
"update_windows_machine_guid_failed": "Error al actualizar el GUID de máquina Windows: {error}",
"windows_machine_id_updated": "ID de máquina Windows actualizado con éxito",
"update_windows_machine_id_failed": "Error al actualizar el ID de máquina Windows: {error}",
"sqm_client_key_not_found": "No se encontró la clave de registro SQMClient",
"update_windows_system_ids_failed": "Error al actualizar los IDs del sistema Windows: {error}",
"macos_platform_uuid_updated": "UUID de plataforma macOS actualizado con éxito",
"failed_to_execute_plutil_command": "Error al ejecutar el comando plutil",
"update_macos_system_ids_failed": "Error al actualizar los IDs del sistema macOS: {error}",
"ids_to_restore": "IDs de máquina a restaurar",
"confirm": "¿Está seguro de que desea restaurar estos IDs?",
"success": "ID de máquina restaurado con éxito",
"process_error": "Error en el proceso de restauración: {error}",
"press_enter": "Presione Enter para continuar"
}
}
}

View File

@@ -19,9 +19,21 @@
"totally_reset": "Réinitialisation Complète de Cursor",
"outdate": "Obsolete",
"temp_github_register": "Inscription GitHub temporaire",
"coming_soon": "Bientôt"
"coming_soon": "Bientôt",
"fixed_soon": "Bientôt Corrigé",
"contribute": "Contribuer au Projet",
"config": "Afficher la Configuration",
"delete_google_account": "Supprimer le Compte Google Cursor",
"continue_prompt": "Continuer ? (y/N) : ",
"operation_cancelled_by_user": "Opération annulée par l'utilisateur",
"exiting": "Fermeture ……",
"bypass_version_check": "Ignorer la Vérification de Version de Cursor",
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur",
"bypass_token_limit": "Contourner la limite de tokens",
"restore_machine_id": "Restaurer l'ID de machine depuis une sauvegarde"
},
"languages": {
"ar": "Arabe",
"en": "Anglais",
"zh_cn": "Chinois simplifié",
"zh_tw": "Chinois traditionnel",
@@ -31,7 +43,9 @@
"fr": "Français",
"pt": "Portugais",
"ru": "Russe",
"es": "Espagnol"
"es": "Espagnol",
"tr": "Turc",
"bg": "Bulgare"
},
"quit_cursor": {
"start": "Début de la Fermeture de Cursor",
@@ -98,7 +112,14 @@
"package_not_found": "Package.json Non Trouvé : {path}",
"check_version_failed": "Échec de la Vérification de la Version : {error}",
"stack_trace": "Trace de la Pile",
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0"
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0",
"no_write_permission": "Pas de Permission d'Écriture : {path}",
"path_not_found": "Chemin Non Trouvé : {path}",
"modify_file_failed": "Échec de la Modification du Fichier : {error}",
"windows_machine_id_updated": "ID de la Machine Windows Mis à Jour avec Succès",
"update_windows_machine_id_failed": "Échec de la Mise à Jour de l'ID de la Machine Windows : {error}",
"update_windows_machine_guid_failed": "Échec de la Mise à Jour du GUID de la Machine Windows : {error}",
"file_not_found": "Fichier Non Trouvé : {path}"
},
"register": {
"title": "Outil d'Enregistrement de Cursor",
@@ -386,5 +407,48 @@
"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"
},
"restore": {
"title": "Restaurer l'ID de machine depuis une sauvegarde",
"starting": "Démarrage du processus de restauration de l'ID de machine",
"no_backups_found": "Aucune sauvegarde trouvée",
"available_backups": "Sauvegardes disponibles",
"select_backup": "Sélectionnez une sauvegarde à restaurer",
"to_cancel": "pour annuler",
"operation_cancelled": "Opération annulée",
"invalid_selection": "Sélection invalide",
"please_enter_number": "Veuillez entrer un numéro valide",
"missing_id": "ID manquant : {id}",
"read_backup_failed": "Échec de lecture du fichier de sauvegarde : {error}",
"current_file_not_found": "Fichier de stockage actuel introuvable",
"current_backup_created": "Sauvegarde du fichier de stockage actuel créée",
"storage_updated": "Fichier de stockage mis à jour avec succès",
"update_failed": "Échec de la mise à jour du fichier de stockage : {error}",
"sqlite_not_found": "Base de données SQLite introuvable",
"updating_sqlite": "Mise à jour de la base de données SQLite",
"updating_pair": "Mise à jour de la paire clé-valeur",
"sqlite_updated": "Base de données SQLite mise à jour avec succès",
"sqlite_update_failed": "Échec de la mise à jour de la base de données SQLite : {error}",
"machine_id_backup_created": "Sauvegarde du fichier machineId créée",
"backup_creation_failed": "Échec de création de la sauvegarde : {error}",
"machine_id_updated": "Fichier machineId mis à jour avec succès",
"machine_id_update_failed": "Échec de la mise à jour du fichier machineId : {error}",
"updating_system_ids": "Mise à jour des ID système",
"system_ids_update_failed": "Échec de la mise à jour des ID système : {error}",
"permission_denied": "Permission refusée. Veuillez essayer d'exécuter en tant qu'administrateur",
"windows_machine_guid_updated": "GUID de machine Windows mis à jour avec succès",
"update_windows_machine_guid_failed": "Échec de la mise à jour du GUID de machine Windows : {error}",
"windows_machine_id_updated": "ID de machine Windows mis à jour avec succès",
"update_windows_machine_id_failed": "Échec de la mise à jour de l'ID de machine Windows : {error}",
"sqm_client_key_not_found": "Clé de registre SQMClient introuvable",
"update_windows_system_ids_failed": "Échec de la mise à jour des ID système Windows : {error}",
"macos_platform_uuid_updated": "UUID de plateforme macOS mis à jour avec succès",
"failed_to_execute_plutil_command": "Échec d'exécution de la commande plutil",
"update_macos_system_ids_failed": "Échec de la mise à jour des ID système macOS : {error}",
"ids_to_restore": "ID de machine à restaurer",
"confirm": "Êtes-vous sûr de vouloir restaurer ces ID ?",
"success": "ID de machine restauré avec succès",
"process_error": "Erreur du processus de restauration : {error}",
"press_enter": "Appuyez sur Entrée pour continuer"
}
}

View File

@@ -20,9 +20,21 @@
"totally_reset": "Cursor volledig resetten",
"outdate": "Verouderd",
"temp_github_register": "Tijdelijke GitHub-registratie",
"coming_soon": "Binnenkort"
"coming_soon": "Binnenkort",
"fixed_soon": "Binnenkort Opgelost",
"contribute": "Bijdragen aan het Project",
"config": "Configuratie Weergeven",
"delete_google_account": "Cursor Google Account Verwijderen",
"continue_prompt": "Doorgaan? (y/N): ",
"operation_cancelled_by_user": "Operatie geannuleerd door gebruiker",
"exiting": "Afsluiten ……",
"bypass_version_check": "Cursor Versiecontrole Overslaan",
"check_user_authorized": "Gebruikersautorisatie Controleren",
"bypass_token_limit": "Token-limiet omzeilen",
"restore_machine_id": "Machine-ID herstellen vanaf backup"
},
"languages": {
"ar": "Arabisch",
"en": "Engels",
"zh_cn": "Vereenvoudigd Chinees",
"zh_tw": "Traditioneel Chinees",
@@ -99,7 +111,14 @@
"package_not_found": "Package.json niet gevonden: {path}",
"check_version_failed": "Versiecontrole mislukt: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0"
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0",
"no_write_permission": "Geen schrijfrechten: {path}",
"path_not_found": "Pad niet gevonden: {path}",
"modify_file_failed": "Bestand wijzigen mislukt: {error}",
"windows_machine_id_updated": "Windows Machine-ID succesvol bijgewerkt",
"update_windows_machine_id_failed": "Windows Machine-ID bijwerken mislukt: {error}",
"update_windows_machine_guid_failed": "Windows Machine GUID bijwerken mislukt: {error}",
"file_not_found": "Bestand niet gevonden: {path}"
},
"register": {
"title": "Cursor Registratietool",
@@ -387,5 +406,48 @@
"profile_selected": "Geselecteerd profiel: {profile}",
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
},
"restore": {
"title": "Machine-ID herstellen vanaf backup",
"starting": "Herstelproces van machine-ID starten",
"no_backups_found": "Geen backups gevonden",
"available_backups": "Beschikbare backups",
"select_backup": "Selecteer een backup om te herstellen",
"to_cancel": "om te annuleren",
"operation_cancelled": "Bewerking geannuleerd",
"invalid_selection": "Ongeldige selectie",
"please_enter_number": "Voer een geldig nummer in",
"missing_id": "Ontbrekende ID: {id}",
"read_backup_failed": "Lezen van backupbestand mislukt: {error}",
"current_file_not_found": "Huidig opslagbestand niet gevonden",
"current_backup_created": "Backup van huidig opslagbestand gemaakt",
"storage_updated": "Opslagbestand succesvol bijgewerkt",
"update_failed": "Bijwerken van opslagbestand mislukt: {error}",
"sqlite_not_found": "SQLite-database niet gevonden",
"updating_sqlite": "SQLite-database bijwerken",
"updating_pair": "Sleutel-waardepaar bijwerken",
"sqlite_updated": "SQLite-database succesvol bijgewerkt",
"sqlite_update_failed": "Bijwerken van SQLite-database mislukt: {error}",
"machine_id_backup_created": "Backup van machineId-bestand gemaakt",
"backup_creation_failed": "Maken van backup mislukt: {error}",
"machine_id_updated": "MachineId-bestand succesvol bijgewerkt",
"machine_id_update_failed": "Bijwerken van machineId-bestand mislukt: {error}",
"updating_system_ids": "Systeem-ID's bijwerken",
"system_ids_update_failed": "Bijwerken van systeem-ID's mislukt: {error}",
"permission_denied": "Toegang geweigerd. Probeer als administrator uit te voeren",
"windows_machine_guid_updated": "Windows machine-GUID succesvol bijgewerkt",
"update_windows_machine_guid_failed": "Bijwerken van Windows machine-GUID mislukt: {error}",
"windows_machine_id_updated": "Windows machine-ID succesvol bijgewerkt",
"update_windows_machine_id_failed": "Bijwerken van Windows machine-ID mislukt: {error}",
"sqm_client_key_not_found": "SQMClient registersleutel niet gevonden",
"update_windows_system_ids_failed": "Bijwerken van Windows systeem-ID's mislukt: {error}",
"macos_platform_uuid_updated": "macOS platform-UUID succesvol bijgewerkt",
"failed_to_execute_plutil_command": "Uitvoeren van plutil opdracht mislukt",
"update_macos_system_ids_failed": "Bijwerken van macOS systeem-ID's mislukt: {error}",
"ids_to_restore": "Te herstellen machine-ID's",
"confirm": "Weet u zeker dat u deze ID's wilt herstellen?",
"success": "Machine-ID succesvol hersteld",
"process_error": "Fout bij herstelproces: {error}",
"press_enter": "Druk op Enter om door te gaan"
}
}
}

View File

@@ -19,9 +19,21 @@
"totally_reset": "Redefinir Cursor Completamente",
"outdate": "Obsoleto",
"temp_github_register": "Registro temporário do GitHub",
"coming_soon": "Em breve"
"coming_soon": "Em breve",
"fixed_soon": "Será corrigido em breve",
"contribute": "Contribuir para o Projeto",
"config": "Mostrar Configuração",
"delete_google_account": "Excluir Conta Google do Cursor",
"continue_prompt": "Continuar? (y/N): ",
"operation_cancelled_by_user": "Operação cancelada pelo usuário",
"exiting": "Saindo ......",
"bypass_version_check": "Ignorar Verificação de Versão do Cursor",
"check_user_authorized": "Verificar Autorização do Usuário",
"bypass_token_limit": "Contornar Limite de Tokens",
"restore_machine_id": "Restaurar ID da Máquina do Backup"
},
"languages": {
"ar": "Árabe",
"en": "Inglês",
"zh_cn": "Chinês Simplificado",
"zh_tw": "Chinês Tradicional",
@@ -395,5 +407,48 @@
"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"
},
"restore": {
"title": "Restaurar ID da Máquina do Backup",
"starting": "Iniciando processo de restauração de ID da máquina",
"no_backups_found": "Nenhum backup encontrado",
"available_backups": "Backups disponíveis",
"select_backup": "Selecione um backup para restaurar",
"to_cancel": "para cancelar",
"operation_cancelled": "Operação cancelada",
"invalid_selection": "Seleção inválida",
"please_enter_number": "Por favor, insira um número válido",
"missing_id": "ID ausente: {id}",
"read_backup_failed": "Falha ao ler arquivo de backup: {error}",
"current_file_not_found": "Arquivo de armazenamento atual não encontrado",
"current_backup_created": "Backup do arquivo de armazenamento atual criado",
"storage_updated": "Arquivo de armazenamento atualizado com sucesso",
"update_failed": "Falha ao atualizar arquivo de armazenamento: {error}",
"sqlite_not_found": "Banco de dados SQLite não encontrado",
"updating_sqlite": "Atualizando banco de dados SQLite",
"updating_pair": "Atualizando par chave-valor",
"sqlite_updated": "Banco de dados SQLite atualizado com sucesso",
"sqlite_update_failed": "Falha ao atualizar banco de dados SQLite: {error}",
"machine_id_backup_created": "Backup do arquivo machineId criado",
"backup_creation_failed": "Falha ao criar backup: {error}",
"machine_id_updated": "Arquivo machineId atualizado com sucesso",
"machine_id_update_failed": "Falha ao atualizar arquivo machineId: {error}",
"updating_system_ids": "Atualizando IDs do sistema",
"system_ids_update_failed": "Falha ao atualizar IDs do sistema: {error}",
"permission_denied": "Permissão negada. Tente executar como administrador",
"windows_machine_guid_updated": "GUID da máquina Windows atualizado com sucesso",
"update_windows_machine_guid_failed": "Falha ao atualizar GUID da máquina Windows: {error}",
"windows_machine_id_updated": "ID da máquina Windows atualizado com sucesso",
"update_windows_machine_id_failed": "Falha ao atualizar ID da máquina Windows: {error}",
"sqm_client_key_not_found": "Chave de registro SQMClient não encontrada",
"update_windows_system_ids_failed": "Falha ao atualizar IDs do sistema Windows: {error}",
"macos_platform_uuid_updated": "UUID da plataforma macOS atualizado com sucesso",
"failed_to_execute_plutil_command": "Falha ao executar comando plutil",
"update_macos_system_ids_failed": "Falha ao atualizar IDs do sistema macOS: {error}",
"ids_to_restore": "IDs da máquina para restaurar",
"confirm": "Tem certeza que deseja restaurar esses IDs?",
"success": "ID da máquina restaurado com sucesso",
"process_error": "Erro no processo de restauração: {error}",
"press_enter": "Pressione Enter para continuar"
}
}
}

View File

@@ -19,9 +19,22 @@
"totally_reset": "Полностью сбросить Cursor",
"outdate": "Устаревший",
"temp_github_register": "Временная регистрация GitHub",
"coming_soon": "Скоро"
"coming_soon": "Скоро",
"fixed_soon": "Скоро будет исправлено",
"contribute": "Внести вклад в проект",
"config": "Показать конфигурацию",
"delete_google_account": "Удалить Google аккаунт Cursor",
"continue_prompt": "Продолжить? (y/N): ",
"operation_cancelled_by_user": "Операция отменена пользователем",
"exiting": "Выход ......",
"bypass_version_check": "Пропустить проверку версии Cursor",
"check_user_authorized": "Проверить авторизацию пользователя",
"select_chrome_profile": "Выбрать профиль Chrome",
"bypass_token_limit": "Обход ограничения токенов",
"restore_machine_id": "Восстановить ID устройства из резервной копии"
},
"languages": {
"ar": "Арабский",
"en": "Английский",
"zh_cn": "Упрощенный китайский",
"zh_tw": "Традиционный китайский",
@@ -395,5 +408,48 @@
"profile_selected": "Выбран профиль: {profile}",
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
},
"restore": {
"title": "Восстановить ID устройства из резервной копии",
"starting": "Запуск процесса восстановления ID устройства",
"no_backups_found": "Резервные копии не найдены",
"available_backups": "Доступные резервные копии",
"select_backup": "Выберите резервную копию для восстановления",
"to_cancel": "для отмены",
"operation_cancelled": "Операция отменена",
"invalid_selection": "Неверный выбор",
"please_enter_number": "Пожалуйста, введите корректный номер",
"missing_id": "Отсутствует ID: {id}",
"read_backup_failed": "Не удалось прочитать файл резервной копии: {error}",
"current_file_not_found": "Текущий файл хранилища не найден",
"current_backup_created": "Создана резервная копия текущего файла хранилища",
"storage_updated": "Файл хранилища успешно обновлен",
"update_failed": "Не удалось обновить файл хранилища: {error}",
"sqlite_not_found": "База данных SQLite не найдена",
"updating_sqlite": "Обновление базы данных SQLite",
"updating_pair": "Обновление пары ключ-значение",
"sqlite_updated": "База данных SQLite успешно обновлена",
"sqlite_update_failed": "Не удалось обновить базу данных SQLite: {error}",
"machine_id_backup_created": "Создана резервная копия файла machineId",
"backup_creation_failed": "Не удалось создать резервную копию: {error}",
"machine_id_updated": "Файл machineId успешно обновлен",
"machine_id_update_failed": "Не удалось обновить файл machineId: {error}",
"updating_system_ids": "Обновление системных ID",
"system_ids_update_failed": "Не удалось обновить системные ID: {error}",
"permission_denied": "Доступ запрещен. Попробуйте запустить с правами администратора",
"windows_machine_guid_updated": "GUID устройства Windows успешно обновлен",
"update_windows_machine_guid_failed": "Не удалось обновить GUID устройства Windows: {error}",
"windows_machine_id_updated": "ID устройства Windows успешно обновлен",
"update_windows_machine_id_failed": "Не удалось обновить ID устройства Windows: {error}",
"sqm_client_key_not_found": "Ключ реестра SQMClient не найден",
"update_windows_system_ids_failed": "Не удалось обновить системные ID Windows: {error}",
"macos_platform_uuid_updated": "UUID платформы macOS успешно обновлен",
"failed_to_execute_plutil_command": "Не удалось выполнить команду plutil",
"update_macos_system_ids_failed": "Не удалось обновить системные ID macOS: {error}",
"ids_to_restore": "ID устройства для восстановления",
"confirm": "Вы уверены, что хотите восстановить эти ID?",
"success": "ID устройства успешно восстановлен",
"process_error": "Ошибка процесса восстановления: {error}",
"press_enter": "Нажмите Enter для продолжения"
}
}
}

View File

@@ -9,6 +9,7 @@
"register_manual": "Cursor'ı Özel E-posta ile Kaydet",
"quit": "Cursor Uygulamasını Kapat",
"select_language": "Dili Değiştir",
"select_chrome_profile": "Chrome Profilini Seç",
"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ı",
@@ -19,9 +20,23 @@
"totally_reset": "Cursor'ı Tamamen Sıfırla",
"outdate": "güncel değil",
"temp_github_register": "Geçici GitHub Kaydı",
"coming_soon": "Yakında"
"admin_required": "Running as executable, administrator privileges required.",
"admin_required_continue": "Continuing without administrator privileges.",
"coming_soon": "Yakında",
"fixed_soon": "Yakında Düzeltilecek",
"contribute": "Projeye Katkıda Bulun",
"config": "Yapılandırmayı Göster",
"delete_google_account": "Cursor Google Hesabını Sil",
"continue_prompt": "Devam et? (y/N): ",
"operation_cancelled_by_user": "İşlem kullanıcı tarafından iptal edildi",
"exiting": ıkılıyor ......",
"bypass_version_check": "Cursor Sürüm Kontrolünü Atla",
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et",
"bypass_token_limit": "Token Limitini Atla",
"restore_machine_id": "Makine Kimliğini Yedekten Geri Yükle"
},
"languages": {
"ar": "Arapça",
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
@@ -398,5 +413,48 @@
"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"
},
"restore": {
"title": "Makine Kimliğini Yedekten Geri Yükle",
"starting": "Makine kimliği geri yükleme işlemi başlatılıyor",
"no_backups_found": "Yedek bulunamadı",
"available_backups": "Mevcut yedekler",
"select_backup": "Geri yüklemek için bir yedek seçin",
"to_cancel": "iptal etmek için",
"operation_cancelled": "İşlem iptal edildi",
"invalid_selection": "Geçersiz seçim",
"please_enter_number": "Lütfen geçerli bir numara girin",
"missing_id": "Eksik kimlik: {id}",
"read_backup_failed": "Yedek dosyası okunamadı: {error}",
"current_file_not_found": "Mevcut depolama dosyası bulunamadı",
"current_backup_created": "Mevcut depolama dosyasının yedeği oluşturuldu",
"storage_updated": "Depolama dosyası başarıyla güncellendi",
"update_failed": "Depolama dosyası güncellenemedi: {error}",
"sqlite_not_found": "SQLite veritabanı bulunamadı",
"updating_sqlite": "SQLite veritabanı güncelleniyor",
"updating_pair": "Anahtar-değer çifti güncelleniyor",
"sqlite_updated": "SQLite veritabanı başarıyla güncellendi",
"sqlite_update_failed": "SQLite veritabanı güncellenemedi: {error}",
"machine_id_backup_created": "MachineId dosyasının yedeği oluşturuldu",
"backup_creation_failed": "Yedek oluşturulamadı: {error}",
"machine_id_updated": "MachineId dosyası başarıyla güncellendi",
"machine_id_update_failed": "MachineId dosyası güncellenemedi: {error}",
"updating_system_ids": "Sistem kimlikleri güncelleniyor",
"system_ids_update_failed": "Sistem kimlikleri güncellenemedi: {error}",
"permission_denied": "İzin reddedildi. Yönetici olarak çalıştırmayı deneyin",
"windows_machine_guid_updated": "Windows makine GUID'i başarıyla güncellendi",
"update_windows_machine_guid_failed": "Windows makine GUID'i güncellenemedi: {error}",
"windows_machine_id_updated": "Windows makine kimliği başarıyla güncellendi",
"update_windows_machine_id_failed": "Windows makine kimliği güncellenemedi: {error}",
"sqm_client_key_not_found": "SQMClient kayıt anahtarı bulunamadı",
"update_windows_system_ids_failed": "Windows sistem kimlikleri güncellenemedi: {error}",
"macos_platform_uuid_updated": "macOS platform UUID'si başarıyla güncellendi",
"failed_to_execute_plutil_command": "plutil komutu yürütülemedi",
"update_macos_system_ids_failed": "macOS sistem kimlikleri güncellenemedi: {error}",
"ids_to_restore": "Geri yüklenecek makine kimlikleri",
"confirm": "Bu kimlikleri geri yüklemek istediğinizden emin misiniz?",
"success": "Makine kimliği başarıyla geri yüklendi",
"process_error": "Geri yükleme işlemi hatası: {error}",
"press_enter": "Devam etmek için Enter tuşuna basın"
}
}
}

View File

@@ -20,13 +20,31 @@
"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"
"admin_required": "Đang chạy dưới dạng tệp thực thi, yêu cầu quyền quản trị.",
"admin_required_continue": "Tiếp tục mà không có quyền quản trị.",
"coming_soon": "Sắp ra mắt",
"fixed_soon": "Sẽ Sớm Được Sửa",
"contribute": "Đóng Góp Cho Dự Án",
"config": "Hiển Thị Cấu Hình",
"delete_google_account": "Xóa Tài Khoản Google Cursor",
"continue_prompt": "Tiếp tục? (y/N): ",
"operation_cancelled_by_user": "Thao tác đã bị người dùng hủy",
"exiting": "Đang thoát ……",
"bypass_version_check": "Bỏ qua Kiểm tra Phiên bản Cursor",
"check_user_authorized": "Kiểm tra Quyền Người dùng",
"bypass_token_limit": "Bỏ qua giới hạn Token",
"language_config_saved": "Đổi ngôn ngữ thành công",
"lang_invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ ({lang_choices})",
"restore_machine_id": "Khôi phục ID máy từ bản sao lưu"
},
"languages": {
"ar": "Tiếng Ả Rập",
"en": "Tiếng Anh",
"zh_cn": "Tiếng Trung Giản Thể",
"zh_tw": "Tiếng Trung Phồn Thể",
"vi": "Tiếng Việt",
"tr": "Tiếng Thổ Nhĩ Kỳ",
"bg": "Tiếng Bulgaria",
"nl": "Tiếng Hà Lan",
"de": "Tiếng Đức",
"fr": "Tiếng Pháp",
@@ -99,7 +117,14 @@
"package_not_found": "Không Tìm Thấy Package.json: {path}",
"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"
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0",
"no_write_permission": "Không Có Quyền Ghi: {path}",
"path_not_found": "Không Tìm Thấy Đường Dẫn: {path}",
"modify_file_failed": "Sửa Đổi Tệp Thất Bại: {error}",
"windows_machine_id_updated": "Cập Nhật ID Máy Windows Thành Công",
"update_windows_machine_id_failed": "Cập Nhật ID Máy Windows Thất Bại: {error}",
"update_windows_machine_guid_failed": "Cập Nhật GUID Máy Windows Thất Bại: {error}",
"file_not_found": "Không Tìm Thấy Tệp: {path}"
},
"register": {
"title": "Công Cụ Đăng Ký Cursor",
@@ -176,10 +201,12 @@
"password_submitted": "Đã Gửi Mật Khẩu",
"total_usage": "Tổng Sử Dụng: {usage}",
"setting_on_password": "Đang Đặt Mật Khẩu",
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s"
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s",
"human_verify_error": "Không thể xác minh người dùng. Đang thử lại...",
"max_retries_reached": "Đã đạt số lần thử tối đa. Đăng ký thất bại."
},
"auth": {
"title": "Trình Quản Lý Xác Thực Cursor",
"title": "Quản Lý Xác Thực Cursor",
"checking_auth": "Đang Kiểm Tra Tệp Xác Thực",
"auth_not_found": "Không Tìm Thấy Tệp Xác Thực",
"auth_file_error": "Lỗi Tệp Xác Thực: {error}",
@@ -191,7 +218,7 @@
"auth_file_create_failed": "Tạo Tệp Xác Thực Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"reset_machine_id": "Đặt Lại ID Máy",
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
"connected_to_database": "Đã Kết Nối Đến Cơ Sở Dữ Liệu",
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
@@ -213,7 +240,7 @@
"navigation_error": "Lỗi Điều Hướng: {error}",
"email_copy_error": "Lỗi Sao Chép Email: {error}",
"mailbox_error": "Lỗi Hộp Thư: {error}",
"token_saved_to_file": "Token Đã Lưu Vào cursor_tokens.txt",
"token_saved_to_file": "Token Đã Được Lưu Vào cursor_tokens.txt",
"navigate_to": "Đang Điều Hướng Đến {url}",
"generate_email_success": "Tạo Email Thành Công",
"select_email_domain": "Chọn Tên Miền Email",
@@ -229,12 +256,12 @@
"get_cursor_session_token_failed": "Lấy Token Phiên Cursor Thất Bại",
"save_token_failed": "Lưu Token Thất Bại",
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
"no_valid_verification_code": "Không Có Mã Xác Minh Hợp Lệ"
},
"email": {
"starting_browser": "Đang Khởi Động Trình Duyệt",
"visiting_site": "Đang Truy Cập mail domains",
"visiting_site": "Đang Truy Cập Tên Miền Mail",
"create_success": "Tạo Email Thành Công",
"create_failed": "Tạo Email Thất Bại",
"create_error": "Lỗi Tạo Email: {error}",
@@ -261,16 +288,22 @@
"blocked_domains_loaded": "Đã Tải Tên Miền Bị Chặn: {count}",
"blocked_domains_loaded_error": "Lỗi Tải Tên Miền Bị Chặn: {error}",
"blocked_domains_loaded_success": "Tải Tên Miền Bị Chặn Thành Công",
"blocked_domains_loaded_timeout": "Tải Tên Miền Bị Chặn Hết Thời Gian: {timeout}s",
"blocked_domains_loaded_timeout": "Hết Thời Gian Tải Tên Miền Bị Chặn: {timeout}s",
"blocked_domains_loaded_timeout_error": "Lỗi Hết Thời Gian Tải Tên Miền Bị Chặn: {error}",
"available_domains_loaded": "Đã Tải Tên Miền Khả Dụng: {count}",
"domains_filtered": "Tên Miền Đã Lọc: {count}",
"trying_to_create_email": "Đang cố gắng tạo email: {email}",
"domain_blocked": "Tên Miền Bị Chặn: {domain}"
"domains_filtered": "Đã Lọc Tên Miền: {count}",
"trying_to_create_email": "Đang Thử Tạo Email: {email}",
"domain_blocked": "Tên Miền Bị Chặn: {domain}",
"using_chrome_profile": "Đang Sử Dụng Hồ Sơ Chrome từ: {user_data_dir}",
"no_display_found": "Không Tìm Thấy Màn Hình. Đảm Bảo X Server Đang Chạy.",
"try_export_display": "Thử: export DISPLAY=:0",
"extension_load_error": "Lỗi Tải Tiện Ích Mở Rộng: {error}",
"make_sure_chrome_chromium_is_properly_installed": "Đảm Bảo Chrome/Chromium Được Cài Đặt Đúng Cách",
"try_install_chromium": "Thử: sudo apt install chromium-browser"
},
"update": {
"title": "Tắt Tự Động Cập Nhật Cursor",
"disable_success": "Tắt Tự Động Cập Nhật Thành Công",
"disable_success": "Đã Tắt Tự Động Cập Nhật Thành Công",
"disable_failed": "Tắt Tự Động Cập Nhật Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"start_disable": "Bắt Đầu Tắt Tự Động Cập Nhật",
@@ -279,122 +312,468 @@
"removing_directory": "Đang Xóa Thư Mục",
"directory_removed": "Đã Xóa Thư Mục",
"creating_block_file": "Đang Tạo Tệp Chặn",
"block_file_created": "Đã Tạo Tệp Chặn"
"block_file_created": "Đã Tạo Tệp Chặn",
"clearing_update_yml": "Đang Xóa Tệp update.yml",
"update_yml_cleared": "Đã Xóa Tệp update.yml",
"update_yml_not_found": "Không Tìm Thấy Tệp update.yml",
"clear_update_yml_failed": "Xóa Tệp update.yml Thất Bại: {error}",
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {system}",
"remove_directory_failed": "Xóa Thư Mục Thất Bại: {error}",
"create_block_file_failed": "Tạo Tệp Chặn Thất Bại: {error}",
"directory_locked": "Thư Mục Bị Khóa: {path}",
"yml_locked": "Tệp update.yml Bị Khóa",
"block_file_locked": "Tệp Chặn Bị Khóa",
"yml_already_locked": "Tệp update.yml Đã Bị Khóa",
"block_file_already_locked": "Tệp Chặn Đã Bị Khóa",
"block_file_locked_error": "Lỗi Khóa Tệp Chặn: {error}",
"yml_locked_error": "Lỗi Khóa Tệp update.yml: {error}",
"block_file_already_locked_error": "Lỗi Tệp Chặn Đã Bị Khóa: {error}",
"yml_already_locked_error": "Lỗi Tệp update.yml Đã Bị Khóa: {error}"
},
"updater": {
"checking": "Đang Kiểm Tra Cập Nhật...",
"new_version_available": "Có Phiên Bản Mới! (Hiện Tại: {current}, Mới Nhất: {latest})",
"updating": "Đang Cập Nhật Lên Phiên Bản Mới Nhất. Chương Trình STĐộng Khởi Động Lại.",
"up_to_date": "Bạn Đang SDụng Phiên Bản Mới Nhất.",
"check_failed": "Không Thể Kiểm Tra Cập Nhật: {error}",
"continue_anyway": "Tiếp Tục Với Phiên Bản Hiện Tại...",
"update_confirm": "Bạn Có Muốn Cập Nhật Lên Phiên Bản Mới Nhất Không? (Y/n)",
"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'.",
"new_version_available": "Có phiên bản mới! (Hiện tại: {current}, Mới nhất: {latest})",
"updating": "Đang cập nhật lên phiên bản mới nhất. Chương trình stđộng khởi động lại.",
"up_to_date": "Bạn đang sdụng phiên bản mới nhất.",
"check_failed": "Kiểm tra cập nhật thất bại: {error}",
"continue_anyway": "Tiếp tục với phiên bản hiện tại...",
"update_confirm": "Bạn có muốn cập nhật lên phiên bản mới nhất không? (Y/n)",
"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": "Nhật ký thay đổi"
"changelog_title": "Nhật Ký Thay Đổi",
"rate_limit_exceeded": "Đã vượt quá giới hạn API GitHub. Bỏ qua kiểm tra cập nhật."
},
"totally_reset": {
"title": "Đặt lại hoàn toàn Cursor",
"checking_config": "Đang kiểm tra tệp cấu hình",
"config_not_found": "Không tìm thấy tệp cấu hình",
"no_permission": "Không thể đọc hoặc ghi tệp cấu hình, vui lòng kiểm tra quyền tệp",
"reading_config": "Đang đọc cấu hình hiện tại",
"creating_backup": "Đang tạo bản sao lưu cấu hình",
"backup_exists": "Tệp sao lưu đã tồn tại, bqua bước sao lưu",
"generating_new_machine_id": "Đang tạo ID máy mới",
"saving_new_config": "Đang lưu cấu hình mới vào JSON",
"success": "Đặt lại Cursor thành công",
"error": "Đặt lại Cursor thất bại: {error}",
"press_enter": "Nhấn Enter để thoát",
"reset_machine_id": "Đặt lại ID máy",
"database_connection_closed": "Kết nối cơ sdliệu đã đóng",
"database_updated_successfully": "Cập nhật cơ sdliệu thành công",
"connected_to_database": "Đã kết nối với cơ sdliệu",
"updating_pair": "Đang cập nhật cặp khóa-gtrị",
"title": "Đặt Lại Hoàn Toàn Cursor",
"checking_config": "Đang Kiểm Tra Tệp Cấu Hình",
"config_not_found": "Không Tìm Thấy Tệp Cấu Hình",
"no_permission": "Không Thể Đọc Hoặc Ghi Tệp Cấu Hình, Vui Lòng Kiểm Tra Quyền Tệp",
"reading_config": "Đang Đọc Cấu Hình Hiện Tại",
"creating_backup": "Đang Tạo Bản Sao Lưu Cấu Hình",
"backup_exists": "Tệp Sao Lưu Đã Tồn Tại, BQua Bước Sao Lưu",
"generating_new_machine_id": "Đang Tạo ID Máy Mới",
"saving_new_config": "Đang Lưu Cấu Hình Mới Vào JSON",
"success": "Đặt Lại Cursor Thành Công",
"error": "Đặt Lại Cursor Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"reset_machine_id": "Đặt Lại ID Máy",
"database_connection_closed": "Đã Đóng Kết Nối Cơ SDLiệu",
"database_updated_successfully": "Cập Nhật Cơ SDLiệu Thành Công",
"connected_to_database": "Đã Kết Nối Đến Cơ SDLiệu",
"updating_pair": "Đang Cập Nhật Cặp Khóa-GTrị",
"db_not_found": "Không tìm thấy tệp cơ sở dữ liệu tại: {path}",
"db_permission_error": "Không thể truy cập tệp cơ sở dữ liệu, vui lòng kiểm tra quyền",
"db_connection_error": "Kết nối cơ sở dữ liệu thất bại: {error}",
"db_permission_error": "Không thể truy cập tệp cơ sở dữ liệu. Vui lòng kiểm tra quyền",
"db_connection_error": "Không thể kết nối đến cơ sở dữ liệu: {error}",
"feature_title": "TÍNH NĂNG",
"feature_1": "Xóa hoàn toàn các cài đặt và cấu hình của Cursor AI",
"feature_2": "Xóa tất cả dữ liệu bộ nhớ đệm bao gồm lịch sử và gợi ý AI",
"feature_3": "Đặt lại ID máy để vượt qua kiểm tra dùng thử",
"feature_4": "Tạo định danh máy ngẫu nhiên mới",
"feature_5": "Xóa tiện ích mở rộng và tùy chỉnh",
"feature_1": "Xóa hoàn toàn cài đặt và cấu hình của Cursor AI",
"feature_2": "Xóa tất cả dữ liệu đã lưu trong bộ nhớ cache bao gồm lịch sử AI và lời nhắc",
"feature_3": "Đặt lại ID máy để bỏ qua phát hiện dùng thử",
"feature_4": "Tạo định danh máy mới ngẫu nhiên",
"feature_5": "Xóa tiện ích mở rộng và tùy chọn tùy chỉnh",
"feature_6": "Đặt lại thông tin dùng thử và dữ liệu kích hoạt",
"feature_7": "Quét sâu các tệp ẩn liên quan đến giấy phép và dùng thử",
"feature_8": "Bảo toàn các tệp và ứng dụng không liên quan đến Cursor",
"feature_7": "Quét sâu các tệp giấy phép và dùng thử ẩn",
"feature_8": "Bảo toàn an toàn các tệp và ứng dụng không phải của Cursor",
"feature_9": "Tương thích với Windows, macOS và Linux",
"disclaimer_title": "LƯU Ý",
"disclaimer_1": "Công cụ này sẽ xóa vĩnh viễn tất cả cài đặt,",
"disclaimer_2": "cấu hình và dữ liệu bộ nhớ đệm của Cursor AI. Thao tác này không thể hoàn tác.",
"disclaimer_3": "Tệp mã nguồn của bạn sẽ KHÔNG bị ảnh hưởng, công cụ chỉ nhắm đến",
"disclaimer_4": "các tệp chỉnh sửa Cursor AI và cơ chế kiểm tra dùng thử.",
"disclaimer_title": "TUYÊN BỐ MIỄN TRỪ",
"disclaimer_1": "Công cụ này sẽ xóa vĩnh viễn tất cả cài đặt Cursor AI,",
"disclaimer_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"disclaimer_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, công cụ được thiết kế",
"disclaimer_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"disclaimer_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"disclaimer_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"disclaimer_7": "Sử dụng ới sự tự chịu trách nhiệm",
"disclaimer_7": "Sử dụng với rủi ro của riêng bạn",
"confirm_title": "Bạn có chắc chắn muốn tiếp tục không?",
"confirm_1": "Hành động này sẽ xóa tất cả cài đặt,",
"confirm_2": "cấu hình và dữ liệu bộ nhớ đệm của Cursor AI. Hành động này không thể hoàn tác.",
"confirm_3": "Tệp mã nguồn của bạn sẽ KHÔNG bị ảnh hưởng, công cụ chỉ nhắm đến",
"confirm_4": "các tệp chỉnh sửa Cursor AI và cơ chế kiểm tra dùng thử.",
"confirm_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
"confirm_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"confirm_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, công cụ được thiết kế",
"confirm_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"confirm_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"confirm_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"confirm_7": "Sử dụng ới sự tự chịu trách nhiệm",
"confirm_7": "Sử dụng với rủi ro của riêng bạn",
"invalid_choice": "Vui lòng nhập 'Y' hoặc 'n'",
"skipped_for_safety": "Bỏ qua vì an toàn (không liên quan đến Cursor): {path}",
"deleted": "Đã xóa: {path}",
"error_deleting": "Lỗi khi xóa {path}: {error}",
"skipped_for_safety": "Đã bỏ qua vì an toàn (không liên quan đến Cursor): {path}",
"deleted": "Đã Xóa: {path}",
"error_deleting": "Lỗi xóa {path}: {error}",
"not_found": "Không tìm thấy tệp: {path}",
"resetting_machine_id": "Đang đặt lại ID máy để vượt qua kiểm tra dùng thử...",
"resetting_machine_id": "Đang đặt lại định danh máy để bỏ qua phát hiện dùng thử...",
"created_machine_id": "Đã tạo ID máy mới: {path}",
"error_creating_machine_id": "Lỗi khi tạo tệp ID máy {path}: {error}",
"error_searching": "Lỗi khi tìm kiếm tệp trong {path}: {error}",
"error_creating_machine_id": "Lỗi tạo tệp ID máy {path}: {error}",
"error_searching": "Lỗi tìm kiếm tệp trong {path}: {error}",
"created_extended_trial_info": "Đã tạo thông tin dùng thử mở rộng mới: {path}",
"error_creating_trial_info": "Lỗi khi tạo tệp thông tin dùng thử {path}: {error}",
"resetting_cursor_ai_editor": "Đang đặt lại Cursor AI Editor... Vui lòng chờ.",
"reset_cancelled": "Đã hủy đặt lại. Thoát mà không thực hiện thay đổi nào.",
"windows_machine_id_modification_skipped": "Bỏ qua sửa đổi ID máy Windows: {error}",
"linux_machine_id_modification_skipped": "Bỏ qua sửa đổi machine-id Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Lưu ý: Đặt lại ID máy hoàn toàn có thể yêu cầu chạy ới quyền quản trị viên",
"error_creating_trial_info": "Lỗi tạo tệp thông tin dùng thử {path}: {error}",
"resetting_cursor_ai_editor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
"reset_cancelled": "Đã hủy đặt lại. Thoát mà không thay đổi .",
"windows_machine_id_modification_skipped": "Đã bỏ qua sửa đổi ID máy Windows: {error}",
"linux_machine_id_modification_skipped": "Đã bỏ qua sửa đổi machine-id Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Lưu ý: Đặt lại ID máy hoàn toàn có thể yêu cầu chạy với quyền quản trị",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Lưu ý: Đặt lại machine-id hệ thống hoàn toàn có thể yêu cầu quyền sudo",
"windows_registry_instructions": "📝 LƯU Ý: Để đặt lại hoàn toàn trên Windows, bạn có thể cần dọn dẹp các mục registry.",
"windows_registry_instructions_2": " Chạy 'regedit' và tìm kiếm khóa chứa 'Cursor' hoặc 'CursorAI' dưới HKEY_CURRENT_USER\\Software\\ và xóa chúng.\n",
"reset_log_1": "Cursor AI đã được đặt lại hoàn toàn và vượt qua kiểm tra dùng thử!",
"reset_log_2": "Vui lòng khởi động lại hệ thống để thay đổi có hiệu lực.",
"reset_log_3": "Bạn cần cài đặt lại Cursor AI và sẽ có kỳ dùng thử mới.",
"reset_log_4": "Để có kết quả tốt nhất, bạn có thể cân nhắc:",
"reset_log_5": "Sử dụng địa chỉ email khác khi đăng ký dùng thử mới",
"reset_log_6": "Nếu có thể, sử dụng VPN để thay đổi địa chỉ IP",
"windows_registry_instructions": "📝 LƯU Ý: Để đặt lại hoàn toàn trên Windows, bạn có thể cần xóa các mục đăng ký.",
"windows_registry_instructions_2": " Chạy 'regedit' và tìm kiếm các khóa chứa 'Cursor' hoặc 'CursorAI' trong HKEY_CURRENT_USER\\Software\\ và xóa chúng.",
"reset_log_1": "Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
"reset_log_2": "Vui lòng khởi động lại hệ thống để các thay đổi có hiệu lực.",
"reset_log_3": "Bạn sẽ cần cài đặt lại Cursor AI và bây giờ sẽ có một giai đoạn dùng thử mới.",
"reset_log_4": "Để có kết quả tốt nhất, hãy xem xét:",
"reset_log_5": "Sử dụng một địa chỉ email khác khi đăng ký dùng thử mới",
"reset_log_6": "Nếu có thể, sử dụng VPN để thay đổi địa chỉ IP của bạn",
"reset_log_7": "Xóa cookie và bộ nhớ cache trình duyệt trước khi truy cập trang web Cursor AI",
"reset_log_8": "Nếu vn gặp sự cố, hãy thử cài Cursor AI vào vị trí khác",
"reset_log_9": "Nếu gặp bất kỳ vấn đề nào, hãy truy cập Github Issue Tracker và tạo issue tại https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Đã xảy ra lỗi không mong muốn: {error}",
"report_issue": "Vui lòng báo cáo sự cố này ti Github Issue Tracker: https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Quá trình bị người dùng hủy. Đang thoát...",
"return_to_main_menu": "Trở về menu chính...",
"process_interrupted": "Quá trình bị gián đoạn. Đang thoát...",
"reset_log_8": "Nếu vn đề vẫn còn, hãy thử cài đặt Cursor AI ở một vị trí khác",
"reset_log_9": "Nếu bạn gặp bất kỳ vấn đề nào, hãy truy cập Github Issue Tracker và tạo một vấn đề tại https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Đã xảy ra lỗi không mong đợi: {error}",
"report_issue": "Vui lòng báo cáo vấn đề này ti Github Issue Tracker tại https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Quá trình bị người dùng ngắt. Đang thoát...",
"return_to_main_menu": "Đang trở về menu chính...",
"process_interrupted": "Quá trình bị ngắt. Đang thoát...",
"press_enter_to_return_to_main_menu": "Nhấn Enter để trở về menu chính...",
"removing_known": "Đang xóa các tệp thử nghiệm/giấy phép đã biết",
"performing_deep_scan": "Đang quét sâu để tìm thêm tệp thử nghiệm/giấy phép",
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp thử nghiệm/giấy phép tiềm năng bổ sung",
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp Electron localStorage",
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy thêm tệp thử nghiệm/giấy phép nào trong quá trình quét sâu",
"removing_electron_localstorage_files": "Đang xóa các tệp Electron localStorage",
"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"
"removing_known": "Đang xóa các tệp dùng thử/giấy phép đã biết",
"performing_deep_scan": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp giấy phép/dùng thử tiềm năng bổ sung",
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp localStorage của Electron",
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy tệp giấy phép/dùng thử bổ sung trong quét sâu",
"removing_electron_localstorage_files": "Đang xóa các tệp localStorage của Electron",
"electron_localstorage_files_removed": "Đã xóa các tệp localStorage của Electron",
"electron_localstorage_files_removal_error": "Lỗi xóa các tệp localStorage của Electron: {error}",
"electron_localstorage_files_removal_completed": "Hoàn tất xóa các tệp localStorage của Electron",
"warning_title": "CẢNH BÁO",
"warning_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
"warning_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"warning_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
"warning_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"warning_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"warning_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"warning_7": "Sử dụng với rủi ro của riêng bạn",
"removed": "Đã Xóa: {path}",
"failed_to_reset_machine_guid": "Không thể đặt lại GUID máy",
"failed_to_remove": "Không thể xóa: {path}",
"failed_to_delete_file": "Không thể xóa tệp: {path}",
"failed_to_delete_directory": "Không thể xóa thư mục: {path}",
"failed_to_delete_file_or_directory": "Không thể xóa tệp hoặc thư mục: {path}",
"deep_scanning": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
"resetting_cursor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
"completed_in": "Hoàn thành trong {time} giây",
"cursor_reset_completed": "Trình soạn thảo Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
"cursor_reset_failed": "Đặt lại Trình soạn thảo Cursor AI thất bại: {error}",
"cursor_reset_cancelled": "Đã hủy đặt lại Trình soạn thảo Cursor AI. Thoát mà không thay đổi gì.",
"operation_cancelled": "Đã hủy thao tác. Thoát mà không thay đổi gì.",
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
"already_on_settings": "Đã ở trang cài đặt",
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục"
},
"github_register": {
"title": "Tự Động Hóa Đăng Ký GitHub + Cursor AI",
"features_header": "Tính Năng",
"feature1": "Tạo email tạm thời sử dụng 1secmail.",
"feature2": "Đăng ký tài khoản GitHub mới với thông tin ngẫu nhiên.",
"feature3": "Tự động xác minh email GitHub.",
"feature4": "Đăng nhập vào Cursor AI sử dụng xác thực GitHub.",
"feature5": "Đặt lại ID máy để bỏ qua phát hiện dùng thử.",
"feature6": "Lưu tất cả thông tin đăng nhập vào tệp.",
"warnings_header": "Cảnh Báo",
"warning1": "Script này tự động hóa việc tạo tài khoản, có thể vi phạm điều khoản dịch vụ của GitHub/Cursor.",
"warning2": "Yêu cầu truy cập internet và quyền quản trị.",
"warning3": "CAPTCHA hoặc xác minh bổ sung có thể làm gián đoạn tự động hóa.",
"warning4": "Sử dụng có trách nhiệm và tự chịu rủi ro.",
"confirm": "Bạn có chắc chắn muốn tiếp tục không?",
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'yes' hoặc 'no'",
"cancelled": "Đã hủy thao tác",
"program_terminated": "Chương trình bị người dùng chấm dứt",
"starting_automation": "Bắt đầu tự động hóa...",
"github_username": "Tên Người Dùng GitHub",
"github_password": "Mật Khẩu GitHub",
"email_address": "Địa Chỉ Email",
"credentials_saved": "Các thông tin đăng nhập này đã được lưu vào github_cursor_accounts.txt",
"completed_successfully": "Đăng ký GitHub + Cursor hoàn tất thành công!",
"registration_encountered_issues": "Đăng ký GitHub + Cursor gặp vấn đề.",
"check_browser_windows_for_manual_intervention_or_try_again_later": "Kiểm tra cửa sổ trình duyệt để can thiệp thủ công hoặc thử lại sau."
},
"account_info": {
"subscription": "Gói Đăng Ký",
"trial_remaining": "Thời Gian Dùng Thử Pro Còn Lại",
"days": "ngày",
"subscription_not_found": "Không tìm thấy thông tin đăng ký",
"email_not_found": "Không tìm thấy email",
"failed_to_get_account": "Không thể lấy thông tin tài khoản",
"config_not_found": "Không tìm thấy cấu hình.",
"failed_to_get_usage": "Không thể lấy thông tin sử dụng",
"failed_to_get_subscription": "Không thể lấy thông tin đăng ký",
"failed_to_get_email": "Không thể lấy địa chỉ email",
"failed_to_get_token": "Không thể lấy token",
"failed_to_get_account_info": "Không thể lấy thông tin tài khoản",
"title": "Thông Tin Tài Khoản",
"email": "Email",
"token": "Token",
"usage": "Sử Dụng",
"subscription_type": "Loại Đăng Ký",
"remaining_trial": "Thời Gian Dùng Thử Còn Lại",
"days_remaining": "Số Ngày Còn Lại",
"premium": "Premium",
"pro": "Pro",
"pro_trial": "Dùng Thử Pro",
"team": "Team",
"enterprise": "Enterprise",
"free": "Miễn Phí",
"active": "Đang Hoạt Động",
"inactive": "Không Hoạt Động",
"premium_usage": "Sử Dụng Premium",
"basic_usage": "Sử Dụng Cơ Bản",
"usage_not_found": "Không tìm thấy thông tin sử dụng",
"lifetime_access_enabled": "Đã Bật Truy Cập Trọn Đời",
"token_not_found": "Không tìm thấy token"
},
"config": {
"config_not_available": "Không có sẵn cấu hình",
"configuration": "Cấu Hình",
"enabled": "Đã Bật",
"disabled": "Đã Tắt",
"config_directory": "Thư Mục Cấu Hình",
"neither_cursor_nor_cursor_directory_found": "Không tìm thấy Cursor hoặc thư mục Cursor trong {config_base}",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Vui lòng đảm bảo Cursor đã được cài đặt và đã chạy ít nhất một lần",
"storage_directory_not_found": "Không tìm thấy thư mục lưu trữ: {storage_dir}",
"storage_file_found": "Đã tìm thấy tệp lưu trữ: {storage_path}",
"file_size": "Kích thước tệp: {size} bytes",
"file_permissions": "Quyền tệp: {permissions}",
"file_owner": "Chủ sở hữu tệp: {owner}",
"file_group": "Nhóm tệp: {group}",
"error_getting_file_stats": "Lỗi lấy thông tin tệp: {error}",
"permission_denied": "Từ chối quyền: {storage_path}",
"try_running": "Thử chạy: {command}",
"and": "Và",
"storage_file_is_empty": "Tệp lưu trữ trống: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "Tệp có thể bị hỏng, vui lòng cài đặt lại Cursor",
"storage_file_not_found": "Không tìm thấy tệp lưu trữ: {storage_path}",
"error_checking_linux_paths": "Lỗi kiểm tra đường dẫn Linux: {error}",
"config_option_added": "Đã thêm tùy chọn cấu hình: {option}",
"config_updated": "Đã cập nhật cấu hình",
"config_created": "Đã tạo cấu hình: {config_file}",
"config_setup_error": "Lỗi thiết lập cấu hình: {error}",
"storage_file_is_valid_and_contains_data": "Tệp lưu trữ hợp lệ và chứa dữ liệu",
"error_reading_storage_file": "Lỗi đọc tệp lưu trữ: {error}",
"also_checked": "Cũng đã kiểm tra {path}",
"backup_created": "Đã tạo bản sao lưu: {path}",
"config_removed": "Đã xóa tệp cấu hình để cập nhật bắt buộc",
"backup_failed": "Sao lưu cấu hình thất bại: {error}",
"force_update_failed": "Cập nhật bắt buộc cấu hình thất bại: {error}",
"config_force_update_disabled": "Đã tắt cập nhật bắt buộc tệp cấu hình, bỏ qua cập nhật bắt buộc",
"config_force_update_enabled": "Đã bật cập nhật bắt buộc tệp cấu hình, thực hiện cập nhật bắt buộc"
},
"oauth": {
"authentication_button_not_found": "Không tìm thấy nút xác thực",
"authentication_failed": "Xác thực thất bại: {error}",
"found_cookies": "Đã tìm thấy {count} cookie",
"token_extraction_error": "Lỗi trích xuất token: {error}",
"authentication_successful": "Xác thực thành công - Email: {email}",
"missing_authentication_data": "Thiếu dữ liệu xác thực: {data}",
"failed_to_delete_account": "Không thể xóa tài khoản: {error}",
"invalid_authentication_type": "Loại xác thực không hợp lệ",
"auth_update_success": "Cập nhật xác thực thành công",
"browser_closed": "Đã đóng trình duyệt",
"auth_update_failed": "Cập nhật xác thực thất bại",
"google_start": "Bắt đầu Google",
"github_start": "Bắt đầu Github",
"usage_count": "Số lần sử dụng: {usage}",
"account_has_reached_maximum_usage": "Tài khoản đã đạt số lần sử dụng tối đa, {deleting}",
"starting_new_authentication_process": "Bắt đầu quá trình xác thực mới...",
"failed_to_delete_expired_account": "Không thể xóa tài khoản hết hạn",
"could_not_check_usage_count": "Không thể kiểm tra số lần sử dụng: {error}",
"found_email": "Đã tìm thấy email: {email}",
"could_not_find_email": "Không thể tìm thấy email: {error}",
"could_not_find_usage_count": "Không thể tìm thấy số lần sử dụng: {error}",
"already_on_settings_page": "Đã ở trang cài đặt!",
"failed_to_extract_auth_info": "Không thể trích xuất thông tin xác thực: {error}",
"no_chrome_profiles_found": "Không tìm thấy hồ sơ Chrome, sử dụng Mặc định",
"found_default_chrome_profile": "Đã tìm thấy hồ sơ Chrome Mặc định",
"using_first_available_chrome_profile": "Sử dụng hồ sơ Chrome khả dụng đầu tiên: {profile}",
"error_finding_chrome_profile": "Lỗi tìm hồ sơ Chrome, sử dụng Mặc định: {error}",
"initializing_browser_setup": "Đang khởi tạo thiết lập trình duyệt...",
"detected_platform": "Đã phát hiện nền tảng: {platform}",
"running_as_root_warning": "Chạy với quyền root không được khuyến nghị cho tự động hóa trình duyệt",
"consider_running_without_sudo": "Hãy xem xét chạy script mà không cần sudo",
"no_compatible_browser_found": "Không tìm thấy trình duyệt tương thích. Vui lòng cài đặt Google Chrome hoặc Chromium.",
"supported_browsers": "Trình duyệt được hỗ trợ cho {platform}",
"using_browser_profile": "Đang sử dụng hồ sơ trình duyệt: {profile}",
"starting_browser": "Đang khởi động trình duyệt tại: {path}",
"browser_setup_completed": "Thiết lập trình duyệt hoàn tất thành công",
"browser_setup_failed": "Thiết lập trình duyệt thất bại: {error}",
"try_running_without_sudo_admin": "Thử chạy mà không cần quyền sudo/quản trị",
"redirecting_to_authenticator_cursor_sh": "Đang chuyển hướng đến authenticator.cursor.sh...",
"starting_google_authentication": "Bắt đầu xác thực Google...",
"starting_github_authentication": "Bắt đầu xác thực GitHub...",
"waiting_for_authentication": "Đang chờ xác thực...",
"page_changed_checking_auth": "Trang đã thay đổi, đang kiểm tra xác thực...",
"status_check_error": "Lỗi kiểm tra trạng thái: {error}",
"authentication_timeout": "Hết thời gian xác thực",
"account_is_still_valid": "Tài khoản vẫn còn hợp lệ (Sử dụng: {usage})",
"starting_re_authentication_process": "Bắt đầu quá trình xác thực lại...",
"starting_new_google_authentication": "Bắt đầu xác thực Google mới...",
"failed_to_delete_account_or_re_authenticate": "Không thể xóa tài khoản hoặc xác thực lại: {error}",
"navigating_to_authentication_page": "Đang điều hướng đến trang xác thực...",
"please_select_your_google_account_to_continue": "Vui lòng chọn tài khoản Google của bạn để tiếp tục...",
"found_browser_data_directory": "Đã tìm thấy thư mục dữ liệu trình duyệt: {path}",
"authentication_successful_getting_account_info": "Xác thực thành công, đang lấy thông tin tài khoản...",
"warning_could_not_kill_existing_browser_processes": "Cảnh báo: Không thể kết thúc các tiến trình trình duyệt hiện có: {error}",
"browser_failed_to_start": "Trình duyệt không thể khởi động: {error}",
"browser_failed": "Trình duyệt không thể khởi động: {error}",
"browser_failed_to_start_fallback": "Trình duyệt không thể khởi động: {error}"
},
"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:",
"select_profile": "Chọn một hồ sơ Chrome để sử dụng:",
"profile_list": "Hồ sơ khả dụng:",
"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}",
"error_loading": "Lỗi 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"
},
"account_delete": {
"title": "Công Cụ Xóa Tài Khoản Google Cursor",
"warning": "CẢNH BÁO: Điều này sẽ xóa vĩnh viễn tài khoản Cursor của bạn. Hành động này không thể hoàn tác.",
"cancelled": "Đã hủy xóa tài khoản.",
"starting_process": "Bắt đầu quá trình xóa tài khoản...",
"google_button_not_found": "Không tìm thấy nút đăng nhập Google",
"logging_in": "Đang đăng nhập bằng Google...",
"waiting_for_auth": "Đang chờ xác thực Google...",
"login_successful": "Đăng nhập thành công",
"unexpected_page": "Trang không mong đợi sau khi đăng nhập: {url}",
"trying_settings": "Đang thử điều hướng đến trang cài đặt...",
"select_google_account": "Vui lòng chọn tài khoản Google của bạn...",
"auth_timeout": "Hết thời gian xác thực, vẫn tiếp tục...",
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
"already_on_settings": "Đã ở trang cài đặt",
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục",
"typed_delete": "Đã nhập \"Delete\" vào ô xác nhận",
"confirm_button_not_found": "Không tìm thấy nút Xác nhận sau nhiều lần thử",
"confirm_button_retry": "Không tìm thấy nút Xác nhận, lần thử {attempt}/{max_attempts}",
"confirm_button_error": "Lỗi tìm nút Xác nhận: {error}",
"account_deleted": "Đã xóa tài khoản thành công!",
"error": "Lỗi trong quá trình xóa tài khoản: {error}",
"success": "Tài khoản Cursor của bạn đã được xóa thành công!",
"failed": "Quá trình xóa tài khoản thất bại hoặc đã bị hủy.",
"interrupted": "Quá trình xóa tài khoản bị người dùng ngắt.",
"unexpected_error": "Lỗi không mong đợi: {error}",
"found_email": "Đã tìm thấy email: {email}",
"email_not_found": "Không tìm thấy email: {error}",
"confirm_prompt": "Bạn có chắc chắn muốn tiếp tục không? (y/N): "
},
"bypass": {
"starting": "Bắt đầu bỏ qua phiên bản Cursor...",
"found_product_json": "Đã tìm thấy product.json: {path}",
"no_write_permission": "Không có quyền ghi cho tệp: {path}",
"read_failed": "Không thể đọc product.json: {error}",
"current_version": "Phiên bản hiện tại: {version}",
"backup_created": "Đã tạo bản sao lưu: {path}",
"version_updated": "Đã cập nhật phiên bản từ {old} lên {new}",
"write_failed": "Không thể ghi product.json: {error}",
"no_update_needed": "Không cần cập nhật. Phiên bản hiện tại {version} đã >= 0.46.0",
"bypass_failed": "Bỏ qua phiên bản thất bại: {error}",
"stack_trace": "Dấu vết ngăn xếp",
"localappdata_not_found": "Không tìm thấy biến môi trường LOCALAPPDATA",
"product_json_not_found": "Không tìm thấy product.json trong các đường dẫn Linux thông thường",
"unsupported_os": "Hệ điều hành không được hỗ trợ: {system}",
"file_not_found": "Không tìm thấy tệp: {path}",
"title": "Công Cụ Bỏ Qua Phiên Bản Cursor",
"description": "Công cụ này sửa đổi product.json của Cursor để bỏ qua hạn chế phiên bản",
"menu_option": "Bỏ Qua Kiểm Tra Phiên Bản Cursor"
},
"auth_check": {
"checking_authorization": "Đang kiểm tra quyền...",
"token_source": "Lấy token từ cơ sở dữ liệu hay nhập thủ công? (d/m, mặc định: d)",
"getting_token_from_db": "Đang lấy token từ cơ sở dữ liệu...",
"token_found_in_db": "Đã tìm thấy token trong cơ sở dữ liệu",
"token_not_found_in_db": "Không tìm thấy token trong cơ sở dữ liệu",
"cursor_acc_info_not_found": "Không tìm thấy cursor_acc_info.py",
"error_getting_token_from_db": "Lỗi lấy token từ cơ sở dữ liệu: {error}",
"enter_token": "Nhập token Cursor của bạn: ",
"token_length": "Độ dài token: {length} ký tự",
"usage_response_status": "Trạng thái phản hồi sử dụng: {response}",
"unexpected_status_code": "Mã trạng thái không mong đợi: {code}",
"jwt_token_warning": "Token có vẻ ở định dạng JWT, nhưng kiểm tra API trả về mã trạng thái không mong đợi. Token có thể hợp lệ nhưng truy cập API bị hạn chế.",
"invalid_token": "Token không hợp lệ",
"user_authorized": "Người dùng được ủy quyền",
"user_unauthorized": "Người dùng không được ủy quyền",
"request_timeout": "Yêu cầu hết thời gian",
"connection_error": "Lỗi kết nối",
"check_error": "Lỗi kiểm tra quyền: {error}",
"authorization_successful": "Ủy quyền thành công!",
"authorization_failed": "Ủy quyền thất bại!",
"operation_cancelled": "Thao tác bị người dùng hủy",
"unexpected_error": "Lỗi không mong đợi: {error}",
"error_generating_checksum": "Lỗi tạo checksum: {error}",
"checking_usage_information": "Đang kiểm tra thông tin sử dụng...",
"check_usage_response": "Phản hồi kiểm tra sử dụng: {response}",
"usage_response": "Phản hồi sử dụng: {response}"
},
"restore": {
"title": "Khôi phục ID máy từ bản sao lưu",
"starting": "Bắt đầu quá trình khôi phục ID máy",
"no_backups_found": "Không tìm thấy bản sao lưu nào",
"available_backups": "Các bản sao lưu có sẵn",
"select_backup": "Chọn bản sao lưu để khôi phục",
"to_cancel": "để hủy",
"operation_cancelled": "Đã hủy thao tác",
"invalid_selection": "Lựa chọn không hợp lệ",
"please_enter_number": "Vui lòng nhập một số hợp lệ",
"missing_id": "Thiếu ID: {id}",
"read_backup_failed": "Không thể đọc tệp sao lưu: {error}",
"current_file_not_found": "Không tìm thấy tệp lưu trữ hiện tại",
"current_backup_created": "Đã tạo bản sao lưu của tệp lưu trữ hiện tại",
"storage_updated": "Tệp lưu trữ đã được cập nhật thành công",
"update_failed": "Không thể cập nhật tệp lưu trữ: {error}",
"sqlite_not_found": "Không tìm thấy cơ sở dữ liệu SQLite",
"updating_sqlite": "Đang cập nhật cơ sở dữ liệu SQLite",
"updating_pair": "Đang cập nhật cặp khóa-giá trị",
"sqlite_updated": "Cơ sở dữ liệu SQLite đã được cập nhật thành công",
"sqlite_update_failed": "Không thể cập nhật cơ sở dữ liệu SQLite: {error}",
"machine_id_backup_created": "Đã tạo bản sao lưu của tệp machineId",
"backup_creation_failed": "Không thể tạo bản sao lưu: {error}",
"machine_id_updated": "Tệp machineId đã được cập nhật thành công",
"machine_id_update_failed": "Không thể cập nhật tệp machineId: {error}",
"updating_system_ids": "Đang cập nhật ID hệ thống",
"system_ids_update_failed": "Không thể cập nhật ID hệ thống: {error}",
"permission_denied": "Quyền truy cập bị từ chối. Vui lòng thử chạy với quyền quản trị",
"windows_machine_guid_updated": "GUID máy Windows đã được cập nhật thành công",
"update_windows_machine_guid_failed": "Không thể cập nhật GUID máy Windows: {error}",
"windows_machine_id_updated": "ID máy Windows đã được cập nhật thành công",
"update_windows_machine_id_failed": "Không thể cập nhật ID máy Windows: {error}",
"sqm_client_key_not_found": "Không tìm thấy khóa đăng ký SQMClient",
"update_windows_system_ids_failed": "Không thể cập nhật ID hệ thống Windows: {error}",
"macos_platform_uuid_updated": "UUID nền tảng macOS đã được cập nhật thành công",
"failed_to_execute_plutil_command": "Không thể thực thi lệnh plutil",
"update_macos_system_ids_failed": "Không thể cập nhật ID hệ thống macOS: {error}",
"ids_to_restore": "ID máy cần khôi phục",
"confirm": "Bạn có chắc chắn muốn khôi phục những ID này không?",
"success": "ID máy đã được khôi phục thành công",
"process_error": "Lỗi quá trình khôi phục: {error}",
"press_enter": "Nhấn Enter để tiếp tục"
}
}

View File

@@ -4,8 +4,8 @@
"exit": "退出程序",
"reset": "重置机器ID",
"register": "注册新的Cursor账户",
"register_google": "使用Google账户注册",
"register_github": "使用GitHub账户注册",
"register_google": "使用自己的Google账户注册",
"register_github": "使用自己的GitHub账户注册",
"register_manual": "使用自定义邮箱注册Cursor",
"quit": "关闭Cursor应用",
"select_language": "更改语言",
@@ -30,9 +30,15 @@
"continue_prompt": "继续?(y/N): ",
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查"
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权",
"bypass_token_limit": "绕过 Token 限制",
"language_config_saved": "语言配置保存成功",
"lang_invalid_choice": "选择无效。请输入以下选项之一:({lang_choices})",
"restore_machine_id": "从备份恢复机器ID"
},
"languages": {
"ar": "阿拉伯语",
"en": "英语",
"zh_cn": "简体中文",
"zh_tw": "繁体中文",
@@ -117,7 +123,8 @@
"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}"
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}",
"file_not_found": "文件未找到: {path}"
},
"register": {
"title": "Cursor 注册工具",
@@ -194,7 +201,17 @@
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}",
"setting_on_password": "设置密码",
"getting_code": "获取验证码将在60秒内尝试..."
"getting_code": "获取验证码将在60秒内尝试...",
"browser_path_invalid": "{browser} 路径无效,使用默认路径",
"using_browser": "正在使用 {browser} 浏览器: {path}",
"using_browser_profile": "使用 {browser} 配置文件: {user_data_dir}",
"make_sure_browser_is_properly_installed": "确保 {browser} 已正确安装",
"try_install_browser": "尝试使用包管理器安装浏览器",
"tracking_processes": "正在跟踪 {count} 个 {browser} 进程",
"no_new_processes_detected": "未检测到新的 {browser} 进程",
"could_not_track_processes": "无法跟踪 {browser} 进程: {error}",
"human_verify_error": "无法验证用户是人类,正在重试...",
"max_retries_reached": "已达到最大重试次数,注册失败。"
},
"auth": {
"title": "Cursor 认证管理器",
@@ -533,7 +550,16 @@
"config_setup_error": "配置设置错误",
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
"error_reading_storage_file": "读取存储文件时出错",
"also_checked": "也检查了 {path}"
"also_checked": "也检查了 {path}",
"backup_created": "备份创建: {path}",
"config_removed": "配置文件已删除用于强制更新",
"backup_failed": "备份失败: {error}",
"force_update_failed": "强制更新配置失败: {error}",
"config_force_update_disabled": "配置文件强制更新已禁用,跳过强制更新",
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新",
"documents_path_not_found": "找不到文档路径,使用当前目录",
"config_dir_created": "已创建配置目录: {path}",
"using_temp_dir": "由于错误使用临时目录: {path} (错误: {error})"
},
"oauth": {
"authentication_button_not_found": "未找到认证按钮",
@@ -592,19 +618,29 @@
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
"browser_failed_to_start": "浏览器启动失败: {error}",
"browser_failed": "浏览器启动失败: {error}",
"browser_failed_to_start_fallback": "浏览器启动失败: {error}"
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
"user_data_dir_not_found": "{browser} 用户数据目录未找到:{path},将尝试使用 Chrome",
"error_getting_user_data_directory": "获取用户数据目录出错:{error}",
"warning_browser_close": "警告:这将关闭所有正在运行的 {browser} 进程",
"killing_browser_processes": "正在关闭 {browser} 进程...",
"profile_selection_error": "配置文件选择过程中出错: {error}",
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
"found_chrome_at": "找到 Chrome: {path}",
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}",
"select_profile": "选择要使用的 {browser} 配置文件:",
"profile_list": "可用 {browser} 配置文件:"
},
"chrome_profile": {
"title": "Chrome配置文件选择",
"select_profile": "选择要使用的Chrome配置文件:",
"profile_list": "可用配置文件:",
"browser_profile": {
"title": "浏览器配置文件选择",
"select_profile": "选择要使用的{browser}配置文件:",
"profile_list": "可用{browser}配置文件:",
"default_profile": "默认配置文件",
"profile": "配置文件 {number}",
"no_profiles": "未找到Chrome配置文件",
"error_loading": "加载Chrome配置文件时出错:{error}",
"no_profiles": "未找到{browser}配置文件",
"error_loading": "加载{browser}配置文件时出错:{error}",
"profile_selected": "已选择配置文件:{profile}",
"invalid_selection": "选择无效。请重试",
"warning_chrome_close": "警告这将关闭所有正在运行的Chrome进程"
"invalid_selection": "选择无效。请重试"
},
"account_delete": {
"title": "Cursor Google 账号删除工具",
@@ -669,5 +705,93 @@
"title": "Cursor 版本绕过工具",
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
"menu_option": "绕过 Cursor 版本检查"
},
"auth_check": {
"checking_authorization": "检查授权...",
"token_source": "从数据库获取 token 或手动输入d/m, 默认: d",
"getting_token_from_db": "从数据库获取 token...",
"token_found_in_db": "在数据库中找到 token",
"token_not_found_in_db": "在数据库中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
"enter_token": "请输入您的 Cursor token: ",
"token_length": "token 长度: {length}",
"usage_response_status": "使用情况响应状态: {response}",
"unexpected_status_code": "意外状态码: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
"invalid_token": "无效的 token",
"user_authorized": "用户已授权",
"user_unauthorized": "用户未授权",
"request_timeout": "请求超时",
"connection_error": "连接错误",
"check_error": "检查授权时出错: {error}",
"authorization_successful": "授权成功",
"authorization_failed": "授权失败",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外错误: {error}",
"error_generating_checksum": "生成校验和时出错: {error}",
"checking_usage_information": "检查使用情况...",
"check_usage_response": "检查使用情况响应: {response}",
"usage_response": "使用情况响应: {response}"
},
"bypass_token_limit": {
"title": "绕过 Token 限制工具",
"description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制",
"press_enter": "按回车键继续..."
},
"token": {
"refreshing": "正在刷新令牌...",
"refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire}",
"no_access_token": "响应中没有访问令牌",
"refresh_failed": "令牌刷新失败: {error}",
"invalid_response": "刷新服务器返回无效的 JSON 响应",
"server_error": "刷新服务器错误: HTTP {status}",
"request_timeout": "刷新服务器请求超时",
"connection_error": "连接刷新服务器错误",
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
"extraction_error": "提取令牌时出错: {error}"
},
"restore": {
"title": "从备份恢复机器ID",
"starting": "正在启动机器ID恢复进程",
"no_backups_found": "未找到备份文件",
"available_backups": "可用的备份文件",
"select_backup": "选择要恢复的备份",
"to_cancel": "取消操作",
"operation_cancelled": "操作已取消",
"invalid_selection": "选择无效",
"please_enter_number": "请输入有效的数字",
"missing_id": "缺少ID: {id}",
"read_backup_failed": "读取备份文件失败: {error}",
"current_file_not_found": "未找到当前存储文件",
"current_backup_created": "已创建当前存储文件的备份",
"storage_updated": "存储文件已成功更新",
"update_failed": "更新存储文件失败: {error}",
"sqlite_not_found": "未找到SQLite数据库",
"updating_sqlite": "正在更新SQLite数据库",
"updating_pair": "正在更新键值对",
"sqlite_updated": "SQLite数据库已成功更新",
"sqlite_update_failed": "更新SQLite数据库失败: {error}",
"machine_id_backup_created": "已创建machineId文件的备份",
"backup_creation_failed": "创建备份失败: {error}",
"machine_id_updated": "machineId文件已成功更新",
"machine_id_update_failed": "更新machineId文件失败: {error}",
"updating_system_ids": "正在更新系统ID",
"system_ids_update_failed": "更新系统ID失败: {error}",
"permission_denied": "权限被拒绝。请尝试以管理员身份运行",
"windows_machine_guid_updated": "Windows机器GUID已成功更新",
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}",
"windows_machine_id_updated": "Windows机器ID已成功更新",
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
"sqm_client_key_not_found": "未找到SQMClient注册表项",
"update_windows_system_ids_failed": "更新Windows系统ID失败: {error}",
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
"failed_to_execute_plutil_command": "执行plutil命令失败",
"update_macos_system_ids_failed": "更新macOS系统ID失败: {error}",
"ids_to_restore": "要恢复的机器ID",
"confirm": "您确定要恢复这些ID吗",
"success": "机器ID已成功恢复",
"process_error": "恢复过程错误: {error}",
"press_enter": "按Enter键继续"
}
}
}

View File

@@ -4,8 +4,8 @@
"exit": "退出程式",
"reset": "重置機器ID",
"register": "註冊新的Cursor帳戶",
"register_google": "使用Google帳戶註冊",
"register_github": "使用GitHub帳戶註冊",
"register_google": "使用自己的Google帳戶註冊",
"register_github": "使用自己的GitHub帳戶註冊",
"register_manual": "使用自定義郵箱註冊Cursor",
"quit": "關閉Cursor應用",
"select_language": "更改語言",
@@ -30,9 +30,15 @@
"continue_prompt": "繼續?(y/N): ",
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查"
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權",
"bypass_token_limit": "繞過 Token 限制",
"language_config_saved": "語言配置保存成功",
"lang_invalid_choice": "選擇無效。請輸入以下選項之一:({lang_choices})",
"restore_machine_id": "從備份恢復機器ID"
},
"languages": {
"ar": "阿拉伯語",
"en": "英文",
"zh_cn": "簡體中文",
"zh_tw": "繁體中文",
@@ -117,9 +123,9 @@
"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}"
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
"file_not_found": "文件未找到: {path}"
},
"register": {
"title": "Cursor 註冊工具",
"start": "正在啟動註冊流程...",
@@ -146,6 +152,22 @@
"no_turnstile": "未檢測到 Turnstile 驗證",
"turnstile_passed": "驗證通過",
"verification_start": "開始獲取驗證碼",
"verification_timeout": "獲取驗證碼超時",
"verification_not_found": "未找到驗證碼",
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
"get_account": "獲取帳戶信息",
"get_token": "獲取 Cursor Session Token",
"token_success": "Token 獲取成功",
"token_attempt": "第 {attempt} 次嘗試未獲取到 Token{time}秒後重試",
"token_max_attempts": "已達到最大嘗試次數({max}),獲取 Token 失敗",
"token_failed": "獲取 Token 失敗: {error}",
"account_error": "獲取帳戶信息失敗: {error}",
"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}秒",
"verification_code_filled": "驗證碼填寫完成",
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
"detect_login_page": "檢測到登錄頁面,開始登錄...",
@@ -515,7 +537,13 @@
"config_setup_error": "配置設置錯誤",
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
"error_reading_storage_file": "讀取儲存文件時出錯",
"also_checked": "也檢查了 {path}"
"also_checked": "也檢查了 {path}",
"backup_created": "備份已創建: {path}",
"config_removed": "配置文件已刪除用於強制更新",
"backup_failed": "備份失敗: {error}",
"force_update_failed": "強制更新配置失敗: {error}",
"config_force_update_disabled": "配置文件強制更新已禁用,跳過強制更新",
"config_force_update_enabled": "配置文件強制更新已啟用,正在執行強制更新"
},
"oauth": {
"authentication_button_not_found": "未找到認證按鈕",
@@ -573,7 +601,18 @@
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
"browser_failed": "瀏覽器啟動失敗: {error}",
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}"
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}",
"using_configured_browser_path": "使用配置的 {browser} 路徑: {path}",
"found_browser_user_data_dir": "找到 {browser} 用戶數據目錄: {path}",
"warning_browser_close": "警告:這將關閉所有正在執行的 {browser} 進程",
"killing_browser_processes": "正在關閉 {browser} 進程...",
"profile_selection_error": "配置文件選擇過程中出錯: {error}",
"select_profile": "選擇要使用的 {browser} 配置文件:",
"profile_list": "可用 {browser} 配置文件:",
"no_profiles": "未找到 {browser} 配置文件",
"error_loading": "載入 {browser} 配置文件時出錯:{error}",
"profile_selected": "已選擇配置文件:{profile}",
"invalid_selection": "選擇無效。請重試"
},
"chrome_profile": {
"title": "Chrome配置檔案選擇",
@@ -651,5 +690,81 @@
"title": "Cursor 版本繞過工具",
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
"menu_option": "繞過 Cursor 版本檢查"
},
"auth_check": {
"checking_authorization": "檢查授權...",
"token_source": "從資料庫獲取 token 或手動輸入d/m, 預設: d",
"getting_token_from_db": "從資料庫獲取 token...",
"token_found_in_db": "在資料庫中找到 token",
"token_not_found_in_db": "在資料庫中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"usage_response_status": "使用情況響應狀態: {response}",
"unexpected_status_code": "意外狀態碼: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
"enter_token": "請輸入您的 Cursor token: ",
"token_length": "token 長度: {length}",
"invalid_token": "無效的 token",
"user_authorized": "用戶已授權",
"user_unauthorized": "用戶未授權",
"request_timeout": "請求超時",
"connection_error": "連接錯誤",
"check_error": "檢查授權時出錯: {error}",
"authorization_successful": "授權成功",
"authorization_failed": "授權失敗",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外錯誤: {error}",
"error_generating_checksum": "生成校驗和時出錯: {error}",
"checking_usage_information": "檢查使用情況...",
"check_usage_response": "檢查使用情況響應: {response}",
"usage_response": "使用情況響應: {response}"
},
"bypass_token_limit": {
"title": "繞過 Token 限制工具",
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
"press_enter": "按回車鍵繼續..."
},
"restore": {
"title": "從備份恢復機器ID",
"starting": "正在啟動機器ID恢復進程",
"no_backups_found": "未找到備份文件",
"available_backups": "可用的備份文件",
"select_backup": "選擇要恢復的備份",
"to_cancel": "取消操作",
"operation_cancelled": "操作已取消",
"invalid_selection": "選擇無效",
"please_enter_number": "請輸入有效的數字",
"missing_id": "缺少ID: {id}",
"read_backup_failed": "讀取備份文件失敗: {error}",
"current_file_not_found": "未找到當前存儲文件",
"current_backup_created": "已創建當前存儲文件的備份",
"storage_updated": "存儲文件已成功更新",
"update_failed": "更新存儲文件失敗: {error}",
"sqlite_not_found": "未找到SQLite數據庫",
"updating_sqlite": "正在更新SQLite數據庫",
"updating_pair": "正在更新鍵值對",
"sqlite_updated": "SQLite數據庫已成功更新",
"sqlite_update_failed": "更新SQLite數據庫失敗: {error}",
"machine_id_backup_created": "已創建machineId文件的備份",
"backup_creation_failed": "創建備份失敗: {error}",
"machine_id_updated": "machineId文件已成功更新",
"machine_id_update_failed": "更新machineId文件失敗: {error}",
"updating_system_ids": "正在更新系統ID",
"system_ids_update_failed": "更新系統ID失敗: {error}",
"permission_denied": "權限被拒絕。請嘗試以管理員身份運行",
"windows_machine_guid_updated": "Windows機器GUID已成功更新",
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
"windows_machine_id_updated": "Windows機器ID已成功更新",
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
"sqm_client_key_not_found": "未找到SQMClient註冊表項",
"update_windows_system_ids_failed": "更新Windows系統ID失敗: {error}",
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
"failed_to_execute_plutil_command": "執行plutil命令失敗",
"update_macos_system_ids_failed": "更新macOS系統ID失敗: {error}",
"ids_to_restore": "要恢復的機器ID",
"confirm": "您確定要恢復這些ID嗎",
"success": "機器ID已成功恢復",
"process_error": "恢復過程錯誤: {error}",
"press_enter": "按Enter鍵繼續"
}
}
}

View File

@@ -83,7 +83,7 @@ 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}"""
Press 4 to change language | 按下 4 键切换语言{Style.RESET_ALL}"""
# center display LOGO and DESCRIPTION
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)

541
main.py
View File

@@ -9,9 +9,18 @@ import locale
import platform
import requests
import subprocess
from config import get_config
from config import get_config, force_update_config
import shutil
import re
from utils import get_user_documents_path
# Add these imports for Arabic support
try:
import arabic_reshaper
from bidi.algorithm import get_display
except ImportError:
arabic_reshaper = None
get_display = None
# Only import windll on Windows systems
if platform.system() == 'Windows':
@@ -79,8 +88,37 @@ def run_as_admin():
class Translator:
def __init__(self):
self.translations = {}
self.current_language = self.detect_system_language() # Use correct method name
self.fallback_language = 'en' # Fallback language if translation is missing
self.config = get_config()
# Create language cache directory if it doesn't exist
if self.config and self.config.has_section('Language'):
self.language_cache_dir = self.config.get('Language', 'language_cache_dir')
os.makedirs(self.language_cache_dir, exist_ok=True)
else:
self.language_cache_dir = None
# Set fallback language from config if available
self.fallback_language = 'en'
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'fallback_language'):
self.fallback_language = self.config.get('Language', 'fallback_language')
# Load saved language from config if available, otherwise detect system language
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'current_language'):
saved_language = self.config.get('Language', 'current_language')
if saved_language and saved_language.strip():
self.current_language = saved_language
else:
self.current_language = self.detect_system_language()
# Save detected language to config
if self.config.has_section('Language'):
self.config.set('Language', 'current_language', self.current_language)
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
with open(config_file, 'w', encoding='utf-8') as f:
self.config.write(f)
else:
self.current_language = self.detect_system_language()
self.load_translations()
def detect_system_language(self):
@@ -110,18 +148,26 @@ class Translator:
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
# Map language ID to our language codes
language_map = {
0x0409: 'en', # English
0x0404: 'zh_tw', # Traditional Chinese
0x0804: 'zh_cn', # Simplified Chinese
0x0422: 'vi', # Vietnamese
0x0419: 'ru', # Russian
0x0415: 'tr', # Turkish
0x0402: 'bg', # Bulgarian
}
return language_map.get(layout_id, 'en')
# Map language ID to our language codes using match-case
match layout_id:
case 0x0409:
return 'en' # English
case 0x0404:
return 'zh_tw' # Traditional Chinese
case 0x0804:
return 'zh_cn' # Simplified Chinese
case 0x0422:
return 'vi' # Vietnamese
case 0x0419:
return 'ru' # Russian
case 0x0415:
return 'tr' # Turkish
case 0x0402:
return 'bg' # Bulgarian
case 0x0401:
return 'ar' # Arabic
case _:
return 'en' # Default to English
except:
return self._detect_unix_language()
@@ -129,85 +175,123 @@ class Translator:
"""Detect language on Unix-like systems (Linux, macOS)"""
try:
# Get the system locale
system_locale = locale.getdefaultlocale()[0]
locale.setlocale(locale.LC_ALL, '')
system_locale = locale.getlocale()[0]
if not system_locale:
return 'en'
system_locale = system_locale.lower()
# Map locale to our language codes
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
return 'zh_tw'
elif system_locale.startswith('zh_cn'):
return 'zh_cn'
elif system_locale.startswith('en'):
return 'en'
elif system_locale.startswith('vi'):
return 'vi'
elif system_locale.startswith('nl'):
return 'nl'
elif system_locale.startswith('de'):
return 'de'
elif system_locale.startswith('fr'):
return 'fr'
elif system_locale.startswith('pt'):
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:
return 'zh_tw'
elif 'cn' in env_lang:
return 'zh_cn'
elif 'vi' in env_lang:
return 'vi'
elif 'nl' in env_lang:
return 'nl'
elif 'de' in env_lang:
return 'de'
elif 'fr' in env_lang:
return 'fr'
elif 'pt' in env_lang:
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'
# Map locale to our language codes using match-case
match system_locale:
case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
return 'zh_tw'
case s if s.startswith('zh_cn'):
return 'zh_cn'
case s if s.startswith('en'):
return 'en'
case s if s.startswith('vi'):
return 'vi'
case s if s.startswith('nl'):
return 'nl'
case s if s.startswith('de'):
return 'de'
case s if s.startswith('fr'):
return 'fr'
case s if s.startswith('pt'):
return 'pt'
case s if s.startswith('ru'):
return 'ru'
case s if s.startswith('tr'):
return 'tr'
case s if s.startswith('bg'):
return 'bg'
case s if s.startswith('ar'):
return 'ar'
case _:
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
match env_lang:
case s if 'tw' in s or 'hk' in s:
return 'zh_tw'
case s if 'cn' in s:
return 'zh_cn'
case s if 'vi' in s:
return 'vi'
case s if 'nl' in s:
return 'nl'
case s if 'de' in s:
return 'de'
case s if 'fr' in s:
return 'fr'
case s if 'pt' in s:
return 'pt'
case s if 'ru' in s:
return 'ru'
case s if 'tr' in s:
return 'tr'
case s if 'bg' in s:
return 'bg'
case s if 'ar' in s:
return 'ar'
case _:
return 'en'
except:
return 'en'
def load_translations(self):
"""Load all available translations"""
try:
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
if hasattr(sys, '_MEIPASS'):
locales_dir = os.path.join(sys._MEIPASS, 'locales')
def download_language_file(self, lang_code):
"""Method kept for compatibility but now returns False as language files are integrated"""
print(f"{Fore.YELLOW}{EMOJI['INFO']} Languages are now integrated into the package, no need to download.{Style.RESET_ALL}")
return False
if not os.path.exists(locales_dir):
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
return
def load_translations(self):
"""Load all available translations from the integrated package"""
try:
# Collection of languages we've successfully loaded
loaded_languages = set()
locales_paths = []
# Check for PyInstaller bundle first
if hasattr(sys, '_MEIPASS'):
locales_paths.append(os.path.join(sys._MEIPASS, 'locales'))
# Check script directory next
script_dir = os.path.dirname(os.path.abspath(__file__))
locales_paths.append(os.path.join(script_dir, 'locales'))
# Also check current working directory
locales_paths.append(os.path.join(os.getcwd(), 'locales'))
for locales_dir in locales_paths:
if os.path.exists(locales_dir) and os.path.isdir(locales_dir):
for file in os.listdir(locales_dir):
if file.endswith('.json'):
lang_code = file[:-5] # Remove .json
try:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
loaded_languages.add(lang_code)
loaded_any = True
except (json.JSONDecodeError, UnicodeDecodeError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
continue
for file in os.listdir(locales_dir):
if file.endswith('.json'):
lang_code = file[:-5] # Remove .json
try:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
except (json.JSONDecodeError, UnicodeDecodeError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
continue
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to load translations: {e}{Style.RESET_ALL}")
# Create at least minimal English translations for basic functionality
self.translations['en'] = {"menu": {"title": "Menu", "exit": "Exit", "invalid_choice": "Invalid choice"}}
def fix_arabic(self, text):
if self.current_language == 'ar' and arabic_reshaper and get_display:
try:
reshaped_text = arabic_reshaper.reshape(text)
bidi_text = get_display(reshaped_text)
return bidi_text
except Exception:
return text
return text
def get(self, key, **kwargs):
"""Get translated text with fallback support"""
try:
@@ -216,7 +300,8 @@ class Translator:
if result == key and self.current_language != self.fallback_language:
# Try fallback language if translation not found
result = self._get_translation(self.fallback_language, key)
return result.format(**kwargs) if kwargs else result
formatted = result.format(**kwargs) if kwargs else result
return self.fix_arabic(formatted)
except Exception:
return key
@@ -243,7 +328,11 @@ class Translator:
def get_available_languages(self):
"""Get list of available languages"""
return list(self.translations.keys())
# Get currently loaded languages
available_languages = list(self.translations.keys())
# Sort languages alphabetically for better display
return sorted(available_languages)
# Create translator instance
translator = Translator()
@@ -274,20 +363,21 @@ def print_menu():
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')} ({Fore.RED}{translator.get('menu.outdate')}{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')} ({Fore.RED}{translator.get('menu.outdate')}{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')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}"
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check')}",
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized')}",
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit')}",
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['BACKUP']} {translator.get('menu.restore_machine_id')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}"
}
# Automatically calculate the number of menu items in the left and right columns
@@ -364,21 +454,45 @@ def select_language():
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
# Get available languages either from local directory or GitHub
languages = translator.get_available_languages()
languages_count = len(languages)
# Display all available languages with proper indices
for i, lang in enumerate(languages):
lang_name = translator.get(f"languages.{lang}")
lang_name = translator.get(f"languages.{lang}", fallback=lang)
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_name}")
try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{len(languages)-1}')}: {Style.RESET_ALL}")
if choice.isdigit() and 0 <= int(choice) < len(languages):
translator.set_language(languages[int(choice)])
# Use the actual number of languages in the prompt
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{languages_count-1}')}: {Style.RESET_ALL}")
if choice.isdigit() and 0 <= int(choice) < languages_count:
selected_language = languages[int(choice)]
translator.set_language(selected_language)
# Save selected language to config
config = get_config()
if config and config.has_section('Language'):
config.set('Language', 'current_language', selected_language)
# Get config path from user documents
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
# Write updated config
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('menu.language_config_saved', language=translator.get(f'languages.{selected_language}', fallback=selected_language))}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
# Show invalid choice message with the correct range
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
return False
except (ValueError, IndexError):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
except (ValueError, IndexError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
return False
def check_latest_version():
@@ -386,31 +500,67 @@ def check_latest_version():
try:
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
# Get latest version from GitHub API with timeout and proper headers
# First try GitHub API
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'CursorFreeVIP-Updater'
}
response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
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
latest_version = None
github_error = None
# Check if response is successful
if response.status_code != 200:
raise Exception(f"GitHub API returned status code {response.status_code}")
# Try GitHub API first
try:
github_response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
timeout=10
)
response_data = response.json()
if "tag_name" not in response_data:
raise Exception("No version tag found in GitHub response")
# Check if rate limit exceeded
if github_response.status_code == 403 and "rate limit exceeded" in github_response.text.lower():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Trying backup API...')}{Style.RESET_ALL}")
raise Exception("Rate limit exceeded")
# Check if response is successful
if github_response.status_code != 200:
raise Exception(f"GitHub API returned status code {github_response.status_code}")
github_data = github_response.json()
if "tag_name" not in github_data:
raise Exception("No version tag found in GitHub response")
latest_version = github_data["tag_name"].lstrip('v')
latest_version = response_data["tag_name"].lstrip('v')
except Exception as e:
github_error = str(e)
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.github_api_failed', fallback='GitHub API failed, trying backup API...')}{Style.RESET_ALL}")
# If GitHub API fails, try backup API
try:
backup_headers = {
'Accept': 'application/json',
'User-Agent': 'CursorFreeVIP-Updater'
}
backup_response = requests.get(
"https://pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip",
headers=backup_headers,
timeout=10
)
# Check if response is successful
if backup_response.status_code != 200:
raise Exception(f"Backup API returned status code {backup_response.status_code}")
backup_data = backup_response.json()
if "message" not in backup_data:
raise Exception("No version tag found in backup API response")
latest_version = backup_data["message"].lstrip('v')
except Exception as backup_e:
# If both APIs fail, raise the original GitHub error
raise Exception(f"Both APIs failed. GitHub error: {github_error}, Backup error: {str(backup_e)}")
# Validate version format
if not latest_version:
@@ -552,89 +702,94 @@ def main():
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
return
force_update_config(translator)
if config.getboolean('Utils', 'enabled_update_check'):
check_latest_version() # Add version check before showing menu
print_menu()
while True:
try:
choice_num = 15
choice_num = 16
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
elif choice == "1":
import reset_machine_manual
reset_machine_manual.run(translator)
print_menu()
elif choice == "2":
import cursor_register
cursor_register.main(translator)
print_menu()
elif choice == "3":
import cursor_register_google
cursor_register_google.main(translator)
print_menu()
elif choice == "4":
import cursor_register_github
cursor_register_github.main(translator)
print_menu()
elif choice == "5":
import cursor_register_manual
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 == "8":
if select_language():
match choice:
case "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
case "1":
import reset_machine_manual
reset_machine_manual.run(translator)
print_menu()
case "2":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
case "3":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
case "4":
if select_language():
print_menu()
continue
case "5":
from oauth_auth import main as oauth_main
oauth_main('google',translator)
print_menu()
case "6":
from oauth_auth import main as oauth_main
oauth_main('github',translator)
print_menu()
case "7":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
case "8":
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()
case "9":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
case "10":
from config import print_config
print_config(get_config(), translator)
print_menu()
case "11":
import bypass_version
bypass_version.main(translator)
print_menu()
case "12":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
case "13":
import bypass_token_limit
bypass_token_limit.run(translator)
print_menu()
case "14":
import restore_machine_id
restore_machine_id.run(translator)
print_menu()
case "15":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
case "16":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
case _:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
continue
elif choice == "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
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()
elif choice == "15":
import bypass_version
bypass_version.main(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
except Exception as e:

View File

@@ -8,6 +8,7 @@ import configparser
from pathlib import Path
import sys
from config import get_config
from utils import get_default_browser_path as utils_get_default_browser_path
# Add global variable at the beginning of the file
_translator = None
@@ -112,29 +113,6 @@ def fill_signup_form(page, first_name, last_name, email, config, translator=None
print(f"Error filling form: {e}")
return False
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google/Chrome/Application/chrome.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google/Chrome/Application/chrome.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google/Chrome/Application/chrome.exe')
]
elif sys.platform == "darwin":
paths = [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
]
else: # Linux
paths = [
"/usr/bin/google-chrome",
"/usr/bin/google-chrome-stable"
]
for path in paths:
if os.path.exists(path):
return path
return ""
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
@@ -186,25 +164,32 @@ def setup_driver(translator=None):
# Get config
config = get_config(translator)
# Get Chrome path
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
# Get browser type and path
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
if not chrome_path or not os.path.exists(chrome_path):
if not browser_path or not os.path.exists(browser_path):
if translator:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.chrome_path_invalid') if translator else 'Chrome路径无效使用默认路径'}{Style.RESET_ALL}")
chrome_path = get_default_chrome_path()
print(f"{Fore.YELLOW}⚠️ {browser_type} {translator.get('register.browser_path_invalid')}{Style.RESET_ALL}")
browser_path = utils_get_default_browser_path(browser_type)
# For backward compatibility, also check Chrome path
if browser_type == 'chrome':
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
if chrome_path and os.path.exists(chrome_path):
browser_path = chrome_path
# Set browser options
co = ChromiumOptions()
# Set Chrome path
co.set_browser_path(chrome_path)
# Set browser path
co.set_browser_path(browser_path)
# Use incognito mode
co.set_argument("--incognito")
if sys.platform == "linux":
# Set random port
# Set Linux specific options
co.set_argument("--no-sandbox")
# Set random port
@@ -213,6 +198,10 @@ def setup_driver(translator=None):
# Use headless mode (must be set to False, simulate human operation)
co.headless(False)
# Log browser info
if translator:
print(f"{Fore.CYAN}🌐 {translator.get('register.using_browser', browser=browser_type, path=browser_path)}{Style.RESET_ALL}")
try:
# Load extension
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
@@ -234,30 +223,38 @@ def setup_driver(translator=None):
before_pids = []
try:
import psutil
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
browser_process_names = {
'chrome': ['chrome', 'chromium'],
'edge': ['msedge', 'edge'],
'firefox': ['firefox'],
'brave': ['brave', 'brave-browser']
}
process_names = browser_process_names.get(browser_type, ['chrome'])
before_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
except:
pass
# Launch browser
page = ChromiumPage(co)
# Wait a moment for Chrome to fully launch
# Wait a moment for browser to fully launch
time.sleep(1)
# Record Chrome processes after launching and find new ones
# Record browser 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
process_names = browser_process_names.get(browser_type, ['chrome'])
after_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
# Find new browser 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")
print(f"{translator.get('register.tracking_processes', count=len(_chrome_process_ids), browser=browser_type)}")
else:
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Warning: {translator.get('register.no_new_processes_detected', browser=browser_type)}{Style.RESET_ALL}")
except Exception as e:
print(f"Warning: Could not track Chrome processes: {e}")
print(f"{translator.get('register.could_not_track_processes', browser=browser_type, error=str(e))}")
return config, page

View File

@@ -1,337 +0,0 @@
from DrissionPage import ChromiumPage, ChromiumOptions
import time
import os
import sys
from colorama import Fore, Style, init
import requests
import random
import string
from utils import get_random_wait_time
# Initialize colorama
init()
class NewTempEmail:
def __init__(self, translator=None):
self.translator = translator
self.page = None
self.setup_browser()
def get_blocked_domains(self):
"""Get blocked domains list"""
try:
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
response = requests.get(block_url, timeout=5)
if response.status_code == 200:
# Split text and remove empty lines
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
return self._load_local_blocked_domains()
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
return self._load_local_blocked_domains()
def _load_local_blocked_domains(self):
"""Load blocked domains from local file as fallback"""
try:
local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "block_domain.txt")
if os.path.exists(local_path):
with open(local_path, 'r', encoding='utf-8') as f:
domains = [line.strip() for line in f.readlines() if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.local_blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已从本地加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
else:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 本地被屏蔽域名文件不存在{Style.RESET_ALL}")
return []
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 读取本地被屏蔽域名文件失败: {str(e)}{Style.RESET_ALL}")
return []
def exclude_blocked_domains(self, domains):
"""Exclude blocked domains"""
if not self.blocked_domains:
return domains
filtered_domains = []
for domain in domains:
if domain['domain'] not in self.blocked_domains:
filtered_domains.append(domain)
excluded_count = len(domains) - len(filtered_domains)
if excluded_count > 0:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
return filtered_domains
def get_extension_block(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "PBlock")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "PBlock")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def setup_browser(self):
"""设置浏览器"""
try:
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.starting_browser')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在启动浏览器...{Style.RESET_ALL}")
# 创建浏览器选项
co = ChromiumOptions()
# 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() # 自动设置端口
# 加载 uBlock 插件
try:
extension_path = self.get_extension_block()
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 加载插件失败: {str(e)}{Style.RESET_ALL}")
self.page = ChromiumPage(co)
return True
except Exception as e:
if self.translator:
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)
# 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: # 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:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
return email
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_failed')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}")
return None
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}")
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) # wait for refresh to complete
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 邮箱刷新成功{Style.RESET_ALL}")
return True
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 未找到刷新按钮{Style.RESET_ALL}")
return False
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.refresh_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 刷新邮箱出错: {str(e)}{Style.RESET_ALL}")
return False
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}")
# use JavaScript to click element
self.page.run_js('arguments[0].click()', email_div)
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}")
else:
print(f"{Fore.YELLOW}⚠️ 未找到验证邮件{Style.RESET_ALL}")
return False
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.verification_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 检查验证邮件出错: {str(e)}{Style.RESET_ALL}")
return False
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()
if code.isdigit() and len(code) == 6:
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
return code
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 未找到有效的验证码{Style.RESET_ALL}")
return None
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.verification_code_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 获取验证码出错: {str(e)}{Style.RESET_ALL}")
return None
def main(translator=None):
temp_email = NewTempEmail(translator)
try:
email = temp_email.create_email()
if email:
if translator:
print(f"\n{Fore.CYAN}📧 {translator.get('email.address')}: {email}{Style.RESET_ALL}")
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()
else:
choice = input("\n按 R 刷新邮箱,按 Q 退出: ").lower()
if choice == 'r':
temp_email.refresh_inbox()
elif choice == 'q':
break
finally:
temp_email.close()
if __name__ == "__main__":
main()

View File

@@ -1,3 +1,4 @@
# oauth_auth.py
import os
from colorama import Fore, Style, init
import time
@@ -7,9 +8,10 @@ import sys
import json
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_chrome_path
from utils import get_random_wait_time, get_default_browser_path
from config import get_config
import platform
from get_user_token import get_token_from_cookie
# Initialize colorama
init()
@@ -29,7 +31,7 @@ class OAuthHandler:
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
self.auth_type = auth_type
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.selected_profile = None
@@ -63,41 +65,100 @@ class OAuthHandler:
return []
def _select_profile(self):
"""Select a Chrome profile to use"""
"""Allow user to select a browser 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
# 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type_display = browser_type.capitalize()
if self.translator:
# 动态使用浏览器类型
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.select_profile', browser=browser_type_display)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('oauth.profile_list', browser=browser_type_display)}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}")
print(f"Available {browser_type_display} profiles:")
# Get the user data directory for the browser type
user_data_dir = self._get_user_data_directory()
# Load available profiles from the selected browser type
try:
local_state_file = os.path.join(user_data_dir, "Local State")
if os.path.exists(local_state_file):
with open(local_state_file, 'r', encoding='utf-8') as f:
state_data = json.load(f)
profiles_data = state_data.get('profile', {}).get('info_cache', {})
# Create a list of available profiles
profiles = []
for profile_id, profile_info in profiles_data.items():
name = profile_info.get('name', profile_id)
# Mark the default profile
if profile_id.lower() == 'default':
name = f"{name} (Default)"
profiles.append((profile_id, name))
# Sort profiles by name
profiles.sort(key=lambda x: x[1])
# Show available profiles
if self.translator:
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}")
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}")
print(f"{Fore.CYAN}0. Exit{Style.RESET_ALL}")
for i, (profile_id, name) in enumerate(profiles, 1):
print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}")
# Get user's choice
max_choice = len(profiles)
choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}")
try:
choice = int(choice_str)
if choice == 0:
return False
elif 1 <= choice <= max_choice:
selected_profile = profiles[choice-1][0]
self.selected_profile = selected_profile
if self.translator:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.profile_selected', profile=selected_profile)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Selected profile: {selected_profile}{Style.RESET_ALL}")
return True
else:
if self.translator:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.invalid_selection')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
return self._select_profile()
except ValueError:
if self.translator:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.invalid_selection')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
return self._select_profile()
else:
# No Local State file, use Default profile
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
# Error loading profiles, use Default profile
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
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 False
# General error, use Default profile
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
def setup_browser(self):
"""Setup browser for OAuth flow using selected profile"""
try:
@@ -107,20 +168,35 @@ class OAuthHandler:
platform_name = platform.system().lower()
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}")
# 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
# Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
chrome_path = self._get_browser_path()
browser_path = self._get_browser_path()
if not chrome_path:
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")
if not browser_path:
error_msg = (
f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}" +
"\n" +
f"{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, google-chrome-stable"
)
raise Exception(error_msg)
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}")
# Show warning about closing browser first - 使用动态提示
if self.translator:
warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type)
else:
warning_msg = f'Warning: This will close all running {browser_type} processes'
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {warning_msg}{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}")
@@ -135,14 +211,14 @@ class OAuthHandler:
return False
# Configure browser options
co = self._configure_browser_options(chrome_path, user_data_dir, self.selected_profile)
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
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}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
# Verify browser launched successfully
if not self.browser:
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start', error=str(e)) if self.translator else 'Failed to initialize browser instance'}")
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
@@ -158,14 +234,61 @@ class OAuthHandler:
return False
def _kill_browser_processes(self):
"""Kill existing browser processes based on platform"""
"""Kill existing browser processes based on platform and browser type"""
try:
# 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
# 根据浏览器类型和平台定义要关闭的进程
browser_processes = {
'chrome': {
'win': ['chrome.exe', 'chromium.exe'],
'linux': ['chrome', 'chromium', 'chromium-browser', 'google-chrome-stable'],
'mac': ['Chrome', 'Chromium']
},
'brave': {
'win': ['brave.exe'],
'linux': ['brave', 'brave-browser'],
'mac': ['Brave Browser']
},
'edge': {
'win': ['msedge.exe'],
'linux': ['msedge'],
'mac': ['Microsoft Edge']
},
'firefox': {
'win': ['firefox.exe'],
'linux': ['firefox'],
'mac': ['Firefox']
},
'opera': {
'win': ['opera.exe', 'launcher.exe'],
'linux': ['opera'],
'mac': ['Opera']
}
}
# 获取平台类型
if os.name == 'nt':
platform_type = 'win'
elif sys.platform == 'darwin':
platform_type = 'mac'
else:
platform_type = 'linux'
# 获取要关闭的进程列表
processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, [])
if self.translator:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
# 根据平台关闭进程
if os.name == 'nt': # Windows
processes = ['chrome.exe', 'chromium.exe']
for proc in processes:
os.system(f'taskkill /f /im {proc} >nul 2>&1')
else: # Linux/Mac
processes = ['chrome', 'chromium', 'chromium-browser']
for proc in processes:
os.system(f'pkill -f {proc} >/dev/null 2>&1')
@@ -174,83 +297,155 @@ class OAuthHandler:
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"""
"""Get the default user data directory based on browser type and platform"""
try:
# 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
# 根据操作系统和浏览器类型获取用户数据目录
if os.name == 'nt': # Windows
possible_paths = [
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
]
user_data_dirs = {
'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'),
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'),
'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'),
'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'),
'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable')
}
elif sys.platform == 'darwin': # macOS
possible_paths = [
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
os.path.expanduser('~/Library/Application Support/Chromium')
]
user_data_dirs = {
'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'),
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'),
'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'),
'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
}
else: # Linux
possible_paths = [
os.path.expanduser('~/.config/google-chrome'),
os.path.expanduser('~/.config/chromium'),
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser'
]
user_data_dirs = {
'chrome': os.path.expanduser('~/.config/google-chrome'),
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/.config/microsoft-edge'),
'firefox': os.path.expanduser('~/.mozilla/firefox'),
'opera': os.path.expanduser('~/.config/opera'),
'operagx': os.path.expanduser('~/.config/opera-gx')
}
# Try each possible path
for path in possible_paths:
if os.path.exists(path):
return path
# 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
user_data_dir = user_data_dirs.get(browser_type)
# 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']} {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
if user_data_dir and os.path.exists(user_data_dir):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}")
return user_data_dir
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}")
return user_data_dirs['chrome'] # 回退到 Chrome 目录
except Exception as e:
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
# 在出错时提供一个默认目录
if os.name == 'nt':
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
elif sys.platform == 'darwin':
return os.path.expanduser('~/Library/Application Support/Google/Chrome')
else:
return os.path.expanduser('~/.config/google-chrome')
def _get_browser_path(self):
"""Get the browser executable path based on platform"""
"""Get appropriate browser path based on platform and selected browser type"""
try:
# Try default path first
chrome_path = get_default_chrome_path()
if chrome_path and os.path.exists(chrome_path):
return chrome_path
# 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
# 首先检查配置中是否有明确指定的浏览器路径
browser_path = config.get('Browser', f'{browser_type}_path', fallback=None)
if browser_path and os.path.exists(browser_path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}")
return browser_path
# 尝试获取默认路径
browser_path = get_default_browser_path(browser_type)
if browser_path and os.path.exists(browser_path):
return browser_path
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
alt_paths = [
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
r'C:\Program Files\Chromium\Application\chrome.exe',
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
]
possible_paths = []
if browser_type == 'brave':
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
]
elif browser_type == 'edge':
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe')
]
elif browser_type == 'firefox':
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Mozilla Firefox', 'firefox.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Mozilla Firefox', 'firefox.exe')
]
elif browser_type == 'opera':
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Opera', 'opera.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Opera', 'opera.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
]
else: # 默认为 Chrome
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'Application', 'chrome.exe')
]
elif sys.platform == 'darwin': # macOS
alt_paths = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
]
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser']
elif browser_type == 'edge':
possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge']
elif browser_type == 'firefox':
possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox']
else: # 默认为 Chrome
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
else: # Linux
alt_paths = [
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser',
'/usr/bin/chromium',
'/snap/bin/chromium',
'/usr/local/bin/chrome',
'/usr/local/bin/chromium'
]
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave']
elif browser_type == 'edge':
possible_paths = ['/usr/bin/microsoft-edge']
elif browser_type == 'firefox':
possible_paths = ['/usr/bin/firefox']
else: # 默认为 Chrome
possible_paths = [
'/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable
'/usr/bin/google-chrome',
'/usr/bin/chromium',
'/usr/bin/chromium-browser'
]
# 检查每个可能的路径
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}")
return path
# Try each alternative path
for path in alt_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
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
# 如果找不到指定浏览器,则尝试使用 Chrome
if browser_type != 'chrome':
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}")
return self._get_chrome_path()
return None
@@ -258,17 +453,18 @@ class OAuthHandler:
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):
def _configure_browser_options(self, browser_path, user_data_dir, active_profile):
"""Configure browser options based on platform"""
try:
co = ChromiumOptions()
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
co.set_paths(browser_path=browser_path, user_data_path=user_data_dir)
co.set_argument(f'--profile-directory={active_profile}')
# Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
# Platform-specific options
if sys.platform.startswith('linux'):
@@ -330,13 +526,19 @@ class OAuthHandler:
# Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
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(f"""
alert('{alert_message}');
""")
except:
pass # Alert is optional
# 获取配置中是否启用 alert 选项
config = get_config(self.translator)
show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False)
if show_alert:
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(f"""
alert('{alert_message}');
""")
except:
pass # Alert is optional
# Wait for authentication to complete
auth_info = self._wait_for_auth()
@@ -378,13 +580,7 @@ class OAuthHandler:
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
token = None
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
token = get_token_from_cookie(value, self.translator)
if token:
# Get email from settings page
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}")
@@ -420,7 +616,6 @@ class OAuthHandler:
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":
@@ -459,7 +654,7 @@ class OAuthHandler:
# 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', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
@@ -590,11 +785,7 @@ class OAuthHandler:
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
token = get_token_from_cookie(value, self.translator)
if token:
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
@@ -632,7 +823,6 @@ class OAuthHandler:
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":
@@ -658,10 +848,7 @@ class OAuthHandler:
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
token = get_token_from_cookie(value, self.translator)
if token:
# Get email and check usage here too
try:
@@ -691,19 +878,18 @@ class OAuthHandler:
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()
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:
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}")
return self.handle_github_auth()
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}")
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']} {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}")
@@ -759,12 +945,10 @@ class OAuthHandler:
if name == "WorkosCursorSessionToken":
try:
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
token = get_token_from_cookie(value, self.translator)
except Exception as e:
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}")
error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
@@ -777,11 +961,13 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
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}")
error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None
except Exception as e:
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}")
error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None
def _delete_current_account(self):
@@ -823,7 +1009,8 @@ class OAuthHandler:
return True
except Exception as e:
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}")
error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False
def main(auth_type, translator=None):

View File

@@ -8,3 +8,5 @@ pyinstaller
DrissionPage>=4.0.0
selenium
webdriver_manager
arabic-reshaper
python-bidi

View File

@@ -12,9 +12,9 @@ import glob
from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
from datetime import datetime
# Initialize colorama
init()
@@ -30,6 +30,20 @@ EMOJI = {
"WARNING": "⚠️",
}
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
@@ -220,8 +234,16 @@ def get_workbench_cursor_path(translator=None) -> str:
if system == "Windows":
base_path = config.get('WindowsPaths', 'cursor_path')
else:
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
main_path = os.path.join(base_path, paths_map[system]["main"])
@@ -334,37 +356,40 @@ def modify_workbench_js(file_path: str, translator=None) -> bool:
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
content = main_file.read()
if sys.platform == "win32":
# Define replacement patterns
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'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)'
patterns = {
# 通用按钮替换模式
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
# Windows/Linux/Mac 通用按钮替换模式
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': 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)',
# Badge 替换
r'<div>Pro Trial': r'<div>Pro',
CBadge_old_pattern = r'<div>Pro Trial'
CBadge_new_pattern = r'<div>Pro'
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
#
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
# Pro
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
# Toast 替换
r'notifications-toasts': r'notifications-toasts hidden'
}
CToast_old_pattern = r'notifications-toasts'
CToast_new_pattern = r'notifications-toasts hidden'
# Replace content
content = content.replace(CButton_old_pattern, CButton_new_pattern)
content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
content = content.replace(CToast_old_pattern, CToast_new_pattern)
# 使用patterns进行替换
for old_pattern, new_pattern in patterns.items():
content = content.replace(old_pattern, new_pattern)
# Write to temporary file
tmp_file.write(content)
tmp_path = tmp_file.name
# Backup original file
backup_path = file_path + ".backup"
if os.path.exists(backup_path):
os.remove(backup_path)
# Backup original file with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{file_path}.backup.{timestamp}"
shutil.copy2(file_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
# Move temporary file to original position
if os.path.exists(file_path):
@@ -411,7 +436,10 @@ def modify_main_js(main_path: str, translator) -> bool:
tmp_file.write(content)
tmp_path = tmp_file.name
shutil.copy2(main_path, main_path + ".old")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{main_path}.old.{timestamp}"
shutil.copy2(main_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
shutil.move(tmp_path, main_path)
os.chmod(main_path, original_mode)
@@ -461,7 +489,8 @@ def patch_cursor_get_machine_id(translator) -> bool:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
# Backup file
backup_path = main_path + ".bak"
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{main_path}.bak.{timestamp}"
if not os.path.exists(backup_path):
shutil.copy2(main_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
@@ -638,8 +667,8 @@ class MachineIDResetter:
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
except PermissionError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied', error=str(e))}{Style.RESET_ALL}")
raise
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}")
@@ -717,12 +746,10 @@ class MachineIDResetter:
with open(self.db_path, "r", encoding="utf-8") as f:
config = json.load(f)
backup_path = self.db_path + ".bak"
if not os.path.exists(backup_path):
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
shutil.copy2(self.db_path, backup_path)
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.db_path}.bak.{timestamp}"
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
shutil.copy2(self.db_path, backup_path)
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
new_ids = self.generate_new_ids()
@@ -786,7 +813,8 @@ class MachineIDResetter:
# Create backup if file exists
if os.path.exists(machine_id_path):
backup_path = machine_id_path + ".backup"
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{machine_id_path}.backup.{timestamp}"
try:
shutil.copy2(machine_id_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")

406
restore_machine_id.py Normal file
View File

@@ -0,0 +1,406 @@
import os
import sys
import json
import uuid
import hashlib
import shutil
import sqlite3
import platform
import re
import glob
import tempfile
from colorama import Fore, Style, init
from typing import Tuple
import configparser
import traceback
from config import get_config
from datetime import datetime
# 导入共享函数
from reset_machine_manual import get_cursor_machine_id_path, get_user_documents_path
# 初始化 colorama
init()
# 定义表情符号常量
EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "🔄",
"WARNING": "⚠️",
}
class ConfigError(Exception):
"""配置错误异常"""
pass
class MachineIDRestorer:
def __init__(self, translator=None):
self.translator = translator
# 读取配置
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file, encoding='utf-8')
# 根据操作系统获取路径
if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA")
if appdata is None:
raise EnvironmentError("APPDATA Environment Variable Not Set")
if not config.has_section('WindowsPaths'):
raise ConfigError("WindowsPaths section not found in config")
self.db_path = config.get('WindowsPaths', 'storage_path')
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == "darwin": # macOS
if not config.has_section('MacPaths'):
raise ConfigError("MacPaths section not found in config")
self.db_path = config.get('MacPaths', 'storage_path')
self.sqlite_path = config.get('MacPaths', 'sqlite_path')
elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'):
raise ConfigError("LinuxPaths section not found in config")
self.db_path = config.get('LinuxPaths', 'storage_path')
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
def find_backups(self):
"""查找可用的备份文件"""
db_dir = os.path.dirname(self.db_path)
db_name = os.path.basename(self.db_path)
# 查找格式为 {db_name}.bak.{timestamp} 的文件
backup_pattern = f"{db_name}.bak.*"
backups = glob.glob(os.path.join(db_dir, backup_pattern))
# 按创建时间排序(最新的在前)
backups.sort(key=os.path.getctime, reverse=True)
return backups
def list_backups(self):
"""列出所有可用备份"""
backups = self.find_backups()
if not backups:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.no_backups_found')}{Style.RESET_ALL}")
return None
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.available_backups')}:{Style.RESET_ALL}")
for i, backup in enumerate(backups, 1):
# 获取备份文件信息
timestamp_str = backup.split('.')[-1]
try:
# 尝试解析时间戳(如果格式为 YYYYmmdd_HHMMSS
timestamp = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
date_str = timestamp.strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
date_str = "未知日期"
# 获取文件大小
size = os.path.getsize(backup)
size_str = f"{size / 1024:.1f} KB"
print(f"{i}. {Fore.GREEN}{os.path.basename(backup)}{Style.RESET_ALL} ({date_str}, {size_str})")
return backups
def select_backup(self):
"""让用户选择要恢复的备份"""
backups = self.list_backups()
if not backups:
return None
while True:
try:
choice = input(f"{EMOJI['INFO']} {self.translator.get('restore.select_backup')} (1-{len(backups)}, 0 {self.translator.get('restore.to_cancel')}): ")
if choice.strip() == '0':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
return None
index = int(choice) - 1
if 0 <= index < len(backups):
return backups[index]
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.invalid_selection')}{Style.RESET_ALL}")
except ValueError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.please_enter_number')}{Style.RESET_ALL}")
def extract_ids_from_backup(self, backup_path):
"""从备份文件中提取机器ID"""
try:
with open(backup_path, "r", encoding="utf-8") as f:
backup_data = json.load(f)
# 提取需要恢复的ID
ids = {
"telemetry.devDeviceId": backup_data.get("telemetry.devDeviceId", ""),
"telemetry.macMachineId": backup_data.get("telemetry.macMachineId", ""),
"telemetry.machineId": backup_data.get("telemetry.machineId", ""),
"telemetry.sqmId": backup_data.get("telemetry.sqmId", ""),
"storage.serviceMachineId": backup_data.get("storage.serviceMachineId",
backup_data.get("telemetry.devDeviceId", ""))
}
# 确保所有ID都存在
for key, value in ids.items():
if not value:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.missing_id', id=key)}{Style.RESET_ALL}")
return ids
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.read_backup_failed', error=str(e))}{Style.RESET_ALL}")
return None
def update_current_file(self, ids):
"""更新当前的storage.json文件"""
try:
if not os.path.exists(self.db_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.current_file_not_found')}: {self.db_path}{Style.RESET_ALL}")
return False
# 读取当前文件
with open(self.db_path, "r", encoding="utf-8") as f:
current_data = json.load(f)
# 创建当前文件的备份
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.db_path}.restore_bak.{timestamp}"
shutil.copy2(self.db_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['BACKUP']} {self.translator.get('restore.current_backup_created')}: {backup_path}{Style.RESET_ALL}")
# 更新ID
current_data.update(ids)
# 保存更新后的文件
with open(self.db_path, "w", encoding="utf-8") as f:
json.dump(current_data, f, indent=4)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.storage_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_sqlite_db(self, ids):
"""更新SQLite数据库中的ID"""
try:
if not os.path.exists(self.sqlite_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_not_found')}: {self.sqlite_path}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_sqlite')}...{Style.RESET_ALL}")
conn = sqlite3.connect(self.sqlite_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS ItemTable (
key TEXT PRIMARY KEY,
value TEXT
)
""")
for key, value in ids.items():
cursor.execute("""
INSERT OR REPLACE INTO ItemTable (key, value)
VALUES (?, ?)
""", (key, value))
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('restore.updating_pair')}: {key}{Style.RESET_ALL}")
conn.commit()
conn.close()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.sqlite_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_machine_id_file(self, dev_device_id):
"""更新machineId文件"""
try:
machine_id_path = get_cursor_machine_id_path(self.translator)
# 创建目录(如果不存在)
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
# 备份当前文件(如果存在)
if os.path.exists(machine_id_path):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{machine_id_path}.restore_bak.{timestamp}"
try:
shutil.copy2(machine_id_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('restore.machine_id_backup_created')}: {backup_path}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.backup_creation_failed', error=str(e))}{Style.RESET_ALL}")
# 写入新的ID
with open(machine_id_path, "w", encoding="utf-8") as f:
f.write(dev_device_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.machine_id_updated')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.machine_id_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def update_system_ids(self, ids):
"""更新系统级ID特定于操作系统"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_system_ids')}...{Style.RESET_ALL}")
if sys.platform.startswith("win"):
self._update_windows_system_ids(ids)
elif sys.platform == "darwin":
self._update_macos_system_ids(ids)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
return False
def _update_windows_system_ids(self, ids):
"""更新Windows系统ID"""
try:
import winreg
# 更新MachineGuid
guid = ids.get("telemetry.devDeviceId", "")
if guid:
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Cryptography",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, guid)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_guid_updated')}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
# 更新SQMClient MachineId
sqm_id = ids.get("telemetry.sqmId", "")
if sqm_id:
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\SQMClient",
0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
)
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, sqm_id)
winreg.CloseKey(key)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_id_updated')}{Style.RESET_ALL}")
except FileNotFoundError:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.sqm_client_key_not_found')}{Style.RESET_ALL}")
except PermissionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
def _update_macos_system_ids(self, ids):
"""更新macOS系统ID"""
try:
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
if os.path.exists(uuid_file):
mac_id = ids.get("telemetry.macMachineId", "")
if mac_id:
cmd = f'sudo plutil -replace "UUID" -string "{mac_id}" "{uuid_file}"'
result = os.system(cmd)
if result == 0:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.macos_platform_uuid_updated')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_macos_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
def restore_machine_ids(self):
"""恢复之前备份的机器ID"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.starting')}...{Style.RESET_ALL}")
# 选择要恢复的备份
backup_path = self.select_backup()
if not backup_path:
return False
# 从备份中提取ID
ids = self.extract_ids_from_backup(backup_path)
if not ids:
return False
# 显示将要恢复的ID
print(f"\n{Fore.CYAN}{self.translator.get('restore.ids_to_restore')}:{Style.RESET_ALL}")
for key, value in ids.items():
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
# 确认恢复
confirm = input(f"\n{EMOJI['WARNING']} {self.translator.get('restore.confirm')} (y/n): ")
if confirm.lower() != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
return False
# 更新当前文件
if not self.update_current_file(ids):
return False
# 更新SQLite数据库
self.update_sqlite_db(ids)
# 更新machineId文件
self.update_machine_id_file(ids.get("telemetry.devDeviceId", ""))
# 更新系统ID
self.update_system_ids(ids)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.success')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.process_error', error=str(e))}{Style.RESET_ALL}")
return False
def run(translator=None):
"""恢复机器ID的主函数"""
config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('restore.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
restorer = MachineIDRestorer(translator)
restorer.restore_machine_ids()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('restore.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)

View File

@@ -11,7 +11,6 @@ import tempfile
from colorama import Fore, Style, init
from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
import glob
@@ -26,10 +25,24 @@ EMOJI = {
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "<EFBFBD><EFBFBD>",
"RESET": "🔄",
"WARNING": "⚠️",
}
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
@@ -111,7 +124,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
# 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):
if os.path.exists(path):
base_path = path
# Update config with the found path
config.set(section, 'cursor_path', path)
@@ -178,15 +191,22 @@ 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
"base": "/Applications/Cursor.app/Contents/Resources/app",
"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"],
@@ -210,7 +230,20 @@ def get_workbench_cursor_path(translator=None) -> str:
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')
elif system == "Darwin":
base_path = paths_map[system]["base"]
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
base_path = config.get('MacPaths', 'cursor_path')
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
base_path = config.get('LinuxPaths', 'cursor_path')
# Get the main path for non-Linux systems or if Linux path wasn't found in the loop
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):
@@ -529,7 +562,7 @@ class MachineIDResetter:
self.db_path = config.get('LinuxPaths', 'storage_path')
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
else:
else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
# Save any changes to config file
@@ -811,4 +844,4 @@ def run(translator=None):
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)
run(main_translator)

View File

@@ -1,18 +0,0 @@
{
"manifest_version": 3,
"name": "Turnstile Patcher",
"version": "2.1",
"content_scripts": [
{
"js": [
"./script.js"
],
"matches": [
"<all_urls>"
],
"run_at": "document_start",
"all_frames": true,
"world": "MAIN"
}
]
}

View File

@@ -1,50 +0,0 @@
function qSelector(selector) {
return document.querySelector(selector);
}
(function() {
'use strict';
var solved = false;
var checkBoxClicked = false;
var requestCount = 0;
const MAX_ATTEMPTS = 1;
const CHECK_BOX = ".recaptcha-checkbox-border";
const AUDIO_BUTTON = "#recaptcha-audio-button";
const PLAY_BUTTON = ".rc-audiochallenge-play-button .rc-button-default";
const AUDIO_SOURCE = "#audio-source";
const IMAGE_SELECT = "#rc-imageselect";
const RESPONSE_FIELD = ".rc-audiochallenge-response-field";
const AUDIO_ERROR_MESSAGE = ".rc-audiochallenge-error-message";
const AUDIO_RESPONSE = "#audio-response";
const RELOAD_BUTTON = "#recaptcha-reload-button";
const RECAPTCHA_STATUS = "#recaptcha-accessible-status";
const DOSCAPTCHA = ".rc-doscaptcha-body";
const VERIFY_BUTTON = "#recaptcha-verify-button";
var recaptchaInitialStatus = qSelector(RECAPTCHA_STATUS) ? qSelector(RECAPTCHA_STATUS).innerText : ""
function isHidden(el) {
return(el.offsetParent === null)
}
try {
if(!checkBoxClicked && qSelector(CHECK_BOX) && !isHidden(qSelector(CHECK_BOX))) {
//console.log("checkbox clicked");
qSelector(CHECK_BOX).click();
checkBoxClicked = true;
}
//Check if the captcha is solved
if(qSelector(RECAPTCHA_STATUS) && (qSelector(RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
solved = true;
console.log("SOLVED");
}
if(requestCount > MAX_ATTEMPTS) {
console.log("Attempted Max Retries. Stopping the solver");
solved = true;
}
//Stop solving when Automated queries message is shown
if(qSelector(DOSCAPTCHA) && qSelector(DOSCAPTCHA).innerText.length > 0) {
console.log("Automated Queries Detected");
}
} catch(err) {
console.log(err.message);
console.log("An error occurred while solving. Stopping the solver.");
}
})();

View File

@@ -1,12 +0,0 @@
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// old method wouldn't work on 4k screens
let screenX = getRandomInt(800, 1200);
let screenY = getRandomInt(400, 600);
Object.defineProperty(MouseEvent.prototype, 'screenX', { value: screenX });
Object.defineProperty(MouseEvent.prototype, 'screenY', { value: screenY });

181
utils.py
View File

@@ -9,24 +9,171 @@ def get_user_documents_path():
return os.path.expanduser("~\\Documents")
else:
return os.path.expanduser("~/Documents")
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"
def get_default_driver_path(browser_type='chrome'):
"""Get default driver path based on browser type"""
browser_type = browser_type.lower()
if browser_type == 'chrome':
return get_default_chrome_driver_path()
elif browser_type == 'edge':
return get_default_edge_driver_path()
elif browser_type == 'firefox':
return get_default_firefox_driver_path()
elif browser_type == 'brave':
# Brave 使用 Chrome 的 driver
return get_default_chrome_driver_path()
else:
return "/usr/bin/google-chrome"
# Default to Chrome if browser type is unknown
return get_default_chrome_driver_path()
def get_default_chrome_driver_path():
"""Get default Chrome driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver")
else:
return "/usr/local/bin/chromedriver"
def get_default_edge_driver_path():
"""Get default Edge driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver")
else:
return "/usr/local/bin/msedgedriver"
def get_default_firefox_driver_path():
"""Get default Firefox driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver")
else:
return "/usr/local/bin/geckodriver"
def get_default_brave_driver_path():
"""Get default Brave driver path (uses Chrome driver)"""
# Brave 浏览器基于 Chromium所以使用相同的 chromedriver
return get_default_chrome_driver_path()
def get_default_browser_path(browser_type='chrome'):
"""Get default browser executable path"""
browser_type = browser_type.lower()
if sys.platform == "win32":
if browser_type == 'chrome':
# 尝试在 PATH 中找到 Chrome
try:
import shutil
chrome_in_path = shutil.which("chrome")
if chrome_in_path:
return chrome_in_path
except:
pass
# 使用默认路径
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif browser_type == 'edge':
return r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
elif browser_type == 'firefox':
return r"C:\Program Files\Mozilla Firefox\firefox.exe"
elif browser_type == 'opera':
# 尝试多个可能的 Opera 路径
opera_paths = [
r"C:\Program Files\Opera\opera.exe",
r"C:\Program Files (x86)\Opera\opera.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
]
for path in opera_paths:
if os.path.exists(path):
return path
return opera_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'operagx':
# 尝试多个可能的 Opera GX 路径
operagx_paths = [
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
r"C:\Program Files\Opera GX\opera.exe",
r"C:\Program Files (x86)\Opera GX\opera.exe"
]
for path in operagx_paths:
if os.path.exists(path):
return path
return operagx_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'brave':
# Brave 浏览器的默认安装路径
paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe')
]
for path in paths:
if os.path.exists(path):
return path
return paths[0] # 返回第一个路径,即使它不存在
elif sys.platform == "darwin":
if browser_type == 'chrome':
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
elif browser_type == 'edge':
return "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
elif browser_type == 'firefox':
return "/Applications/Firefox.app/Contents/MacOS/firefox"
elif browser_type == 'brave':
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
elif browser_type == 'opera':
return "/Applications/Opera.app/Contents/MacOS/Opera"
elif browser_type == 'operagx':
return "/Applications/Opera GX.app/Contents/MacOS/Opera"
else: # Linux
if browser_type == 'chrome':
# 尝试多种可能的名称
chrome_names = ["google-chrome", "chrome", "chromium", "chromium-browser"]
for name in chrome_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/google-chrome"
elif browser_type == 'edge':
return "/usr/bin/microsoft-edge"
elif browser_type == 'firefox':
return "/usr/bin/firefox"
elif browser_type == 'opera':
return "/usr/bin/opera"
elif browser_type == 'operagx':
# 尝试常见的 Opera GX 路径
operagx_names = ["opera-gx"]
for name in operagx_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/opera-gx"
elif browser_type == 'brave':
# 尝试常见的 Brave 路径
brave_names = ["brave", "brave-browser"]
for name in brave_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/brave-browser"
# 如果找不到指定的浏览器类型,则返回 Chrome 的路径
return get_default_browser_path('chrome')
def get_linux_cursor_path():
"""Get Linux Cursor path"""