Compare commits

..

97 Commits

Author SHA1 Message Date
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
32 changed files with 2478 additions and 581 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.04.10.g5863891
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.9.05
VERSION=1.9.05

View File

@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.8.07'
default: '1.9.02'
permissions:
contents: write
@@ -153,7 +153,7 @@ jobs:
- 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
@@ -211,6 +211,9 @@ jobs:
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
@@ -220,18 +223,66 @@ jobs:
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
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 +292,4 @@ jobs:
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,79 @@
# Change Log
## 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 +319,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 | 修復啟動瀏覽器卡住問題

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.04.10.g5863891
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,12 +7,21 @@
<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.
@@ -24,15 +33,17 @@ 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.
這是一個自動化工具,自動註冊,支持 Windows macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
這是一個自動化工具,自動註冊,支持 Windows macOS 和 Linux 系統,完成 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>
</p>
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
##### If you don't have browser, you can download it from
##### 如果沒有 Google Chrome可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
##### 如果沒有瀏覽器,可以從这里下載
[Google Chrome](https://www.google.com/intl/en_pk/chrome/) | [Opera](https://www.opera.com/download) | [Edge](https://www.microsoft.com/en-us/edge) | [Firefox](https://www.mozilla.org/en-US/firefox/new/) | [Brave](https://www.brave.com/download/)
</div>
@@ -48,7 +59,7 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
* Support Windows and macOS systems<br>支持 WindowsmacOS 系統<br>
* Support Windows macOS and Linux systems<br>支持 WindowsmacOS 和 Linux 系統<br>
* Complete Auth verification<br>完成 Auth 驗證<br>
@@ -60,24 +71,32 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
## 💻 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 +107,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 +185,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>

174
bypass_token_limit.py Normal file
View File

@@ -0,0 +1,174 @@
import os
import shutil
import platform
import tempfile
import glob
from colorama import Fore, Style, init
import configparser
from new_signup import get_user_documents_path
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_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"]
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]
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/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',
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)

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()

119
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,17 @@ 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
}
}
@@ -257,6 +309,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

@@ -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"
@@ -168,7 +169,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

@@ -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

@@ -20,7 +20,17 @@
"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 профил"
},
"languages": {
"en": "English",

View File

@@ -22,7 +22,16 @@
"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"
},
"languages": {
"en": "Englisch",
@@ -34,7 +43,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 +112,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",

View File

@@ -30,7 +30,9 @@
"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"
},
"languages": {
"en": "English",
@@ -117,7 +119,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 +199,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 +566,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 +634,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 +719,50 @@
"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}"
}
}

View File

@@ -22,7 +22,15 @@
"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"
},
"languages": {
"en": "Inglés",
@@ -103,8 +111,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",
@@ -452,4 +466,4 @@
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
}
}
}

View File

@@ -19,7 +19,16 @@
"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"
},
"languages": {
"en": "Anglais",
@@ -31,7 +40,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 +109,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",

View File

@@ -20,7 +20,16 @@
"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"
},
"languages": {
"en": "Engels",
@@ -99,7 +108,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",
@@ -388,4 +404,4 @@
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
}
}
}

View File

@@ -19,7 +19,16 @@
"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"
},
"languages": {
"en": "Inglês",

View File

@@ -19,7 +19,17 @@
"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"
},
"languages": {
"en": "Английский",
@@ -396,4 +406,4 @@
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
}
}
}

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,7 +20,18 @@
"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"
},
"languages": {
"en": "English",

View File

@@ -20,13 +20,26 @@
"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"
},
"languages": {
"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 +112,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 +196,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 +213,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 +235,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 +251,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 +283,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 +307,425 @@
"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}"
}
}

View File

@@ -30,7 +30,9 @@
"continue_prompt": "继续?(y/N): ",
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查"
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权",
"bypass_token_limit": "绕过 Token 限制"
},
"languages": {
"en": "英语",
@@ -117,7 +119,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 +197,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 +546,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 +614,27 @@
"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}"
},
"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 +699,50 @@
"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}"
}
}
}

View File

@@ -30,7 +30,9 @@
"continue_prompt": "繼續?(y/N): ",
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查"
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權",
"bypass_token_limit": "繞過 Token 限制"
},
"languages": {
"en": "英文",
@@ -117,7 +119,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": {
@@ -146,6 +149,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 +534,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": "未找到認證按鈕",
@@ -651,5 +676,38 @@
"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": "按回車鍵繼續..."
}
}
}

294
main.py
View File

@@ -9,7 +9,7 @@ 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
@@ -110,18 +110,24 @@ 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 _:
return 'en' # Default to English
except:
return self._detect_unix_language()
@@ -129,59 +135,63 @@ 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 _:
# 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 _:
return 'en'
except:
return 'en'
@@ -275,8 +285,8 @@ def print_menu():
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}))",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
@@ -287,7 +297,9 @@ def print_menu():
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')}"
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}",
17: f"{Fore.GREEN}17{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit', fallback='Bypass Token Limit')}"
}
# Automatically calculate the number of menu items in the left and right columns
@@ -552,89 +564,99 @@ 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 = 17
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
cursor_register.main(translator)
print_menu()
case "3":
import cursor_register_google
cursor_register_google.main(translator)
print_menu()
case "4":
import cursor_register_github
cursor_register_github.main(translator)
print_menu()
case "5":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
case "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()
case "7":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
case "8":
if select_language():
print_menu()
continue
case "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
case "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()
case "11":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
case "12":
from config import print_config
print_config(get_config(), translator)
print_menu()
case "13":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
case "14":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
case "15":
import bypass_version
bypass_version.main(translator)
print_menu()
case "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
case "17":
import bypass_token_limit
bypass_token_limit.run(translator)
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_type} {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

@@ -6,7 +6,8 @@ from colorama import Fore, Style, init
import requests
import random
import string
from utils import get_random_wait_time
from config import get_config
from utils import get_random_wait_time, get_default_browser_path as utils_get_default_browser_path
# Initialize colorama
init()
@@ -104,16 +105,41 @@ class NewTempEmail:
else:
print(f"{Fore.CYAN} 正在启动浏览器...{Style.RESET_ALL}")
# 获取配置
config = get_config(self.translator)
# 获取浏览器类型和路径
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 browser_path or not os.path.exists(browser_path):
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.browser_path_invalid', browser=browser_type) if self.translator else f'{browser_type} 路径无效,使用默认路径'}{Style.RESET_ALL}")
browser_path = utils_get_default_browser_path(browser_type)
# 为了向后兼容,也检查 Chrome 路径
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
# 创建浏览器选项
co = ChromiumOptions()
# 设置浏览器路径
co.set_browser_path(browser_path)
# 记录浏览器信息
if self.translator:
print(f"{Fore.CYAN}🌐 {self.translator.get('email.using_browser', browser=browser_type, path=browser_path) if self.translator else f'使用 {browser_type} 浏览器: {browser_path}'}{Style.RESET_ALL}")
# 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'):
if "--headless=new" not in co.arguments 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
@@ -122,22 +148,43 @@ class NewTempEmail:
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 running as root, try to use actual user's browser 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")
# 根据浏览器类型选择配置文件夹
profile_dirs = {
'chrome': os.path.join(actual_home, ".config", "google-chrome"),
'brave': os.path.join(actual_home, ".config", "BraveSoftware", "Brave-Browser"),
'edge': os.path.join(actual_home, ".config", "microsoft-edge"),
'firefox': os.path.join(actual_home, ".mozilla", "firefox")
}
user_data_dir = profile_dirs.get(browser_type, profile_dirs['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}")
print(f"{Fore.CYAN} {self.translator.get('email.using_browser_profile', browser=browser_type, user_data_dir=user_data_dir) if self.translator else f'Using {browser_type} profile from: {user_data_dir}'}{Style.RESET_ALL}")
co.set_argument(f"--user-data-dir={user_data_dir}")
co.auto_port() # 自动设置端口
# 根据浏览器类型设置扩展参数
extension_args = {
'chrome': "--allow-extensions-in-incognito",
'brave': "--allow-extensions-in-brave-incognito", # Brave 可能使用不同的参数
'edge': "--allow-extensions-in-incognito",
'firefox': None # Firefox 可能使用不同的方式加载扩展
}
extension_arg = extension_args.get(browser_type, "--allow-extensions-in-incognito")
# 加载 uBlock 插件
try:
extension_path = self.get_extension_block()
co.set_argument("--allow-extensions-in-incognito")
if extension_arg: # 如果有扩展参数
co.set_argument(extension_arg)
co.add_extension(extension_path)
except Exception as e:
if self.translator:
@@ -154,8 +201,17 @@ class NewTempEmail:
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}")
browser_install_suggestions = {
'chrome': "sudo apt install chromium-browser 或 sudo apt install google-chrome-stable",
'brave': "sudo apt install brave-browser",
'edge': "sudo apt install microsoft-edge-stable",
'firefox': "sudo apt install firefox"
}
suggestion = browser_install_suggestions.get(browser_type, browser_install_suggestions['chrome'])
print(f"{Fore.YELLOW} {self.translator.get('email.make_sure_browser_is_properly_installed', browser=browser_type) if self.translator else f'Make sure {browser_type} is properly installed'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW} {self.translator.get('email.try_install_browser') if self.translator else f'Try: {suggestion}'}{Style.RESET_ALL}")
return False
def create_email(self):

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('browser_profile.select_profile', browser=browser_type_display)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('browser_profile.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('browser_profile.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('browser_profile.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('browser_profile.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('browser_profile.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('browser_profile.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

@@ -15,6 +15,7 @@ import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
from datetime import datetime
# Initialize colorama
init()
@@ -220,8 +221,12 @@ 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"]
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]
main_path = os.path.join(base_path, paths_map[system]["main"])
@@ -334,37 +339,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 +419,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 +472,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 +650,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 +729,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 +796,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}")

View File

@@ -26,7 +26,7 @@ EMOJI = {
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "<EFBFBD><EFBFBD>",
"RESET": "🔄",
"WARNING": "⚠️",
}
@@ -111,7 +111,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)
@@ -529,7 +529,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 +811,4 @@ def run(translator=None):
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)
run(main_translator)

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"""