mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-26 21:50:39 +03:00
Compare commits
205 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 | ||
|
|
07bed23848 | ||
|
|
c12c52269e | ||
|
|
8d279ce972 | ||
|
|
849ec5ea8d | ||
|
|
c48c35fd09 | ||
|
|
a361d2fe6d | ||
|
|
74be8a0a77 | ||
|
|
bce205b252 | ||
|
|
8fcf3b62cf | ||
|
|
eeaee073cf | ||
|
|
2f353da381 | ||
|
|
efd1417407 | ||
|
|
2cc7cce283 | ||
|
|
4c30afcd7f | ||
|
|
bfb2f5e95a | ||
|
|
35e01edf9c | ||
|
|
df9e88131b | ||
|
|
8a4d22103e | ||
|
|
1244d86994 | ||
|
|
a2833fcbd4 | ||
|
|
b3e7c101d3 | ||
|
|
0a779063bf | ||
|
|
c96433e446 | ||
|
|
5604f5073d | ||
|
|
3cad68be36 | ||
|
|
d8799cdf2e | ||
|
|
3e5c22b816 | ||
|
|
893c3f8a52 | ||
|
|
edb74e21be | ||
|
|
be52ce98a6 | ||
|
|
163748fd96 | ||
|
|
f888b8c90c | ||
|
|
3c12561997 | ||
|
|
80aab8741f | ||
|
|
2595c4c95d | ||
|
|
f18e65f391 | ||
|
|
671307b53b | ||
|
|
f58bb70d3a | ||
|
|
5bfe653a92 | ||
|
|
a66a0e5395 | ||
|
|
ea44218a8a | ||
|
|
fdc8317380 | ||
|
|
523daea54e | ||
|
|
5a4e32f3f4 | ||
|
|
d91485ec75 | ||
|
|
51cef9c2b2 | ||
|
|
6e6180e331 | ||
|
|
e2d7c1c496 | ||
|
|
a56b978669 | ||
|
|
9793b91bc7 | ||
|
|
735dd8c1eb | ||
|
|
e5d192efcb | ||
|
|
a4d5b0e467 | ||
|
|
d7b056b339 | ||
|
|
bd152be4e8 | ||
|
|
172d9fb4bb |
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
|
||||
|
||||
|
||||
75
.github/ISSUE_TEMPLATE/cn_feature_request.yml
vendored
Normal file
75
.github/ISSUE_TEMPLATE/cn_feature_request.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: 💡 功能建议
|
||||
description: 为这个项目提出新的想法
|
||||
title: "[功能建议]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您抽出时间提出新功能建议!
|
||||
请确保填写以下所有部分。
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: 功能描述
|
||||
description: 清晰简洁地描述您想要实现的功能。
|
||||
placeholder: "我希望能够..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem-solution
|
||||
attributes:
|
||||
label: 问题和解决方案
|
||||
description: 描述您试图解决的问题,以及这个功能将如何帮助解决。
|
||||
placeholder: "目前,当我尝试..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 考虑过的替代方案
|
||||
description: 描述您考虑过的任何替代解决方案或功能。
|
||||
placeholder: "我考虑过..."
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: 优先级
|
||||
description: 这个功能对您来说有多重要?
|
||||
options:
|
||||
- 高(对我的工作流程至关重要)
|
||||
- 中(会很有帮助)
|
||||
- 低(有更好)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: 影响范围
|
||||
description: 有多少用户会从这个功能中受益?
|
||||
options:
|
||||
- 所有用户
|
||||
- 大多数用户
|
||||
- 部分用户
|
||||
- 仅我个人
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: technical-details
|
||||
attributes:
|
||||
label: 技术细节
|
||||
description: 您想分享的任何技术考虑或实现细节。
|
||||
placeholder: "这个功能可以通过..."
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: 截图
|
||||
description: 如果适用,添加截图以帮助解释您的功能建议。
|
||||
placeholder: "您可以在此处拖放截图..."
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: 行为准则
|
||||
description: 提交此问题即表示您同意遵守本项目的行为准则
|
||||
options:
|
||||
- label: 我同意遵守本项目的行为准则
|
||||
required: true
|
||||
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
|
||||
|
||||
|
||||
75
.github/ISSUE_TEMPLATE/en_feature_request.yml
vendored
Normal file
75
.github/ISSUE_TEMPLATE/en_feature_request.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: 💡 Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "[Feature]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to suggest a new feature!
|
||||
Please make sure to fill out all the sections below.
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
description: A clear and concise description of what you want to happen.
|
||||
placeholder: "I would like to..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem-solution
|
||||
attributes:
|
||||
label: Problem & Solution
|
||||
description: Describe the problem you're trying to solve and how this feature would help.
|
||||
placeholder: "Currently, when I try to..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: Describe any alternative solutions or features you've considered.
|
||||
placeholder: "I've considered..."
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How important is this feature to you?
|
||||
options:
|
||||
- High (Critical for my workflow)
|
||||
- Medium (Would be very helpful)
|
||||
- Low (Nice to have)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: Impact
|
||||
description: How many users would benefit from this feature?
|
||||
options:
|
||||
- All users
|
||||
- Most users
|
||||
- Some users
|
||||
- Just me
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: technical-details
|
||||
attributes:
|
||||
label: Technical Details
|
||||
description: Any technical considerations or implementation details you'd like to share.
|
||||
placeholder: "The feature could be implemented by..."
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your feature.
|
||||
placeholder: "You can drag and drop screenshots here..."
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow this project's Code of Conduct
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
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.02'
|
||||
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 }}
|
||||
154
CHANGELOG.md
154
CHANGELOG.md
@@ -1,5 +1,157 @@
|
||||
# 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 | 添加繞過的多語言支持
|
||||
3. MSG: Free & free trial accounts can no longer use chat with premium models on Cursor Version 0.45 or less. Please upgrade to Pro or use Cursor Version 0.46 or later. Install Cursor at https://www.cursor.com/downloads or update from within the editor.
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.06
|
||||
1. Add: Google Account Deletion Feature | 添加 Google 账号删除功能
|
||||
2. Update: Menu with new account deletion option | 更新菜单添加账号删除选项
|
||||
3. Add: Multilanguage support for account deletion | 添加账号删除功能的多语言支持
|
||||
4. Fix: Improve usage limits check and tuple index error | 修复使用限制检查和元组索引错误
|
||||
5. Fix: bug in disable cursor auto update | 修复禁用 Cursor 自动更新的错误
|
||||
6. Fix: Linux-appimage | 修复 Linux-appimage 问题
|
||||
7. Add: Support for custom Cursor installation paths on Windows | 添加 Windows 系统下自定义 Cursor 安装路径支持
|
||||
8. Add: Chrome profile selection feature | 添加 Chrome 配置文件选择功能
|
||||
9. Fix: improve account usage limit detection | 修復賬號檢測
|
||||
10. Fix: For custom Chrome Installations | 修復自定義chrome遊覽器安裝
|
||||
|
||||
## v1.8.05
|
||||
1. Fix: Linux Path Not Found | 修復linuxpath問題
|
||||
2. Add: support for detecting both 150/150 and 50/50 usage limits | 添加偵測50 或者150的使用量
|
||||
3. Improve: usage parsing and validation | 檢測使用量
|
||||
|
||||
## v1.8.04
|
||||
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
|
||||
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
|
||||
3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.03
|
||||
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
@@ -216,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"
|
||||
}
|
||||
94
README.md
94
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/new_2025-03-22_19-53-10.png" alt="new" width="400" 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,15 +51,7 @@ 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>
|
||||
|
||||
@@ -58,24 +59,32 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
|
||||
|
||||
## 💻 System Support | 系統支持
|
||||
|
||||
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|
||||
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
|
||||
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
|
||||
| Linux | x64 | ✅ | Linux | x86 | ✅ |
|
||||
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
|
||||
| Operating System | Architecture | Supported |
|
||||
|------------------|-------------------|-----------|
|
||||
| Windows | x64, x86 | ✅ |
|
||||
| macOS | Intel, Apple Silicon | ✅ |
|
||||
| Linux | x64, x86, ARM64 | ✅ |
|
||||
|
||||
## 👀 How to use | 如何使用
|
||||
|
||||
<details open>
|
||||
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
### **Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
**Windows**
|
||||
### **Archlinux**
|
||||
|
||||
Install via [AUR](https://aur.archlinux.org/packages/cursor-free-vip-git)
|
||||
|
||||
```bash
|
||||
yay -S cursor-free-vip-git
|
||||
```
|
||||
|
||||
### **Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
||||
@@ -86,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
|
||||
@@ -164,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>
|
||||
|
||||
144
block_domain.txt
144
block_domain.txt
@@ -1,22 +1,134 @@
|
||||
oakon.com
|
||||
famamail.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
|
||||
teihu.com
|
||||
raleigh-construction.com
|
||||
pastryofistanbul.com
|
||||
jetable.org
|
||||
kaspop.com
|
||||
letthemeatspam.com
|
||||
linshiyouxiang.net
|
||||
Mohmal.com
|
||||
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
|
||||
ikomail.com
|
||||
ofanda.com
|
||||
pusmail.com
|
||||
ikomail.com
|
||||
mailpull.com
|
||||
drewzen.com
|
||||
begemail.com
|
||||
dugmail.com
|
||||
raleigh-construction.com
|
||||
rcpt.at
|
||||
safemail.link
|
||||
sendspamhere.com
|
||||
sharklasers.com
|
||||
shortmail.net
|
||||
solerbe.net
|
||||
corhash.net
|
||||
mailshou.com
|
||||
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
|
||||
20
build.spec
20
build.spec
@@ -23,26 +23,14 @@ 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', '.'),
|
||||
('.env', '.'),
|
||||
('block_domain.txt', '.')
|
||||
('utils.py', '.'),
|
||||
('.env', '.')
|
||||
],
|
||||
hiddenimports=[
|
||||
'cursor_auth',
|
||||
'reset_machine_manual',
|
||||
'new_signup',
|
||||
'new_tempemail',
|
||||
'quit_cursor',
|
||||
'cursor_register_manual'
|
||||
'utils'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
@@ -65,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)
|
||||
160
bypass_version.py
Normal file
160
bypass_version.py
Normal file
@@ -0,0 +1,160 @@
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import platform
|
||||
import configparser
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
import sys
|
||||
import traceback
|
||||
from utils import get_user_documents_path
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'INFO': 'ℹ️',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WARNING': '⚠️',
|
||||
'FILE': '📄',
|
||||
'BACKUP': '💾',
|
||||
'RESET': '🔄',
|
||||
'VERSION': '🏷️'
|
||||
}
|
||||
|
||||
def get_product_json_path(translator=None):
|
||||
"""Get Cursor product.json 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)
|
||||
|
||||
if system == "Windows":
|
||||
localappdata = os.environ.get("LOCALAPPDATA")
|
||||
if not localappdata:
|
||||
raise OSError(translator.get('bypass.localappdata_not_found') if translator else "LOCALAPPDATA environment variable not found")
|
||||
|
||||
product_json_path = os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json")
|
||||
|
||||
# Check if path exists in config
|
||||
if 'WindowsPaths' in config and 'cursor_path' in config['WindowsPaths']:
|
||||
cursor_path = config.get('WindowsPaths', 'cursor_path')
|
||||
product_json_path = os.path.join(cursor_path, "product.json")
|
||||
|
||||
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
|
||||
possible_paths = [
|
||||
"/opt/Cursor/resources/app/product.json",
|
||||
"/usr/share/cursor/resources/app/product.json",
|
||||
"/usr/lib/cursor/app/product.json"
|
||||
]
|
||||
|
||||
# Add extracted AppImage paths
|
||||
extracted_usr_paths = os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app/product.json")
|
||||
if os.path.exists(extracted_usr_paths):
|
||||
possible_paths.append(extracted_usr_paths)
|
||||
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
product_json_path = path
|
||||
break
|
||||
else:
|
||||
raise OSError(translator.get('bypass.product_json_not_found') if translator else "product.json not found in common Linux paths")
|
||||
|
||||
else:
|
||||
raise OSError(translator.get('bypass.unsupported_os', system=system) if translator else f"Unsupported operating system: {system}")
|
||||
|
||||
if not os.path.exists(product_json_path):
|
||||
raise OSError(translator.get('bypass.file_not_found', path=product_json_path) if translator else f"File not found: {product_json_path}")
|
||||
|
||||
return product_json_path
|
||||
|
||||
def compare_versions(version1, version2):
|
||||
"""Compare two version strings"""
|
||||
v1_parts = [int(x) for x in version1.split('.')]
|
||||
v2_parts = [int(x) for x in version2.split('.')]
|
||||
|
||||
for i in range(max(len(v1_parts), len(v2_parts))):
|
||||
v1 = v1_parts[i] if i < len(v1_parts) else 0
|
||||
v2 = v2_parts[i] if i < len(v2_parts) else 0
|
||||
if v1 < v2:
|
||||
return -1
|
||||
elif v1 > v2:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
def bypass_version(translator=None):
|
||||
"""Bypass Cursor version check by modifying product.json"""
|
||||
try:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('bypass.starting') if translator else 'Starting Cursor version bypass...'}{Style.RESET_ALL}")
|
||||
|
||||
# Get product.json path
|
||||
product_json_path = get_product_json_path(translator)
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {translator.get('bypass.found_product_json', path=product_json_path) if translator else f'Found product.json: {product_json_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check file permissions
|
||||
if not os.access(product_json_path, os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.no_write_permission', path=product_json_path) if translator else f'No write permission for file: {product_json_path}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Read product.json
|
||||
try:
|
||||
with open(product_json_path, "r", encoding="utf-8") as f:
|
||||
product_data = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.read_failed', error=str(e)) if translator else f'Failed to read product.json: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Get current version
|
||||
current_version = product_data.get("version", "0.0.0")
|
||||
print(f"{Fore.CYAN}{EMOJI['VERSION']} {translator.get('bypass.current_version', version=current_version) if translator else f'Current version: {current_version}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if version needs to be modified
|
||||
if compare_versions(current_version, "0.46.0") < 0:
|
||||
# Create backup
|
||||
timestamp = time.strftime("%Y%m%d%H%M%S")
|
||||
backup_path = f"{product_json_path}.{timestamp}"
|
||||
shutil.copy2(product_json_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['BACKUP']} {translator.get('bypass.backup_created', path=backup_path) if translator else f'Backup created: {backup_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Modify version
|
||||
new_version = "0.48.7"
|
||||
product_data["version"] = new_version
|
||||
|
||||
# Save modified product.json
|
||||
try:
|
||||
with open(product_json_path, "w", encoding="utf-8") as f:
|
||||
json.dump(product_data, f, indent=2)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('bypass.version_updated', old=current_version, new=new_version) if translator else f'Version updated from {current_version} to {new_version}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.write_failed', error=str(e)) if translator else f'Failed to write product.json: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.no_update_needed', version=current_version) if translator else f'No update needed. Current version {current_version} is already >= 0.46.0'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.bypass_failed', error=str(e)) if translator else f'Version bypass failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.stack_trace') if translator else 'Stack trace'}: {traceback.format_exc()}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function"""
|
||||
return bypass_version(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
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()
|
||||
229
config.py
229
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +124,8 @@ def setup_config(translator=None):
|
||||
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
|
||||
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
|
||||
'updater_path': os.path.join(localappdata, "cursor-updater"),
|
||||
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml")
|
||||
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml"),
|
||||
'product_json_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json")
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True)
|
||||
@@ -78,35 +137,53 @@ def setup_config(translator=None):
|
||||
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
|
||||
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
|
||||
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml",
|
||||
'product_json_path': "/Applications/Cursor.app/Contents/Resources/app/product.json"
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "linux":
|
||||
# Get the actual user's home directory, handling both sudo and normal cases
|
||||
current_user = os.getenv('USER') or os.getenv('USERNAME') or os.getenv('SUDO_USER')
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
current_user = sudo_user if sudo_user else (os.getenv('USER') or os.getenv('USERNAME'))
|
||||
|
||||
if not current_user:
|
||||
current_user = os.path.expanduser('~').split('/')[-1]
|
||||
|
||||
actual_home = f"/home/{current_user}"
|
||||
# Handle sudo case
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
root_home = "/root"
|
||||
else:
|
||||
actual_home = f"/home/{current_user}"
|
||||
root_home = None
|
||||
|
||||
if not os.path.exists(actual_home):
|
||||
actual_home = os.path.expanduser("~")
|
||||
|
||||
# Define base config directory
|
||||
config_base = os.path.join(actual_home, ".config")
|
||||
|
||||
# Try both "Cursor" and "cursor" directory names
|
||||
# Try both "Cursor" and "cursor" directory names in both user and root locations
|
||||
cursor_dir = None
|
||||
for dir_name in ["Cursor", "cursor"]:
|
||||
test_dir = os.path.join(config_base, dir_name)
|
||||
if os.path.exists(test_dir):
|
||||
cursor_dir = test_dir
|
||||
possible_paths = [
|
||||
os.path.join(config_base, "Cursor"),
|
||||
os.path.join(config_base, "cursor"),
|
||||
os.path.join(root_home, ".config", "Cursor") if root_home else None,
|
||||
os.path.join(root_home, ".config", "cursor") if root_home else None
|
||||
]
|
||||
|
||||
for path in possible_paths:
|
||||
if path and os.path.exists(path):
|
||||
cursor_dir = path
|
||||
break
|
||||
|
||||
if not cursor_dir:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base) if translator else f'Neither Cursor nor cursor directory found in {config_base}'}{Style.RESET_ALL}")
|
||||
if root_home:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.also_checked', path=f'{root_home}/.config') if translator else f'Also checked {root_home}/.config'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
# Define Linux paths using the found cursor directory
|
||||
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
|
||||
@@ -116,46 +193,50 @@ def setup_config(translator=None):
|
||||
try:
|
||||
# Check storage directory
|
||||
if storage_dir and not os.path.exists(storage_dir):
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir) if translator else f'Storage directory not found: {storage_dir}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
# Check storage.json with more detailed verification
|
||||
if storage_path and os.path.exists(storage_path):
|
||||
# Get file stats
|
||||
try:
|
||||
stat = os.stat(storage_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', stat.st_size)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', oct(stat.st_mode & 0o777))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', stat.st_uid)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', stat.st_gid)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path) if translator else f'Storage file found: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', size=stat.st_size) if translator else f'File size: {stat.st_size} bytes'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', permissions=oct(stat.st_mode & 0o777)) if translator else f'File permissions: {oct(stat.st_mode & 0o777)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', owner=stat.st_uid) if translator else f'File owner: {stat.st_uid}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', group=stat.st_gid) if translator else f'File group: {stat.st_gid}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e)) if translator else f'Error getting file stats: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if file is readable and writable
|
||||
if not os.access(storage_path, os.R_OK | os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', current_user=current_user, storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path) if translator else f'Permission denied: {storage_path}'}{Style.RESET_ALL}")
|
||||
if sudo_user:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {sudo_user}:{sudo_user} {storage_path}') if translator else f'Try running: chown {sudo_user}:{sudo_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {current_user}:{current_user} {storage_path}') if translator else f'Try running: chown {current_user}:{current_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
|
||||
|
||||
# Try to read the file to verify it's not corrupted
|
||||
try:
|
||||
with open(storage_path, 'r') as f:
|
||||
content = f.read()
|
||||
if not content.strip():
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path) if translator else f'Storage file is empty: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted, please reinstall Cursor'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data') if translator else 'Storage file is valid and contains data'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e)) if translator else f'Error reading storage file: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted. Please reinstall Cursor'}{Style.RESET_ALL}")
|
||||
elif storage_path:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path) if translator else f'Storage file not found: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e)) if translator else f'Error checking Linux paths: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Define all paths using the found cursor directory
|
||||
default_config['LinuxPaths'] = {
|
||||
@@ -164,7 +245,8 @@ def setup_config(translator=None):
|
||||
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
|
||||
'cursor_path': get_linux_cursor_path(),
|
||||
'updater_path': os.path.join(config_base, "cursor-updater"),
|
||||
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else ""
|
||||
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else "",
|
||||
'product_json_path': os.path.join(cursor_dir, "resources/app/product.json") if cursor_dir else ""
|
||||
}
|
||||
|
||||
# Read existing configuration and merge
|
||||
@@ -181,13 +263,13 @@ def setup_config(translator=None):
|
||||
config.set(section, option, str(value))
|
||||
config_modified = True
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.config_option_added', option=f'{section}.{option}') if translator else f'Config option added: {section}.{option}'}{Style.RESET_ALL}")
|
||||
|
||||
if config_modified:
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_updated')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_updated') if translator else 'Config updated'}{Style.RESET_ALL}")
|
||||
else:
|
||||
for section, options in default_config.items():
|
||||
config.add_section(section)
|
||||
@@ -197,31 +279,31 @@ def setup_config(translator=None):
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.config_created', config_file=config_file) if translator else f'Config created: {config_file}'}{Style.RESET_ALL}")
|
||||
|
||||
return config
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.config_setup_error', error=str(e)) if translator else f'Error setting up config: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def print_config(config, translator=None):
|
||||
"""Print configuration in a readable format"""
|
||||
if not config:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available') if translator else 'Configuration not available'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration')}:{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration') if translator else 'Configuration'}:{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
for section in config.sections():
|
||||
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
|
||||
for key, value in config.items(section):
|
||||
# 对布尔值进行特殊处理,使其显示为彩色
|
||||
if value.lower() in ('true', 'yes', 'on', '1'):
|
||||
value_display = f"{Fore.GREEN}{translator.get('config.enabled')}{Style.RESET_ALL}"
|
||||
value_display = f"{Fore.GREEN}{translator.get('config.enabled') if translator else 'Enabled'}{Style.RESET_ALL}"
|
||||
elif value.lower() in ('false', 'no', 'off', '0'):
|
||||
value_display = f"{Fore.RED}{translator.get('config.disabled')}{Style.RESET_ALL}"
|
||||
value_display = f"{Fore.RED}{translator.get('config.disabled') if translator else 'Disabled'}{Style.RESET_ALL}"
|
||||
else:
|
||||
value_display = value
|
||||
|
||||
@@ -229,10 +311,63 @@ def print_config(config, translator=None):
|
||||
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory')}: {config_dir}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory') if translator else 'Config Directory'}: {config_dir}{Style.RESET_ALL}")
|
||||
|
||||
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
|
||||
|
||||
386
delete_cursor_google.py
Normal file
386
delete_cursor_google.py
Normal file
@@ -0,0 +1,386 @@
|
||||
from oauth_auth import OAuthHandler
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
import sys
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'DELETE': '🗑️',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
class CursorGoogleAccountDeleter(OAuthHandler):
|
||||
def __init__(self, translator=None):
|
||||
super().__init__(translator, auth_type='google')
|
||||
|
||||
def delete_google_account(self):
|
||||
"""Delete Cursor account using Google OAuth"""
|
||||
try:
|
||||
# Setup browser and select profile
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.starting_process') if self.translator else 'Starting account deletion process...'}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate to Cursor auth page - using the same URL as in registration
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(2)
|
||||
|
||||
# Click Google auth button using same selectors as in registration
|
||||
selectors = [
|
||||
"//a[contains(@href,'GoogleOAuth')]",
|
||||
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
|
||||
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
|
||||
]
|
||||
|
||||
auth_btn = None
|
||||
for selector in selectors:
|
||||
try:
|
||||
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
|
||||
if auth_btn:
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not auth_btn:
|
||||
raise Exception(self.translator.get('account_delete.google_button_not_found') if self.translator else "Google login button not found")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.logging_in') if self.translator else 'Logging in with Google...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
|
||||
# Wait for authentication to complete using a more robust method
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('account_delete.waiting_for_auth', fallback='Waiting for Google authentication...')}{Style.RESET_ALL}")
|
||||
|
||||
# Dynamic wait for authentication
|
||||
max_wait_time = 120 # Increase maximum wait time to 120 seconds
|
||||
start_time = time.time()
|
||||
check_interval = 3 # Check every 3 seconds
|
||||
google_account_alert_shown = False # Track if we've shown the alert already
|
||||
|
||||
while time.time() - start_time < max_wait_time:
|
||||
current_url = self.browser.url
|
||||
|
||||
# If we're already on the settings or dashboard page, we're successful
|
||||
if "/dashboard" in current_url or "/settings" in current_url or "cursor.com" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.login_successful') if self.translator else 'Login successful'}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
# If we're on Google accounts page or accounts.google.com, wait for user selection
|
||||
if "accounts.google.com" in current_url:
|
||||
# Only show the alert once to avoid spamming
|
||||
if not google_account_alert_shown:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.select_google_account', fallback='Please select your Google account...')}{Style.RESET_ALL}")
|
||||
# Alert to indicate user action needed
|
||||
try:
|
||||
self.browser.run_js("""
|
||||
alert('Please select your Google account to continue with Cursor authentication');
|
||||
""")
|
||||
google_account_alert_shown = True # Mark that we've shown the alert
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# Sleep before checking again
|
||||
time.sleep(check_interval)
|
||||
else:
|
||||
# If the loop completed without breaking, it means we hit the timeout
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.auth_timeout', fallback='Authentication timeout, continuing anyway...')}{Style.RESET_ALL}")
|
||||
|
||||
# Check current URL to determine next steps
|
||||
current_url = self.browser.url
|
||||
|
||||
# If we're already on the settings page, no need to navigate
|
||||
if "/settings" in current_url and "cursor.com" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.already_on_settings', fallback='Already on settings page')}{Style.RESET_ALL}")
|
||||
# If we're on the dashboard or any Cursor page but not settings, navigate to settings
|
||||
elif "cursor.com" in current_url or "authenticator.cursor.sh" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.navigating_to_settings', fallback='Navigating to settings page...')}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
# If we're still on Google auth or somewhere else, try directly going to settings
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.login_redirect_failed', fallback='Login redirection failed, trying direct navigation...')}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
|
||||
# Wait for the settings page to load
|
||||
time.sleep(3) # Reduced from 5 seconds
|
||||
|
||||
# First look for the email element to confirm we're logged in
|
||||
try:
|
||||
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||
if email_element:
|
||||
email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.found_email', email=email, fallback=f'Found email: {email}')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.email_not_found', error=str(e), fallback=f'Email not found: {str(e)}')}{Style.RESET_ALL}")
|
||||
|
||||
# Click on "Advanced" tab or dropdown - keep only the successful approach
|
||||
advanced_found = False
|
||||
|
||||
# Direct JavaScript querySelector approach that worked according to logs
|
||||
try:
|
||||
advanced_element_js = self.browser.run_js("""
|
||||
// Try to find the Advanced dropdown using querySelector with the exact classes
|
||||
let advancedElement = document.querySelector('div.mb-0.flex.cursor-pointer.items-center.text-xs:not([style*="display: none"])');
|
||||
|
||||
// If not found, try a more general approach
|
||||
if (!advancedElement) {
|
||||
const allDivs = document.querySelectorAll('div');
|
||||
for (const div of allDivs) {
|
||||
if (div.textContent.includes('Advanced') &&
|
||||
div.className.includes('mb-0') &&
|
||||
div.className.includes('flex') &&
|
||||
div.className.includes('cursor-pointer')) {
|
||||
advancedElement = div;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Click the element if found
|
||||
if (advancedElement) {
|
||||
advancedElement.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
""")
|
||||
|
||||
if advanced_element_js:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.advanced_tab_clicked', fallback='Found and clicked Advanced using direct JavaScript selector')}{Style.RESET_ALL}")
|
||||
advanced_found = True
|
||||
time.sleep(1) # Reduced from 2 seconds
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.advanced_tab_error', error=str(e), fallback='JavaScript querySelector approach failed: {str(e)}')}{Style.RESET_ALL}")
|
||||
|
||||
if not advanced_found:
|
||||
# Fallback to direct URL navigation which is faster and more reliable
|
||||
try:
|
||||
self.browser.get("https://www.cursor.com/settings?tab=advanced")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('account_delete.direct_advanced_navigation', fallback='Trying direct navigation to advanced tab')}{Style.RESET_ALL}")
|
||||
advanced_found = True
|
||||
except:
|
||||
raise Exception(self.translator.get('account_delete.advanced_tab_not_found') if self.translator else "Advanced option not found after multiple attempts")
|
||||
|
||||
# Wait for dropdown/tab content to load
|
||||
time.sleep(2) # Reduced from 4 seconds
|
||||
|
||||
# Find and click the "Delete Account" button
|
||||
delete_button_found = False
|
||||
|
||||
# Simplified approach for delete button based on what worked
|
||||
delete_button_selectors = [
|
||||
'xpath://button[contains(., "Delete Account")]',
|
||||
'xpath://button[text()="Delete Account"]',
|
||||
'xpath://div[contains(text(), "Delete Account")]',
|
||||
'xpath://button[contains(text(), "Delete") and contains(text(), "Account")]'
|
||||
]
|
||||
|
||||
for selector in delete_button_selectors:
|
||||
try:
|
||||
delete_button = self.browser.ele(selector, timeout=2)
|
||||
if delete_button:
|
||||
delete_button.click()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.delete_button_clicked') if self.translator else 'Clicked on Delete Account button'}{Style.RESET_ALL}")
|
||||
delete_button_found = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not delete_button_found:
|
||||
raise Exception(self.translator.get('account_delete.delete_button_not_found') if self.translator else "Delete Account button not found")
|
||||
|
||||
# Wait for confirmation dialog to appear
|
||||
time.sleep(2)
|
||||
|
||||
# Check if we need to input "Delete" at all - some modals might not require it
|
||||
input_required = True
|
||||
try:
|
||||
# Try detecting if the DELETE button is already enabled
|
||||
delete_button_enabled = self.browser.run_js("""
|
||||
const buttons = Array.from(document.querySelectorAll('button'));
|
||||
const deleteButtons = buttons.filter(btn =>
|
||||
btn.textContent.trim() === 'DELETE' ||
|
||||
btn.textContent.trim() === 'Delete'
|
||||
);
|
||||
|
||||
if (deleteButtons.length > 0) {
|
||||
return !deleteButtons.some(btn => btn.disabled);
|
||||
}
|
||||
return false;
|
||||
""")
|
||||
|
||||
if delete_button_enabled:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} DELETE button appears to be enabled already. Input may not be required.{Style.RESET_ALL}")
|
||||
input_required = False
|
||||
except:
|
||||
pass
|
||||
|
||||
# Type "Delete" in the confirmation input - only if required
|
||||
delete_input_found = False
|
||||
|
||||
if input_required:
|
||||
# Try common selectors for the input field
|
||||
delete_input_selectors = [
|
||||
'xpath://input[@placeholder="Delete"]',
|
||||
'xpath://div[contains(@class, "modal")]//input',
|
||||
'xpath://input',
|
||||
'css:input'
|
||||
]
|
||||
|
||||
for selector in delete_input_selectors:
|
||||
try:
|
||||
delete_input = self.browser.ele(selector, timeout=3)
|
||||
if delete_input:
|
||||
delete_input.clear()
|
||||
delete_input.input("Delete")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete', fallback='Typed \"Delete\" in confirmation box')}{Style.RESET_ALL}")
|
||||
delete_input_found = True
|
||||
time.sleep(2)
|
||||
break
|
||||
except:
|
||||
# Try direct JavaScript input as fallback
|
||||
try:
|
||||
self.browser.run_js(r"""
|
||||
arguments[0].value = "Delete";
|
||||
const event = new Event('input', { bubbles: true });
|
||||
arguments[0].dispatchEvent(event);
|
||||
const changeEvent = new Event('change', { bubbles: true });
|
||||
arguments[0].dispatchEvent(changeEvent);
|
||||
""", delete_input)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
|
||||
delete_input_found = True
|
||||
time.sleep(2)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not delete_input_found:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.delete_input_not_found', fallback='Delete confirmation input not found, continuing anyway')}{Style.RESET_ALL}")
|
||||
time.sleep(2)
|
||||
|
||||
# Wait before clicking the final DELETE button
|
||||
time.sleep(2)
|
||||
|
||||
# Click on the final DELETE button
|
||||
confirm_button_found = False
|
||||
|
||||
# Use JavaScript approach for the DELETE button
|
||||
try:
|
||||
delete_button_js = self.browser.run_js("""
|
||||
// Try to find the DELETE button by exact text content
|
||||
const buttons = Array.from(document.querySelectorAll('button'));
|
||||
const deleteButton = buttons.find(btn =>
|
||||
btn.textContent.trim() === 'DELETE' ||
|
||||
btn.textContent.trim() === 'Delete'
|
||||
);
|
||||
|
||||
if (deleteButton) {
|
||||
console.log("Found DELETE button with JavaScript");
|
||||
deleteButton.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not found by text, try to find right-most button in the modal
|
||||
const modalButtons = Array.from(document.querySelectorAll('.relative button, [role="dialog"] button, .modal button, [aria-modal="true"] button'));
|
||||
|
||||
if (modalButtons.length > 1) {
|
||||
modalButtons.sort((a, b) => {
|
||||
const rectA = a.getBoundingClientRect();
|
||||
const rectB = b.getBoundingClientRect();
|
||||
return rectB.right - rectA.right;
|
||||
});
|
||||
|
||||
console.log("Clicking right-most button in modal");
|
||||
modalButtons[0].click();
|
||||
return true;
|
||||
} else if (modalButtons.length === 1) {
|
||||
console.log("Clicking single button found in modal");
|
||||
modalButtons[0].click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
""")
|
||||
|
||||
if delete_button_js:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Clicked DELETE button')}{Style.RESET_ALL}")
|
||||
confirm_button_found = True
|
||||
except:
|
||||
pass
|
||||
|
||||
if not confirm_button_found:
|
||||
# Fallback to simple selectors
|
||||
delete_button_selectors = [
|
||||
'xpath://button[text()="DELETE"]',
|
||||
'xpath://div[contains(@class, "modal")]//button[last()]'
|
||||
]
|
||||
|
||||
for selector in delete_button_selectors:
|
||||
try:
|
||||
delete_button = self.browser.ele(selector, timeout=2)
|
||||
if delete_button:
|
||||
delete_button.click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Account deleted successfully!')}{Style.RESET_ALL}")
|
||||
confirm_button_found = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not confirm_button_found:
|
||||
raise Exception(self.translator.get('account_delete.confirm_button_not_found') if self.translator else "Confirm button not found")
|
||||
|
||||
# Wait a moment to see the confirmation
|
||||
time.sleep(2)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('account_delete.error', error=str(e)) if self.translator else f'Error during account deletion: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
# Clean up browser
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to handle Google account deletion"""
|
||||
print(f"\n{Fore.CYAN}{EMOJI['START']} {translator.get('account_delete.title') if translator else 'Cursor Google Account Deletion Tool'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 50}{Style.RESET_ALL}")
|
||||
|
||||
deleter = CursorGoogleAccountDeleter(translator)
|
||||
|
||||
try:
|
||||
# Ask for confirmation
|
||||
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('account_delete.warning') if translator else 'WARNING: This will permanently delete your Cursor account. This action cannot be undone.'}{Style.RESET_ALL}")
|
||||
confirm = input(f"{Fore.RED} {translator.get('account_delete.confirm_prompt') if translator else 'Are you sure you want to proceed? (y/N): '}{Style.RESET_ALL}").lower()
|
||||
|
||||
if confirm != 'y':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.cancelled') if translator else 'Account deletion cancelled.'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
success = deleter.delete_google_account()
|
||||
|
||||
if success:
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('account_delete.success') if translator else 'Your Cursor account has been successfully deleted!'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.failed') if translator else 'Account deletion process failed or was cancelled.'}{Style.RESET_ALL}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.interrupted') if translator else 'Account deletion process interrupted by user.'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
|
||||
finally:
|
||||
print(f"{Fore.YELLOW}{'─' * 50}{Style.RESET_ALL}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -34,12 +34,15 @@ class AutoUpdateDisabler:
|
||||
if self.system == "Windows":
|
||||
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
|
||||
self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"))
|
||||
self.product_json_path = config.get('WindowsPaths', 'product_json_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"))
|
||||
elif self.system == "Darwin":
|
||||
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
|
||||
self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml")
|
||||
self.product_json_path = config.get('MacPaths', 'product_json_path', fallback="/Applications/Cursor.app/Contents/Resources/app/product.json")
|
||||
elif self.system == "Linux":
|
||||
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
|
||||
self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml"))
|
||||
self.product_json_path = config.get('LinuxPaths', 'product_json_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/product.json"))
|
||||
else:
|
||||
# If configuration loading fails, use default paths
|
||||
self.updater_paths = {
|
||||
@@ -56,21 +59,29 @@ class AutoUpdateDisabler:
|
||||
}
|
||||
self.update_yml_path = self.update_yml_paths.get(self.system)
|
||||
|
||||
def _change_main_js(self):
|
||||
"""Change main.js"""
|
||||
self.product_json_paths = {
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"),
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app/product.json",
|
||||
"Linux": os.path.expanduser("~/.config/cursor/resources/app/product.json")
|
||||
}
|
||||
self.product_json_path = self.product_json_paths.get(self.system)
|
||||
|
||||
def _remove_update_url(self):
|
||||
"""Remove update URL"""
|
||||
try:
|
||||
main_path = get_config(self.translator).get('main_js_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/main.js"))
|
||||
original_stat = os.stat(main_path)
|
||||
original_stat = os.stat(self.product_json_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
|
||||
with open(main_path, "r", encoding="utf-8") as main_file:
|
||||
content = main_file.read()
|
||||
with open(self.product_json_path, "r", encoding="utf-8") as product_json_file:
|
||||
content = product_json_file.read()
|
||||
|
||||
patterns = {
|
||||
r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
|
||||
r"https://api2.cursor.sh/updates": r"",
|
||||
r"http://cursorapi.com/updates": r"",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
@@ -79,12 +90,12 @@ class AutoUpdateDisabler:
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
shutil.move(tmp_path, main_path)
|
||||
shutil.copy2(self.product_json_path, self.product_json_path + ".old")
|
||||
shutil.move(tmp_path, self.product_json_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
os.chmod(self.product_json_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(main_path, original_uid, original_gid)
|
||||
os.chown(self.product_json_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
@@ -122,17 +133,18 @@ class AutoUpdateDisabler:
|
||||
print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}")
|
||||
|
||||
if os.path.exists(updater_path):
|
||||
if os.path.isdir(updater_path):
|
||||
shutil.rmtree(updater_path)
|
||||
else:
|
||||
os.remove(updater_path)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
|
||||
try:
|
||||
if os.path.isdir(updater_path):
|
||||
shutil.rmtree(updater_path)
|
||||
else:
|
||||
os.remove(updater_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.directory_locked', path=updater_path) if self.translator else f'更新程序目录已被锁定,跳过删除: {updater_path}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
|
||||
# 即使删除失败,也返回 True,继续执行下一步
|
||||
return True
|
||||
|
||||
def _clear_update_yml_file(self):
|
||||
@@ -145,15 +157,15 @@ class AutoUpdateDisabler:
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}")
|
||||
|
||||
if os.path.exists(update_yml_path):
|
||||
# 清空文件内容
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('')
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
|
||||
return True
|
||||
try:
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('')
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已被锁定,跳过清空'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
|
||||
return True
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}")
|
||||
@@ -170,37 +182,43 @@ class AutoUpdateDisabler:
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
|
||||
|
||||
# 创建 updater_path 阻止文件
|
||||
os.makedirs(os.path.dirname(updater_path), exist_ok=True)
|
||||
open(updater_path, 'w').close()
|
||||
|
||||
# 设置 updater_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{updater_path}"')
|
||||
else:
|
||||
os.chmod(updater_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
|
||||
try:
|
||||
os.makedirs(os.path.dirname(updater_path), exist_ok=True)
|
||||
open(updater_path, 'w').close()
|
||||
|
||||
# 设置 updater_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{updater_path}"')
|
||||
else:
|
||||
os.chmod(updater_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.block_file_locked') if self.translator else '阻止文件已被锁定,跳过创建'}{Style.RESET_ALL}")
|
||||
|
||||
# 检查 update_yml_path
|
||||
update_yml_path = self.update_yml_path
|
||||
if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)):
|
||||
# 创建 update_yml_path 阻止文件
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
|
||||
|
||||
# 设置 update_yml_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{update_yml_path}"')
|
||||
else:
|
||||
os.chmod(update_yml_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
|
||||
try:
|
||||
# 创建 update_yml_path 阻止文件
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
|
||||
|
||||
# 设置 update_yml_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{update_yml_path}"')
|
||||
else:
|
||||
os.chmod(update_yml_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_already_locked') if self.translator else '更新配置文件已被锁定,跳过修改'}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
return True # 返回 True 以继续执行后续步骤
|
||||
|
||||
def disable_auto_update(self):
|
||||
"""Disable auto update"""
|
||||
@@ -222,8 +240,8 @@ class AutoUpdateDisabler:
|
||||
if not self._create_blocking_file():
|
||||
return False
|
||||
|
||||
# 5. Change main.js
|
||||
if not self._change_main_js():
|
||||
# 5. Remove update URL from product.json
|
||||
if not self._remove_update_url():
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||
|
||||
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/pro_2025-04-05_18-47-56.png
Normal file
BIN
images/pro_2025-04-05_18-47-56.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
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": "繁體中文",
|
||||
@@ -388,5 +401,60 @@
|
||||
"electron_localstorage_files_removed": "Electron localStorage файлове бяха премахнати",
|
||||
"electron_localstorage_files_removal_error": "Грешка при премахване на Electron localStorage файлове: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Премахването на Electron localStorage файлове беше завършено"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Избор на Chrome Профил",
|
||||
"select_profile": "Изберете Chrome профил за използване:",
|
||||
"profile_list": "Налични профили:",
|
||||
"default_profile": "Профил по Подразбиране",
|
||||
"profile": "Профил {number}",
|
||||
"no_profiles": "Не са намерени Chrome профили",
|
||||
"error_loading": "Грешка при зареждане на Chrome профили: {error}",
|
||||
"profile_selected": "Избран профил: {profile}",
|
||||
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
|
||||
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
|
||||
},
|
||||
"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, за да продължите"
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,11 @@
|
||||
"register": "Neues Cursor-Konto Registrieren",
|
||||
"register_google": "Mit Google-Konto Registrieren",
|
||||
"register_github": "Mit GitHub-Konto Registrieren",
|
||||
"register_manual": "Cursor mit Benutzerdefinierter E-Mail Registrieren",
|
||||
"register_manual": "Cursor Mit Benutzerdefinierter E-Mail Registrieren",
|
||||
"quit": "Cursor-Anwendung Schließen",
|
||||
"select_language": "Sprache Ändern",
|
||||
"input_choice": "Bitte Auswahl eingeben ({choices})",
|
||||
"select_chrome_profile": "Chrome-Profil Auswählen",
|
||||
"input_choice": "Bitte geben Sie Ihre Auswahl ein ({choices})",
|
||||
"invalid_choice": "Ungültige Auswahl. Bitte eine Nummer von {choices} eingeben",
|
||||
"program_terminated": "Programm wurde vom Benutzer beendet",
|
||||
"error_occurred": "Ein Fehler ist aufgetreten: {error}. Bitte erneut versuchen",
|
||||
@@ -21,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",
|
||||
@@ -33,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",
|
||||
@@ -100,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",
|
||||
@@ -376,5 +398,60 @@
|
||||
"electron_localstorage_files_removed": "Electron localStorage-Dateien entfernt",
|
||||
"electron_localstorage_files_removal_error": "Fehler beim Entfernen von Electron localStorage-Dateien: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Entfernen von Electron localStorage-Dateien abgeschlossen"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome-Profil Auswahl",
|
||||
"select_profile": "Wählen Sie ein Chrome-Profil zum Verwenden:",
|
||||
"profile_list": "Verfügbare Profile:",
|
||||
"default_profile": "Standard-Profil",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Keine Chrome-Profile gefunden",
|
||||
"error_loading": "Fehler beim Laden der Chrome-Profile: {error}",
|
||||
"profile_selected": "Ausgewähltes Profil: {profile}",
|
||||
"invalid_selection": "Ungültige Auswahl. Bitte versuchen Sie es erneut",
|
||||
"warning_chrome_close": "Warnung: Dies wird alle laufenden Chrome-Prozesse beenden"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
326
locales/en.json
326
locales/en.json
@@ -4,11 +4,12 @@
|
||||
"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",
|
||||
"select_chrome_profile": "Select Chrome Profile",
|
||||
"input_choice": "Please enter your choice ({choices})",
|
||||
"invalid_choice": "Invalid selection. Please enter a number from {choices}",
|
||||
"program_terminated": "Program was terminated by user",
|
||||
@@ -24,9 +25,20 @@
|
||||
"coming_soon": "Coming Soon",
|
||||
"fixed_soon": "Fixed Soon",
|
||||
"contribute": "Contribute to the Project",
|
||||
"config": "Show Config"
|
||||
"config": "Show Config",
|
||||
"delete_google_account": "Delete Cursor Google Account",
|
||||
"continue_prompt": "Continue? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operation cancelled by user",
|
||||
"exiting": "Exiting ……",
|
||||
"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": "繁體中文",
|
||||
@@ -111,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",
|
||||
@@ -190,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",
|
||||
@@ -280,7 +301,13 @@
|
||||
"available_domains_loaded": "Available Domains Loaded: {count}",
|
||||
"domains_filtered": "Domains Filtered: {count}",
|
||||
"trying_to_create_email": "Trying to create email: {email}",
|
||||
"domain_blocked": "Domain Blocked: {domain}"
|
||||
"domain_blocked": "Domain Blocked: {domain}",
|
||||
"using_chrome_profile": "Using Chrome profile from: {user_data_dir}",
|
||||
"no_display_found": "No display found. Make sure X server is running.",
|
||||
"try_export_display": "Try: export DISPLAY=:0",
|
||||
"extension_load_error": "Extension Load Error: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "Make sure Chrome/Chromium is properly installed",
|
||||
"try_install_chromium": "Try: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "Disable Cursor Auto Update",
|
||||
@@ -300,7 +327,16 @@
|
||||
"clear_update_yml_failed": "Failed to clear update.yml file: {error}",
|
||||
"unsupported_os": "Unsupported OS: {system}",
|
||||
"remove_directory_failed": "Failed to remove directory: {error}",
|
||||
"create_block_file_failed": "Failed to create block file: {error}"
|
||||
"create_block_file_failed": "Failed to create block file: {error}",
|
||||
"directory_locked": "Directory is locked: {path}",
|
||||
"yml_locked": "update.yml file is locked",
|
||||
"block_file_locked": "block file is locked",
|
||||
"yml_already_locked": "update.yml file is already locked",
|
||||
"block_file_already_locked": "block file is already locked",
|
||||
"block_file_locked_error": "Block file locked error: {error}",
|
||||
"yml_locked_error": "update.yml file locked error: {error}",
|
||||
"block_file_already_locked_error": "Block file already locked error: {error}",
|
||||
"yml_already_locked_error": "update.yml file already locked error: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Checking for updates...",
|
||||
@@ -426,7 +462,24 @@
|
||||
"cursor_reset_completed": "Cursor AI Editor has been fully reset and trial detection bypassed!",
|
||||
"cursor_reset_failed": "Cursor AI Editor reset failed: {error}",
|
||||
"cursor_reset_cancelled": "Cursor AI Editor reset cancelled. Exiting without making any changes.",
|
||||
"operation_cancelled": "Operation cancelled. Exiting without making any changes."
|
||||
"operation_cancelled": "Operation cancelled. Exiting without making any changes.",
|
||||
"navigating_to_settings": "Navigating to settings page...",
|
||||
"already_on_settings": "Already on settings page",
|
||||
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
|
||||
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
|
||||
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Error finding Advanced tab: {error}",
|
||||
"advanced_tab_clicked": "Clicked on Advanced tab",
|
||||
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
|
||||
"delete_button_not_found": "Delete Account button not found after multiple attempts",
|
||||
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Error finding Delete button: {error}",
|
||||
"delete_button_clicked": "Clicked on Delete Account button",
|
||||
"found_danger_zone": "Found Danger Zone section",
|
||||
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
|
||||
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Error finding Delete input: {error}",
|
||||
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "GitHub + Cursor AI Registration Automation",
|
||||
@@ -486,7 +539,8 @@
|
||||
"premium_usage": "Premium Usage",
|
||||
"basic_usage": "Basic Usage",
|
||||
"usage_not_found": "Usage not found",
|
||||
"lifetime_access_enabled": "Lifetime Access Enabled"
|
||||
"lifetime_access_enabled": "Lifetime Access Enabled",
|
||||
"token_not_found": "Token not found"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "Configuration not available",
|
||||
@@ -498,14 +552,14 @@
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Please make sure Cursor is installed and has been run at least once",
|
||||
"storage_directory_not_found": "Storage directory not found: {storage_dir}",
|
||||
"storage_file_found": "Storage file found: {storage_path}",
|
||||
"file_size": "File size: {size}",
|
||||
"file_size": "File size: {size} bytes",
|
||||
"file_permissions": "File permissions: {permissions}",
|
||||
"file_owner": "File owner: {owner}",
|
||||
"file_group": "File group: {group}",
|
||||
"error_getting_file_stats": "Error getting file stats: {error}",
|
||||
"permission_denied": "Permission denied: {storage_path}",
|
||||
"try_running": "Try running 'cursor --help' to check if it's installed",
|
||||
"and": "and",
|
||||
"try_running": "Try running: {command}",
|
||||
"and": "And",
|
||||
"storage_file_is_empty": "Storage file is empty: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
|
||||
"storage_file_not_found": "Storage file not found: {storage_path}",
|
||||
@@ -513,7 +567,249 @@
|
||||
"config_option_added": "Config option added: {option}",
|
||||
"config_updated": "Config updated",
|
||||
"config_created": "Config created: {config_file}",
|
||||
"config_setup_error": "Error setting up config: {error}"
|
||||
|
||||
"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}",
|
||||
"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",
|
||||
"authentication_failed": "Authentication failed: {error}",
|
||||
"found_cookies": "Found {count} cookies",
|
||||
"token_extraction_error": "Token extraction error: {error}",
|
||||
"authentication_successful": "Authentication successful - Email: {email}",
|
||||
"missing_authentication_data": "Missing authentication data: {data}",
|
||||
"failed_to_delete_account": "Failed to delete account: {error}",
|
||||
"invalid_authentication_type": "Invalid authentication type",
|
||||
"auth_update_success": "Auth update success",
|
||||
"browser_closed": "Browser closed",
|
||||
"auth_update_failed": "Auth update failed",
|
||||
"google_start": "Google start",
|
||||
"github_start": "Github start",
|
||||
"usage_count": "Usage count: {usage}",
|
||||
"account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
|
||||
"starting_new_authentication_process": "Starting new authentication process...",
|
||||
"failed_to_delete_expired_account": "Failed to delete expired account",
|
||||
"could_not_check_usage_count": "Could not check usage count: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"could_not_find_email": "Could not find email: {error}",
|
||||
"could_not_find_usage_count": "Could not find usage count: {error}",
|
||||
"already_on_settings_page": "Already on settings page!",
|
||||
"failed_to_extract_auth_info": "Failed to extract auth info: {error}",
|
||||
"no_chrome_profiles_found": "No Chrome profiles found, using Default",
|
||||
"found_default_chrome_profile": "Found Default Chrome profile",
|
||||
"using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
|
||||
"error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
|
||||
"initializing_browser_setup": "Initializing browser setup...",
|
||||
"detected_platform": "Detected platform: {platform}",
|
||||
"running_as_root_warning": "Running as root is not recommended for browser automation",
|
||||
"consider_running_without_sudo": "Consider running the script without sudo",
|
||||
"no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
|
||||
"supported_browsers": "Supported browsers for {platform}",
|
||||
"using_browser_profile": "Using browser profile: {profile}",
|
||||
"starting_browser": "Starting browser at: {path}",
|
||||
"browser_setup_completed": "Browser setup completed successfully",
|
||||
"browser_setup_failed": "Browser setup failed: {error}",
|
||||
"try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
|
||||
"redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Starting Google authentication...",
|
||||
"starting_github_authentication": "Starting GitHub authentication...",
|
||||
"waiting_for_authentication": "Waiting for authentication...",
|
||||
"page_changed_checking_auth": "Page changed, checking auth...",
|
||||
"status_check_error": "Status check error: {error}",
|
||||
"authentication_timeout": "Authentication timeout",
|
||||
"account_is_still_valid": "Account is still valid (Usage: {usage})",
|
||||
"starting_re_authentication_process": "Starting re-authentication process...",
|
||||
"starting_new_google_authentication": "Starting new Google authentication...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
|
||||
"navigating_to_authentication_page": "Navigating to authentication page...",
|
||||
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
|
||||
"found_browser_data_directory": "Found browser data directory: {path}",
|
||||
"authentication_successful_getting_account_info": "Authentication successful, getting account info...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}",
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
|
||||
"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}"
|
||||
},
|
||||
"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 {browser} profiles found",
|
||||
"error_loading": "Error loading {browser} profiles: {error}",
|
||||
"profile_selected": "Selected profile: {profile}",
|
||||
"invalid_selection": "Invalid selection. Please try again."
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google Account Deletion Tool",
|
||||
"warning": "WARNING: This will permanently delete your Cursor account. This action cannot be undone.",
|
||||
"cancelled": "Account deletion cancelled.",
|
||||
"starting_process": "Starting account deletion process...",
|
||||
"google_button_not_found": "Google login button not found",
|
||||
"logging_in": "Logging in with Google...",
|
||||
"waiting_for_auth": "Waiting for Google authentication...",
|
||||
"login_successful": "Login successful",
|
||||
"unexpected_page": "Unexpected page after login: {url}",
|
||||
"trying_settings": "Trying to navigate to settings page...",
|
||||
"select_google_account": "Please select your Google account...",
|
||||
"auth_timeout": "Authentication timeout, continuing anyway...",
|
||||
"navigating_to_settings": "Navigating to settings page...",
|
||||
"already_on_settings": "Already on settings page",
|
||||
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
|
||||
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
|
||||
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Error finding Advanced tab: {error}",
|
||||
"advanced_tab_clicked": "Clicked on Advanced tab",
|
||||
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
|
||||
"delete_button_not_found": "Delete Account button not found after multiple attempts",
|
||||
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Error finding Delete button: {error}",
|
||||
"delete_button_clicked": "Clicked on Delete Account button",
|
||||
"found_danger_zone": "Found Danger Zone section",
|
||||
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
|
||||
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Error finding Delete input: {error}",
|
||||
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway",
|
||||
"typed_delete": "Typed \"Delete\" in confirmation box",
|
||||
"confirm_button_not_found": "Confirm button not found after multiple attempts",
|
||||
"confirm_button_retry": "Confirm button not found, attempt {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "Error finding Confirm button: {error}",
|
||||
"account_deleted": "Account deleted successfully!",
|
||||
"error": "Error during account deletion: {error}",
|
||||
"success": "Your Cursor account has been successfully deleted!",
|
||||
"failed": "Account deletion process failed or was cancelled.",
|
||||
"interrupted": "Account deletion process interrupted by user.",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"email_not_found": "Email not found: {error}",
|
||||
"confirm_prompt": "Are you sure you want to proceed? (y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "Starting Cursor version bypass...",
|
||||
"found_product_json": "Found product.json: {path}",
|
||||
"no_write_permission": "No write permission for file: {path}",
|
||||
"read_failed": "Failed to read product.json: {error}",
|
||||
"current_version": "Current version: {version}",
|
||||
"backup_created": "Backup created: {path}",
|
||||
"version_updated": "Version updated from {old} to {new}",
|
||||
"write_failed": "Failed to write product.json: {error}",
|
||||
"no_update_needed": "No update needed. Current version {version} is already >= 0.46.0",
|
||||
"bypass_failed": "Version bypass failed: {error}",
|
||||
"stack_trace": "Stack trace",
|
||||
"localappdata_not_found": "LOCALAPPDATA environment variable not found",
|
||||
"product_json_not_found": "product.json not found in common Linux paths",
|
||||
"unsupported_os": "Unsupported operating system: {system}",
|
||||
"file_not_found": "File not found: {path}",
|
||||
"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",
|
||||
@@ -439,5 +456,60 @@
|
||||
"completed_successfully": "¡Registro de GitHub + Cursor completado exitosamente!",
|
||||
"registration_encountered_issues": "El registro de GitHub + Cursor encontró problemas.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Revise las ventanas del navegador para intervención manual o intente nuevamente más tarde."
|
||||
}
|
||||
}
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Selección de Perfil de Chrome",
|
||||
"select_profile": "Seleccione un perfil de Chrome para usar:",
|
||||
"profile_list": "Perfiles disponibles:",
|
||||
"default_profile": "Perfil Predeterminado",
|
||||
"profile": "Perfil {number}",
|
||||
"no_profiles": "No se encontraron perfiles de Chrome",
|
||||
"error_loading": "Error al cargar perfiles de Chrome: {error}",
|
||||
"profile_selected": "Perfil seleccionado: {profile}",
|
||||
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
|
||||
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
|
||||
},
|
||||
"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",
|
||||
@@ -374,5 +395,60 @@
|
||||
"electron_localstorage_files_removed": "Fichiers localStorage Electron supprimés",
|
||||
"electron_localstorage_files_removal_error": "Erreur de suppression des fichiers localStorage Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Suppression des fichiers localStorage Electron terminée"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Sélection du Profil Chrome",
|
||||
"select_profile": "Sélectionnez un profil Chrome à utiliser :",
|
||||
"profile_list": "Profils disponibles :",
|
||||
"default_profile": "Profil par Défaut",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Aucun profil Chrome trouvé",
|
||||
"error_loading": "Erreur lors du chargement des profils Chrome : {error}",
|
||||
"profile_selected": "Profil sélectionné : {profile}",
|
||||
"invalid_selection": "Sélection invalide. Veuillez réessayer",
|
||||
"warning_chrome_close": "Attention : Cela fermera tous les processus Chrome en cours d'exécution"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
101
locales/nl.json
101
locales/nl.json
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Beschikbare opties",
|
||||
"exit": "Programma afsluiten",
|
||||
"reset": "Machine-ID resetten",
|
||||
"register": "Nieuw Cursor-account registreren",
|
||||
"register_google": "Registreren met Google-account",
|
||||
"register_github": "Registreren met GitHub-account",
|
||||
"register_manual": "Cursor registreren met aangepast e-mailadres",
|
||||
"quit": "Cursor-applicatie sluiten",
|
||||
"select_language": "Taal wijzigen",
|
||||
"input_choice": "Voer uw keuze in: {choices}",
|
||||
"title": "Beschikbare Opties",
|
||||
"exit": "Programma Afsluiten",
|
||||
"reset": "Machine ID Resetten",
|
||||
"register": "Nieuw Cursor Account Registreren",
|
||||
"register_google": "Registreren met Google Account",
|
||||
"register_github": "Registreren met GitHub Account",
|
||||
"register_manual": "Cursor Registreren met Aangepaste E-mail",
|
||||
"quit": "Cursor Toepassing Sluiten",
|
||||
"select_language": "Taal Wijzigen",
|
||||
"select_chrome_profile": "Chrome Profiel Selecteren",
|
||||
"input_choice": "Voer uw keuze in ({choices})",
|
||||
"invalid_choice": "Ongeldige selectie. Voer een nummer in uit {choices}.",
|
||||
"program_terminated": "Programma is beëindigd door de gebruiker",
|
||||
"error_occurred": "Er is een fout opgetreden: {error}. Probeer het opnieuw.",
|
||||
@@ -19,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",
|
||||
@@ -98,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",
|
||||
@@ -374,5 +394,60 @@
|
||||
"electron_localstorage_files_removed": "Electron localStorage-bestanden verwijderd",
|
||||
"electron_localstorage_files_removal_error": "Fout bij het verwijderen van Electron localStorage-bestanden: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage-bestanden verwijderd"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profiel Selectie",
|
||||
"select_profile": "Selecteer een Chrome profiel om te gebruiken:",
|
||||
"profile_list": "Beschikbare profielen:",
|
||||
"default_profile": "Standaard Profiel",
|
||||
"profile": "Profiel {number}",
|
||||
"no_profiles": "Geen Chrome profielen gevonden",
|
||||
"error_loading": "Fout bij laden van Chrome profielen: {error}",
|
||||
"profile_selected": "Geselecteerd profiel: {profile}",
|
||||
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
|
||||
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
|
||||
},
|
||||
"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",
|
||||
@@ -383,5 +395,60 @@
|
||||
"electron_localstorage_files_removed": "Arquivos localStorage do Electron removidos",
|
||||
"electron_localstorage_files_removal_error": "Erro ao remover arquivos localStorage do Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Remoção dos arquivos localStorage do Electron concluída"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Seleção de Perfil do Chrome",
|
||||
"select_profile": "Selecione um perfil do Chrome para usar:",
|
||||
"profile_list": "Perfis disponíveis:",
|
||||
"default_profile": "Perfil Padrão",
|
||||
"profile": "Perfil {number}",
|
||||
"no_profiles": "Nenhum perfil do Chrome encontrado",
|
||||
"error_loading": "Erro ao carregar perfis do Chrome: {error}",
|
||||
"profile_selected": "Perfil selecionado: {profile}",
|
||||
"invalid_selection": "Seleção inválida. Por favor, tente novamente",
|
||||
"warning_chrome_close": "Aviso: Isso fechará todos os processos do Chrome em execução"
|
||||
},
|
||||
"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": "Традиционный китайский",
|
||||
@@ -383,5 +396,60 @@
|
||||
"electron_localstorage_files_removed": "Файлы localStorage Electron удалены",
|
||||
"electron_localstorage_files_removal_error": "Ошибка удаления файлов localStorage Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Удаление файлов localStorage Electron завершено"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Выбор Профиля Chrome",
|
||||
"select_profile": "Выберите профиль Chrome для использования:",
|
||||
"profile_list": "Доступные профили:",
|
||||
"default_profile": "Профиль по умолчанию",
|
||||
"profile": "Профиль {number}",
|
||||
"no_profiles": "Профили Chrome не найдены",
|
||||
"error_loading": "Ошибка загрузки профилей Chrome: {error}",
|
||||
"profile_selected": "Выбран профиль: {profile}",
|
||||
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
|
||||
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
|
||||
},
|
||||
"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": "繁體中文",
|
||||
@@ -386,5 +401,60 @@
|
||||
"electron_localstorage_files_removed": "Electron localStorage dosyaları kaldırıldı",
|
||||
"electron_localstorage_files_removal_error": "Electron localStorage dosyaları kaldırılırken hata: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage dosyaları kaldırma işlemi tamamlandı"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profil Seçimi",
|
||||
"select_profile": "Kullanılacak Chrome profilini seçin:",
|
||||
"profile_list": "Mevcut profiller:",
|
||||
"default_profile": "Varsayılan Profil",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Chrome profili bulunamadı",
|
||||
"error_loading": "Chrome profilleri yüklenirken hata: {error}",
|
||||
"profile_selected": "Seçilen profil: {profile}",
|
||||
"invalid_selection": "Geçersiz seçim. Lütfen tekrar deneyin",
|
||||
"warning_chrome_close": "Uyarı: Bu işlem tüm çalışan Chrome işlemlerini kapatacaktır"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
588
locales/vi.json
588
locales/vi.json
@@ -1,12 +1,15 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Các Tùy Chọn Khả Dụng",
|
||||
"title": "Các Tùy Chọn",
|
||||
"exit": "Thoát Chương Trình",
|
||||
"reset": "Đặt Lại ID Máy",
|
||||
"register": "Đăng Ký Tài Khoản Cursor Mới",
|
||||
"register_google": "Đăng Ký Bằng Tài Khoản Google",
|
||||
"register_github": "Đăng Ký Bằng Tài Khoản GitHub",
|
||||
"register_manual": "Đăng Ký Cursor Với Email Tùy Chỉnh",
|
||||
"quit": "Đóng Ứng Dụng Cursor",
|
||||
"select_language": "Thay Đổi Ngôn Ngữ",
|
||||
"select_chrome_profile": "Chọn Hồ Sơ Chrome",
|
||||
"input_choice": "Vui lòng nhập lựa chọn của bạn ({choices})",
|
||||
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ {choices}",
|
||||
"program_terminated": "Chương trình đã bị người dùng chấm dứt",
|
||||
@@ -17,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",
|
||||
@@ -96,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",
|
||||
@@ -173,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}",
|
||||
@@ -188,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ị",
|
||||
@@ -210,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",
|
||||
@@ -226,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}",
|
||||
@@ -258,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",
|
||||
@@ -276,110 +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 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 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"
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,14 @@
|
||||
"menu": {
|
||||
"title": "可用选项",
|
||||
"exit": "退出程序",
|
||||
"reset": "重置机器标识",
|
||||
"register": "注册新 Cursor 账号",
|
||||
"register_google": "使用 Google 账号注册",
|
||||
"register_github": "使用 GitHub 账号注册",
|
||||
"register_manual": "使用自定义邮箱注册",
|
||||
"quit": "关闭 Cursor 应用",
|
||||
"reset": "重置机器ID",
|
||||
"register": "注册新的Cursor账户",
|
||||
"register_google": "使用自己的Google账户注册",
|
||||
"register_github": "使用自己的GitHub账户注册",
|
||||
"register_manual": "使用自定义邮箱注册Cursor",
|
||||
"quit": "关闭Cursor应用",
|
||||
"select_language": "更改语言",
|
||||
"select_chrome_profile": "选择Chrome配置文件",
|
||||
"input_choice": "请输入您的选择 ({choices})",
|
||||
"invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
|
||||
"program_terminated": "程序已被用户终止",
|
||||
@@ -24,9 +25,20 @@
|
||||
"coming_soon": "即将推出",
|
||||
"fixed_soon": "即将修复",
|
||||
"contribute": "贡献项目",
|
||||
"config": "显示配置"
|
||||
"config": "显示配置",
|
||||
"delete_google_account": "删除 Cursor Google 账号",
|
||||
"continue_prompt": "继续?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被用户取消",
|
||||
"exiting": "退出中 ……",
|
||||
"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": "繁体中文",
|
||||
@@ -111,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 注册工具",
|
||||
@@ -188,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 认证管理器",
|
||||
@@ -275,7 +298,13 @@
|
||||
"available_domains_loaded": "获取到 {count} 个可用域名",
|
||||
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
|
||||
"trying_to_create_email": "尝试创建邮箱: {email}",
|
||||
"domain_blocked": "域名被屏蔽: {domain}"
|
||||
"domain_blocked": "域名被屏蔽: {domain}",
|
||||
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
|
||||
"no_display_found": "未找到显示器。确保 X 服务器正在运行。",
|
||||
"try_export_display": "尝试: export DISPLAY=:0",
|
||||
"extension_load_error": "加载插件失败: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "确保 Chrome/Chromium 已正确安装",
|
||||
"try_install_chromium": "尝试: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "禁用 Cursor 自动更新",
|
||||
@@ -295,7 +324,16 @@
|
||||
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
|
||||
"unsupported_os": "不支持的操作系统: {system}",
|
||||
"remove_directory_failed": "删除目录失败: {error}",
|
||||
"create_block_file_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": "检查更新...",
|
||||
@@ -481,7 +519,8 @@
|
||||
"premium_usage": "高级使用量",
|
||||
"basic_usage": "基础使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久访问已启用"
|
||||
"lifetime_access_enabled": "永久访问已启用",
|
||||
"token_not_found": "Token 未找到"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
@@ -499,7 +538,7 @@
|
||||
"file_group": "文件组",
|
||||
"error_getting_file_stats": "获取文件统计信息时出错",
|
||||
"permission_denied": "权限拒绝",
|
||||
"try_running": "尝试运行 'cursor --help' 检查是否已安装",
|
||||
"try_running": "尝试运行: {command}",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "存储文件为空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
|
||||
@@ -508,7 +547,251 @@
|
||||
"config_option_added": "添加配置选项",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已创建",
|
||||
"config_setup_error": "配置设置错误"
|
||||
"config_setup_error": "配置设置错误",
|
||||
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
|
||||
"error_reading_storage_file": "读取存储文件时出错",
|
||||
"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} 个 Cookie",
|
||||
"token_extraction_error": "Token 提取错误: {error}",
|
||||
"authentication_successful": "认证成功 - 邮箱: {email}",
|
||||
"missing_authentication_data": "缺少认证数据: {data}",
|
||||
"failed_to_delete_account": "删除账户失败: {error}",
|
||||
"invalid_authentication_type": "无效的认证类型",
|
||||
"auth_update_success": "认证更新成功",
|
||||
"browser_closed": "浏览器已关闭",
|
||||
"auth_update_failed": "认证更新失败",
|
||||
"google_start": "Google 开始",
|
||||
"github_start": "Github 开始",
|
||||
"usage_count": "使用次数: {usage}",
|
||||
"account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "开始新的认证过程...",
|
||||
"failed_to_delete_expired_account": "删除过期账户失败",
|
||||
"could_not_check_usage_count": "无法检查使用次数: {error}",
|
||||
"found_email": "找到邮箱: {email}",
|
||||
"could_not_find_email": "未找到邮箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用次数: {error}",
|
||||
"already_on_settings_page": "已处于设置页面",
|
||||
"failed_to_extract_auth_info": "提取认证信息失败: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
|
||||
"found_default_chrome_profile": "找到默认 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化浏览器设置...",
|
||||
"detected_platform": "检测平台: {platform}",
|
||||
"running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
|
||||
"consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
|
||||
"no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的浏览器: {platform}",
|
||||
"using_browser_profile": "使用浏览器配置文件: {profile}",
|
||||
"starting_browser": "正在启动浏览器: {path}",
|
||||
"browser_setup_completed": "浏览器设置完成",
|
||||
"browser_setup_failed": "浏览器设置失败: {error}",
|
||||
"try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "开始 Google 认证...",
|
||||
"starting_github_authentication": "开始 Github 认证...",
|
||||
"waiting_for_authentication": "等待认证...",
|
||||
"page_changed_checking_auth": "页面改变, 检查认证...",
|
||||
"status_check_error": "状态检查错误: {error}",
|
||||
"authentication_timeout": "认证超时",
|
||||
"account_is_still_valid": "账户仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "开始重新认证过程...",
|
||||
"starting_new_google_authentication": "开始新的 Google 认证...",
|
||||
"failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
|
||||
"navigating_to_authentication_page": "正在导航到认证页面...",
|
||||
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
|
||||
"found_browser_data_directory": "找到浏览器数据目录: {path}",
|
||||
"authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}",
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
|
||||
"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} 配置文件:"
|
||||
},
|
||||
"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": "已点击删除账号按钮",
|
||||
"delete_input_not_found": "多次尝试后未找到删除确认输入框",
|
||||
"delete_input_retry": "未找到删除输入框,尝试 {attempt}/{max_attempts}",
|
||||
"delete_input_error": "查找删除输入框时出错:{error}",
|
||||
"delete_input_not_found_continuing": "未找到删除确认输入框,尝试继续执行...",
|
||||
"typed_delete": "已在确认框中输入\"Delete\"",
|
||||
"confirm_button_not_found": "多次尝试后未找到确认按钮",
|
||||
"confirm_button_retry": "未找到确认按钮,尝试 {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "查找确认按钮时出错:{error}",
|
||||
"account_deleted": "账号删除成功!",
|
||||
"error": "账号删除过程中出错:{error}",
|
||||
"success": "您的 Cursor 账号已成功删除!",
|
||||
"failed": "账号删除过程失败或已取消。",
|
||||
"interrupted": "账号删除过程被用户中断。",
|
||||
"unexpected_error": "意外错误:{error}",
|
||||
"found_email": "找到邮箱:{email}",
|
||||
"email_not_found": "未找到邮箱: {error}",
|
||||
"found_danger_zone": "已找到危险区域部分",
|
||||
"confirm_prompt": "您确定要继续吗?(y/N): "
|
||||
},
|
||||
"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": "此工具修改 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键继续"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,14 @@
|
||||
"menu": {
|
||||
"title": "可用選項",
|
||||
"exit": "退出程式",
|
||||
"reset": "重置機器識別碼",
|
||||
"register": "註冊新 Cursor 帳號",
|
||||
"register_manual": "使用自訂郵箱註冊",
|
||||
"quit": "關閉 Cursor 應用程式",
|
||||
"select_language": "變更語言",
|
||||
"reset": "重置機器ID",
|
||||
"register": "註冊新的Cursor帳戶",
|
||||
"register_google": "使用自己的Google帳戶註冊",
|
||||
"register_github": "使用自己的GitHub帳戶註冊",
|
||||
"register_manual": "使用自定義郵箱註冊Cursor",
|
||||
"quit": "關閉Cursor應用",
|
||||
"select_language": "更改語言",
|
||||
"select_chrome_profile": "選擇Chrome配置檔案",
|
||||
"input_choice": "請輸入您的選擇 ({choices})",
|
||||
"invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
|
||||
"program_terminated": "程式已被使用者終止",
|
||||
@@ -22,9 +25,20 @@
|
||||
"coming_soon": "即將推出",
|
||||
"fixed_soon": "即將修復",
|
||||
"contribute": "貢獻項目",
|
||||
"config": "顯示配置"
|
||||
"config": "顯示配置",
|
||||
"delete_google_account": "刪除 Cursor Google 帳號",
|
||||
"continue_prompt": "繼續?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被使用者取消",
|
||||
"exiting": "退出中 ……",
|
||||
"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": "繁體中文",
|
||||
@@ -109,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": "正在啟動註冊流程...",
|
||||
@@ -138,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": "檢測到登錄頁面,開始登錄...",
|
||||
@@ -254,7 +284,14 @@
|
||||
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
|
||||
"available_domains_loaded": "獲取到 {count} 個可用域名",
|
||||
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
|
||||
"trying_to_create_email": "嘗試創建郵箱: {email}"
|
||||
"trying_to_create_email": "嘗試創建郵箱: {email}",
|
||||
"domain_blocked": "域名被屏蔽: {domain}",
|
||||
"using_chrome_profile": "使用 Chrome 配置文件: {user_data_dir}",
|
||||
"no_display_found": "未找到顯示器。確保 X 伺服器正在運行。",
|
||||
"try_export_display": "嘗試: export DISPLAY=:0",
|
||||
"extension_load_error": "加載插件失敗: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "確保 Chrome/Chromium 已正確安裝",
|
||||
"try_install_chromium": "嘗試: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "禁用 Cursor 自动更新",
|
||||
@@ -274,7 +311,16 @@
|
||||
"clear_update_yml_failed": "清空 update.yml 文件失败: {error}",
|
||||
"unsupported_os": "不支持的操作系统: {system}",
|
||||
"remove_directory_failed": "刪除目錄失败: {error}",
|
||||
"create_block_file_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": "檢查更新...",
|
||||
@@ -460,7 +506,8 @@
|
||||
"premium_usage": "高級使用量",
|
||||
"basic_usage": "基礎使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久訪問已啟用"
|
||||
"lifetime_access_enabled": "永久訪問已啟用",
|
||||
"token_not_found": "Token 未找到"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
@@ -478,7 +525,7 @@
|
||||
"file_group": "文件組",
|
||||
"error_getting_file_stats": "獲取文件統計信息時出錯",
|
||||
"permission_denied": "權限拒絕",
|
||||
"try_running": "嘗試運行 'cursor --help' 檢查是否已安裝",
|
||||
"try_running": "嘗試運行: {command}",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "儲存文件為空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
|
||||
@@ -487,7 +534,237 @@
|
||||
"config_option_added": "添加配置選項",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已創建",
|
||||
"config_setup_error": "配置設置錯誤"
|
||||
|
||||
"config_setup_error": "配置設置錯誤",
|
||||
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
|
||||
"error_reading_storage_file": "讀取儲存文件時出錯",
|
||||
"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": "未找到認證按鈕",
|
||||
"authentication_failed": "認證失敗: {error}",
|
||||
"found_cookies": "找到 {count} 個 Cookie",
|
||||
"token_extraction_error": "Token 提取錯誤: {error}",
|
||||
"authentication_successful": "認證成功 - 郵箱: {email}",
|
||||
"missing_authentication_data": "缺少認證數據: {data}",
|
||||
"failed_to_delete_account": "刪除帳戶失敗: {error}",
|
||||
"invalid_authentication_type": "無效的認證類型",
|
||||
"auth_update_success": "認證更新成功",
|
||||
"browser_closed": "瀏覽器已關閉",
|
||||
"auth_update_failed": "認證更新失敗",
|
||||
"google_start": "Google 開始",
|
||||
"github_start": "Github 開始",
|
||||
"usage_count": "使用量: {usage}",
|
||||
"account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "開始新的認證過程...",
|
||||
"failed_to_delete_expired_account": "刪除過期帳戶失敗",
|
||||
"could_not_check_usage_count": "無法檢查使用量: {error}",
|
||||
"found_email": "找到郵箱: {email}",
|
||||
"could_not_find_email": "未找到郵箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用量: {erro r}",
|
||||
"already_on_settings_page": "已處於設置頁面",
|
||||
"failed_to_extract_auth_info": "提取認證信息失敗: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
|
||||
"found_default_chrome_profile": "找到默認 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化瀏覽器設置...",
|
||||
"detected_platform": "檢測平台: {platform}",
|
||||
"running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
|
||||
"consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
|
||||
"no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的瀏覽器: {platform}",
|
||||
"using_browser_profile": "使用瀏覽器配置文件: {profile}",
|
||||
"starting_browser": "正在啟動瀏覽器: {path}",
|
||||
"browser_setup_completed": "瀏覽器設置完成成功",
|
||||
"browser_setup_failed": "瀏覽器設置失敗: {error}",
|
||||
"try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_github_authentication": "開始 Github 認證...",
|
||||
"waiting_for_authentication": "等待認證...",
|
||||
"page_changed_checking_auth": "頁面改變, 檢查認證...",
|
||||
"status_check_error": "狀態檢查錯誤: {error}",
|
||||
"authentication_timeout": "認證超時",
|
||||
"account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "開始重新認證過程...",
|
||||
"starting_new_google_authentication": "開始新的 Google 認證...",
|
||||
"failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
|
||||
"navigating_to_authentication_page": "正在導航到認證頁面...",
|
||||
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
|
||||
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
|
||||
"authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
|
||||
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}",
|
||||
"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配置檔案選擇",
|
||||
"select_profile": "選擇要使用的Chrome配置檔案:",
|
||||
"profile_list": "可用配置檔案:",
|
||||
"default_profile": "預設配置檔案",
|
||||
"profile": "配置檔案 {number}",
|
||||
"no_profiles": "未找到Chrome配置檔案",
|
||||
"error_loading": "載入Chrome配置檔案時出錯:{error}",
|
||||
"profile_selected": "已選擇配置檔案:{profile}",
|
||||
"invalid_selection": "選擇無效。請重試",
|
||||
"warning_chrome_close": "警告:這將關閉所有正在執行的Chrome程序"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google 帳號刪除工具",
|
||||
"warning": "警告:這將永久刪除您的 Cursor 帳號。此操作無法撤銷。",
|
||||
"cancelled": "帳號刪除已取消。",
|
||||
"starting_process": "開始帳號刪除過程...",
|
||||
"google_button_not_found": "未找到 Google 登錄按鈕",
|
||||
"logging_in": "正在使用 Google 登錄...",
|
||||
"waiting_for_auth": "等待 Google 驗證...",
|
||||
"login_successful": "登錄成功",
|
||||
"unexpected_page": "登錄後頁面異常:{url}",
|
||||
"trying_settings": "嘗試導航到設置頁面...",
|
||||
"select_google_account": "請選擇您的 Google 帳號...",
|
||||
"auth_timeout": "認證超時,繼續執行...",
|
||||
"navigating_to_settings": "正在導航到設置頁面...",
|
||||
"already_on_settings": "已在設置頁面",
|
||||
"login_redirect_failed": "登錄重定向失敗,嘗試直接導航...",
|
||||
"advanced_tab_not_found": "多次嘗試後未找到高級選項卡",
|
||||
"advanced_tab_retry": "未找到高級選項卡,嘗試 {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "查找高級選項卡時出錯:{error}",
|
||||
"advanced_tab_clicked": "已點擊高級選項卡",
|
||||
"direct_advanced_navigation": "嘗試直接導航到高級選項卡",
|
||||
"delete_button_not_found": "多次嘗試後未找到刪除帳號按鈕",
|
||||
"delete_button_retry": "未找到刪除按鈕,嘗試 {attempt}/{max_attempts}",
|
||||
"delete_button_error": "查找刪除按鈕時出錯:{error}",
|
||||
"delete_button_clicked": "已點擊刪除帳號按鈕",
|
||||
"delete_input_not_found": "多次嘗試後未找到刪除確認輸入框",
|
||||
"delete_input_retry": "未找到刪除輸入框,嘗試 {attempt}/{max_attempts}",
|
||||
"delete_input_error": "查找刪除輸入框時出錯:{error}",
|
||||
"delete_input_not_found_continuing": "未找到刪除確認輸入框,嘗試繼續執行...",
|
||||
"typed_delete": "已在確認框中輸入\"Delete\"",
|
||||
"confirm_button_not_found": "多次嘗試後未找到確認按鈕",
|
||||
"confirm_button_retry": "未找到確認按鈕,嘗試 {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "查找確認按鈕時出錯:{error}",
|
||||
"account_deleted": "帳號刪除成功!",
|
||||
"error": "帳號刪除過程中出錯:{error}",
|
||||
"success": "您的 Cursor 帳號已成功刪除!",
|
||||
"failed": "帳號刪除過程失敗或已取消。",
|
||||
"interrupted": "帳號刪除過程被用戶中斷。",
|
||||
"unexpected_error": "意外錯誤:{error}",
|
||||
"found_email": "找到郵箱:{email}",
|
||||
"email_not_found": "未找到郵箱: {error}",
|
||||
"found_danger_zone": "已找到危險區域部分",
|
||||
"confirm_prompt": "您確定要繼續嗎?(y/N): ",
|
||||
"typed_delete_js": "已使用 JavaScript 輸入\"Delete\""
|
||||
},
|
||||
"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": "此工具修改 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鍵繼續"
|
||||
}
|
||||
}
|
||||
}
|
||||
4
logo.py
4
logo.py
@@ -79,11 +79,11 @@ Contributors:
|
||||
BasaiCorp aliensb handwerk2016 Nigel1992
|
||||
UntaDotMy RenjiYuusei imbajin ahmed98Osama
|
||||
bingoohuang mALIk-sHAHId MFaiqKhan httpmerak
|
||||
muhammedfurkan plamkatawe
|
||||
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)
|
||||
|
||||
525
main.py
525
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,17 +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')}{Style.RESET_ALL})",
|
||||
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
|
||||
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
|
||||
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
|
||||
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
|
||||
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
|
||||
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
|
||||
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
|
||||
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}"
|
||||
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
|
||||
@@ -361,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():
|
||||
@@ -383,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:
|
||||
@@ -549,76 +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 = 12
|
||||
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()
|
||||
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
|
||||
|
||||
|
||||
313
new_tempemail.py
313
new_tempemail.py
@@ -1,313 +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()
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
|
||||
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}")
|
||||
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()
|
||||
810
oauth_auth.py
810
oauth_auth.py
File diff suppressed because it is too large
Load Diff
@@ -8,3 +8,5 @@ pyinstaller
|
||||
DrissionPage>=4.0.0
|
||||
selenium
|
||||
webdriver_manager
|
||||
arabic-reshaper
|
||||
python-bidi
|
||||
|
||||
@@ -8,12 +8,13 @@ import sqlite3
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
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()
|
||||
@@ -25,10 +26,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()
|
||||
@@ -46,9 +61,28 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
default_paths = {
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app")]
|
||||
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app"), "/usr/lib/cursor/app/"]
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Look for extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
# Also check current directory for extraction without home path prefix
|
||||
current_dir_paths = glob.glob("squashfs-root/usr/share/cursor/resources/app")
|
||||
|
||||
# Add any found paths to the Linux paths list
|
||||
default_paths["Linux"].extend(extracted_usr_paths)
|
||||
default_paths["Linux"].extend(current_dir_paths)
|
||||
|
||||
# Print debug information
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Available paths found:{Style.RESET_ALL}")
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {path} (exists){Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {path} (not found){Style.RESET_ALL}")
|
||||
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
@@ -159,6 +193,14 @@ def get_cursor_machine_id_path(translator=None) -> str:
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
@@ -166,14 +208,19 @@ def get_workbench_cursor_path(translator=None) -> str:
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Add extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
|
||||
if system not in paths_map:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
@@ -181,11 +228,23 @@ def get_workbench_cursor_path(translator=None) -> str:
|
||||
if system == "Linux":
|
||||
for base in paths_map["Linux"]["bases"]:
|
||||
main_path = os.path.join(base, paths_map["Linux"]["main"])
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
|
||||
if os.path.exists(main_path):
|
||||
return main_path
|
||||
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
|
||||
|
||||
base_path = paths_map[system]["base"]
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
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):
|
||||
@@ -297,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'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
elif sys.platform == "linux":
|
||||
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
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):
|
||||
@@ -374,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)
|
||||
@@ -424,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}")
|
||||
@@ -601,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}")
|
||||
@@ -680,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()
|
||||
@@ -749,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}")
|
||||
@@ -786,4 +851,4 @@ def run(translator=None):
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
run(main_translator)
|
||||
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 });
|
||||
172
utils.py
172
utils.py
@@ -9,15 +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":
|
||||
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