mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-26 13:40:39 +03:00
Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809dac091d | ||
|
|
da5bff5994 | ||
|
|
296a69bf73 | ||
|
|
5ca6c97d96 | ||
|
|
fb52888f9c | ||
|
|
8e6df1d1f8 | ||
|
|
be5a17c861 | ||
|
|
ca496ea53f | ||
|
|
52eaecd040 | ||
|
|
5fac4d6e45 | ||
|
|
271d5d9db9 | ||
|
|
c2af657c88 | ||
|
|
cf55c91117 | ||
|
|
9abf4f4899 | ||
|
|
a78b1160c2 | ||
|
|
c5453a4374 | ||
|
|
8c497d7639 | ||
|
|
c0c2cd6120 | ||
|
|
e36f6d986e | ||
|
|
0b547c0542 | ||
|
|
ef17ba8803 | ||
|
|
31bf75e4de | ||
|
|
63e4e72ec7 | ||
|
|
b51d9c7a74 | ||
|
|
4aba849cf1 | ||
|
|
1489357328 | ||
|
|
243c47adb4 | ||
|
|
b571356fbf | ||
|
|
615c3ea2db | ||
|
|
cffde7066e | ||
|
|
73a8b23257 | ||
|
|
6d182fda55 | ||
|
|
c243e9f2f6 | ||
|
|
caf996864f | ||
|
|
ce9411dcda | ||
|
|
1c10750c2b | ||
|
|
5aa8dbb614 | ||
|
|
30df4d9ad1 | ||
|
|
4a533436eb | ||
|
|
b271166247 | ||
|
|
75825fe3fe | ||
|
|
56f9a86e7a | ||
|
|
bdb7fa4ddf | ||
|
|
51ac969f76 | ||
|
|
da9d4a3648 | ||
|
|
33a497bf52 | ||
|
|
c392e287fe | ||
|
|
ea4cfaa7ab | ||
|
|
24c1acc562 | ||
|
|
43eea92f21 | ||
|
|
631f8be5e4 | ||
|
|
c8fa00589e | ||
|
|
a3dff87da9 | ||
|
|
861f258ce4 | ||
|
|
ab9202fe48 | ||
|
|
d60c46bac6 | ||
|
|
4757f9777a | ||
|
|
caecbd4c8d | ||
|
|
105b5d4517 | ||
|
|
f325690e32 | ||
|
|
3de2db74b2 | ||
|
|
6153041607 | ||
|
|
6eba95c055 | ||
|
|
21535104a6 | ||
|
|
b42b4b01b9 | ||
|
|
4b9c465dd5 | ||
|
|
b98059a476 | ||
|
|
56882f0663 | ||
|
|
0852472746 | ||
|
|
d867f5cfe9 | ||
|
|
e69e500e8d | ||
|
|
2f012b9dc5 | ||
|
|
d3c6bf227b | ||
|
|
f697b71755 | ||
|
|
5863891f4b | ||
|
|
7e0da4a0cb | ||
|
|
1cdb543ea9 | ||
|
|
5dad4f35a6 | ||
|
|
34a23a69a1 | ||
|
|
3cca2e3b17 | ||
|
|
ee287b91f2 | ||
|
|
96704e9f38 | ||
|
|
f541bc40b4 | ||
|
|
a730b145a1 | ||
|
|
1e72e59985 | ||
|
|
7d355f126c | ||
|
|
abcc3a84fa | ||
|
|
d8f1cbfc3b | ||
|
|
431550e4a9 | ||
|
|
dde25cba02 | ||
|
|
0466421823 | ||
|
|
aa4177d5ec | ||
|
|
75b17bb515 | ||
|
|
80a76b507a | ||
|
|
84b77e8b13 | ||
|
|
f471450e12 | ||
|
|
9f81f94957 | ||
|
|
b423779d04 | ||
|
|
5203634f0a | ||
|
|
1a73ec0a32 | ||
|
|
92263013f2 | ||
|
|
3edb69e831 | ||
|
|
271fc818b1 | ||
|
|
61803031dc | ||
|
|
9a56bc5f7e | ||
|
|
32fea2c82b | ||
|
|
45f10a6da8 | ||
|
|
a4692d11dc | ||
|
|
43a58db339 | ||
|
|
bc55000668 | ||
|
|
84358805fc | ||
|
|
82e2625dfe | ||
|
|
d5404e8f57 | ||
|
|
fb3e532058 | ||
|
|
a7c4631ea4 | ||
|
|
5b64e54e90 | ||
|
|
f667da64b3 | ||
|
|
26a8e8da28 | ||
|
|
3862176867 | ||
|
|
5adc598661 | ||
|
|
5e6651bb32 | ||
|
|
bdc606ce2d | ||
|
|
564e421288 | ||
|
|
dce359dc33 | ||
|
|
ff79fae77b | ||
|
|
db3a2032dc | ||
|
|
e2a33d178d | ||
|
|
42d97cfa87 | ||
|
|
c42d7d5422 | ||
|
|
c7a84ca59f | ||
|
|
4746af7ce9 | ||
|
|
9f51ba8128 | ||
|
|
9aa09c436e | ||
|
|
1e3e9c99eb | ||
|
|
3f9cbc3d08 | ||
|
|
12d46d5f18 | ||
|
|
6cb3ad79af | ||
|
|
6470c65f8b | ||
|
|
491b227486 | ||
|
|
96c0cd5274 | ||
|
|
60a438e618 | ||
|
|
6a25871366 | ||
|
|
b46a58bd23 | ||
|
|
fea2b88a8e | ||
|
|
63fe39f2c1 | ||
|
|
386ffa4568 | ||
|
|
9c66725caf | ||
|
|
16b6ba95e2 | ||
|
|
3424f49a57 |
21
.SRCINFO
Normal file
21
.SRCINFO
Normal file
@@ -0,0 +1,21 @@
|
||||
pkgbase = cursor-free-vip-git
|
||||
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
|
||||
pkgver = 1.9.05
|
||||
pkgrel = 1
|
||||
url = https://github.com/yeongpin/cursor-free-vip
|
||||
arch = x86_64
|
||||
license = MIT
|
||||
license = Attribution-NonCommercial-NoDerivatives 4.0 International
|
||||
makedepends = git
|
||||
makedepends = python
|
||||
makedepends = pyinstaller
|
||||
makedepends = uv
|
||||
depends = python
|
||||
depends = cursor-bin
|
||||
provides = cursor-free-vip
|
||||
source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git
|
||||
source = https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE
|
||||
sha256sums = SKIP
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = cursor-free-vip-git
|
||||
2
.github/ISSUE_TEMPLATE/cn_bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/cn_bug_report.yml
vendored
@@ -43,7 +43,7 @@ body:
|
||||
attributes:
|
||||
label: 版本
|
||||
description: 您正在运行的 Cursor Free Vip 版本是什么?
|
||||
placeholder: 例如 v1.0.0
|
||||
placeholder: 例如 v1.0.0 ( 不是 Cursor AI 版本 )
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/en_bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/en_bug_report.yml
vendored
@@ -43,7 +43,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Cursor Free Vip are you running?
|
||||
placeholder: For example v1.0.0
|
||||
placeholder: For example v1.0.0 ( Not Cursor AI Version )
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
155
.github/workflows/build.yml
vendored
155
.github/workflows/build.yml
vendored
@@ -3,10 +3,14 @@ name: Build Executables
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version number (e.g. 1.0.9)'
|
||||
use_env_version:
|
||||
description: 'Use version from .env file (yes/no)'
|
||||
required: true
|
||||
default: '1.8.07'
|
||||
default: 'yes'
|
||||
version:
|
||||
description: 'Version number (only used if not using .env version)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -14,27 +18,65 @@ permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
determine-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.set-version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Get version from .env file
|
||||
id: env-version
|
||||
if: ${{ github.event.inputs.use_env_version == 'yes' }}
|
||||
run: |
|
||||
VERSION=$(grep "^version=" .env | cut -d'=' -f2)
|
||||
echo "ENV_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Using version from .env file: $VERSION"
|
||||
|
||||
- name: Use manual version
|
||||
id: manual-version
|
||||
if: ${{ github.event.inputs.use_env_version != 'yes' }}
|
||||
run: |
|
||||
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
||||
echo "Using manually entered version: ${{ github.event.inputs.version }}"
|
||||
|
||||
- name: Set final version
|
||||
id: set-version
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.use_env_version }}" == "yes" ]; then
|
||||
echo "version=${{ env.ENV_VERSION }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
create-tag:
|
||||
needs: determine-version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # 获取所有标签
|
||||
|
||||
- name: Delete existing tag if exists
|
||||
- name: Check if tag exists
|
||||
id: check_tag
|
||||
run: |
|
||||
if git ls-remote --tags origin | grep -q "refs/tags/v${{ github.event.inputs.version }}"; then
|
||||
git push origin --delete "v${{ github.event.inputs.version }}" || true
|
||||
git tag -d "v${{ github.event.inputs.version }}" || true
|
||||
if git ls-remote --tags origin | grep -q "refs/tags/v${{ needs.determine-version.outputs.version }}"; then
|
||||
echo "Tag v${{ needs.determine-version.outputs.version }} already exists, will use existing tag"
|
||||
echo "tag_exists=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Tag v${{ needs.determine-version.outputs.version }} does not exist, will create new tag"
|
||||
echo "tag_exists=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create Tag
|
||||
- name: Create Tag if not exists
|
||||
if: steps.check_tag.outputs.tag_exists == 'false'
|
||||
run: |
|
||||
git tag "v${{ github.event.inputs.version }}"
|
||||
git push origin "v${{ github.event.inputs.version }}"
|
||||
git tag "v${{ needs.determine-version.outputs.version }}"
|
||||
git push origin "v${{ needs.determine-version.outputs.version }}"
|
||||
|
||||
build-windows:
|
||||
needs: create-tag
|
||||
needs: [determine-version, create-tag]
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
@@ -47,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -66,7 +108,7 @@ jobs:
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
|
||||
build-macos-arm64:
|
||||
needs: create-tag
|
||||
needs: [determine-version, create-tag]
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
@@ -79,7 +121,7 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -99,7 +141,7 @@ jobs:
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
|
||||
build-linux-x64:
|
||||
needs: create-tag
|
||||
needs: [determine-version, create-tag]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
@@ -112,7 +154,7 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -136,7 +178,7 @@ jobs:
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
|
||||
|
||||
build-linux-arm64:
|
||||
needs: create-tag
|
||||
needs: [determine-version, create-tag]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -149,11 +191,11 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build in ARM64 Docker container
|
||||
run: |
|
||||
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
|
||||
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.10-slim bash -c "
|
||||
apt-get update && apt-get install -y build-essential
|
||||
pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
@@ -171,7 +213,7 @@ jobs:
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
|
||||
|
||||
build-macos-intel:
|
||||
needs: create-tag
|
||||
needs: [determine-version, create-tag]
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
@@ -184,7 +226,7 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -207,31 +249,82 @@ jobs:
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
|
||||
create-release:
|
||||
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
|
||||
needs: [determine-version, build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Prepare release files
|
||||
- name: Calculate SHA256 checksums
|
||||
run: |
|
||||
cd artifacts
|
||||
echo "Contents of artifacts directory:"
|
||||
ls -la
|
||||
echo "Contents of subdirectories:"
|
||||
ls -la */
|
||||
mkdir -p checksums
|
||||
for file in artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
do
|
||||
if [ -f "$file" ]; then
|
||||
filename=$(basename $file)
|
||||
sha256sum "$file" | cut -d ' ' -f 1 > checksums/${filename}.sha256
|
||||
echo "${filename}: $(cat checksums/${filename}.sha256)" >> checksums/all_checksums.txt
|
||||
else
|
||||
echo "Warning: File $file not found"
|
||||
fi
|
||||
done
|
||||
cat checksums/all_checksums.txt
|
||||
|
||||
- name: Extract release notes from CHANGELOG
|
||||
run: |
|
||||
version_pattern="## v${{ env.VERSION }}"
|
||||
next_version_pattern="## v"
|
||||
|
||||
# Find the start line number of the current version
|
||||
start_line=$(grep -n "$version_pattern" CHANGELOG.md | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$start_line" ]; then
|
||||
echo "Error: Version ${{ env.VERSION }} not found in CHANGELOG.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find the line number of the next version
|
||||
next_version_line=$(tail -n +$((start_line + 1)) CHANGELOG.md | grep -n "$next_version_pattern" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$next_version_line" ]; then
|
||||
# If there's no next version, get to the end of the file
|
||||
changelog_content=$(tail -n +$start_line CHANGELOG.md)
|
||||
else
|
||||
# Extract content between current version and next version
|
||||
end_line=$((start_line + next_version_line - 1))
|
||||
changelog_content=$(sed -n "${start_line},${end_line}p" CHANGELOG.md)
|
||||
fi
|
||||
|
||||
# Create release notes file
|
||||
{
|
||||
echo "$changelog_content"
|
||||
echo ""
|
||||
echo "## SHA256 Checksums"
|
||||
cat checksums/all_checksums.txt
|
||||
} > release_notes.md
|
||||
|
||||
# Display release notes for debugging
|
||||
cat release_notes.md
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
body_path: release_notes.md
|
||||
files: |
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
@@ -241,4 +334,4 @@ jobs:
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
125
CHANGELOG.md
125
CHANGELOG.md
@@ -1,5 +1,128 @@
|
||||
# Change Log
|
||||
|
||||
## v1.11.01
|
||||
0. Must Update to this version to get full experience | 必須更新到此版本以獲取完整體驗
|
||||
1. Restore: Some Main Code | 恢復一些主程式碼
|
||||
2. Add: Arabic language | 增加阿拉伯語
|
||||
3. Add: Language configuration saved setting | 增加語言配置保存設定
|
||||
4. Add: Restore Machine ID from Backup | 增加從備份恢復機器ID
|
||||
5. Add: Owned Website Check Version | 增加擁有網站檢查版本
|
||||
6. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
|
||||
7. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
|
||||
8. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.10.05
|
||||
1. Remove block_domain.txt | 移除 block_domain.txt
|
||||
2. Original Code In Github , If u afraid of virus, please clone the code and run locally | 原始碼在 Github 上,如果怕病毒,請複製原始碼並在本機運行
|
||||
3. All Action using github workflow , not build myself , so i cant place virus in the file | 所有 Action 使用 github workflow ,不是我自己 build 的,所以我不會在文件中放置病毒
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
|
||||
## v1.10.04
|
||||
1. Hotfix: Reset Process Error: cannot access local variable 'main_path' where it is not associated with a value on windows & macos | 修復在 Windows 和 macOS 上無法訪問局部變量 'main_path' 的問題
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.10.03
|
||||
1. Add: Manual Registration | 增加手動註冊
|
||||
2. Only support your own Email | 只支持自己的Email 請勿使用Temp Email 註冊 註冊假賬號.
|
||||
3. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path
|
||||
4. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file
|
||||
5. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.10.02
|
||||
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
|
||||
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
|
||||
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
|
||||
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
|
||||
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
|
||||
6. Use & Cherish | 切用且珍惜
|
||||
7. Same as v1.10.01 | 與 v1.10.01 相同
|
||||
8. Fix: reset machine ID no module name 'new_signup' | 修復機器 ID 重置 no module name 'new_signup'
|
||||
9. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.10.01
|
||||
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
|
||||
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
|
||||
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
|
||||
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
|
||||
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
|
||||
6. Use & Cherish | 切用且珍惜
|
||||
7. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.05
|
||||
1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。
|
||||
2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。
|
||||
3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤
|
||||
4. Sync AUR new version 1.9.04 | 同步 AUR 新版本 1.9.04
|
||||
5. Fix: missing license install on pkgbuild @michaeldavis246611119 mention here | 修復 pkgbuild 中缺少授權安裝 @michaeldavis246611119 提到這裡
|
||||
6. Fix: readme table | 修復 readme 表格
|
||||
7. Fix: google-chrome package name problem, add "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 | 修復 google-chrome 包名稱問題,添加 "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616
|
||||
8. Fix: exception error log | 修復異常錯誤日誌
|
||||
9. Fix: github oauth error [Bug]: #564 | 修復 github oauth 錯誤 [Bug]: #564
|
||||
10. Fix: ChromiumOptions.arguments type error: list object has no attribute 'get' | 修復 ChromiumOptions.arguments 類型錯誤:list 對象沒有屬性 'get'
|
||||
11. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.04
|
||||
1. Add: Opera GX Support | 添加 Opera GX 支持
|
||||
2. Same as v1.9.03 | 與 v1.9.03 相同
|
||||
3. Hotfix: Some Issues | 修復一些問題
|
||||
4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.03[Skip & Merge to v1.9.04]
|
||||
1. Hotfix: Some Issues | 修復一些問題
|
||||
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.02
|
||||
1. Add: Bypass Token Limit | 添加繞過 Token 限制
|
||||
2. Add: More Browser Support | 添加更多瀏覽器支持
|
||||
3. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
4. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
|
||||
5. Add config manual browser path | 添加配置手動選擇遊覽器路徑
|
||||
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
|
||||
6. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
7. Fix: Config File Path | 修復配置文件路徑
|
||||
8. Fix: window user permission | 修復 window 用戶權限
|
||||
9. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.01
|
||||
1. Add: Bypass Token Limit | 添加繞過 Token 限制
|
||||
2. Add: More Browser Support | 添加更多瀏覽器支持
|
||||
3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
|
||||
4. Add config manual browser path | 添加配置手動選擇遊覽器路徑
|
||||
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
|
||||
## v1.8.10
|
||||
1. Add: Check User Authorized | 添加檢查用戶授權
|
||||
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
|
||||
3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統
|
||||
4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑
|
||||
5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.09
|
||||
1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查
|
||||
2. Add:Bypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5)
|
||||
3. Add: Force Update Config | 添加強制更新配置
|
||||
4. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
5. Fix: Reset break | 修復重置中斷
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.08
|
||||
1. Add: Force Update Config | 添加強制更新配置
|
||||
2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
3. Fix: Google Auth & Github Auth JWT Problem | 修復 Google Auth & Github Auth JWT 問題
|
||||
4. Fix: Totally reset import & import * raw options problem | 修復 totally reset import & import * raw 選項問題
|
||||
5. Fix: reset.file_not_found problem | 修復 reset.file_not_found 問題
|
||||
6. Outdated: Bypass Cursor Version Check | 過期:繞過 Cursor 版本檢查
|
||||
7. Document: i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx"); | 文檔:i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx");
|
||||
8. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.07
|
||||
1. Add: Bypass Cursor Version Check | 添加繞過 Cursor 版本檢查
|
||||
2. Add: Multilanguage support for bypass | 添加繞過的多語言支持
|
||||
@@ -245,7 +368,7 @@ These changes make the application more user-friendly by only requesting admin p
|
||||
1. Fix: Cursor Auth | 修復 Cursor Auth
|
||||
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
|
||||
3. Fix: Cursor Auth Error | 修復 Cursor Auth 錯誤
|
||||
4. Fix: Update Curl Faild | 修復更新 Curl 失敗
|
||||
4. Fix: Update Curl Failed | 修復更新 Curl 失敗
|
||||
|
||||
## v1.5.03
|
||||
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Import the contents of your other files
|
||||
importScripts('default_filters.js', 'block.js');
|
||||
|
||||
// Your service worker initialization code can go here
|
||||
console.log('Service worker initialized');
|
||||
129
PBlock/block.js
129
PBlock/block.js
@@ -1,129 +0,0 @@
|
||||
/**
|
||||
* All the actual functionality of the extension; loads as part of the background page.
|
||||
*
|
||||
* Active ingredient is enable(), which sets up the webRequest callbacks.
|
||||
*
|
||||
* */
|
||||
|
||||
let blockingEnabled = false;
|
||||
let allFilters = null;
|
||||
let webRTCPrivacy = null;
|
||||
|
||||
function setFilters(newFilters) {
|
||||
allFilters = newFilters;
|
||||
chrome.storage.local.set({"filters": newFilters});
|
||||
if (blockingEnabled) {
|
||||
refreshFilters();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert URL patterns to declarativeNetRequest rule format
|
||||
function createRules(filters) {
|
||||
return filters.map((filter, index) => ({
|
||||
id: index + 1,
|
||||
priority: 1,
|
||||
action: {
|
||||
type: "block"
|
||||
},
|
||||
condition: {
|
||||
urlFilter: filter.replace("*://", "*"),
|
||||
resourceTypes: [
|
||||
"main_frame", "sub_frame", "stylesheet", "script", "image",
|
||||
"font", "object", "xmlhttprequest", "ping", "media", "websocket"
|
||||
]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async function enable(icon = true) {
|
||||
if (blockingEnabled) return;
|
||||
|
||||
if (allFilters && allFilters.length > 0) {
|
||||
const rules = createRules(allFilters);
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: rules.map(rule => rule.id),
|
||||
addRules: rules
|
||||
});
|
||||
}
|
||||
|
||||
blockingEnabled = true;
|
||||
if (icon) {
|
||||
chrome.action.setIcon({
|
||||
path: {
|
||||
"16": "enabled16.png",
|
||||
"48": "enabled48.png"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function disable(icon = true) {
|
||||
if (!blockingEnabled) return;
|
||||
|
||||
const rules = await chrome.declarativeNetRequest.getDynamicRules();
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: rules.map(rule => rule.id),
|
||||
addRules: []
|
||||
});
|
||||
|
||||
blockingEnabled = false;
|
||||
if (icon) {
|
||||
chrome.action.setIcon({
|
||||
path: {
|
||||
"16": "disabled.png",
|
||||
"32": "disabled.png"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshFilters() {
|
||||
await disable(false);
|
||||
await enable(true);
|
||||
}
|
||||
|
||||
async function toggleEnabled() {
|
||||
if (blockingEnabled) {
|
||||
await disable();
|
||||
} else {
|
||||
await enable();
|
||||
}
|
||||
}
|
||||
|
||||
function setWebRTCPrivacy(flag, store = true) {
|
||||
webRTCPrivacy = flag;
|
||||
const privacySetting = flag ? "default_public_interface_only" : "default";
|
||||
chrome.privacy.network.webRTCIPHandlingPolicy.set({value: privacySetting});
|
||||
if (store) {
|
||||
chrome.storage.local.set({"webrtc_privacy": flag});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization
|
||||
chrome.storage.local.get("filters",
|
||||
function(result) {
|
||||
if (result["filters"] == undefined) {
|
||||
console.log("Initializing filters to defaults.");
|
||||
setFilters(defaultFilters);
|
||||
} else {
|
||||
setFilters(result["filters"]);
|
||||
allFilters = result["filters"];
|
||||
}
|
||||
|
||||
// toggle blocking on-off via the extension icon
|
||||
chrome.action.onClicked.addListener(toggleEnabled);
|
||||
// initialize blocking
|
||||
enable();
|
||||
}
|
||||
);
|
||||
|
||||
chrome.storage.local.get("webrtc_privacy",
|
||||
function(result) {
|
||||
if (result["webrtc_privacy"] == undefined) {
|
||||
console.log("Initializing WebRTC privacy to default.");
|
||||
setWebRTCPrivacy(false, true);
|
||||
} else {
|
||||
setWebRTCPrivacy(result["webrtc_privacy"], false);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
defaultFilters = [
|
||||
// personally, I can't stand the like box
|
||||
//"http://www.facebook.com/plugins/likebox.php?*",
|
||||
"*://*.doubleclick.net/*",
|
||||
"*://partner.googleadservices.com/*",
|
||||
"*://*.googlesyndication.com/*",
|
||||
"*://*.google-analytics.com/*",
|
||||
"*://creative.ak.fbcdn.net/*",
|
||||
"*://*.adbrite.com/*",
|
||||
"*://*.exponential.com/*",
|
||||
"*://*.quantserve.com/*",
|
||||
"*://*.scorecardresearch.com/*",
|
||||
"*://*.zedo.com/*",
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 850 B |
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "PBlock",
|
||||
"version": "0.1",
|
||||
"manifest_version": 3,
|
||||
"author": "yeongpin",
|
||||
"url": "https://github.com/yeongpin/PBlock",
|
||||
"description": "Just a simple blocker.",
|
||||
"action": {
|
||||
"default_icon": "enabled48.png"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"icons": {
|
||||
"16": "enabled16.png",
|
||||
"48": "enabled48.png"
|
||||
},
|
||||
"permissions": [
|
||||
"declarativeNetRequest",
|
||||
"storage",
|
||||
"privacy"
|
||||
],
|
||||
"host_permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*",
|
||||
"ws://*/*",
|
||||
"wss://*/*"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "ruleset_1",
|
||||
"version": "1.0",
|
||||
"rules": []
|
||||
}
|
||||
34
PKGBUILD
Normal file
34
PKGBUILD
Normal file
@@ -0,0 +1,34 @@
|
||||
# Maintainer: Canmi21 <9997200@qq.com>
|
||||
# Contributor: Canmi (Canmi21)
|
||||
|
||||
pkgname=cursor-free-vip-git
|
||||
pkgver=1.9.05
|
||||
pkgrel=1
|
||||
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/yeongpin/cursor-free-vip"
|
||||
license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
|
||||
depends=('python' 'cursor-bin')
|
||||
makedepends=('git' 'python' 'pyinstaller' 'uv')
|
||||
provides=('cursor-free-vip')
|
||||
source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git" "https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE")
|
||||
sha256sums=('SKIP' 'SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "$srcdir/cursor-free-vip"
|
||||
git describe --tags --always | sed 's/^v//;s/-/./g'
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "$srcdir/cursor-free-vip"
|
||||
uv venv .venv
|
||||
source .venv/bin/activate
|
||||
uv pip install -r requirements.txt
|
||||
pyinstaller --clean --noconfirm --onefile main.py --name cursor-free-vip
|
||||
}
|
||||
|
||||
package() {
|
||||
install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
|
||||
install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license"
|
||||
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
|
||||
}
|
||||
96
README.md
96
README.md
@@ -7,33 +7,42 @@
|
||||
|
||||
<p align="center">
|
||||
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
<a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a>
|
||||
|
||||
</p>
|
||||
|
||||
<a href="https://trendshift.io/repositories/13425" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13425" alt="yeongpin%2Fcursor-free-vip | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
<br>
|
||||
<a href="https://www.buymeacoffee.com/yeongpin" target="_blank">
|
||||
<img src="https://img.buymeacoffee.com/button-api/?text=buy me a coffee&emoji=☕&slug=yeongpin&button_colour=ffda33&font_colour=000000&font_family=Bree&outline_colour=000000&coffee_colour=FFDD00&latest=2" width="160" height='55' alt="Buy Me a Coffee"/>
|
||||
</a>
|
||||
|
||||
|
||||
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
|
||||
|
||||
This tool registers accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes, resets and wipes Cursor data and hardware info.
|
||||
This tool is for educational purposes, currently the repo does not violate any laws. Please support the original project.
|
||||
This tool will not generate any fake email accounts and OAuth access.
|
||||
|
||||
Supports Windows, macOS and Linux.
|
||||
|
||||
For optimal performance, run with privileges and always stay up to date.
|
||||
|
||||
Always clean your browser's cache and cookies. If possible, use a VPN to create new accounts.
|
||||
這是一款用於學習和研究的工具,目前 repo 沒有違反任何法律。請支持原作者。
|
||||
這款工具不會生成任何假的電子郵件帳戶和 OAuth 訪問。
|
||||
|
||||
支持 Windows、macOS 和 Linux。
|
||||
|
||||
對於最佳性能,請以管理員身份運行並始終保持最新。
|
||||
|
||||
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
|
||||
<img src="./images/product_2025-04-16_10-40-21.png" alt="new" width="800" style="border-radius: 6px;"/><br>
|
||||
</p>
|
||||
|
||||
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||
|
||||
##### 如果沒有 Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||
|
||||
</div>
|
||||
|
||||
## 🔄 Change Log | 更新日志
|
||||
@@ -42,42 +51,40 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
|
||||
|
||||
## ✨ Features | 功能特點
|
||||
|
||||
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
|
||||
|
||||
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
|
||||
|
||||
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
|
||||
|
||||
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
|
||||
|
||||
* Complete Auth verification<br>完成 Auth 驗證<br>
|
||||
* Support Windows macOS and Linux systems<br>支持 Windows、macOS 和 Linux 系統<br>
|
||||
|
||||
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
|
||||
|
||||
* Delete Cursor Google Account<br>删除 Cursor Google 账号<br>
|
||||
|
||||
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
|
||||
|
||||
## 💻 System Support | 系統支持
|
||||
|
||||
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|
||||
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
|
||||
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
|
||||
| Linux | x64 | ✅ | Linux | x86 | ✅ |
|
||||
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
|
||||
| Operating System | Architecture | Supported |
|
||||
|------------------|-------------------|-----------|
|
||||
| Windows | x64, x86 | ✅ |
|
||||
| macOS | Intel, Apple Silicon | ✅ |
|
||||
| Linux | x64, x86, ARM64 | ✅ |
|
||||
|
||||
## 👀 How to use | 如何使用
|
||||
|
||||
<details open>
|
||||
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
### **Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
**Windows**
|
||||
### **Archlinux**
|
||||
|
||||
Install via [AUR](https://aur.archlinux.org/packages/cursor-free-vip-git)
|
||||
|
||||
```bash
|
||||
yay -S cursor-free-vip-git
|
||||
```
|
||||
|
||||
### **Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
||||
@@ -88,13 +95,13 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/inst
|
||||
<details>
|
||||
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
### **Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
|
||||
```
|
||||
|
||||
**Windows**
|
||||
### **Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
|
||||
@@ -166,6 +173,33 @@ max_timeout = 160
|
||||
check_update = True
|
||||
# Show Account Info | 顯示賬號信息
|
||||
show_account_info = True
|
||||
|
||||
[WindowsPaths]
|
||||
storage_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\storage.json
|
||||
sqlite_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\state.vscdb
|
||||
machine_id_path = C:\Users\yeongpin\AppData\Roaming\Cursor\machineId
|
||||
cursor_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app
|
||||
updater_path = C:\Users\yeongpin\AppData\Local\cursor-updater
|
||||
update_yml_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app-update.yml
|
||||
product_json_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app\product.json
|
||||
|
||||
[Browser]
|
||||
default_browser = opera
|
||||
chrome_path = C:\Program Files\Google\Chrome\Application\chrome.exe
|
||||
edge_path = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
|
||||
firefox_path = C:\Program Files\Mozilla Firefox\firefox.exe
|
||||
brave_path = C:\Program Files\BraveSoftware/Brave-Browser/Application/brave.exe
|
||||
chrome_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
edge_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\msedgedriver.exe
|
||||
firefox_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\geckodriver.exe
|
||||
brave_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
opera_path = C:\Users\yeongpin\AppData\Local\Programs\Opera\opera.exe
|
||||
opera_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
|
||||
[OAuth]
|
||||
show_selection_alert = False
|
||||
timeout = 120
|
||||
max_attempts = 3
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -1,83 +1,134 @@
|
||||
oakon.com
|
||||
famamail.com
|
||||
2925.com
|
||||
indigobook.com
|
||||
teihu.com
|
||||
raleigh-construction.com
|
||||
pastryofistanbul.com
|
||||
linshiyouxiang.net
|
||||
Mohmal.com
|
||||
pusmail.com
|
||||
questtechsystems.com
|
||||
ikomail.com
|
||||
ofanda.com
|
||||
pusmail.com
|
||||
ikomail.com
|
||||
mailpull.com
|
||||
drewzen.com
|
||||
begemail.com
|
||||
dugmail.com
|
||||
solerbe.net
|
||||
corhash.net
|
||||
mailshou.com
|
||||
0-mail.com
|
||||
10minemail.com
|
||||
1secmail.com
|
||||
20minutemail.com
|
||||
2925.com
|
||||
2prong.com
|
||||
33mail.com
|
||||
abusemail.de
|
||||
afrobacon.com
|
||||
anonbox.net
|
||||
anonymbox.com
|
||||
antichef.com
|
||||
bareed.ws
|
||||
begemail.com
|
||||
boun.cr
|
||||
brefmail.com
|
||||
burnermail.io
|
||||
byom.de
|
||||
chammy.info
|
||||
cloud-mail.top
|
||||
cocovpn.com
|
||||
cool.fr.nf
|
||||
corhash.net
|
||||
crazymailing.com
|
||||
cuvox.de
|
||||
dayrep.com
|
||||
deadaddress.com
|
||||
discard.email
|
||||
dispostable.com
|
||||
drewzen.com
|
||||
dudmail.com
|
||||
dugmail.com
|
||||
emailondeck.com
|
||||
emailtemporario.com.br
|
||||
ephemail.net
|
||||
fakeinbox.com
|
||||
fakeinbox.org
|
||||
fakemailgenerator.com
|
||||
famamail.com
|
||||
fastmailbox.net
|
||||
filzmail.com
|
||||
fizmail.com
|
||||
getairmail.com
|
||||
getnada.com
|
||||
givmail.com
|
||||
guerrillamail.com
|
||||
gustr.com
|
||||
harakirimail.com
|
||||
hottempmail.com
|
||||
ikomail.com
|
||||
inboxbear.com
|
||||
inboxkitten.com
|
||||
incognitomail.org
|
||||
indigobook.com
|
||||
jetable.org
|
||||
kaspop.com
|
||||
letthemeatspam.com
|
||||
linshiyouxiang.net
|
||||
luxusmail.org
|
||||
mail-temp.com
|
||||
mail1a.de
|
||||
mailbucket.org
|
||||
mailcatch.com
|
||||
maildrop.cc
|
||||
mailexpire.com
|
||||
mailhazard.com
|
||||
mailimate.com
|
||||
mailin8r.com
|
||||
mailinator.com
|
||||
mailme.lv
|
||||
mailnesia.com
|
||||
mailnull.com
|
||||
mailpull.com
|
||||
mailsac.com
|
||||
mailshou.com
|
||||
mailtemp.net
|
||||
mailzilla.org
|
||||
meltmail.com
|
||||
mintemail.com
|
||||
moakt.com
|
||||
mohmal.com
|
||||
my10minutemail.com
|
||||
mycleaninbox.net
|
||||
mytrashmail.com
|
||||
no-spam.ws
|
||||
nomail.pw
|
||||
nospamfor.us
|
||||
notmailinator.com
|
||||
nowmymail.com
|
||||
oakon.com
|
||||
objectmail.com
|
||||
ofanda.com
|
||||
openmailbox.org
|
||||
owlpic.com
|
||||
pastryofistanbul.com
|
||||
privacyroot.com
|
||||
pusmail.com
|
||||
questtechsystems.com
|
||||
raleigh-construction.com
|
||||
rcpt.at
|
||||
safemail.link
|
||||
sendspamhere.com
|
||||
sharklasers.com
|
||||
shortmail.net
|
||||
solerbe.net
|
||||
spam4.me
|
||||
spamavert.com
|
||||
spambog.com
|
||||
spamdecoy.net
|
||||
spamex.com
|
||||
spamfree24.org
|
||||
spamgourmet.com
|
||||
spamhereplease.com
|
||||
spaml.com
|
||||
spamslicer.com
|
||||
spamsphere.com
|
||||
spamtroll.net
|
||||
teihu.com
|
||||
temp-mail.org
|
||||
tempmail.net
|
||||
tempmailaddress.com
|
||||
temporaryemail.net
|
||||
throwawayemail.com
|
||||
tmail.ws
|
||||
trash-mail.com
|
||||
trash2009.com
|
||||
trashdevil.com
|
||||
trashmail.com
|
||||
trashmail.de
|
||||
trbvn.com
|
||||
wegwerfadresse.org
|
||||
yepmail.net
|
||||
yopmail.com
|
||||
zippymail.info
|
||||
zippymail.info
|
||||
20
build.spec
20
build.spec
@@ -23,29 +23,13 @@ a = Analysis(
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[
|
||||
('turnstilePatch', 'turnstilePatch'),
|
||||
('PBlock', 'PBlock'),
|
||||
('locales', 'locales'),
|
||||
('cursor_auth.py', '.'),
|
||||
('reset_machine_manual.py', '.'),
|
||||
('cursor_register.py', '.'),
|
||||
('new_signup.py', '.'),
|
||||
('new_tempemail.py', '.'),
|
||||
('quit_cursor.py', '.'),
|
||||
('cursor_register_manual.py', '.'),
|
||||
('oauth_auth.py', '.'),
|
||||
('utils.py', '.'),
|
||||
('.env', '.'),
|
||||
('block_domain.txt', '.')
|
||||
('.env', '.')
|
||||
],
|
||||
hiddenimports=[
|
||||
'cursor_auth',
|
||||
'reset_machine_manual',
|
||||
'new_signup',
|
||||
'new_tempemail',
|
||||
'quit_cursor',
|
||||
'cursor_register_manual',
|
||||
'oauth_auth',
|
||||
'utils'
|
||||
],
|
||||
hookspath=[],
|
||||
@@ -69,7 +53,7 @@ exe = EXE(
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
|
||||
194
bypass_token_limit.py
Normal file
194
bypass_token_limit.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
import tempfile
|
||||
import glob
|
||||
from colorama import Fore, Style, init
|
||||
import configparser
|
||||
import sys
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user Documents folder path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
else: # Linux
|
||||
# Get actual user's home directory
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
return os.path.join("/home", sudo_user, "Documents")
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
|
||||
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Add extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
|
||||
if system not in paths_map:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if system == "Linux":
|
||||
for base in paths_map["Linux"]["bases"]:
|
||||
main_path = os.path.join(base, paths_map["Linux"]["main"])
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
|
||||
if os.path.exists(main_path):
|
||||
return main_path
|
||||
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
|
||||
base_path = config.get('MacPaths', 'cursor_path')
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
|
||||
base_path = config.get('LinuxPaths', 'cursor_path')
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
|
||||
|
||||
return main_path
|
||||
|
||||
|
||||
def modify_workbench_js(file_path: str, translator=None) -> bool:
|
||||
"""
|
||||
Modify file content
|
||||
"""
|
||||
try:
|
||||
# Save original file permissions
|
||||
original_stat = os.stat(file_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
# Create temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
|
||||
# Read original content
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Mac 通用按钮替换模式
|
||||
r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)': r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
shutil.move(tmp_path, file_path)
|
||||
|
||||
# Restore original permissions
|
||||
os.chmod(file_path, original_mode)
|
||||
if os.name != "nt": # Not Windows
|
||||
os.chown(file_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
return False
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('bypass_token_limit.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
modify_workbench_js(get_workbench_cursor_path(translator), translator)
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('bypass_token_limit.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
@@ -50,6 +50,8 @@ def get_product_json_path(translator=None):
|
||||
|
||||
elif system == "Darwin": # macOS
|
||||
product_json_path = "/Applications/Cursor.app/Contents/Resources/app/product.json"
|
||||
if config.has_section('MacPaths') and config.has_option('MacPaths', 'product_json_path'):
|
||||
product_json_path = config.get('MacPaths', 'product_json_path')
|
||||
|
||||
elif system == "Linux":
|
||||
# Try multiple common paths
|
||||
|
||||
214
check_user_authorized.py
Normal file
214
check_user_authorized.py
Normal file
@@ -0,0 +1,214 @@
|
||||
import os
|
||||
import requests
|
||||
import time
|
||||
import hashlib
|
||||
import base64
|
||||
import struct
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"KEY": "🔑",
|
||||
"CHECK": "🔍"
|
||||
}
|
||||
|
||||
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
|
||||
"""Generate a SHA-256 hash of input + salt and return as hex"""
|
||||
hash_obj = hashlib.sha256()
|
||||
hash_obj.update((input_str + salt).encode('utf-8'))
|
||||
return hash_obj.hexdigest()
|
||||
|
||||
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
|
||||
"""Obfuscate bytes using the algorithm from utils.js"""
|
||||
t = 165
|
||||
for r in range(len(byte_array)):
|
||||
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
|
||||
t = byte_array[r]
|
||||
return byte_array
|
||||
|
||||
def generate_cursor_checksum(token: str, translator=None) -> str:
|
||||
"""Generate Cursor checksum from token using the algorithm"""
|
||||
try:
|
||||
# Clean the token
|
||||
clean_token = token.strip()
|
||||
|
||||
# Generate machineId and macMachineId
|
||||
machine_id = generate_hashed64_hex(clean_token, 'machineId')
|
||||
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
|
||||
|
||||
# Get timestamp and convert to byte array
|
||||
timestamp = int(time.time() * 1000) // 1000000
|
||||
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
|
||||
|
||||
# Obfuscate bytes and encode as base64
|
||||
obfuscated_bytes = obfuscate_bytes(byte_array)
|
||||
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
|
||||
|
||||
# Combine final checksum
|
||||
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
|
||||
return ""
|
||||
|
||||
def check_user_authorized(token: str, translator=None) -> bool:
|
||||
"""
|
||||
Check if the user is authorized with the given token
|
||||
|
||||
Args:
|
||||
token (str): The authorization token
|
||||
translator: Optional translator for internationalization
|
||||
|
||||
Returns:
|
||||
bool: True if authorized, False otherwise
|
||||
"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
|
||||
|
||||
# Clean the token
|
||||
if token and '%3A%3A' in token:
|
||||
token = token.split('%3A%3A')[1]
|
||||
elif token and '::' in token:
|
||||
token = token.split('::')[1]
|
||||
|
||||
# Remove any whitespace
|
||||
token = token.strip()
|
||||
|
||||
if not token or len(token) < 10: # Add a basic validation for token length
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
|
||||
|
||||
# Try to get usage info using the DashboardService API
|
||||
try:
|
||||
# Generate checksum
|
||||
checksum = generate_cursor_checksum(token, translator)
|
||||
|
||||
# Create request headers
|
||||
headers = {
|
||||
'accept-encoding': 'gzip',
|
||||
'authorization': f'Bearer {token}',
|
||||
'connect-protocol-version': '1',
|
||||
'content-type': 'application/proto',
|
||||
'user-agent': 'connect-es/1.6.1',
|
||||
'x-cursor-checksum': checksum,
|
||||
'x-cursor-client-version': '0.48.7',
|
||||
'x-cursor-timezone': 'Asia/Shanghai',
|
||||
'x-ghost-mode': 'false',
|
||||
'Host': 'api2.cursor.sh'
|
||||
}
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
|
||||
|
||||
# Make the request - this endpoint doesn't need a request body
|
||||
usage_response = requests.post(
|
||||
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
|
||||
headers=headers,
|
||||
data=b'', # Empty body
|
||||
timeout=10
|
||||
)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
if usage_response.status_code == 200:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
|
||||
return True
|
||||
elif usage_response.status_code == 401 or usage_response.status_code == 403:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
"""Run function to be called from main.py"""
|
||||
try:
|
||||
# Ask user if they want to get token from database or input manually
|
||||
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
|
||||
|
||||
token = None
|
||||
|
||||
# If user chooses database or default
|
||||
if not choice or choice == 'd':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Import functions from cursor_acc_info.py
|
||||
from cursor_acc_info import get_token
|
||||
|
||||
# Get token using the get_token function
|
||||
token = get_token()
|
||||
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
|
||||
except ImportError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# If token not found in database or user chooses manual input
|
||||
if not token:
|
||||
# Try to get token from environment
|
||||
token = os.environ.get('CURSOR_TOKEN')
|
||||
|
||||
# If not in environment, ask user to input
|
||||
if not token:
|
||||
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
|
||||
|
||||
# Check authorization
|
||||
is_authorized = check_user_authorized(token, translator)
|
||||
|
||||
if is_authorized:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
|
||||
|
||||
return is_authorized
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to check user authorization"""
|
||||
return run(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
125
config.py
125
config.py
@@ -2,7 +2,9 @@ import os
|
||||
import sys
|
||||
import configparser
|
||||
from colorama import Fore, Style
|
||||
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
|
||||
from utils import get_user_documents_path, get_linux_cursor_path, get_default_driver_path, get_default_browser_path
|
||||
import shutil
|
||||
import datetime
|
||||
|
||||
EMOJI = {
|
||||
"INFO": "ℹ️",
|
||||
@@ -16,19 +18,59 @@ EMOJI = {
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
# global config cache
|
||||
_config_cache = None
|
||||
|
||||
def setup_config(translator=None):
|
||||
"""Setup configuration file and return config object"""
|
||||
try:
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
# get documents path
|
||||
docs_path = get_user_documents_path()
|
||||
if not docs_path or not os.path.exists(docs_path):
|
||||
# if documents path not found, use current directory
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.documents_path_not_found', fallback='Documents path not found, using current directory') if translator else 'Documents path not found, using current directory'}{Style.RESET_ALL}")
|
||||
docs_path = os.path.abspath('.')
|
||||
|
||||
# normalize path
|
||||
config_dir = os.path.normpath(os.path.join(docs_path, ".cursor-free-vip"))
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
|
||||
# create config directory, only print message when directory not exists
|
||||
dir_exists = os.path.exists(config_dir)
|
||||
try:
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not dir_exists: # only print message when directory not exists
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_dir_created', path=config_dir) if translator else f'Config directory created: {config_dir}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
# if cannot create directory, use temporary directory
|
||||
import tempfile
|
||||
temp_dir = os.path.normpath(os.path.join(tempfile.gettempdir(), ".cursor-free-vip"))
|
||||
temp_exists = os.path.exists(temp_dir)
|
||||
config_dir = temp_dir
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not temp_exists: # only print message when temporary directory not exists
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.using_temp_dir', path=config_dir, error=str(e)) if translator else f'Using temporary directory due to error: {config_dir} (Error: {str(e)})'}{Style.RESET_ALL}")
|
||||
|
||||
# create config object
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# Default configuration
|
||||
default_config = {
|
||||
'Chrome': {
|
||||
'chromepath': get_default_chrome_path()
|
||||
'Browser': {
|
||||
'default_browser': 'chrome',
|
||||
'chrome_path': get_default_browser_path('chrome'),
|
||||
'chrome_driver_path': get_default_driver_path('chrome'),
|
||||
'edge_path': get_default_browser_path('edge'),
|
||||
'edge_driver_path': get_default_driver_path('edge'),
|
||||
'firefox_path': get_default_browser_path('firefox'),
|
||||
'firefox_driver_path': get_default_driver_path('firefox'),
|
||||
'brave_path': get_default_browser_path('brave'),
|
||||
'brave_driver_path': get_default_driver_path('brave'),
|
||||
'opera_path': get_default_browser_path('opera'),
|
||||
'opera_driver_path': get_default_driver_path('opera'),
|
||||
'operagx_path': get_default_browser_path('operagx'),
|
||||
'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
|
||||
},
|
||||
'Turnstile': {
|
||||
'handle_turnstile_time': '2',
|
||||
@@ -52,7 +94,23 @@ def setup_config(translator=None):
|
||||
},
|
||||
'Utils': {
|
||||
'enabled_update_check': 'True',
|
||||
'enabled_force_update': 'False',
|
||||
'enabled_account_info': 'True'
|
||||
},
|
||||
'OAuth': {
|
||||
'show_selection_alert': False, # 默认不显示选择提示弹窗
|
||||
'timeout': 120,
|
||||
'max_attempts': 3
|
||||
},
|
||||
'Token': {
|
||||
'refresh_server': 'https://token.cursorpro.com.cn',
|
||||
'enable_refresh': True
|
||||
},
|
||||
'Language': {
|
||||
'current_language': '', # Set by local system detection if empty
|
||||
'fallback_language': 'en',
|
||||
'auto_update_languages': 'True',
|
||||
'language_cache_dir': os.path.join(config_dir, "language_cache")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +315,59 @@ def print_config(config, translator=None):
|
||||
|
||||
print()
|
||||
|
||||
def force_update_config(translator=None):
|
||||
"""
|
||||
Force update configuration file with latest defaults if update check is enabled.
|
||||
Args:
|
||||
translator: Translator instance
|
||||
Returns:
|
||||
ConfigParser instance or None if failed
|
||||
"""
|
||||
try:
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
current_time = datetime.datetime.now()
|
||||
|
||||
# If the config file exists, check if forced update is enabled
|
||||
if os.path.exists(config_file):
|
||||
# First, read the existing configuration
|
||||
existing_config = configparser.ConfigParser()
|
||||
existing_config.read(config_file, encoding='utf-8')
|
||||
# Check if "enabled_update_check" is True
|
||||
update_enabled = True # Default to True if not set
|
||||
if existing_config.has_section('Utils') and existing_config.has_option('Utils', 'enabled_force_update'):
|
||||
update_enabled = existing_config.get('Utils', 'enabled_force_update').strip().lower() in ('true', 'yes', '1', 'on')
|
||||
|
||||
if update_enabled:
|
||||
try:
|
||||
# Create a backup
|
||||
backup_file = f"{config_file}.bak.{current_time.strftime('%Y%m%d_%H%M%S')}"
|
||||
shutil.copy2(config_file, backup_file)
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.backup_created', path=backup_file) if translator else f'Backup created: {backup_file}'}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_enabled') if translator else 'Config file force update enabled'}{Style.RESET_ALL}")
|
||||
# Delete the original config file (forced update)
|
||||
os.remove(config_file)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_removed') if translator else 'Config file removed for forced update'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.backup_failed', error=str(e)) if translator else f'Failed to backup config: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_disabled', fallback='Config file force update disabled by configuration. Keeping existing config file.') if translator else 'Config file force update disabled by configuration. Keeping existing config file.'}{Style.RESET_ALL}")
|
||||
|
||||
# Generate a new (or updated) configuration if needed
|
||||
return setup_config(translator)
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.force_update_failed', error=str(e)) if translator else f'Force update config failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
return setup_config(translator)
|
||||
global _config_cache
|
||||
if _config_cache is None:
|
||||
_config_cache = setup_config(translator)
|
||||
return _config_cache
|
||||
@@ -156,4 +156,4 @@ class CursorAuth:
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
|
||||
@@ -1,263 +0,0 @@
|
||||
import os
|
||||
from colorama import Fore, Style, init
|
||||
import time
|
||||
import random
|
||||
from cursor_auth import CursorAuth
|
||||
from reset_machine_manual import MachineIDResetter
|
||||
|
||||
os.environ["PYTHONVERBOSE"] = "0"
|
||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
'VERIFY': '🔄',
|
||||
'PASSWORD': '🔑',
|
||||
'CODE': '📱',
|
||||
'DONE': '✨',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'SUCCESS': '✅',
|
||||
'MAIL': '📧',
|
||||
'KEY': '🔐',
|
||||
'UPDATE': '🔄',
|
||||
'INFO': 'ℹ️'
|
||||
}
|
||||
|
||||
class CursorRegistration:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
# Set to display mode
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
self.controller = None
|
||||
self.mail_url = "https://yopmail.com/zh/email-generator"
|
||||
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||
self.settings_url = "https://www.cursor.com/settings"
|
||||
self.email_address = None
|
||||
self.signup_tab = None
|
||||
self.email_tab = None
|
||||
|
||||
# Account information
|
||||
self.password = self._generate_password()
|
||||
# Generate first name and last name separately
|
||||
first_name = random.choice([
|
||||
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
|
||||
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
|
||||
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
|
||||
])
|
||||
self.last_name = random.choice([
|
||||
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
|
||||
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
|
||||
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
|
||||
])
|
||||
|
||||
# Modify first letter of first name
|
||||
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
self.first_name = new_first_letter + first_name[1:]
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
|
||||
|
||||
def _generate_password(self, length=12):
|
||||
"""Generate Random Password"""
|
||||
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
||||
return ''.join(random.choices(chars, k=length))
|
||||
|
||||
def setup_email(self):
|
||||
"""Setup Email"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# Create a temporary email using new_tempemail, passing translator
|
||||
from new_tempemail import NewTempEmail
|
||||
self.temp_email = NewTempEmail(self.translator) # Pass translator
|
||||
|
||||
# Create a temporary email
|
||||
email_address = self.temp_email.create_email()
|
||||
if not email_address:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Save email address
|
||||
self.email_address = email_address
|
||||
self.email_tab = self.temp_email # Pass NewTempEmail instance
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_cursor(self):
|
||||
"""注册 Cursor"""
|
||||
browser_tab = None
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# Directly use new_signup.py to sign up
|
||||
from new_signup import main as new_signup_main
|
||||
|
||||
# Execute the new registration process, passing translator
|
||||
result, browser_tab = new_signup_main(
|
||||
email=self.email_address,
|
||||
password=self.password,
|
||||
first_name=self.first_name,
|
||||
last_name=self.last_name,
|
||||
email_tab=self.email_tab,
|
||||
controller=self.controller,
|
||||
translator=self.translator
|
||||
)
|
||||
|
||||
if result:
|
||||
# Use the returned browser instance to get account information
|
||||
self.signup_tab = browser_tab # Save browser instance
|
||||
success = self._get_account_info()
|
||||
|
||||
# Close browser after getting information
|
||||
if browser_tab:
|
||||
try:
|
||||
browser_tab.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
return success
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
# Ensure browser is closed in any case
|
||||
if browser_tab:
|
||||
try:
|
||||
browser_tab.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def _get_account_info(self):
|
||||
"""Get Account Information and Token"""
|
||||
try:
|
||||
self.signup_tab.get(self.settings_url)
|
||||
time.sleep(2)
|
||||
|
||||
usage_selector = (
|
||||
"css:div.col-span-2 > div > div > div > div > "
|
||||
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
|
||||
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
|
||||
)
|
||||
usage_ele = self.signup_tab.ele(usage_selector)
|
||||
total_usage = "未知"
|
||||
if usage_ele:
|
||||
total_usage = usage_ele.text.split("/")[-1].strip()
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.total_usage', usage=total_usage)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
|
||||
max_attempts = 30
|
||||
retry_interval = 2
|
||||
attempts = 0
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
cookies = self.signup_tab.cookies()
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
|
||||
self._save_account_info(token, total_usage)
|
||||
return True
|
||||
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _save_account_info(self, token, total_usage):
|
||||
"""Save Account Information to File"""
|
||||
try:
|
||||
# Update authentication information first
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
|
||||
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
|
||||
|
||||
# Reset machine ID
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
|
||||
resetter = MachineIDResetter(self.translator) # Pass translator when creating instance
|
||||
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
|
||||
raise Exception("Failed to reset machine ID")
|
||||
|
||||
# Save account information to file
|
||||
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n{'='*50}\n")
|
||||
f.write(f"Email: {self.email_address}\n")
|
||||
f.write(f"Password: {self.password}\n")
|
||||
f.write(f"Token: {token}\n")
|
||||
f.write(f"Usage Limit: {total_usage}\n")
|
||||
f.write(f"{'='*50}\n")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""Start Registration Process"""
|
||||
try:
|
||||
if self.setup_email():
|
||||
if self.register_cursor():
|
||||
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
# Close email tab
|
||||
if hasattr(self, 'temp_email'):
|
||||
try:
|
||||
self.temp_email.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
|
||||
"""Update Cursor Auth Info"""
|
||||
auth_manager = CursorAuth(translator=self.translator)
|
||||
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to be called from main.py"""
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
registration = CursorRegistration(translator)
|
||||
registration.start()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
main(main_translator)
|
||||
@@ -1,5 +0,0 @@
|
||||
from oauth_auth import main as oauth_main
|
||||
|
||||
def main(translator=None):
|
||||
"""Handle GitHub OAuth registration"""
|
||||
oauth_main('github', translator)
|
||||
@@ -1,5 +0,0 @@
|
||||
from oauth_auth import main as oauth_main
|
||||
|
||||
def main(translator=None):
|
||||
"""Handle Google OAuth registration"""
|
||||
oauth_main('google', translator)
|
||||
@@ -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
|
||||
|
||||
@@ -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
112
get_user_token.py
Normal 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
|
||||
@@ -1,701 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import json
|
||||
import random
|
||||
import string
|
||||
import requests
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from webdriver_manager.chrome import ChromeDriverManager
|
||||
import logging
|
||||
import platform
|
||||
from colorama import Fore, Style, init
|
||||
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
|
||||
import shutil
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
'VERIFY': '🔄',
|
||||
'PASSWORD': '🔑',
|
||||
'CODE': '📱',
|
||||
'DONE': '✨',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'SUCCESS': '✅',
|
||||
'MAIL': '📧',
|
||||
'KEY': '🔐',
|
||||
'UPDATE': '🔄',
|
||||
'INFO': 'ℹ️',
|
||||
'EMAIL': '📧',
|
||||
'REFRESH': '🔄',
|
||||
'LINK': '🔗',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
class GitHubCursorRegistration:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
# Set browser to visible mode
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
self.email_address = None
|
||||
|
||||
# Generate random credentials
|
||||
self.github_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
||||
self.github_password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=16))
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup and configure the web browser"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} Setting up browser...{Style.RESET_ALL}")
|
||||
|
||||
options = Options()
|
||||
options.add_argument('--incognito')
|
||||
options.add_argument('--no-sandbox')
|
||||
options.add_argument('--disable-dev-shm-usage')
|
||||
options.add_argument('--window-size=1920,1080')
|
||||
options.add_argument('--disable-notifications')
|
||||
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36')
|
||||
|
||||
self.browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
|
||||
self.browser.set_page_load_timeout(30)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to setup browser: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_temp_email(self):
|
||||
"""Get a temporary email address using YOPmail"""
|
||||
try:
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['MAIL']} Generating temporary email address...{Style.RESET_ALL}")
|
||||
self.browser.get("https://yopmail.com/")
|
||||
time.sleep(2)
|
||||
|
||||
# Generate a realistic username
|
||||
first_names = ["john", "sara", "michael", "emma", "david", "jennifer", "robert", "lisa"]
|
||||
last_names = ["smith", "johnson", "williams", "brown", "jones", "miller", "davis", "garcia"]
|
||||
|
||||
random_first = random.choice(first_names)
|
||||
random_last = random.choice(last_names)
|
||||
random_num = random.randint(100, 999)
|
||||
|
||||
username = f"{random_first}.{random_last}{random_num}"
|
||||
|
||||
# Enter the username and check inbox
|
||||
email_field = self.browser.find_element(By.XPATH, "//input[@id='login']")
|
||||
if email_field:
|
||||
email_field.clear()
|
||||
email_field.send_keys(username)
|
||||
time.sleep(1)
|
||||
|
||||
# Click the check button
|
||||
check_button = self.browser.find_element(By.XPATH, "//button[@title='Check Inbox' or @class='sbut' or contains(@onclick, 'ver')]")
|
||||
if check_button:
|
||||
check_button.click()
|
||||
time.sleep(2)
|
||||
self.email_address = f"{username}@yopmail.com"
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Temp email created: {self.email_address}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to create YOPmail address{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting temporary email: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_github(self):
|
||||
"""Register a new GitHub account"""
|
||||
if not self.email_address:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No email address available{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['FORM']} Registering GitHub account...{Style.RESET_ALL}")
|
||||
self.browser.get("https://github.com/join")
|
||||
time.sleep(3)
|
||||
|
||||
# Fill in the registration form
|
||||
WebDriverWait(self.browser, 15).until(EC.visibility_of_element_located((By.ID, "user_login")))
|
||||
self.browser.find_element(By.ID, "user_login").send_keys(self.github_username)
|
||||
self.browser.find_element(By.ID, "user_email").send_keys(self.email_address)
|
||||
self.browser.find_element(By.ID, "user_password").send_keys(self.github_password)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub password: {self.github_password}{Style.RESET_ALL}")
|
||||
|
||||
# Check for any notice or popup and handle it
|
||||
try:
|
||||
signup_button = self.browser.find_element(By.ID, "signup_button")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking sign up button...{Style.RESET_ALL}")
|
||||
signup_button.click()
|
||||
except NoSuchElementException:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Signup button not found, trying alternative selector{Style.RESET_ALL}")
|
||||
buttons = self.browser.find_elements(By.TAG_NAME, "button")
|
||||
for button in buttons:
|
||||
if "Sign up" in button.text:
|
||||
button.click()
|
||||
break
|
||||
|
||||
# Wait for page transition and check for CAPTCHA
|
||||
time.sleep(5)
|
||||
|
||||
# Check if registration was successful or if CAPTCHA appeared
|
||||
current_url = self.browser.current_url
|
||||
|
||||
# Look for CAPTCHA in URL or on page
|
||||
if "captcha" in current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} CAPTCHA detected, please complete it manually{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You have 60 seconds to solve the CAPTCHA...{Style.RESET_ALL}")
|
||||
|
||||
# Wait for user to solve CAPTCHA (60 seconds max)
|
||||
for i in range(60):
|
||||
current_url = self.browser.current_url
|
||||
if "captcha" not in current_url.lower() and "are you a robot" not in self.browser.page_source.lower():
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} CAPTCHA completed successfully{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
if i % 10 == 0 and i > 0:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Still waiting for CAPTCHA completion... {60-i} seconds remaining{Style.RESET_ALL}")
|
||||
|
||||
# Check if CAPTCHA was solved after waiting
|
||||
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA not solved within time limit{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want more time to solve the CAPTCHA? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've completed the CAPTCHA...{Style.RESET_ALL}")
|
||||
input()
|
||||
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA still not solved{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
# Wait for registration to complete
|
||||
time.sleep(5)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub account registered{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register GitHub account: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_email_verification(self):
|
||||
"""Check for GitHub verification email and click the verification link"""
|
||||
if not self.email_address or not self.browser:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Email or browser not available{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['EMAIL']} Checking for verification email...{Style.RESET_ALL}")
|
||||
|
||||
# Extract username from email for YOPmail
|
||||
username = self.email_address.split('@')[0]
|
||||
|
||||
max_attempts = 10
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
print(f"{Fore.CYAN}{EMOJI['REFRESH']} Checking YOPmail inbox (attempt {attempt}/{max_attempts})...{Style.RESET_ALL}")
|
||||
|
||||
# Go to YOPmail inbox
|
||||
self.browser.get(f"https://yopmail.com/en/wm")
|
||||
time.sleep(2)
|
||||
|
||||
# Enter email address
|
||||
try:
|
||||
email_input = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "login"))
|
||||
)
|
||||
email_input.clear()
|
||||
email_input.send_keys(username)
|
||||
|
||||
# Click the check inbox button
|
||||
check_button = self.browser.find_element(By.CSS_SELECTOR, "button[onclick='verif()']")
|
||||
check_button.click()
|
||||
time.sleep(3)
|
||||
|
||||
# Switch to inbox frame
|
||||
iframe = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "ifinbox"))
|
||||
)
|
||||
self.browser.switch_to.frame(iframe)
|
||||
|
||||
# Look for GitHub email
|
||||
emails = self.browser.find_elements(By.CSS_SELECTOR, "div.m")
|
||||
github_email = None
|
||||
|
||||
for email in emails:
|
||||
if "github" in email.text.lower():
|
||||
github_email = email
|
||||
break
|
||||
|
||||
if github_email:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub verification email found{Style.RESET_ALL}")
|
||||
github_email.click()
|
||||
time.sleep(2)
|
||||
|
||||
# Switch back to default content
|
||||
self.browser.switch_to.default_content()
|
||||
|
||||
# Switch to email content frame
|
||||
iframe = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "ifmail"))
|
||||
)
|
||||
self.browser.switch_to.frame(iframe)
|
||||
|
||||
# Find verification link
|
||||
try:
|
||||
# Look for the verification button or link
|
||||
verification_elements = self.browser.find_elements(By.XPATH, "//a[contains(text(), 'Verify') or contains(text(), 'verify') or contains(@href, 'verify')]")
|
||||
|
||||
if verification_elements:
|
||||
verification_link = verification_elements[0].get_attribute('href')
|
||||
print(f"{Fore.CYAN}{EMOJI['LINK']} Found verification link{Style.RESET_ALL}")
|
||||
|
||||
# Open the verification link in the same window
|
||||
self.browser.get(verification_link)
|
||||
time.sleep(5)
|
||||
|
||||
# Check if verification was successful
|
||||
if "verified" in self.browser.page_source.lower() or "successful" in self.browser.page_source.lower():
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Email verified successfully{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Email verification page loaded but success not confirmed{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check if verification was successful manually and press Enter to continue...{Style.RESET_ALL}")
|
||||
input()
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No verification link found in email{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error extracting verification link: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} No GitHub verification email yet, waiting... ({attempt}/{max_attempts}){Style.RESET_ALL}")
|
||||
time.sleep(15) # Wait before checking again
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error checking email: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No verification email received after {max_attempts} attempts{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to check manually? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check your YOPmail inbox manually at: https://yopmail.com/en/wm")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Username: {username}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've verified the email...{Style.RESET_ALL}")
|
||||
input()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to check verification email: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_cursor(self):
|
||||
"""Register with Cursor using GitHub"""
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} Registering with Cursor using GitHub...{Style.RESET_ALL}")
|
||||
|
||||
# Navigate to Cursor login page
|
||||
self.browser.get("https://cursor.sh/login")
|
||||
time.sleep(3)
|
||||
|
||||
try:
|
||||
# Look for GitHub login button
|
||||
github_buttons = WebDriverWait(self.browser, 15).until(
|
||||
EC.presence_of_all_elements_located((By.XPATH, "//button[contains(., 'GitHub') or contains(@class, 'github')]"))
|
||||
)
|
||||
|
||||
if not github_buttons:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} GitHub login button not found{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Click the first GitHub button
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking GitHub login button...{Style.RESET_ALL}")
|
||||
github_buttons[0].click()
|
||||
time.sleep(5)
|
||||
|
||||
# Check if we're redirected to GitHub login
|
||||
current_url = self.browser.current_url
|
||||
if "github.com" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirected to GitHub login{Style.RESET_ALL}")
|
||||
|
||||
# Check if we need to log in to GitHub
|
||||
if "login" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Logging into GitHub...{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Enter GitHub credentials
|
||||
username_field = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "login_field"))
|
||||
)
|
||||
username_field.send_keys(self.github_username)
|
||||
|
||||
password_field = self.browser.find_element(By.ID, "password")
|
||||
password_field.send_keys(self.github_password)
|
||||
|
||||
# Click sign in
|
||||
signin_button = self.browser.find_element(By.CSS_SELECTOR, "input[type='submit']")
|
||||
signin_button.click()
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub login: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check if we're on the authorization page
|
||||
if "authorize" in self.browser.current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Authorizing Cursor app...{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Look for authorization button
|
||||
auth_buttons = self.browser.find_elements(By.XPATH, "//button[contains(., 'Authorize') or contains(@class, 'btn-primary')]")
|
||||
|
||||
if auth_buttons:
|
||||
auth_buttons[0].click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor authorized with GitHub{Style.RESET_ALL}")
|
||||
time.sleep(5)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No authorization button found, GitHub may be already authorized{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub authorization: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for Cursor dashboard to load
|
||||
timeout = 30
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
if "cursor.sh" in self.browser.current_url and not "login" in self.browser.current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Successfully logged into Cursor{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if "login" in self.browser.current_url:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to log into Cursor after {timeout} seconds{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Wait for dashboard elements to load
|
||||
time.sleep(3)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor registered with GitHub successfully{Style.RESET_ALL}")
|
||||
|
||||
# Now reset the machine ID
|
||||
return self.reset_machine_id()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during Cursor registration: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register with Cursor: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def reset_machine_id(self):
|
||||
"""Reset the Cursor machine ID to bypass limitations"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Resetting Cursor machine ID...{Style.RESET_ALL}")
|
||||
|
||||
# Find Cursor app data location based on platform
|
||||
cursor_data_dir = None
|
||||
if platform.system() == "Windows":
|
||||
appdata = os.getenv('APPDATA')
|
||||
if appdata:
|
||||
cursor_data_dir = os.path.join(appdata, "cursor", "Local Storage", "leveldb")
|
||||
elif platform.system() == "Darwin": # macOS
|
||||
home = os.path.expanduser("~")
|
||||
cursor_data_dir = os.path.join(home, "Library", "Application Support", "cursor", "Local Storage", "leveldb")
|
||||
elif platform.system() == "Linux":
|
||||
home = os.path.expanduser("~")
|
||||
cursor_data_dir = os.path.join(home, ".config", "cursor", "Local Storage", "leveldb")
|
||||
|
||||
if not cursor_data_dir or not os.path.exists(cursor_data_dir):
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found at: {cursor_data_dir}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reset the machine ID manually{Style.RESET_ALL}")
|
||||
|
||||
# Try to find the Cursor data directory
|
||||
if platform.system() == "Linux":
|
||||
possible_paths = [
|
||||
os.path.join(os.path.expanduser("~"), ".config", "cursor"),
|
||||
os.path.join(os.path.expanduser("~"), ".cursor")
|
||||
]
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor directory at: {path}{Style.RESET_ALL}")
|
||||
# Look for Local Storage subfolder
|
||||
for root, dirs, files in os.walk(path):
|
||||
if "Local Storage" in dirs:
|
||||
cursor_data_dir = os.path.join(root, "Local Storage", "leveldb")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor data directory at: {cursor_data_dir}{Style.RESET_ALL}")
|
||||
break
|
||||
break
|
||||
|
||||
if cursor_data_dir and os.path.exists(cursor_data_dir):
|
||||
# Generate a new UUID
|
||||
new_machine_id = str(uuid.uuid4())
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} New machine ID: {new_machine_id}{Style.RESET_ALL}")
|
||||
|
||||
# Ask for permission to modify files
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} This operation will modify Cursor app data files{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to continue? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response not in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Machine ID reset aborted{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Backup the directory
|
||||
backup_dir = cursor_data_dir + "_backup_" + time.strftime("%Y%m%d%H%M%S")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Creating backup of data directory to: {backup_dir}{Style.RESET_ALL}")
|
||||
try:
|
||||
shutil.copytree(cursor_data_dir, backup_dir)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Backup created successfully{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Failed to create backup: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without backup...{Style.RESET_ALL}")
|
||||
|
||||
# Find and modify files containing the machine ID
|
||||
modified = False
|
||||
for filename in os.listdir(cursor_data_dir):
|
||||
if filename.endswith(".log") or filename.endswith(".ldb"):
|
||||
file_path = os.path.join(cursor_data_dir, filename)
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
content = f.read()
|
||||
|
||||
# Look for patterns that might contain machine ID
|
||||
if b"machineId" in content:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found machineId reference in: {filename}{Style.RESET_ALL}")
|
||||
modified = True
|
||||
|
||||
# For safety, don't modify the binary files directly
|
||||
# Instead, instruct user to uninstall and reinstall Cursor
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Binary files found that may contain machine ID{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} For best results, please:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 1. Close Cursor if it's running{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 2. Uninstall Cursor completely{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 3. Reinstall Cursor{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 4. Login with your new GitHub account{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error processing file {filename}: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if not modified:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No machine ID references found in data files{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reinstall Cursor for a complete reset{Style.RESET_ALL}")
|
||||
|
||||
# Save credentials before returning
|
||||
self.save_credentials()
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID reset process completed{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to manually reset the machine ID by reinstalling Cursor{Style.RESET_ALL}")
|
||||
|
||||
# Still save credentials
|
||||
self.save_credentials()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to reset machine ID: {str(e)}{Style.RESET_ALL}")
|
||||
# Still save credentials even if machine ID reset fails
|
||||
self.save_credentials()
|
||||
return False
|
||||
|
||||
def save_credentials(self):
|
||||
"""Save the generated credentials to a file"""
|
||||
try:
|
||||
if not self.email_address or not self.github_username or not self.github_password:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No credentials to save{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
output_file = "github_cursor_accounts.txt"
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
credentials = {
|
||||
"timestamp": timestamp,
|
||||
"github_username": self.github_username,
|
||||
"github_password": self.github_password,
|
||||
"email": self.email_address
|
||||
}
|
||||
|
||||
credentials_json = json.dumps(credentials)
|
||||
|
||||
# Check if file exists and create if not
|
||||
file_exists = os.path.exists(output_file)
|
||||
|
||||
with open(output_file, "a") as f:
|
||||
if not file_exists:
|
||||
f.write("# GitHub + Cursor AI Accounts\n")
|
||||
f.write("# Format: JSON with timestamp, github_username, github_password, email\n\n")
|
||||
|
||||
f.write(credentials_json + "\n")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Credentials saved to: {output_file}{Style.RESET_ALL}")
|
||||
|
||||
# Print a summary
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} Registration Summary:{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Saved to: {output_file}{Style.RESET_ALL}\n")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to save credentials: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} Make sure to copy these credentials manually:{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}\n")
|
||||
return False
|
||||
|
||||
def cleanup(self):
|
||||
"""Clean up resources"""
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def start_registration(self):
|
||||
"""Start the GitHub Cursor registration process"""
|
||||
try:
|
||||
# Step 1: Get temporary email
|
||||
if not self.get_temp_email():
|
||||
return False
|
||||
|
||||
# Step 2: Register GitHub account
|
||||
if not self.register_github():
|
||||
return False
|
||||
|
||||
# Step 3: Check and verify email
|
||||
if not self.check_email_verification():
|
||||
return False
|
||||
|
||||
# Step 4: Register Cursor with GitHub
|
||||
if not self.register_cursor():
|
||||
return False
|
||||
|
||||
# Step 5: Reset machine ID
|
||||
self.reset_machine_id()
|
||||
|
||||
return True
|
||||
finally:
|
||||
self.cleanup()
|
||||
|
||||
def display_features_and_warnings(translator=None):
|
||||
"""Display features and warnings before proceeding"""
|
||||
if translator:
|
||||
print(f"\n🚀 {translator.get('github_register.title')}")
|
||||
print("=====================================")
|
||||
print(f"{translator.get('github_register.features_header')}:")
|
||||
print(f" - {translator.get('github_register.feature1')}")
|
||||
print(f" - {translator.get('github_register.feature2')}")
|
||||
print(f" - {translator.get('github_register.feature3')}")
|
||||
print(f" - {translator.get('github_register.feature4')}")
|
||||
print(f" - {translator.get('github_register.feature5')}")
|
||||
print(f" - {translator.get('github_register.feature6')}")
|
||||
print(f"\n⚠️ {translator.get('github_register.warnings_header')}:")
|
||||
print(f" - {translator.get('github_register.warning1')}")
|
||||
print(f" - {translator.get('github_register.warning2')}")
|
||||
print(f" - {translator.get('github_register.warning3')}")
|
||||
print(f" - {translator.get('github_register.warning4')}")
|
||||
print("=====================================\n")
|
||||
else:
|
||||
print("\n🚀 GitHub + Cursor AI Registration Automation")
|
||||
print("=====================================")
|
||||
print("Features:")
|
||||
print(" - Creates a temporary email using YOPmail")
|
||||
print(" - Registers a new GitHub account with random credentials")
|
||||
print(" - Verifies the GitHub email automatically")
|
||||
print(" - Logs into Cursor AI using GitHub authentication")
|
||||
print(" - Resets the machine ID to bypass trial detection")
|
||||
print(" - Saves all credentials to a file")
|
||||
print("\n⚠️ Warnings:")
|
||||
print(" - This script automates account creation, which may violate GitHub/Cursor terms of service")
|
||||
print(" - Requires internet access and administrative privileges")
|
||||
print(" - CAPTCHA or additional verification may interrupt automation")
|
||||
print(" - Use responsibly and at your own risk")
|
||||
print("=====================================\n")
|
||||
|
||||
def get_user_confirmation(translator=None):
|
||||
"""Prompt the user for confirmation to proceed"""
|
||||
while True:
|
||||
if translator:
|
||||
response = input(f"{translator.get('github_register.confirm')} (yes/no): ").lower().strip()
|
||||
else:
|
||||
response = input("Do you want to proceed with GitHub + Cursor AI registration? (yes/no): ").lower().strip()
|
||||
|
||||
if response in ['yes', 'y']:
|
||||
return True
|
||||
elif response in ['no', 'n']:
|
||||
if translator:
|
||||
print(f"❌ {translator.get('github_register.cancelled')}")
|
||||
else:
|
||||
print("❌ Operation cancelled.")
|
||||
return False
|
||||
else:
|
||||
if translator:
|
||||
print(f"{translator.get('github_register.invalid_choice')}")
|
||||
else:
|
||||
print("Please enter 'yes' or 'no'.")
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to run the GitHub Cursor registration process"""
|
||||
logging.info(f"{Fore.CYAN} {translator.get('github_register.starting_automation')}{Style.RESET_ALL}")
|
||||
|
||||
# Display features and warnings
|
||||
display_features_and_warnings(translator)
|
||||
|
||||
# Get user confirmation
|
||||
if not get_user_confirmation(translator):
|
||||
return
|
||||
|
||||
# Start registration process
|
||||
registration = GitHubCursorRegistration(translator)
|
||||
success = registration.start_registration()
|
||||
|
||||
# Display final message
|
||||
if success:
|
||||
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {translator.get('github_register.completed_successfully')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_username')}: {registration.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_password')}: {registration.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.email')}: {registration.email_address}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.credentials_saved')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('github_register.registration_encountered_issues')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('github_register.check_browser_windows_for_manual_intervention_or_try_again_later')}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for user acknowledgment
|
||||
if translator:
|
||||
input(f"\n{EMOJI['INFO']} {translator.get('register.press_enter')}...")
|
||||
else:
|
||||
input(f"\n{EMOJI['INFO']} Press Enter to continue...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
images/product_2025-04-16_10-40-21.png
Normal file
BIN
images/product_2025-04-16_10-40-21.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
769
locales/ar.json
Normal file
769
locales/ar.json
Normal file
@@ -0,0 +1,769 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "الخيارات المتاحة",
|
||||
"exit": "خروج من البرنامج",
|
||||
"reset": "إعادة تعيين معرف الجهاز",
|
||||
"register": "تسجيل حساب Cursor جديد",
|
||||
"register_google": "تسجيل باستخدام حساب جوجل",
|
||||
"register_github": "تسجيل باستخدام حساب GitHub",
|
||||
"register_manual": "تسجيل Cursor باستخدام بريد إلكتروني مخصص",
|
||||
"quit": "إغلاق تطبيق Cursor",
|
||||
"select_language": "تغيير اللغة",
|
||||
"select_chrome_profile": "اختيار ملف تعريف Chrome",
|
||||
"input_choice": "الرجاء إدخال اختيارك ({choices})",
|
||||
"invalid_choice": "اختيار غير صالح. الرجاء إدخال رقم من {choices}",
|
||||
"program_terminated": "تم إنهاء البرنامج بواسطة المستخدم",
|
||||
"error_occurred": "حدث خطأ: {error}. يرجى المحاولة مرة أخرى",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"disable_auto_update": "تعطيل التحديث التلقائي لـ Cursor",
|
||||
"lifetime_access_enabled": "تم تفعيل الوصول مدى الحياة",
|
||||
"totally_reset": "إعادة تعيين Cursor بالكامل",
|
||||
"outdate": "قديم",
|
||||
"temp_github_register": "تسجيل GitHub مؤقت",
|
||||
"admin_required": "يجب تشغيل البرنامج كمسؤول عند التنفيذ.",
|
||||
"admin_required_continue": "المتابعة بدون صلاحيات المسؤول.",
|
||||
"coming_soon": "قريباً",
|
||||
"fixed_soon": "سيتم إصلاحه قريباً",
|
||||
"contribute": "المساهمة في المشروع",
|
||||
"config": "عرض الإعدادات",
|
||||
"delete_google_account": "حذف حساب Cursor المرتبط بجوجل",
|
||||
"continue_prompt": "المتابعة؟ (y/N): ",
|
||||
"operation_cancelled_by_user": "تم إلغاء العملية بواسطة المستخدم",
|
||||
"exiting": "جاري الخروج ……",
|
||||
"bypass_version_check": "تجاوز فحص إصدار Cursor",
|
||||
"check_user_authorized": "فحص صلاحية المستخدم",
|
||||
"bypass_token_limit": "تجاوز حد الرمز المميز (Token)"
|
||||
},
|
||||
"languages": {
|
||||
"en": "الإنجليزية",
|
||||
"zh_cn": "الصينية المبسطة",
|
||||
"zh_tw": "الصينية التقليدية",
|
||||
"vi": "الفيتنامية",
|
||||
"nl": "الهولندية",
|
||||
"de": "الألمانية",
|
||||
"fr": "الفرنسية",
|
||||
"pt": "البرتغالية",
|
||||
"ru": "الروسية",
|
||||
"tr": "التركية",
|
||||
"bg": "البلغارية",
|
||||
"es": "الإسبانية",
|
||||
"ar": "العربية"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "بدء إغلاق Cursor",
|
||||
"no_process": "لا توجد عمليات Cursor قيد التشغيل",
|
||||
"terminating": "إنهاء العملية {pid}",
|
||||
"waiting": "في انتظار إنهاء العملية",
|
||||
"success": "تم إغلاق جميع عمليات Cursor",
|
||||
"timeout": "انتهت مهلة العملية: {pids}",
|
||||
"error": "حدث خطأ: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "أداة إعادة تعيين معرف جهاز Cursor",
|
||||
"checking": "جارٍ فحص ملف الإعدادات",
|
||||
"not_found": "لم يتم العثور على ملف الإعدادات",
|
||||
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
|
||||
"reading": "جارٍ قراءة الإعدادات الحالية",
|
||||
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
|
||||
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
|
||||
"generating": "جارٍ إنشاء معرف جهاز جديد",
|
||||
"saving_json": "جارٍ حفظ الإعدادات الجديدة في JSON",
|
||||
"success": "تم إعادة تعيين معرف الجهاز بنجاح",
|
||||
"new_id": "معرف الجهاز الجديد",
|
||||
"permission_error": "خطأ في الصلاحيات: {error}",
|
||||
"run_as_admin": "يرجى محاولة تشغيل هذا البرنامج كمسؤول",
|
||||
"process_error": "خطأ في عملية الإعادة: {error}",
|
||||
"updating_sqlite": "جارٍ تحديث قاعدة بيانات SQLite",
|
||||
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
|
||||
"sqlite_success": "تم تحديث قاعدة بيانات SQLite بنجاح",
|
||||
"sqlite_error": "فشل تحديث قاعدة بيانات SQLite: {error}",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"unsupported_os": "نظام تشغيل غير مدعوم: {os}",
|
||||
"linux_path_not_found": "لم يتم العثور على مسار Linux",
|
||||
"updating_system_ids": "جارٍ تحديث معرفات النظام",
|
||||
"system_ids_updated": "تم تحديث معرفات النظام بنجاح",
|
||||
"system_ids_update_failed": "فشل تحديث معرفات النظام: {error}",
|
||||
"windows_guid_updated": "تم تحديث Windows GUID بنجاح",
|
||||
"windows_permission_denied": "تم رفض الصلاحية في Windows",
|
||||
"windows_guid_update_failed": "فشل تحديث Windows GUID",
|
||||
"macos_uuid_updated": "تم تحديث macOS UUID بنجاح",
|
||||
"plutil_command_failed": "فشل أمر plutil",
|
||||
"start_patching": "بدء تصحيح getMachineId",
|
||||
"macos_uuid_update_failed": "فشل تحديث macOS UUID",
|
||||
"current_version": "إصدار Cursor الحالي: {version}",
|
||||
"patch_completed": "تم تصحيح getMachineId بنجاح",
|
||||
"patch_failed": "فشل تصحيح getMachineId: {error}",
|
||||
"version_check_passed": "تم اجتياز فحص إصدار Cursor",
|
||||
"file_modified": "تم تعديل الملف",
|
||||
"version_less_than_0_45": "إصدار Cursor < 0.45.0، تخطي تصحيح getMachineId",
|
||||
"detecting_version": "جارٍ اكتشاف إصدار Cursor",
|
||||
"patching_getmachineid": "جارٍ تصحيح getMachineId",
|
||||
"version_greater_than_0_45": "إصدار Cursor >= 0.45.0، جارٍ تصحيح getMachineId",
|
||||
"permission_denied": "تم رفض الصلاحية: {error}",
|
||||
"backup_created": "تم إنشاء نسخة احتياطية",
|
||||
"update_success": "تم التحديث بنجاح",
|
||||
"update_failed": "فشل التحديث: {error}",
|
||||
"windows_machine_guid_updated": "تم تحديث Windows Machine GUID بنجاح",
|
||||
"reading_package_json": "جارٍ قراءة package.json {path}",
|
||||
"invalid_json_object": "كائن JSON غير صالح",
|
||||
"no_version_field": "لم يتم العثور على حقل الإصدار في package.json",
|
||||
"version_field_empty": "حقل الإصدار فارغ",
|
||||
"invalid_version_format": "تنسيق إصدار غير صالح: {version}",
|
||||
"found_version": "تم العثور على الإصدار: {version}",
|
||||
"version_parse_error": "خطأ في تحليل الإصدار: {error}",
|
||||
"package_not_found": "لم يتم العثور على package.json: {path}",
|
||||
"check_version_failed": "فشل فحص الإصدار: {error}",
|
||||
"stack_trace": "تتبع المكدس",
|
||||
"version_too_low": "إصدار Cursor منخفض جداً: {version} < 0.45.0",
|
||||
"no_write_permission": "لا توجد صلاحيات كتابة: {path}",
|
||||
"path_not_found": "لم يتم العثور على المسار: {path}",
|
||||
"modify_file_failed": "فشل تعديل الملف: {error}",
|
||||
"windows_machine_id_updated": "تم تحديث Windows Machine ID بنجاح",
|
||||
"update_windows_machine_id_failed": "فشل تحديث Windows Machine ID: {error}",
|
||||
"update_windows_machine_guid_failed": "فشل تحديث Windows Machine GUID: {error}",
|
||||
"file_not_found": "لم يتم العثور على الملف: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "أداة تسجيل Cursor",
|
||||
"start": "بدء عملية التسجيل...",
|
||||
"handling_turnstile": "جارٍ معالجة التحقق الأمني...",
|
||||
"retry_verification": "إعادة محاولة التحقق...",
|
||||
"detect_turnstile": "جارٍ التحقق من الأمان...",
|
||||
"verification_success": "تم التحقق الأمني بنجاح",
|
||||
"starting_browser": "جارٍ فتح المتصفح...",
|
||||
"form_success": "تم إرسال النموذج بنجاح",
|
||||
"browser_started": "تم فتح المتصفح بنجاح",
|
||||
"waiting_for_second_verification": "في انتظار التحقق عبر البريد الإلكتروني...",
|
||||
"waiting_for_verification_code": "في انتظار رمز التحقق...",
|
||||
"password_success": "تم تعيين كلمة المرور بنجاح",
|
||||
"password_error": "تعذر تعيين كلمة المرور: {error}. يرجى المحاولة مرة أخرى",
|
||||
"waiting_for_page_load": "جارٍ تحميل الصفحة...",
|
||||
"first_verification_passed": "تم اجتياز التحقق الأولي بنجاح",
|
||||
"mailbox": "تم الوصول إلى صندوق البريد بنجاح",
|
||||
"register_start": "بدء التسجيل",
|
||||
"form_submitted": "تم إرسال النموذج، بدء التحقق...",
|
||||
"filling_form": "تعبئة النموذج",
|
||||
"visiting_url": "جارٍ زيارة الرابط",
|
||||
"basic_info": "تم إرسال المعلومات الأساسية",
|
||||
"handle_turnstile": "معالجة Turnstile",
|
||||
"no_turnstile": "لم يتم اكتشاف Turnstile",
|
||||
"turnstile_passed": "تم اجتياز Turnstile",
|
||||
"verification_start": "بدء الحصول على رمز التحقق",
|
||||
"verification_timeout": "انتهت مهلة الحصول على رمز التحقق",
|
||||
"verification_not_found": "لم يتم العثور على رمز تحقق",
|
||||
"try_get_code": "محاولة | {attempt} الحصول على رمز التحقق | الوقت المتبقي: {time}ثانية",
|
||||
"get_account": "جارٍ الحصول على معلومات الحساب",
|
||||
"get_token": "الحصول على رمز جلسة Cursor",
|
||||
"token_success": "تم الحصول على الرمز بنجاح",
|
||||
"token_attempt": "محاولة | {attempt} مرات للحصول على الرمز | سيتم إعادة المحاولة بعد {time}ثانية",
|
||||
"token_max_attempts": "تم الوصول إلى الحد الأقصى للمحاولات ({max}) | فشل الحصول على الرمز",
|
||||
"token_failed": "فشل الحصول على الرمز: {error}",
|
||||
"account_error": "فشل الحصول على معلومات الحساب: {error}",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"browser_start": "بدء تشغيل المتصفح",
|
||||
"open_mailbox": "جارٍ فتح صندوق البريد",
|
||||
"email_error": "فشل الحصول على عنوان البريد الإلكتروني",
|
||||
"setup_error": "خطأ في إعداد البريد الإلكتروني: {error}",
|
||||
"start_getting_verification_code": "بدء الحصول على رمز التحقق، سيتم المحاولة خلال 60 ثانية",
|
||||
"get_verification_code_timeout": "انتهت مهلة الحصول على رمز التحقق",
|
||||
"get_verification_code_success": "تم الحصول على رمز التحقق بنجاح",
|
||||
"try_get_verification_code": "محاولة | {attempt} الحصول على رمز التحقق | الوقت المتبقي: {remaining_time}ثانية",
|
||||
"verification_code_filled": "تم تعبئة رمز التحقق",
|
||||
"login_success_and_jump_to_settings_page": "تم تسجيل الدخول بنجاح والانتقال إلى صفحة الإعدادات",
|
||||
"detect_login_page": "تم اكتشاف صفحة تسجيل الدخول، بدء التسجيل...",
|
||||
"cursor_registration_completed": "تم تسجيل Cursor بنجاح!",
|
||||
"set_password": "تعيين كلمة المرور",
|
||||
"basic_info_submitted": "تم إرسال المعلومات الأساسية",
|
||||
"cursor_auth_info_updated": "تم تحديث معلومات مصادقة Cursor",
|
||||
"cursor_auth_info_update_failed": "فشل تحديث معلومات مصادقة Cursor",
|
||||
"reset_machine_id": "إعادة تعيين معرف الجهاز",
|
||||
"account_info_saved": "تم حفظ معلومات الحساب",
|
||||
"save_account_info_failed": "فشل حفظ معلومات الحساب",
|
||||
"get_email_address": "الحصول على عنوان البريد الإلكتروني",
|
||||
"update_cursor_auth_info": "تحديث معلومات مصادقة Cursor",
|
||||
"register_process_error": "خطأ في عملية التسجيل: {error}",
|
||||
"setting_password": "جارٍ تعيين كلمة المرور",
|
||||
"manual_code_input": "إدخال الرمز يدوياً",
|
||||
"manual_email_input": "إدخال البريد الإلكتروني يدوياً",
|
||||
"password": "كلمة المرور",
|
||||
"first_name": "الاسم الأول",
|
||||
"last_name": "الاسم الأخير",
|
||||
"exit_signal": "إشارة خروج",
|
||||
"email_address": "عنوان البريد الإلكتروني",
|
||||
"config_created": "تم إنشاء الإعدادات",
|
||||
"verification_failed": "فشل التحقق",
|
||||
"verification_error": "خطأ في التحقق: {error}",
|
||||
"config_option_added": "تمت إضافة خيار الإعدادات: {option}",
|
||||
"config_updated": "تم تحديث الإعدادات",
|
||||
"password_submitted": "تم إرسال كلمة المرور",
|
||||
"total_usage": "إجمالي الاستخدام: {usage}",
|
||||
"setting_on_password": "جارٍ تعيين كلمة المرور",
|
||||
"getting_code": "جارٍ الحصول على رمز التحقق، سيتم المحاولة خلال 60 ثانية",
|
||||
"human_verify_error": "تعذر التحقق من أن المستخدم بشري. جارٍ إعادة المحاولة...",
|
||||
"max_retries_reached": "تم الوصول إلى الحد الأقصى للمحاولات. فشل التسجيل.",
|
||||
"browser_path_invalid": "مسار {browser} غير صالح، جارٍ استخدام المسار الافتراضي",
|
||||
"using_browser": "جارٍ استخدام متصفح {browser}: {path}",
|
||||
"using_browser_profile": "جارٍ استخدام ملف تعريف {browser} من: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "تأكد من تثبيت {browser} بشكل صحيح",
|
||||
"try_install_browser": "حاول تثبيت المتصفح باستخدام مدير الحزم الخاص بك",
|
||||
"tracking_processes": "جارٍ تتبع {count} عمليات {browser}",
|
||||
"no_new_processes_detected": "لم يتم اكتشاف عمليات {browser} جديدة للتتبع",
|
||||
"could_not_track_processes": "تعذر تتبع عمليات {browser}: {error}"
|
||||
},
|
||||
"auth": {
|
||||
"title": "مدير مصادقة Cursor",
|
||||
"checking_auth": "جارٍ فحص ملف المصادقة",
|
||||
"auth_not_found": "لم يتم العثور على ملف المصادقة",
|
||||
"auth_file_error": "خطأ في ملف المصادقة: {error}",
|
||||
"reading_auth": "جارٍ قراءة ملف المصادقة",
|
||||
"updating_auth": "جارٍ تحديث معلومات المصادقة",
|
||||
"auth_updated": "تم تحديث معلومات المصادقة بنجاح",
|
||||
"auth_update_failed": "فشل تحديث معلومات المصادقة: {error}",
|
||||
"auth_file_created": "تم إنشاء ملف المصادقة",
|
||||
"auth_file_create_failed": "فشل إنشاء ملف المصادقة: {error}",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"reset_machine_id": "إعادة تعيين معرف الجهاز",
|
||||
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
|
||||
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
|
||||
"connected_to_database": "تم الاتصال بقاعدة البيانات",
|
||||
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
|
||||
"db_not_found": "لم يتم العثور على ملف قاعدة البيانات في: {path}",
|
||||
"db_permission_error": "لا يمكن الوصول إلى ملف قاعدة البيانات. يرجى التحقق من الصلاحيات",
|
||||
"db_connection_error": "فشل الاتصال بقاعدة البيانات: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "جارٍ إنشاء بريد إلكتروني جديد",
|
||||
"blocked_domain": "نطاق محظور",
|
||||
"select_domain": "جارٍ اختيار نطاق عشوائي",
|
||||
"copy_email": "جارٍ نسخ عنوان البريد الإلكتروني",
|
||||
"enter_mailbox": "جارٍ الدخول إلى صندوق البريد",
|
||||
"refresh_mailbox": "جارٍ تحديث صندوق البريد",
|
||||
"check_verification": "جارٍ التحقق من رمز التحقق",
|
||||
"verification_found": "تم العثور على رمز التحقق",
|
||||
"verification_not_found": "لم يتم العثور على رمز تحقق",
|
||||
"browser_error": "خطأ في التحكم بالمتصفح: {error}",
|
||||
"navigation_error": "خطأ في التنقل: {error}",
|
||||
"email_copy_error": "خطأ في نسخ البريد الإلكتروني: {error}",
|
||||
"mailbox_error": "خطأ في صندوق البريد: {error}",
|
||||
"token_saved_to_file": "تم حفظ الرمز في ملف cursor_tokens.txt",
|
||||
"navigate_to": "جارٍ الانتقال إلى {url}",
|
||||
"generate_email_success": "تم إنشاء البريد الإلكتروني بنجاح",
|
||||
"select_email_domain": "اختيار نطاق البريد الإلكتروني",
|
||||
"select_email_domain_success": "تم اختيار نطاق البريد الإلكتروني بنجاح",
|
||||
"get_email_name": "الحصول على اسم البريد الإلكتروني",
|
||||
"get_email_name_success": "تم الحصول على اسم البريد الإلكتروني بنجاح",
|
||||
"get_email_address": "الحصول على عنوان البريد الإلكتروني",
|
||||
"get_email_address_success": "تم الحصول على عنوان البريد الإلكتروني بنجاح",
|
||||
"enter_mailbox_success": "تم الدخول إلى صندوق البريد بنجاح",
|
||||
"found_verification_code": "تم العثور على رمز التحقق",
|
||||
"get_cursor_session_token": "الحصول على رمز جلسة Cursor",
|
||||
"get_cursor_session_token_success": "تم الحصول على رمز جلسة Cursor بنجاح",
|
||||
"get_cursor_session_token_failed": "فشل الحصول على رمز جلسة Cursor",
|
||||
"save_token_failed": "فشل حفظ الرمز",
|
||||
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
|
||||
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
|
||||
"no_valid_verification_code": "لا يوجد رمز تحقق صالح"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "جارٍ بدء تشغيل المتصفح",
|
||||
"visiting_site": "جارٍ زيارة نطاقات البريد",
|
||||
"create_success": "تم إنشاء البريد الإلكتروني بنجاح",
|
||||
"create_failed": "فشل إنشاء البريد الإلكتروني",
|
||||
"create_error": "خطأ في إنشاء البريد الإلكتروني: {error}",
|
||||
"refreshing": "جارٍ تحديث البريد الإلكتروني",
|
||||
"refresh_success": "تم تحديث البريد الإلكتروني بنجاح",
|
||||
"refresh_error": "خطأ في تحديث البريد الإلكتروني: {error}",
|
||||
"refresh_button_not_found": "لم يتم العثور على زر التحديث",
|
||||
"verification_found": "تم العثور على التحقق",
|
||||
"verification_not_found": "لم يتم العثور على التحقق",
|
||||
"verification_error": "خطأ في التحقق: {error}",
|
||||
"verification_code_found": "تم العثور على رمز التحقق",
|
||||
"verification_code_not_found": "لم يتم العثور على رمز تحقق",
|
||||
"verification_code_error": "خطأ في رمز التحقق: {error}",
|
||||
"address": "عنوان البريد الإلكتروني",
|
||||
"all_domains_blocked": "جميع النطاقات محظورة، جارٍ التحويل إلى خدمة أخرى",
|
||||
"no_available_domains_after_filtering": "لا توجد نطاقات متاحة بعد التصفية",
|
||||
"switching_service": "جارٍ التحويل إلى خدمة {service}",
|
||||
"domains_list_error": "فشل الحصول على قائمة النطاقات: {error}",
|
||||
"failed_to_get_available_domains": "فشل الحصول على نطاقات متاحة",
|
||||
"domains_excluded": "النطاقات المستثناة: {domains}",
|
||||
"failed_to_create_account": "فشل إنشاء الحساب",
|
||||
"account_creation_error": "خطأ في إنشاء الحساب: {error}",
|
||||
"blocked_domains": "النطاقات المحظورة: {domains}",
|
||||
"blocked_domains_loaded": "تم تحميل النطاقات المحظورة: {count}",
|
||||
"blocked_domains_loaded_error": "خطأ في تحميل النطاقات المحظورة: {error}",
|
||||
"blocked_domains_loaded_success": "تم تحميل النطاقات المحظورة بنجاح",
|
||||
"blocked_domains_loaded_timeout": "انتهت مهلة تحميل النطاقات المحظورة: {timeout}ثانية",
|
||||
"blocked_domains_loaded_timeout_error": "خطأ في مهلة تحميل النطاقات المحظورة: {error}",
|
||||
"available_domains_loaded": "تم تحميل النطاقات المتاحة: {count}",
|
||||
"domains_filtered": "تم تصفية النطاقات: {count}",
|
||||
"trying_to_create_email": "جارٍ محاولة إنشاء بريد إلكتروني: {email}",
|
||||
"domain_blocked": "النطاق محظور: {domain}",
|
||||
"using_chrome_profile": "جارٍ استخدام ملف تعريف Chrome من: {user_data_dir}",
|
||||
"no_display_found": "لم يتم العثور على شاشة. تأكد من تشغيل خادم X.",
|
||||
"try_export_display": "حاول: export DISPLAY=:0",
|
||||
"extension_load_error": "خطأ في تحميل الامتداد: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "تأكد من تثبيت Chrome/Chromium بشكل صحيح",
|
||||
"try_install_chromium": "حاول: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "تعطيل التحديث التلقائي لـ Cursor",
|
||||
"disable_success": "تم تعطيل التحديث التلقائي بنجاح",
|
||||
"disable_failed": "فشل تعطيل التحديث التلقائي: {error}",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"start_disable": "بدء تعطيل التحديث التلقائي",
|
||||
"killing_processes": "جارٍ إنهاء العمليات",
|
||||
"processes_killed": "تم إنهاء العمليات",
|
||||
"removing_directory": "جارٍ إزالة الدليل",
|
||||
"directory_removed": "تمت إزالة الدليل",
|
||||
"creating_block_file": "جارٍ إنشاء ملف حظر",
|
||||
"block_file_created": "تم إنشاء ملف الحظر",
|
||||
"clearing_update_yml": "جارٍ مسح ملف update.yml",
|
||||
"update_yml_cleared": "تم مسح ملف update.yml",
|
||||
"update_yml_not_found": "لم يتم العثور على ملف update.yml",
|
||||
"clear_update_yml_failed": "فشل مسح ملف update.yml: {error}",
|
||||
"unsupported_os": "نظام تشغيل غير مدعوم: {system}",
|
||||
"remove_directory_failed": "فشل إزالة الدليل: {error}",
|
||||
"create_block_file_failed": "فشل إنشاء ملف الحظر: {error}",
|
||||
"directory_locked": "الدليل مقفل: {path}",
|
||||
"yml_locked": "ملف update.yml مقفل",
|
||||
"block_file_locked": "ملف الحظر مقفل",
|
||||
"yml_already_locked": "ملف update.yml مقفل بالفعل",
|
||||
"block_file_already_locked": "ملف الحظر مقفل بالفعل",
|
||||
"block_file_locked_error": "خطأ في قفل ملف الحظر: {error}",
|
||||
"yml_locked_error": "خطأ في قفل ملف update.yml: {error}",
|
||||
"block_file_already_locked_error": "خطأ في قفل ملف الحظر الموجود بالفعل: {error}",
|
||||
"yml_already_locked_error": "خطأ في قفل ملف update.yml الموجود بالفعل: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "جارٍ التحقق من التحديثات...",
|
||||
"new_version_available": "يتوفر إصدار جديد! (الحالي: {current}, الأحدث: {latest})",
|
||||
"updating": "جارٍ التحديث إلى أحدث إصدار. سيعيد البرنامج التشغيل تلقائياً.",
|
||||
"up_to_date": "أنت تستخدم أحدث إصدار.",
|
||||
"check_failed": "فشل التحقق من التحديثات: {error}",
|
||||
"continue_anyway": "المتابعة باستخدام الإصدار الحالي...",
|
||||
"update_confirm": "هل تريد التحديث إلى أحدث إصدار؟ (Y/n)",
|
||||
"update_skipped": "تخطي التحديث.",
|
||||
"invalid_choice": "اختيار غير صالح. الرجاء إدخال 'Y' أو 'n'.",
|
||||
"development_version": "إصدار التطوير {current} > {latest}",
|
||||
"changelog_title": "سجل التغييرات",
|
||||
"rate_limit_exceeded": "تم تجاوز حد معدل GitHub API. تخطي فحص التحديث."
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "إعادة تعيين Cursor بالكامل",
|
||||
"checking_config": "جارٍ فحص ملف الإعدادات",
|
||||
"config_not_found": "لم يتم العثور على ملف الإعدادات",
|
||||
"no_permission": "لا يمكن قراءة أو كتابة ملف الإعدادات، يرجى التحقق من صلاحيات الملف",
|
||||
"reading_config": "جارٍ قراءة الإعدادات الحالية",
|
||||
"creating_backup": "جارٍ إنشاء نسخة احتياطية للإعدادات",
|
||||
"backup_exists": "النسخة الاحتياطية موجودة بالفعل، تخطي خطوة النسخ الاحتياطي",
|
||||
"generating_new_machine_id": "جارٍ إنشاء معرف جهاز جديد",
|
||||
"saving_new_config": "جارٍ حفظ الإعدادات الجديدة في JSON",
|
||||
"success": "تم إعادة تعيين Cursor بنجاح",
|
||||
"error": "فشل إعادة تعيين Cursor: {error}",
|
||||
"press_enter": "اضغط Enter للخروج",
|
||||
"reset_machine_id": "إعادة تعيين معرف الجهاز",
|
||||
"database_connection_closed": "تم إغلاق اتصال قاعدة البيانات",
|
||||
"database_updated_successfully": "تم تحديث قاعدة البيانات بنجاح",
|
||||
"connected_to_database": "تم الاتصال بقاعدة البيانات",
|
||||
"updating_pair": "جارٍ تحديث زوج المفتاح-القيمة",
|
||||
"db_not_found": "لم يتم العثور على ملف قاعدة البيانات في: {path}",
|
||||
"db_permission_error": "لا يمكن الوصول إلى ملف قاعدة البيانات. يرجى التحقق من الصلاحيات",
|
||||
"db_connection_error": "فشل الاتصال بقاعدة البيانات: {error}",
|
||||
"feature_title": "الميزات",
|
||||
"feature_1": "إزالة كاملة لإعدادات وتكوينات Cursor AI",
|
||||
"feature_2": "مسح جميع البيانات المخزنة مؤقتاً بما في ذلك سجل الذكاء الاصطناعي",
|
||||
"feature_3": "إعادة تعيين معرف الجهاز لتجاوز كشف الفترة التجريبية",
|
||||
"feature_4": "إنشاء معرفات أجهزة جديدة عشوائية",
|
||||
"feature_5": "إزالة الامتدادات المخصصة والتفضيلات",
|
||||
"feature_6": "إعادة تعيين معلومات الفترة التجريبية وبيانات التفعيل",
|
||||
"feature_7": "فحص عميق لملفات الرخصة والملفات المتعلقة بالفترة التجريبية المخفية",
|
||||
"feature_8": "الحفاظ على الملفات والتطبيقات غير المتعلقة بـ Cursor بأمان",
|
||||
"feature_9": "متوافق مع Windows وmacOS وLinux",
|
||||
"disclaimer_title": "تنبيه",
|
||||
"disclaimer_1": "ستقوم هذه الأداة بحذف جميع إعدادات Cursor AI،",
|
||||
"disclaimer_2": "والتكوينات والبيانات المخزنة مؤقتاً بشكل دائم. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"disclaimer_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
|
||||
"disclaimer_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
|
||||
"disclaimer_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
|
||||
"disclaimer_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
|
||||
"disclaimer_7": "استخدمها على مسؤوليتك الخاصة",
|
||||
"confirm_title": "هل أنت متأكد أنك تريد المتابعة؟",
|
||||
"confirm_1": "سيؤدي هذا الإجراء إلى حذف جميع إعدادات Cursor AI،",
|
||||
"confirm_2": "والتكوينات والبيانات المخزنة مؤقتاً. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"confirm_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
|
||||
"confirm_4": "للاستهداف فقط ملفات محرر Cursor AI وآليات كشف الفترة التجريبية.",
|
||||
"confirm_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
|
||||
"confirm_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
|
||||
"confirm_7": "استخدمها على مسؤوليتك الخاصة",
|
||||
"invalid_choice": "الرجاء إدخال 'Y' أو 'n'",
|
||||
"skipped_for_safety": "تم تخطيه لأسباب أمنية (غير متعلق بـ Cursor): {path}",
|
||||
"deleted": "تم الحذف: {path}",
|
||||
"error_deleting": "خطأ في حذف {path}: {error}",
|
||||
"not_found": "لم يتم العثور على الملف: {path}",
|
||||
"resetting_machine_id": "جارٍ إعادة تعيين معرفات الجهاز لتجاوز كشف الفترة التجريبية...",
|
||||
"created_machine_id": "تم إنشاء معرف جهاز جديد: {path}",
|
||||
"error_creating_machine_id": "خطأ في إنشاء ملف معرف الجهاز {path}: {error}",
|
||||
"error_searching": "خطأ في البحث عن الملفات في {path}: {error}",
|
||||
"created_extended_trial_info": "تم إنشاء معلومات الفترة التجريبية الممتدة: {path}",
|
||||
"error_creating_trial_info": "خطأ في إنشاء ملف معلومات الفترة التجريبية {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "جارٍ إعادة تعيين محرر Cursor AI... يرجى الانتظار.",
|
||||
"reset_cancelled": "تم إلغاء الإعادة. الخروج دون إجراء أي تغييرات.",
|
||||
"windows_machine_id_modification_skipped": "تم تخطي تعديل معرف جهاز Windows: {error}",
|
||||
"linux_machine_id_modification_skipped": "تم تخطي تعديل machine-id في Linux: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل تشغيل البرنامج كمسؤول",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "ملاحظة: قد تتطلب إعادة تعيين معرف الجهاز الكامل صلاحيات sudo",
|
||||
"windows_registry_instructions": "📝 ملاحظة: للإعادة الكاملة على Windows، قد تحتاج أيضاً إلى تنظيف إدخالات التسجيل.",
|
||||
"windows_registry_instructions_2": " قم بتشغيل 'regedit' وابحث عن المفاتيح التي تحتوي على 'Cursor' أو 'CursorAI' تحت HKEY_CURRENT_USER\\Software\\ واحذفها.\n",
|
||||
"reset_log_1": "تمت إعادة تعيين Cursor AI بالكامل وتجاوز كشف الفترة التجريبية!",
|
||||
"reset_log_2": "يرجى إعادة تشغيل النظام لتفعيل التغييرات.",
|
||||
"reset_log_3": "ستحتاج إلى إعادة تثبيت Cursor AI ويجب أن تحصل الآن على فترة تجريبية جديدة.",
|
||||
"reset_log_4": "للحصول على أفضل النتائج، ضع في الاعتبار أيضاً:",
|
||||
"reset_log_5": "استخدم عنوان بريد إلكتروني مختلف عند التسجيل للحصول على فترة تجريبية جديدة",
|
||||
"reset_log_6": "استخدم شبكة VPN لتغيير عنوان IP الخاص بك إذا كان ذلك متاحًا",
|
||||
"reset_log_7": "قم بمسح ملفات تعريف الارتباط وذاكرة التخزين المؤقت للمتصفح قبل زيارة موقع Cursor AI",
|
||||
"reset_log_8": "إذا استمرت المشكلات، حاول تثبيت Cursor AI في موقع مختلف",
|
||||
"reset_log_9": "إذا واجهتك أي مشكلات، انتقل إلى متتبع المشكلات على Github وقم بإنشاء مشكلة جديدة على https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "حدث خطأ غير متوقع: {error}",
|
||||
"report_issue": "يرجى الإبلاغ عن هذه المشكلة على متتبع المشكلات في Github على https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "تمت مقاطعة العملية بواسطة المستخدم. جارٍ الخروج...",
|
||||
"return_to_main_menu": "جارٍ العودة إلى القائمة الرئيسية...",
|
||||
"process_interrupted": "تمت مقاطعة العملية. جارٍ الخروج...",
|
||||
"press_enter_to_return_to_main_menu": "اضغط على Enter للعودة إلى القائمة الرئيسية...",
|
||||
"removing_known": "جارٍ إزالة ملفات الفترة التجريبية/الترخيص المعروفة",
|
||||
"performing_deep_scan": "جارٍ إجراء فحص عميق للبحث عن ملفات ترخيص/تجريبية إضافية",
|
||||
"found_additional_potential_license_trial_files": "تم العثور على {count} ملفات محتملة إضافية للترخيص/الفترة التجريبية",
|
||||
"checking_for_electron_localstorage_files": "جارٍ التحقق من وجود ملفات التخزين المحلي لـ Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "لم يتم العثور على ملفات ترخيص/تجريبية إضافية في الفحص العميق",
|
||||
"removing_electron_localstorage_files": "جارٍ إزالة ملفات التخزين المحلي لـ Electron",
|
||||
"electron_localstorage_files_removed": "تمت إزالة ملفات التخزين المحلي لـ Electron",
|
||||
"electron_localstorage_files_removal_error": "خطأ في إزالة ملفات التخزين المحلي لـ Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "اكتملت إزالة ملفات التخزين المحلي لـ Electron",
|
||||
"warning_title": "تحذير",
|
||||
"warning_1": "هذا الإجراء سيحذف جميع إعدادات Cursor AI،",
|
||||
"warning_2": "والتكوينات والبيانات المخزنة مؤقتاً. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"warning_3": "لن تتأثر ملفات الكود الخاصة بك، وقد صممت الأداة",
|
||||
"warning_4": "لاستهداف ملفات محرر Cursor AI وآليات اكتشاف الفترة التجريبية فقط.",
|
||||
"warning_5": "لن تتأثر التطبيقات الأخرى على نظامك.",
|
||||
"warning_6": "ستحتاج إلى إعداد Cursor AI مرة أخرى بعد تشغيل هذه الأداة.",
|
||||
"warning_7": "استخدم على مسؤوليتك الخاصة",
|
||||
"removed": "تمت الإزالة: {path}",
|
||||
"failed_to_reset_machine_guid": "فشل إعادة تعيين معرّف الجهاز",
|
||||
"failed_to_remove": "فشل في الإزالة: {path}",
|
||||
"failed_to_delete_file": "فشل في حذف الملف: {path}",
|
||||
"failed_to_delete_directory": "فشل في حذف المجلد: {path}",
|
||||
"failed_to_delete_file_or_directory": "فشل في حذف الملف أو المجلد: {path}",
|
||||
"deep_scanning": "جارٍ إجراء فحص عميق للبحث عن ملفات ترخيص/تجريبية إضافية",
|
||||
"resetting_cursor": "جارٍ إعادة تعيين محرر Cursor AI... يرجى الانتظار.",
|
||||
"completed_in": "اكتمل في {time} ثانية",
|
||||
"cursor_reset_completed": "تمت إعادة تعيين محرر Cursor AI بالكامل وتجاوز اكتشاف الفترة التجريبية!",
|
||||
"cursor_reset_failed": "فشلت إعادة تعيين محرر Cursor AI: {error}",
|
||||
"cursor_reset_cancelled": "تم إلغاء إعادة تعيين محرر Cursor AI. جارٍ الخروج دون إجراء أي تغييرات.",
|
||||
"operation_cancelled": "تم إلغاء العملية. جارٍ الخروج دون إجراء أي تغييرات.",
|
||||
"navigating_to_settings": "جارٍ الانتقال إلى صفحة الإعدادات...",
|
||||
"already_on_settings": "أنت بالفعل في صفحة الإعدادات",
|
||||
"login_redirect_failed": "فشلت إعادة توجيه تسجيل الدخول، جارٍ محاولة التنقل المباشر...",
|
||||
"advanced_tab_not_found": "لم يتم العثور على علامة التبويب المتقدمة بعد عدة محاولات",
|
||||
"advanced_tab_retry": "لم يتم العثور على علامة التبويب المتقدمة، المحاولة {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "خطأ في العثور على علامة التبويب المتقدمة: {error}",
|
||||
"advanced_tab_clicked": "تم النقر على علامة التبويب المتقدمة",
|
||||
"direct_advanced_navigation": "جارٍ محاولة التنقل المباشر إلى علامة التبويب المتقدمة",
|
||||
"delete_button_not_found": "لم يتم العثور على زر حذف الحساب بعد عدة محاولات",
|
||||
"delete_button_retry": "لم يتم العثور على زر الحذف، المحاولة {attempt}/{max_attempts}",
|
||||
"delete_button_error": "خطأ في العثور على زر الحذف: {error}",
|
||||
"delete_button_clicked": "تم النقر على زر حذف الحساب",
|
||||
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
|
||||
"delete_input_not_found": "لم يتم العثور على حقل تأكيد الحذف بعد عدة محاولات",
|
||||
"delete_input_retry": "لم يتم العثور على حقل الحذف، المحاولة {attempt}/{max_attempts}",
|
||||
"delete_input_error": "خطأ في العثور على حقل الحذف: {error}",
|
||||
"delete_input_not_found_continuing": "لم يتم العثور على حقل تأكيد الحذف، جارٍ محاولة المتابعة على أي حال"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "أتمتة تسجيل GitHub و Cursor AI",
|
||||
"features_header": "الميزات",
|
||||
"feature1": "ينشئ بريدًا إلكترونيًا مؤقتًا باستخدام 1secmail.",
|
||||
"feature2": "يسجل حساب GitHub جديد ببيانات اعتماد عشوائية.",
|
||||
"feature3": "يتحقق من بريد GitHub تلقائيًا.",
|
||||
"feature4": "يسجل الدخول إلى Cursor AI باستخدام مصادقة GitHub.",
|
||||
"feature5": "يعيد تعيين معرف الجهاز لتجاوز اكتشاف الفترة التجريبية.",
|
||||
"feature6": "يحفظ جميع بيانات الاعتماد في ملف.",
|
||||
"warnings_header": "تحذيرات",
|
||||
"warning1": "تقوم هذه الأداة بأتمتة إنشاء الحساب، مما قد ينتهك شروط خدمة GitHub/Cursor.",
|
||||
"warning2": "تتطلب وصولاً إلى الإنترنت وامتيازات المسؤول.",
|
||||
"warning3": "قد تتعارض CAPTCHA أو عمليات التحقق الإضافية مع الأتمتة.",
|
||||
"warning4": "استخدمها بمسؤولية وعلى مسؤوليتك الخاصة.",
|
||||
"confirm": "هل أنت متأكد أنك تريد المتابعة؟",
|
||||
"invalid_choice": "اختيار غير صالح. الرجاء إدخال 'yes' أو 'no'",
|
||||
"cancelled": "تم إلغاء العملية",
|
||||
"program_terminated": "تم إنهاء البرنامج بواسطة المستخدم",
|
||||
"starting_automation": "جارٍ بدء الأتمتة...",
|
||||
"github_username": "اسم مستخدم GitHub",
|
||||
"github_password": "كلمة مرور GitHub",
|
||||
"email_address": "عنوان البريد الإلكتروني",
|
||||
"credentials_saved": "تم حفظ بيانات الاعتماد هذه في ملف github_cursor_accounts.txt",
|
||||
"completed_successfully": "اكتمل تسجيل GitHub و Cursor بنجاح!",
|
||||
"registration_encountered_issues": "واجه تسجيل GitHub و Cursor مشكلات.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "تحقق من نوافذ المتصفح للتدخل اليدوي أو حاول مرة أخرى لاحقًا."
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "الاشتراك",
|
||||
"trial_remaining": "المتبقي من الفترة التجريبية Pro",
|
||||
"days": "أيام",
|
||||
"subscription_not_found": "لم يتم العثور على معلومات الاشتراك",
|
||||
"email_not_found": "لم يتم العثور على البريد الإلكتروني",
|
||||
"failed_to_get_account": "فشل في الحصول على معلومات الحساب",
|
||||
"config_not_found": "لم يتم العثور على الإعدادات.",
|
||||
"failed_to_get_usage": "فشل في الحصول على معلومات الاستخدام",
|
||||
"failed_to_get_subscription": "فشل في الحصول على معلومات الاشتراك",
|
||||
"failed_to_get_email": "فشل في الحصول على عنوان البريد الإلكتروني",
|
||||
"failed_to_get_token": "فشل في الحصول على الرمز",
|
||||
"failed_to_get_account_info": "فشل في الحصول على معلومات الحساب",
|
||||
"title": "معلومات الحساب",
|
||||
"email": "البريد الإلكتروني",
|
||||
"token": "الرمز",
|
||||
"usage": "الاستخدام",
|
||||
"subscription_type": "نوع الاشتراك",
|
||||
"remaining_trial": "الفترة التجريبية المتبقية",
|
||||
"days_remaining": "الأيام المتبقية",
|
||||
"premium": "مميز",
|
||||
"pro": "محترف",
|
||||
"pro_trial": "تجريبي محترف",
|
||||
"team": "فريق",
|
||||
"enterprise": "مؤسسة",
|
||||
"free": "مجاني",
|
||||
"active": "نشط",
|
||||
"inactive": "غير نشط",
|
||||
"premium_usage": "استخدام مميز",
|
||||
"basic_usage": "استخدام أساسي",
|
||||
"usage_not_found": "لم يتم العثور على الاستخدام",
|
||||
"lifetime_access_enabled": "تم تمكين الوصول مدى الحياة",
|
||||
"token_not_found": "لم يتم العثور على الرمز"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "الإعدادات غير متاحة",
|
||||
"configuration": "الإعدادات",
|
||||
"enabled": "ممكّن",
|
||||
"disabled": "معطّل",
|
||||
"config_directory": "دليل الإعدادات",
|
||||
"neither_cursor_nor_cursor_directory_found": "لم يتم العثور على Cursor أو دليل Cursor في {config_base}",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "يرجى التأكد من تثبيت Cursor وتشغيله مرة واحدة على الأقل",
|
||||
"storage_directory_not_found": "لم يتم العثور على دليل التخزين: {storage_dir}",
|
||||
"storage_file_found": "تم العثور على ملف التخزين: {storage_path}",
|
||||
"file_size": "حجم الملف: {size} بايت",
|
||||
"file_permissions": "صلاحيات الملف: {permissions}",
|
||||
"file_owner": "مالك الملف: {owner}",
|
||||
"file_group": "مجموعة الملف: {group}",
|
||||
"error_getting_file_stats": "خطأ في الحصول على إحصائيات الملف: {error}",
|
||||
"permission_denied": "تم رفض الإذن: {storage_path}",
|
||||
"try_running": "حاول تشغيل: {command}",
|
||||
"and": "و",
|
||||
"storage_file_is_empty": "ملف التخزين فارغ: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "قد يكون الملف تالفًا، يرجى إعادة تثبيت Cursor",
|
||||
"storage_file_not_found": "لم يتم العثور على ملف التخزين: {storage_path}",
|
||||
"error_checking_linux_paths": "خطأ في فحص مسارات Linux: {error}",
|
||||
"config_option_added": "تمت إضافة خيار الإعدادات: {option}",
|
||||
"config_updated": "تم تحديث الإعدادات",
|
||||
"config_created": "تم إنشاء الإعدادات: {config_file}",
|
||||
"config_setup_error": "خطأ في إعداد الإعدادات: {error}",
|
||||
"storage_file_is_valid_and_contains_data": "ملف التخزين صالح ويحتوي على بيانات",
|
||||
"error_reading_storage_file": "خطأ في قراءة ملف التخزين: {error}",
|
||||
"also_checked": "تم فحص {path} أيضًا",
|
||||
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
|
||||
"config_removed": "تمت إزالة ملف الإعدادات للتحديث القسري",
|
||||
"backup_failed": "فشل نسخ الإعدادات احتياطيًا: {error}",
|
||||
"force_update_failed": "فشل تحديث الإعدادات القسري: {error}",
|
||||
"config_force_update_disabled": "تم تعطيل التحديث القسري لملف الإعدادات، جارٍ تخطي التحديث القسري",
|
||||
"config_force_update_enabled": "تم تمكين التحديث القسري لملف الإعدادات، جارٍ إجراء التحديث القسري",
|
||||
"documents_path_not_found": "لم يتم العثور على مسار المستندات، جارٍ استخدام الدليل الحالي",
|
||||
"config_dir_created": "تم إنشاء دليل الإعدادات: {path}",
|
||||
"using_temp_dir": "جارٍ استخدام دليل مؤقت بسبب خطأ: {path} (الخطأ: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "لم يتم العثور على زر المصادقة",
|
||||
"authentication_failed": "فشلت المصادقة: {error}",
|
||||
"found_cookies": "تم العثور على {count} من ملفات تعريف الارتباط",
|
||||
"token_extraction_error": "خطأ في استخراج الرمز: {error}",
|
||||
"authentication_successful": "تمت المصادقة بنجاح - البريد الإلكتروني: {email}",
|
||||
"missing_authentication_data": "بيانات المصادقة مفقودة: {data}",
|
||||
"failed_to_delete_account": "فشل حذف الحساب: {error}",
|
||||
"invalid_authentication_type": "نوع المصادقة غير صالح",
|
||||
"auth_update_success": "تم تحديث المصادقة بنجاح",
|
||||
"browser_closed": "تم إغلاق المتصفح",
|
||||
"auth_update_failed": "فشل تحديث المصادقة",
|
||||
"google_start": "بدء Google",
|
||||
"github_start": "بدء Github",
|
||||
"usage_count": "عدد مرات الاستخدام: {usage}",
|
||||
"account_has_reached_maximum_usage": "وصل الحساب إلى الحد الأقصى للاستخدام، {deleting}",
|
||||
"starting_new_authentication_process": "جارٍ بدء عملية مصادقة جديدة...",
|
||||
"failed_to_delete_expired_account": "فشل حذف الحساب المنتهي",
|
||||
"could_not_check_usage_count": "تعذر التحقق من عدد مرات الاستخدام: {error}",
|
||||
"found_email": "تم العثور على البريد الإلكتروني: {email}",
|
||||
"could_not_find_email": "تعذر العثور على البريد الإلكتروني: {error}",
|
||||
"could_not_find_usage_count": "تعذر العثور على عدد مرات الاستخدام: {error}",
|
||||
"already_on_settings_page": "أنت بالفعل في صفحة الإعدادات!",
|
||||
"failed_to_extract_auth_info": "فشل استخراج معلومات المصادقة: {error}",
|
||||
"no_chrome_profiles_found": "لم يتم العثور على ملفات تعريف Chrome، جارٍ استخدام الملف الافتراضي",
|
||||
"found_default_chrome_profile": "تم العثور على ملف تعريف Chrome الافتراضي",
|
||||
"using_first_available_chrome_profile": "جارٍ استخدام أول ملف تعريف Chrome متاح: {profile}",
|
||||
"error_finding_chrome_profile": "خطأ في العثور على ملف تعريف Chrome، جارٍ استخدام الملف الافتراضي: {error}",
|
||||
"initializing_browser_setup": "جارٍ تهيئة إعداد المتصفح...",
|
||||
"detected_platform": "تم اكتشاف النظام: {platform}",
|
||||
"running_as_root_warning": "التشغيل كمستخدم جذر غير مستحسن لأتمتة المتصفح",
|
||||
"consider_running_without_sudo": "فكر في تشغيل البرنامج بدون sudo",
|
||||
"no_compatible_browser_found": "لم يتم العثور على متصفح متوافق. يرجى تثبيت Google Chrome أو Chromium.",
|
||||
"supported_browsers": "المتصفحات المدعومة لـ {platform}",
|
||||
"using_browser_profile": "جارٍ استخدام ملف تعريف المتصفح: {profile}",
|
||||
"starting_browser": "جارٍ بدء المتصفح في: {path}",
|
||||
"browser_setup_completed": "تم إكمال إعداد المتصفح بنجاح",
|
||||
"browser_setup_failed": "فشل إعداد المتصفح: {error}",
|
||||
"try_running_without_sudo_admin": "حاول التشغيل بدون امتيازات sudo/المسؤول",
|
||||
"redirecting_to_authenticator_cursor_sh": "جارٍ إعادة التوجيه إلى authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "جارٍ بدء مصادقة Google...",
|
||||
"starting_github_authentication": "جارٍ بدء مصادقة GitHub...",
|
||||
"waiting_for_authentication": "في انتظار المصادقة...",
|
||||
"page_changed_checking_auth": "تغيرت الصفحة، جارٍ التحقق من المصادقة...",
|
||||
"status_check_error": "خطأ في فحص الحالة: {error}",
|
||||
"authentication_timeout": "انتهت مهلة المصادقة",
|
||||
"account_is_still_valid": "الحساب لا يزال صالحًا (الاستخدام: {usage})",
|
||||
"starting_re_authentication_process": "جارٍ بدء عملية إعادة المصادقة...",
|
||||
"starting_new_google_authentication": "جارٍ بدء مصادقة Google جديدة...",
|
||||
"failed_to_delete_account_or_re_authenticate": "فشل حذف الحساب أو إعادة المصادقة: {error}",
|
||||
"navigating_to_authentication_page": "جارٍ الانتقال إلى صفحة المصادقة...",
|
||||
"please_select_your_google_account_to_continue": "يرجى اختيار حساب Google الخاص بك للمتابعة...",
|
||||
"found_browser_data_directory": "تم العثور على دليل بيانات المتصفح: {path}",
|
||||
"authentication_successful_getting_account_info": "تمت المصادقة بنجاح، جارٍ الحصول على معلومات الحساب...",
|
||||
"warning_could_not_kill_existing_browser_processes": "تحذير: تعذر إنهاء عمليات المتصفح الحالية: {error}",
|
||||
"browser_failed_to_start": "فشل بدء تشغيل المتصفح: {error}",
|
||||
"browser_failed": "فشل بدء تشغيل المتصفح: {error}",
|
||||
"browser_failed_to_start_fallback": "فشل بدء تشغيل المتصفح: {error}",
|
||||
"user_data_dir_not_found": "لم يتم العثور على دليل بيانات مستخدم {browser} في {path}، سيتم تجربة Chrome بدلاً من ذلك",
|
||||
"error_getting_user_data_directory": "خطأ في الحصول على دليل بيانات المستخدم: {error}",
|
||||
"warning_browser_close": "تحذير: سيؤدي هذا إلى إغلاق جميع عمليات {browser} قيد التشغيل",
|
||||
"killing_browser_processes": "جارٍ إنهاء عمليات {browser}...",
|
||||
"profile_selection_error": "خطأ أثناء اختيار الملف الشخصي: {error}",
|
||||
"using_configured_browser_path": "جارٍ استخدام مسار {browser} المُكوّن: {path}",
|
||||
"browser_not_found_trying_chrome": "تعذر العثور على {browser}، جارٍ تجربة Chrome بدلاً من ذلك",
|
||||
"found_chrome_at": "تم العثور على Chrome في: {path}",
|
||||
"found_browser_user_data_dir": "تم العثور على دليل بيانات مستخدم {browser}: {path}"
|
||||
},
|
||||
"browser_profile": {
|
||||
"title": "اختيار ملف تعريف المتصفح",
|
||||
"select_profile": "اختر ملف تعريف {browser} للاستخدام:",
|
||||
"profile_list": "ملفات تعريف {browser} المتاحة:",
|
||||
"default_profile": "ملف التعريف الافتراضي",
|
||||
"profile": "ملف التعريف {number}",
|
||||
"no_profiles": "لم يتم العثور على ملفات تعريف {browser}",
|
||||
"error_loading": "خطأ في تحميل ملفات تعريف {browser}: {error}",
|
||||
"profile_selected": "ملف التعريف المحدد: {profile}",
|
||||
"invalid_selection": "اختيار غير صالح. يرجى المحاولة مرة أخرى."
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "أداة حذف حساب Cursor المرتبط بـ Google",
|
||||
"warning": "تحذير: سيؤدي هذا إلى حذف حساب Cursor الخاص بك بشكل دائم. لا يمكن التراجع عن هذا الإجراء.",
|
||||
"cancelled": "تم إلغاء حذف الحساب.",
|
||||
"starting_process": "جارٍ بدء عملية حذف الحساب...",
|
||||
"google_button_not_found": "لم يتم العثور على زر تسجيل الدخول بـ Google",
|
||||
"logging_in": "جارٍ تسجيل الدخول باستخدام Google...",
|
||||
"waiting_for_auth": "في انتظار مصادقة Google...",
|
||||
"login_successful": "تم تسجيل الدخول بنجاح",
|
||||
"unexpected_page": "صفحة غير متوقعة بعد تسجيل الدخول: {url}",
|
||||
"trying_settings": "جارٍ محاولة الانتقال إلى صفحة الإعدادات...",
|
||||
"select_google_account": "يرجى اختيار حساب Google الخاص بك...",
|
||||
"auth_timeout": "انتهت مهلة المصادقة، جارٍ المتابعة على أي حال...",
|
||||
"navigating_to_settings": "جارٍ الانتقال إلى صفحة الإعدادات...",
|
||||
"already_on_settings": "أنت بالفعل في صفحة الإعدادات",
|
||||
"login_redirect_failed": "فشلت إعادة توجيه تسجيل الدخول، جارٍ محاولة التنقل المباشر...",
|
||||
"advanced_tab_not_found": "لم يتم العثور على علامة التبويب المتقدمة بعد عدة محاولات",
|
||||
"advanced_tab_retry": "لم يتم العثور على علامة التبويب المتقدمة، المحاولة {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "خطأ في العثور على علامة التبويب المتقدمة: {error}",
|
||||
"advanced_tab_clicked": "تم النقر على علامة التبويب المتقدمة",
|
||||
"direct_advanced_navigation": "جارٍ محاولة التنقل المباشر إلى علامة التبويب المتقدمة",
|
||||
"delete_button_not_found": "لم يتم العثور على زر حذف الحساب بعد عدة محاولات",
|
||||
"delete_button_retry": "لم يتم العثور على زر الحذف، المحاولة {attempt}/{max_attempts}",
|
||||
"delete_button_error": "خطأ في العثور على زر الحذف: {error}",
|
||||
"delete_button_clicked": "تم النقر على زر حذف الحساب",
|
||||
"found_danger_zone": "تم العثور على قسم المنطقة الخطرة",
|
||||
"delete_input_not_found": "لم يتم العثور على حقل تأكيد الحذف بعد عدة محاولات",
|
||||
"delete_input_retry": "لم يتم العثور على حقل الحذف، المحاولة {attempt}/{max_attempts}",
|
||||
"delete_input_error": "خطأ في العثور على حقل الحذف: {error}",
|
||||
"delete_input_not_found_continuing": "لم يتم العثور على حقل تأكيد الحذف، جارٍ محاولة المتابعة على أي حال",
|
||||
"typed_delete": "تم كتابة \"Delete\" في مربع التأكيد",
|
||||
"confirm_button_not_found": "لم يتم العثور على زر التأكيد بعد عدة محاولات",
|
||||
"confirm_button_retry": "لم يتم العثور على زر التأكيد، المحاولة {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "خطأ في العثور على زر التأكيد: {error}",
|
||||
"account_deleted": "تم حذف الحساب بنجاح!",
|
||||
"error": "خطأ أثناء حذف الحساب: {error}",
|
||||
"success": "تم حذف حساب Cursor الخاص بك بنجاح!",
|
||||
"failed": "فشلت عملية حذف الحساب أو تم إلغاؤها.",
|
||||
"interrupted": "تمت مقاطعة عملية حذف الحساب بواسطة المستخدم.",
|
||||
"unexpected_error": "خطأ غير متوقع: {error}",
|
||||
"found_email": "تم العثور على البريد الإلكتروني: {email}",
|
||||
"email_not_found": "لم يتم العثور على البريد الإلكتروني: {error}",
|
||||
"confirm_prompt": "هل أنت متأكد أنك تريد المتابعة؟ (y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "جارٍ بدء تجاوز إصدار Cursor...",
|
||||
"found_product_json": "تم العثور على product.json: {path}",
|
||||
"no_write_permission": "لا توجد صلاحية كتابة للملف: {path}",
|
||||
"read_failed": "فشلت قراءة product.json: {error}",
|
||||
"current_version": "الإصدار الحالي: {version}",
|
||||
"backup_created": "تم إنشاء نسخة احتياطية: {path}",
|
||||
"version_updated": "تم تحديث الإصدار من {old} إلى {new}",
|
||||
"write_failed": "فشلت كتابة product.json: {error}",
|
||||
"no_update_needed": "لا يلزم التحديث. الإصدار الحالي {version} بالفعل >= 0.46.0",
|
||||
"bypass_failed": "فشل تجاوز الإصدار: {error}",
|
||||
"stack_trace": "تتبع المكدس",
|
||||
"localappdata_not_found": "لم يتم العثور على متغير بيئة LOCALAPPDATA",
|
||||
"product_json_not_found": "لم يتم العثور على product.json في مسارات Linux الشائعة",
|
||||
"unsupported_os": "نظام تشغيل غير مدعوم: {system}",
|
||||
"file_not_found": "لم يتم العثور على الملف: {path}",
|
||||
"title": "أداة تجاوز إصدار Cursor",
|
||||
"description": "تقوم هذه الأداة بتعديل ملف product.json الخاص بـ Cursor لتجاوز قيود الإصدار",
|
||||
"menu_option": "تجاوز فحص إصدار Cursor"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "جارٍ التحقق من التصريح...",
|
||||
"token_source": "الحصول على الرمز من قاعدة البيانات أو إدخاله يدوياً؟ (d/m، الافتراضي: d)",
|
||||
"getting_token_from_db": "جارٍ الحصول على الرمز من قاعدة البيانات...",
|
||||
"token_found_in_db": "تم العثور على الرمز في قاعدة البيانات",
|
||||
"token_not_found_in_db": "لم يتم العثور على الرمز في قاعدة البيانات",
|
||||
"cursor_acc_info_not_found": "لم يتم العثور على cursor_acc_info.py",
|
||||
"error_getting_token_from_db": "خطأ في الحصول على الرمز من قاعدة البيانات: {error}",
|
||||
"enter_token": "أدخل رمز Cursor الخاص بك: ",
|
||||
"token_length": "طول الرمز: {length} حرفاً",
|
||||
"usage_response_status": "حالة استجابة الاستخدام: {response}",
|
||||
"unexpected_status_code": "رمز حالة غير متوقع: {code}",
|
||||
"jwt_token_warning": "يبدو أن الرمز بتنسيق JWT، لكن فحص API أعاد رمز حالة غير متوقع. قد يكون الرمز صالحاً ولكن الوصول إلى API مقيد.",
|
||||
"invalid_token": "رمز غير صالح",
|
||||
"user_authorized": "المستخدم مصرح له",
|
||||
"user_unauthorized": "المستخدم غير مصرح له",
|
||||
"request_timeout": "انتهت مهلة الطلب",
|
||||
"connection_error": "خطأ في الاتصال",
|
||||
"check_error": "خطأ في التحقق من التصريح: {error}",
|
||||
"authorization_successful": "تم التصريح بنجاح!",
|
||||
"authorization_failed": "فشل التصريح!",
|
||||
"operation_cancelled": "تم إلغاء العملية بواسطة المستخدم",
|
||||
"unexpected_error": "خطأ غير متوقع: {error}",
|
||||
"error_generating_checksum": "خطأ في إنشاء المجموع الاختباري: {error}",
|
||||
"checking_usage_information": "جارٍ التحقق من معلومات الاستخدام...",
|
||||
"check_usage_response": "استجابة فحص الاستخدام: {response}",
|
||||
"usage_response": "استجابة الاستخدام: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "أداة تجاوز حد الرمز",
|
||||
"description": "تقوم هذه الأداة بتعديل ملف workbench.desktop.main.js لتجاوز حد الرمز",
|
||||
"press_enter": "اضغط Enter للمتابعة..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "جارٍ تحديث الرمز...",
|
||||
"refresh_success": "تم تحديث الرمز بنجاح! صالح لمدة {days} يوماً (تاريخ انتهاء الصلاحية: {expire})",
|
||||
"no_access_token": "لا يوجد رمز وصول في الاستجابة",
|
||||
"refresh_failed": "فشل تحديث الرمز: {error}",
|
||||
"invalid_response": "استجابة JSON غير صالحة من خادم التحديث",
|
||||
"server_error": "خطأ في خادم التحديث: HTTP {status}",
|
||||
"request_timeout": "انتهت مهلة طلب خادم التحديث",
|
||||
"connection_error": "خطأ في الاتصال بخادم التحديث",
|
||||
"unexpected_error": "خطأ غير متوقع أثناء تحديث الرمز: {error}",
|
||||
"extraction_error": "خطأ في استخراج الرمز: {error}"
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,22 @@
|
||||
"totally_reset": "Нулирайте изцяло Курсор",
|
||||
"outdate": "Изтекъл срок",
|
||||
"temp_github_register": "Временно регистриране с GitHub",
|
||||
"coming_soon": "Очаквайте скоро"
|
||||
"coming_soon": "Очаквайте скоро",
|
||||
"fixed_soon": "Ще бъде поправено скоро",
|
||||
"contribute": "Принос към проекта",
|
||||
"config": "Покажи конфигурацията",
|
||||
"delete_google_account": "Изтрий Google акаунта на Cursor",
|
||||
"continue_prompt": "Продължи? (y/N): ",
|
||||
"operation_cancelled_by_user": "Операцията е отменена от потребителя",
|
||||
"exiting": "Излизане ......",
|
||||
"bypass_version_check": "Пропусни проверката на версията на Cursor",
|
||||
"check_user_authorized": "Провери оторизацията на потребителя",
|
||||
"select_chrome_profile": "Избери Chrome профил",
|
||||
"bypass_token_limit": "Заобикаляне на ограничението на токените",
|
||||
"restore_machine_id": "Възстановяване на машинен идентификатор от резервно копие"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Арабски",
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
@@ -400,5 +413,48 @@
|
||||
"profile_selected": "Избран профил: {profile}",
|
||||
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
|
||||
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Възстановяване на машинен идентификатор от резервно копие",
|
||||
"starting": "Стартиране на процеса на възстановяване на машинния идентификатор",
|
||||
"no_backups_found": "Не са намерени резервни копия",
|
||||
"available_backups": "Налични резервни копия",
|
||||
"select_backup": "Изберете резервно копие за възстановяване",
|
||||
"to_cancel": "за отказ",
|
||||
"operation_cancelled": "Операцията е отменена",
|
||||
"invalid_selection": "Невалиден избор",
|
||||
"please_enter_number": "Моля, въведете валиден номер",
|
||||
"missing_id": "Липсващ идентификатор: {id}",
|
||||
"read_backup_failed": "Неуспешно четене на резервното копие: {error}",
|
||||
"current_file_not_found": "Текущият файл за съхранение не е намерен",
|
||||
"current_backup_created": "Създадено е резервно копие на текущия файл за съхранение",
|
||||
"storage_updated": "Файлът за съхранение е успешно актуализиран",
|
||||
"update_failed": "Неуспешно актуализиране на файла за съхранение: {error}",
|
||||
"sqlite_not_found": "SQLite базата данни не е намерена",
|
||||
"updating_sqlite": "Актуализиране на SQLite базата данни",
|
||||
"updating_pair": "Актуализиране на двойката ключ-стойност",
|
||||
"sqlite_updated": "SQLite базата данни е успешно актуализирана",
|
||||
"sqlite_update_failed": "Неуспешно актуализиране на SQLite базата данни: {error}",
|
||||
"machine_id_backup_created": "Създадено е резервно копие на файла с машинния идентификатор",
|
||||
"backup_creation_failed": "Неуспешно създаване на резервно копие: {error}",
|
||||
"machine_id_updated": "Файлът с машинния идентификатор е успешно актуализиран",
|
||||
"machine_id_update_failed": "Неуспешно актуализиране на файла с машинния идентификатор: {error}",
|
||||
"updating_system_ids": "Актуализиране на системните идентификатори",
|
||||
"system_ids_update_failed": "Неуспешно актуализиране на системните идентификатори: {error}",
|
||||
"permission_denied": "Достъпът е отказан. Опитайте да стартирате като администратор",
|
||||
"windows_machine_guid_updated": "Windows машинният GUID е успешно актуализиран",
|
||||
"update_windows_machine_guid_failed": "Неуспешно актуализиране на Windows машинния GUID: {error}",
|
||||
"windows_machine_id_updated": "Windows машинният идентификатор е успешно актуализиран",
|
||||
"update_windows_machine_id_failed": "Неуспешно актуализиране на Windows машинния идентификатор: {error}",
|
||||
"sqm_client_key_not_found": "Регистърният ключ SQMClient не е намерен",
|
||||
"update_windows_system_ids_failed": "Неуспешно актуализиране на Windows системните идентификатори: {error}",
|
||||
"macos_platform_uuid_updated": "macOS платформеният UUID е успешно актуализиран",
|
||||
"failed_to_execute_plutil_command": "Неуспешно изпълнение на plutil командата",
|
||||
"update_macos_system_ids_failed": "Неуспешно актуализиране на macOS системните идентификатори: {error}",
|
||||
"ids_to_restore": "Машинни идентификатори за възстановяване",
|
||||
"confirm": "Сигурни ли сте, че искате да възстановите тези идентификатори?",
|
||||
"success": "Машинният идентификатор е успешно възстановен",
|
||||
"process_error": "Грешка при процеса на възстановяване: {error}",
|
||||
"press_enter": "Натиснете Enter, за да продължите"
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,21 @@
|
||||
"temp_github_register": "Temporäre GitHub-Registrierung",
|
||||
"admin_required": "Ausführen als ausführbare Datei, Administratorrechte erforderlich.",
|
||||
"admin_required_continue": "Mit der aktuellen Version fortfahren...",
|
||||
"coming_soon": "Bald verfügbar"
|
||||
"coming_soon": "Bald verfügbar",
|
||||
"fixed_soon": "Bald Behoben",
|
||||
"contribute": "Zum Projekt Beitragen",
|
||||
"config": "Konfiguration Anzeigen",
|
||||
"delete_google_account": "Cursor Google-Konto Löschen",
|
||||
"continue_prompt": "Fortfahren? (y/N): ",
|
||||
"operation_cancelled_by_user": "Vorgang vom Benutzer abgebrochen",
|
||||
"exiting": "Wird beendet ……",
|
||||
"bypass_version_check": "Cursor Versionsprüfung Überspringen",
|
||||
"check_user_authorized": "Benutzerautorisierung Prüfen",
|
||||
"bypass_token_limit": "Token-Limit umgehen",
|
||||
"restore_machine_id": "Geräte-ID aus Backup wiederherstellen"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Arabisch",
|
||||
"en": "Englisch",
|
||||
"zh_cn": "Vereinfachtes Chinesisch",
|
||||
"zh_tw": "Traditionelles Chinesisch",
|
||||
@@ -34,7 +46,9 @@
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ru": "Russisch",
|
||||
"es": "Spanisch"
|
||||
"es": "Spanisch",
|
||||
"tr": "Türkisch",
|
||||
"bg": "Bulgarisch"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Beginne Cursor zu Beenden",
|
||||
@@ -101,7 +115,14 @@
|
||||
"package_not_found": "Package.json Nicht Gefunden: {path}",
|
||||
"check_version_failed": "Versionsüberprüfung Fehlgeschlagen: {error}",
|
||||
"stack_trace": "Stack Trace",
|
||||
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0"
|
||||
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0",
|
||||
"no_write_permission": "Keine Schreibberechtigung: {path}",
|
||||
"path_not_found": "Pfad Nicht Gefunden: {path}",
|
||||
"modify_file_failed": "Datei Ändern Fehlgeschlagen: {error}",
|
||||
"windows_machine_id_updated": "Windows Maschinen-ID Erfolgreich Aktualisiert",
|
||||
"update_windows_machine_id_failed": "Windows Maschinen-ID Aktualisierung Fehlgeschlagen: {error}",
|
||||
"update_windows_machine_guid_failed": "Windows Maschinen-GUID Aktualisierung Fehlgeschlagen: {error}",
|
||||
"file_not_found": "Datei Nicht Gefunden: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registrierungstool",
|
||||
@@ -389,5 +410,48 @@
|
||||
"profile_selected": "Ausgewähltes Profil: {profile}",
|
||||
"invalid_selection": "Ungültige Auswahl. Bitte versuchen Sie es erneut",
|
||||
"warning_chrome_close": "Warnung: Dies wird alle laufenden Chrome-Prozesse beenden"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Geräte-ID aus Backup wiederherstellen",
|
||||
"starting": "Starte Wiederherstellungsprozess für Geräte-ID",
|
||||
"no_backups_found": "Keine Backup-Dateien gefunden",
|
||||
"available_backups": "Verfügbare Backup-Dateien",
|
||||
"select_backup": "Wählen Sie ein Backup zur Wiederherstellung aus",
|
||||
"to_cancel": "zum Abbrechen",
|
||||
"operation_cancelled": "Vorgang abgebrochen",
|
||||
"invalid_selection": "Ungültige Auswahl",
|
||||
"please_enter_number": "Bitte geben Sie eine gültige Zahl ein",
|
||||
"missing_id": "Fehlende ID: {id}",
|
||||
"read_backup_failed": "Backup-Datei konnte nicht gelesen werden: {error}",
|
||||
"current_file_not_found": "Aktuelle Speicherdatei nicht gefunden",
|
||||
"current_backup_created": "Backup der aktuellen Speicherdatei erstellt",
|
||||
"storage_updated": "Speicherdatei erfolgreich aktualisiert",
|
||||
"update_failed": "Aktualisierung der Speicherdatei fehlgeschlagen: {error}",
|
||||
"sqlite_not_found": "SQLite-Datenbank nicht gefunden",
|
||||
"updating_sqlite": "Aktualisiere SQLite-Datenbank",
|
||||
"updating_pair": "Aktualisiere Schlüssel-Wert-Paar",
|
||||
"sqlite_updated": "SQLite-Datenbank erfolgreich aktualisiert",
|
||||
"sqlite_update_failed": "Aktualisierung der SQLite-Datenbank fehlgeschlagen: {error}",
|
||||
"machine_id_backup_created": "Backup der machineId-Datei erstellt",
|
||||
"backup_creation_failed": "Backup-Erstellung fehlgeschlagen: {error}",
|
||||
"machine_id_updated": "machineId-Datei erfolgreich aktualisiert",
|
||||
"machine_id_update_failed": "Aktualisierung der machineId-Datei fehlgeschlagen: {error}",
|
||||
"updating_system_ids": "Aktualisiere System-IDs",
|
||||
"system_ids_update_failed": "Aktualisierung der System-IDs fehlgeschlagen: {error}",
|
||||
"permission_denied": "Zugriff verweigert. Bitte versuchen Sie es mit Administratorrechten",
|
||||
"windows_machine_guid_updated": "Windows-Geräte-GUID erfolgreich aktualisiert",
|
||||
"update_windows_machine_guid_failed": "Aktualisierung des Windows-Geräte-GUID fehlgeschlagen: {error}",
|
||||
"windows_machine_id_updated": "Windows-Geräte-ID erfolgreich aktualisiert",
|
||||
"update_windows_machine_id_failed": "Aktualisierung der Windows-Geräte-ID fehlgeschlagen: {error}",
|
||||
"sqm_client_key_not_found": "SQMClient-Registrierungsschlüssel nicht gefunden",
|
||||
"update_windows_system_ids_failed": "Aktualisierung der Windows-System-IDs fehlgeschlagen: {error}",
|
||||
"macos_platform_uuid_updated": "macOS-Plattform-UUID erfolgreich aktualisiert",
|
||||
"failed_to_execute_plutil_command": "Ausführung des plutil-Befehls fehlgeschlagen",
|
||||
"update_macos_system_ids_failed": "Aktualisierung der macOS-System-IDs fehlgeschlagen: {error}",
|
||||
"ids_to_restore": "Wiederherzustellende Geräte-IDs",
|
||||
"confirm": "Sind Sie sicher, dass Sie diese IDs wiederherstellen möchten?",
|
||||
"success": "Geräte-ID erfolgreich wiederhergestellt",
|
||||
"process_error": "Fehler beim Wiederherstellungsprozess: {error}",
|
||||
"press_enter": "Drücken Sie Enter, um fortzufahren"
|
||||
}
|
||||
}
|
||||
154
locales/en.json
154
locales/en.json
@@ -4,8 +4,8 @@
|
||||
"exit": "Exit Program",
|
||||
"reset": "Reset Machine ID",
|
||||
"register": "Register New Cursor Account",
|
||||
"register_google": "Register with Google Account",
|
||||
"register_github": "Register with GitHub Account",
|
||||
"register_google": "Register with Self Google Account",
|
||||
"register_github": "Register with Self GitHub Account",
|
||||
"register_manual": "Register Cursor with Custom Email",
|
||||
"quit": "Close Cursor Application",
|
||||
"select_language": "Change Language",
|
||||
@@ -30,9 +30,15 @@
|
||||
"continue_prompt": "Continue? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operation cancelled by user",
|
||||
"exiting": "Exiting ……",
|
||||
"bypass_version_check": "Bypass Cursor Version Check"
|
||||
"bypass_version_check": "Bypass Cursor Version Check",
|
||||
"check_user_authorized": "Check User Authorized",
|
||||
"bypass_token_limit": "Bypass Token Limit",
|
||||
"language_config_saved": "Language configuration saved successfully",
|
||||
"lang_invalid_choice": "Invalid choice. Please enter one of the following options: ({lang_choices})",
|
||||
"restore_machine_id": "Restore Machine ID from Backup"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Arabic",
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
@@ -117,7 +123,8 @@
|
||||
"modify_file_failed": "Modify File Failed: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine ID Updated Successfully",
|
||||
"update_windows_machine_id_failed": "Update Windows Machine ID Failed: {error}",
|
||||
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}"
|
||||
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}",
|
||||
"file_not_found": "File Not Found: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registration Tool",
|
||||
@@ -196,7 +203,15 @@
|
||||
"setting_on_password": "Setting Password",
|
||||
"getting_code": "Getting Verification Code, Will Try in 60s",
|
||||
"human_verify_error": "Can't verify the user is human. Retrying...",
|
||||
"max_retries_reached": "Maximum retry attempts reached. Registration failed."
|
||||
"max_retries_reached": "Maximum retry attempts reached. Registration failed.",
|
||||
"browser_path_invalid": "{browser} path is invalid, using default path",
|
||||
"using_browser": "Using {browser} browser: {path}",
|
||||
"using_browser_profile": "Using {browser} profile from: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "Make sure {browser} is properly installed",
|
||||
"try_install_browser": "Try installing the browser with your package manager",
|
||||
"tracking_processes": "Tracking {count} {browser} processes",
|
||||
"no_new_processes_detected": "No new {browser} processes detected to track",
|
||||
"could_not_track_processes": "Could not track {browser} processes: {error}"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Auth Manager",
|
||||
@@ -555,7 +570,16 @@
|
||||
"config_setup_error": "Error setting up config: {error}",
|
||||
"storage_file_is_valid_and_contains_data": "Storage file is valid and contains data",
|
||||
"error_reading_storage_file": "Error reading storage file: {error}",
|
||||
"also_checked": "Also checked {path}"
|
||||
"also_checked": "Also checked {path}",
|
||||
"backup_created": "Backup created: {path}",
|
||||
"config_removed": "Config file removed for forced update",
|
||||
"backup_failed": "Failed to backup config: {error}",
|
||||
"force_update_failed": "Force update config failed: {error}",
|
||||
"config_force_update_disabled": "Config file force update disabled , skipping forced update",
|
||||
"config_force_update_enabled": "Config file force update enabled , performing forced update",
|
||||
"documents_path_not_found": "Documents path not found, using current directory",
|
||||
"config_dir_created": "Config directory created: {path}",
|
||||
"using_temp_dir": "Using temporary directory due to error: {path} (Error: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Authentication button not found",
|
||||
@@ -614,19 +638,27 @@
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}",
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}"
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
|
||||
"user_data_dir_not_found": "{browser} user data directory not found at {path}, will try Chrome instead",
|
||||
"error_getting_user_data_directory": "Error getting user data directory: {error}",
|
||||
"warning_browser_close": "Warning: This will close all running {browser} processes",
|
||||
"killing_browser_processes": "Killing {browser} processes...",
|
||||
"profile_selection_error": "Error during profile selection: {error}",
|
||||
"using_configured_browser_path": "Using configured {browser} path: {path}",
|
||||
"browser_not_found_trying_chrome": "Could not find {browser}, trying Chrome instead",
|
||||
"found_chrome_at": "Found Chrome at: {path}",
|
||||
"found_browser_user_data_dir": "Found {browser} user data directory: {path}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profile Selection",
|
||||
"select_profile": "Select a Chrome profile to use:",
|
||||
"profile_list": "Available profiles:",
|
||||
"default_profile": "Default Profile",
|
||||
"browser_profile": {
|
||||
"title": "Browser Profile Selection",
|
||||
"select_profile": "Select {browser} profile to use:",
|
||||
"profile_list": "Available {browser} profiles:",
|
||||
"default_profile": "Default profile",
|
||||
"profile": "Profile {number}",
|
||||
"no_profiles": "No Chrome profiles found",
|
||||
"error_loading": "Error loading Chrome profiles: {error}",
|
||||
"no_profiles": "No {browser} profiles found",
|
||||
"error_loading": "Error loading {browser} profiles: {error}",
|
||||
"profile_selected": "Selected profile: {profile}",
|
||||
"invalid_selection": "Invalid selection. Please try again",
|
||||
"warning_chrome_close": "Warning: This will close all running Chrome processes"
|
||||
"invalid_selection": "Invalid selection. Please try again."
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google Account Deletion Tool",
|
||||
@@ -691,5 +723,93 @@
|
||||
"title": "Cursor Version Bypass Tool",
|
||||
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
|
||||
"menu_option": "Bypass Cursor Version Check"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "Checking authorization...",
|
||||
"token_source": "Get token from database or input manually? (d/m, default: d)",
|
||||
"getting_token_from_db": "Getting token from database...",
|
||||
"token_found_in_db": "Token found in database",
|
||||
"token_not_found_in_db": "Token not found in database",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
|
||||
"error_getting_token_from_db": "Error getting token from database: {error}",
|
||||
"enter_token": "Enter your Cursor token: ",
|
||||
"token_length": "Token length: {length} characters",
|
||||
"usage_response_status": "Usage response status: {response}",
|
||||
"unexpected_status_code": "Unexpected status code: {code}",
|
||||
"jwt_token_warning": "Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.",
|
||||
"invalid_token": "Invalid token",
|
||||
"user_authorized": "User is authorized",
|
||||
"user_unauthorized": "User is unauthorized",
|
||||
"request_timeout": "Request timed out",
|
||||
"connection_error": "Connection error",
|
||||
"check_error": "Error checking authorization: {error}",
|
||||
"authorization_successful": "Authorization successful!",
|
||||
"authorization_failed": "Authorization failed!",
|
||||
"operation_cancelled": "Operation cancelled by user",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"error_generating_checksum": "Error generating checksum: {error}",
|
||||
"checking_usage_information": "Checking usage information...",
|
||||
"check_usage_response": "Check usage response: {response}",
|
||||
"usage_response": "Usage response: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "Bypass Token Limit Tool",
|
||||
"description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit",
|
||||
"press_enter": "Press Enter to continue..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "Refreshing token...",
|
||||
"refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})",
|
||||
"no_access_token": "No access token in response",
|
||||
"refresh_failed": "Token refresh failed: {error}",
|
||||
"invalid_response": "Invalid JSON response from refresh server",
|
||||
"server_error": "Refresh server error: HTTP {status}",
|
||||
"request_timeout": "Request to refresh server timed out",
|
||||
"connection_error": "Connection error to refresh server",
|
||||
"unexpected_error": "Unexpected error during token refresh: {error}",
|
||||
"extraction_error": "Error extracting token: {error}"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Restore Machine ID from Backup",
|
||||
"starting": "Starting Machine ID Restore Process",
|
||||
"no_backups_found": "No backup files found",
|
||||
"available_backups": "Available backup files",
|
||||
"select_backup": "Select backup to restore",
|
||||
"to_cancel": "to cancel",
|
||||
"operation_cancelled": "Operation cancelled",
|
||||
"invalid_selection": "Invalid selection",
|
||||
"please_enter_number": "Please enter a valid number",
|
||||
"missing_id": "Missing ID: {id}",
|
||||
"read_backup_failed": "Failed to read backup file: {error}",
|
||||
"current_file_not_found": "Current storage file not found",
|
||||
"current_backup_created": "Created backup of current storage file",
|
||||
"storage_updated": "Storage file updated successfully",
|
||||
"update_failed": "Failed to update storage file: {error}",
|
||||
"sqlite_not_found": "SQLite database not found",
|
||||
"updating_sqlite": "Updating SQLite database",
|
||||
"updating_pair": "Updating key-value pair",
|
||||
"sqlite_updated": "SQLite database updated successfully",
|
||||
"sqlite_update_failed": "Failed to update SQLite database: {error}",
|
||||
"machine_id_backup_created": "Created backup of machineId file",
|
||||
"backup_creation_failed": "Failed to create backup: {error}",
|
||||
"machine_id_updated": "machineId file updated successfully",
|
||||
"machine_id_update_failed": "Failed to update machineId file: {error}",
|
||||
"updating_system_ids": "Updating system IDs",
|
||||
"system_ids_update_failed": "Failed to update system IDs: {error}",
|
||||
"permission_denied": "Permission denied. Please try running as administrator",
|
||||
"windows_machine_guid_updated": "Windows Machine GUID updated successfully",
|
||||
"update_windows_machine_guid_failed": "Failed to update Windows Machine GUID: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine ID updated successfully",
|
||||
"update_windows_machine_id_failed": "Failed to update Windows Machine ID: {error}",
|
||||
"sqm_client_key_not_found": "SQMClient registry key not found",
|
||||
"update_windows_system_ids_failed": "Failed to update Windows system IDs: {error}",
|
||||
"macos_platform_uuid_updated": "macOS Platform UUID updated successfully",
|
||||
"failed_to_execute_plutil_command": "Failed to execute plutil command",
|
||||
"update_macos_system_ids_failed": "Failed to update macOS system IDs: {error}",
|
||||
"ids_to_restore": "Machine IDs to restore",
|
||||
"confirm": "Are you sure you want to restore these IDs?",
|
||||
"success": "Machine ID restored successfully",
|
||||
"process_error": "Restore process error: {error}",
|
||||
"press_enter": "Press Enter to continue"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,20 @@
|
||||
"admin_required": "Ejecutando como ejecutable, se requieren privilegios de administrador.",
|
||||
"admin_required_continue": "Continuando sin privilegios de administrador.",
|
||||
"coming_soon": "Próximamente",
|
||||
"fixed_soon": "Arreglado Pronto"
|
||||
"fixed_soon": "Arreglado Pronto",
|
||||
"contribute": "Contribuir al Proyecto",
|
||||
"config": "Mostrar Configuración",
|
||||
"delete_google_account": "Eliminar Cuenta Google de Cursor",
|
||||
"continue_prompt": "¿Continuar? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operación cancelada por el usuario",
|
||||
"exiting": "Saliendo ……",
|
||||
"bypass_version_check": "Omitir Verificación de Versión de Cursor",
|
||||
"check_user_authorized": "Verificar Usuario Autorizado",
|
||||
"bypass_token_limit": "Omitir límite de tokens",
|
||||
"restore_machine_id": "Restaurar ID de máquina desde copia de seguridad"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Árabe",
|
||||
"en": "Inglés",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
@@ -103,8 +114,14 @@
|
||||
"package_not_found": "Package.json No Encontrado: {path}",
|
||||
"check_version_failed": "Falló la Verificación de Versión: {error}",
|
||||
"stack_trace": "Traza de la Pila",
|
||||
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0"
|
||||
|
||||
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0",
|
||||
"no_write_permission": "Sin Permiso de Escritura: {path}",
|
||||
"path_not_found": "Ruta No Encontrada: {path}",
|
||||
"modify_file_failed": "Falló la Modificación del Archivo: {error}",
|
||||
"windows_machine_id_updated": "ID de Máquina Windows Actualizado Exitosamente",
|
||||
"update_windows_machine_id_failed": "Falló la Actualización del ID de Máquina Windows: {error}",
|
||||
"update_windows_machine_guid_failed": "Falló la Actualización del GUID de Máquina Windows: {error}",
|
||||
"file_not_found": "Archivo No Encontrado: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Herramienta de Registro de Cursor",
|
||||
@@ -451,5 +468,48 @@
|
||||
"profile_selected": "Perfil seleccionado: {profile}",
|
||||
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
|
||||
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Restaurar ID de máquina desde copia de seguridad",
|
||||
"starting": "Iniciando proceso de restauración de ID de máquina",
|
||||
"no_backups_found": "No se encontraron copias de seguridad",
|
||||
"available_backups": "Copias de seguridad disponibles",
|
||||
"select_backup": "Seleccione una copia de seguridad para restaurar",
|
||||
"to_cancel": "para cancelar",
|
||||
"operation_cancelled": "Operación cancelada",
|
||||
"invalid_selection": "Selección inválida",
|
||||
"please_enter_number": "Por favor, introduzca un número válido",
|
||||
"missing_id": "ID faltante: {id}",
|
||||
"read_backup_failed": "Error al leer el archivo de copia de seguridad: {error}",
|
||||
"current_file_not_found": "No se encontró el archivo de almacenamiento actual",
|
||||
"current_backup_created": "Se creó una copia de seguridad del archivo de almacenamiento actual",
|
||||
"storage_updated": "Archivo de almacenamiento actualizado con éxito",
|
||||
"update_failed": "Error al actualizar el archivo de almacenamiento: {error}",
|
||||
"sqlite_not_found": "No se encontró la base de datos SQLite",
|
||||
"updating_sqlite": "Actualizando base de datos SQLite",
|
||||
"updating_pair": "Actualizando par clave-valor",
|
||||
"sqlite_updated": "Base de datos SQLite actualizada con éxito",
|
||||
"sqlite_update_failed": "Error al actualizar la base de datos SQLite: {error}",
|
||||
"machine_id_backup_created": "Se creó una copia de seguridad del archivo machineId",
|
||||
"backup_creation_failed": "Error al crear la copia de seguridad: {error}",
|
||||
"machine_id_updated": "Archivo machineId actualizado con éxito",
|
||||
"machine_id_update_failed": "Error al actualizar el archivo machineId: {error}",
|
||||
"updating_system_ids": "Actualizando IDs del sistema",
|
||||
"system_ids_update_failed": "Error al actualizar los IDs del sistema: {error}",
|
||||
"permission_denied": "Permiso denegado. Intente ejecutar como administrador",
|
||||
"windows_machine_guid_updated": "GUID de máquina Windows actualizado con éxito",
|
||||
"update_windows_machine_guid_failed": "Error al actualizar el GUID de máquina Windows: {error}",
|
||||
"windows_machine_id_updated": "ID de máquina Windows actualizado con éxito",
|
||||
"update_windows_machine_id_failed": "Error al actualizar el ID de máquina Windows: {error}",
|
||||
"sqm_client_key_not_found": "No se encontró la clave de registro SQMClient",
|
||||
"update_windows_system_ids_failed": "Error al actualizar los IDs del sistema Windows: {error}",
|
||||
"macos_platform_uuid_updated": "UUID de plataforma macOS actualizado con éxito",
|
||||
"failed_to_execute_plutil_command": "Error al ejecutar el comando plutil",
|
||||
"update_macos_system_ids_failed": "Error al actualizar los IDs del sistema macOS: {error}",
|
||||
"ids_to_restore": "IDs de máquina a restaurar",
|
||||
"confirm": "¿Está seguro de que desea restaurar estos IDs?",
|
||||
"success": "ID de máquina restaurado con éxito",
|
||||
"process_error": "Error en el proceso de restauración: {error}",
|
||||
"press_enter": "Presione Enter para continuar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,21 @@
|
||||
"totally_reset": "Réinitialisation Complète de Cursor",
|
||||
"outdate": "Obsolete",
|
||||
"temp_github_register": "Inscription GitHub temporaire",
|
||||
"coming_soon": "Bientôt"
|
||||
"coming_soon": "Bientôt",
|
||||
"fixed_soon": "Bientôt Corrigé",
|
||||
"contribute": "Contribuer au Projet",
|
||||
"config": "Afficher la Configuration",
|
||||
"delete_google_account": "Supprimer le Compte Google Cursor",
|
||||
"continue_prompt": "Continuer ? (y/N) : ",
|
||||
"operation_cancelled_by_user": "Opération annulée par l'utilisateur",
|
||||
"exiting": "Fermeture ……",
|
||||
"bypass_version_check": "Ignorer la Vérification de Version de Cursor",
|
||||
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur",
|
||||
"bypass_token_limit": "Contourner la limite de tokens",
|
||||
"restore_machine_id": "Restaurer l'ID de machine depuis une sauvegarde"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Arabe",
|
||||
"en": "Anglais",
|
||||
"zh_cn": "Chinois simplifié",
|
||||
"zh_tw": "Chinois traditionnel",
|
||||
@@ -31,7 +43,9 @@
|
||||
"fr": "Français",
|
||||
"pt": "Portugais",
|
||||
"ru": "Russe",
|
||||
"es": "Espagnol"
|
||||
"es": "Espagnol",
|
||||
"tr": "Turc",
|
||||
"bg": "Bulgare"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Début de la Fermeture de Cursor",
|
||||
@@ -98,7 +112,14 @@
|
||||
"package_not_found": "Package.json Non Trouvé : {path}",
|
||||
"check_version_failed": "Échec de la Vérification de la Version : {error}",
|
||||
"stack_trace": "Trace de la Pile",
|
||||
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0"
|
||||
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0",
|
||||
"no_write_permission": "Pas de Permission d'Écriture : {path}",
|
||||
"path_not_found": "Chemin Non Trouvé : {path}",
|
||||
"modify_file_failed": "Échec de la Modification du Fichier : {error}",
|
||||
"windows_machine_id_updated": "ID de la Machine Windows Mis à Jour avec Succès",
|
||||
"update_windows_machine_id_failed": "Échec de la Mise à Jour de l'ID de la Machine Windows : {error}",
|
||||
"update_windows_machine_guid_failed": "Échec de la Mise à Jour du GUID de la Machine Windows : {error}",
|
||||
"file_not_found": "Fichier Non Trouvé : {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Outil d'Enregistrement de Cursor",
|
||||
@@ -386,5 +407,48 @@
|
||||
"profile_selected": "Profil sélectionné : {profile}",
|
||||
"invalid_selection": "Sélection invalide. Veuillez réessayer",
|
||||
"warning_chrome_close": "Attention : Cela fermera tous les processus Chrome en cours d'exécution"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Restaurer l'ID de machine depuis une sauvegarde",
|
||||
"starting": "Démarrage du processus de restauration de l'ID de machine",
|
||||
"no_backups_found": "Aucune sauvegarde trouvée",
|
||||
"available_backups": "Sauvegardes disponibles",
|
||||
"select_backup": "Sélectionnez une sauvegarde à restaurer",
|
||||
"to_cancel": "pour annuler",
|
||||
"operation_cancelled": "Opération annulée",
|
||||
"invalid_selection": "Sélection invalide",
|
||||
"please_enter_number": "Veuillez entrer un numéro valide",
|
||||
"missing_id": "ID manquant : {id}",
|
||||
"read_backup_failed": "Échec de lecture du fichier de sauvegarde : {error}",
|
||||
"current_file_not_found": "Fichier de stockage actuel introuvable",
|
||||
"current_backup_created": "Sauvegarde du fichier de stockage actuel créée",
|
||||
"storage_updated": "Fichier de stockage mis à jour avec succès",
|
||||
"update_failed": "Échec de la mise à jour du fichier de stockage : {error}",
|
||||
"sqlite_not_found": "Base de données SQLite introuvable",
|
||||
"updating_sqlite": "Mise à jour de la base de données SQLite",
|
||||
"updating_pair": "Mise à jour de la paire clé-valeur",
|
||||
"sqlite_updated": "Base de données SQLite mise à jour avec succès",
|
||||
"sqlite_update_failed": "Échec de la mise à jour de la base de données SQLite : {error}",
|
||||
"machine_id_backup_created": "Sauvegarde du fichier machineId créée",
|
||||
"backup_creation_failed": "Échec de création de la sauvegarde : {error}",
|
||||
"machine_id_updated": "Fichier machineId mis à jour avec succès",
|
||||
"machine_id_update_failed": "Échec de la mise à jour du fichier machineId : {error}",
|
||||
"updating_system_ids": "Mise à jour des ID système",
|
||||
"system_ids_update_failed": "Échec de la mise à jour des ID système : {error}",
|
||||
"permission_denied": "Permission refusée. Veuillez essayer d'exécuter en tant qu'administrateur",
|
||||
"windows_machine_guid_updated": "GUID de machine Windows mis à jour avec succès",
|
||||
"update_windows_machine_guid_failed": "Échec de la mise à jour du GUID de machine Windows : {error}",
|
||||
"windows_machine_id_updated": "ID de machine Windows mis à jour avec succès",
|
||||
"update_windows_machine_id_failed": "Échec de la mise à jour de l'ID de machine Windows : {error}",
|
||||
"sqm_client_key_not_found": "Clé de registre SQMClient introuvable",
|
||||
"update_windows_system_ids_failed": "Échec de la mise à jour des ID système Windows : {error}",
|
||||
"macos_platform_uuid_updated": "UUID de plateforme macOS mis à jour avec succès",
|
||||
"failed_to_execute_plutil_command": "Échec d'exécution de la commande plutil",
|
||||
"update_macos_system_ids_failed": "Échec de la mise à jour des ID système macOS : {error}",
|
||||
"ids_to_restore": "ID de machine à restaurer",
|
||||
"confirm": "Êtes-vous sûr de vouloir restaurer ces ID ?",
|
||||
"success": "ID de machine restauré avec succès",
|
||||
"process_error": "Erreur du processus de restauration : {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour continuer"
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,21 @@
|
||||
"totally_reset": "Cursor volledig resetten",
|
||||
"outdate": "Verouderd",
|
||||
"temp_github_register": "Tijdelijke GitHub-registratie",
|
||||
"coming_soon": "Binnenkort"
|
||||
"coming_soon": "Binnenkort",
|
||||
"fixed_soon": "Binnenkort Opgelost",
|
||||
"contribute": "Bijdragen aan het Project",
|
||||
"config": "Configuratie Weergeven",
|
||||
"delete_google_account": "Cursor Google Account Verwijderen",
|
||||
"continue_prompt": "Doorgaan? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operatie geannuleerd door gebruiker",
|
||||
"exiting": "Afsluiten ……",
|
||||
"bypass_version_check": "Cursor Versiecontrole Overslaan",
|
||||
"check_user_authorized": "Gebruikersautorisatie Controleren",
|
||||
"bypass_token_limit": "Token-limiet omzeilen",
|
||||
"restore_machine_id": "Machine-ID herstellen vanaf backup"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Arabisch",
|
||||
"en": "Engels",
|
||||
"zh_cn": "Vereenvoudigd Chinees",
|
||||
"zh_tw": "Traditioneel Chinees",
|
||||
@@ -99,7 +111,14 @@
|
||||
"package_not_found": "Package.json niet gevonden: {path}",
|
||||
"check_version_failed": "Versiecontrole mislukt: {error}",
|
||||
"stack_trace": "Stack Trace",
|
||||
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0"
|
||||
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0",
|
||||
"no_write_permission": "Geen schrijfrechten: {path}",
|
||||
"path_not_found": "Pad niet gevonden: {path}",
|
||||
"modify_file_failed": "Bestand wijzigen mislukt: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine-ID succesvol bijgewerkt",
|
||||
"update_windows_machine_id_failed": "Windows Machine-ID bijwerken mislukt: {error}",
|
||||
"update_windows_machine_guid_failed": "Windows Machine GUID bijwerken mislukt: {error}",
|
||||
"file_not_found": "Bestand niet gevonden: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registratietool",
|
||||
@@ -387,5 +406,48 @@
|
||||
"profile_selected": "Geselecteerd profiel: {profile}",
|
||||
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
|
||||
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Machine-ID herstellen vanaf backup",
|
||||
"starting": "Herstelproces van machine-ID starten",
|
||||
"no_backups_found": "Geen backups gevonden",
|
||||
"available_backups": "Beschikbare backups",
|
||||
"select_backup": "Selecteer een backup om te herstellen",
|
||||
"to_cancel": "om te annuleren",
|
||||
"operation_cancelled": "Bewerking geannuleerd",
|
||||
"invalid_selection": "Ongeldige selectie",
|
||||
"please_enter_number": "Voer een geldig nummer in",
|
||||
"missing_id": "Ontbrekende ID: {id}",
|
||||
"read_backup_failed": "Lezen van backupbestand mislukt: {error}",
|
||||
"current_file_not_found": "Huidig opslagbestand niet gevonden",
|
||||
"current_backup_created": "Backup van huidig opslagbestand gemaakt",
|
||||
"storage_updated": "Opslagbestand succesvol bijgewerkt",
|
||||
"update_failed": "Bijwerken van opslagbestand mislukt: {error}",
|
||||
"sqlite_not_found": "SQLite-database niet gevonden",
|
||||
"updating_sqlite": "SQLite-database bijwerken",
|
||||
"updating_pair": "Sleutel-waardepaar bijwerken",
|
||||
"sqlite_updated": "SQLite-database succesvol bijgewerkt",
|
||||
"sqlite_update_failed": "Bijwerken van SQLite-database mislukt: {error}",
|
||||
"machine_id_backup_created": "Backup van machineId-bestand gemaakt",
|
||||
"backup_creation_failed": "Maken van backup mislukt: {error}",
|
||||
"machine_id_updated": "MachineId-bestand succesvol bijgewerkt",
|
||||
"machine_id_update_failed": "Bijwerken van machineId-bestand mislukt: {error}",
|
||||
"updating_system_ids": "Systeem-ID's bijwerken",
|
||||
"system_ids_update_failed": "Bijwerken van systeem-ID's mislukt: {error}",
|
||||
"permission_denied": "Toegang geweigerd. Probeer als administrator uit te voeren",
|
||||
"windows_machine_guid_updated": "Windows machine-GUID succesvol bijgewerkt",
|
||||
"update_windows_machine_guid_failed": "Bijwerken van Windows machine-GUID mislukt: {error}",
|
||||
"windows_machine_id_updated": "Windows machine-ID succesvol bijgewerkt",
|
||||
"update_windows_machine_id_failed": "Bijwerken van Windows machine-ID mislukt: {error}",
|
||||
"sqm_client_key_not_found": "SQMClient registersleutel niet gevonden",
|
||||
"update_windows_system_ids_failed": "Bijwerken van Windows systeem-ID's mislukt: {error}",
|
||||
"macos_platform_uuid_updated": "macOS platform-UUID succesvol bijgewerkt",
|
||||
"failed_to_execute_plutil_command": "Uitvoeren van plutil opdracht mislukt",
|
||||
"update_macos_system_ids_failed": "Bijwerken van macOS systeem-ID's mislukt: {error}",
|
||||
"ids_to_restore": "Te herstellen machine-ID's",
|
||||
"confirm": "Weet u zeker dat u deze ID's wilt herstellen?",
|
||||
"success": "Machine-ID succesvol hersteld",
|
||||
"process_error": "Fout bij herstelproces: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,21 @@
|
||||
"totally_reset": "Redefinir Cursor Completamente",
|
||||
"outdate": "Obsoleto",
|
||||
"temp_github_register": "Registro temporário do GitHub",
|
||||
"coming_soon": "Em breve"
|
||||
"coming_soon": "Em breve",
|
||||
"fixed_soon": "Será corrigido em breve",
|
||||
"contribute": "Contribuir para o Projeto",
|
||||
"config": "Mostrar Configuração",
|
||||
"delete_google_account": "Excluir Conta Google do Cursor",
|
||||
"continue_prompt": "Continuar? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operação cancelada pelo usuário",
|
||||
"exiting": "Saindo ......",
|
||||
"bypass_version_check": "Ignorar Verificação de Versão do Cursor",
|
||||
"check_user_authorized": "Verificar Autorização do Usuário",
|
||||
"bypass_token_limit": "Contornar Limite de Tokens",
|
||||
"restore_machine_id": "Restaurar ID da Máquina do Backup"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Árabe",
|
||||
"en": "Inglês",
|
||||
"zh_cn": "Chinês Simplificado",
|
||||
"zh_tw": "Chinês Tradicional",
|
||||
@@ -395,5 +407,48 @@
|
||||
"profile_selected": "Perfil selecionado: {profile}",
|
||||
"invalid_selection": "Seleção inválida. Por favor, tente novamente",
|
||||
"warning_chrome_close": "Aviso: Isso fechará todos os processos do Chrome em execução"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Restaurar ID da Máquina do Backup",
|
||||
"starting": "Iniciando processo de restauração de ID da máquina",
|
||||
"no_backups_found": "Nenhum backup encontrado",
|
||||
"available_backups": "Backups disponíveis",
|
||||
"select_backup": "Selecione um backup para restaurar",
|
||||
"to_cancel": "para cancelar",
|
||||
"operation_cancelled": "Operação cancelada",
|
||||
"invalid_selection": "Seleção inválida",
|
||||
"please_enter_number": "Por favor, insira um número válido",
|
||||
"missing_id": "ID ausente: {id}",
|
||||
"read_backup_failed": "Falha ao ler arquivo de backup: {error}",
|
||||
"current_file_not_found": "Arquivo de armazenamento atual não encontrado",
|
||||
"current_backup_created": "Backup do arquivo de armazenamento atual criado",
|
||||
"storage_updated": "Arquivo de armazenamento atualizado com sucesso",
|
||||
"update_failed": "Falha ao atualizar arquivo de armazenamento: {error}",
|
||||
"sqlite_not_found": "Banco de dados SQLite não encontrado",
|
||||
"updating_sqlite": "Atualizando banco de dados SQLite",
|
||||
"updating_pair": "Atualizando par chave-valor",
|
||||
"sqlite_updated": "Banco de dados SQLite atualizado com sucesso",
|
||||
"sqlite_update_failed": "Falha ao atualizar banco de dados SQLite: {error}",
|
||||
"machine_id_backup_created": "Backup do arquivo machineId criado",
|
||||
"backup_creation_failed": "Falha ao criar backup: {error}",
|
||||
"machine_id_updated": "Arquivo machineId atualizado com sucesso",
|
||||
"machine_id_update_failed": "Falha ao atualizar arquivo machineId: {error}",
|
||||
"updating_system_ids": "Atualizando IDs do sistema",
|
||||
"system_ids_update_failed": "Falha ao atualizar IDs do sistema: {error}",
|
||||
"permission_denied": "Permissão negada. Tente executar como administrador",
|
||||
"windows_machine_guid_updated": "GUID da máquina Windows atualizado com sucesso",
|
||||
"update_windows_machine_guid_failed": "Falha ao atualizar GUID da máquina Windows: {error}",
|
||||
"windows_machine_id_updated": "ID da máquina Windows atualizado com sucesso",
|
||||
"update_windows_machine_id_failed": "Falha ao atualizar ID da máquina Windows: {error}",
|
||||
"sqm_client_key_not_found": "Chave de registro SQMClient não encontrada",
|
||||
"update_windows_system_ids_failed": "Falha ao atualizar IDs do sistema Windows: {error}",
|
||||
"macos_platform_uuid_updated": "UUID da plataforma macOS atualizado com sucesso",
|
||||
"failed_to_execute_plutil_command": "Falha ao executar comando plutil",
|
||||
"update_macos_system_ids_failed": "Falha ao atualizar IDs do sistema macOS: {error}",
|
||||
"ids_to_restore": "IDs da máquina para restaurar",
|
||||
"confirm": "Tem certeza que deseja restaurar esses IDs?",
|
||||
"success": "ID da máquina restaurado com sucesso",
|
||||
"process_error": "Erro no processo de restauração: {error}",
|
||||
"press_enter": "Pressione Enter para continuar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,22 @@
|
||||
"totally_reset": "Полностью сбросить Cursor",
|
||||
"outdate": "Устаревший",
|
||||
"temp_github_register": "Временная регистрация GitHub",
|
||||
"coming_soon": "Скоро"
|
||||
"coming_soon": "Скоро",
|
||||
"fixed_soon": "Скоро будет исправлено",
|
||||
"contribute": "Внести вклад в проект",
|
||||
"config": "Показать конфигурацию",
|
||||
"delete_google_account": "Удалить Google аккаунт Cursor",
|
||||
"continue_prompt": "Продолжить? (y/N): ",
|
||||
"operation_cancelled_by_user": "Операция отменена пользователем",
|
||||
"exiting": "Выход ......",
|
||||
"bypass_version_check": "Пропустить проверку версии Cursor",
|
||||
"check_user_authorized": "Проверить авторизацию пользователя",
|
||||
"select_chrome_profile": "Выбрать профиль Chrome",
|
||||
"bypass_token_limit": "Обход ограничения токенов",
|
||||
"restore_machine_id": "Восстановить ID устройства из резервной копии"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Арабский",
|
||||
"en": "Английский",
|
||||
"zh_cn": "Упрощенный китайский",
|
||||
"zh_tw": "Традиционный китайский",
|
||||
@@ -395,5 +408,48 @@
|
||||
"profile_selected": "Выбран профиль: {profile}",
|
||||
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
|
||||
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Восстановить ID устройства из резервной копии",
|
||||
"starting": "Запуск процесса восстановления ID устройства",
|
||||
"no_backups_found": "Резервные копии не найдены",
|
||||
"available_backups": "Доступные резервные копии",
|
||||
"select_backup": "Выберите резервную копию для восстановления",
|
||||
"to_cancel": "для отмены",
|
||||
"operation_cancelled": "Операция отменена",
|
||||
"invalid_selection": "Неверный выбор",
|
||||
"please_enter_number": "Пожалуйста, введите корректный номер",
|
||||
"missing_id": "Отсутствует ID: {id}",
|
||||
"read_backup_failed": "Не удалось прочитать файл резервной копии: {error}",
|
||||
"current_file_not_found": "Текущий файл хранилища не найден",
|
||||
"current_backup_created": "Создана резервная копия текущего файла хранилища",
|
||||
"storage_updated": "Файл хранилища успешно обновлен",
|
||||
"update_failed": "Не удалось обновить файл хранилища: {error}",
|
||||
"sqlite_not_found": "База данных SQLite не найдена",
|
||||
"updating_sqlite": "Обновление базы данных SQLite",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"sqlite_updated": "База данных SQLite успешно обновлена",
|
||||
"sqlite_update_failed": "Не удалось обновить базу данных SQLite: {error}",
|
||||
"machine_id_backup_created": "Создана резервная копия файла machineId",
|
||||
"backup_creation_failed": "Не удалось создать резервную копию: {error}",
|
||||
"machine_id_updated": "Файл machineId успешно обновлен",
|
||||
"machine_id_update_failed": "Не удалось обновить файл machineId: {error}",
|
||||
"updating_system_ids": "Обновление системных ID",
|
||||
"system_ids_update_failed": "Не удалось обновить системные ID: {error}",
|
||||
"permission_denied": "Доступ запрещен. Попробуйте запустить с правами администратора",
|
||||
"windows_machine_guid_updated": "GUID устройства Windows успешно обновлен",
|
||||
"update_windows_machine_guid_failed": "Не удалось обновить GUID устройства Windows: {error}",
|
||||
"windows_machine_id_updated": "ID устройства Windows успешно обновлен",
|
||||
"update_windows_machine_id_failed": "Не удалось обновить ID устройства Windows: {error}",
|
||||
"sqm_client_key_not_found": "Ключ реестра SQMClient не найден",
|
||||
"update_windows_system_ids_failed": "Не удалось обновить системные ID Windows: {error}",
|
||||
"macos_platform_uuid_updated": "UUID платформы macOS успешно обновлен",
|
||||
"failed_to_execute_plutil_command": "Не удалось выполнить команду plutil",
|
||||
"update_macos_system_ids_failed": "Не удалось обновить системные ID macOS: {error}",
|
||||
"ids_to_restore": "ID устройства для восстановления",
|
||||
"confirm": "Вы уверены, что хотите восстановить эти ID?",
|
||||
"success": "ID устройства успешно восстановлен",
|
||||
"process_error": "Ошибка процесса восстановления: {error}",
|
||||
"press_enter": "Нажмите Enter для продолжения"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
"register_manual": "Cursor'ı Özel E-posta ile Kaydet",
|
||||
"quit": "Cursor Uygulamasını Kapat",
|
||||
"select_language": "Dili Değiştir",
|
||||
"select_chrome_profile": "Chrome Profilini Seç",
|
||||
"input_choice": "Lütfen seçiminizi girin ({choices})",
|
||||
"invalid_choice": "Geçersiz seçim. Lütfen {choices} arasından bir sayı girin",
|
||||
"program_terminated": "Program kullanıcı tarafından sonlandırıldı",
|
||||
@@ -19,9 +20,23 @@
|
||||
"totally_reset": "Cursor'ı Tamamen Sıfırla",
|
||||
"outdate": "güncel değil",
|
||||
"temp_github_register": "Geçici GitHub Kaydı",
|
||||
"coming_soon": "Yakında"
|
||||
"admin_required": "Running as executable, administrator privileges required.",
|
||||
"admin_required_continue": "Continuing without administrator privileges.",
|
||||
"coming_soon": "Yakında",
|
||||
"fixed_soon": "Yakında Düzeltilecek",
|
||||
"contribute": "Projeye Katkıda Bulun",
|
||||
"config": "Yapılandırmayı Göster",
|
||||
"delete_google_account": "Cursor Google Hesabını Sil",
|
||||
"continue_prompt": "Devam et? (y/N): ",
|
||||
"operation_cancelled_by_user": "İşlem kullanıcı tarafından iptal edildi",
|
||||
"exiting": "Çıkılıyor ......",
|
||||
"bypass_version_check": "Cursor Sürüm Kontrolünü Atla",
|
||||
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et",
|
||||
"bypass_token_limit": "Token Limitini Atla",
|
||||
"restore_machine_id": "Makine Kimliğini Yedekten Geri Yükle"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Arapça",
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
@@ -398,5 +413,48 @@
|
||||
"profile_selected": "Seçilen profil: {profile}",
|
||||
"invalid_selection": "Geçersiz seçim. Lütfen tekrar deneyin",
|
||||
"warning_chrome_close": "Uyarı: Bu işlem tüm çalışan Chrome işlemlerini kapatacaktır"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Makine Kimliğini Yedekten Geri Yükle",
|
||||
"starting": "Makine kimliği geri yükleme işlemi başlatılıyor",
|
||||
"no_backups_found": "Yedek bulunamadı",
|
||||
"available_backups": "Mevcut yedekler",
|
||||
"select_backup": "Geri yüklemek için bir yedek seçin",
|
||||
"to_cancel": "iptal etmek için",
|
||||
"operation_cancelled": "İşlem iptal edildi",
|
||||
"invalid_selection": "Geçersiz seçim",
|
||||
"please_enter_number": "Lütfen geçerli bir numara girin",
|
||||
"missing_id": "Eksik kimlik: {id}",
|
||||
"read_backup_failed": "Yedek dosyası okunamadı: {error}",
|
||||
"current_file_not_found": "Mevcut depolama dosyası bulunamadı",
|
||||
"current_backup_created": "Mevcut depolama dosyasının yedeği oluşturuldu",
|
||||
"storage_updated": "Depolama dosyası başarıyla güncellendi",
|
||||
"update_failed": "Depolama dosyası güncellenemedi: {error}",
|
||||
"sqlite_not_found": "SQLite veritabanı bulunamadı",
|
||||
"updating_sqlite": "SQLite veritabanı güncelleniyor",
|
||||
"updating_pair": "Anahtar-değer çifti güncelleniyor",
|
||||
"sqlite_updated": "SQLite veritabanı başarıyla güncellendi",
|
||||
"sqlite_update_failed": "SQLite veritabanı güncellenemedi: {error}",
|
||||
"machine_id_backup_created": "MachineId dosyasının yedeği oluşturuldu",
|
||||
"backup_creation_failed": "Yedek oluşturulamadı: {error}",
|
||||
"machine_id_updated": "MachineId dosyası başarıyla güncellendi",
|
||||
"machine_id_update_failed": "MachineId dosyası güncellenemedi: {error}",
|
||||
"updating_system_ids": "Sistem kimlikleri güncelleniyor",
|
||||
"system_ids_update_failed": "Sistem kimlikleri güncellenemedi: {error}",
|
||||
"permission_denied": "İzin reddedildi. Yönetici olarak çalıştırmayı deneyin",
|
||||
"windows_machine_guid_updated": "Windows makine GUID'i başarıyla güncellendi",
|
||||
"update_windows_machine_guid_failed": "Windows makine GUID'i güncellenemedi: {error}",
|
||||
"windows_machine_id_updated": "Windows makine kimliği başarıyla güncellendi",
|
||||
"update_windows_machine_id_failed": "Windows makine kimliği güncellenemedi: {error}",
|
||||
"sqm_client_key_not_found": "SQMClient kayıt anahtarı bulunamadı",
|
||||
"update_windows_system_ids_failed": "Windows sistem kimlikleri güncellenemedi: {error}",
|
||||
"macos_platform_uuid_updated": "macOS platform UUID'si başarıyla güncellendi",
|
||||
"failed_to_execute_plutil_command": "plutil komutu yürütülemedi",
|
||||
"update_macos_system_ids_failed": "macOS sistem kimlikleri güncellenemedi: {error}",
|
||||
"ids_to_restore": "Geri yüklenecek makine kimlikleri",
|
||||
"confirm": "Bu kimlikleri geri yüklemek istediğinizden emin misiniz?",
|
||||
"success": "Makine kimliği başarıyla geri yüklendi",
|
||||
"process_error": "Geri yükleme işlemi hatası: {error}",
|
||||
"press_enter": "Devam etmek için Enter tuşuna basın"
|
||||
}
|
||||
}
|
||||
}
|
||||
577
locales/vi.json
577
locales/vi.json
@@ -20,13 +20,31 @@
|
||||
"totally_reset": "Đặt lại hoàn toàn Cursor",
|
||||
"outdate": "Quá cũ",
|
||||
"temp_github_register": "Đăng ký GitHub tạm thời",
|
||||
"coming_soon": "Sắp ra mắt"
|
||||
"admin_required": "Đang chạy dưới dạng tệp thực thi, yêu cầu quyền quản trị.",
|
||||
"admin_required_continue": "Tiếp tục mà không có quyền quản trị.",
|
||||
"coming_soon": "Sắp ra mắt",
|
||||
"fixed_soon": "Sẽ Sớm Được Sửa",
|
||||
"contribute": "Đóng Góp Cho Dự Án",
|
||||
"config": "Hiển Thị Cấu Hình",
|
||||
"delete_google_account": "Xóa Tài Khoản Google Cursor",
|
||||
"continue_prompt": "Tiếp tục? (y/N): ",
|
||||
"operation_cancelled_by_user": "Thao tác đã bị người dùng hủy",
|
||||
"exiting": "Đang thoát ……",
|
||||
"bypass_version_check": "Bỏ qua Kiểm tra Phiên bản Cursor",
|
||||
"check_user_authorized": "Kiểm tra Quyền Người dùng",
|
||||
"bypass_token_limit": "Bỏ qua giới hạn Token",
|
||||
"language_config_saved": "Đổi ngôn ngữ thành công",
|
||||
"lang_invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ ({lang_choices})",
|
||||
"restore_machine_id": "Khôi phục ID máy từ bản sao lưu"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Tiếng Ả Rập",
|
||||
"en": "Tiếng Anh",
|
||||
"zh_cn": "Tiếng Trung Giản Thể",
|
||||
"zh_tw": "Tiếng Trung Phồn Thể",
|
||||
"vi": "Tiếng Việt",
|
||||
"tr": "Tiếng Thổ Nhĩ Kỳ",
|
||||
"bg": "Tiếng Bulgaria",
|
||||
"nl": "Tiếng Hà Lan",
|
||||
"de": "Tiếng Đức",
|
||||
"fr": "Tiếng Pháp",
|
||||
@@ -99,7 +117,14 @@
|
||||
"package_not_found": "Không Tìm Thấy Package.json: {path}",
|
||||
"check_version_failed": "Kiểm Tra Phiên Bản Thất Bại: {error}",
|
||||
"stack_trace": "Dấu Vết Ngăn Xếp",
|
||||
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0"
|
||||
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0",
|
||||
"no_write_permission": "Không Có Quyền Ghi: {path}",
|
||||
"path_not_found": "Không Tìm Thấy Đường Dẫn: {path}",
|
||||
"modify_file_failed": "Sửa Đổi Tệp Thất Bại: {error}",
|
||||
"windows_machine_id_updated": "Cập Nhật ID Máy Windows Thành Công",
|
||||
"update_windows_machine_id_failed": "Cập Nhật ID Máy Windows Thất Bại: {error}",
|
||||
"update_windows_machine_guid_failed": "Cập Nhật GUID Máy Windows Thất Bại: {error}",
|
||||
"file_not_found": "Không Tìm Thấy Tệp: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Công Cụ Đăng Ký Cursor",
|
||||
@@ -176,10 +201,12 @@
|
||||
"password_submitted": "Đã Gửi Mật Khẩu",
|
||||
"total_usage": "Tổng Sử Dụng: {usage}",
|
||||
"setting_on_password": "Đang Đặt Mật Khẩu",
|
||||
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s"
|
||||
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s",
|
||||
"human_verify_error": "Không thể xác minh người dùng. Đang thử lại...",
|
||||
"max_retries_reached": "Đã đạt số lần thử tối đa. Đăng ký thất bại."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Trình Quản Lý Xác Thực Cursor",
|
||||
"title": "Quản Lý Xác Thực Cursor",
|
||||
"checking_auth": "Đang Kiểm Tra Tệp Xác Thực",
|
||||
"auth_not_found": "Không Tìm Thấy Tệp Xác Thực",
|
||||
"auth_file_error": "Lỗi Tệp Xác Thực: {error}",
|
||||
@@ -191,7 +218,7 @@
|
||||
"auth_file_create_failed": "Tạo Tệp Xác Thực Thất Bại: {error}",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"reset_machine_id": "Đặt Lại ID Máy",
|
||||
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
|
||||
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
|
||||
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
|
||||
"connected_to_database": "Đã Kết Nối Đến Cơ Sở Dữ Liệu",
|
||||
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
|
||||
@@ -213,7 +240,7 @@
|
||||
"navigation_error": "Lỗi Điều Hướng: {error}",
|
||||
"email_copy_error": "Lỗi Sao Chép Email: {error}",
|
||||
"mailbox_error": "Lỗi Hộp Thư: {error}",
|
||||
"token_saved_to_file": "Token Đã Lưu Vào cursor_tokens.txt",
|
||||
"token_saved_to_file": "Token Đã Được Lưu Vào cursor_tokens.txt",
|
||||
"navigate_to": "Đang Điều Hướng Đến {url}",
|
||||
"generate_email_success": "Tạo Email Thành Công",
|
||||
"select_email_domain": "Chọn Tên Miền Email",
|
||||
@@ -229,12 +256,12 @@
|
||||
"get_cursor_session_token_failed": "Lấy Token Phiên Cursor Thất Bại",
|
||||
"save_token_failed": "Lưu Token Thất Bại",
|
||||
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
|
||||
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
|
||||
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
|
||||
"no_valid_verification_code": "Không Có Mã Xác Minh Hợp Lệ"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Đang Khởi Động Trình Duyệt",
|
||||
"visiting_site": "Đang Truy Cập mail domains",
|
||||
"visiting_site": "Đang Truy Cập Tên Miền Mail",
|
||||
"create_success": "Tạo Email Thành Công",
|
||||
"create_failed": "Tạo Email Thất Bại",
|
||||
"create_error": "Lỗi Tạo Email: {error}",
|
||||
@@ -261,16 +288,22 @@
|
||||
"blocked_domains_loaded": "Đã Tải Tên Miền Bị Chặn: {count}",
|
||||
"blocked_domains_loaded_error": "Lỗi Tải Tên Miền Bị Chặn: {error}",
|
||||
"blocked_domains_loaded_success": "Tải Tên Miền Bị Chặn Thành Công",
|
||||
"blocked_domains_loaded_timeout": "Tải Tên Miền Bị Chặn Hết Thời Gian: {timeout}s",
|
||||
"blocked_domains_loaded_timeout": "Hết Thời Gian Tải Tên Miền Bị Chặn: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Lỗi Hết Thời Gian Tải Tên Miền Bị Chặn: {error}",
|
||||
"available_domains_loaded": "Đã Tải Tên Miền Khả Dụng: {count}",
|
||||
"domains_filtered": "Tên Miền Đã Lọc: {count}",
|
||||
"trying_to_create_email": "Đang cố gắng tạo email: {email}",
|
||||
"domain_blocked": "Tên Miền Bị Chặn: {domain}"
|
||||
"domains_filtered": "Đã Lọc Tên Miền: {count}",
|
||||
"trying_to_create_email": "Đang Thử Tạo Email: {email}",
|
||||
"domain_blocked": "Tên Miền Bị Chặn: {domain}",
|
||||
"using_chrome_profile": "Đang Sử Dụng Hồ Sơ Chrome từ: {user_data_dir}",
|
||||
"no_display_found": "Không Tìm Thấy Màn Hình. Đảm Bảo X Server Đang Chạy.",
|
||||
"try_export_display": "Thử: export DISPLAY=:0",
|
||||
"extension_load_error": "Lỗi Tải Tiện Ích Mở Rộng: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "Đảm Bảo Chrome/Chromium Được Cài Đặt Đúng Cách",
|
||||
"try_install_chromium": "Thử: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "Tắt Tự Động Cập Nhật Cursor",
|
||||
"disable_success": "Tắt Tự Động Cập Nhật Thành Công",
|
||||
"disable_success": "Đã Tắt Tự Động Cập Nhật Thành Công",
|
||||
"disable_failed": "Tắt Tự Động Cập Nhật Thất Bại: {error}",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"start_disable": "Bắt Đầu Tắt Tự Động Cập Nhật",
|
||||
@@ -279,122 +312,468 @@
|
||||
"removing_directory": "Đang Xóa Thư Mục",
|
||||
"directory_removed": "Đã Xóa Thư Mục",
|
||||
"creating_block_file": "Đang Tạo Tệp Chặn",
|
||||
"block_file_created": "Đã Tạo Tệp Chặn"
|
||||
"block_file_created": "Đã Tạo Tệp Chặn",
|
||||
"clearing_update_yml": "Đang Xóa Tệp update.yml",
|
||||
"update_yml_cleared": "Đã Xóa Tệp update.yml",
|
||||
"update_yml_not_found": "Không Tìm Thấy Tệp update.yml",
|
||||
"clear_update_yml_failed": "Xóa Tệp update.yml Thất Bại: {error}",
|
||||
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {system}",
|
||||
"remove_directory_failed": "Xóa Thư Mục Thất Bại: {error}",
|
||||
"create_block_file_failed": "Tạo Tệp Chặn Thất Bại: {error}",
|
||||
"directory_locked": "Thư Mục Bị Khóa: {path}",
|
||||
"yml_locked": "Tệp update.yml Bị Khóa",
|
||||
"block_file_locked": "Tệp Chặn Bị Khóa",
|
||||
"yml_already_locked": "Tệp update.yml Đã Bị Khóa",
|
||||
"block_file_already_locked": "Tệp Chặn Đã Bị Khóa",
|
||||
"block_file_locked_error": "Lỗi Khóa Tệp Chặn: {error}",
|
||||
"yml_locked_error": "Lỗi Khóa Tệp update.yml: {error}",
|
||||
"block_file_already_locked_error": "Lỗi Tệp Chặn Đã Bị Khóa: {error}",
|
||||
"yml_already_locked_error": "Lỗi Tệp update.yml Đã Bị Khóa: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Đang Kiểm Tra Cập Nhật...",
|
||||
"new_version_available": "Có Phiên Bản Mới! (Hiện Tại: {current}, Mới Nhất: {latest})",
|
||||
"updating": "Đang Cập Nhật Lên Phiên Bản Mới Nhất. Chương Trình Sẽ Tự Động Khởi Động Lại.",
|
||||
"up_to_date": "Bạn Đang Sử Dụ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 sẽ tự động khởi động lại.",
|
||||
"up_to_date": "Bạn đang sử dụ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, bỏ qua 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ơ sở dữ liệu đã đóng",
|
||||
"database_updated_successfully": "Cập nhật cơ sở dữ liệu thành công",
|
||||
"connected_to_database": "Đã kết nối với cơ sở dữ liệu",
|
||||
"updating_pair": "Đang cập nhật cặp khóa-giá trị",
|
||||
"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, Bỏ Qua 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ơ 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ị",
|
||||
"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, và 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 dướ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, và 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 dướ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 dướ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 gì.",
|
||||
"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 vẫn 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 tại 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 vấn đề 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 tới Github Issue Tracker tại https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Quá trình bị người dùng ngắt. Đang thoát...",
|
||||
"return_to_main_menu": "Đang trở về menu chính...",
|
||||
"process_interrupted": "Quá trình bị ngắt. Đang thoát...",
|
||||
"press_enter_to_return_to_main_menu": "Nhấn Enter để trở về menu chính...",
|
||||
"removing_known": "Đang xóa các tệp thử nghiệm/giấy phép đã biết",
|
||||
"performing_deep_scan": "Đang quét sâu để tìm thêm tệp thử nghiệm/giấy phép",
|
||||
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp thử nghiệm/giấy phép tiềm năng bổ sung",
|
||||
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp Electron localStorage",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy thêm tệp thử nghiệm/giấy phép nào trong quá trình quét sâu",
|
||||
"removing_electron_localstorage_files": "Đang xóa các tệp Electron localStorage",
|
||||
"electron_localstorage_files_removed": "Đã xóa các tệp Electron localStorage",
|
||||
"electron_localstorage_files_removal_error": "Lỗi khi xóa các tệp Electron localStorage: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Đã hoàn tất việc xóa các tệp Electron localStorage"
|
||||
"removing_known": "Đang xóa các tệp dùng thử/giấy phép đã biết",
|
||||
"performing_deep_scan": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
|
||||
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp giấy phép/dùng thử tiềm năng bổ sung",
|
||||
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp localStorage của Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy tệp giấy phép/dùng thử bổ sung trong quét sâu",
|
||||
"removing_electron_localstorage_files": "Đang xóa các tệp localStorage của Electron",
|
||||
"electron_localstorage_files_removed": "Đã xóa các tệp localStorage của Electron",
|
||||
"electron_localstorage_files_removal_error": "Lỗi xóa các tệp localStorage của Electron: {error}",
|
||||
"electron_localstorage_files_removal_completed": "Hoàn tất xóa các tệp localStorage của Electron",
|
||||
"warning_title": "CẢNH BÁO",
|
||||
"warning_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
|
||||
"warning_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
|
||||
"warning_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
|
||||
"warning_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
|
||||
"warning_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
|
||||
"warning_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
|
||||
"warning_7": "Sử dụng với rủi ro của riêng bạn",
|
||||
"removed": "Đã Xóa: {path}",
|
||||
"failed_to_reset_machine_guid": "Không thể đặt lại GUID máy",
|
||||
"failed_to_remove": "Không thể xóa: {path}",
|
||||
"failed_to_delete_file": "Không thể xóa tệp: {path}",
|
||||
"failed_to_delete_directory": "Không thể xóa thư mục: {path}",
|
||||
"failed_to_delete_file_or_directory": "Không thể xóa tệp hoặc thư mục: {path}",
|
||||
"deep_scanning": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
|
||||
"resetting_cursor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
|
||||
"completed_in": "Hoàn thành trong {time} giây",
|
||||
"cursor_reset_completed": "Trình soạn thảo Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
|
||||
"cursor_reset_failed": "Đặt lại Trình soạn thảo Cursor AI thất bại: {error}",
|
||||
"cursor_reset_cancelled": "Đã hủy đặt lại Trình soạn thảo Cursor AI. Thoát mà không thay đổi gì.",
|
||||
"operation_cancelled": "Đã hủy thao tác. Thoát mà không thay đổi gì.",
|
||||
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
|
||||
"already_on_settings": "Đã ở trang cài đặt",
|
||||
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
|
||||
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
|
||||
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
|
||||
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
|
||||
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
|
||||
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
|
||||
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
|
||||
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
|
||||
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
|
||||
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
|
||||
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
|
||||
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "Tự Động Hóa Đăng Ký GitHub + Cursor AI",
|
||||
"features_header": "Tính Năng",
|
||||
"feature1": "Tạo email tạm thời sử dụng 1secmail.",
|
||||
"feature2": "Đăng ký tài khoản GitHub mới với thông tin ngẫu nhiên.",
|
||||
"feature3": "Tự động xác minh email GitHub.",
|
||||
"feature4": "Đăng nhập vào Cursor AI sử dụng xác thực GitHub.",
|
||||
"feature5": "Đặt lại ID máy để bỏ qua phát hiện dùng thử.",
|
||||
"feature6": "Lưu tất cả thông tin đăng nhập vào tệp.",
|
||||
"warnings_header": "Cảnh Báo",
|
||||
"warning1": "Script này tự động hóa việc tạo tài khoản, có thể vi phạm điều khoản dịch vụ của GitHub/Cursor.",
|
||||
"warning2": "Yêu cầu truy cập internet và quyền quản trị.",
|
||||
"warning3": "CAPTCHA hoặc xác minh bổ sung có thể làm gián đoạn tự động hóa.",
|
||||
"warning4": "Sử dụng có trách nhiệm và tự chịu rủi ro.",
|
||||
"confirm": "Bạn có chắc chắn muốn tiếp tục không?",
|
||||
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'yes' hoặc 'no'",
|
||||
"cancelled": "Đã hủy thao tác",
|
||||
"program_terminated": "Chương trình bị người dùng chấm dứt",
|
||||
"starting_automation": "Bắt đầu tự động hóa...",
|
||||
"github_username": "Tên Người Dùng GitHub",
|
||||
"github_password": "Mật Khẩu GitHub",
|
||||
"email_address": "Địa Chỉ Email",
|
||||
"credentials_saved": "Các thông tin đăng nhập này đã được lưu vào github_cursor_accounts.txt",
|
||||
"completed_successfully": "Đăng ký GitHub + Cursor hoàn tất thành công!",
|
||||
"registration_encountered_issues": "Đăng ký GitHub + Cursor gặp vấn đề.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Kiểm tra cửa sổ trình duyệt để can thiệp thủ công hoặc thử lại sau."
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "Gói Đăng Ký",
|
||||
"trial_remaining": "Thời Gian Dùng Thử Pro Còn Lại",
|
||||
"days": "ngày",
|
||||
"subscription_not_found": "Không tìm thấy thông tin đăng ký",
|
||||
"email_not_found": "Không tìm thấy email",
|
||||
"failed_to_get_account": "Không thể lấy thông tin tài khoản",
|
||||
"config_not_found": "Không tìm thấy cấu hình.",
|
||||
"failed_to_get_usage": "Không thể lấy thông tin sử dụng",
|
||||
"failed_to_get_subscription": "Không thể lấy thông tin đăng ký",
|
||||
"failed_to_get_email": "Không thể lấy địa chỉ email",
|
||||
"failed_to_get_token": "Không thể lấy token",
|
||||
"failed_to_get_account_info": "Không thể lấy thông tin tài khoản",
|
||||
"title": "Thông Tin Tài Khoản",
|
||||
"email": "Email",
|
||||
"token": "Token",
|
||||
"usage": "Sử Dụng",
|
||||
"subscription_type": "Loại Đăng Ký",
|
||||
"remaining_trial": "Thời Gian Dùng Thử Còn Lại",
|
||||
"days_remaining": "Số Ngày Còn Lại",
|
||||
"premium": "Premium",
|
||||
"pro": "Pro",
|
||||
"pro_trial": "Dùng Thử Pro",
|
||||
"team": "Team",
|
||||
"enterprise": "Enterprise",
|
||||
"free": "Miễn Phí",
|
||||
"active": "Đang Hoạt Động",
|
||||
"inactive": "Không Hoạt Động",
|
||||
"premium_usage": "Sử Dụng Premium",
|
||||
"basic_usage": "Sử Dụng Cơ Bản",
|
||||
"usage_not_found": "Không tìm thấy thông tin sử dụng",
|
||||
"lifetime_access_enabled": "Đã Bật Truy Cập Trọn Đời",
|
||||
"token_not_found": "Không tìm thấy token"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "Không có sẵn cấu hình",
|
||||
"configuration": "Cấu Hình",
|
||||
"enabled": "Đã Bật",
|
||||
"disabled": "Đã Tắt",
|
||||
"config_directory": "Thư Mục Cấu Hình",
|
||||
"neither_cursor_nor_cursor_directory_found": "Không tìm thấy Cursor hoặc thư mục Cursor trong {config_base}",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Vui lòng đảm bảo Cursor đã được cài đặt và đã chạy ít nhất một lần",
|
||||
"storage_directory_not_found": "Không tìm thấy thư mục lưu trữ: {storage_dir}",
|
||||
"storage_file_found": "Đã tìm thấy tệp lưu trữ: {storage_path}",
|
||||
"file_size": "Kích thước tệp: {size} bytes",
|
||||
"file_permissions": "Quyền tệp: {permissions}",
|
||||
"file_owner": "Chủ sở hữu tệp: {owner}",
|
||||
"file_group": "Nhóm tệp: {group}",
|
||||
"error_getting_file_stats": "Lỗi lấy thông tin tệp: {error}",
|
||||
"permission_denied": "Từ chối quyền: {storage_path}",
|
||||
"try_running": "Thử chạy: {command}",
|
||||
"and": "Và",
|
||||
"storage_file_is_empty": "Tệp lưu trữ trống: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "Tệp có thể bị hỏng, vui lòng cài đặt lại Cursor",
|
||||
"storage_file_not_found": "Không tìm thấy tệp lưu trữ: {storage_path}",
|
||||
"error_checking_linux_paths": "Lỗi kiểm tra đường dẫn Linux: {error}",
|
||||
"config_option_added": "Đã thêm tùy chọn cấu hình: {option}",
|
||||
"config_updated": "Đã cập nhật cấu hình",
|
||||
"config_created": "Đã tạo cấu hình: {config_file}",
|
||||
"config_setup_error": "Lỗi thiết lập cấu hình: {error}",
|
||||
"storage_file_is_valid_and_contains_data": "Tệp lưu trữ hợp lệ và chứa dữ liệu",
|
||||
"error_reading_storage_file": "Lỗi đọc tệp lưu trữ: {error}",
|
||||
"also_checked": "Cũng đã kiểm tra {path}",
|
||||
"backup_created": "Đã tạo bản sao lưu: {path}",
|
||||
"config_removed": "Đã xóa tệp cấu hình để cập nhật bắt buộc",
|
||||
"backup_failed": "Sao lưu cấu hình thất bại: {error}",
|
||||
"force_update_failed": "Cập nhật bắt buộc cấu hình thất bại: {error}",
|
||||
"config_force_update_disabled": "Đã tắt cập nhật bắt buộc tệp cấu hình, bỏ qua cập nhật bắt buộc",
|
||||
"config_force_update_enabled": "Đã bật cập nhật bắt buộc tệp cấu hình, thực hiện cập nhật bắt buộc"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Không tìm thấy nút xác thực",
|
||||
"authentication_failed": "Xác thực thất bại: {error}",
|
||||
"found_cookies": "Đã tìm thấy {count} cookie",
|
||||
"token_extraction_error": "Lỗi trích xuất token: {error}",
|
||||
"authentication_successful": "Xác thực thành công - Email: {email}",
|
||||
"missing_authentication_data": "Thiếu dữ liệu xác thực: {data}",
|
||||
"failed_to_delete_account": "Không thể xóa tài khoản: {error}",
|
||||
"invalid_authentication_type": "Loại xác thực không hợp lệ",
|
||||
"auth_update_success": "Cập nhật xác thực thành công",
|
||||
"browser_closed": "Đã đóng trình duyệt",
|
||||
"auth_update_failed": "Cập nhật xác thực thất bại",
|
||||
"google_start": "Bắt đầu Google",
|
||||
"github_start": "Bắt đầu Github",
|
||||
"usage_count": "Số lần sử dụng: {usage}",
|
||||
"account_has_reached_maximum_usage": "Tài khoản đã đạt số lần sử dụng tối đa, {deleting}",
|
||||
"starting_new_authentication_process": "Bắt đầu quá trình xác thực mới...",
|
||||
"failed_to_delete_expired_account": "Không thể xóa tài khoản hết hạn",
|
||||
"could_not_check_usage_count": "Không thể kiểm tra số lần sử dụng: {error}",
|
||||
"found_email": "Đã tìm thấy email: {email}",
|
||||
"could_not_find_email": "Không thể tìm thấy email: {error}",
|
||||
"could_not_find_usage_count": "Không thể tìm thấy số lần sử dụng: {error}",
|
||||
"already_on_settings_page": "Đã ở trang cài đặt!",
|
||||
"failed_to_extract_auth_info": "Không thể trích xuất thông tin xác thực: {error}",
|
||||
"no_chrome_profiles_found": "Không tìm thấy hồ sơ Chrome, sử dụng Mặc định",
|
||||
"found_default_chrome_profile": "Đã tìm thấy hồ sơ Chrome Mặc định",
|
||||
"using_first_available_chrome_profile": "Sử dụng hồ sơ Chrome khả dụng đầu tiên: {profile}",
|
||||
"error_finding_chrome_profile": "Lỗi tìm hồ sơ Chrome, sử dụng Mặc định: {error}",
|
||||
"initializing_browser_setup": "Đang khởi tạo thiết lập trình duyệt...",
|
||||
"detected_platform": "Đã phát hiện nền tảng: {platform}",
|
||||
"running_as_root_warning": "Chạy với quyền root không được khuyến nghị cho tự động hóa trình duyệt",
|
||||
"consider_running_without_sudo": "Hãy xem xét chạy script mà không cần sudo",
|
||||
"no_compatible_browser_found": "Không tìm thấy trình duyệt tương thích. Vui lòng cài đặt Google Chrome hoặc Chromium.",
|
||||
"supported_browsers": "Trình duyệt được hỗ trợ cho {platform}",
|
||||
"using_browser_profile": "Đang sử dụng hồ sơ trình duyệt: {profile}",
|
||||
"starting_browser": "Đang khởi động trình duyệt tại: {path}",
|
||||
"browser_setup_completed": "Thiết lập trình duyệt hoàn tất thành công",
|
||||
"browser_setup_failed": "Thiết lập trình duyệt thất bại: {error}",
|
||||
"try_running_without_sudo_admin": "Thử chạy mà không cần quyền sudo/quản trị",
|
||||
"redirecting_to_authenticator_cursor_sh": "Đang chuyển hướng đến authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Bắt đầu xác thực Google...",
|
||||
"starting_github_authentication": "Bắt đầu xác thực GitHub...",
|
||||
"waiting_for_authentication": "Đang chờ xác thực...",
|
||||
"page_changed_checking_auth": "Trang đã thay đổi, đang kiểm tra xác thực...",
|
||||
"status_check_error": "Lỗi kiểm tra trạng thái: {error}",
|
||||
"authentication_timeout": "Hết thời gian xác thực",
|
||||
"account_is_still_valid": "Tài khoản vẫn còn hợp lệ (Sử dụng: {usage})",
|
||||
"starting_re_authentication_process": "Bắt đầu quá trình xác thực lại...",
|
||||
"starting_new_google_authentication": "Bắt đầu xác thực Google mới...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Không thể xóa tài khoản hoặc xác thực lại: {error}",
|
||||
"navigating_to_authentication_page": "Đang điều hướng đến trang xác thực...",
|
||||
"please_select_your_google_account_to_continue": "Vui lòng chọn tài khoản Google của bạn để tiếp tục...",
|
||||
"found_browser_data_directory": "Đã tìm thấy thư mục dữ liệu trình duyệt: {path}",
|
||||
"authentication_successful_getting_account_info": "Xác thực thành công, đang lấy thông tin tài khoản...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Cảnh báo: Không thể kết thúc các tiến trình trình duyệt hiện có: {error}",
|
||||
"browser_failed_to_start": "Trình duyệt không thể khởi động: {error}",
|
||||
"browser_failed": "Trình duyệt không thể khởi động: {error}",
|
||||
"browser_failed_to_start_fallback": "Trình duyệt không thể khởi động: {error}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chọn Hồ Sơ Chrome",
|
||||
"select_profile": "Chọn hồ sơ Chrome để sử dụng:",
|
||||
"profile_list": "Các hồ sơ có sẵn:",
|
||||
"select_profile": "Chọn một hồ sơ Chrome để sử dụng:",
|
||||
"profile_list": "Hồ sơ khả dụng:",
|
||||
"default_profile": "Hồ Sơ Mặc Định",
|
||||
"profile": "Hồ Sơ {number}",
|
||||
"no_profiles": "Không tìm thấy hồ sơ Chrome",
|
||||
"error_loading": "Lỗi khi tải hồ sơ Chrome: {error}",
|
||||
"error_loading": "Lỗi tải hồ sơ Chrome: {error}",
|
||||
"profile_selected": "Đã chọn hồ sơ: {profile}",
|
||||
"invalid_selection": "Lựa chọn không hợp lệ. Vui lòng thử lại",
|
||||
"warning_chrome_close": "Cảnh báo: Điều này sẽ đóng tất cả các tiến trình Chrome đang chạy"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Công Cụ Xóa Tài Khoản Google Cursor",
|
||||
"warning": "CẢNH BÁO: Điều này sẽ xóa vĩnh viễn tài khoản Cursor của bạn. Hành động này không thể hoàn tác.",
|
||||
"cancelled": "Đã hủy xóa tài khoản.",
|
||||
"starting_process": "Bắt đầu quá trình xóa tài khoản...",
|
||||
"google_button_not_found": "Không tìm thấy nút đăng nhập Google",
|
||||
"logging_in": "Đang đăng nhập bằng Google...",
|
||||
"waiting_for_auth": "Đang chờ xác thực Google...",
|
||||
"login_successful": "Đăng nhập thành công",
|
||||
"unexpected_page": "Trang không mong đợi sau khi đăng nhập: {url}",
|
||||
"trying_settings": "Đang thử điều hướng đến trang cài đặt...",
|
||||
"select_google_account": "Vui lòng chọn tài khoản Google của bạn...",
|
||||
"auth_timeout": "Hết thời gian xác thực, vẫn tiếp tục...",
|
||||
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
|
||||
"already_on_settings": "Đã ở trang cài đặt",
|
||||
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
|
||||
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
|
||||
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
|
||||
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
|
||||
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
|
||||
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
|
||||
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
|
||||
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
|
||||
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
|
||||
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
|
||||
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
|
||||
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục",
|
||||
"typed_delete": "Đã nhập \"Delete\" vào ô xác nhận",
|
||||
"confirm_button_not_found": "Không tìm thấy nút Xác nhận sau nhiều lần thử",
|
||||
"confirm_button_retry": "Không tìm thấy nút Xác nhận, lần thử {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "Lỗi tìm nút Xác nhận: {error}",
|
||||
"account_deleted": "Đã xóa tài khoản thành công!",
|
||||
"error": "Lỗi trong quá trình xóa tài khoản: {error}",
|
||||
"success": "Tài khoản Cursor của bạn đã được xóa thành công!",
|
||||
"failed": "Quá trình xóa tài khoản thất bại hoặc đã bị hủy.",
|
||||
"interrupted": "Quá trình xóa tài khoản bị người dùng ngắt.",
|
||||
"unexpected_error": "Lỗi không mong đợi: {error}",
|
||||
"found_email": "Đã tìm thấy email: {email}",
|
||||
"email_not_found": "Không tìm thấy email: {error}",
|
||||
"confirm_prompt": "Bạn có chắc chắn muốn tiếp tục không? (y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "Bắt đầu bỏ qua phiên bản Cursor...",
|
||||
"found_product_json": "Đã tìm thấy product.json: {path}",
|
||||
"no_write_permission": "Không có quyền ghi cho tệp: {path}",
|
||||
"read_failed": "Không thể đọc product.json: {error}",
|
||||
"current_version": "Phiên bản hiện tại: {version}",
|
||||
"backup_created": "Đã tạo bản sao lưu: {path}",
|
||||
"version_updated": "Đã cập nhật phiên bản từ {old} lên {new}",
|
||||
"write_failed": "Không thể ghi product.json: {error}",
|
||||
"no_update_needed": "Không cần cập nhật. Phiên bản hiện tại {version} đã >= 0.46.0",
|
||||
"bypass_failed": "Bỏ qua phiên bản thất bại: {error}",
|
||||
"stack_trace": "Dấu vết ngăn xếp",
|
||||
"localappdata_not_found": "Không tìm thấy biến môi trường LOCALAPPDATA",
|
||||
"product_json_not_found": "Không tìm thấy product.json trong các đường dẫn Linux thông thường",
|
||||
"unsupported_os": "Hệ điều hành không được hỗ trợ: {system}",
|
||||
"file_not_found": "Không tìm thấy tệp: {path}",
|
||||
"title": "Công Cụ Bỏ Qua Phiên Bản Cursor",
|
||||
"description": "Công cụ này sửa đổi product.json của Cursor để bỏ qua hạn chế phiên bản",
|
||||
"menu_option": "Bỏ Qua Kiểm Tra Phiên Bản Cursor"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "Đang kiểm tra quyền...",
|
||||
"token_source": "Lấy token từ cơ sở dữ liệu hay nhập thủ công? (d/m, mặc định: d)",
|
||||
"getting_token_from_db": "Đang lấy token từ cơ sở dữ liệu...",
|
||||
"token_found_in_db": "Đã tìm thấy token trong cơ sở dữ liệu",
|
||||
"token_not_found_in_db": "Không tìm thấy token trong cơ sở dữ liệu",
|
||||
"cursor_acc_info_not_found": "Không tìm thấy cursor_acc_info.py",
|
||||
"error_getting_token_from_db": "Lỗi lấy token từ cơ sở dữ liệu: {error}",
|
||||
"enter_token": "Nhập token Cursor của bạn: ",
|
||||
"token_length": "Độ dài token: {length} ký tự",
|
||||
"usage_response_status": "Trạng thái phản hồi sử dụng: {response}",
|
||||
"unexpected_status_code": "Mã trạng thái không mong đợi: {code}",
|
||||
"jwt_token_warning": "Token có vẻ ở định dạng JWT, nhưng kiểm tra API trả về mã trạng thái không mong đợi. Token có thể hợp lệ nhưng truy cập API bị hạn chế.",
|
||||
"invalid_token": "Token không hợp lệ",
|
||||
"user_authorized": "Người dùng được ủy quyền",
|
||||
"user_unauthorized": "Người dùng không được ủy quyền",
|
||||
"request_timeout": "Yêu cầu hết thời gian",
|
||||
"connection_error": "Lỗi kết nối",
|
||||
"check_error": "Lỗi kiểm tra quyền: {error}",
|
||||
"authorization_successful": "Ủy quyền thành công!",
|
||||
"authorization_failed": "Ủy quyền thất bại!",
|
||||
"operation_cancelled": "Thao tác bị người dùng hủy",
|
||||
"unexpected_error": "Lỗi không mong đợi: {error}",
|
||||
"error_generating_checksum": "Lỗi tạo checksum: {error}",
|
||||
"checking_usage_information": "Đang kiểm tra thông tin sử dụng...",
|
||||
"check_usage_response": "Phản hồi kiểm tra sử dụng: {response}",
|
||||
"usage_response": "Phản hồi sử dụng: {response}"
|
||||
},
|
||||
"restore": {
|
||||
"title": "Khôi phục ID máy từ bản sao lưu",
|
||||
"starting": "Bắt đầu quá trình khôi phục ID máy",
|
||||
"no_backups_found": "Không tìm thấy bản sao lưu nào",
|
||||
"available_backups": "Các bản sao lưu có sẵn",
|
||||
"select_backup": "Chọn bản sao lưu để khôi phục",
|
||||
"to_cancel": "để hủy",
|
||||
"operation_cancelled": "Đã hủy thao tác",
|
||||
"invalid_selection": "Lựa chọn không hợp lệ",
|
||||
"please_enter_number": "Vui lòng nhập một số hợp lệ",
|
||||
"missing_id": "Thiếu ID: {id}",
|
||||
"read_backup_failed": "Không thể đọc tệp sao lưu: {error}",
|
||||
"current_file_not_found": "Không tìm thấy tệp lưu trữ hiện tại",
|
||||
"current_backup_created": "Đã tạo bản sao lưu của tệp lưu trữ hiện tại",
|
||||
"storage_updated": "Tệp lưu trữ đã được cập nhật thành công",
|
||||
"update_failed": "Không thể cập nhật tệp lưu trữ: {error}",
|
||||
"sqlite_not_found": "Không tìm thấy cơ sở dữ liệu SQLite",
|
||||
"updating_sqlite": "Đang cập nhật cơ sở dữ liệu SQLite",
|
||||
"updating_pair": "Đang cập nhật cặp khóa-giá trị",
|
||||
"sqlite_updated": "Cơ sở dữ liệu SQLite đã được cập nhật thành công",
|
||||
"sqlite_update_failed": "Không thể cập nhật cơ sở dữ liệu SQLite: {error}",
|
||||
"machine_id_backup_created": "Đã tạo bản sao lưu của tệp machineId",
|
||||
"backup_creation_failed": "Không thể tạo bản sao lưu: {error}",
|
||||
"machine_id_updated": "Tệp machineId đã được cập nhật thành công",
|
||||
"machine_id_update_failed": "Không thể cập nhật tệp machineId: {error}",
|
||||
"updating_system_ids": "Đang cập nhật ID hệ thống",
|
||||
"system_ids_update_failed": "Không thể cập nhật ID hệ thống: {error}",
|
||||
"permission_denied": "Quyền truy cập bị từ chối. Vui lòng thử chạy với quyền quản trị",
|
||||
"windows_machine_guid_updated": "GUID máy Windows đã được cập nhật thành công",
|
||||
"update_windows_machine_guid_failed": "Không thể cập nhật GUID máy Windows: {error}",
|
||||
"windows_machine_id_updated": "ID máy Windows đã được cập nhật thành công",
|
||||
"update_windows_machine_id_failed": "Không thể cập nhật ID máy Windows: {error}",
|
||||
"sqm_client_key_not_found": "Không tìm thấy khóa đăng ký SQMClient",
|
||||
"update_windows_system_ids_failed": "Không thể cập nhật ID hệ thống Windows: {error}",
|
||||
"macos_platform_uuid_updated": "UUID nền tảng macOS đã được cập nhật thành công",
|
||||
"failed_to_execute_plutil_command": "Không thể thực thi lệnh plutil",
|
||||
"update_macos_system_ids_failed": "Không thể cập nhật ID hệ thống macOS: {error}",
|
||||
"ids_to_restore": "ID máy cần khôi phục",
|
||||
"confirm": "Bạn có chắc chắn muốn khôi phục những ID này không?",
|
||||
"success": "ID máy đã được khôi phục thành công",
|
||||
"process_error": "Lỗi quá trình khôi phục: {error}",
|
||||
"press_enter": "Nhấn Enter để tiếp tục"
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
"exit": "退出程序",
|
||||
"reset": "重置机器ID",
|
||||
"register": "注册新的Cursor账户",
|
||||
"register_google": "使用Google账户注册",
|
||||
"register_github": "使用GitHub账户注册",
|
||||
"register_google": "使用自己的Google账户注册",
|
||||
"register_github": "使用自己的GitHub账户注册",
|
||||
"register_manual": "使用自定义邮箱注册Cursor",
|
||||
"quit": "关闭Cursor应用",
|
||||
"select_language": "更改语言",
|
||||
@@ -30,9 +30,15 @@
|
||||
"continue_prompt": "继续?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被用户取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "绕过 Cursor 版本检查"
|
||||
"bypass_version_check": "绕过 Cursor 版本检查",
|
||||
"check_user_authorized": "检查用户授权",
|
||||
"bypass_token_limit": "绕过 Token 限制",
|
||||
"language_config_saved": "语言配置保存成功",
|
||||
"lang_invalid_choice": "选择无效。请输入以下选项之一:({lang_choices})",
|
||||
"restore_machine_id": "从备份恢复机器ID"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "阿拉伯语",
|
||||
"en": "英语",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁体中文",
|
||||
@@ -117,7 +123,8 @@
|
||||
"modify_file_failed": "修改文件失败: {error}",
|
||||
"windows_machine_id_updated": "Windows机器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}"
|
||||
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}",
|
||||
"file_not_found": "文件未找到: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor 注册工具",
|
||||
@@ -194,7 +201,17 @@
|
||||
"password_submitted": "密码已提交",
|
||||
"total_usage": "总使用量: {usage}",
|
||||
"setting_on_password": "设置密码",
|
||||
"getting_code": "获取验证码,将在60秒内尝试..."
|
||||
"getting_code": "获取验证码,将在60秒内尝试...",
|
||||
"browser_path_invalid": "{browser} 路径无效,使用默认路径",
|
||||
"using_browser": "正在使用 {browser} 浏览器: {path}",
|
||||
"using_browser_profile": "使用 {browser} 配置文件: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "确保 {browser} 已正确安装",
|
||||
"try_install_browser": "尝试使用包管理器安装浏览器",
|
||||
"tracking_processes": "正在跟踪 {count} 个 {browser} 进程",
|
||||
"no_new_processes_detected": "未检测到新的 {browser} 进程",
|
||||
"could_not_track_processes": "无法跟踪 {browser} 进程: {error}",
|
||||
"human_verify_error": "无法验证用户是人类,正在重试...",
|
||||
"max_retries_reached": "已达到最大重试次数,注册失败。"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor 认证管理器",
|
||||
@@ -533,7 +550,16 @@
|
||||
"config_setup_error": "配置设置错误",
|
||||
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
|
||||
"error_reading_storage_file": "读取存储文件时出错",
|
||||
"also_checked": "也检查了 {path}"
|
||||
"also_checked": "也检查了 {path}",
|
||||
"backup_created": "备份创建: {path}",
|
||||
"config_removed": "配置文件已删除用于强制更新",
|
||||
"backup_failed": "备份失败: {error}",
|
||||
"force_update_failed": "强制更新配置失败: {error}",
|
||||
"config_force_update_disabled": "配置文件强制更新已禁用,跳过强制更新",
|
||||
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新",
|
||||
"documents_path_not_found": "找不到文档路径,使用当前目录",
|
||||
"config_dir_created": "已创建配置目录: {path}",
|
||||
"using_temp_dir": "由于错误使用临时目录: {path} (错误: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到认证按钮",
|
||||
@@ -592,19 +618,29 @@
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}",
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}"
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
|
||||
"user_data_dir_not_found": "{browser} 用户数据目录未找到:{path},将尝试使用 Chrome",
|
||||
"error_getting_user_data_directory": "获取用户数据目录出错:{error}",
|
||||
"warning_browser_close": "警告:这将关闭所有正在运行的 {browser} 进程",
|
||||
"killing_browser_processes": "正在关闭 {browser} 进程...",
|
||||
"profile_selection_error": "配置文件选择过程中出错: {error}",
|
||||
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
|
||||
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
|
||||
"found_chrome_at": "找到 Chrome: {path}",
|
||||
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}",
|
||||
"select_profile": "选择要使用的 {browser} 配置文件:",
|
||||
"profile_list": "可用 {browser} 配置文件:"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome配置文件选择",
|
||||
"select_profile": "选择要使用的Chrome配置文件:",
|
||||
"profile_list": "可用配置文件:",
|
||||
"browser_profile": {
|
||||
"title": "浏览器配置文件选择",
|
||||
"select_profile": "选择要使用的{browser}配置文件:",
|
||||
"profile_list": "可用{browser}配置文件:",
|
||||
"default_profile": "默认配置文件",
|
||||
"profile": "配置文件 {number}",
|
||||
"no_profiles": "未找到Chrome配置文件",
|
||||
"error_loading": "加载Chrome配置文件时出错:{error}",
|
||||
"no_profiles": "未找到{browser}配置文件",
|
||||
"error_loading": "加载{browser}配置文件时出错:{error}",
|
||||
"profile_selected": "已选择配置文件:{profile}",
|
||||
"invalid_selection": "选择无效。请重试",
|
||||
"warning_chrome_close": "警告:这将关闭所有正在运行的Chrome进程"
|
||||
"invalid_selection": "选择无效。请重试"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google 账号删除工具",
|
||||
@@ -669,5 +705,93 @@
|
||||
"title": "Cursor 版本绕过工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
|
||||
"menu_option": "绕过 Cursor 版本检查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "检查授权...",
|
||||
"token_source": "从数据库获取 token 或手动输入?(d/m, 默认: d)",
|
||||
"getting_token_from_db": "从数据库获取 token...",
|
||||
"token_found_in_db": "在数据库中找到 token",
|
||||
"token_not_found_in_db": "在数据库中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
|
||||
"enter_token": "请输入您的 Cursor token: ",
|
||||
"token_length": "token 长度: {length}",
|
||||
"usage_response_status": "使用情况响应状态: {response}",
|
||||
"unexpected_status_code": "意外状态码: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
|
||||
"invalid_token": "无效的 token",
|
||||
"user_authorized": "用户已授权",
|
||||
"user_unauthorized": "用户未授权",
|
||||
"request_timeout": "请求超时",
|
||||
"connection_error": "连接错误",
|
||||
"check_error": "检查授权时出错: {error}",
|
||||
"authorization_successful": "授权成功",
|
||||
"authorization_failed": "授权失败",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外错误: {error}",
|
||||
"error_generating_checksum": "生成校验和时出错: {error}",
|
||||
"checking_usage_information": "检查使用情况...",
|
||||
"check_usage_response": "检查使用情况响应: {response}",
|
||||
"usage_response": "使用情况响应: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "绕过 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制",
|
||||
"press_enter": "按回车键继续..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "正在刷新令牌...",
|
||||
"refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire})",
|
||||
"no_access_token": "响应中没有访问令牌",
|
||||
"refresh_failed": "令牌刷新失败: {error}",
|
||||
"invalid_response": "刷新服务器返回无效的 JSON 响应",
|
||||
"server_error": "刷新服务器错误: HTTP {status}",
|
||||
"request_timeout": "刷新服务器请求超时",
|
||||
"connection_error": "连接刷新服务器错误",
|
||||
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
|
||||
"extraction_error": "提取令牌时出错: {error}"
|
||||
},
|
||||
"restore": {
|
||||
"title": "从备份恢复机器ID",
|
||||
"starting": "正在启动机器ID恢复进程",
|
||||
"no_backups_found": "未找到备份文件",
|
||||
"available_backups": "可用的备份文件",
|
||||
"select_backup": "选择要恢复的备份",
|
||||
"to_cancel": "取消操作",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"invalid_selection": "选择无效",
|
||||
"please_enter_number": "请输入有效的数字",
|
||||
"missing_id": "缺少ID: {id}",
|
||||
"read_backup_failed": "读取备份文件失败: {error}",
|
||||
"current_file_not_found": "未找到当前存储文件",
|
||||
"current_backup_created": "已创建当前存储文件的备份",
|
||||
"storage_updated": "存储文件已成功更新",
|
||||
"update_failed": "更新存储文件失败: {error}",
|
||||
"sqlite_not_found": "未找到SQLite数据库",
|
||||
"updating_sqlite": "正在更新SQLite数据库",
|
||||
"updating_pair": "正在更新键值对",
|
||||
"sqlite_updated": "SQLite数据库已成功更新",
|
||||
"sqlite_update_failed": "更新SQLite数据库失败: {error}",
|
||||
"machine_id_backup_created": "已创建machineId文件的备份",
|
||||
"backup_creation_failed": "创建备份失败: {error}",
|
||||
"machine_id_updated": "machineId文件已成功更新",
|
||||
"machine_id_update_failed": "更新machineId文件失败: {error}",
|
||||
"updating_system_ids": "正在更新系统ID",
|
||||
"system_ids_update_failed": "更新系统ID失败: {error}",
|
||||
"permission_denied": "权限被拒绝。请尝试以管理员身份运行",
|
||||
"windows_machine_guid_updated": "Windows机器GUID已成功更新",
|
||||
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}",
|
||||
"windows_machine_id_updated": "Windows机器ID已成功更新",
|
||||
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
|
||||
"sqm_client_key_not_found": "未找到SQMClient注册表项",
|
||||
"update_windows_system_ids_failed": "更新Windows系统ID失败: {error}",
|
||||
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
|
||||
"failed_to_execute_plutil_command": "执行plutil命令失败",
|
||||
"update_macos_system_ids_failed": "更新macOS系统ID失败: {error}",
|
||||
"ids_to_restore": "要恢复的机器ID",
|
||||
"confirm": "您确定要恢复这些ID吗?",
|
||||
"success": "机器ID已成功恢复",
|
||||
"process_error": "恢复过程错误: {error}",
|
||||
"press_enter": "按Enter键继续"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
"exit": "退出程式",
|
||||
"reset": "重置機器ID",
|
||||
"register": "註冊新的Cursor帳戶",
|
||||
"register_google": "使用Google帳戶註冊",
|
||||
"register_github": "使用GitHub帳戶註冊",
|
||||
"register_google": "使用自己的Google帳戶註冊",
|
||||
"register_github": "使用自己的GitHub帳戶註冊",
|
||||
"register_manual": "使用自定義郵箱註冊Cursor",
|
||||
"quit": "關閉Cursor應用",
|
||||
"select_language": "更改語言",
|
||||
@@ -30,9 +30,15 @@
|
||||
"continue_prompt": "繼續?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被使用者取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "繞過 Cursor 版本檢查"
|
||||
"bypass_version_check": "繞過 Cursor 版本檢查",
|
||||
"check_user_authorized": "檢查用戶授權",
|
||||
"bypass_token_limit": "繞過 Token 限制",
|
||||
"language_config_saved": "語言配置保存成功",
|
||||
"lang_invalid_choice": "選擇無效。請輸入以下選項之一:({lang_choices})",
|
||||
"restore_machine_id": "從備份恢復機器ID"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "阿拉伯語",
|
||||
"en": "英文",
|
||||
"zh_cn": "簡體中文",
|
||||
"zh_tw": "繁體中文",
|
||||
@@ -117,9 +123,9 @@
|
||||
"modify_file_failed": "修改文件失敗: {error}",
|
||||
"windows_machine_id_updated": "Windows機器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}"
|
||||
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
|
||||
"file_not_found": "文件未找到: {path}"
|
||||
},
|
||||
|
||||
"register": {
|
||||
"title": "Cursor 註冊工具",
|
||||
"start": "正在啟動註冊流程...",
|
||||
@@ -146,6 +152,22 @@
|
||||
"no_turnstile": "未檢測到 Turnstile 驗證",
|
||||
"turnstile_passed": "驗證通過",
|
||||
"verification_start": "開始獲取驗證碼",
|
||||
"verification_timeout": "獲取驗證碼超時",
|
||||
"verification_not_found": "未找到驗證碼",
|
||||
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
|
||||
"get_account": "獲取帳戶信息",
|
||||
"get_token": "獲取 Cursor Session Token",
|
||||
"token_success": "Token 獲取成功",
|
||||
"token_attempt": "第 {attempt} 次嘗試未獲取到 Token,{time}秒後重試",
|
||||
"token_max_attempts": "已達到最大嘗試次數({max}),獲取 Token 失敗",
|
||||
"token_failed": "獲取 Token 失敗: {error}",
|
||||
"account_error": "獲取帳戶信息失敗: {error}",
|
||||
"email_error": "獲取郵箱地址失敗",
|
||||
"setup_error": "郵箱設置出錯: {error}",
|
||||
"start_getting_verification_code": "開始獲取驗證碼,將在60秒內嘗試...",
|
||||
"get_verification_code_timeout": "獲取驗證碼超時",
|
||||
"get_verification_code_success": "成功獲取驗證碼",
|
||||
"try_get_verification_code": "第 {attempt} 次嘗試未獲取到驗證碼,剩餘時間: {remaining_time}秒",
|
||||
"verification_code_filled": "驗證碼填寫完成",
|
||||
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
|
||||
"detect_login_page": "檢測到登錄頁面,開始登錄...",
|
||||
@@ -515,7 +537,13 @@
|
||||
"config_setup_error": "配置設置錯誤",
|
||||
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
|
||||
"error_reading_storage_file": "讀取儲存文件時出錯",
|
||||
"also_checked": "也檢查了 {path}"
|
||||
"also_checked": "也檢查了 {path}",
|
||||
"backup_created": "備份已創建: {path}",
|
||||
"config_removed": "配置文件已刪除用於強制更新",
|
||||
"backup_failed": "備份失敗: {error}",
|
||||
"force_update_failed": "強制更新配置失敗: {error}",
|
||||
"config_force_update_disabled": "配置文件強制更新已禁用,跳過強制更新",
|
||||
"config_force_update_enabled": "配置文件強制更新已啟用,正在執行強制更新"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到認證按鈕",
|
||||
@@ -573,7 +601,18 @@
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
|
||||
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}"
|
||||
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}",
|
||||
"using_configured_browser_path": "使用配置的 {browser} 路徑: {path}",
|
||||
"found_browser_user_data_dir": "找到 {browser} 用戶數據目錄: {path}",
|
||||
"warning_browser_close": "警告:這將關閉所有正在執行的 {browser} 進程",
|
||||
"killing_browser_processes": "正在關閉 {browser} 進程...",
|
||||
"profile_selection_error": "配置文件選擇過程中出錯: {error}",
|
||||
"select_profile": "選擇要使用的 {browser} 配置文件:",
|
||||
"profile_list": "可用 {browser} 配置文件:",
|
||||
"no_profiles": "未找到 {browser} 配置文件",
|
||||
"error_loading": "載入 {browser} 配置文件時出錯:{error}",
|
||||
"profile_selected": "已選擇配置文件:{profile}",
|
||||
"invalid_selection": "選擇無效。請重試"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome配置檔案選擇",
|
||||
@@ -651,5 +690,81 @@
|
||||
"title": "Cursor 版本繞過工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
|
||||
"menu_option": "繞過 Cursor 版本檢查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "檢查授權...",
|
||||
"token_source": "從資料庫獲取 token 或手動輸入?(d/m, 預設: d)",
|
||||
"getting_token_from_db": "從資料庫獲取 token...",
|
||||
"token_found_in_db": "在資料庫中找到 token",
|
||||
"token_not_found_in_db": "在資料庫中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"usage_response_status": "使用情況響應狀態: {response}",
|
||||
"unexpected_status_code": "意外狀態碼: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
|
||||
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
|
||||
"enter_token": "請輸入您的 Cursor token: ",
|
||||
"token_length": "token 長度: {length}",
|
||||
"invalid_token": "無效的 token",
|
||||
"user_authorized": "用戶已授權",
|
||||
"user_unauthorized": "用戶未授權",
|
||||
"request_timeout": "請求超時",
|
||||
"connection_error": "連接錯誤",
|
||||
"check_error": "檢查授權時出錯: {error}",
|
||||
"authorization_successful": "授權成功",
|
||||
"authorization_failed": "授權失敗",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外錯誤: {error}",
|
||||
"error_generating_checksum": "生成校驗和時出錯: {error}",
|
||||
"checking_usage_information": "檢查使用情況...",
|
||||
"check_usage_response": "檢查使用情況響應: {response}",
|
||||
"usage_response": "使用情況響應: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "繞過 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
|
||||
"press_enter": "按回車鍵繼續..."
|
||||
},
|
||||
"restore": {
|
||||
"title": "從備份恢復機器ID",
|
||||
"starting": "正在啟動機器ID恢復進程",
|
||||
"no_backups_found": "未找到備份文件",
|
||||
"available_backups": "可用的備份文件",
|
||||
"select_backup": "選擇要恢復的備份",
|
||||
"to_cancel": "取消操作",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"invalid_selection": "選擇無效",
|
||||
"please_enter_number": "請輸入有效的數字",
|
||||
"missing_id": "缺少ID: {id}",
|
||||
"read_backup_failed": "讀取備份文件失敗: {error}",
|
||||
"current_file_not_found": "未找到當前存儲文件",
|
||||
"current_backup_created": "已創建當前存儲文件的備份",
|
||||
"storage_updated": "存儲文件已成功更新",
|
||||
"update_failed": "更新存儲文件失敗: {error}",
|
||||
"sqlite_not_found": "未找到SQLite數據庫",
|
||||
"updating_sqlite": "正在更新SQLite數據庫",
|
||||
"updating_pair": "正在更新鍵值對",
|
||||
"sqlite_updated": "SQLite數據庫已成功更新",
|
||||
"sqlite_update_failed": "更新SQLite數據庫失敗: {error}",
|
||||
"machine_id_backup_created": "已創建machineId文件的備份",
|
||||
"backup_creation_failed": "創建備份失敗: {error}",
|
||||
"machine_id_updated": "machineId文件已成功更新",
|
||||
"machine_id_update_failed": "更新machineId文件失敗: {error}",
|
||||
"updating_system_ids": "正在更新系統ID",
|
||||
"system_ids_update_failed": "更新系統ID失敗: {error}",
|
||||
"permission_denied": "權限被拒絕。請嘗試以管理員身份運行",
|
||||
"windows_machine_guid_updated": "Windows機器GUID已成功更新",
|
||||
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}",
|
||||
"windows_machine_id_updated": "Windows機器ID已成功更新",
|
||||
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
|
||||
"sqm_client_key_not_found": "未找到SQMClient註冊表項",
|
||||
"update_windows_system_ids_failed": "更新Windows系統ID失敗: {error}",
|
||||
"macos_platform_uuid_updated": "macOS平台UUID已成功更新",
|
||||
"failed_to_execute_plutil_command": "執行plutil命令失敗",
|
||||
"update_macos_system_ids_failed": "更新macOS系統ID失敗: {error}",
|
||||
"ids_to_restore": "要恢復的機器ID",
|
||||
"confirm": "您確定要恢復這些ID嗎?",
|
||||
"success": "機器ID已成功恢復",
|
||||
"process_error": "恢復過程錯誤: {error}",
|
||||
"press_enter": "按Enter鍵繼續"
|
||||
}
|
||||
}
|
||||
}
|
||||
2
logo.py
2
logo.py
@@ -83,7 +83,7 @@ muhammedfurkan plamkatawe Lucaszmv
|
||||
"""
|
||||
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
||||
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
|
||||
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
|
||||
Press 4 to change language | 按下 4 键切换语言{Style.RESET_ALL}"""
|
||||
|
||||
# center display LOGO and DESCRIPTION
|
||||
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
|
||||
|
||||
541
main.py
541
main.py
@@ -9,9 +9,18 @@ import locale
|
||||
import platform
|
||||
import requests
|
||||
import subprocess
|
||||
from config import get_config
|
||||
from config import get_config, force_update_config
|
||||
import shutil
|
||||
import re
|
||||
from utils import get_user_documents_path
|
||||
|
||||
# Add these imports for Arabic support
|
||||
try:
|
||||
import arabic_reshaper
|
||||
from bidi.algorithm import get_display
|
||||
except ImportError:
|
||||
arabic_reshaper = None
|
||||
get_display = None
|
||||
|
||||
# Only import windll on Windows systems
|
||||
if platform.system() == 'Windows':
|
||||
@@ -79,8 +88,37 @@ def run_as_admin():
|
||||
class Translator:
|
||||
def __init__(self):
|
||||
self.translations = {}
|
||||
self.current_language = self.detect_system_language() # Use correct method name
|
||||
self.fallback_language = 'en' # Fallback language if translation is missing
|
||||
self.config = get_config()
|
||||
|
||||
# Create language cache directory if it doesn't exist
|
||||
if self.config and self.config.has_section('Language'):
|
||||
self.language_cache_dir = self.config.get('Language', 'language_cache_dir')
|
||||
os.makedirs(self.language_cache_dir, exist_ok=True)
|
||||
else:
|
||||
self.language_cache_dir = None
|
||||
|
||||
# Set fallback language from config if available
|
||||
self.fallback_language = 'en'
|
||||
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'fallback_language'):
|
||||
self.fallback_language = self.config.get('Language', 'fallback_language')
|
||||
|
||||
# Load saved language from config if available, otherwise detect system language
|
||||
if self.config and self.config.has_section('Language') and self.config.has_option('Language', 'current_language'):
|
||||
saved_language = self.config.get('Language', 'current_language')
|
||||
if saved_language and saved_language.strip():
|
||||
self.current_language = saved_language
|
||||
else:
|
||||
self.current_language = self.detect_system_language()
|
||||
# Save detected language to config
|
||||
if self.config.has_section('Language'):
|
||||
self.config.set('Language', 'current_language', self.current_language)
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
self.config.write(f)
|
||||
else:
|
||||
self.current_language = self.detect_system_language()
|
||||
|
||||
self.load_translations()
|
||||
|
||||
def detect_system_language(self):
|
||||
@@ -110,18 +148,26 @@ class Translator:
|
||||
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
|
||||
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
|
||||
|
||||
# Map language ID to our language codes
|
||||
language_map = {
|
||||
0x0409: 'en', # English
|
||||
0x0404: 'zh_tw', # Traditional Chinese
|
||||
0x0804: 'zh_cn', # Simplified Chinese
|
||||
0x0422: 'vi', # Vietnamese
|
||||
0x0419: 'ru', # Russian
|
||||
0x0415: 'tr', # Turkish
|
||||
0x0402: 'bg', # Bulgarian
|
||||
}
|
||||
|
||||
return language_map.get(layout_id, 'en')
|
||||
# Map language ID to our language codes using match-case
|
||||
match layout_id:
|
||||
case 0x0409:
|
||||
return 'en' # English
|
||||
case 0x0404:
|
||||
return 'zh_tw' # Traditional Chinese
|
||||
case 0x0804:
|
||||
return 'zh_cn' # Simplified Chinese
|
||||
case 0x0422:
|
||||
return 'vi' # Vietnamese
|
||||
case 0x0419:
|
||||
return 'ru' # Russian
|
||||
case 0x0415:
|
||||
return 'tr' # Turkish
|
||||
case 0x0402:
|
||||
return 'bg' # Bulgarian
|
||||
case 0x0401:
|
||||
return 'ar' # Arabic
|
||||
case _:
|
||||
return 'en' # Default to English
|
||||
except:
|
||||
return self._detect_unix_language()
|
||||
|
||||
@@ -129,85 +175,123 @@ class Translator:
|
||||
"""Detect language on Unix-like systems (Linux, macOS)"""
|
||||
try:
|
||||
# Get the system locale
|
||||
system_locale = locale.getdefaultlocale()[0]
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
system_locale = locale.getlocale()[0]
|
||||
if not system_locale:
|
||||
return 'en'
|
||||
|
||||
system_locale = system_locale.lower()
|
||||
|
||||
# Map locale to our language codes
|
||||
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
|
||||
return 'zh_tw'
|
||||
elif system_locale.startswith('zh_cn'):
|
||||
return 'zh_cn'
|
||||
elif system_locale.startswith('en'):
|
||||
return 'en'
|
||||
elif system_locale.startswith('vi'):
|
||||
return 'vi'
|
||||
elif system_locale.startswith('nl'):
|
||||
return 'nl'
|
||||
elif system_locale.startswith('de'):
|
||||
return 'de'
|
||||
elif system_locale.startswith('fr'):
|
||||
return 'fr'
|
||||
elif system_locale.startswith('pt'):
|
||||
return 'pt'
|
||||
elif system_locale.startswith('ru'):
|
||||
return 'ru'
|
||||
elif system_locale.startswith('tr'):
|
||||
return 'tr'
|
||||
elif system_locale.startswith('bg'):
|
||||
return 'bg'
|
||||
# Try to get language from LANG environment variable as fallback
|
||||
env_lang = os.getenv('LANG', '').lower()
|
||||
if 'tw' in env_lang or 'hk' in env_lang:
|
||||
return 'zh_tw'
|
||||
elif 'cn' in env_lang:
|
||||
return 'zh_cn'
|
||||
elif 'vi' in env_lang:
|
||||
return 'vi'
|
||||
elif 'nl' in env_lang:
|
||||
return 'nl'
|
||||
elif 'de' in env_lang:
|
||||
return 'de'
|
||||
elif 'fr' in env_lang:
|
||||
return 'fr'
|
||||
elif 'pt' in env_lang:
|
||||
return 'pt'
|
||||
elif 'ru' in env_lang:
|
||||
return 'ru'
|
||||
elif 'tr' in env_lang:
|
||||
return 'tr'
|
||||
elif 'bg' in env_lang:
|
||||
return 'bg'
|
||||
|
||||
return 'en'
|
||||
# Map locale to our language codes using match-case
|
||||
match system_locale:
|
||||
case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
|
||||
return 'zh_tw'
|
||||
case s if s.startswith('zh_cn'):
|
||||
return 'zh_cn'
|
||||
case s if s.startswith('en'):
|
||||
return 'en'
|
||||
case s if s.startswith('vi'):
|
||||
return 'vi'
|
||||
case s if s.startswith('nl'):
|
||||
return 'nl'
|
||||
case s if s.startswith('de'):
|
||||
return 'de'
|
||||
case s if s.startswith('fr'):
|
||||
return 'fr'
|
||||
case s if s.startswith('pt'):
|
||||
return 'pt'
|
||||
case s if s.startswith('ru'):
|
||||
return 'ru'
|
||||
case s if s.startswith('tr'):
|
||||
return 'tr'
|
||||
case s if s.startswith('bg'):
|
||||
return 'bg'
|
||||
case s if s.startswith('ar'):
|
||||
return 'ar'
|
||||
case _:
|
||||
# Try to get language from LANG environment variable as fallback
|
||||
env_lang = os.getenv('LANG', '').lower()
|
||||
match env_lang:
|
||||
case s if 'tw' in s or 'hk' in s:
|
||||
return 'zh_tw'
|
||||
case s if 'cn' in s:
|
||||
return 'zh_cn'
|
||||
case s if 'vi' in s:
|
||||
return 'vi'
|
||||
case s if 'nl' in s:
|
||||
return 'nl'
|
||||
case s if 'de' in s:
|
||||
return 'de'
|
||||
case s if 'fr' in s:
|
||||
return 'fr'
|
||||
case s if 'pt' in s:
|
||||
return 'pt'
|
||||
case s if 'ru' in s:
|
||||
return 'ru'
|
||||
case s if 'tr' in s:
|
||||
return 'tr'
|
||||
case s if 'bg' in s:
|
||||
return 'bg'
|
||||
case s if 'ar' in s:
|
||||
return 'ar'
|
||||
case _:
|
||||
return 'en'
|
||||
except:
|
||||
return 'en'
|
||||
|
||||
def load_translations(self):
|
||||
"""Load all available translations"""
|
||||
try:
|
||||
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
locales_dir = os.path.join(sys._MEIPASS, 'locales')
|
||||
def download_language_file(self, lang_code):
|
||||
"""Method kept for compatibility but now returns False as language files are integrated"""
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Languages are now integrated into the package, no need to download.{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if not os.path.exists(locales_dir):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
|
||||
return
|
||||
def load_translations(self):
|
||||
"""Load all available translations from the integrated package"""
|
||||
try:
|
||||
# Collection of languages we've successfully loaded
|
||||
loaded_languages = set()
|
||||
|
||||
locales_paths = []
|
||||
|
||||
# Check for PyInstaller bundle first
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
locales_paths.append(os.path.join(sys._MEIPASS, 'locales'))
|
||||
|
||||
# Check script directory next
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
locales_paths.append(os.path.join(script_dir, 'locales'))
|
||||
|
||||
# Also check current working directory
|
||||
locales_paths.append(os.path.join(os.getcwd(), 'locales'))
|
||||
|
||||
for locales_dir in locales_paths:
|
||||
if os.path.exists(locales_dir) and os.path.isdir(locales_dir):
|
||||
for file in os.listdir(locales_dir):
|
||||
if file.endswith('.json'):
|
||||
lang_code = file[:-5] # Remove .json
|
||||
try:
|
||||
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
|
||||
self.translations[lang_code] = json.load(f)
|
||||
loaded_languages.add(lang_code)
|
||||
loaded_any = True
|
||||
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
|
||||
continue
|
||||
|
||||
for file in os.listdir(locales_dir):
|
||||
if file.endswith('.json'):
|
||||
lang_code = file[:-5] # Remove .json
|
||||
try:
|
||||
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
|
||||
self.translations[lang_code] = json.load(f)
|
||||
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to load translations: {e}{Style.RESET_ALL}")
|
||||
# Create at least minimal English translations for basic functionality
|
||||
self.translations['en'] = {"menu": {"title": "Menu", "exit": "Exit", "invalid_choice": "Invalid choice"}}
|
||||
|
||||
def fix_arabic(self, text):
|
||||
if self.current_language == 'ar' and arabic_reshaper and get_display:
|
||||
try:
|
||||
reshaped_text = arabic_reshaper.reshape(text)
|
||||
bidi_text = get_display(reshaped_text)
|
||||
return bidi_text
|
||||
except Exception:
|
||||
return text
|
||||
return text
|
||||
|
||||
def get(self, key, **kwargs):
|
||||
"""Get translated text with fallback support"""
|
||||
try:
|
||||
@@ -216,7 +300,8 @@ class Translator:
|
||||
if result == key and self.current_language != self.fallback_language:
|
||||
# Try fallback language if translation not found
|
||||
result = self._get_translation(self.fallback_language, key)
|
||||
return result.format(**kwargs) if kwargs else result
|
||||
formatted = result.format(**kwargs) if kwargs else result
|
||||
return self.fix_arabic(formatted)
|
||||
except Exception:
|
||||
return key
|
||||
|
||||
@@ -243,7 +328,11 @@ class Translator:
|
||||
|
||||
def get_available_languages(self):
|
||||
"""Get list of available languages"""
|
||||
return list(self.translations.keys())
|
||||
# Get currently loaded languages
|
||||
available_languages = list(self.translations.keys())
|
||||
|
||||
# Sort languages alphabetically for better display
|
||||
return sorted(available_languages)
|
||||
|
||||
# Create translator instance
|
||||
translator = Translator()
|
||||
@@ -274,20 +363,21 @@ def print_menu():
|
||||
menu_items = {
|
||||
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
|
||||
1: f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}",
|
||||
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})",
|
||||
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL}))",
|
||||
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL}))",
|
||||
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
|
||||
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
|
||||
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
|
||||
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
|
||||
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
|
||||
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
|
||||
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
|
||||
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
|
||||
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
|
||||
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}"
|
||||
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
|
||||
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
|
||||
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
|
||||
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')}",
|
||||
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')}",
|
||||
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
|
||||
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
|
||||
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
|
||||
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
|
||||
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check')}",
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized')}",
|
||||
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit')}",
|
||||
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['BACKUP']} {translator.get('menu.restore_machine_id')}",
|
||||
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account')}",
|
||||
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}"
|
||||
}
|
||||
|
||||
# Automatically calculate the number of menu items in the left and right columns
|
||||
@@ -364,21 +454,45 @@ def select_language():
|
||||
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
|
||||
# Get available languages either from local directory or GitHub
|
||||
languages = translator.get_available_languages()
|
||||
languages_count = len(languages)
|
||||
|
||||
# Display all available languages with proper indices
|
||||
for i, lang in enumerate(languages):
|
||||
lang_name = translator.get(f"languages.{lang}")
|
||||
lang_name = translator.get(f"languages.{lang}", fallback=lang)
|
||||
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_name}")
|
||||
|
||||
try:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{len(languages)-1}')}: {Style.RESET_ALL}")
|
||||
if choice.isdigit() and 0 <= int(choice) < len(languages):
|
||||
translator.set_language(languages[int(choice)])
|
||||
# Use the actual number of languages in the prompt
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{languages_count-1}')}: {Style.RESET_ALL}")
|
||||
|
||||
if choice.isdigit() and 0 <= int(choice) < languages_count:
|
||||
selected_language = languages[int(choice)]
|
||||
translator.set_language(selected_language)
|
||||
|
||||
# Save selected language to config
|
||||
config = get_config()
|
||||
if config and config.has_section('Language'):
|
||||
config.set('Language', 'current_language', selected_language)
|
||||
|
||||
# Get config path from user documents
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
|
||||
# Write updated config
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('menu.language_config_saved', language=translator.get(f'languages.{selected_language}', fallback=selected_language))}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
# Show invalid choice message with the correct range
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except (ValueError, IndexError):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
except (ValueError, IndexError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.lang_invalid_choice', lang_choices=f'0-{languages_count-1}')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_latest_version():
|
||||
@@ -386,31 +500,67 @@ def check_latest_version():
|
||||
try:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
|
||||
|
||||
# Get latest version from GitHub API with timeout and proper headers
|
||||
# First try GitHub API
|
||||
headers = {
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
'User-Agent': 'CursorFreeVIP-Updater'
|
||||
}
|
||||
response = requests.get(
|
||||
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
# Check if rate limit exceeded
|
||||
if response.status_code == 403 and "rate limit exceeded" in response.text.lower():
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Skipping update check.')}{Style.RESET_ALL}")
|
||||
return
|
||||
latest_version = None
|
||||
github_error = None
|
||||
|
||||
# Check if response is successful
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"GitHub API returned status code {response.status_code}")
|
||||
# Try GitHub API first
|
||||
try:
|
||||
github_response = requests.get(
|
||||
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
if "tag_name" not in response_data:
|
||||
raise Exception("No version tag found in GitHub response")
|
||||
# Check if rate limit exceeded
|
||||
if github_response.status_code == 403 and "rate limit exceeded" in github_response.text.lower():
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.rate_limit_exceeded', fallback='GitHub API rate limit exceeded. Trying backup API...')}{Style.RESET_ALL}")
|
||||
raise Exception("Rate limit exceeded")
|
||||
|
||||
# Check if response is successful
|
||||
if github_response.status_code != 200:
|
||||
raise Exception(f"GitHub API returned status code {github_response.status_code}")
|
||||
|
||||
github_data = github_response.json()
|
||||
if "tag_name" not in github_data:
|
||||
raise Exception("No version tag found in GitHub response")
|
||||
|
||||
latest_version = github_data["tag_name"].lstrip('v')
|
||||
|
||||
latest_version = response_data["tag_name"].lstrip('v')
|
||||
except Exception as e:
|
||||
github_error = str(e)
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.github_api_failed', fallback='GitHub API failed, trying backup API...')}{Style.RESET_ALL}")
|
||||
|
||||
# If GitHub API fails, try backup API
|
||||
try:
|
||||
backup_headers = {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'CursorFreeVIP-Updater'
|
||||
}
|
||||
backup_response = requests.get(
|
||||
"https://pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip",
|
||||
headers=backup_headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
# Check if response is successful
|
||||
if backup_response.status_code != 200:
|
||||
raise Exception(f"Backup API returned status code {backup_response.status_code}")
|
||||
|
||||
backup_data = backup_response.json()
|
||||
if "message" not in backup_data:
|
||||
raise Exception("No version tag found in backup API response")
|
||||
|
||||
latest_version = backup_data["message"].lstrip('v')
|
||||
|
||||
except Exception as backup_e:
|
||||
# If both APIs fail, raise the original GitHub error
|
||||
raise Exception(f"Both APIs failed. GitHub error: {github_error}, Backup error: {str(backup_e)}")
|
||||
|
||||
# Validate version format
|
||||
if not latest_version:
|
||||
@@ -552,89 +702,94 @@ def main():
|
||||
if not config:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
force_update_config(translator)
|
||||
|
||||
if config.getboolean('Utils', 'enabled_update_check'):
|
||||
check_latest_version() # Add version check before showing menu
|
||||
print_menu()
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice_num = 15
|
||||
choice_num = 16
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
|
||||
|
||||
if choice == "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
elif choice == "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run(translator)
|
||||
print_menu()
|
||||
elif choice == "2":
|
||||
import cursor_register
|
||||
cursor_register.main(translator)
|
||||
print_menu()
|
||||
elif choice == "3":
|
||||
import cursor_register_google
|
||||
cursor_register_google.main(translator)
|
||||
print_menu()
|
||||
elif choice == "4":
|
||||
import cursor_register_github
|
||||
cursor_register_github.main(translator)
|
||||
print_menu()
|
||||
elif choice == "5":
|
||||
import cursor_register_manual
|
||||
cursor_register_manual.main(translator)
|
||||
print_menu()
|
||||
elif choice == "6":
|
||||
import github_cursor_register
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
|
||||
# github_cursor_register.main(translator)
|
||||
print_menu()
|
||||
elif choice == "7":
|
||||
import quit_cursor
|
||||
quit_cursor.quit_cursor(translator)
|
||||
print_menu()
|
||||
elif choice == "8":
|
||||
if select_language():
|
||||
match choice:
|
||||
case "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
case "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run(translator)
|
||||
print_menu()
|
||||
case "2":
|
||||
import cursor_register_manual
|
||||
cursor_register_manual.main(translator)
|
||||
print_menu()
|
||||
case "3":
|
||||
import quit_cursor
|
||||
quit_cursor.quit_cursor(translator)
|
||||
print_menu()
|
||||
case "4":
|
||||
if select_language():
|
||||
print_menu()
|
||||
continue
|
||||
case "5":
|
||||
from oauth_auth import main as oauth_main
|
||||
oauth_main('google',translator)
|
||||
print_menu()
|
||||
case "6":
|
||||
from oauth_auth import main as oauth_main
|
||||
oauth_main('github',translator)
|
||||
print_menu()
|
||||
case "7":
|
||||
import disable_auto_update
|
||||
disable_auto_update.run(translator)
|
||||
print_menu()
|
||||
case "8":
|
||||
import totally_reset_cursor
|
||||
totally_reset_cursor.run(translator)
|
||||
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
case "9":
|
||||
import logo
|
||||
print(logo.CURSOR_CONTRIBUTORS)
|
||||
print_menu()
|
||||
case "10":
|
||||
from config import print_config
|
||||
print_config(get_config(), translator)
|
||||
print_menu()
|
||||
case "11":
|
||||
import bypass_version
|
||||
bypass_version.main(translator)
|
||||
print_menu()
|
||||
case "12":
|
||||
import check_user_authorized
|
||||
check_user_authorized.main(translator)
|
||||
print_menu()
|
||||
case "13":
|
||||
import bypass_token_limit
|
||||
bypass_token_limit.run(translator)
|
||||
print_menu()
|
||||
case "14":
|
||||
import restore_machine_id
|
||||
restore_machine_id.run(translator)
|
||||
print_menu()
|
||||
case "15":
|
||||
import delete_cursor_google
|
||||
delete_cursor_google.main(translator)
|
||||
print_menu()
|
||||
case "16":
|
||||
from oauth_auth import OAuthHandler
|
||||
oauth = OAuthHandler(translator)
|
||||
oauth._select_profile()
|
||||
print_menu()
|
||||
case _:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
continue
|
||||
elif choice == "9":
|
||||
import disable_auto_update
|
||||
disable_auto_update.run(translator)
|
||||
print_menu()
|
||||
elif choice == "10":
|
||||
import totally_reset_cursor
|
||||
totally_reset_cursor.run(translator)
|
||||
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
elif choice == "11":
|
||||
import logo
|
||||
print(logo.CURSOR_CONTRIBUTORS)
|
||||
print_menu()
|
||||
elif choice == "12":
|
||||
from config import print_config
|
||||
print_config(get_config(), translator)
|
||||
print_menu()
|
||||
elif choice == "13":
|
||||
from oauth_auth import OAuthHandler
|
||||
oauth = OAuthHandler(translator)
|
||||
oauth._select_profile()
|
||||
print_menu()
|
||||
elif choice == "14":
|
||||
import delete_cursor_google
|
||||
delete_cursor_google.main(translator)
|
||||
print_menu()
|
||||
elif choice == "15":
|
||||
import bypass_version
|
||||
bypass_version.main(translator)
|
||||
print_menu()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
except Exception as e:
|
||||
|
||||
@@ -8,6 +8,7 @@ import configparser
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from config import get_config
|
||||
from utils import get_default_browser_path as utils_get_default_browser_path
|
||||
|
||||
# Add global variable at the beginning of the file
|
||||
_translator = None
|
||||
@@ -112,29 +113,6 @@ def fill_signup_form(page, first_name, last_name, email, config, translator=None
|
||||
print(f"Error filling form: {e}")
|
||||
return False
|
||||
|
||||
def get_default_chrome_path():
|
||||
"""Get default Chrome path"""
|
||||
if sys.platform == "win32":
|
||||
paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google/Chrome/Application/chrome.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google/Chrome/Application/chrome.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google/Chrome/Application/chrome.exe')
|
||||
]
|
||||
elif sys.platform == "darwin":
|
||||
paths = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
]
|
||||
else: # Linux
|
||||
paths = [
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/google-chrome-stable"
|
||||
]
|
||||
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return ""
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user Documents folder path"""
|
||||
if sys.platform == "win32":
|
||||
@@ -186,25 +164,32 @@ def setup_driver(translator=None):
|
||||
# Get config
|
||||
config = get_config(translator)
|
||||
|
||||
# Get Chrome path
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
|
||||
# Get browser type and path
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
|
||||
|
||||
if not chrome_path or not os.path.exists(chrome_path):
|
||||
if not browser_path or not os.path.exists(browser_path):
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {translator.get('register.chrome_path_invalid') if translator else 'Chrome路径无效,使用默认路径'}{Style.RESET_ALL}")
|
||||
chrome_path = get_default_chrome_path()
|
||||
print(f"{Fore.YELLOW}⚠️ {browser_type} {translator.get('register.browser_path_invalid')}{Style.RESET_ALL}")
|
||||
browser_path = utils_get_default_browser_path(browser_type)
|
||||
|
||||
# For backward compatibility, also check Chrome path
|
||||
if browser_type == 'chrome':
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
browser_path = chrome_path
|
||||
|
||||
# Set browser options
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Set Chrome path
|
||||
co.set_browser_path(chrome_path)
|
||||
# Set browser path
|
||||
co.set_browser_path(browser_path)
|
||||
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Set random port
|
||||
# Set Linux specific options
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
# Set random port
|
||||
@@ -213,6 +198,10 @@ def setup_driver(translator=None):
|
||||
# Use headless mode (must be set to False, simulate human operation)
|
||||
co.headless(False)
|
||||
|
||||
# Log browser info
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🌐 {translator.get('register.using_browser', browser=browser_type, path=browser_path)}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Load extension
|
||||
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
|
||||
@@ -234,30 +223,38 @@ def setup_driver(translator=None):
|
||||
before_pids = []
|
||||
try:
|
||||
import psutil
|
||||
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
browser_process_names = {
|
||||
'chrome': ['chrome', 'chromium'],
|
||||
'edge': ['msedge', 'edge'],
|
||||
'firefox': ['firefox'],
|
||||
'brave': ['brave', 'brave-browser']
|
||||
}
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
before_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Launch browser
|
||||
page = ChromiumPage(co)
|
||||
|
||||
# Wait a moment for Chrome to fully launch
|
||||
# Wait a moment for browser to fully launch
|
||||
time.sleep(1)
|
||||
|
||||
# Record Chrome processes after launching and find new ones
|
||||
# Record browser processes after launching and find new ones
|
||||
try:
|
||||
import psutil
|
||||
after_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
# Find new Chrome processes
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
after_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
# Find new browser processes
|
||||
new_pids = [pid for pid in after_pids if pid not in before_pids]
|
||||
_chrome_process_ids.extend(new_pids)
|
||||
|
||||
if _chrome_process_ids:
|
||||
print(f"Tracking {len(_chrome_process_ids)} Chrome processes")
|
||||
print(f"{translator.get('register.tracking_processes', count=len(_chrome_process_ids), browser=browser_type)}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}Warning: {translator.get('register.no_new_processes_detected', browser=browser_type)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not track Chrome processes: {e}")
|
||||
print(f"{translator.get('register.could_not_track_processes', browser=browser_type, error=str(e))}")
|
||||
|
||||
return config, page
|
||||
|
||||
|
||||
337
new_tempemail.py
337
new_tempemail.py
@@ -1,337 +0,0 @@
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
from colorama import Fore, Style, init
|
||||
import requests
|
||||
import random
|
||||
import string
|
||||
from utils import get_random_wait_time
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
class NewTempEmail:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
self.page = None
|
||||
self.setup_browser()
|
||||
|
||||
def get_blocked_domains(self):
|
||||
"""Get blocked domains list"""
|
||||
try:
|
||||
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
|
||||
response = requests.get(block_url, timeout=5)
|
||||
if response.status_code == 200:
|
||||
# Split text and remove empty lines
|
||||
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
return domains
|
||||
return self._load_local_blocked_domains()
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
|
||||
return self._load_local_blocked_domains()
|
||||
|
||||
def _load_local_blocked_domains(self):
|
||||
"""Load blocked domains from local file as fallback"""
|
||||
try:
|
||||
local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "block_domain.txt")
|
||||
if os.path.exists(local_path):
|
||||
with open(local_path, 'r', encoding='utf-8') as f:
|
||||
domains = [line.strip() for line in f.readlines() if line.strip()]
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.local_blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 已从本地加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
return domains
|
||||
else:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 本地被屏蔽域名文件不存在{Style.RESET_ALL}")
|
||||
return []
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 读取本地被屏蔽域名文件失败: {str(e)}{Style.RESET_ALL}")
|
||||
return []
|
||||
|
||||
def exclude_blocked_domains(self, domains):
|
||||
"""Exclude blocked domains"""
|
||||
if not self.blocked_domains:
|
||||
return domains
|
||||
|
||||
filtered_domains = []
|
||||
for domain in domains:
|
||||
if domain['domain'] not in self.blocked_domains:
|
||||
filtered_domains.append(domain)
|
||||
|
||||
excluded_count = len(domains) - len(filtered_domains)
|
||||
if excluded_count > 0:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
|
||||
return filtered_domains
|
||||
|
||||
|
||||
def get_extension_block(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "PBlock")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "PBlock")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def setup_browser(self):
|
||||
"""设置浏览器"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.starting_browser')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在启动浏览器...{Style.RESET_ALL}")
|
||||
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Only use headless for non-OAuth operations
|
||||
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Check if DISPLAY is set when not in headless mode
|
||||
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
co.set_argument("--no-sandbox")
|
||||
co.set_argument("--disable-dev-shm-usage")
|
||||
co.set_argument("--disable-gpu")
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
# 加载 uBlock 插件
|
||||
try:
|
||||
extension_path = self.get_extension_block()
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
co.add_extension(extension_path)
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 加载插件失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
self.page = ChromiumPage(co)
|
||||
return True
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform == "linux":
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def create_email(self):
|
||||
"""create temporary email"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在访问 smailpro.com...{Style.RESET_ALL}")
|
||||
|
||||
# load blocked domains list
|
||||
self.blocked_domains = self.get_blocked_domains()
|
||||
|
||||
# visit website
|
||||
self.page.get("https://smailpro.com/")
|
||||
time.sleep(2)
|
||||
|
||||
# click create email button
|
||||
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
|
||||
if create_button:
|
||||
create_button.click()
|
||||
time.sleep(1)
|
||||
|
||||
# click Create button in popup
|
||||
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]')
|
||||
if modal_create_button:
|
||||
modal_create_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# get email address - modify selector
|
||||
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
|
||||
if email_div:
|
||||
email = email_div.text.strip()
|
||||
if '@' in email: # check if it's a valid email address
|
||||
# check if domain is blocked
|
||||
domain = email.split('@')[1]
|
||||
if self.blocked_domains and domain in self.blocked_domains:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domain_blocked')}: {domain}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 域名已被屏蔽: {domain},尝试重新创建邮箱{Style.RESET_ALL}")
|
||||
# create email again
|
||||
return self.create_email()
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
|
||||
return email
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.create_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
"""close browser"""
|
||||
if self.page:
|
||||
self.page.quit()
|
||||
|
||||
def refresh_inbox(self):
|
||||
"""refresh inbox"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
|
||||
|
||||
# click refresh button
|
||||
refresh_button = self.page.ele('xpath://button[@id="refresh"]')
|
||||
if refresh_button:
|
||||
refresh_button.click()
|
||||
time.sleep(2) # wait for refresh to complete
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 邮箱刷新成功{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 未找到刷新按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 刷新邮箱出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_for_cursor_email(self):
|
||||
"""检查是否有 Cursor 的验证邮件"""
|
||||
try:
|
||||
# find verification email - use more accurate selector
|
||||
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]')
|
||||
if email_div:
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
|
||||
# use JavaScript to click element
|
||||
self.page.run_js('arguments[0].click()', email_div)
|
||||
time.sleep(2) # wait for email content to load
|
||||
return True
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 未找到验证邮件{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.verification_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 检查验证邮件出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_verification_code(self):
|
||||
"""获取验证码"""
|
||||
try:
|
||||
# find verification code element
|
||||
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]')
|
||||
if code_element:
|
||||
code = code_element.text.strip()
|
||||
if code.isdigit() and len(code) == 6:
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
|
||||
return code
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 未找到有效的验证码{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.verification_code_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 获取验证码出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def main(translator=None):
|
||||
temp_email = NewTempEmail(translator)
|
||||
|
||||
try:
|
||||
email = temp_email.create_email()
|
||||
if email:
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}📧 {translator.get('email.address')}: {email}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
|
||||
|
||||
# test refresh function
|
||||
while True:
|
||||
if translator:
|
||||
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()
|
||||
else:
|
||||
choice = input("\n按 R 刷新邮箱,按 Q 退出: ").lower()
|
||||
if choice == 'r':
|
||||
temp_email.refresh_inbox()
|
||||
elif choice == 'q':
|
||||
break
|
||||
|
||||
finally:
|
||||
temp_email.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
491
oauth_auth.py
491
oauth_auth.py
@@ -1,3 +1,4 @@
|
||||
# oauth_auth.py
|
||||
import os
|
||||
from colorama import Fore, Style, init
|
||||
import time
|
||||
@@ -7,9 +8,10 @@ import sys
|
||||
import json
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from cursor_auth import CursorAuth
|
||||
from utils import get_random_wait_time, get_default_chrome_path
|
||||
from utils import get_random_wait_time, get_default_browser_path
|
||||
from config import get_config
|
||||
import platform
|
||||
from get_user_token import get_token_from_cookie
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -29,7 +31,7 @@ class OAuthHandler:
|
||||
def __init__(self, translator=None, auth_type=None):
|
||||
self.translator = translator
|
||||
self.config = get_config(translator)
|
||||
self.auth_type = auth_type # make sure the auth_type is not None
|
||||
self.auth_type = auth_type
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
self.selected_profile = None
|
||||
@@ -63,41 +65,100 @@ class OAuthHandler:
|
||||
return []
|
||||
|
||||
def _select_profile(self):
|
||||
"""Select a Chrome profile to use"""
|
||||
"""Allow user to select a browser profile to use"""
|
||||
try:
|
||||
# Get available profiles
|
||||
profiles = self._get_available_profiles(self._get_user_data_directory())
|
||||
if not profiles:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('chrome_profile.no_profiles') if self.translator else 'No Chrome profiles found'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Display available profiles
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('chrome_profile.select_profile') if self.translator else 'Select a Chrome profile to use:'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{self.translator.get('chrome_profile.profile_list') if self.translator else 'Available profiles:'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit') if self.translator else 'Exit'}{Style.RESET_ALL}")
|
||||
for i, (dir_name, display_name) in enumerate(profiles, 1):
|
||||
print(f"{Fore.CYAN}{i}. {display_name} ({dir_name}){Style.RESET_ALL}")
|
||||
|
||||
# Get user selection
|
||||
while True:
|
||||
try:
|
||||
choice = int(input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{len(profiles)}') if self.translator else f'Please enter your choice (0-{len(profiles)}): '}{Style.RESET_ALL}"))
|
||||
if choice == 0: # Add quit
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.exiting') if self.translator else 'Exiting profile selection...'}{Style.RESET_ALL}")
|
||||
return False
|
||||
elif 1 <= choice <= len(profiles):
|
||||
self.selected_profile = profiles[choice - 1][0]
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('chrome_profile.profile_selected', profile=self.selected_profile) if self.translator else f'Selected profile: {self.selected_profile}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
# 从配置中获取浏览器类型
|
||||
config = get_config(self.translator)
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_type_display = browser_type.capitalize()
|
||||
|
||||
if self.translator:
|
||||
# 动态使用浏览器类型
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.select_profile', browser=browser_type_display)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{self.translator.get('oauth.profile_list', browser=browser_type_display)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}")
|
||||
print(f"Available {browser_type_display} profiles:")
|
||||
|
||||
# Get the user data directory for the browser type
|
||||
user_data_dir = self._get_user_data_directory()
|
||||
|
||||
# Load available profiles from the selected browser type
|
||||
try:
|
||||
local_state_file = os.path.join(user_data_dir, "Local State")
|
||||
if os.path.exists(local_state_file):
|
||||
with open(local_state_file, 'r', encoding='utf-8') as f:
|
||||
state_data = json.load(f)
|
||||
profiles_data = state_data.get('profile', {}).get('info_cache', {})
|
||||
|
||||
# Create a list of available profiles
|
||||
profiles = []
|
||||
for profile_id, profile_info in profiles_data.items():
|
||||
name = profile_info.get('name', profile_id)
|
||||
# Mark the default profile
|
||||
if profile_id.lower() == 'default':
|
||||
name = f"{name} (Default)"
|
||||
profiles.append((profile_id, name))
|
||||
|
||||
# Sort profiles by name
|
||||
profiles.sort(key=lambda x: x[1])
|
||||
|
||||
# Show available profiles
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
|
||||
except ValueError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}0. Exit{Style.RESET_ALL}")
|
||||
|
||||
for i, (profile_id, name) in enumerate(profiles, 1):
|
||||
print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}")
|
||||
|
||||
# Get user's choice
|
||||
max_choice = len(profiles)
|
||||
choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
choice = int(choice_str)
|
||||
if choice == 0:
|
||||
return False
|
||||
elif 1 <= choice <= max_choice:
|
||||
selected_profile = profiles[choice-1][0]
|
||||
self.selected_profile = selected_profile
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.profile_selected', profile=selected_profile)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Selected profile: {selected_profile}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.invalid_selection')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
|
||||
return self._select_profile()
|
||||
except ValueError:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.invalid_selection')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
|
||||
return self._select_profile()
|
||||
else:
|
||||
# No Local State file, use Default profile
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}")
|
||||
self.selected_profile = "Default"
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Error loading profiles, use Default profile
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}")
|
||||
self.selected_profile = "Default"
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# General error, use Default profile
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}")
|
||||
self.selected_profile = "Default"
|
||||
return True
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup browser for OAuth flow using selected profile"""
|
||||
try:
|
||||
@@ -107,20 +168,35 @@ class OAuthHandler:
|
||||
platform_name = platform.system().lower()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
|
||||
|
||||
# 从配置中获取浏览器类型
|
||||
config = get_config(self.translator)
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
|
||||
# Get browser paths and user data directory
|
||||
user_data_dir = self._get_user_data_directory()
|
||||
chrome_path = self._get_browser_path()
|
||||
browser_path = self._get_browser_path()
|
||||
|
||||
if not chrome_path:
|
||||
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
|
||||
"- Windows: Google Chrome, Chromium\n" +
|
||||
"- macOS: Google Chrome, Chromium\n" +
|
||||
"- Linux: Google Chrome, Chromium, chromium-browser")
|
||||
if not browser_path:
|
||||
error_msg = (
|
||||
f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}" +
|
||||
"\n" +
|
||||
f"{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
|
||||
"- Windows: Google Chrome, Chromium\n" +
|
||||
"- macOS: Google Chrome, Chromium\n" +
|
||||
"- Linux: Google Chrome, Chromium, google-chrome-stable"
|
||||
)
|
||||
raise Exception(error_msg)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
|
||||
# Show warning about closing Chrome first
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('chrome_profile.warning_chrome_close') if self.translator else 'Warning: This will close all running Chrome processes'}{Style.RESET_ALL}")
|
||||
# Show warning about closing browser first - 使用动态提示
|
||||
if self.translator:
|
||||
warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type)
|
||||
else:
|
||||
warning_msg = f'Warning: This will close all running {browser_type} processes'
|
||||
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {warning_msg}{Style.RESET_ALL}")
|
||||
|
||||
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
|
||||
if choice != 'y':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
|
||||
@@ -135,14 +211,14 @@ class OAuthHandler:
|
||||
return False
|
||||
|
||||
# Configure browser options
|
||||
co = self._configure_browser_options(chrome_path, user_data_dir, self.selected_profile)
|
||||
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}")
|
||||
self.browser = ChromiumPage(co)
|
||||
|
||||
# Verify browser launched successfully
|
||||
if not self.browser:
|
||||
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
|
||||
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start', error=str(e)) if self.translator else 'Failed to initialize browser instance'}")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
|
||||
return True
|
||||
@@ -158,14 +234,61 @@ class OAuthHandler:
|
||||
return False
|
||||
|
||||
def _kill_browser_processes(self):
|
||||
"""Kill existing browser processes based on platform"""
|
||||
"""Kill existing browser processes based on platform and browser type"""
|
||||
try:
|
||||
# 从配置中获取浏览器类型
|
||||
config = get_config(self.translator)
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_type = browser_type.lower()
|
||||
|
||||
# 根据浏览器类型和平台定义要关闭的进程
|
||||
browser_processes = {
|
||||
'chrome': {
|
||||
'win': ['chrome.exe', 'chromium.exe'],
|
||||
'linux': ['chrome', 'chromium', 'chromium-browser', 'google-chrome-stable'],
|
||||
'mac': ['Chrome', 'Chromium']
|
||||
},
|
||||
'brave': {
|
||||
'win': ['brave.exe'],
|
||||
'linux': ['brave', 'brave-browser'],
|
||||
'mac': ['Brave Browser']
|
||||
},
|
||||
'edge': {
|
||||
'win': ['msedge.exe'],
|
||||
'linux': ['msedge'],
|
||||
'mac': ['Microsoft Edge']
|
||||
},
|
||||
'firefox': {
|
||||
'win': ['firefox.exe'],
|
||||
'linux': ['firefox'],
|
||||
'mac': ['Firefox']
|
||||
},
|
||||
'opera': {
|
||||
'win': ['opera.exe', 'launcher.exe'],
|
||||
'linux': ['opera'],
|
||||
'mac': ['Opera']
|
||||
}
|
||||
}
|
||||
|
||||
# 获取平台类型
|
||||
if os.name == 'nt':
|
||||
platform_type = 'win'
|
||||
elif sys.platform == 'darwin':
|
||||
platform_type = 'mac'
|
||||
else:
|
||||
platform_type = 'linux'
|
||||
|
||||
# 获取要关闭的进程列表
|
||||
processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, [])
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
|
||||
|
||||
# 根据平台关闭进程
|
||||
if os.name == 'nt': # Windows
|
||||
processes = ['chrome.exe', 'chromium.exe']
|
||||
for proc in processes:
|
||||
os.system(f'taskkill /f /im {proc} >nul 2>&1')
|
||||
else: # Linux/Mac
|
||||
processes = ['chrome', 'chromium', 'chromium-browser']
|
||||
for proc in processes:
|
||||
os.system(f'pkill -f {proc} >/dev/null 2>&1')
|
||||
|
||||
@@ -174,83 +297,155 @@ class OAuthHandler:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
|
||||
|
||||
def _get_user_data_directory(self):
|
||||
"""Get the appropriate user data directory based on platform"""
|
||||
"""Get the default user data directory based on browser type and platform"""
|
||||
try:
|
||||
# 从配置中获取浏览器类型
|
||||
config = get_config(self.translator)
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_type = browser_type.lower()
|
||||
|
||||
# 根据操作系统和浏览器类型获取用户数据目录
|
||||
if os.name == 'nt': # Windows
|
||||
possible_paths = [
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
|
||||
]
|
||||
user_data_dirs = {
|
||||
'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'),
|
||||
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
|
||||
'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'),
|
||||
'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'),
|
||||
'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'),
|
||||
'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable')
|
||||
}
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||
os.path.expanduser('~/Library/Application Support/Chromium')
|
||||
]
|
||||
user_data_dirs = {
|
||||
'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
|
||||
'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'),
|
||||
'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'),
|
||||
'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
|
||||
'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
|
||||
}
|
||||
else: # Linux
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/.config/google-chrome'),
|
||||
os.path.expanduser('~/.config/chromium'),
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser'
|
||||
]
|
||||
user_data_dirs = {
|
||||
'chrome': os.path.expanduser('~/.config/google-chrome'),
|
||||
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
|
||||
'edge': os.path.expanduser('~/.config/microsoft-edge'),
|
||||
'firefox': os.path.expanduser('~/.mozilla/firefox'),
|
||||
'opera': os.path.expanduser('~/.config/opera'),
|
||||
'operagx': os.path.expanduser('~/.config/opera-gx')
|
||||
}
|
||||
|
||||
# Try each possible path
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
# 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
|
||||
user_data_dir = user_data_dirs.get(browser_type)
|
||||
|
||||
# Create temporary profile if no existing profile found
|
||||
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
|
||||
os.makedirs(temp_profile, exist_ok=True)
|
||||
return temp_profile
|
||||
if user_data_dir and os.path.exists(user_data_dir):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
return user_data_dir
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}")
|
||||
return user_data_dirs['chrome'] # 回退到 Chrome 目录
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
# 在出错时提供一个默认目录
|
||||
if os.name == 'nt':
|
||||
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
|
||||
elif sys.platform == 'darwin':
|
||||
return os.path.expanduser('~/Library/Application Support/Google/Chrome')
|
||||
else:
|
||||
return os.path.expanduser('~/.config/google-chrome')
|
||||
|
||||
def _get_browser_path(self):
|
||||
"""Get the browser executable path based on platform"""
|
||||
"""Get appropriate browser path based on platform and selected browser type"""
|
||||
try:
|
||||
# Try default path first
|
||||
chrome_path = get_default_chrome_path()
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
return chrome_path
|
||||
# 从配置中获取浏览器类型
|
||||
config = get_config(self.translator)
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_type = browser_type.lower()
|
||||
|
||||
# 首先检查配置中是否有明确指定的浏览器路径
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=None)
|
||||
if browser_path and os.path.exists(browser_path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}")
|
||||
return browser_path
|
||||
|
||||
# 尝试获取默认路径
|
||||
browser_path = get_default_browser_path(browser_type)
|
||||
if browser_path and os.path.exists(browser_path):
|
||||
return browser_path
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific paths
|
||||
# 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器
|
||||
if os.name == 'nt': # Windows
|
||||
alt_paths = [
|
||||
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files\Chromium\Application\chrome.exe',
|
||||
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
|
||||
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
|
||||
]
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe')
|
||||
]
|
||||
elif browser_type == 'firefox':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Mozilla Firefox', 'firefox.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Mozilla Firefox', 'firefox.exe')
|
||||
]
|
||||
elif browser_type == 'opera':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Opera', 'opera.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Opera', 'opera.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
|
||||
]
|
||||
else: # 默认为 Chrome
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'Application', 'chrome.exe')
|
||||
]
|
||||
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
alt_paths = [
|
||||
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser']
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge']
|
||||
elif browser_type == 'firefox':
|
||||
possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox']
|
||||
else: # 默认为 Chrome
|
||||
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
|
||||
|
||||
else: # Linux
|
||||
alt_paths = [
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser',
|
||||
'/usr/bin/chromium',
|
||||
'/snap/bin/chromium',
|
||||
'/usr/local/bin/chrome',
|
||||
'/usr/local/bin/chromium'
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave']
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = ['/usr/bin/microsoft-edge']
|
||||
elif browser_type == 'firefox':
|
||||
possible_paths = ['/usr/bin/firefox']
|
||||
else: # 默认为 Chrome
|
||||
possible_paths = [
|
||||
'/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium',
|
||||
'/usr/bin/chromium-browser'
|
||||
]
|
||||
|
||||
# 检查每个可能的路径
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}")
|
||||
return path
|
||||
|
||||
# Try each alternative path
|
||||
for path in alt_paths:
|
||||
expanded_path = os.path.expanduser(path)
|
||||
if os.path.exists(expanded_path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
|
||||
return expanded_path
|
||||
# 如果找不到指定浏览器,则尝试使用 Chrome
|
||||
if browser_type != 'chrome':
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}")
|
||||
return self._get_chrome_path()
|
||||
|
||||
return None
|
||||
|
||||
@@ -258,17 +453,18 @@ class OAuthHandler:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
|
||||
def _configure_browser_options(self, browser_path, user_data_dir, active_profile):
|
||||
"""Configure browser options based on platform"""
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||
co.set_paths(browser_path=browser_path, user_data_path=user_data_dir)
|
||||
co.set_argument(f'--profile-directory={active_profile}')
|
||||
|
||||
# Basic options
|
||||
co.set_argument('--no-first-run')
|
||||
co.set_argument('--no-default-browser-check')
|
||||
co.set_argument('--disable-gpu')
|
||||
co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
|
||||
|
||||
# Platform-specific options
|
||||
if sys.platform.startswith('linux'):
|
||||
@@ -330,13 +526,19 @@ class OAuthHandler:
|
||||
# Check if we're on account selection page
|
||||
if "accounts.google.com" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# 获取配置中是否启用 alert 选项
|
||||
config = get_config(self.translator)
|
||||
show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False)
|
||||
|
||||
if show_alert:
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
@@ -378,13 +580,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
token = None
|
||||
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
# Get email from settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
|
||||
@@ -420,7 +616,6 @@ class OAuthHandler:
|
||||
|
||||
if check_usage_limits(usage_text):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
if self._delete_current_account():
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
@@ -459,7 +654,7 @@ class OAuthHandler:
|
||||
|
||||
# Setup browser
|
||||
if not self.setup_browser():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
# Navigate to auth URL
|
||||
@@ -590,11 +785,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
|
||||
# Navigate to settings page
|
||||
@@ -632,7 +823,6 @@ class OAuthHandler:
|
||||
|
||||
if check_usage_limits(usage_text):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
if self._delete_current_account():
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
@@ -658,10 +848,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
# Get email and check usage here too
|
||||
try:
|
||||
@@ -691,19 +878,18 @@ class OAuthHandler:
|
||||
except:
|
||||
return False
|
||||
|
||||
if check_usage_limits(usage_text):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
if self._delete_current_account():
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
return self.handle_google_auth()
|
||||
else:
|
||||
return self.handle_github_auth()
|
||||
if check_usage_limits(usage_text):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
if self._delete_current_account():
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
return self.handle_google_auth()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
|
||||
return self.handle_github_auth()
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
@@ -759,12 +945,10 @@ class OAuthHandler:
|
||||
if name == "WorkosCursorSessionToken":
|
||||
try:
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
|
||||
error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
|
||||
elif name == "cursor_email":
|
||||
email = cookie.get("value")
|
||||
|
||||
@@ -777,11 +961,13 @@ class OAuthHandler:
|
||||
missing.append("email")
|
||||
if not token:
|
||||
missing.append("token")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
|
||||
error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
|
||||
error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def _delete_current_account(self):
|
||||
@@ -823,7 +1009,8 @@ class OAuthHandler:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
|
||||
error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(auth_type, translator=None):
|
||||
|
||||
@@ -8,3 +8,5 @@ pyinstaller
|
||||
DrissionPage>=4.0.0
|
||||
selenium
|
||||
webdriver_manager
|
||||
arabic-reshaper
|
||||
python-bidi
|
||||
|
||||
@@ -12,9 +12,9 @@ import glob
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -30,6 +30,20 @@ EMOJI = {
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user Documents folder path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
else: # Linux
|
||||
# Get actual user's home directory
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
return os.path.join("/home", sudo_user, "Documents")
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
""" Get Cursor related paths"""
|
||||
system = platform.system()
|
||||
@@ -220,8 +234,16 @@ def get_workbench_cursor_path(translator=None) -> str:
|
||||
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
else:
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
|
||||
base_path = config.get('MacPaths', 'cursor_path')
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
|
||||
base_path = config.get('LinuxPaths', 'cursor_path')
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
@@ -334,37 +356,40 @@ def modify_workbench_js(file_path: str, translator=None) -> bool:
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
if sys.platform == "win32":
|
||||
# Define replacement patterns
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
elif sys.platform == "linux":
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
elif sys.platform == "darwin":
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux/Mac 通用按钮替换模式
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
CBadge_old_pattern = r'<div>Pro Trial'
|
||||
CBadge_new_pattern = r'<div>Pro'
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
CToast_old_pattern = r'notifications-toasts'
|
||||
CToast_new_pattern = r'notifications-toasts hidden'
|
||||
|
||||
# Replace content
|
||||
content = content.replace(CButton_old_pattern, CButton_new_pattern)
|
||||
content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
|
||||
content = content.replace(CToast_old_pattern, CToast_new_pattern)
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file
|
||||
backup_path = file_path + ".backup"
|
||||
if os.path.exists(backup_path):
|
||||
os.remove(backup_path)
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
@@ -411,7 +436,10 @@ def modify_main_js(main_path: str, translator) -> bool:
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.old.{timestamp}"
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
@@ -461,7 +489,8 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# Backup file
|
||||
backup_path = main_path + ".bak"
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.bak.{timestamp}"
|
||||
if not os.path.exists(backup_path):
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
@@ -638,8 +667,8 @@ class MachineIDResetter:
|
||||
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
@@ -717,12 +746,10 @@ class MachineIDResetter:
|
||||
with open(self.db_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
|
||||
backup_path = self.db_path + ".bak"
|
||||
if not os.path.exists(backup_path):
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{self.db_path}.bak.{timestamp}"
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
@@ -786,7 +813,8 @@ class MachineIDResetter:
|
||||
|
||||
# Create backup if file exists
|
||||
if os.path.exists(machine_id_path):
|
||||
backup_path = machine_id_path + ".backup"
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{machine_id_path}.backup.{timestamp}"
|
||||
try:
|
||||
shutil.copy2(machine_id_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
|
||||
|
||||
406
restore_machine_id.py
Normal file
406
restore_machine_id.py
Normal file
@@ -0,0 +1,406 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import uuid
|
||||
import hashlib
|
||||
import shutil
|
||||
import sqlite3
|
||||
import platform
|
||||
import re
|
||||
import glob
|
||||
import tempfile
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
import traceback
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# 导入共享函数
|
||||
from reset_machine_manual import get_cursor_machine_id_path, get_user_documents_path
|
||||
|
||||
# 初始化 colorama
|
||||
init()
|
||||
|
||||
# 定义表情符号常量
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
class ConfigError(Exception):
|
||||
"""配置错误异常"""
|
||||
pass
|
||||
|
||||
class MachineIDRestorer:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
|
||||
# 读取配置
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if not os.path.exists(config_file):
|
||||
raise FileNotFoundError(f"Config file not found: {config_file}")
|
||||
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# 根据操作系统获取路径
|
||||
if sys.platform == "win32": # Windows
|
||||
appdata = os.getenv("APPDATA")
|
||||
if appdata is None:
|
||||
raise EnvironmentError("APPDATA Environment Variable Not Set")
|
||||
|
||||
if not config.has_section('WindowsPaths'):
|
||||
raise ConfigError("WindowsPaths section not found in config")
|
||||
|
||||
self.db_path = config.get('WindowsPaths', 'storage_path')
|
||||
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
|
||||
|
||||
elif sys.platform == "darwin": # macOS
|
||||
if not config.has_section('MacPaths'):
|
||||
raise ConfigError("MacPaths section not found in config")
|
||||
|
||||
self.db_path = config.get('MacPaths', 'storage_path')
|
||||
self.sqlite_path = config.get('MacPaths', 'sqlite_path')
|
||||
|
||||
elif sys.platform == "linux": # Linux
|
||||
if not config.has_section('LinuxPaths'):
|
||||
raise ConfigError("LinuxPaths section not found in config")
|
||||
|
||||
self.db_path = config.get('LinuxPaths', 'storage_path')
|
||||
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
|
||||
|
||||
else:
|
||||
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
|
||||
|
||||
def find_backups(self):
|
||||
"""查找可用的备份文件"""
|
||||
db_dir = os.path.dirname(self.db_path)
|
||||
db_name = os.path.basename(self.db_path)
|
||||
|
||||
# 查找格式为 {db_name}.bak.{timestamp} 的文件
|
||||
backup_pattern = f"{db_name}.bak.*"
|
||||
backups = glob.glob(os.path.join(db_dir, backup_pattern))
|
||||
|
||||
# 按创建时间排序(最新的在前)
|
||||
backups.sort(key=os.path.getctime, reverse=True)
|
||||
|
||||
return backups
|
||||
|
||||
def list_backups(self):
|
||||
"""列出所有可用备份"""
|
||||
backups = self.find_backups()
|
||||
|
||||
if not backups:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.no_backups_found')}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.available_backups')}:{Style.RESET_ALL}")
|
||||
for i, backup in enumerate(backups, 1):
|
||||
# 获取备份文件信息
|
||||
timestamp_str = backup.split('.')[-1]
|
||||
try:
|
||||
# 尝试解析时间戳(如果格式为 YYYYmmdd_HHMMSS)
|
||||
timestamp = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
|
||||
date_str = timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except ValueError:
|
||||
date_str = "未知日期"
|
||||
|
||||
# 获取文件大小
|
||||
size = os.path.getsize(backup)
|
||||
size_str = f"{size / 1024:.1f} KB"
|
||||
|
||||
print(f"{i}. {Fore.GREEN}{os.path.basename(backup)}{Style.RESET_ALL} ({date_str}, {size_str})")
|
||||
|
||||
return backups
|
||||
|
||||
def select_backup(self):
|
||||
"""让用户选择要恢复的备份"""
|
||||
backups = self.list_backups()
|
||||
|
||||
if not backups:
|
||||
return None
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"{EMOJI['INFO']} {self.translator.get('restore.select_backup')} (1-{len(backups)}, 0 {self.translator.get('restore.to_cancel')}): ")
|
||||
|
||||
if choice.strip() == '0':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
index = int(choice) - 1
|
||||
if 0 <= index < len(backups):
|
||||
return backups[index]
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.invalid_selection')}{Style.RESET_ALL}")
|
||||
except ValueError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.please_enter_number')}{Style.RESET_ALL}")
|
||||
|
||||
def extract_ids_from_backup(self, backup_path):
|
||||
"""从备份文件中提取机器ID"""
|
||||
try:
|
||||
with open(backup_path, "r", encoding="utf-8") as f:
|
||||
backup_data = json.load(f)
|
||||
|
||||
# 提取需要恢复的ID
|
||||
ids = {
|
||||
"telemetry.devDeviceId": backup_data.get("telemetry.devDeviceId", ""),
|
||||
"telemetry.macMachineId": backup_data.get("telemetry.macMachineId", ""),
|
||||
"telemetry.machineId": backup_data.get("telemetry.machineId", ""),
|
||||
"telemetry.sqmId": backup_data.get("telemetry.sqmId", ""),
|
||||
"storage.serviceMachineId": backup_data.get("storage.serviceMachineId",
|
||||
backup_data.get("telemetry.devDeviceId", ""))
|
||||
}
|
||||
|
||||
# 确保所有ID都存在
|
||||
for key, value in ids.items():
|
||||
if not value:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.missing_id', id=key)}{Style.RESET_ALL}")
|
||||
|
||||
return ids
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.read_backup_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def update_current_file(self, ids):
|
||||
"""更新当前的storage.json文件"""
|
||||
try:
|
||||
if not os.path.exists(self.db_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.current_file_not_found')}: {self.db_path}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 读取当前文件
|
||||
with open(self.db_path, "r", encoding="utf-8") as f:
|
||||
current_data = json.load(f)
|
||||
|
||||
# 创建当前文件的备份
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{self.db_path}.restore_bak.{timestamp}"
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['BACKUP']} {self.translator.get('restore.current_backup_created')}: {backup_path}{Style.RESET_ALL}")
|
||||
|
||||
# 更新ID
|
||||
current_data.update(ids)
|
||||
|
||||
# 保存更新后的文件
|
||||
with open(self.db_path, "w", encoding="utf-8") as f:
|
||||
json.dump(current_data, f, indent=4)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.storage_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def update_sqlite_db(self, ids):
|
||||
"""更新SQLite数据库中的ID"""
|
||||
try:
|
||||
if not os.path.exists(self.sqlite_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_not_found')}: {self.sqlite_path}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_sqlite')}...{Style.RESET_ALL}")
|
||||
|
||||
conn = sqlite3.connect(self.sqlite_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ItemTable (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
for key, value in ids.items():
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO ItemTable (key, value)
|
||||
VALUES (?, ?)
|
||||
""", (key, value))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('restore.updating_pair')}: {key}{Style.RESET_ALL}")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.sqlite_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.sqlite_update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def update_machine_id_file(self, dev_device_id):
|
||||
"""更新machineId文件"""
|
||||
try:
|
||||
machine_id_path = get_cursor_machine_id_path(self.translator)
|
||||
|
||||
# 创建目录(如果不存在)
|
||||
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
|
||||
|
||||
# 备份当前文件(如果存在)
|
||||
if os.path.exists(machine_id_path):
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{machine_id_path}.restore_bak.{timestamp}"
|
||||
try:
|
||||
shutil.copy2(machine_id_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('restore.machine_id_backup_created')}: {backup_path}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.backup_creation_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
# 写入新的ID
|
||||
with open(machine_id_path, "w", encoding="utf-8") as f:
|
||||
f.write(dev_device_id)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.machine_id_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.machine_id_update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def update_system_ids(self, ids):
|
||||
"""更新系统级ID(特定于操作系统)"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.updating_system_ids')}...{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self._update_windows_system_ids(ids)
|
||||
elif sys.platform == "darwin":
|
||||
self._update_macos_system_ids(ids)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _update_windows_system_ids(self, ids):
|
||||
"""更新Windows系统ID"""
|
||||
try:
|
||||
import winreg
|
||||
|
||||
# 更新MachineGuid
|
||||
guid = ids.get("telemetry.devDeviceId", "")
|
||||
if guid:
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Cryptography",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, guid)
|
||||
winreg.CloseKey(key)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_guid_updated')}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
# 更新SQMClient MachineId
|
||||
sqm_id = ids.get("telemetry.sqmId", "")
|
||||
if sqm_id:
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, sqm_id)
|
||||
winreg.CloseKey(key)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.windows_machine_id_updated')}{Style.RESET_ALL}")
|
||||
except FileNotFoundError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('restore.sqm_client_key_not_found')}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.permission_denied')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_windows_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
def _update_macos_system_ids(self, ids):
|
||||
"""更新macOS系统ID"""
|
||||
try:
|
||||
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||
if os.path.exists(uuid_file):
|
||||
mac_id = ids.get("telemetry.macMachineId", "")
|
||||
if mac_id:
|
||||
cmd = f'sudo plutil -replace "UUID" -string "{mac_id}" "{uuid_file}"'
|
||||
result = os.system(cmd)
|
||||
if result == 0:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.macos_platform_uuid_updated')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.update_macos_system_ids_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
def restore_machine_ids(self):
|
||||
"""恢复之前备份的机器ID"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('restore.starting')}...{Style.RESET_ALL}")
|
||||
|
||||
# 选择要恢复的备份
|
||||
backup_path = self.select_backup()
|
||||
if not backup_path:
|
||||
return False
|
||||
|
||||
# 从备份中提取ID
|
||||
ids = self.extract_ids_from_backup(backup_path)
|
||||
if not ids:
|
||||
return False
|
||||
|
||||
# 显示将要恢复的ID
|
||||
print(f"\n{Fore.CYAN}{self.translator.get('restore.ids_to_restore')}:{Style.RESET_ALL}")
|
||||
for key, value in ids.items():
|
||||
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
|
||||
|
||||
# 确认恢复
|
||||
confirm = input(f"\n{EMOJI['WARNING']} {self.translator.get('restore.confirm')} (y/n): ")
|
||||
if confirm.lower() != 'y':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('restore.operation_cancelled')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 更新当前文件
|
||||
if not self.update_current_file(ids):
|
||||
return False
|
||||
|
||||
# 更新SQLite数据库
|
||||
self.update_sqlite_db(ids)
|
||||
|
||||
# 更新machineId文件
|
||||
self.update_machine_id_file(ids.get("telemetry.devDeviceId", ""))
|
||||
|
||||
# 更新系统ID
|
||||
self.update_system_ids(ids)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('restore.success')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('restore.process_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
"""恢复机器ID的主函数"""
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
return False
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('restore.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
restorer = MachineIDRestorer(translator)
|
||||
restorer.restore_machine_ids()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('restore.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
@@ -11,7 +11,6 @@ import tempfile
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
import glob
|
||||
@@ -26,10 +25,24 @@ EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "<EFBFBD><EFBFBD>",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user Documents folder path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
else: # Linux
|
||||
# Get actual user's home directory
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
return os.path.join("/home", sudo_user, "Documents")
|
||||
return os.path.join(os.path.expanduser("~"), "Documents")
|
||||
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
""" Get Cursor related paths"""
|
||||
system = platform.system()
|
||||
@@ -111,7 +124,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
# For Linux, try to find the first existing path if the configured one doesn't exist
|
||||
if system == "Linux" and not os.path.exists(base_path):
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
if os.path.exists(path):
|
||||
base_path = path
|
||||
# Update config with the found path
|
||||
config.set(section, 'cursor_path', path)
|
||||
@@ -178,15 +191,22 @@ def get_cursor_machine_id_path(translator=None) -> str:
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
|
||||
@@ -210,7 +230,20 @@ def get_workbench_cursor_path(translator=None) -> str:
|
||||
return main_path
|
||||
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
|
||||
|
||||
base_path = paths_map[system]["base"]
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'):
|
||||
base_path = config.get('MacPaths', 'cursor_path')
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'):
|
||||
base_path = config.get('LinuxPaths', 'cursor_path')
|
||||
|
||||
# Get the main path for non-Linux systems or if Linux path wasn't found in the loop
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
if not os.path.exists(main_path):
|
||||
@@ -529,7 +562,7 @@ class MachineIDResetter:
|
||||
self.db_path = config.get('LinuxPaths', 'storage_path')
|
||||
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
|
||||
|
||||
else:
|
||||
else:
|
||||
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
|
||||
|
||||
# Save any changes to config file
|
||||
@@ -811,4 +844,4 @@ def run(translator=None):
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
run(main_translator)
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Turnstile Patcher",
|
||||
"version": "2.1",
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": [
|
||||
"./script.js"
|
||||
],
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"world": "MAIN"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
function qSelector(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
var solved = false;
|
||||
var checkBoxClicked = false;
|
||||
var requestCount = 0;
|
||||
const MAX_ATTEMPTS = 1;
|
||||
const CHECK_BOX = ".recaptcha-checkbox-border";
|
||||
const AUDIO_BUTTON = "#recaptcha-audio-button";
|
||||
const PLAY_BUTTON = ".rc-audiochallenge-play-button .rc-button-default";
|
||||
const AUDIO_SOURCE = "#audio-source";
|
||||
const IMAGE_SELECT = "#rc-imageselect";
|
||||
const RESPONSE_FIELD = ".rc-audiochallenge-response-field";
|
||||
const AUDIO_ERROR_MESSAGE = ".rc-audiochallenge-error-message";
|
||||
const AUDIO_RESPONSE = "#audio-response";
|
||||
const RELOAD_BUTTON = "#recaptcha-reload-button";
|
||||
const RECAPTCHA_STATUS = "#recaptcha-accessible-status";
|
||||
const DOSCAPTCHA = ".rc-doscaptcha-body";
|
||||
const VERIFY_BUTTON = "#recaptcha-verify-button";
|
||||
var recaptchaInitialStatus = qSelector(RECAPTCHA_STATUS) ? qSelector(RECAPTCHA_STATUS).innerText : ""
|
||||
function isHidden(el) {
|
||||
return(el.offsetParent === null)
|
||||
}
|
||||
try {
|
||||
if(!checkBoxClicked && qSelector(CHECK_BOX) && !isHidden(qSelector(CHECK_BOX))) {
|
||||
//console.log("checkbox clicked");
|
||||
qSelector(CHECK_BOX).click();
|
||||
checkBoxClicked = true;
|
||||
}
|
||||
//Check if the captcha is solved
|
||||
if(qSelector(RECAPTCHA_STATUS) && (qSelector(RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
|
||||
solved = true;
|
||||
console.log("SOLVED");
|
||||
}
|
||||
if(requestCount > MAX_ATTEMPTS) {
|
||||
console.log("Attempted Max Retries. Stopping the solver");
|
||||
solved = true;
|
||||
}
|
||||
//Stop solving when Automated queries message is shown
|
||||
if(qSelector(DOSCAPTCHA) && qSelector(DOSCAPTCHA).innerText.length > 0) {
|
||||
console.log("Automated Queries Detected");
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err.message);
|
||||
console.log("An error occurred while solving. Stopping the solver.");
|
||||
}
|
||||
})();
|
||||
@@ -1,12 +0,0 @@
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
// old method wouldn't work on 4k screens
|
||||
|
||||
let screenX = getRandomInt(800, 1200);
|
||||
let screenY = getRandomInt(400, 600);
|
||||
|
||||
Object.defineProperty(MouseEvent.prototype, 'screenX', { value: screenX });
|
||||
|
||||
Object.defineProperty(MouseEvent.prototype, 'screenY', { value: screenY });
|
||||
181
utils.py
181
utils.py
@@ -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"""
|
||||
|
||||
Reference in New Issue
Block a user