Compare commits
284 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d7c1c496 | ||
|
|
a56b978669 | ||
|
|
9793b91bc7 | ||
|
|
735dd8c1eb | ||
|
|
e5d192efcb | ||
|
|
a4d5b0e467 | ||
|
|
d7b056b339 | ||
|
|
bd152be4e8 | ||
|
|
172d9fb4bb | ||
|
|
e2d70c1738 | ||
|
|
650e2d33db | ||
|
|
2d1347c8c6 | ||
|
|
67d3554664 | ||
|
|
cb202e08e5 | ||
|
|
0c0ec47432 | ||
|
|
f00678ddcb | ||
|
|
dbc220053d | ||
|
|
d7ab362c4c | ||
|
|
4448a62458 | ||
|
|
50d09e5172 | ||
|
|
ffff3bdeb1 | ||
|
|
816a09d4de | ||
|
|
e5b7e5727c | ||
|
|
087e3ebf78 | ||
|
|
bd96107911 | ||
|
|
17799e0209 | ||
|
|
c15ea25cb3 | ||
|
|
350d781ce8 | ||
|
|
4485cc5571 | ||
|
|
df58b2e4ab | ||
|
|
5f380ebe5e | ||
|
|
c97bfd1475 | ||
|
|
28cd662e83 | ||
|
|
1b6ba5eab8 | ||
|
|
3e3cd40e3f | ||
|
|
8f35f163c5 | ||
|
|
f6ffb18427 | ||
|
|
b6bf62f841 | ||
|
|
1c1174fa6c | ||
|
|
4587fd9373 | ||
|
|
51fcf83ebb | ||
|
|
5cc7630ce6 | ||
|
|
bd9e10056f | ||
|
|
36f5356b4a | ||
|
|
18fc0532fd | ||
|
|
7405c61cc9 | ||
|
|
80e9946294 | ||
|
|
4ef293da81 | ||
|
|
eb6ef6bb3b | ||
|
|
3700239c99 | ||
|
|
a8966de771 | ||
|
|
8943266f6e | ||
|
|
35ed9cb6f6 | ||
|
|
3d1450ced0 | ||
|
|
892fc85efa | ||
|
|
feaf9906ba | ||
|
|
568e38e70c | ||
|
|
ab7281921e | ||
|
|
58b4b0dece | ||
|
|
07bee3207d | ||
|
|
06a9d47dad | ||
|
|
048a69be8f | ||
|
|
0b2081175d | ||
|
|
eb31209b6a | ||
|
|
7a239d3348 | ||
|
|
a9eed9818c | ||
|
|
0260d6be04 | ||
|
|
cff7e05e4e | ||
|
|
9fe6d1ae2c | ||
|
|
b3f905acb6 | ||
|
|
492482199a | ||
|
|
846a044704 | ||
|
|
b131281515 | ||
|
|
b797d93db5 | ||
|
|
e0a7afd835 | ||
|
|
902f6bd6f8 | ||
|
|
56cdeaa116 | ||
|
|
225a24aad5 | ||
|
|
6464306958 | ||
|
|
5d70214a2f | ||
|
|
854e987927 | ||
|
|
8f47801dad | ||
|
|
05e4d7faa9 | ||
|
|
34f5c679a5 | ||
|
|
f90a2916b1 | ||
|
|
7b757c2d57 | ||
|
|
209c58e3f8 | ||
|
|
caa47fad03 | ||
|
|
47e4a752a3 | ||
|
|
bd254cb43c | ||
|
|
b5d50ac15a | ||
|
|
dd190fac8e | ||
|
|
7971b6449e | ||
|
|
e58acce44e | ||
|
|
dc8cbca8ce | ||
|
|
2385cc2b3f | ||
|
|
52348d565d | ||
|
|
93f4f05ac4 | ||
|
|
869f1e5225 | ||
|
|
f6c958ccbb | ||
|
|
8fd4235fba | ||
|
|
5d3439a7d8 | ||
|
|
684323e328 | ||
|
|
8115a0b397 | ||
|
|
cca8ba1ae7 | ||
|
|
a392c8cc27 | ||
|
|
63209d6ed6 | ||
|
|
cbdd4fae4a | ||
|
|
414b5a7837 | ||
|
|
cba470344f | ||
|
|
12a3a522be | ||
|
|
6346059916 | ||
|
|
ab8489cdb9 | ||
|
|
bcb3565b9e | ||
|
|
c30e7dc3df | ||
|
|
aad19d89e4 | ||
|
|
4eac8a9e0e | ||
|
|
fd2da5d701 | ||
|
|
a7433ec032 | ||
|
|
bef2162509 | ||
|
|
eb6a5d5ac7 | ||
|
|
17212081ae | ||
|
|
5544a78f6f | ||
|
|
415da78768 | ||
|
|
2cb788ddc1 | ||
|
|
60ff9897f3 | ||
|
|
21ca0a6b2d | ||
|
|
1dba533e93 | ||
|
|
9146677bc4 | ||
|
|
50366e319a | ||
|
|
ad98bed98d | ||
|
|
6f2ec1b373 | ||
|
|
5574091273 | ||
|
|
0226c9e735 | ||
|
|
375347a7a5 | ||
|
|
8301ea74d5 | ||
|
|
96981a2c37 | ||
|
|
975647765d | ||
|
|
808b1ba3dc | ||
|
|
9595a8792f | ||
|
|
6b5dfab362 | ||
|
|
d5fceb2624 | ||
|
|
45046002df | ||
|
|
6bb33cec4c | ||
|
|
66f6197e6d | ||
|
|
5514e47759 | ||
|
|
3a53d59d52 | ||
|
|
c2e5499f19 | ||
|
|
9a223756c5 | ||
|
|
9b5912357d | ||
|
|
3200271156 | ||
|
|
8401f4718e | ||
|
|
b761bf0b6d | ||
|
|
db95689a8e | ||
|
|
652ffc809a | ||
|
|
a854969682 | ||
|
|
9c5ac85759 | ||
|
|
a67264d5c2 | ||
|
|
68b1dae466 | ||
|
|
0da6f9a1b7 | ||
|
|
59fccecb0f | ||
|
|
1769d245f9 | ||
|
|
b98f094407 | ||
|
|
ff358588bb | ||
|
|
6ca80ccb10 | ||
|
|
2fca5218fb | ||
|
|
71ecf5a201 | ||
|
|
4570b174ab | ||
|
|
f708ce443b | ||
|
|
4f6f3fe814 | ||
|
|
54ecf2d752 | ||
|
|
1f1231d1a9 | ||
|
|
d10999a517 | ||
|
|
fb4be6334a | ||
|
|
786eba5371 | ||
|
|
41ddbf519e | ||
|
|
ffd48201fd | ||
|
|
cd4f36725c | ||
|
|
dccb524bd7 | ||
|
|
90e9a5b287 | ||
|
|
66a67fce8b | ||
|
|
0981f00b9c | ||
|
|
02851c9a09 | ||
|
|
6312d66813 | ||
|
|
1b1a21f3d7 | ||
|
|
c94bd605e5 | ||
|
|
9f6eee77e0 | ||
|
|
2bdcc2f633 | ||
|
|
4aabe2e403 | ||
|
|
005aa2cd95 | ||
|
|
14f6dfc29d | ||
|
|
2e9bd269ad | ||
|
|
f9b7e23253 | ||
|
|
0d979f7543 | ||
|
|
cce3025f7f | ||
|
|
fe3e27561b | ||
|
|
bf2bea71eb | ||
|
|
77a61647dd | ||
|
|
813dd4431e | ||
|
|
d7116b8cf3 | ||
|
|
f5a7acc4e3 | ||
|
|
479933844a | ||
|
|
93046d7f03 | ||
|
|
e7ca31b710 | ||
|
|
87b99b0d16 | ||
|
|
e983b6f560 | ||
|
|
21ca7ab24f | ||
|
|
e7468644a4 | ||
|
|
2bca7d7d14 | ||
|
|
040c5f5836 | ||
|
|
4a86bbeeb4 | ||
|
|
68b7888a83 | ||
|
|
78dee025a7 | ||
|
|
17c2b4b243 | ||
|
|
2acb271f19 | ||
|
|
b688b67c26 | ||
|
|
3543615a69 | ||
|
|
24b0a5c09e | ||
|
|
5bbba05f69 | ||
|
|
72c95e4b4f | ||
|
|
8afd5df4ea | ||
|
|
a7a97b5621 | ||
|
|
e2e2ebc12e | ||
|
|
d131bccac0 | ||
|
|
d852bcff50 | ||
|
|
4c91525082 | ||
|
|
b565232ba6 | ||
|
|
5725a2b2f7 | ||
|
|
6ec6f8bdbf | ||
|
|
5c1bcff55a | ||
|
|
025c4e2ef3 | ||
|
|
6ac1294bee | ||
|
|
fb443592d3 | ||
|
|
c30a62b072 | ||
|
|
8bc509cccf | ||
|
|
9f814708d1 | ||
|
|
1889f9827a | ||
|
|
57d2d40e67 | ||
|
|
04fa6ee935 | ||
|
|
ed1e0f787e | ||
|
|
240716e45f | ||
|
|
35bbe6c93c | ||
|
|
fed50a31cc | ||
|
|
57ea4dd25a | ||
|
|
72eadfaf74 | ||
|
|
9521c0ce75 | ||
|
|
637f923c16 | ||
|
|
1cc93ffc22 | ||
|
|
887239d80c | ||
|
|
5d944fa3b1 | ||
|
|
654157b371 | ||
|
|
f4314cc6da | ||
|
|
1f29b2dbbe | ||
|
|
9c2b3f2fc8 | ||
|
|
8e20a0ed3d | ||
|
|
4122701468 | ||
|
|
1ee9813155 | ||
|
|
a9e4b3a5c6 | ||
|
|
a83851d441 | ||
|
|
1e290d0417 | ||
|
|
53ab15604e | ||
|
|
638916e5ef | ||
|
|
9500ce1249 | ||
|
|
721c13cb2f | ||
|
|
a04eed2c6b | ||
|
|
8107bede63 | ||
|
|
4166bc5135 | ||
|
|
69994d2486 | ||
|
|
ffcb8ec140 | ||
|
|
b61b61c607 | ||
|
|
7326d0eeb0 | ||
|
|
371f00645d | ||
|
|
a873291481 | ||
|
|
7710ea4bb3 | ||
|
|
13483eed77 | ||
|
|
eb69f933af | ||
|
|
54cd8cf323 | ||
|
|
4e0289c86c | ||
|
|
af4a1c4065 | ||
|
|
e5a4134a41 | ||
|
|
731db36121 | ||
|
|
f139fa189d | ||
|
|
a7e927a557 | ||
|
|
ffd7b839e7 |
70
.github/ISSUE_TEMPLATE/cn_bug_report.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: ❌ 错误报告 [中文]
|
||||
description: 创建一个报告以帮助我们改进
|
||||
title: '[Bug]: '
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您花时间填写此错误报告!
|
||||
在提交 Issue 前请确保您已经阅读了[Github Issues](https://github.com/yeongpin/cursor-free-vip/issues)
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: 提交前检查
|
||||
description: |
|
||||
请确保您在提交 Issue 前已经完成了以下所有步骤
|
||||
options:
|
||||
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
|
||||
required: true
|
||||
- label: 我已经查看了置顶 Issue 并搜索了现有的 [开放 Issue](https://github.com/yeongpin/cursor-free-vip/issues)和[已关闭 Issue](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的问题。
|
||||
required: true
|
||||
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等。
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: 平台
|
||||
description: 您正在使用哪个平台?
|
||||
options:
|
||||
- Windows x32
|
||||
- Windows x64
|
||||
- macOS Intel
|
||||
- macOS ARM64
|
||||
- Linux x64
|
||||
- Linux ARM64
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本
|
||||
description: 您正在运行的 Cursor Free Vip 版本是什么?
|
||||
placeholder: 例如 v1.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 错误描述
|
||||
description: 描述问题时请尽可能详细
|
||||
placeholder: 告诉我们发生了什么...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 相关日志输出
|
||||
description: 请复制并粘贴任何相关的日志输出
|
||||
render: shell
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: 附加信息
|
||||
description: 任何能让我们对你所遇到的问题有更多了解的东西
|
||||
75
.github/ISSUE_TEMPLATE/cn_question.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: ❓ 讨论 & 提问 (中文)
|
||||
description: 寻求帮助、讨论问题、提出疑问等...
|
||||
title: '[讨论]: '
|
||||
labels: ['question']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您的提问!请尽可能详细地描述您的问题,这样我们才能更好地帮助您。
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Issue 检查清单
|
||||
description: |
|
||||
在提交 Issue 前请确保您已经完成了以下所有步骤
|
||||
options:
|
||||
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
|
||||
required: true
|
||||
- label: 我确认自己需要的是提出问题并且讨论问题,而不是 Bug 反馈或需求建议。
|
||||
required: true
|
||||
- label: 我已阅读 [Github Issues](https://github.com/yeongpin/cursor-free-vip/issues) 并搜索了现有的 [开放 Issue](https://github.com/yeongpin/cursor-free-vip/issues) 和 [已关闭 Issue](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的问题。
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: 平台
|
||||
description: 您正在使用哪个平台?
|
||||
options:
|
||||
- Windows x32
|
||||
- Windows x64
|
||||
- macOS Intel
|
||||
- macOS ARM64
|
||||
- Linux x64
|
||||
- Linux ARM64
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本
|
||||
description: 您正在运行的 Cursor Free Vip 版本是什么?
|
||||
placeholder: 例如 v1.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: 您的问题
|
||||
description: 请详细描述您的问题
|
||||
placeholder: 请尽可能清楚地说明您的问题...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: 补充信息
|
||||
description: 任何其他相关的信息、截图或代码示例
|
||||
render: shell
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: 优先级
|
||||
description: 这个问题对您来说有多紧急?
|
||||
options:
|
||||
- 低 (有空再看)
|
||||
- 中 (希望尽快得到答复)
|
||||
- 高 (阻碍工作进行)
|
||||
validations:
|
||||
required: true
|
||||
70
.github/ISSUE_TEMPLATE/en_bug_report.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: ❌ Bug Report [English]
|
||||
description: Create a report to help us improve
|
||||
title: '[Bug]: '
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out this bug report!
|
||||
Before submitting this issue, please ensure that you have read the [github issues](https://github.com/yeongpin/cursor-free-vip/issues)
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Commit before submitting
|
||||
description: |
|
||||
Please ensure that you have completed all of the following steps before submitting an issue
|
||||
options:
|
||||
- label: I understand that Issues are used to provide feedback and solve problems, not to complain in the comments section, and will provide more information to help solve the problem.
|
||||
required: true
|
||||
- label: I have checked the top Issue and searched for existing [open issues](https://github.com/yeongpin/cursor-free-vip/issues) and [closed issues](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20), and found no similar issues.
|
||||
required: true
|
||||
- label: I have filled out a short and clear title, so that developers can quickly determine the general problem when browsing the Issue list. Not "a suggestion", "stuck", etc.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Which platform are you using?
|
||||
options:
|
||||
- Windows x32
|
||||
- Windows x64
|
||||
- macOS Intel
|
||||
- macOS ARM64
|
||||
- Linux x64
|
||||
- Linux ARM64
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Cursor Free Vip are you running?
|
||||
placeholder: For example v1.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe the problem as detailed as possible
|
||||
placeholder: Tell us what happened...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Related log output
|
||||
description: Please copy and paste any related log output
|
||||
render: shell
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Anything that might help us understand the problem better
|
||||
75
.github/ISSUE_TEMPLATE/en_question.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: ❓ Discussion & Question [English]
|
||||
description: Seeking help, discussing problems, asking questions, etc.
|
||||
title: '[Discussion]: '
|
||||
labels: ['question']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for your question! Please describe your problem as detailed as possible so we can help you better.
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Issue Checklist
|
||||
description: |
|
||||
Please ensure that you have completed all of the following steps before submitting an issue
|
||||
options:
|
||||
- label: I understand that Issues are used to provide feedback and solve problems, not to complain in the comments section, and will provide more information to help solve the problem.
|
||||
required: true
|
||||
- label: I confirm that I need to raise questions and discuss problems, not Bug feedback or demand suggestions.
|
||||
required: true
|
||||
- label: I have read [Github Issues](https://github.com/yeongpin/cursor-free-vip/issues) and searched for existing [open issues](https://github.com/yeongpin/cursor-free-vip/issues) and [closed issues](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20), and found no similar issues.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Which platform are you using?
|
||||
options:
|
||||
- Windows x32
|
||||
- Windows x64
|
||||
- macOS Intel
|
||||
- macOS ARM64
|
||||
- Linux x64
|
||||
- Linux ARM64
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Cursor Free Vip are you running?
|
||||
placeholder: For example v1.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Your question
|
||||
description: Please describe your problem as detailed as possible
|
||||
placeholder: Please explain your question as clearly as possible...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Any other related information, screenshots, or code examples
|
||||
render: shell
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How urgent is this issue for you?
|
||||
options:
|
||||
- Low (I'll look at it when I have time)
|
||||
- Medium (I hope to get an answer soon)
|
||||
- High (It blocks my work)
|
||||
validations:
|
||||
required: true
|
||||
244
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
name: Build Executables
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version number (e.g. 1.0.9)'
|
||||
required: true
|
||||
default: '1.8.04'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
create-tag:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # 获取所有标签
|
||||
|
||||
- name: Delete existing tag if exists
|
||||
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
|
||||
fi
|
||||
|
||||
- name: Create Tag
|
||||
run: |
|
||||
git tag "v${{ github.event.inputs.version }}"
|
||||
git push origin "v${{ github.event.inputs.version }}"
|
||||
|
||||
build-windows:
|
||||
needs: create-tag
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Build EXE
|
||||
run: |
|
||||
pyinstaller build.spec
|
||||
|
||||
- name: Upload Windows artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
|
||||
build-macos-arm64:
|
||||
needs: create-tag
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Build MacOS ARM executable
|
||||
run: |
|
||||
pyinstaller build.spec
|
||||
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64"
|
||||
|
||||
- name: Upload MacOS ARM artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
|
||||
build-linux-x64:
|
||||
needs: create-tag
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Build Linux x64 executable
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
pyinstaller build.spec
|
||||
mv "dist/CursorFreeVIP_${{ env.VERSION }}_linux" "dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64"
|
||||
echo "Contents of dist directory:"
|
||||
ls -la dist/
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_linux_x64
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
|
||||
|
||||
build-linux-arm64:
|
||||
needs: create-tag
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.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 "
|
||||
apt-get update && apt-get install -y build-essential
|
||||
pip install --upgrade pip
|
||||
pip install pyinstaller
|
||||
pip install -r requirements.txt
|
||||
python -m PyInstaller build.spec
|
||||
mv /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
|
||||
"
|
||||
echo "Contents of dist directory:"
|
||||
ls -la dist/
|
||||
|
||||
- name: Upload Linux ARM64 artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_linux_arm64
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
|
||||
|
||||
build-macos-intel:
|
||||
needs: create-tag
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Set version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
arch -x86_64 pip3 install --upgrade pip
|
||||
arch -x86_64 pip3 install pyinstaller
|
||||
arch -x86_64 pip3 install -r requirements.txt
|
||||
|
||||
- name: Build MacOS Intel executable
|
||||
env:
|
||||
TARGET_ARCH: 'x86_64'
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
arch -x86_64 python3 -m PyInstaller build.spec
|
||||
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel"
|
||||
|
||||
- name: Upload MacOS Intel artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
|
||||
create-release:
|
||||
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Get version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Prepare release files
|
||||
run: |
|
||||
cd artifacts
|
||||
echo "Contents of artifacts directory:"
|
||||
ls -la
|
||||
echo "Contents of subdirectories:"
|
||||
ls -la */
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
files: |
|
||||
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
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
53
.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
__pycache__
|
||||
server/
|
||||
venv/
|
||||
check_license.py
|
||||
cursor_modifier.py
|
||||
reset_machine.py
|
||||
Run_Venv.bat
|
||||
token_monitor.py
|
||||
get_mac.py
|
||||
.gitignore
|
||||
build.bat
|
||||
build.mac.command
|
||||
build.py
|
||||
build.sh
|
||||
ENV/
|
||||
test.py
|
||||
new_tempemail_smail.py
|
||||
new_tempemail_api.py
|
||||
|
||||
install.bat
|
||||
run.bat
|
||||
|
||||
temp_account_info.txt
|
||||
|
||||
.env copy
|
||||
|
||||
# PyInstaller
|
||||
build/
|
||||
dist/
|
||||
*.spec2
|
||||
|
||||
credentials.txt
|
||||
cursor_accounts.txt
|
||||
recaptcha.py
|
||||
install_requirements.bat
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Project specific
|
||||
*.log
|
||||
*.db
|
||||
*.sqlite3
|
||||
|
||||
# Mac
|
||||
run_venv.mac.command
|
||||
451
CHANGELOG.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# Change Log
|
||||
|
||||
## v1.8.04
|
||||
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
|
||||
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
|
||||
3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.03
|
||||
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.02
|
||||
1. Add: New Temp Email | 增加新臨時郵箱
|
||||
2. Add: Config Options | 增加配置選項
|
||||
3. Add: Update Windows Machine ID | 增加更新 Windows 機器 ID
|
||||
4. Add: Contributors Options | 增加貢獻者選項
|
||||
5. Add: Check update enable Options In config | 增加在 config 中檢查更新選項
|
||||
6. Add: Show account info enabled options in config | 增加在 config 中顯示賬號信息選項
|
||||
7. Optimize Row & Colume Options | 優化行與列選項
|
||||
8. Fix: Too Many Free Trial On Some Machine | 修復某些機器上太多免費試用
|
||||
9. Fix: Disable Auto Update | 修復禁用自動更新
|
||||
10. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
|
||||
11. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.01
|
||||
1. Add: Cursor Account Info | 增加 Cursor 賬號信息
|
||||
2. Fix: Disable Auto Update | 修復禁用自動更新
|
||||
3. Add: 0.48.x Version Support | 增加 0.48.x 版本支持
|
||||
4. Revert: Totally Reser Cursor to Beta | 恢復完全重置 Cursor 到 Beta
|
||||
5. Reopen: Totally Reset Cursor | 重新開啟完全重置 Cursor
|
||||
6. Fix: Logo.py Center | 修復 Logo.py 居中
|
||||
7. Fix: Linux Chrome Not Open Correct | 修復 Linux Chrome 未正確打開
|
||||
8. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.7.18
|
||||
1. Fix: No Write Permission | 修復沒有寫入權限
|
||||
2. Fix: Improve Linux path detection and config handling | 修正 linux 路徑和config寫入讀取
|
||||
3. Fix: Locale path_no_exist missing | 修正 path_no_exist 語言遺失
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.7.17
|
||||
1. Fix: Remove 10 options Totally Reset Cursor | 修復完全重置 Cursor 選項
|
||||
|
||||
## v1.7.16
|
||||
1. Add bulgarian language | 增加保加利亚语
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
3. Add: Contributors | 增加貢獻者
|
||||
4. Fix: Total Reset Cursor | 修復完全重置 Cursor
|
||||
5. Add: Display Features and Warnings | 增加顯示功能與警告
|
||||
6. Fix: Totally Reset Cursor | 修復完全重置 Cursor
|
||||
7. Remake: Logo.py Center | 重做 Logo.py 居中
|
||||
|
||||
## v1.7.15
|
||||
1. Fix: Cant Verify the User is Human | 修復無法驗證用戶是否為人類
|
||||
2. Added temporary email & GitHub + Cursor AI registration automation | 增加临时邮箱 & GitHub + Cursor AI 注册自动化
|
||||
3. Added Turkish language support | 增加土耳其语支持
|
||||
4. Removed outdated temporary option in Option 2 | 移除选项2中的过期临时添加项
|
||||
5. Enhanced machine ID reset (Linux, Windows, macOS), bypasses Cursor free trial detection | 机器 ID 重置支持 Linux/Windows/macOS,绕过 Cursor 免费试用检测
|
||||
6. Expanded Cursor AI file detection, deep removal of leftover trial files | 扩展 Cursor AI 文件检测,深度清理残留试用文件
|
||||
7. Optimized logging, replaced print with logging module, added verification steps | 日志优化,统一采用 logging 模块,增加验证步骤提示
|
||||
8. Added retry mechanism for email verification | 增加邮箱验证重试机制
|
||||
9. Automated GitHub OAuth login for Cursor AI | 自动 GitHub OAuth 登录 Cursor AI
|
||||
10. Saved registered GitHub accounts and timestamps | 保存 GitHub 账户和注册时间戳
|
||||
11. Added user confirmation before execution | 添加用户确认步骤,防止误操作
|
||||
12. Displayed feature list & warnings before actions | 显示功能与风险警告
|
||||
13. Improved Selenium flow and error handling | 增强 Selenium 流程与错误处理
|
||||
14. Added Chrome process tracking and cleanup | 增加 Chrome 进程跟踪和清理
|
||||
|
||||
## v1.7.14
|
||||
1. Added a Russian locale to program, fixed a typo in readme.md. Also translated other files |
|
||||
為程式新增了俄語語系,修正了 readme.md 的拼寫錯誤,並翻譯了其他文件。
|
||||
|
||||
2. Changing the directory from ~/.config/Cursor to ~/.config/cursor |
|
||||
將目錄從 `~/.config/Cursor` 更改為 `~/.config/cursor`。
|
||||
|
||||
3. Changing the filename from machineId to machineid |
|
||||
將檔案名稱從 `machineId` 更改為 `machineid`。
|
||||
|
||||
4. Updated all related paths in: |
|
||||
更新了以下檔案中的相關路徑:
|
||||
- `reset_machine_manual.py`
|
||||
- `config.py`
|
||||
|
||||
5. Added Linux path note in README.md |
|
||||
在 `README.md` 中新增了 Linux 路徑的說明。
|
||||
|
||||
6. These changes align with Linux filesystem conventions and fix issues with Chrome/Chromium integration |
|
||||
這些變更符合 Linux 文件系統的規範,並修復了與 Chrome/Chromium 整合的問題。
|
||||
|
||||
7. This PR adds retry logic to handle the 'Can't verify the user is human' error during registration |
|
||||
此 PR 新增了重試機制,以處理註冊時的「無法驗證用戶是否為人類」錯誤。
|
||||
|
||||
8. Added max 5 retries for form submission when human verification fails |
|
||||
當人類驗證失敗時,最多允許 5 次表單提交重試。
|
||||
|
||||
9. Added random delays between retries (2-4 seconds) |
|
||||
在重試之間隨機延遲 2-4 秒。
|
||||
|
||||
10. Enhanced browser fingerprint randomization to better bypass detection |
|
||||
增強了瀏覽器指紋的隨機性,以更好地繞過檢測。
|
||||
|
||||
11. Added new translation strings for retry status messages |
|
||||
新增了重試狀態訊息的翻譯字串。
|
||||
|
||||
12. Improved error handling and user feedback |
|
||||
改進了錯誤處理和用戶回饋機制。
|
||||
|
||||
13. The changes ensure a more robust registration process by automatically retrying when human verification fails, while maintaining human-like behavior through randomized delays and improved browser fingerprinting |
|
||||
這些變更確保了更穩定的註冊流程,透過自動重試機制處理人類驗證失敗的情況,同時透過隨機延遲與增強的瀏覽器指紋技術,維持類似人類的行為模式。
|
||||
|
||||
## v1.7.13
|
||||
1. Added _delete_current_account method to handle account deletion via API | 新增 _delete_current_account 方法,透過 API 處理帳號刪除
|
||||
|
||||
2. Updated account reset logic to use the appropriate auth method based on auth_type | 更新帳號重置邏輯,根據 auth_type 選擇適當的驗證方式
|
||||
|
||||
3. Maintained existing Google OAuth reset functionality | 維持現有的 Google OAuth 重置功能
|
||||
|
||||
4. Added proper error handling for account deletion failures | 新增帳號刪除失敗時的錯誤處理
|
||||
|
||||
5. Ensures GitHub authentication maintains its flow when resetting accounts | 確保 GitHub 認證在帳號重置時保持正常流程
|
||||
|
||||
6. The _delete_current_account method makes a POST request to https://www.cursor.com/api/dashboard/delete-account |
|
||||
_delete_current_account 方法會發送 POST 請求至 https://www.cursor.com/api/dashboard/delete-account
|
||||
|
||||
7. After successful deletion, redirects back to the authentication page | 刪除成功後,會導回驗證頁面
|
||||
|
||||
8. Uses Promise-based JavaScript for reliable API communication | 使用 Promise-based JavaScript,確保 API 通訊穩定
|
||||
|
||||
9. Includes proper error handling and logging | 包含適當的錯誤處理與日誌記錄
|
||||
|
||||
10. Add Brazilian Portuguese language | 新增巴西葡萄牙語
|
||||
|
||||
|
||||
## v1.7.12
|
||||
1. Add: Changelog Show in Menu | 增加更新日志在菜單中
|
||||
2. Remake Create Mail Logic | 重做創建郵箱邏輯
|
||||
3. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.7.11 ( Skip & Merge to v1.7.12 )
|
||||
1. Add: Multi-language Support | 增加多語言支持
|
||||
2. Add: German Language | 增加德語
|
||||
3. Add: Dutch Language | 增加荷蘭語
|
||||
4. Add: French Language | 增加法語
|
||||
5. Add: Auto Detect Max Use Count | 增加自動檢測最大使用次數
|
||||
6. Add: Detect & Auto Delete Account | 增加檢測 & 自動刪除賬號
|
||||
7. Add: Optimize Some Logic | 優化一些邏輯
|
||||
8. Add: Local Blocked Domains | 增加本地被屏蔽域名
|
||||
9. Fix : Get Verification Code for None | 修復獲取驗證碼為 None
|
||||
|
||||
## v1.7.10
|
||||
1. Add: Totally Reset Cursor | 增加完全重置 Cursor
|
||||
2. Add: Multi-language Support for Totally Reset | 增加完全重置多語言支持
|
||||
|
||||
## v1.7.09
|
||||
1. Add: Development Version Check | 增加開發版本檢查
|
||||
2. Remove: Github Trial Reset | 移除 Github 試用重置
|
||||
3. Fixed: Some Issues | 修復一些問題
|
||||
|
||||
## v1.7.08
|
||||
1. Add: Google OAuth Authentication | 增加 Google OAuth 認證
|
||||
2. Add: GitHub OAuth Authentication | 增加 GitHub OAuth 認證
|
||||
3. Add: Lifetime Access for OAuth Users | 增加 OAuth 用戶終身訪問權限
|
||||
4. Add: OAuth Authentication Integration | 增加 OAuth 認證集成
|
||||
5. Update: Menu System with OAuth Options | 更新菜單系統,添加 OAuth 選項
|
||||
6. Add: Multi-language Support for OAuth | 增加 OAuth 多語言支持
|
||||
|
||||
## v1.7.07
|
||||
1. Add: Vietnamese Language | 增加越南語言
|
||||
2. Add: Admin Privilege Management for Windows Executable | 增加 Windows 可執行文件管理員權限
|
||||
3. Implement admin privilege detection for Windows platform | 實現 Windows 平台管理員權限檢測
|
||||
4. Add functions to check and request admin rights when running as a frozen executable | 增加檢查和請求管理員權限的功能
|
||||
5. Enhance startup process with admin privilege verification | 增強啟動過程中的管理員權限驗證
|
||||
6. Add new admin-related emoji to the EMOJI dictionary | 增加新的管理員相關表情符號到 EMOJI 字典
|
||||
7. Provide fallback mechanism for non-Windows platforms (macos and linux ) | 提供非 Windows 平台(macos 和 linux)的回退機制
|
||||
These changes make the application more user-friendly by only requesting admin privileges when necessary (when running as an executable). | 這些改進使應用程序更易於使用,只在必要時(當作為可執行文件運行時)請求管理員權限。
|
||||
|
||||
## v1.7.06
|
||||
1. Add: Update Confirm | 增加更新確認
|
||||
2. Add: Update Skipped | 增加更新跳過
|
||||
3. Add: Invalid Choice | 增加無效選擇
|
||||
4. Fix: Cursor Path | 修復 Cursor 路徑
|
||||
5. Fix: Path Encoding | 修復路徑編碼
|
||||
6. Fix: Getting Verification Code | 修復獲取驗證碼
|
||||
7. Fix: Setting Password | 修復設置密碼
|
||||
8. Fix: Disable Auto Update | 修復禁用自動更新
|
||||
9. Add Config.py | 增加 Config.py
|
||||
10. Add utils.py | 增加 utils.py
|
||||
11. Rebuild some logic | 重新構建一些邏輯
|
||||
|
||||
|
||||
## v1.7.05
|
||||
1. Fix: Cursor Version Check | 修復 Cursor 版本檢查
|
||||
2. Fix: Small Problem | 修復一些小問題
|
||||
|
||||
|
||||
## v1.7.04
|
||||
1. Hotfix: Small Problem | 修復一些小問題
|
||||
|
||||
## v1.7.03
|
||||
1. Hotfix: Small Problem | 修復一些小問題
|
||||
|
||||
## v1.7.02
|
||||
1. Fix: Cursor Path | 修復 Cursor 路徑
|
||||
2. Add: Config File | 增加配置文件
|
||||
3. Remove: Workbench Cursor Path | 移除 Workbench Cursor 路徑
|
||||
4. Remove: Cursor Main JS | 移除 Cursor main.js
|
||||
|
||||
## v1.7.01
|
||||
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
|
||||
- Optimization: Improve code maintainability and make configuration management and browser settings more clear
|
||||
- Improvement: The creation and update logic of the configuration file is clearer and more independent
|
||||
|
||||
## v1.6.03
|
||||
1. Hotfix: Small Problem | 修復一些問題
|
||||
|
||||
## v1.6.02
|
||||
1. Hotfix: Small Problem | 修復一些問題
|
||||
2. Add: Test some Bypass Code | 測試一些繞過代碼
|
||||
|
||||
## v1.6.01
|
||||
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 失敗
|
||||
|
||||
## v1.5.03
|
||||
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
|
||||
2. Small Fix: Error Handling | 小修錯誤處理
|
||||
3. Small Fix: Translation | 小修翻譯
|
||||
4. Small Fix: Performance | 小修性能
|
||||
|
||||
## v1.5.02
|
||||
1. Add: Generate Random Name Alias | 增加生成隨機真實姓名
|
||||
2. Add: Realistic Name Input | 增加真實姓名輸入
|
||||
3. Optimize: Error Handling | 優化錯誤處理
|
||||
4. Optimize: Translation | 優化翻譯
|
||||
5. Optimize: Performance | 優化性能
|
||||
|
||||
## v1.5.01
|
||||
1. Add: Check Latest Version | 增加檢查最新版本
|
||||
2. Add: Update Command | 增加更新命令
|
||||
|
||||
## v1.4.08
|
||||
1. Add: Print Some Account Info | 增加打印一些賬號信息
|
||||
|
||||
## v1.4.07
|
||||
1. Add Removed break statements after each operation | 修改結束 event 後的 break 暫停應用
|
||||
2. Added print_menu() calls to show the menu again | 添加 print_menu()調用以再次顯示菜單
|
||||
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
|
||||
|
||||
## v1.4.06
|
||||
|
||||
1. Add: Blocked Domains Loaded | 增加被屏蔽的域名加載
|
||||
2. Fix: Cleanup Error | 修復清理進程時出錯
|
||||
3. Fix: Blocked Domains Loaded Error | 修復被屏蔽的域名加載錯誤
|
||||
4. Fix: Available Domains Loaded Error | 修復可用域名加載錯誤
|
||||
5. Fix: Domains Filtered Error | 修復過濾後剩餘域名錯誤
|
||||
6. Fix: Domains Excluded Error | 修復排除域名錯誤
|
||||
|
||||
|
||||
## v1.4.05
|
||||
|
||||
1. Fix: macOS Language Detection | 修復 macOS 語言檢測
|
||||
|
||||
|
||||
## v1.4.04
|
||||
|
||||
1. Change Some Language Info to English | 更改一些語言信息為英文
|
||||
2. Add Auto Detect System Language | 增加自動檢測系統語言
|
||||
3. Fixed Some Issues | 修復一些問題
|
||||
|
||||
|
||||
## v1.4.03
|
||||
|
||||
1. Switch to API-based Registration System | 改用 API 註冊系統替代瀏覽器操作
|
||||
2. Add Support for Latest Cursor Version | 增加支持最新版本 Cursor
|
||||
3. Enhance Translation System | 優化多語言翻譯系統
|
||||
4. Add Database Connection Status Messages | 增加數據庫連接狀態提示
|
||||
5. Improve Error Handling for Database Operations | 改進數據庫操作的錯誤處理
|
||||
6. Add New API Integration | 新增 API 集成
|
||||
7. Optimize Performance and Stability | 優化性能和穩定性
|
||||
|
||||
## v1.4.01
|
||||
|
||||
1. Add Disable Cursor Auto Upgrade | 增加禁用 Cursor 自動升級
|
||||
|
||||
## v1.3.02
|
||||
|
||||
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
|
||||
2. Add PayPal | 增加 PayPal
|
||||
3. Very Small Fix | 非常小的修復
|
||||
4. Fix main.py option number | 修復 main.py 選項數量
|
||||
|
||||
## v1.3.01
|
||||
|
||||
1. Add Manual Email Input | 增加手動輸入郵箱地址
|
||||
2. Add Manual Code Input | 增加手動輸入驗證碼
|
||||
3. Fix Cursor Options | 修復 Cursor 選項
|
||||
|
||||
|
||||
## v1.2.02
|
||||
|
||||
1. Add PBlock | 增加 PBlock
|
||||
2. Remove uBlock0.chromium | 移除 uBlock0.chromium
|
||||
3. Optimize the logic of the script | 優化腳本邏輯
|
||||
4. Optimize Size | 優化大小
|
||||
|
||||
|
||||
## v1.2.01
|
||||
|
||||
1. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
|
||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||
3. Change Mail Site | 改變郵箱網站
|
||||
4. Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
|
||||
|
||||
|
||||
## v1.1.01
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
||||
</p>
|
||||
|
||||
1. Hot Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
|
||||
2. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
|
||||
3. Remake signup logic | 重做註冊邏輯
|
||||
|
||||
|
||||
## v1.0.10
|
||||
|
||||
1. Hot Fix Mac Chrome Problem | 修復 Mac Chrome 問題
|
||||
2. Fix Linux Start Donet Problem | 修復 Linux 啟動開發者問題
|
||||
|
||||
|
||||
## v1.0.9
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
||||
</p>
|
||||
|
||||
1. Fixed New 0.45.x Version Reset Machine | 修復新 0.45 版本重置機器
|
||||
2. Fix Locale Language | 修復多語言
|
||||
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
||||
4. Add Remake main.js | 重做 main.js
|
||||
|
||||
|
||||
## v1.0.8
|
||||
|
||||
1. Fix New 0.45 Version Reset Machine | 修復新 0.45 版本重置機器
|
||||
2. Fix Locale Language | 修復多語言
|
||||
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
||||
|
||||
|
||||
## v1.0.7 - HotFix
|
||||
|
||||
1. Fix Reset Machine | 修復重置機器
|
||||
2. Fix Locale Language | 修復多語言
|
||||
|
||||
|
||||
## v1.0.7
|
||||
|
||||
1. Add Locale Language Support | 增加多語言支持
|
||||
<p align="center">
|
||||
<img src="./images/locale_2025-01-15_13-40-08.png" alt="locale" width="400"/><br>
|
||||
</p>
|
||||
|
||||
|
||||
## v1.0.6
|
||||
|
||||
1. Add Quit Cursor Option | 增加退出 Cursor 選項
|
||||
2. Add Recaptcha Path Patch | 增加 Recaptcha 路徑修復
|
||||
3. Fix Admin Permission | 修復管理員權限問題
|
||||
4. Remove all need admin permission | 移除所有需要管理員權限
|
||||
|
||||
|
||||
## v1.0.5 - HotFix
|
||||
|
||||
1. Fix: Mac Browser Control | 修復 Mac 瀏覽器控制問題
|
||||
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
||||
3. Add Linux Support | 增加 Linux 支持
|
||||
<p align="center">
|
||||
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
|
||||
</p>
|
||||
|
||||
|
||||
## v1.0.5
|
||||
|
||||
1. Remove MachineID | 移除機器碼 ID
|
||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
||||
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-14_14-40-37.png" alt="Why" width="400"/><br>
|
||||
</p>
|
||||
|
||||
|
||||
## v1.0.4
|
||||
|
||||
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||
2. Fix Cloud Lame | 修復雲端慢速模式
|
||||
|
||||
|
||||
## v1.0.3
|
||||
|
||||
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||
2. Add Manual Reset Machine | 增加手動重置機器
|
||||
3. Add CDN Cloud Control WatchDog | 增加 CDN 雲端控制 WatchDog
|
||||
4. Add Mac OS Support | 增加 Mac OS 支持
|
||||
5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊
|
||||
<p align="center">
|
||||
<img src="./images/what_2025-01-13_13-32-54.png" alt="Why" width="400"/><br>
|
||||
</p>
|
||||
|
||||
|
||||
## v1.0.2
|
||||
|
||||
1. Fix: Some known issues | 修復了一些已知問題
|
||||
2. Add cloud control device code | 增加雲端控制設備碼
|
||||
3. Cloud reset device code | 雲端重置設備碼
|
||||
4. Remove official WatchDog monitoring | 移除官方 WatchDog 監控
|
||||
5. Remove Proxy official prompt | 移除 Proxy 官方提示
|
||||
6. Fix: Too Many Computer | 修復 Too Many Computer 問題
|
||||
7. Fix Billing Issue | 修復計費問題
|
||||
8. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||
9. Fix cursor-slow mode | 修復 cursor-slow 模式
|
||||
|
||||
|
||||
## v1.0.1
|
||||
|
||||
1. Fix: Reset machine ID | 修復了重置機器 ID 的問題
|
||||
2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題
|
||||
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為 pro 會員的問題
|
||||
4. Fix: Real-time send Token request | 修復了 實時發送 Token 請求的問題
|
||||
5. Fix: Reset Cursor's configuration | 修復了 重置 Cursor 的配置的問題
|
||||
|
||||
|
||||
|
||||
## v1.0
|
||||
1. Preview Image | 預覽圖<br>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_00-50-40.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_00-51-07.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下 MachineID 聯繫作者
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
5
PBlock/background.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// 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
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
);
|
||||
14
PBlock/default_filters.js
Normal file
@@ -0,0 +1,14 @@
|
||||
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/*",
|
||||
]
|
||||
BIN
PBlock/enabled16.png
Normal file
|
After Width: | Height: | Size: 850 B |
BIN
PBlock/enabled48.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
29
PBlock/manifest.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"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://*/*"
|
||||
]
|
||||
}
|
||||
5
PBlock/rules.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"id": "ruleset_1",
|
||||
"version": "1.0",
|
||||
"rules": []
|
||||
}
|
||||
259
README.md
@@ -1,7 +1,8 @@
|
||||
# ➤ Cursor Free VIP
|
||||
|
||||
<div align="center">
|
||||
<p align="center">
|
||||
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200"/>
|
||||
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -9,197 +10,219 @@
|
||||
[](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)
|
||||
|
||||
</p>
|
||||
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
|
||||
|
||||
This is a tool to automatically register (except for Google verification code), support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
||||
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.
|
||||
|
||||
這是一個自動化工具,自動註冊(除了Google驗證碼),支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
||||
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.
|
||||
|
||||
|
||||
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-14_14-40-37.png" alt="new" width="400"/><br>
|
||||
<img src="./images/new_2025-03-22_19-53-10.png" alt="new" width="400" style="border-radius: 6px;"/><br>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="./images/free_2025-01-14_14-59-15.png" alt="free" width="400"/><br>
|
||||
</p>
|
||||
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||
|
||||
## ⚠️ Google Recaptcha need to be manually verified, don't be lazy, move your fingers, verify it, otherwise it will keep prompting you to verify ⚠️
|
||||
### If you dont 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/)下載
|
||||
|
||||
## ⚠️ 郵箱驗證 需要手動驗證,不要那麼懶,動一動手指,驗證一下,不然會一直提示你驗證 ⚠️
|
||||
### 如果沒有Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## 🔄 Change Log | 更新日志
|
||||
|
||||
## 🔄 更新日志
|
||||
<details open>
|
||||
<summary>v1.0.5 - HotFix</summary>
|
||||
|
||||
1. Fix: Mac Browser Control | 修復Mac瀏覽器控制問題
|
||||
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
||||
3. Add Linux Support | 增加Linux支持
|
||||
<p align="center">
|
||||
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.5</summary>
|
||||
|
||||
1. Remove MachineID | 移除機器碼ID
|
||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
||||
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-14_14-40-37.png" alt="Why" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.4</summary>
|
||||
|
||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
2. Fix Cloud Lame | 修復雲端慢速模式
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.3</summary>
|
||||
|
||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
2. Add Manual Reset Machine | 增加手動重置機器
|
||||
3. Add CDN Cloud Control WatchDog | 增加CDN雲端控制WatchDog
|
||||
4. Add Mac OS Support | 增加Mac OS支持
|
||||
5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊
|
||||
<p align="center">
|
||||
<img src="./images/what_2025-01-13_13-32-54.png" alt="Why" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.2</summary>
|
||||
|
||||
1. Fix: Some known issues | 修復了一些已知問題
|
||||
2. Add cloud control device code | 增加雲端控制設備碼
|
||||
3. Cloud reset device code | 雲端重置設備碼
|
||||
4. Remove official WatchDog monitoring | 移除官方WatchDog監控
|
||||
5. Remove Proxy official prompt | 移除Proxy 官方提示
|
||||
6. Fix: Too Many Computer | 修復Too Many Computer 問題
|
||||
7. Fix Billing Issue | 修復計費問題
|
||||
8. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
9. Fix cursor-slow mode | 修復cursor-slow模式
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.1</summary>
|
||||
|
||||
1. Fix: Reset machine ID | 修復了重置機器ID的問題
|
||||
2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題
|
||||
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為pro會員的問題
|
||||
4. Fix: Real-time send Token request | 修復了 實時發送Token請求的問題
|
||||
5. Fix: Reset Cursor's configuration | 修復了 重置Cursor的配置的問題
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>v1.0</summary>
|
||||
1. Preview Image | 預覽圖<br>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_00-50-40.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_00-51-07.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下MachineID 聯繫作者
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
[Watch Change Log | 查看更新日志](CHANGELOG.md)
|
||||
|
||||
## ✨ Features | 功能特點
|
||||
|
||||
* Automatically register Cursor membership<br>自動註冊Cursor會員<br>
|
||||
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
|
||||
|
||||
* Except for Google verification code<br>除了Google驗證碼<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>
|
||||
* Complete Auth verification<br>完成 Auth 驗證<br>
|
||||
|
||||
* Reset Cursor's configuration<br>重置Cursor的配置<br>
|
||||
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
|
||||
|
||||
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
|
||||
|
||||
## 💻 System Support | 系統支持
|
||||
|
||||
|Windows|x64|✅|macOS|Intel|✅|
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Windows|x86|✅|macOS|Apple Silicon|✅|
|
||||
|Linux|x64|✅|Linux|x86|✅|
|
||||
|Linux|ARM64|✅|Linux|ARM64|✅|
|
||||
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|
||||
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
|
||||
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
|
||||
| Linux | x64 | ✅ | Linux | x86 | ✅ |
|
||||
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
|
||||
|
||||
## 👀 How to use | 如何使用
|
||||
|⚠️Must logout your account before running the script⚠️|⚠️必須先登出你的帳戶再運行腳本⚠️ |
|
||||
|:---:|:---:|
|
||||
<br>
|
||||
|
||||
<details open>
|
||||
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
||||
|
||||
**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**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
|
||||
```
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
||||
If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
||||
|
||||
## ❗ Note | 注意事項
|
||||
|
||||
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
||||
📝 Config | 文件配置
|
||||
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
|
||||
<details>
|
||||
<summary><b>⭐ Config | 文件配置</b></summary>
|
||||
|
||||
* Do not close this script when using Cursor <br>使用Cursor時請勿關閉此腳本<br>
|
||||
```
|
||||
[Chrome]
|
||||
# Default Google Chrome Path | 默認Google Chrome 遊覽器路徑
|
||||
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
|
||||
|
||||
[Turnstile]
|
||||
# Handle Turnstile Wait Time | 等待人機驗證時間
|
||||
handle_turnstile_time = 2
|
||||
# Handle Turnstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
|
||||
handle_turnstile_random_time = 1-3
|
||||
|
||||
[OSPaths]
|
||||
# Storage Path | 存儲路徑
|
||||
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
|
||||
# SQLite Path | SQLite路徑
|
||||
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
|
||||
# Machine ID Path | 機器ID路徑
|
||||
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
|
||||
# For Linux users: ~/.config/cursor/machineid
|
||||
|
||||
[Timing]
|
||||
# Min Random Time | 最小隨機時間
|
||||
min_random_time = 0.1
|
||||
# Max Random Time | 最大隨機時間
|
||||
max_random_time = 0.8
|
||||
# Page Load Wait | 頁面加載等待時間
|
||||
page_load_wait = 0.1-0.8
|
||||
# Input Wait | 輸入等待時間
|
||||
input_wait = 0.3-0.8
|
||||
# Submit Wait | 提交等待時間
|
||||
submit_wait = 0.5-1.5
|
||||
# Verification Code Input | 驗證碼輸入等待時間
|
||||
verification_code_input = 0.1-0.3
|
||||
# Verification Success Wait | 驗證成功等待時間
|
||||
verification_success_wait = 2-3
|
||||
# Verification Retry Wait | 驗證重試等待時間
|
||||
verification_retry_wait = 2-3
|
||||
# Email Check Initial Wait | 郵件檢查初始等待時間
|
||||
email_check_initial_wait = 4-6
|
||||
# Email Refresh Wait | 郵件刷新等待時間
|
||||
email_refresh_wait = 2-4
|
||||
# Settings Page Load Wait | 設置頁面加載等待時間
|
||||
settings_page_load_wait = 1-2
|
||||
# Failed Retry Time | 失敗重試時間
|
||||
failed_retry_time = 0.5-1
|
||||
# Retry Interval | 重試間隔
|
||||
retry_interval = 8-12
|
||||
# Max Timeout | 最大超時時間
|
||||
max_timeout = 160
|
||||
|
||||
[Utils]
|
||||
# Check Update | 檢查更新
|
||||
check_update = True
|
||||
# Show Account Info | 顯示賬號信息
|
||||
show_account_info = True
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
* Use administrator privileges to run the script <br>請使用管理員身份運行腳本
|
||||
|
||||
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
||||
|
||||
* This tool is only for learning and research purposes <br>此工具僅供學習和研究使用<br>
|
||||
|
||||
* Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款
|
||||
|
||||
|
||||
|
||||
## 🚨 Common Issues | 常見問題
|
||||
|
||||
|如果遇到權限問題,請確保:|If you encounter permission issues, please ensure:|
|
||||
|:---:|:---:|
|
||||
| 此腳本以管理員身份運行 | This script is run with administrator privileges |
|
||||
|
||||
|
||||
|
||||
| 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 |
|
||||
|:--------------------------------------------------:|:------------------------------------------------:|
|
||||
| If you encounter permission issues, please ensure: | This script is run with administrator privileges |
|
||||
| Error 'User is not authorized' | This means your account was banned for using temporary (disposal) mail. Ensure using a non-temporary mail service |
|
||||
## 🤩 Contribution | 貢獻
|
||||
|
||||
歡迎提交 Issue 和 Pull Request!
|
||||
|
||||
|
||||
<a href="https://github.com/yeongpin/cursor-free-vip/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=yeongpin/cursor-free-vip&preview=true&max=&columns=" />
|
||||
</a>
|
||||
<br /><br />
|
||||
|
||||
## 📩 Disclaimer | 免責聲明
|
||||
|
||||
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
||||
|
||||
源代碼靈感來之 | Original code inspiration from [Here](https://github.com/hmhm2022/gpt-cursor-auto)
|
||||
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne
|
||||
by the user.
|
||||
|
||||
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
|
||||
## 💰 Buy Me a Coffee | 請我喝杯咖啡
|
||||
|
||||
<div align="center">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="./images/provi-code.jpg" alt="buy_me_a_coffee" width="280"/><br>
|
||||
</td>
|
||||
<td>
|
||||
<img src="./images/paypal.png" alt="buy_me_a_coffee" width="280"/><br>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
## ⭐ Star History | 星星數
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://star-history.com/#yeongpin/cursor-free-vip&Date)
|
||||
|
||||
</div>
|
||||
|
||||
## 📝 License | 授權
|
||||
|
||||
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
|
||||
Please refer to the [LICENSE](LICENSE.md) file for details.
|
||||
|
||||
22
block_domain.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
oakon.com
|
||||
famamail.com
|
||||
2925.com
|
||||
indigobook.com
|
||||
teihu.com
|
||||
raleigh-construction.com
|
||||
pastryofistanbul.com
|
||||
linshiyouxiang.net
|
||||
Mohmal.com
|
||||
pusmail.com
|
||||
questtechsystems.com
|
||||
ikomail.com
|
||||
ofanda.com
|
||||
pusmail.com
|
||||
ikomail.com
|
||||
mailpull.com
|
||||
drewzen.com
|
||||
begemail.com
|
||||
dugmail.com
|
||||
solerbe.net
|
||||
corhash.net
|
||||
mailshou.com
|
||||
98
browser.py
@@ -1,98 +0,0 @@
|
||||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
|
||||
class BrowserManager:
|
||||
def __init__(self, noheader=False):
|
||||
self.browser = None
|
||||
self.noheader = noheader
|
||||
|
||||
def init_browser(self):
|
||||
"""初始化浏览器"""
|
||||
co = self._get_browser_options()
|
||||
|
||||
# 如果设置了 noheader,添加相应的参数
|
||||
if self.noheader:
|
||||
co.set_argument('--headless=new')
|
||||
|
||||
self.browser = ChromiumPage(co)
|
||||
return self.browser
|
||||
|
||||
def _get_browser_options(self):
|
||||
"""获取浏览器配置"""
|
||||
co = ChromiumOptions()
|
||||
try:
|
||||
extension_path = self._get_extension_path()
|
||||
co.add_extension(extension_path)
|
||||
|
||||
extension_block_path = self.get_extension_block()
|
||||
co.add_extension(extension_block_path)
|
||||
|
||||
extension_recaptcha_path = self.get_extension_recaptcha()
|
||||
co.add_extension(extension_recaptcha_path)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logging.warning(f"警告: {e}")
|
||||
|
||||
co.set_user_agent(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36"
|
||||
)
|
||||
co.set_pref("credentials_enable_service", False)
|
||||
co.set_argument("--hide-crash-restore-bubble")
|
||||
co.auto_port()
|
||||
|
||||
# Mac 系统特殊处理
|
||||
if sys.platform == "darwin":
|
||||
co.set_argument("--no-sandbox")
|
||||
co.set_argument("--disable-gpu")
|
||||
|
||||
return co
|
||||
|
||||
def _get_extension_path(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "turnstilePatch")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def get_extension_block(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "uBlock0.chromium")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "uBlock0.chromium")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def get_extension_recaptcha(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "recaptchaPatch")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "recaptchaPatch")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def quit(self):
|
||||
"""关闭浏览器"""
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
30
build.bat
@@ -2,28 +2,28 @@
|
||||
chcp 65001 > nul
|
||||
cls
|
||||
|
||||
:: 檢查是否以管理員權限運行
|
||||
:: Check if running with administrator privileges
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% == 0 (
|
||||
:: 如果是管理員權限,只創建虛擬環境後就降權運行
|
||||
:: If running with administrator privileges, create virtual environment and then run with normal user privileges
|
||||
if not exist venv (
|
||||
echo ℹ️ 正在創建虛擬環境...
|
||||
python -m venv venv
|
||||
)
|
||||
|
||||
:: 降權運行剩餘的步驟
|
||||
:: Run remaining steps with normal user privileges
|
||||
echo ℹ️ 以普通用戶權限繼續...
|
||||
powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO"
|
||||
exit /b
|
||||
) else (
|
||||
:: 檢查是否是第二階段運行
|
||||
:: Check if running in second stage
|
||||
if "%1"=="run" (
|
||||
goto RUN_BUILD
|
||||
) else (
|
||||
:: 如果是普通權限且需要創建虛擬環境,請求管理員權限
|
||||
:: If running with normal privileges and creating virtual environment is required, request administrator privileges
|
||||
if not exist venv (
|
||||
echo ⚠️ 需要管理員權限來創建虛擬環境
|
||||
echo ℹ️ 正在請求管理員權限...
|
||||
echo ⚠️ Requires administrator privileges to create virtual environment
|
||||
echo ℹ️ Requesting administrator privileges...
|
||||
powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'"
|
||||
exit /b
|
||||
) else (
|
||||
@@ -33,30 +33,30 @@ if %errorLevel% == 0 (
|
||||
)
|
||||
|
||||
:RUN_BUILD
|
||||
echo ℹ️ 啟動虛擬環境...
|
||||
echo ℹ️ Starting virtual environment...
|
||||
call venv\Scripts\activate.bat
|
||||
if errorlevel 1 (
|
||||
echo ❌ 啟動虛擬環境失敗
|
||||
echo ❌ Failed to start virtual environment
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 檢查並安裝缺失的依賴
|
||||
echo ℹ️ 檢查依賴...
|
||||
:: Check and install missing dependencies
|
||||
echo ℹ️ Checking dependencies...
|
||||
for /f "tokens=1" %%i in (requirements.txt) do (
|
||||
pip show %%i >nul 2>&1 || (
|
||||
echo ℹ️ 安裝 %%i...
|
||||
echo ℹ️ Installing %%i...
|
||||
pip install %%i
|
||||
)
|
||||
)
|
||||
|
||||
echo ℹ️ 開始構建...
|
||||
echo ℹ️ Starting build...
|
||||
python build.py
|
||||
if errorlevel 1 (
|
||||
echo ❌ 構建失敗
|
||||
echo ❌ Build failed
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ✅ 完成!
|
||||
echo ✅ Completed!
|
||||
pause
|
||||
26
build.py
@@ -8,7 +8,7 @@ import shutil
|
||||
from logo import print_logo
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 忽略特定警告
|
||||
# Ignore specific warnings
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
class LoadingAnimation:
|
||||
@@ -50,21 +50,21 @@ def simulate_progress(message, duration=1.0, steps=20):
|
||||
progress_bar(i, steps, prefix="Progress:", length=40)
|
||||
|
||||
def build():
|
||||
# 清理屏幕
|
||||
# Clean screen
|
||||
os.system("cls" if platform.system().lower() == "windows" else "clear")
|
||||
|
||||
# 顯示 logo
|
||||
# Display logo
|
||||
print_logo()
|
||||
|
||||
# 清理 PyInstaller 緩存
|
||||
print("\033[93m🧹 清理構建緩存...\033[0m")
|
||||
# Clean PyInstaller cache
|
||||
print("\033[93m🧹 Cleaning build cache...\033[0m")
|
||||
if os.path.exists('build'):
|
||||
shutil.rmtree('build')
|
||||
|
||||
# 重新加載環境變量以確保獲取最新版本
|
||||
# Reload environment variables to ensure getting the latest version
|
||||
load_dotenv(override=True)
|
||||
version = os.getenv('VERSION', '1.0.0')
|
||||
print(f"\033[93m📦 正在構建版本: v{version}\033[0m")
|
||||
print(f"\033[93m📦 Building version: v{version}\033[0m")
|
||||
|
||||
try:
|
||||
simulate_progress("Preparing build environment...", 0.5)
|
||||
@@ -72,7 +72,7 @@ def build():
|
||||
loading = LoadingAnimation()
|
||||
loading.start("Building in progress")
|
||||
|
||||
# 根据系统类型设置输出名称
|
||||
# Set output name based on system type
|
||||
system = platform.system().lower()
|
||||
if system == "windows":
|
||||
os_type = "windows"
|
||||
@@ -86,7 +86,7 @@ def build():
|
||||
|
||||
output_name = f"CursorFreeVIP_{version}_{os_type}"
|
||||
|
||||
# 构建命令
|
||||
# Build command
|
||||
build_command = f'pyinstaller --clean --noconfirm build.spec'
|
||||
output_path = os.path.join('dist', f'{output_name}{ext}')
|
||||
|
||||
@@ -95,16 +95,16 @@ def build():
|
||||
loading.stop()
|
||||
|
||||
if os.path.exists(output_path):
|
||||
print(f"\n\033[92m✅ 構建完成!")
|
||||
print(f"📦 可執行文件位於: {output_path}\033[0m")
|
||||
print(f"\n\033[92m✅ Build completed!")
|
||||
print(f"📦 Executable file located: {output_path}\033[0m")
|
||||
else:
|
||||
print("\n\033[91m❌ 構建失敗:未找到輸出文件\033[0m")
|
||||
print("\n\033[91m❌ Build failed: Output file not found\033[0m")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if loading:
|
||||
loading.stop()
|
||||
print(f"\n\033[91m❌ 構建過程出錯: {str(e)}\033[0m")
|
||||
print(f"\n\033[91m❌ Build process error: {str(e)}\033[0m")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
20
build.sh
@@ -8,7 +8,7 @@ NC='\033[0m' # No Color
|
||||
|
||||
# 检查并安装必要的依赖
|
||||
check_dependencies() {
|
||||
echo -e "${YELLOW}检查系统依赖...${NC}"
|
||||
echo -e "${YELLOW}Checking system dependencies...${NC}"
|
||||
|
||||
# 检查是否为 Ubuntu/Debian
|
||||
if [ -f /etc/debian_version ]; then
|
||||
@@ -16,42 +16,42 @@ check_dependencies() {
|
||||
PACKAGES="python3 python3-pip python3-venv"
|
||||
for pkg in $PACKAGES; do
|
||||
if ! dpkg -l | grep -q "^ii $pkg "; then
|
||||
echo -e "${YELLOW}安装 $pkg...${NC}"
|
||||
echo -e "${YELLOW}Installing $pkg...${NC}"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y $pkg
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${RED}不支持的系统,请手动安装 python3, pip3 和 python3-venv${NC}"
|
||||
echo -e "${RED}Unsupported system, please install python3, pip3 and python3-venv manually${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建并激活虚拟环境
|
||||
setup_venv() {
|
||||
echo -e "${GREEN}正在创建虚拟环境...${NC}"
|
||||
echo -e "${GREEN}Creating virtual environment...${NC}"
|
||||
python3 -m venv venv
|
||||
|
||||
echo -e "${GREEN}启动虚拟环境...${NC}"
|
||||
echo -e "${GREEN}Starting virtual environment...${NC}"
|
||||
. ./venv/bin/activate || source ./venv/bin/activate
|
||||
}
|
||||
|
||||
# 安装依赖
|
||||
install_dependencies() {
|
||||
echo -e "${GREEN}安装依赖...${NC}"
|
||||
echo -e "${GREEN}Installing dependencies...${NC}"
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r requirements.txt
|
||||
}
|
||||
|
||||
# 构建程序
|
||||
build_program() {
|
||||
echo -e "${GREEN}开始构建...${NC}"
|
||||
echo -e "${GREEN}Starting build...${NC}"
|
||||
python3 build.py
|
||||
}
|
||||
|
||||
# 清理
|
||||
cleanup() {
|
||||
echo -e "${GREEN}清理虚拟环境...${NC}"
|
||||
echo -e "${GREEN}Cleaning virtual environment...${NC}"
|
||||
deactivate 2>/dev/null || true
|
||||
rm -rf venv
|
||||
}
|
||||
@@ -73,8 +73,8 @@ main() {
|
||||
# 清理
|
||||
cleanup
|
||||
|
||||
echo -e "${GREEN}完成!${NC}"
|
||||
echo "按任意键退出..."
|
||||
echo -e "${GREEN}Completed!${NC}"
|
||||
echo "Press any key to exit..."
|
||||
# 使用兼容的方式读取输入
|
||||
if [ "$(uname)" = "Linux" ]; then
|
||||
read dummy
|
||||
|
||||
18
build.spec
@@ -24,19 +24,25 @@ a = Analysis(
|
||||
binaries=[],
|
||||
datas=[
|
||||
('turnstilePatch', 'turnstilePatch'),
|
||||
('recaptchaPatch', 'recaptchaPatch'),
|
||||
('uBlock0.chromium', 'uBlock0.chromium'),
|
||||
('PBlock', 'PBlock'),
|
||||
('locales', 'locales'),
|
||||
('cursor_auth.py', '.'),
|
||||
('reset_machine_manual.py', '.'),
|
||||
('cursor_register.py', '.'),
|
||||
('browser.py', '.'),
|
||||
('control.py', '.')
|
||||
('new_signup.py', '.'),
|
||||
('new_tempemail.py', '.'),
|
||||
('quit_cursor.py', '.'),
|
||||
('cursor_register_manual.py', '.'),
|
||||
('.env', '.'),
|
||||
('block_domain.txt', '.')
|
||||
],
|
||||
hiddenimports=[
|
||||
'cursor_auth',
|
||||
'reset_machine_manual',
|
||||
'browser',
|
||||
'control'
|
||||
'new_signup',
|
||||
'new_tempemail',
|
||||
'quit_cursor',
|
||||
'cursor_register_manual'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
||||
259
config.py
Normal file
@@ -0,0 +1,259 @@
|
||||
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
|
||||
|
||||
EMOJI = {
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"ERROR": "❌",
|
||||
"SUCCESS": "✅",
|
||||
"ADMIN": "🔒",
|
||||
"ARROW": "➡️",
|
||||
"USER": "👤",
|
||||
"KEY": "🔑",
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# Default configuration
|
||||
default_config = {
|
||||
'Chrome': {
|
||||
'chromepath': get_default_chrome_path()
|
||||
},
|
||||
'Turnstile': {
|
||||
'handle_turnstile_time': '2',
|
||||
'handle_turnstile_random_time': '1-3'
|
||||
},
|
||||
'Timing': {
|
||||
'min_random_time': '0.1',
|
||||
'max_random_time': '0.8',
|
||||
'page_load_wait': '0.1-0.8',
|
||||
'input_wait': '0.3-0.8',
|
||||
'submit_wait': '0.5-1.5',
|
||||
'verification_code_input': '0.1-0.3',
|
||||
'verification_success_wait': '2-3',
|
||||
'verification_retry_wait': '2-3',
|
||||
'email_check_initial_wait': '4-6',
|
||||
'email_refresh_wait': '2-4',
|
||||
'settings_page_load_wait': '1-2',
|
||||
'failed_retry_time': '0.5-1',
|
||||
'retry_interval': '8-12',
|
||||
'max_timeout': '160'
|
||||
},
|
||||
'Utils': {
|
||||
'enabled_update_check': 'True',
|
||||
'enabled_account_info': 'True'
|
||||
}
|
||||
}
|
||||
|
||||
# Add system-specific path configuration
|
||||
if sys.platform == "win32":
|
||||
appdata = os.getenv("APPDATA")
|
||||
localappdata = os.getenv("LOCALAPPDATA", "")
|
||||
default_config['WindowsPaths'] = {
|
||||
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
|
||||
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
|
||||
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
|
||||
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
|
||||
'updater_path': os.path.join(localappdata, "cursor-updater"),
|
||||
'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml")
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
default_config['MacPaths'] = {
|
||||
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
|
||||
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
|
||||
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
|
||||
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
|
||||
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||
'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml"
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "linux":
|
||||
# Get the actual user's home directory, handling both sudo and normal cases
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
current_user = sudo_user if sudo_user else (os.getenv('USER') or os.getenv('USERNAME'))
|
||||
|
||||
if not current_user:
|
||||
current_user = os.path.expanduser('~').split('/')[-1]
|
||||
|
||||
# Handle sudo case
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
root_home = "/root"
|
||||
else:
|
||||
actual_home = f"/home/{current_user}"
|
||||
root_home = None
|
||||
|
||||
if not os.path.exists(actual_home):
|
||||
actual_home = os.path.expanduser("~")
|
||||
|
||||
# Define base config directory
|
||||
config_base = os.path.join(actual_home, ".config")
|
||||
|
||||
# Try both "Cursor" and "cursor" directory names in both user and root locations
|
||||
cursor_dir = None
|
||||
possible_paths = [
|
||||
os.path.join(config_base, "Cursor"),
|
||||
os.path.join(config_base, "cursor"),
|
||||
os.path.join(root_home, ".config", "Cursor") if root_home else None,
|
||||
os.path.join(root_home, ".config", "cursor") if root_home else None
|
||||
]
|
||||
|
||||
for path in possible_paths:
|
||||
if path and os.path.exists(path):
|
||||
cursor_dir = path
|
||||
break
|
||||
|
||||
if not cursor_dir:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.neither_cursor_nor_cursor_directory_found', config_base=config_base) if translator else f'Neither Cursor nor cursor directory found in {config_base}'}{Style.RESET_ALL}")
|
||||
if root_home:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.also_checked', path=f'{root_home}/.config') if translator else f'Also checked {root_home}/.config'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
# Define Linux paths using the found cursor directory
|
||||
storage_path = os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/storage.json")) if cursor_dir else ""
|
||||
storage_dir = os.path.dirname(storage_path) if storage_path else ""
|
||||
|
||||
# Verify paths and permissions
|
||||
try:
|
||||
# Check storage directory
|
||||
if storage_dir and not os.path.exists(storage_dir):
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_directory_not_found', storage_dir=storage_dir) if translator else f'Storage directory not found: {storage_dir}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
# Check storage.json with more detailed verification
|
||||
if storage_path and os.path.exists(storage_path):
|
||||
# Get file stats
|
||||
try:
|
||||
stat = os.stat(storage_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.storage_file_found', storage_path=storage_path) if translator else f'Storage file found: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_size', size=stat.st_size) if translator else f'File size: {stat.st_size} bytes'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_permissions', permissions=oct(stat.st_mode & 0o777)) if translator else f'File permissions: {oct(stat.st_mode & 0o777)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_owner', owner=stat.st_uid) if translator else f'File owner: {stat.st_uid}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {translator.get('config.file_group', group=stat.st_gid) if translator else f'File group: {stat.st_gid}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_getting_file_stats', error=str(e)) if translator else f'Error getting file stats: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if file is readable and writable
|
||||
if not os.access(storage_path, os.R_OK | os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.permission_denied', storage_path=storage_path) if translator else f'Permission denied: {storage_path}'}{Style.RESET_ALL}")
|
||||
if sudo_user:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {sudo_user}:{sudo_user} {storage_path}') if translator else f'Try running: chown {sudo_user}:{sudo_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.try_running', command=f'chown {current_user}:{current_user} {storage_path}') if translator else f'Try running: chown {current_user}:{current_user} {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.and') if translator else 'And'}: chmod 644 {storage_path}{Style.RESET_ALL}")
|
||||
|
||||
# Try to read the file to verify it's not corrupted
|
||||
try:
|
||||
with open(storage_path, 'r') as f:
|
||||
content = f.read()
|
||||
if not content.strip():
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_is_empty', storage_path=storage_path) if translator else f'Storage file is empty: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted, please reinstall Cursor'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('config.storage_file_is_valid_and_contains_data') if translator else 'Storage file is valid and contains data'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_reading_storage_file', error=str(e)) if translator else f'Error reading storage file: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.the_file_might_be_corrupted_please_reinstall_cursor') if translator else 'The file might be corrupted. Please reinstall Cursor'}{Style.RESET_ALL}")
|
||||
elif storage_path:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.storage_file_not_found', storage_path=storage_path) if translator else f'Storage file not found: {storage_path}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('config.please_make_sure_cursor_is_installed_and_has_been_run_at_least_once') if translator else 'Please make sure Cursor is installed and has been run at least once'}{Style.RESET_ALL}")
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.error_checking_linux_paths', error=str(e)) if translator else f'Error checking Linux paths: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Define all paths using the found cursor directory
|
||||
default_config['LinuxPaths'] = {
|
||||
'storage_path': storage_path,
|
||||
'sqlite_path': os.path.abspath(os.path.join(cursor_dir, "User/globalStorage/state.vscdb")) if cursor_dir else "",
|
||||
'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "",
|
||||
'cursor_path': get_linux_cursor_path(),
|
||||
'updater_path': os.path.join(config_base, "cursor-updater"),
|
||||
'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else ""
|
||||
}
|
||||
|
||||
# Read existing configuration and merge
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file, encoding='utf-8')
|
||||
config_modified = False
|
||||
|
||||
for section, options in default_config.items():
|
||||
if not config.has_section(section):
|
||||
config.add_section(section)
|
||||
config_modified = True
|
||||
for option, value in options.items():
|
||||
if not config.has_option(section, option):
|
||||
config.set(section, option, str(value))
|
||||
config_modified = True
|
||||
if translator:
|
||||
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('config.config_updated') if translator else 'Config updated'}{Style.RESET_ALL}")
|
||||
else:
|
||||
for section, options in default_config.items():
|
||||
config.add_section(section)
|
||||
for option, value in options.items():
|
||||
config.set(section, option, str(value))
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
if translator:
|
||||
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('config.config_setup_error', error=str(e)) if translator else f'Error setting up config: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def print_config(config, translator=None):
|
||||
"""Print configuration in a readable format"""
|
||||
if not config:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.config_not_available') if translator else 'Configuration not available'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.configuration') if translator else 'Configuration'}:{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
for section in config.sections():
|
||||
print(f"{Fore.GREEN}[{section}]{Style.RESET_ALL}")
|
||||
for key, value in config.items(section):
|
||||
# 对布尔值进行特殊处理,使其显示为彩色
|
||||
if value.lower() in ('true', 'yes', 'on', '1'):
|
||||
value_display = f"{Fore.GREEN}{translator.get('config.enabled') if translator else 'Enabled'}{Style.RESET_ALL}"
|
||||
elif value.lower() in ('false', 'no', 'off', '0'):
|
||||
value_display = f"{Fore.RED}{translator.get('config.disabled') if translator else 'Disabled'}{Style.RESET_ALL}"
|
||||
else:
|
||||
value_display = value
|
||||
|
||||
print(f" {key} = {value_display}")
|
||||
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip", "config.ini")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_directory') if translator else 'Config Directory'}: {config_dir}{Style.RESET_ALL}")
|
||||
|
||||
print()
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
return setup_config(translator)
|
||||
378
control.py
@@ -1,378 +0,0 @@
|
||||
import time
|
||||
from colorama import Fore, Style
|
||||
import random
|
||||
import os
|
||||
|
||||
class BrowserControl:
|
||||
def __init__(self, browser):
|
||||
self.browser = browser
|
||||
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||
self.current_tab = None # 当前标签页
|
||||
self.signup_tab = None # 注册标签页
|
||||
self.email_tab = None # 邮箱标签页
|
||||
|
||||
def create_new_tab(self):
|
||||
"""创建新标签页"""
|
||||
try:
|
||||
# 保存当前标签页
|
||||
self.current_tab = self.browser
|
||||
|
||||
# 创建新的浏览器实例
|
||||
from browser import BrowserManager
|
||||
browser_manager = BrowserManager()
|
||||
new_browser = browser_manager.init_browser()
|
||||
|
||||
# 保存新标签页
|
||||
self.signup_tab = new_browser
|
||||
|
||||
print(f"{Fore.GREEN}Create New Tab Success | 成功创建新窗口{Style.RESET_ALL}")
|
||||
return new_browser
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Create New Tab Failed | 创建新窗口时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def switch_to_tab(self, browser):
|
||||
"""切换到指定浏览器窗口"""
|
||||
try:
|
||||
self.browser = browser
|
||||
print(f"{Fore.GREEN}Switch Tab Success | 成功切换窗口{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Switch Tab Failed | 切换窗口时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_current_tab(self):
|
||||
"""获取当前标签页"""
|
||||
return self.browser
|
||||
|
||||
def generate_new_email(self):
|
||||
"""点击新的按钮生成新邮箱"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Click Generate New Email | 点击生成新邮箱...{Style.RESET_ALL}")
|
||||
new_button = self.browser.ele('xpath://button[contains(@class, "egenbut")]')
|
||||
if new_button:
|
||||
new_button.click()
|
||||
time.sleep(1) # 等待生成
|
||||
print(f"{Fore.GREEN}Generate New Email | 成功生成新邮箱{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No Generate Button Found | 未找到生成按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Generate New Email Failed | 生成新邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def select_email_domain(self, domain_index=None):
|
||||
"""选择邮箱域名,如果不指定index则随机选择"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Select Email Domain | 选择邮箱域名...{Style.RESET_ALL}")
|
||||
# 找到下拉框
|
||||
select_element = self.browser.ele('xpath://select[@id="seldom"]')
|
||||
if select_element:
|
||||
# 获取所有选项,包括两个 optgroup 下的所有 option
|
||||
all_options = []
|
||||
|
||||
# 获取 "新的" 组下的选项
|
||||
new_options = self.browser.eles('xpath://select[@id="seldom"]/optgroup[@label="-- 新的 --"]/option')
|
||||
all_options.extend(new_options)
|
||||
|
||||
# 获取 "其他" 组下的选项
|
||||
other_options = self.browser.eles('xpath://select[@id="seldom"]/optgroup[@label="-- 其他 --"]/option')
|
||||
all_options.extend(other_options)
|
||||
|
||||
if all_options:
|
||||
# 如果没有指定索引,随机选择一个
|
||||
if domain_index is None:
|
||||
domain_index = random.randint(0, len(all_options) - 1)
|
||||
|
||||
if domain_index < len(all_options):
|
||||
# 获取选中选项的文本
|
||||
selected_domain = all_options[domain_index].text
|
||||
print(f"{Fore.CYAN}Select Email Domain | 选择域名: {selected_domain}{Style.RESET_ALL}")
|
||||
|
||||
# 点击选择
|
||||
all_options[domain_index].click()
|
||||
time.sleep(1)
|
||||
print(f"{Fore.GREEN}Select Email Domain | 成功选择邮箱域名{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}No Available Domain Options | 未找到可用的域名选项,总共有 {len(all_options)} 个选项{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.RED}No Domain Select Box Found | 未找到域名选择框{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Select Email Domain Failed | 选择邮箱域名时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def wait_for_page_load(self, seconds=2):
|
||||
"""等待页面加载"""
|
||||
time.sleep(seconds)
|
||||
|
||||
def navigate_to(self, url):
|
||||
"""导航到指定URL"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Navigate to {url} | 正在访问 {url}...{Style.RESET_ALL}")
|
||||
self.browser.get(url)
|
||||
self.wait_for_page_load()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Visit {url} Failed | 访问 {url} 时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def copy_and_get_email(self):
|
||||
"""获取邮箱地址"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Get Email Info | 获取邮箱信息...{Style.RESET_ALL}")
|
||||
|
||||
# 等待元素加载
|
||||
time.sleep(1)
|
||||
|
||||
# 获取邮箱名称
|
||||
try:
|
||||
email_div = self.browser.ele('xpath://div[@class="segen"]//div[contains(@style, "color: #e5e5e5")]')
|
||||
if email_div:
|
||||
email_name = email_div.text.split()[0]
|
||||
print(f"{Fore.CYAN}Get Email Name | 找到邮箱名称: {email_name}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}Get Email Name Failed | 无法找到邮箱名称元素{Style.RESET_ALL}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Email Name Failed | 获取邮箱名称时出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
# 直接使用上一步选择的域名
|
||||
try:
|
||||
domain = self.browser.ele('xpath://select[@id="seldom"]').value
|
||||
if not domain: # 如果获取不到value,尝试获取选中的选项文本
|
||||
selected_option = self.browser.ele('xpath://select[@id="seldom"]/option[1]')
|
||||
domain = selected_option.text if selected_option else "@yopmail.com" # 使用默认域名作为后备
|
||||
except:
|
||||
domain = "@yopmail.com" # 如果出错,使用默认域名
|
||||
|
||||
# 组合完整邮箱地址
|
||||
full_email = f"{email_name}{domain}"
|
||||
print(f"{Fore.GREEN}Get Email Address | 完整邮箱地址: {full_email}{Style.RESET_ALL}")
|
||||
return full_email
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Email Address Failed | 获取邮箱地址时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def view_mailbox(self):
|
||||
"""点击查看邮箱按钮"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Enter Mailbox | 正在进入邮箱...{Style.RESET_ALL}")
|
||||
view_button = self.browser.ele('xpath://button[contains(@class, "egenbut") and contains(.//span, "查看邮箱")]')
|
||||
if view_button:
|
||||
view_button.click()
|
||||
time.sleep(2) # 等待页面加载
|
||||
print(f"{Fore.GREEN}Successfully Entered Mailbox | 成功进入邮箱{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No View Mailbox Button Found | 未找到查看邮箱按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Enter Mailbox Failed | 进入邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def refresh_mailbox(self):
|
||||
"""刷新邮箱获取最新信息"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Refresh Mailbox | 正在刷新邮箱...{Style.RESET_ALL}")
|
||||
refresh_button = self.browser.ele('xpath://button[@id="refresh"]')
|
||||
if refresh_button:
|
||||
refresh_button.click()
|
||||
time.sleep(2) # 等待刷新完成
|
||||
print(f"{Fore.GREEN}Mailbox Refreshed Successfully | 邮箱刷新成功{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No Refresh Button Found | 未找到刷新按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Refresh Mailbox Failed | 刷新邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_and_click_recaptcha(self):
|
||||
"""检查并点击验证码复选框"""
|
||||
try:
|
||||
# 使用环境变量或配置文件中预设的坐标
|
||||
click_x = int(os.getenv('RECAPTCHA_X', '100')) # 默认值100
|
||||
click_y = int(os.getenv('RECAPTCHA_Y', '100')) # 默认值100
|
||||
|
||||
print(f"{Fore.CYAN}使用预设坐标点击: x={click_x}, y={click_y}{Style.RESET_ALL}")
|
||||
|
||||
# 直接点击预设坐标
|
||||
self.browser.page.mouse.click(click_x, click_y)
|
||||
print(f"{Fore.GREEN}Clicked reCAPTCHA Position | 已点击 reCAPTCHA 位置{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}Click reCAPTCHA Failed | 点击 reCAPTCHA 失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_verification_code(self):
|
||||
"""从邮件中获取验证码"""
|
||||
try:
|
||||
# 尝试所有可能的样式组合
|
||||
selectors = [
|
||||
# 新样式
|
||||
'xpath://div[contains(@style, "font-family:-apple-system") and contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "color:#202020")]',
|
||||
# 带行高的样式
|
||||
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "line-height:30px")]',
|
||||
# rgba 颜色样式
|
||||
'xpath://div[contains(@style, "font-size: 28px") and contains(@style, "letter-spacing: 2px") and contains(@style, "color: rgba(32, 32, 32, 1)")]',
|
||||
# 宽松样式
|
||||
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]'
|
||||
]
|
||||
|
||||
# 依次尝试每个选择器
|
||||
for selector in selectors:
|
||||
code_div = self.browser.ele(selector)
|
||||
if code_div:
|
||||
verification_code = code_div.text.strip()
|
||||
if verification_code.isdigit() and len(verification_code) == 6:
|
||||
print(f"{Fore.GREEN}Found Verification Code | 找到验证码: {verification_code}{Style.RESET_ALL}")
|
||||
return verification_code
|
||||
|
||||
print(f"{Fore.YELLOW}No Valid Verification Code Found | 未找到有效验证码{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Verification Code Error | 获取验证码时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def fill_verification_code(self, code):
|
||||
"""填写验证码"""
|
||||
try:
|
||||
if not code or len(code) != 6:
|
||||
print(f"{Fore.RED}Verification Code Format Error | 验证码格式不正确{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}Fill Verification Code | 正在填写验证码...{Style.RESET_ALL}")
|
||||
|
||||
# 记住当前标签页(邮箱页面)
|
||||
email_tab = self.browser
|
||||
|
||||
# 切换回注册页面标签
|
||||
self.switch_to_tab(self.signup_tab)
|
||||
time.sleep(1)
|
||||
|
||||
# 输入验证码
|
||||
for digit in code:
|
||||
self.browser.actions.input(digit)
|
||||
time.sleep(random.uniform(0.1, 0.3))
|
||||
|
||||
print(f"{Fore.GREEN}Verification Code Filled | 验证码填写完成{Style.RESET_ALL}")
|
||||
|
||||
# 等待页面加载和登录完成
|
||||
print(f"{Fore.CYAN}Wait for Login | 等待登录完成...{Style.RESET_ALL}")
|
||||
time.sleep(5)
|
||||
|
||||
# 先访问登录页面确保登录状态
|
||||
login_url = "https://authenticator.cursor.sh"
|
||||
self.browser.get(login_url)
|
||||
time.sleep(3) # 增加等待时间
|
||||
|
||||
# 获取cookies(第一次尝试)
|
||||
token = self.get_cursor_session_token()
|
||||
if not token:
|
||||
print(f"{Fore.YELLOW}Get Token Failed | 首次获取token失败,等待后重试...{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
token = self.get_cursor_session_token()
|
||||
|
||||
if token:
|
||||
self.save_token_to_file(token)
|
||||
|
||||
# 获取到token后再访问设置页面
|
||||
settings_url = "https://www.cursor.com/settings"
|
||||
print(f"{Fore.CYAN}Get Account Info | 正在访问设置页面获取账户信息...{Style.RESET_ALL}")
|
||||
self.browser.get(settings_url)
|
||||
time.sleep(2)
|
||||
|
||||
# 获取账户额度信息
|
||||
try:
|
||||
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.browser.ele(usage_selector)
|
||||
if usage_ele:
|
||||
usage_info = usage_ele.text
|
||||
total_usage = usage_info.split("/")[-1].strip()
|
||||
print(f"{Fore.GREEN}Account Usage Limit | 账户可用额度上限: {total_usage}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Account Usage Failed | 获取账户额度信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# 切换回邮箱页面
|
||||
self.switch_to_tab(email_tab)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Fill Verification Code Failed | 填写验证码时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_and_click_turnstile(self):
|
||||
"""检查并点击 Turnstile 验证框"""
|
||||
try:
|
||||
# 等待验证框出现
|
||||
time.sleep(1)
|
||||
|
||||
# 查找验证框
|
||||
verify_checkbox = self.browser.ele('xpath://label[contains(@class, "cb-lb")]//input[@type="checkbox"]')
|
||||
if verify_checkbox:
|
||||
print(f"{Fore.CYAN}Find Turnstile Verification Box | 找到 Turnstile 验证框,尝试点击...{Style.RESET_ALL}")
|
||||
verify_checkbox.click()
|
||||
time.sleep(2) # 等待验证完成
|
||||
print(f"{Fore.GREEN}Clicked Turnstile Verification Box | 已点击 Turnstile 验证框{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}Check and Click Turnstile Failed | 未找到 Turnstile 验证框或点击失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_cursor_session_token(self, max_attempts=3, retry_interval=2):
|
||||
"""获取Cursor会话token"""
|
||||
print(f"{Fore.CYAN}Get Cursor Session Token | 开始获取cookie...{Style.RESET_ALL}")
|
||||
attempts = 0
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
# 直接从浏览器对象获取cookies
|
||||
all_cookies = self.browser.get_cookies()
|
||||
|
||||
# 遍历查找目标cookie
|
||||
for cookie in all_cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
print(f"{Fore.GREEN}成功获取CursorSessionToken: {token}{Style.RESET_ALL}")
|
||||
return token
|
||||
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW} Try | 第 {attempts} 次尝试未获取到CursorSessionToken,{retry_interval}秒后重试...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
print(f"{Fore.RED}Reach Max Attempts ({max_attempts}) | 已达到最大尝试次数({max_attempts}),获取CursorSessionToken失败{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}获取cookie失败: {str(e)}{Style.RESET_ALL}")
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}Will Retry in {retry_interval} seconds | 将在 {retry_interval} 秒后重试...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
return None
|
||||
|
||||
def save_token_to_file(self, token):
|
||||
"""保存token到文件"""
|
||||
try:
|
||||
with open('cursor_tokens.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"Token: {token}\n")
|
||||
f.write("-" * 50 + "\n")
|
||||
print(f"{Fore.GREEN}Token Saved to cursor_tokens.txt | Token已保存到 cursor_tokens.txt{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Save Token Failed | 保存Token时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
552
cursor_acc_info.py
Normal file
@@ -0,0 +1,552 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
import sqlite3
|
||||
from typing import Dict, Optional
|
||||
import platform
|
||||
from colorama import Fore, Style, init
|
||||
import logging
|
||||
import re
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Setup logger
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"USER": "👤",
|
||||
"USAGE": "📊",
|
||||
"PREMIUM": "⭐",
|
||||
"BASIC": "📝",
|
||||
"SUBSCRIPTION": "💳",
|
||||
"INFO": "ℹ️",
|
||||
"ERROR": "❌",
|
||||
"SUCCESS": "✅",
|
||||
"WARNING": "⚠️",
|
||||
"TIME": "🕒"
|
||||
}
|
||||
|
||||
class Config:
|
||||
"""Config"""
|
||||
NAME_LOWER = "cursor"
|
||||
NAME_CAPITALIZE = "Cursor"
|
||||
BASE_HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
class UsageManager:
|
||||
"""Usage Manager"""
|
||||
|
||||
@staticmethod
|
||||
def get_proxy():
|
||||
"""get proxy"""
|
||||
# from config import get_config
|
||||
proxy = os.environ.get("HTTP_PROXY") or os.environ.get("HTTPS_PROXY")
|
||||
if proxy:
|
||||
return {"http": proxy, "https": proxy}
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_usage(token: str) -> Optional[Dict]:
|
||||
"""get usage"""
|
||||
url = f"https://www.{Config.NAME_LOWER}.com/api/usage"
|
||||
headers = Config.BASE_HEADERS.copy()
|
||||
headers.update({"Cookie": f"Workos{Config.NAME_CAPITALIZE}SessionToken=user_01OOOOOOOOOOOOOOOOOOOOOOOO%3A%3A{token}"})
|
||||
try:
|
||||
proxies = UsageManager.get_proxy()
|
||||
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# get Premium usage and limit
|
||||
gpt4_data = data.get("gpt-4", {})
|
||||
premium_usage = gpt4_data.get("numRequestsTotal", 0)
|
||||
max_premium_usage = gpt4_data.get("maxRequestUsage", 999)
|
||||
|
||||
# get Basic usage, but set limit to "No Limit"
|
||||
gpt35_data = data.get("gpt-3.5-turbo", {})
|
||||
basic_usage = gpt35_data.get("numRequestsTotal", 0)
|
||||
|
||||
return {
|
||||
'premium_usage': premium_usage,
|
||||
'max_premium_usage': max_premium_usage,
|
||||
'basic_usage': basic_usage,
|
||||
'max_basic_usage': "No Limit" # set Basic limit to "No Limit"
|
||||
}
|
||||
except requests.RequestException as e:
|
||||
# only log error
|
||||
logger.error(f"Get usage info failed: {str(e)}")
|
||||
return None
|
||||
except Exception as e:
|
||||
# catch all other exceptions
|
||||
logger.error(f"Get usage info failed: {str(e)}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_stripe_profile(token: str) -> Optional[Dict]:
|
||||
"""get user subscription info"""
|
||||
url = f"https://api2.{Config.NAME_LOWER}.sh/auth/full_stripe_profile"
|
||||
headers = Config.BASE_HEADERS.copy()
|
||||
headers.update({"Authorization": f"Bearer {token}"})
|
||||
try:
|
||||
proxies = UsageManager.get_proxy()
|
||||
response = requests.get(url, headers=headers, timeout=10, proxies=proxies)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Get subscription info failed: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_token_from_config():
|
||||
"""get path info from config"""
|
||||
try:
|
||||
from config import get_config
|
||||
config = get_config()
|
||||
if not config:
|
||||
return None
|
||||
|
||||
system = platform.system()
|
||||
if system == "Windows" and config.has_section('WindowsPaths'):
|
||||
return {
|
||||
'storage_path': config.get('WindowsPaths', 'storage_path'),
|
||||
'sqlite_path': config.get('WindowsPaths', 'sqlite_path'),
|
||||
'session_path': os.path.join(os.getenv("APPDATA"), "Cursor", "Session Storage")
|
||||
}
|
||||
elif system == "Darwin" and config.has_section('MacPaths'): # macOS
|
||||
return {
|
||||
'storage_path': config.get('MacPaths', 'storage_path'),
|
||||
'sqlite_path': config.get('MacPaths', 'sqlite_path'),
|
||||
'session_path': os.path.expanduser("~/Library/Application Support/Cursor/Session Storage")
|
||||
}
|
||||
elif system == "Linux" and config.has_section('LinuxPaths'):
|
||||
return {
|
||||
'storage_path': config.get('LinuxPaths', 'storage_path'),
|
||||
'sqlite_path': config.get('LinuxPaths', 'sqlite_path'),
|
||||
'session_path': os.path.expanduser("~/.config/Cursor/Session Storage")
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Get config path failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def get_token_from_storage(storage_path):
|
||||
"""get token from storage.json"""
|
||||
if not os.path.exists(storage_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(storage_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
# try to get accessToken
|
||||
if 'cursorAuth/accessToken' in data:
|
||||
return data['cursorAuth/accessToken']
|
||||
|
||||
# try other possible keys
|
||||
for key in data:
|
||||
if 'token' in key.lower() and isinstance(data[key], str) and len(data[key]) > 20:
|
||||
return data[key]
|
||||
except Exception as e:
|
||||
logger.error(f"get token from storage.json failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def get_token_from_sqlite(sqlite_path):
|
||||
"""get token from sqlite"""
|
||||
if not os.path.exists(sqlite_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(sqlite_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%token%'")
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
for row in rows:
|
||||
try:
|
||||
value = row[0]
|
||||
if isinstance(value, str) and len(value) > 20:
|
||||
return value
|
||||
# try to parse JSON
|
||||
data = json.loads(value)
|
||||
if isinstance(data, dict) and 'token' in data:
|
||||
return data['token']
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"get token from sqlite failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def get_token_from_session(session_path):
|
||||
"""get token from session"""
|
||||
if not os.path.exists(session_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
# try to find all possible session files
|
||||
for file in os.listdir(session_path):
|
||||
if file.endswith('.log'):
|
||||
file_path = os.path.join(session_path, file)
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read().decode('utf-8', errors='ignore')
|
||||
# find token pattern
|
||||
token_match = re.search(r'"token":"([^"]+)"', content)
|
||||
if token_match:
|
||||
return token_match.group(1)
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"get token from session failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def get_token():
|
||||
"""get Cursor token"""
|
||||
# get path from config
|
||||
paths = get_token_from_config()
|
||||
if not paths:
|
||||
return None
|
||||
|
||||
# try to get token from different locations
|
||||
token = get_token_from_storage(paths['storage_path'])
|
||||
if token:
|
||||
return token
|
||||
|
||||
token = get_token_from_sqlite(paths['sqlite_path'])
|
||||
if token:
|
||||
return token
|
||||
|
||||
token = get_token_from_session(paths['session_path'])
|
||||
if token:
|
||||
return token
|
||||
|
||||
return None
|
||||
|
||||
def format_subscription_type(subscription_data: Dict) -> str:
|
||||
"""format subscription type"""
|
||||
if not subscription_data:
|
||||
return "Free"
|
||||
|
||||
# handle new API response format
|
||||
if "membershipType" in subscription_data:
|
||||
membership_type = subscription_data.get("membershipType", "").lower()
|
||||
subscription_status = subscription_data.get("subscriptionStatus", "").lower()
|
||||
|
||||
if subscription_status == "active":
|
||||
if membership_type == "pro":
|
||||
return "Pro"
|
||||
elif membership_type == "free_trial":
|
||||
return "Free Trial"
|
||||
elif membership_type == "pro_trial":
|
||||
return "Pro Trial"
|
||||
elif membership_type == "team":
|
||||
return "Team"
|
||||
elif membership_type == "enterprise":
|
||||
return "Enterprise"
|
||||
elif membership_type:
|
||||
return membership_type.capitalize()
|
||||
else:
|
||||
return "Active Subscription"
|
||||
elif subscription_status:
|
||||
return f"{membership_type.capitalize()} ({subscription_status})"
|
||||
|
||||
# compatible with old API response format
|
||||
subscription = subscription_data.get("subscription")
|
||||
if subscription:
|
||||
plan = subscription.get("plan", {}).get("nickname", "Unknown")
|
||||
status = subscription.get("status", "unknown")
|
||||
|
||||
if status == "active":
|
||||
if "pro" in plan.lower():
|
||||
return "Pro"
|
||||
elif "pro_trial" in plan.lower():
|
||||
return "Pro Trial"
|
||||
elif "free_trial" in plan.lower():
|
||||
return "Free Trial"
|
||||
elif "team" in plan.lower():
|
||||
return "Team"
|
||||
elif "enterprise" in plan.lower():
|
||||
return "Enterprise"
|
||||
else:
|
||||
return plan
|
||||
else:
|
||||
return f"{plan} ({status})"
|
||||
|
||||
return "Free"
|
||||
|
||||
def get_email_from_storage(storage_path):
|
||||
"""get email from storage.json"""
|
||||
if not os.path.exists(storage_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(storage_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
# try to get email
|
||||
if 'cursorAuth/cachedEmail' in data:
|
||||
return data['cursorAuth/cachedEmail']
|
||||
|
||||
# try other possible keys
|
||||
for key in data:
|
||||
if 'email' in key.lower() and isinstance(data[key], str) and '@' in data[key]:
|
||||
return data[key]
|
||||
except Exception as e:
|
||||
logger.error(f"get email from storage.json failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def get_email_from_sqlite(sqlite_path):
|
||||
"""get email from sqlite"""
|
||||
if not os.path.exists(sqlite_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(sqlite_path)
|
||||
cursor = conn.cursor()
|
||||
# try to query records containing email
|
||||
cursor.execute("SELECT value FROM ItemTable WHERE key LIKE '%email%' OR key LIKE '%cursorAuth%'")
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
for row in rows:
|
||||
try:
|
||||
value = row[0]
|
||||
# if it's a string and contains @, it might be an email
|
||||
if isinstance(value, str) and '@' in value:
|
||||
return value
|
||||
|
||||
# try to parse JSON
|
||||
try:
|
||||
data = json.loads(value)
|
||||
if isinstance(data, dict):
|
||||
# check if there's an email field
|
||||
if 'email' in data:
|
||||
return data['email']
|
||||
# check if there's a cachedEmail field
|
||||
if 'cachedEmail' in data:
|
||||
return data['cachedEmail']
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"get email from sqlite failed: {str(e)}")
|
||||
|
||||
return None
|
||||
|
||||
def display_account_info(translator=None):
|
||||
"""display account info"""
|
||||
print(f"\n{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['USER']} {translator.get('account_info.title') if translator else 'Cursor Account Information'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
|
||||
# get token
|
||||
token = get_token()
|
||||
if not token:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.token_not_found') if translator else 'Token not found. Please login to Cursor first.'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
# get path info
|
||||
paths = get_token_from_config()
|
||||
if not paths:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.config_not_found') if translator else 'Configuration not found.'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
# get email info - try multiple sources
|
||||
email = get_email_from_storage(paths['storage_path'])
|
||||
|
||||
# if not found in storage, try from sqlite
|
||||
if not email:
|
||||
email = get_email_from_sqlite(paths['sqlite_path'])
|
||||
|
||||
# get subscription info
|
||||
try:
|
||||
subscription_info = UsageManager.get_stripe_profile(token)
|
||||
except Exception as e:
|
||||
logger.error(f"Get subscription info failed: {str(e)}")
|
||||
subscription_info = None
|
||||
|
||||
# if not found in storage and sqlite, try from subscription info
|
||||
if not email and subscription_info:
|
||||
# try to get email from subscription info
|
||||
if 'customer' in subscription_info and 'email' in subscription_info['customer']:
|
||||
email = subscription_info['customer']['email']
|
||||
|
||||
# get usage info - silently handle errors
|
||||
try:
|
||||
usage_info = UsageManager.get_usage(token)
|
||||
except Exception as e:
|
||||
logger.error(f"Get usage info failed: {str(e)}")
|
||||
usage_info = None
|
||||
|
||||
# Prepare left and right info
|
||||
left_info = []
|
||||
right_info = []
|
||||
|
||||
# Left side shows account info
|
||||
if email:
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['USER']} {translator.get('account_info.email') if translator else 'Email'}: {Fore.WHITE}{email}{Style.RESET_ALL}")
|
||||
else:
|
||||
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.email_not_found') if translator else 'Email not found'}{Style.RESET_ALL}")
|
||||
|
||||
# Add an empty line
|
||||
# left_info.append("")
|
||||
|
||||
# Show subscription type
|
||||
if subscription_info:
|
||||
subscription_type = format_subscription_type(subscription_info)
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['SUBSCRIPTION']} {translator.get('account_info.subscription') if translator else 'Subscription'}: {Fore.WHITE}{subscription_type}{Style.RESET_ALL}")
|
||||
|
||||
# Show remaining trial days
|
||||
days_remaining = subscription_info.get("daysRemainingOnTrial")
|
||||
if days_remaining is not None and days_remaining > 0:
|
||||
left_info.append(f"{Fore.GREEN}{EMOJI['TIME']} {translator.get('account_info.trial_remaining') if translator else 'Remaining Pro Trial'}: {Fore.WHITE}{days_remaining} {translator.get('account_info.days') if translator else 'days'}{Style.RESET_ALL}")
|
||||
else:
|
||||
left_info.append(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('account_info.subscription_not_found') if translator else 'Subscription information not found'}{Style.RESET_ALL}")
|
||||
|
||||
# Right side shows usage info - only if available
|
||||
if usage_info:
|
||||
right_info.append(f"{Fore.GREEN}{EMOJI['USAGE']} {translator.get('account_info.usage') if translator else 'Usage Statistics'}:{Style.RESET_ALL}")
|
||||
|
||||
# Premium usage
|
||||
premium_usage = usage_info.get('premium_usage', 0)
|
||||
max_premium_usage = usage_info.get('max_premium_usage', "No Limit")
|
||||
|
||||
# make sure the value is not None
|
||||
if premium_usage is None:
|
||||
premium_usage = 0
|
||||
|
||||
# handle "No Limit" case
|
||||
if isinstance(max_premium_usage, str) and max_premium_usage == "No Limit":
|
||||
premium_color = Fore.GREEN # when there is no limit, use green
|
||||
premium_display = f"{premium_usage}/{max_premium_usage}"
|
||||
else:
|
||||
# calculate percentage when the value is a number
|
||||
if max_premium_usage is None or max_premium_usage == 0:
|
||||
max_premium_usage = 999
|
||||
premium_percentage = 0
|
||||
else:
|
||||
premium_percentage = (premium_usage / max_premium_usage) * 100
|
||||
|
||||
# select color based on usage percentage
|
||||
premium_color = Fore.GREEN
|
||||
if premium_percentage > 70:
|
||||
premium_color = Fore.YELLOW
|
||||
if premium_percentage > 90:
|
||||
premium_color = Fore.RED
|
||||
|
||||
premium_display = f"{premium_usage}/{max_premium_usage} ({premium_percentage:.1f}%)"
|
||||
|
||||
right_info.append(f"{Fore.YELLOW}{EMOJI['PREMIUM']} {translator.get('account_info.premium_usage') if translator else 'Fast Response'}: {premium_color}{premium_display}{Style.RESET_ALL}")
|
||||
|
||||
# Slow Response
|
||||
basic_usage = usage_info.get('basic_usage', 0)
|
||||
max_basic_usage = usage_info.get('max_basic_usage', "No Limit")
|
||||
|
||||
# make sure the value is not None
|
||||
if basic_usage is None:
|
||||
basic_usage = 0
|
||||
|
||||
# handle "No Limit" case
|
||||
if isinstance(max_basic_usage, str) and max_basic_usage == "No Limit":
|
||||
basic_color = Fore.GREEN # when there is no limit, use green
|
||||
basic_display = f"{basic_usage}/{max_basic_usage}"
|
||||
else:
|
||||
# calculate percentage when the value is a number
|
||||
if max_basic_usage is None or max_basic_usage == 0:
|
||||
max_basic_usage = 999
|
||||
basic_percentage = 0
|
||||
else:
|
||||
basic_percentage = (basic_usage / max_basic_usage) * 100
|
||||
|
||||
# select color based on usage percentage
|
||||
basic_color = Fore.GREEN
|
||||
if basic_percentage > 70:
|
||||
basic_color = Fore.YELLOW
|
||||
if basic_percentage > 90:
|
||||
basic_color = Fore.RED
|
||||
|
||||
basic_display = f"{basic_usage}/{max_basic_usage} ({basic_percentage:.1f}%)"
|
||||
|
||||
right_info.append(f"{Fore.BLUE}{EMOJI['BASIC']} {translator.get('account_info.basic_usage') if translator else 'Slow Response'}: {basic_color}{basic_display}{Style.RESET_ALL}")
|
||||
else:
|
||||
# if get usage info failed, only log in log, not show in interface
|
||||
# you can choose to not show any usage info, or show a simple prompt
|
||||
# right_info.append(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_info.usage_unavailable') if translator else 'Usage information unavailable'}{Style.RESET_ALL}")
|
||||
pass # not show any usage info
|
||||
|
||||
# Calculate the maximum display width of left info
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
|
||||
def get_display_width(s):
|
||||
"""Calculate the display width of a string, considering Chinese characters and emojis"""
|
||||
# Remove ANSI color codes
|
||||
clean_s = ansi_escape.sub('', s)
|
||||
width = 0
|
||||
for c in clean_s:
|
||||
# Chinese characters and some emojis occupy two character widths
|
||||
if ord(c) > 127:
|
||||
width += 2
|
||||
else:
|
||||
width += 1
|
||||
return width
|
||||
|
||||
max_left_width = 0
|
||||
for item in left_info:
|
||||
width = get_display_width(item)
|
||||
max_left_width = max(max_left_width, width)
|
||||
|
||||
# Set the starting position of right info
|
||||
fixed_spacing = 4 # Fixed spacing
|
||||
right_start = max_left_width + fixed_spacing
|
||||
|
||||
# Calculate the number of spaces needed for right info
|
||||
spaces_list = []
|
||||
for i in range(len(left_info)):
|
||||
if i < len(left_info):
|
||||
left_item = left_info[i]
|
||||
left_width = get_display_width(left_item)
|
||||
spaces = right_start - left_width
|
||||
spaces_list.append(spaces)
|
||||
|
||||
# Print info
|
||||
max_rows = max(len(left_info), len(right_info))
|
||||
|
||||
for i in range(max_rows):
|
||||
# Print left info
|
||||
if i < len(left_info):
|
||||
left_item = left_info[i]
|
||||
print(left_item, end='')
|
||||
|
||||
# Use pre-calculated spaces
|
||||
spaces = spaces_list[i]
|
||||
else:
|
||||
# If left side has no items, print only spaces
|
||||
spaces = right_start
|
||||
print('', end='')
|
||||
|
||||
# Print right info
|
||||
if i < len(right_info):
|
||||
print(' ' * spaces + right_info[i])
|
||||
else:
|
||||
print() # Change line
|
||||
|
||||
print(f"{Fore.CYAN}{'─' * 70}{Style.RESET_ALL}")
|
||||
|
||||
def main(translator=None):
|
||||
"""main function"""
|
||||
try:
|
||||
display_account_info(translator)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_info.error') if translator else 'Error'}: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
105
cursor_auth.py
@@ -2,11 +2,12 @@ import sqlite3
|
||||
import os
|
||||
import sys
|
||||
from colorama import Fore, Style, init
|
||||
from config import get_config
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
# Define emoji and color constants
|
||||
EMOJI = {
|
||||
'DB': '🗄️',
|
||||
'UPDATE': '🔄',
|
||||
@@ -14,30 +15,75 @@ EMOJI = {
|
||||
'ERROR': '❌',
|
||||
'WARN': '⚠️',
|
||||
'INFO': 'ℹ️',
|
||||
'KEY': '🔑'
|
||||
'FILE': '📄',
|
||||
'KEY': '🔐'
|
||||
}
|
||||
|
||||
class CursorAuth:
|
||||
def __init__(self):
|
||||
# 判断操作系统
|
||||
if os.name == "nt": # Windows
|
||||
self.db_path = os.path.join(
|
||||
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
)
|
||||
else: # macOS
|
||||
self.db_path = os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||
)
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
|
||||
# Get configuration
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
|
||||
sys.exit(1)
|
||||
|
||||
# Get path based on operating system
|
||||
try:
|
||||
if sys.platform == "win32": # Windows
|
||||
if not config.has_section('WindowsPaths'):
|
||||
raise ValueError("Windows paths not configured")
|
||||
self.db_path = config.get('WindowsPaths', 'sqlite_path')
|
||||
|
||||
elif sys.platform == 'linux': # Linux
|
||||
if not config.has_section('LinuxPaths'):
|
||||
raise ValueError("Linux paths not configured")
|
||||
self.db_path = config.get('LinuxPaths', 'sqlite_path')
|
||||
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
if not config.has_section('MacPaths'):
|
||||
raise ValueError("macOS paths not configured")
|
||||
self.db_path = config.get('MacPaths', 'sqlite_path')
|
||||
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
|
||||
sys.exit(1)
|
||||
|
||||
# Verify if the path exists
|
||||
if not os.path.exists(os.path.dirname(self.db_path)):
|
||||
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
|
||||
sys.exit(1)
|
||||
|
||||
# Check if the database file exists
|
||||
if not os.path.exists(self.db_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path)}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
# Check file permissions
|
||||
if not os.access(self.db_path, os.R_OK | os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_permission_error')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
try:
|
||||
self.conn = sqlite3.connect(self.db_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
|
||||
except sqlite3.Error as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_connection_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
def update_auth(self, email=None, access_token=None, refresh_token=None):
|
||||
conn = None
|
||||
try:
|
||||
# 确保目录存在并设置正确权限
|
||||
# Ensure the directory exists and set the correct permissions
|
||||
db_dir = os.path.dirname(self.db_path)
|
||||
if not os.path.exists(db_dir):
|
||||
os.makedirs(db_dir, mode=0o755, exist_ok=True)
|
||||
|
||||
# 如果数据库文件不存在,创建一个新的
|
||||
# If the database file does not exist, create a new one
|
||||
if not os.path.exists(self.db_path):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
@@ -52,31 +98,34 @@ class CursorAuth:
|
||||
os.chmod(self.db_path, 0o644)
|
||||
conn.close()
|
||||
|
||||
# 重新连接数据库
|
||||
# Reconnect to the database
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
print(f"{EMOJI['INFO']} {Fore.GREEN}Successfully connected to database | 成功连接到数据库{Style.RESET_ALL}")
|
||||
print(f"{EMOJI['INFO']} {Fore.GREEN} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 增加超时和其他优化设置
|
||||
# Add timeout and other optimization settings
|
||||
conn.execute("PRAGMA busy_timeout = 5000")
|
||||
conn.execute("PRAGMA journal_mode = WAL")
|
||||
conn.execute("PRAGMA synchronous = NORMAL")
|
||||
|
||||
# 设置要更新的键值对
|
||||
# Set the key-value pairs to update
|
||||
updates = []
|
||||
|
||||
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
|
||||
|
||||
if email is not None:
|
||||
updates.append(("cursorAuth/cachedEmail", email))
|
||||
if access_token is not None:
|
||||
updates.append(("cursorAuth/accessToken", access_token))
|
||||
if refresh_token is not None:
|
||||
updates.append(("cursorAuth/refreshToken", refresh_token))
|
||||
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
|
||||
|
||||
|
||||
# 使用事务来确保数据完整性
|
||||
# Use transactions to ensure data integrity
|
||||
cursor.execute("BEGIN TRANSACTION")
|
||||
try:
|
||||
for key, value in updates:
|
||||
# 检查键是否存在
|
||||
# Check if the key exists
|
||||
cursor.execute("SELECT COUNT(*) FROM ItemTable WHERE key = ?", (key,))
|
||||
if cursor.fetchone()[0] == 0:
|
||||
cursor.execute("""
|
||||
@@ -88,10 +137,10 @@ class CursorAuth:
|
||||
UPDATE ItemTable SET value = ?
|
||||
WHERE key = ?
|
||||
""", (value, key))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN}Updating {key.split('/')[-1]}...{Style.RESET_ALL}")
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('auth.updating_pair')} {key.split('/')[-1]}...{Style.RESET_ALL}")
|
||||
|
||||
cursor.execute("COMMIT")
|
||||
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}Database updated successfully | 数据库更新成功{Style.RESET_ALL}")
|
||||
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}{self.translator.get('auth.database_updated_successfully')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@@ -99,14 +148,12 @@ class CursorAuth:
|
||||
raise e
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED}Database error | 数据库错误: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.database_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED}An error occurred | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.an_error_occurred', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN}Database connection closed | 数据库连接已关闭{Style.RESET_ALL}")
|
||||
|
||||
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
|
||||
|
||||
@@ -2,18 +2,16 @@ import os
|
||||
from colorama import Fore, Style, init
|
||||
import time
|
||||
import random
|
||||
from browser import BrowserManager
|
||||
from control import BrowserControl
|
||||
from cursor_auth import CursorAuth
|
||||
from reset_machine_manual import MachineIDResetter
|
||||
|
||||
os.environ["PYTHONVERBOSE"] = "0"
|
||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
@@ -24,16 +22,17 @@ EMOJI = {
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'SUCCESS': '✅',
|
||||
'MAIL': '<EFBFBD><EFBFBD>',
|
||||
'MAIL': '📧',
|
||||
'KEY': '🔐',
|
||||
'UPDATE': '🔄'
|
||||
'UPDATE': '🔄',
|
||||
'INFO': 'ℹ️'
|
||||
}
|
||||
|
||||
class CursorRegistration:
|
||||
def __init__(self):
|
||||
# 设置为显示模式
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
# Set to display mode
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser_manager = BrowserManager()
|
||||
self.browser = None
|
||||
self.controller = None
|
||||
self.mail_url = "https://yopmail.com/zh/email-generator"
|
||||
@@ -43,270 +42,111 @@ class CursorRegistration:
|
||||
self.signup_tab = None
|
||||
self.email_tab = None
|
||||
|
||||
# 账号信息
|
||||
# Account information
|
||||
self.password = self._generate_password()
|
||||
self.first_name = self._generate_name()
|
||||
self.last_name = self._generate_name()
|
||||
# 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 _generate_name(self, length=6):
|
||||
"""Generate Random Name"""
|
||||
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
|
||||
return first_letter + rest_letters
|
||||
|
||||
def setup_email(self):
|
||||
"""Setup Temporary Email"""
|
||||
"""Setup Email"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Staring Browser | 正在启动浏览器...{Style.RESET_ALL}")
|
||||
self.browser = self.browser_manager.init_browser()
|
||||
self.controller = BrowserControl(self.browser)
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# 打开邮箱生成器页面(第一个标签页)
|
||||
self.controller.navigate_to(self.mail_url)
|
||||
self.email_tab = self.browser # 保存邮箱标签页
|
||||
self.controller.email_tab = self.email_tab # 同时保存到controller
|
||||
# Create a temporary email using new_tempemail, passing translator
|
||||
from new_tempemail import NewTempEmail
|
||||
self.temp_email = NewTempEmail(self.translator) # Pass translator
|
||||
|
||||
# 生成新邮箱
|
||||
self.controller.generate_new_email()
|
||||
# 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
|
||||
|
||||
# 选择随机域名
|
||||
self.controller.select_email_domain()
|
||||
# Save email address
|
||||
self.email_address = email_address
|
||||
self.email_tab = self.temp_email # Pass NewTempEmail instance
|
||||
|
||||
# 获取邮箱地址
|
||||
self.email_address = self.controller.copy_and_get_email()
|
||||
if self.email_address:
|
||||
print(f"{Fore.CYAN}Get Email Address | 获取到的邮箱地址: {self.email_address}{Style.RESET_ALL}")
|
||||
|
||||
# 进入邮箱
|
||||
if self.controller.view_mailbox():
|
||||
return True
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Error Occured | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
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 账号"""
|
||||
signup_browser_manager = None
|
||||
"""注册 Cursor"""
|
||||
browser_tab = None
|
||||
try:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['START']}Start Register | 开始 Cursor 注册流程{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# 创建新的浏览器实例用于注册
|
||||
from browser import BrowserManager
|
||||
signup_browser_manager = BrowserManager(noheader=True)
|
||||
self.signup_tab = signup_browser_manager.init_browser()
|
||||
# Directly use new_signup.py to sign up
|
||||
from new_signup import main as new_signup_main
|
||||
|
||||
# 访问注册页面
|
||||
self.signup_tab.get(self.sign_up_url)
|
||||
time.sleep(2)
|
||||
|
||||
# 填写注册表单
|
||||
if self.signup_tab.ele("@name=first_name"):
|
||||
print(f"{Fore.YELLOW}{EMOJI['FORM']}Fill Form | 填写注册信息...{Style.RESET_ALL}")
|
||||
|
||||
self.signup_tab.ele("@name=first_name").input(self.first_name)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@name=last_name").input(self.last_name)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@name=email").input(self.email_address)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@type=submit").click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Basic Info Submitted | 基本信息提交完成{Style.RESET_ALL}")
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 设置密码
|
||||
if self.signup_tab.ele("@name=password"):
|
||||
print(f"{Fore.YELLOW}{EMOJI['PASSWORD']} Set Password | 设置密码...{Style.RESET_ALL}")
|
||||
self.signup_tab.ele("@name=password").input(self.password)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
self.signup_tab.ele("@type=submit").click()
|
||||
# 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
|
||||
)
|
||||
|
||||
self._handle_turnstile()
|
||||
|
||||
# 等待并获取验证码
|
||||
time.sleep(5) # 等待验证码邮件
|
||||
|
||||
self.browser.refresh()
|
||||
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
|
||||
|
||||
# 获取验证码,设置60秒超时
|
||||
verification_code = None
|
||||
max_attempts = 10 # 增加到10次尝试
|
||||
retry_interval = 5 # 每5秒重试一次
|
||||
start_time = time.time()
|
||||
timeout = 60 # 60秒超时
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Start Getting Verification Code | 开始获取验证码,将在60秒内尝试...{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
# 检查是否超时
|
||||
if time.time() - start_time > timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Verification Code Timeout | 获取验证码超时{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
verification_code = self.controller.get_verification_code()
|
||||
if verification_code:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Get Verification Code Success | 成功获取验证码: {verification_code}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
remaining_time = int(timeout - (time.time() - start_time))
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Try | 第 {attempt + 1} Get Verification Code | 次尝试未获取到验证码,Time Remaining | 剩余时间: {remaining_time}秒{Style.RESET_ALL}")
|
||||
|
||||
# 刷新邮箱
|
||||
self.browser.refresh()
|
||||
time.sleep(retry_interval)
|
||||
|
||||
if verification_code:
|
||||
# 在注册页面填写验证码
|
||||
for i, digit in enumerate(verification_code):
|
||||
self.signup_tab.ele(f"@data-index={i}").input(digit)
|
||||
time.sleep(random.uniform(0.1, 0.3))
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Verification Code Filled | 验证码填写完成{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
|
||||
self._handle_turnstile()
|
||||
|
||||
# 检查当前URL
|
||||
current_url = self.signup_tab.url
|
||||
if "authenticator.cursor.sh" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['VERIFY']} Detect Login Page | 检测到登录页面,开始登录...{Style.RESET_ALL}")
|
||||
|
||||
# 填写邮箱
|
||||
email_input = self.signup_tab.ele('@name=email')
|
||||
if email_input:
|
||||
email_input.input(self.email_address)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
# 点击提交
|
||||
submit_button = self.signup_tab.ele('@type=submit')
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 填写密码
|
||||
password_input = self.signup_tab.ele('@name=password')
|
||||
if password_input:
|
||||
password_input.input(self.password)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
# 点击提交
|
||||
submit_button = self.signup_tab.ele('@type=submit')
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 等待跳转到设置页面
|
||||
max_wait = 30
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < max_wait:
|
||||
if "cursor.com/settings" in self.signup_tab.url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Login Success and Jump to Settings Page | 成功登录并跳转到设置页面{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# 获取账户信息
|
||||
result = self._get_account_info()
|
||||
|
||||
# 关闭注册窗口
|
||||
if signup_browser_manager:
|
||||
signup_browser_manager.quit()
|
||||
|
||||
return result
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Verification Code Timeout | 未能在60秒内获取到验证码{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Register Process Error | 注册过程出错: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
# 确保在任何情况下都关闭注册窗口
|
||||
if signup_browser_manager:
|
||||
signup_browser_manager.quit()
|
||||
|
||||
def _handle_turnstile(self):
|
||||
"""处理 Turnstile 验证"""
|
||||
print(f"{Fore.YELLOW}{EMOJI['VERIFY']} Handle Turnstile | 处理 Turnstile 验证...{Style.RESET_ALL}")
|
||||
|
||||
# 设置最大等待时间(秒)
|
||||
max_wait_time = 10 # 增加等待时间
|
||||
start_time = time.time()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 检查是否超时
|
||||
if time.time() - start_time > max_wait_time:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Not Detect Turnstile | 未检测到 Turnstile 验证,继续下一步...{Style.RESET_ALL}")
|
||||
time.sleep(2) # 添加短暂延迟
|
||||
break
|
||||
|
||||
# 检查是否存在验证框
|
||||
# Ensure browser is closed in any case
|
||||
if browser_tab:
|
||||
try:
|
||||
challengeCheck = (
|
||||
self.signup_tab.ele("@id=cf-turnstile", timeout=1)
|
||||
.child()
|
||||
.shadow_root.ele("tag:iframe")
|
||||
.ele("tag:body")
|
||||
.sr("tag:input")
|
||||
)
|
||||
|
||||
if challengeCheck:
|
||||
challengeCheck.click()
|
||||
time.sleep(3) # 增加点击后的等待时间
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Turnstile Passed | 验证通过{Style.RESET_ALL}")
|
||||
time.sleep(2) # 确保验证完全完成
|
||||
break
|
||||
browser_tab.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查是否已经通过验证(检查下一步的元素是否存在)
|
||||
try:
|
||||
if (self.signup_tab.ele("@name=password", timeout=0.5) or
|
||||
self.signup_tab.ele("@name=email", timeout=0.5) or
|
||||
self.signup_tab.ele("@data-index=0", timeout=0.5)):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Verification Passed | 验证已通过{Style.RESET_ALL}")
|
||||
time.sleep(2) # 添加短暂延迟
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# 等待短暂时间后继续检查
|
||||
time.sleep(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Turnstile Error | Turnstile 处理出错: {str(e)}{Style.RESET_ALL}")
|
||||
time.sleep(2) # 出错时等待更长时间
|
||||
break
|
||||
|
||||
# 最后再等待一下,确保页面加载完成
|
||||
time.sleep(2)
|
||||
|
||||
def _get_account_info(self):
|
||||
"""获取账户信息和 Token"""
|
||||
"""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 > "
|
||||
@@ -317,8 +157,8 @@ class CursorRegistration:
|
||||
if usage_ele:
|
||||
total_usage = usage_ele.text.split("/")[-1].strip()
|
||||
|
||||
# 获取 Token
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Get Cursor Session Token | 开始获取 Cursor Session Token...{Style.RESET_ALL}")
|
||||
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
|
||||
@@ -329,48 +169,47 @@ class CursorRegistration:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Get Token Success | Token 获取成功{Style.RESET_ALL}")
|
||||
# 保存账户信息
|
||||
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']} Try | 第 {attempts} times to get Token | 次尝试未获取到 Token,{retry_interval}秒后重试...{Style.RESET_ALL}"
|
||||
)
|
||||
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']} Reach Max Attempts ({max_attempts}) | 已达到最大尝试次数({max_attempts}),获取 Token 失败{Style.RESET_ALL}")
|
||||
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']} Get Token Failed | 获取 Token 失败: {str(e)}{Style.RESET_ALL}")
|
||||
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']} Will Retry in {retry_interval} seconds | 将在 {retry_interval} 秒后重试...{Style.RESET_ALL}")
|
||||
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']} Get Account Info Failed | 获取账户信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
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:
|
||||
# 先更新认证信息
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} 正在更新 Cursor 认证信息...{Style.RESET_ALL}")
|
||||
if update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor Auth Info Updated | 认证信息更新成功{Style.RESET_ALL}")
|
||||
# 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']} Cursor Auth Info Update Failed | 认证信息更新失败{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
|
||||
|
||||
# 重置机器ID
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Reset Machine ID | 正在重置机器ID...{Style.RESET_ALL}")
|
||||
MachineIDResetter().reset_machine_ids()
|
||||
# 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")
|
||||
@@ -379,37 +218,46 @@ class CursorRegistration:
|
||||
f.write(f"Usage Limit: {total_usage}\n")
|
||||
f.write(f"{'='*50}\n")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account Info Saved to cursor_accounts.txt | 账户信息已保存到 cursor_accounts.txt{Style.RESET_ALL}")
|
||||
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']} Save Account Info Failed | 保存账户信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
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']} Cursor Registration Completed | 注册完成!{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
if self.browser_manager:
|
||||
self.browser_manager.quit()
|
||||
# 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}")
|
||||
|
||||
def update_cursor_auth(email=None, access_token=None, refresh_token=None):
|
||||
"""
|
||||
更新Cursor的认证信息的便捷函数
|
||||
"""
|
||||
auth_manager = CursorAuth()
|
||||
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||
|
||||
def main():
|
||||
registration = CursorRegistration()
|
||||
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__":
|
||||
main()
|
||||
from main import translator as main_translator
|
||||
main(main_translator)
|
||||
5
cursor_register_github.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from oauth_auth import main as oauth_main
|
||||
|
||||
def main(translator=None):
|
||||
"""Handle GitHub OAuth registration"""
|
||||
oauth_main('github', translator)
|
||||
5
cursor_register_google.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from oauth_auth import main as oauth_main
|
||||
|
||||
def main(translator=None):
|
||||
"""Handle Google OAuth registration"""
|
||||
oauth_main('google', translator)
|
||||
270
cursor_register_manual.py
Normal file
@@ -0,0 +1,270 @@
|
||||
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.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
|
||||
|
||||
# Generate 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.manual_email_input') if self.translator else 'Please enter your email address:'}")
|
||||
self.email_address = input().strip()
|
||||
|
||||
if '@' not in self.email_address:
|
||||
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}")
|
||||
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 get_verification_code(self):
|
||||
"""Manually Get Verification Code"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}")
|
||||
code = input().strip()
|
||||
|
||||
if not code.isdigit() or len(code) != 6:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
return code
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def register_cursor(self):
|
||||
"""Register Cursor"""
|
||||
browser_tab = None
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# Use new_signup.py directly for registration
|
||||
from new_signup import main as new_signup_main
|
||||
|
||||
# Execute 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=None, # No email tab needed
|
||||
controller=self, # Pass self instead of 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"Total Usage: {total_usage}\n")
|
||||
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) # Create instance with translator
|
||||
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):
|
||||
"""Convenient function to update Cursor authentication information"""
|
||||
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)
|
||||
250
disable_auto_update.py
Normal file
@@ -0,0 +1,250 @@
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import shutil
|
||||
from colorama import Fore, Style, init
|
||||
import subprocess
|
||||
from config import get_config
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"PROCESS": "🔄",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"FOLDER": "📁",
|
||||
"FILE": "📄",
|
||||
"STOP": "🛑",
|
||||
"CHECK": "✔️"
|
||||
}
|
||||
|
||||
class AutoUpdateDisabler:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
self.system = platform.system()
|
||||
|
||||
# Get path from configuration file
|
||||
config = get_config(translator)
|
||||
if config:
|
||||
if self.system == "Windows":
|
||||
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
|
||||
self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"))
|
||||
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")
|
||||
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"))
|
||||
else:
|
||||
# If configuration loading fails, use default paths
|
||||
self.updater_paths = {
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
|
||||
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||
"Linux": os.path.expanduser("~/.config/cursor-updater")
|
||||
}
|
||||
self.updater_path = self.updater_paths.get(self.system)
|
||||
|
||||
self.update_yml_paths = {
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"),
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app-update.yml",
|
||||
"Linux": os.path.expanduser("~/.config/cursor/resources/app-update.yml")
|
||||
}
|
||||
self.update_yml_path = self.update_yml_paths.get(self.system)
|
||||
|
||||
def _change_main_js(self):
|
||||
"""Change main.js"""
|
||||
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_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()
|
||||
|
||||
patterns = {
|
||||
r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(main_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
os.unlink(tmp_path)
|
||||
return False
|
||||
|
||||
def _kill_cursor_processes(self):
|
||||
"""End all Cursor processes"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('update.killing_processes') if self.translator else '正在结束 Cursor 进程...'}{Style.RESET_ALL}")
|
||||
|
||||
if self.system == "Windows":
|
||||
subprocess.run(['taskkill', '/F', '/IM', 'Cursor.exe', '/T'], capture_output=True)
|
||||
else:
|
||||
subprocess.run(['pkill', '-f', 'Cursor'], capture_output=True)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.processes_killed') if self.translator else 'Cursor 进程已结束'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.kill_process_failed', error=str(e)) if self.translator else f'结束进程失败: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _remove_updater_directory(self):
|
||||
"""Delete updater directory"""
|
||||
try:
|
||||
updater_path = self.updater_path
|
||||
if not updater_path:
|
||||
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['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}")
|
||||
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):
|
||||
"""Clear update.yml file"""
|
||||
try:
|
||||
update_yml_path = self.update_yml_path
|
||||
if not update_yml_path:
|
||||
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}")
|
||||
|
||||
if os.path.exists(update_yml_path):
|
||||
# 清空文件内容
|
||||
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
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _create_blocking_file(self):
|
||||
"""Create blocking files"""
|
||||
try:
|
||||
# 检查 updater_path
|
||||
updater_path = self.updater_path
|
||||
if not updater_path:
|
||||
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
|
||||
|
||||
# 创建 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}")
|
||||
|
||||
# 检查 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}")
|
||||
|
||||
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
|
||||
|
||||
def disable_auto_update(self):
|
||||
"""Disable auto update"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('update.start_disable') if self.translator else '开始禁用自动更新...'}{Style.RESET_ALL}")
|
||||
|
||||
# 1. End processes
|
||||
if not self._kill_cursor_processes():
|
||||
return False
|
||||
|
||||
# 2. Delete directory - 即使失败也继续执行
|
||||
self._remove_updater_directory()
|
||||
|
||||
# 3. Clear update.yml file
|
||||
if not self._clear_update_yml_file():
|
||||
return False
|
||||
|
||||
# 4. Create blocking file
|
||||
if not self._create_blocking_file():
|
||||
return False
|
||||
|
||||
# 5. Change main.js
|
||||
if not self._change_main_js():
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.disable_failed', error=str(e)) if self.translator else f'禁用自动更新失败: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
"""Convenient function for directly calling the disable function"""
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('update.title') if translator else 'Disable Cursor Auto Update'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
disabler = AutoUpdateDisabler(translator)
|
||||
disabler.disable_auto_update()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('update.press_enter') if translator else 'Press Enter to Continue...'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
701
github_cursor_register.py
Normal file
@@ -0,0 +1,701 @@
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import json
|
||||
import random
|
||||
import string
|
||||
import requests
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from webdriver_manager.chrome import ChromeDriverManager
|
||||
import logging
|
||||
import platform
|
||||
from colorama import Fore, Style, init
|
||||
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
|
||||
import shutil
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
'VERIFY': '🔄',
|
||||
'PASSWORD': '🔑',
|
||||
'CODE': '📱',
|
||||
'DONE': '✨',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'SUCCESS': '✅',
|
||||
'MAIL': '📧',
|
||||
'KEY': '🔐',
|
||||
'UPDATE': '🔄',
|
||||
'INFO': 'ℹ️',
|
||||
'EMAIL': '📧',
|
||||
'REFRESH': '🔄',
|
||||
'LINK': '🔗',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
class GitHubCursorRegistration:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
# Set browser to visible mode
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
self.email_address = None
|
||||
|
||||
# Generate random credentials
|
||||
self.github_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
||||
self.github_password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=16))
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup and configure the web browser"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} Setting up browser...{Style.RESET_ALL}")
|
||||
|
||||
options = Options()
|
||||
options.add_argument('--incognito')
|
||||
options.add_argument('--no-sandbox')
|
||||
options.add_argument('--disable-dev-shm-usage')
|
||||
options.add_argument('--window-size=1920,1080')
|
||||
options.add_argument('--disable-notifications')
|
||||
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36')
|
||||
|
||||
self.browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
|
||||
self.browser.set_page_load_timeout(30)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to setup browser: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_temp_email(self):
|
||||
"""Get a temporary email address using YOPmail"""
|
||||
try:
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['MAIL']} Generating temporary email address...{Style.RESET_ALL}")
|
||||
self.browser.get("https://yopmail.com/")
|
||||
time.sleep(2)
|
||||
|
||||
# Generate a realistic username
|
||||
first_names = ["john", "sara", "michael", "emma", "david", "jennifer", "robert", "lisa"]
|
||||
last_names = ["smith", "johnson", "williams", "brown", "jones", "miller", "davis", "garcia"]
|
||||
|
||||
random_first = random.choice(first_names)
|
||||
random_last = random.choice(last_names)
|
||||
random_num = random.randint(100, 999)
|
||||
|
||||
username = f"{random_first}.{random_last}{random_num}"
|
||||
|
||||
# Enter the username and check inbox
|
||||
email_field = self.browser.find_element(By.XPATH, "//input[@id='login']")
|
||||
if email_field:
|
||||
email_field.clear()
|
||||
email_field.send_keys(username)
|
||||
time.sleep(1)
|
||||
|
||||
# Click the check button
|
||||
check_button = self.browser.find_element(By.XPATH, "//button[@title='Check Inbox' or @class='sbut' or contains(@onclick, 'ver')]")
|
||||
if check_button:
|
||||
check_button.click()
|
||||
time.sleep(2)
|
||||
self.email_address = f"{username}@yopmail.com"
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Temp email created: {self.email_address}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to create YOPmail address{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting temporary email: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_github(self):
|
||||
"""Register a new GitHub account"""
|
||||
if not self.email_address:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No email address available{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['FORM']} Registering GitHub account...{Style.RESET_ALL}")
|
||||
self.browser.get("https://github.com/join")
|
||||
time.sleep(3)
|
||||
|
||||
# Fill in the registration form
|
||||
WebDriverWait(self.browser, 15).until(EC.visibility_of_element_located((By.ID, "user_login")))
|
||||
self.browser.find_element(By.ID, "user_login").send_keys(self.github_username)
|
||||
self.browser.find_element(By.ID, "user_email").send_keys(self.email_address)
|
||||
self.browser.find_element(By.ID, "user_password").send_keys(self.github_password)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub password: {self.github_password}{Style.RESET_ALL}")
|
||||
|
||||
# Check for any notice or popup and handle it
|
||||
try:
|
||||
signup_button = self.browser.find_element(By.ID, "signup_button")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking sign up button...{Style.RESET_ALL}")
|
||||
signup_button.click()
|
||||
except NoSuchElementException:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Signup button not found, trying alternative selector{Style.RESET_ALL}")
|
||||
buttons = self.browser.find_elements(By.TAG_NAME, "button")
|
||||
for button in buttons:
|
||||
if "Sign up" in button.text:
|
||||
button.click()
|
||||
break
|
||||
|
||||
# Wait for page transition and check for CAPTCHA
|
||||
time.sleep(5)
|
||||
|
||||
# Check if registration was successful or if CAPTCHA appeared
|
||||
current_url = self.browser.current_url
|
||||
|
||||
# Look for CAPTCHA in URL or on page
|
||||
if "captcha" in current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} CAPTCHA detected, please complete it manually{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You have 60 seconds to solve the CAPTCHA...{Style.RESET_ALL}")
|
||||
|
||||
# Wait for user to solve CAPTCHA (60 seconds max)
|
||||
for i in range(60):
|
||||
current_url = self.browser.current_url
|
||||
if "captcha" not in current_url.lower() and "are you a robot" not in self.browser.page_source.lower():
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} CAPTCHA completed successfully{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
if i % 10 == 0 and i > 0:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Still waiting for CAPTCHA completion... {60-i} seconds remaining{Style.RESET_ALL}")
|
||||
|
||||
# Check if CAPTCHA was solved after waiting
|
||||
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA not solved within time limit{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want more time to solve the CAPTCHA? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've completed the CAPTCHA...{Style.RESET_ALL}")
|
||||
input()
|
||||
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA still not solved{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
# Wait for registration to complete
|
||||
time.sleep(5)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub account registered{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register GitHub account: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_email_verification(self):
|
||||
"""Check for GitHub verification email and click the verification link"""
|
||||
if not self.email_address or not self.browser:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Email or browser not available{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['EMAIL']} Checking for verification email...{Style.RESET_ALL}")
|
||||
|
||||
# Extract username from email for YOPmail
|
||||
username = self.email_address.split('@')[0]
|
||||
|
||||
max_attempts = 10
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
print(f"{Fore.CYAN}{EMOJI['REFRESH']} Checking YOPmail inbox (attempt {attempt}/{max_attempts})...{Style.RESET_ALL}")
|
||||
|
||||
# Go to YOPmail inbox
|
||||
self.browser.get(f"https://yopmail.com/en/wm")
|
||||
time.sleep(2)
|
||||
|
||||
# Enter email address
|
||||
try:
|
||||
email_input = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "login"))
|
||||
)
|
||||
email_input.clear()
|
||||
email_input.send_keys(username)
|
||||
|
||||
# Click the check inbox button
|
||||
check_button = self.browser.find_element(By.CSS_SELECTOR, "button[onclick='verif()']")
|
||||
check_button.click()
|
||||
time.sleep(3)
|
||||
|
||||
# Switch to inbox frame
|
||||
iframe = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "ifinbox"))
|
||||
)
|
||||
self.browser.switch_to.frame(iframe)
|
||||
|
||||
# Look for GitHub email
|
||||
emails = self.browser.find_elements(By.CSS_SELECTOR, "div.m")
|
||||
github_email = None
|
||||
|
||||
for email in emails:
|
||||
if "github" in email.text.lower():
|
||||
github_email = email
|
||||
break
|
||||
|
||||
if github_email:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub verification email found{Style.RESET_ALL}")
|
||||
github_email.click()
|
||||
time.sleep(2)
|
||||
|
||||
# Switch back to default content
|
||||
self.browser.switch_to.default_content()
|
||||
|
||||
# Switch to email content frame
|
||||
iframe = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "ifmail"))
|
||||
)
|
||||
self.browser.switch_to.frame(iframe)
|
||||
|
||||
# Find verification link
|
||||
try:
|
||||
# Look for the verification button or link
|
||||
verification_elements = self.browser.find_elements(By.XPATH, "//a[contains(text(), 'Verify') or contains(text(), 'verify') or contains(@href, 'verify')]")
|
||||
|
||||
if verification_elements:
|
||||
verification_link = verification_elements[0].get_attribute('href')
|
||||
print(f"{Fore.CYAN}{EMOJI['LINK']} Found verification link{Style.RESET_ALL}")
|
||||
|
||||
# Open the verification link in the same window
|
||||
self.browser.get(verification_link)
|
||||
time.sleep(5)
|
||||
|
||||
# Check if verification was successful
|
||||
if "verified" in self.browser.page_source.lower() or "successful" in self.browser.page_source.lower():
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Email verified successfully{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Email verification page loaded but success not confirmed{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check if verification was successful manually and press Enter to continue...{Style.RESET_ALL}")
|
||||
input()
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No verification link found in email{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error extracting verification link: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} No GitHub verification email yet, waiting... ({attempt}/{max_attempts}){Style.RESET_ALL}")
|
||||
time.sleep(15) # Wait before checking again
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error checking email: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No verification email received after {max_attempts} attempts{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to check manually? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check your YOPmail inbox manually at: https://yopmail.com/en/wm")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Username: {username}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've verified the email...{Style.RESET_ALL}")
|
||||
input()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to check verification email: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_cursor(self):
|
||||
"""Register with Cursor using GitHub"""
|
||||
if not self.browser:
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} Registering with Cursor using GitHub...{Style.RESET_ALL}")
|
||||
|
||||
# Navigate to Cursor login page
|
||||
self.browser.get("https://cursor.sh/login")
|
||||
time.sleep(3)
|
||||
|
||||
try:
|
||||
# Look for GitHub login button
|
||||
github_buttons = WebDriverWait(self.browser, 15).until(
|
||||
EC.presence_of_all_elements_located((By.XPATH, "//button[contains(., 'GitHub') or contains(@class, 'github')]"))
|
||||
)
|
||||
|
||||
if not github_buttons:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} GitHub login button not found{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Click the first GitHub button
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking GitHub login button...{Style.RESET_ALL}")
|
||||
github_buttons[0].click()
|
||||
time.sleep(5)
|
||||
|
||||
# Check if we're redirected to GitHub login
|
||||
current_url = self.browser.current_url
|
||||
if "github.com" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirected to GitHub login{Style.RESET_ALL}")
|
||||
|
||||
# Check if we need to log in to GitHub
|
||||
if "login" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Logging into GitHub...{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Enter GitHub credentials
|
||||
username_field = WebDriverWait(self.browser, 10).until(
|
||||
EC.presence_of_element_located((By.ID, "login_field"))
|
||||
)
|
||||
username_field.send_keys(self.github_username)
|
||||
|
||||
password_field = self.browser.find_element(By.ID, "password")
|
||||
password_field.send_keys(self.github_password)
|
||||
|
||||
# Click sign in
|
||||
signin_button = self.browser.find_element(By.CSS_SELECTOR, "input[type='submit']")
|
||||
signin_button.click()
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub login: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check if we're on the authorization page
|
||||
if "authorize" in self.browser.current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Authorizing Cursor app...{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Look for authorization button
|
||||
auth_buttons = self.browser.find_elements(By.XPATH, "//button[contains(., 'Authorize') or contains(@class, 'btn-primary')]")
|
||||
|
||||
if auth_buttons:
|
||||
auth_buttons[0].click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor authorized with GitHub{Style.RESET_ALL}")
|
||||
time.sleep(5)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No authorization button found, GitHub may be already authorized{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub authorization: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for Cursor dashboard to load
|
||||
timeout = 30
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
if "cursor.sh" in self.browser.current_url and not "login" in self.browser.current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Successfully logged into Cursor{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if "login" in self.browser.current_url:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to log into Cursor after {timeout} seconds{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Wait for dashboard elements to load
|
||||
time.sleep(3)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor registered with GitHub successfully{Style.RESET_ALL}")
|
||||
|
||||
# Now reset the machine ID
|
||||
return self.reset_machine_id()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error during Cursor registration: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register with Cursor: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def reset_machine_id(self):
|
||||
"""Reset the Cursor machine ID to bypass limitations"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Resetting Cursor machine ID...{Style.RESET_ALL}")
|
||||
|
||||
# Find Cursor app data location based on platform
|
||||
cursor_data_dir = None
|
||||
if platform.system() == "Windows":
|
||||
appdata = os.getenv('APPDATA')
|
||||
if appdata:
|
||||
cursor_data_dir = os.path.join(appdata, "cursor", "Local Storage", "leveldb")
|
||||
elif platform.system() == "Darwin": # macOS
|
||||
home = os.path.expanduser("~")
|
||||
cursor_data_dir = os.path.join(home, "Library", "Application Support", "cursor", "Local Storage", "leveldb")
|
||||
elif platform.system() == "Linux":
|
||||
home = os.path.expanduser("~")
|
||||
cursor_data_dir = os.path.join(home, ".config", "cursor", "Local Storage", "leveldb")
|
||||
|
||||
if not cursor_data_dir or not os.path.exists(cursor_data_dir):
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found at: {cursor_data_dir}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reset the machine ID manually{Style.RESET_ALL}")
|
||||
|
||||
# Try to find the Cursor data directory
|
||||
if platform.system() == "Linux":
|
||||
possible_paths = [
|
||||
os.path.join(os.path.expanduser("~"), ".config", "cursor"),
|
||||
os.path.join(os.path.expanduser("~"), ".cursor")
|
||||
]
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor directory at: {path}{Style.RESET_ALL}")
|
||||
# Look for Local Storage subfolder
|
||||
for root, dirs, files in os.walk(path):
|
||||
if "Local Storage" in dirs:
|
||||
cursor_data_dir = os.path.join(root, "Local Storage", "leveldb")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor data directory at: {cursor_data_dir}{Style.RESET_ALL}")
|
||||
break
|
||||
break
|
||||
|
||||
if cursor_data_dir and os.path.exists(cursor_data_dir):
|
||||
# Generate a new UUID
|
||||
new_machine_id = str(uuid.uuid4())
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} New machine ID: {new_machine_id}{Style.RESET_ALL}")
|
||||
|
||||
# Ask for permission to modify files
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} This operation will modify Cursor app data files{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to continue? (yes/no){Style.RESET_ALL}")
|
||||
response = input().lower().strip()
|
||||
if response not in ['yes', 'y']:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Machine ID reset aborted{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Backup the directory
|
||||
backup_dir = cursor_data_dir + "_backup_" + time.strftime("%Y%m%d%H%M%S")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Creating backup of data directory to: {backup_dir}{Style.RESET_ALL}")
|
||||
try:
|
||||
shutil.copytree(cursor_data_dir, backup_dir)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Backup created successfully{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Failed to create backup: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without backup...{Style.RESET_ALL}")
|
||||
|
||||
# Find and modify files containing the machine ID
|
||||
modified = False
|
||||
for filename in os.listdir(cursor_data_dir):
|
||||
if filename.endswith(".log") or filename.endswith(".ldb"):
|
||||
file_path = os.path.join(cursor_data_dir, filename)
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
content = f.read()
|
||||
|
||||
# Look for patterns that might contain machine ID
|
||||
if b"machineId" in content:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Found machineId reference in: {filename}{Style.RESET_ALL}")
|
||||
modified = True
|
||||
|
||||
# For safety, don't modify the binary files directly
|
||||
# Instead, instruct user to uninstall and reinstall Cursor
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Binary files found that may contain machine ID{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} For best results, please:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 1. Close Cursor if it's running{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 2. Uninstall Cursor completely{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 3. Reinstall Cursor{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} 4. Login with your new GitHub account{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error processing file {filename}: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if not modified:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No machine ID references found in data files{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reinstall Cursor for a complete reset{Style.RESET_ALL}")
|
||||
|
||||
# Save credentials before returning
|
||||
self.save_credentials()
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID reset process completed{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to manually reset the machine ID by reinstalling Cursor{Style.RESET_ALL}")
|
||||
|
||||
# Still save credentials
|
||||
self.save_credentials()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to reset machine ID: {str(e)}{Style.RESET_ALL}")
|
||||
# Still save credentials even if machine ID reset fails
|
||||
self.save_credentials()
|
||||
return False
|
||||
|
||||
def save_credentials(self):
|
||||
"""Save the generated credentials to a file"""
|
||||
try:
|
||||
if not self.email_address or not self.github_username or not self.github_password:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} No credentials to save{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
output_file = "github_cursor_accounts.txt"
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
credentials = {
|
||||
"timestamp": timestamp,
|
||||
"github_username": self.github_username,
|
||||
"github_password": self.github_password,
|
||||
"email": self.email_address
|
||||
}
|
||||
|
||||
credentials_json = json.dumps(credentials)
|
||||
|
||||
# Check if file exists and create if not
|
||||
file_exists = os.path.exists(output_file)
|
||||
|
||||
with open(output_file, "a") as f:
|
||||
if not file_exists:
|
||||
f.write("# GitHub + Cursor AI Accounts\n")
|
||||
f.write("# Format: JSON with timestamp, github_username, github_password, email\n\n")
|
||||
|
||||
f.write(credentials_json + "\n")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Credentials saved to: {output_file}{Style.RESET_ALL}")
|
||||
|
||||
# Print a summary
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} Registration Summary:{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Saved to: {output_file}{Style.RESET_ALL}\n")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to save credentials: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} Make sure to copy these credentials manually:{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}\n")
|
||||
return False
|
||||
|
||||
def cleanup(self):
|
||||
"""Clean up resources"""
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def start_registration(self):
|
||||
"""Start the GitHub Cursor registration process"""
|
||||
try:
|
||||
# Step 1: Get temporary email
|
||||
if not self.get_temp_email():
|
||||
return False
|
||||
|
||||
# Step 2: Register GitHub account
|
||||
if not self.register_github():
|
||||
return False
|
||||
|
||||
# Step 3: Check and verify email
|
||||
if not self.check_email_verification():
|
||||
return False
|
||||
|
||||
# Step 4: Register Cursor with GitHub
|
||||
if not self.register_cursor():
|
||||
return False
|
||||
|
||||
# Step 5: Reset machine ID
|
||||
self.reset_machine_id()
|
||||
|
||||
return True
|
||||
finally:
|
||||
self.cleanup()
|
||||
|
||||
def display_features_and_warnings(translator=None):
|
||||
"""Display features and warnings before proceeding"""
|
||||
if translator:
|
||||
print(f"\n🚀 {translator.get('github_register.title')}")
|
||||
print("=====================================")
|
||||
print(f"{translator.get('github_register.features_header')}:")
|
||||
print(f" - {translator.get('github_register.feature1')}")
|
||||
print(f" - {translator.get('github_register.feature2')}")
|
||||
print(f" - {translator.get('github_register.feature3')}")
|
||||
print(f" - {translator.get('github_register.feature4')}")
|
||||
print(f" - {translator.get('github_register.feature5')}")
|
||||
print(f" - {translator.get('github_register.feature6')}")
|
||||
print(f"\n⚠️ {translator.get('github_register.warnings_header')}:")
|
||||
print(f" - {translator.get('github_register.warning1')}")
|
||||
print(f" - {translator.get('github_register.warning2')}")
|
||||
print(f" - {translator.get('github_register.warning3')}")
|
||||
print(f" - {translator.get('github_register.warning4')}")
|
||||
print("=====================================\n")
|
||||
else:
|
||||
print("\n🚀 GitHub + Cursor AI Registration Automation")
|
||||
print("=====================================")
|
||||
print("Features:")
|
||||
print(" - Creates a temporary email using YOPmail")
|
||||
print(" - Registers a new GitHub account with random credentials")
|
||||
print(" - Verifies the GitHub email automatically")
|
||||
print(" - Logs into Cursor AI using GitHub authentication")
|
||||
print(" - Resets the machine ID to bypass trial detection")
|
||||
print(" - Saves all credentials to a file")
|
||||
print("\n⚠️ Warnings:")
|
||||
print(" - This script automates account creation, which may violate GitHub/Cursor terms of service")
|
||||
print(" - Requires internet access and administrative privileges")
|
||||
print(" - CAPTCHA or additional verification may interrupt automation")
|
||||
print(" - Use responsibly and at your own risk")
|
||||
print("=====================================\n")
|
||||
|
||||
def get_user_confirmation(translator=None):
|
||||
"""Prompt the user for confirmation to proceed"""
|
||||
while True:
|
||||
if translator:
|
||||
response = input(f"{translator.get('github_register.confirm')} (yes/no): ").lower().strip()
|
||||
else:
|
||||
response = input("Do you want to proceed with GitHub + Cursor AI registration? (yes/no): ").lower().strip()
|
||||
|
||||
if response in ['yes', 'y']:
|
||||
return True
|
||||
elif response in ['no', 'n']:
|
||||
if translator:
|
||||
print(f"❌ {translator.get('github_register.cancelled')}")
|
||||
else:
|
||||
print("❌ Operation cancelled.")
|
||||
return False
|
||||
else:
|
||||
if translator:
|
||||
print(f"{translator.get('github_register.invalid_choice')}")
|
||||
else:
|
||||
print("Please enter 'yes' or 'no'.")
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to run the GitHub Cursor registration process"""
|
||||
logging.info(f"{Fore.CYAN} {translator.get('github_register.starting_automation')}{Style.RESET_ALL}")
|
||||
|
||||
# Display features and warnings
|
||||
display_features_and_warnings(translator)
|
||||
|
||||
# Get user confirmation
|
||||
if not get_user_confirmation(translator):
|
||||
return
|
||||
|
||||
# Start registration process
|
||||
registration = GitHubCursorRegistration(translator)
|
||||
success = registration.start_registration()
|
||||
|
||||
# Display final message
|
||||
if success:
|
||||
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {translator.get('github_register.completed_successfully')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_username')}: {registration.github_username}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_password')}: {registration.github_password}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.email')}: {registration.email_address}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.credentials_saved')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('github_register.registration_encountered_issues')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('github_register.check_browser_windows_for_manual_intervention_or_try_again_later')}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for user acknowledgment
|
||||
if translator:
|
||||
input(f"\n{EMOJI['INFO']} {translator.get('register.press_enter')}...")
|
||||
else:
|
||||
input(f"\n{EMOJI['INFO']} Press Enter to continue...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
images/cloudflare_2025-02-12_13-43-21.png
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
images/locale_2025-01-15_13-40-08.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
images/new107_2025-01-15_13-53-56.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
images/new_2025-02-27_10-42-44.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
images/new_2025-03-19_00-19-09.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
images/new_2025-03-22_19-53-10.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
images/pass_2025-02-08_21-48-36.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
images/paypal.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
images/pronew_2025-02-13_15-01-32.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
images/provi-code.jpg
Normal file
|
After Width: | Height: | Size: 123 KiB |
392
locales/bg.json
Normal file
@@ -0,0 +1,392 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Възможни избори",
|
||||
"exit": "Затвори програмата",
|
||||
"reset": "Нулирай ид-то на компютъра",
|
||||
"register": "Регистрирай нов Курсор акаунт",
|
||||
"register_google": "Регистрирай се с Google акаунт",
|
||||
"register_github": "Регистрирай се с GitHub акаунт",
|
||||
"register_manual": "Регистрирай се със свой имейл по избор",
|
||||
"quit": "Затвори приложението Курсор",
|
||||
"select_language": "Избери език",
|
||||
"es": "Spanish",
|
||||
"input_choice": "Моля, въведете своя избор ({choices})",
|
||||
"invalid_choice": "Невалиден избор. Моля, въведете избор от {choices}.",
|
||||
"program_terminated": "Програмата беше затворена от вас",
|
||||
"error_occurred": "Възникна грешка: {error}. Опитайте отново",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"disable_auto_update": "Спрете автоматичните ъпдейти на Курсор",
|
||||
"lifetime_access_enabled": "ДОЖИВОТЕН ДОСТЪП Е АКТИВИРАН",
|
||||
"totally_reset": "Нулирайте изцяло Курсор",
|
||||
"outdate": "Изтекъл срок",
|
||||
"temp_github_register": "Временно регистриране с GitHub",
|
||||
"coming_soon": "Очаквайте скоро"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"bg": "Bulgarian",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Започни излизането от Курсор",
|
||||
"no_process": "Няма съществуващ процес, засягащ Курсор",
|
||||
"terminating": "Прекратяване на процес {pid}",
|
||||
"waiting": "Изчакване на процеса да приключи",
|
||||
"success": "Всички процеси, свързани с Курсор, бяха прекратени",
|
||||
"timeout": "Таймаут на процеса: {pids}",
|
||||
"error": "Възникна грешка: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "",
|
||||
"checking": "Проверка на конфигурационния файл",
|
||||
"not_found": "Конфигурационният файл не беше намерен",
|
||||
"no_permission": "Конфигурационният файл не може да бъде прочетен или записан. Моля, проверете разрешенията на файла",
|
||||
"reading": "Четене на текущата конфигурация",
|
||||
"creating_backup": "Създаване на резервно копие на конфигурацията",
|
||||
"backup_exists": "Резервното копие вече съществува. Стъпката за резервно копие се пропуска",
|
||||
"generating": "Генериране на ново машинно ID",
|
||||
"saving_json": "Запазване на новата конфигурация в JSON",
|
||||
"success": "Машинното ID беше успешно нулирано",
|
||||
"new_id": "Ново машинно ID",
|
||||
"permission_error": "Грешка в разрешенията: {error}",
|
||||
"run_as_admin": "Моля, опитайте да стартирате тази програма като администратор",
|
||||
"process_error": "Грешка при нулиране: {error}",
|
||||
"updating_sqlite": "Актуализиране на SQLite базата данни",
|
||||
"updating_pair": "Актуализиране на двойката ключ-стойност",
|
||||
"sqlite_success": "SQLite базата данни беше успешно актуализирана",
|
||||
"sqlite_error": "Грешка при актуализиране на SQLite базата данни: {error}",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"unsupported_os": "Неподдържана операционна система: {os}",
|
||||
"linux_path_not_found": "Linux пътят не беше намерен",
|
||||
"updating_system_ids": "Актуализиране на системните ID",
|
||||
"system_ids_updated": "Системните ID бяха успешно актуализирани",
|
||||
"system_ids_update_failed": "Грешка при актуализиране на системните ID: {error}",
|
||||
"windows_guid_updated": "Windows GUID беше успешно актуализиран",
|
||||
"windows_permission_denied": "Достъпът до Windows беше отказан",
|
||||
"windows_guid_update_failed": "Грешка при актуализиране на Windows GUID",
|
||||
"macos_uuid_updated": "macOS UUID беше успешно актуализиран",
|
||||
"plutil_command_failed": "Командата plutil не беше успешна",
|
||||
"start_patching": "Започване на прилагане на корекция за getMachineId",
|
||||
"macos_uuid_update_failed": "Грешка при актуализиране на macOS UUID",
|
||||
"current_version": "Текуща версия на Курсор: {version}",
|
||||
"patch_completed": "Корекцията на getMachineId беше успешно завършена",
|
||||
"patch_failed": "Грешка при прилагане на корекция за getMachineId: {error}",
|
||||
"version_check_passed": "Проверката на версията на Курсор беше успешна",
|
||||
"file_modified": "Файлът беше променен",
|
||||
"version_less_than_0_45": "Версията на Курсор е < 0.45.0, корекцията на getMachineId се пропуска",
|
||||
"detecting_version": "Откриване на версията на Курсор",
|
||||
"patching_getmachineid": "Прилагане на корекция за getMachineId",
|
||||
"version_greater_than_0_45": "Версията на Курсор е >= 0.45.0, прилага се корекция за getMachineId",
|
||||
"permission_denied": "Достъпът беше отказан: {error}",
|
||||
"backup_created": "Резервното копие беше създадено",
|
||||
"update_success": "Актуализацията беше успешна",
|
||||
"update_failed": "Грешка при актуализация: {error}",
|
||||
"windows_machine_guid_updated": "Windows машинното GUID беше успешно актуализирано",
|
||||
"reading_package_json": "Четене на package.json {path}",
|
||||
"invalid_json_object": "Невалиден JSON обект",
|
||||
"no_version_field": "Няма поле за версия в package.json",
|
||||
"version_field_empty": "Полето за версия е празно",
|
||||
"invalid_version_format": "Невалиден формат на версията: {version}",
|
||||
"found_version": "Намерена версия: {version}",
|
||||
"version_parse_error": "Грешка при анализ на версията: {error}",
|
||||
"package_not_found": "Package.json не беше намерен: {path}",
|
||||
"check_version_failed": "Грешка при проверка на версията: {error}",
|
||||
"stack_trace": "Проследяване на стека",
|
||||
"version_too_low": "Версията на Курсор е твърде ниска: {version} < 0.45.0"
|
||||
},
|
||||
"register": {
|
||||
"title": "Инструмент за регистрация в Курсор",
|
||||
"start": "Започване на процеса на регистрация...",
|
||||
"handling_turnstile": "Обработка на проверка за сигурност...",
|
||||
"retry_verification": "Опит за повторна проверка...",
|
||||
"detect_turnstile": "Проверка на защитната врата...",
|
||||
"verification_success": "Проверката за сигурност беше успешна",
|
||||
"starting_browser": "Стартиране на браузъра...",
|
||||
"form_success": "Формата беше успешно изпратена",
|
||||
"browser_started": "Браузърът беше успешно стартиран",
|
||||
"waiting_for_second_verification": "Изчакване на втората проверка по имейл...",
|
||||
"waiting_for_verification_code": "Изчакване на код за потвърждение...",
|
||||
"password_success": "Паролата беше успешно зададена",
|
||||
"password_error": "Грешка при задаване на парола: {error}. Моля, опитайте отново",
|
||||
"waiting_for_page_load": "Зареждане на страницата...",
|
||||
"first_verification_passed": "Първата проверка беше успешна",
|
||||
"mailbox": "Успешен достъп до пощенската кутия",
|
||||
"register_start": "Начало на регистрацията",
|
||||
"form_submitted": "Формата беше изпратена, започване на проверка...",
|
||||
"filling_form": "Попълване на формуляра",
|
||||
"visiting_url": "Посещение на URL",
|
||||
"basic_info": "Основните данни бяха изпратени",
|
||||
"handle_turnstile": "Обработка на защитната врата",
|
||||
"no_turnstile": "Няма защитна врата",
|
||||
"turnstile_passed": "Защитната врата беше премината",
|
||||
"verification_start": "Започване на получаване на код за потвърждение",
|
||||
"verification_timeout": "Времето за получаване на код за потвърждение изтече",
|
||||
"verification_not_found": "Кодът за потвърждение не беше намерен",
|
||||
"try_get_code": "Опит | {attempt} Получаване на код за потвърждение | Оставащо време: {time}s",
|
||||
"get_account": "Получаване на информация за акаунта",
|
||||
"get_token": "Получаване на токен за сесия на Курсор",
|
||||
"token_success": "Токенът беше успешно получен",
|
||||
"token_attempt": "Опит | {attempt} опита за получаване на токен | Ще бъде опит отново след {time}s",
|
||||
"token_max_attempts": "Достигнат максимален брой опити ({max}) | Неуспешно получаване на токен",
|
||||
"token_failed": "Грешка при получаване на токен: {error}",
|
||||
"account_error": "Грешка при получаване на информация за акаунта: {error}",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"browser_start": "Стартиране на браузъра",
|
||||
"open_mailbox": "Отваряне на страницата на пощенската кутия",
|
||||
"email_error": "Грешка при получаване на имейл адрес",
|
||||
"setup_error": "Грешка при настройка на имейл: {error}",
|
||||
"start_getting_verification_code": "Започване на получаване на код за потвърждение, ще бъде опит след 60 секунди",
|
||||
"get_verification_code_timeout": "Времето за получаване на код за потвърждение изтече",
|
||||
"get_verification_code_success": "Кодът за потвърждение беше успешно получен",
|
||||
"try_get_verification_code": "Опит | {attempt} Получаване на код за потвърждение | Оставащо време: {remaining_time}s",
|
||||
"verification_code_filled": "Кодът за потвърждение беше попълнен",
|
||||
"login_success_and_jump_to_settings_page": "Успешен вход и преход към страницата с настройки",
|
||||
"detect_login_page": "Открита е страница за вход, започване на влизане...",
|
||||
"cursor_registration_completed": "Регистрацията в Курсор беше завършена!",
|
||||
"set_password": "Задаване на парола",
|
||||
"basic_info_submitted": "Основните данни бяха изпратени",
|
||||
"cursor_auth_info_updated": "Информацията за удостоверяване на Курсор беше актуализирана",
|
||||
"cursor_auth_info_update_failed": "Грешка при актуализиране на информацията за удостоверяване на Курсор",
|
||||
"reset_machine_id": "Нулиране на машинното ID",
|
||||
"account_info_saved": "Информацията за акаунта беше запазена",
|
||||
"save_account_info_failed": "Грешка при запазване на информацията за акаунта",
|
||||
"get_email_address": "Получаване на имейл адрес",
|
||||
"update_cursor_auth_info": "Актуализиране на информацията за удостоверяване на Курсор",
|
||||
"register_process_error": "Грешка в процеса на регистрация: {error}",
|
||||
"setting_password": "Задаване на парола",
|
||||
"manual_code_input": "Ръчно въвеждане на код",
|
||||
"manual_email_input": "Ръчно въвеждане на имейл",
|
||||
"password": "Парола",
|
||||
"first_name": "Име",
|
||||
"last_name": "Фамилия",
|
||||
"exit_signal": "Сигнал за изход",
|
||||
"email_address": "Имейл адрес",
|
||||
"config_created": "Конфигурацията беше създадена",
|
||||
"verification_failed": "Проверката беше неуспешна",
|
||||
"verification_error": "Грешка при проверка: {error}",
|
||||
"config_option_added": "Добавена е опция за конфигурация: {option}",
|
||||
"config_updated": "Конфигурацията беше актуализирана",
|
||||
"password_submitted": "Паролата беше изпратена",
|
||||
"total_usage": "Общо използване: {usage}",
|
||||
"setting_on_password": "Задаване на парола",
|
||||
"getting_code": "Получаване на код за потвърждение, ще бъде опит след 60 секунди",
|
||||
"human_verify_error": "Не може да се потвърди, че потребителят е човек. Опитва се отново...",
|
||||
"max_retries_reached": "Достигнат максимален брой опити. Регистрацията беше неуспешна."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Управление на удостоверяването на Курсор",
|
||||
"checking_auth": "Проверка на файла за удостоверяване",
|
||||
"auth_not_found": "Файлът за удостоверяване не беше намерен",
|
||||
"auth_file_error": "Грешка във файла за удостоверяване: {error}",
|
||||
"reading_auth": "Четене на файла за удостоверяване",
|
||||
"updating_auth": "Актуализиране на информацията за удостоверяване",
|
||||
"auth_updated": "Информацията за удостоверяване беше успешно актуализирана",
|
||||
"auth_update_failed": "Грешка при актуализиране на информацията за удостоверяване: {error}",
|
||||
"auth_file_created": "Файлът за удостоверяване беше създаден",
|
||||
"auth_file_create_failed": "Грешка при създаване на файла за удостоверяване: {error}",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"reset_machine_id": "Нулиране на машинното ID",
|
||||
"database_connection_closed": "Връзката с базата беше затворена",
|
||||
"database_updated_successfully": "Базата данни беше успешно актуализирана",
|
||||
"connected_to_database": "Успешно свързване с базата данни",
|
||||
"updating_pair": "Актуализиране на двойката ключ-стойност",
|
||||
"db_not_found": "Файлът на базата данни не беше намерен: {path}",
|
||||
"db_permission_error": "Няма достъп до файла на базата данни. Моля, проверете разрешенията",
|
||||
"db_connection_error": "Грешка при свързване с базата данни: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Създаване на нов имейл",
|
||||
"blocked_domain": "Блокиран домейн",
|
||||
"select_domain": "Избиране на случаен домейн",
|
||||
"copy_email": "Копиране на имейл адрес",
|
||||
"enter_mailbox": "Влизане в пощенската кутия",
|
||||
"refresh_mailbox": "Опресняване на пощенската кутия",
|
||||
"check_verification": "Проверка на кода за потвърждение",
|
||||
"verification_found": "Кодът за потвърждение беше намерен",
|
||||
"verification_not_found": "Кодът за потвърждение не беше намерен",
|
||||
"browser_error": "Грешка при контрол на браузъра: {error}",
|
||||
"navigation_error": "Грешка при навигация: {error}",
|
||||
"email_copy_error": "Грешка при копиране на имейл: {error}",
|
||||
"mailbox_error": "Грешка в пощенската кутия: {error}",
|
||||
"token_saved_to_file": "Токенът беше запазен във файла cursor_tokens.txt",
|
||||
"navigate_to": "Навигиране към {url}",
|
||||
"generate_email_success": "Имейлът беше успешно създаден",
|
||||
"select_email_domain": "Избор на домейн за имейл",
|
||||
"select_email_domain_success": "Успешен избор на домейн за имейл",
|
||||
"get_email_name": "Получаване на име на имейл",
|
||||
"get_email_name_success": "Успешно получаване на име на имейл",
|
||||
"get_email_address": "Получаване на имейл адрес",
|
||||
"get_email_address_success": "Успешно получаване на имейл адрес",
|
||||
"enter_mailbox_success": "Успешно влизане в пощенската кутия",
|
||||
"found_verification_code": "Кодът за потвърждение беше намерен",
|
||||
"get_cursor_session_token": "Получаване на токен за сесия на Курсор",
|
||||
"get_cursor_session_token_success": "Успешно получаване на токен за сесия на Курсор",
|
||||
"get_cursor_session_token_failed": "Грешка при получаване на токен за сесия на Курсор",
|
||||
"save_token_failed": "Грешка при запазване на токен",
|
||||
"database_updated_successfully": "Базата данни беше успешно актуализирана",
|
||||
"database_connection_closed": "Връзката с базата данни беше затворена",
|
||||
"no_valid_verification_code": "Няма валиден код за потвърждение"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Стартиране на браузъра",
|
||||
"visiting_site": "Посещение на сайтове за имейл домейни",
|
||||
"create_success": "Имейлът беше успешно създаден",
|
||||
"create_failed": "Грешка при създаване на имейл",
|
||||
"create_error": "Грешка при създаване на имейл: {error}",
|
||||
"refreshing": "Опресняване на имейл",
|
||||
"refresh_success": "Имейлът беше успешно опреснен",
|
||||
"refresh_error": "Грешка при опресняване на имейл: {error}",
|
||||
"refresh_button_not_found": "Бутонът за опресняване не беше намерен",
|
||||
"verification_found": "Потвърждението беше намерено",
|
||||
"verification_not_found": "Потвърждението не беше намерено",
|
||||
"verification_error": "Грешка при потвърждение: {error}",
|
||||
"verification_code_found": "Кодът за потвърждение беше намерен",
|
||||
"verification_code_not_found": "Кодът за потвърждение не беше намерен",
|
||||
"verification_code_error": "Грешка при код за потвърждение: {error}",
|
||||
"address": "Имейл адрес",
|
||||
"all_domains_blocked": "Всички домейни са блокирани, смяна на услуга",
|
||||
"no_available_domains_after_filtering": "Няма налични домейни след филтриране",
|
||||
"switching_service": "Смяна на услугата {service}",
|
||||
"domains_list_error": "Грешка при получаване на списък с домейни: {error}",
|
||||
"failed_to_get_available_domains": "Неуспешно получаване на налични домейни",
|
||||
"domains_excluded": "Изключени домейни: {domains}",
|
||||
"failed_to_create_account": "Неуспешно създаване на акаунт",
|
||||
"account_creation_error": "Грешка при създаване на акаунт: {error}",
|
||||
"blocked_domains": "Блокирани домейни: {domains}",
|
||||
"blocked_domains_loaded": "Блокирани домейни са заредени: {count}",
|
||||
"blocked_domains_loaded_error": "Грешка при зареждане на блокирани домейни: {error}",
|
||||
"blocked_domains_loaded_success": "Блокираните домейни бяха успешно заредени",
|
||||
"blocked_domains_loaded_timeout": "Време за зареждане на блокирани домейни: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Грешка при време за зареждане на блокирани домейни: {error}",
|
||||
"available_domains_loaded": "Налични домейни са заредени: {count}",
|
||||
"domains_filtered": "Филтрирани домейни: {count}",
|
||||
"trying_to_create_email": "Опит за създаване на имейл: {email}",
|
||||
"domain_blocked": "Домейнът е блокиран: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Деактивиране на автоматичните актуализации на Курсор",
|
||||
"disable_success": "Автоматичните актуализации бяха успешно деактивирани",
|
||||
"disable_failed": "Грешка при деактивиране на автоматичните актуализации: {error}",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"start_disable": "Започване на деактивиране на автоматичните актуализации",
|
||||
"killing_processes": "Прекратяване на процеси",
|
||||
"processes_killed": "Процесите бяха прекратени",
|
||||
"removing_directory": "Премахване на директория",
|
||||
"directory_removed": "Директорията беше премахната",
|
||||
"creating_block_file": "Създаване на блокиращ файл",
|
||||
"block_file_created": "Блокиращият файл беше създаден"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Проверка за актуализации...",
|
||||
"new_version_available": "Налична е нова версия! (Текуща: {current}, Последна: {latest})",
|
||||
"updating": "Актуализиране до последната версия. Програмата ще се рестартира автоматично.",
|
||||
"up_to_date": "Използвате последната версия.",
|
||||
"check_failed": "Грешка при проверка за актуализации: {error}",
|
||||
"continue_anyway": "Продължаване с текущата версия...",
|
||||
"update_confirm": "Искате ли да актуализирате до последната версия? (Y/n)",
|
||||
"update_skipped": "Актуализацията беше пропусната.",
|
||||
"invalid_choice": "Невалиден избор. Моля, въведете 'Y' или 'n'.",
|
||||
"development_version": "Версия за разработка {current} > {latest}",
|
||||
"changelog_title": "Списък с промени"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Пълно нулиране на Курсор",
|
||||
"checking_config": "Проверка на конфигурационния файл",
|
||||
"config_not_found": "Конфигурационният файл не беше намерен",
|
||||
"no_permission": "Конфигурационният файл не може да бъде прочетен или записан. Моля, проверете разрешенията на файла",
|
||||
"reading_config": "Четене на текущата конфигурация",
|
||||
"creating_backup": "Създаване на резервно копие на конфигурацията",
|
||||
"backup_exists": "Резервното копие вече съществува. Стъпката за резервно копие се пропуска",
|
||||
"generating_new_machine_id": "Генериране на ново машинно ID",
|
||||
"saving_new_config": "Запазване на новата конфигурация в JSON",
|
||||
"success": "Курсор беше успешно нулиран",
|
||||
"error": "Грешка при нулиране на Курсор: {error}",
|
||||
"press_enter": "Натиснете Enter, за да излезете",
|
||||
"reset_machine_id": "Нулиране на машинното ID",
|
||||
"database_connection_closed": "Връзката с базата данни беше затворена",
|
||||
"database_updated_successfully": "Базата данни беше успешно актуализирана",
|
||||
"connected_to_database": "Успешно свързване с базата данни",
|
||||
"updating_pair": "Актуализиране на двойката ключ-стойност",
|
||||
"db_not_found": "Файлът на базата данни не беше намерен: {path}",
|
||||
"db_permission_error": "Няма достъп до файла на базата данни. Моля, проверете разрешенията",
|
||||
"db_connection_error": "Грешка при свързване с базата данни: {error}",
|
||||
"feature_title": "ФУНКЦИИ",
|
||||
"feature_1": "Пълно премахване на настройките и конфигурациите на Курсор AI",
|
||||
"feature_2": "Изчистване на всички кеширани данни, включително историята и командите на AI",
|
||||
"feature_3": "Нулиране на машинното ID за заобикаляне на ограниченията за пробен период",
|
||||
"feature_4": "Генериране на нови случайни машинни идентификатори",
|
||||
"feature_5": "Премахване на персонализирани разширения и предпочитания",
|
||||
"feature_6": "Нулиране на информацията за пробния период и активиране",
|
||||
"feature_7": "Дълбоко сканиране за скрити файлове, свързани с лицензи и пробен период",
|
||||
"feature_8": "Безопасно запазване на файлове и приложения, които не са свързани с Курсор",
|
||||
"feature_9": "Съвместимост с Windows, macOS и Linux",
|
||||
"disclaimer_title": "ПРАВНО ИЗВЕСТВИЕ",
|
||||
"disclaimer_1": "Този инструмент ще изтрие за постоянно всички настройки на Курсор AI,",
|
||||
"disclaimer_2": "конфигурации и кеширани данни. Това действие е необратимо.",
|
||||
"disclaimer_3": "Вашите кодови файлове НЯМА да бъдат засегнати и този инструмент",
|
||||
"disclaimer_4": "е проектиран специално да се фокусира върху файловете на редактора на Курсор AI и механизмите за откриване на пробен период.",
|
||||
"disclaimer_5": "Другите приложения на вашата система няма да бъдат засегнати.",
|
||||
"disclaimer_6": "След като използвате този инструмент, ще трябва да преинсталирате Курсор AI.",
|
||||
"disclaimer_7": "Вие носите отговорността за използването му",
|
||||
"confirm_title": "Сигурни ли сте, че искате да продължите?",
|
||||
"confirm_1": "Това действие ще изтрие всички настройки на Курсор AI,",
|
||||
"confirm_2": "конфигурации и кеширани данни. Това действие е необратимо.",
|
||||
"confirm_3": "Вашите кодови файлове НЯМА да бъдат засегнати и този инструмент",
|
||||
"confirm_4": "е проектиран специално да се фокусира върху файловете на редактора на Курсор AI и механизмите за откриване на пробен период.",
|
||||
"confirm_5": "Другите приложения на вашата система няма да бъдат засегнати.",
|
||||
"confirm_6": "След като използвате този инструмент, ще трябва да преинсталирате Курсор AI.",
|
||||
"confirm_7": "Вие носите отговорността за използването му",
|
||||
"invalid_choice": "Моля, въведете 'Y' или 'n'",
|
||||
"skipped_for_safety": "Пропуснато за безопасност (не е свързано с Курсор): {path}",
|
||||
"deleted": "Изтрито: {path}",
|
||||
"error_deleting": "Грешка при изтриване на {path}: {error}",
|
||||
"not_found": "Файлът не беше намерен: {path}",
|
||||
"resetting_machine_id": "Нулиране на машинните идентификатори за заобикаляне на ограниченията за пробен период...",
|
||||
"created_machine_id": "Създадено е ново машинно ID: {path}",
|
||||
"error_creating_machine_id": "Грешка при създаване на файл за машинно ID {path}: {error}",
|
||||
"error_searching": "Грешка при търсене на файлове в {path}: {error}",
|
||||
"created_extended_trial_info": "Създадена е нова информация за удължен пробен период: {path}",
|
||||
"error_creating_trial_info": "Грешка при създаване на файл за информация за пробен период {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Нулиране на редактора на Курсор AI... Моля, изчакайте.",
|
||||
"reset_cancelled": "Нулирането беше отменено. Излизане без промени.",
|
||||
"windows_machine_id_modification_skipped": "Промяната на машинното ID на Windows беше пропусната: {error}",
|
||||
"linux_machine_id_modification_skipped": "Промяната на machine-id на Linux беше пропусната: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Забележка: Пълното нулиране на машинното ID може да изисква работа като администратор",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Забележка: Пълното нулиране на системното machine-id може да изисква sudo права",
|
||||
"windows_registry_instructions": "📝 ЗАБЕЛЕЖКА: За пълно нулиране на Windows може да се наложи да изчистите и записите в регистъра.",
|
||||
"windows_registry_instructions_2": " Стартирайте 'regedit' и потърсете и изтрийте ключове, съдържащи 'Cursor' или 'CursorAI' в HKEY_CURRENT_USER\\Software\\.\n",
|
||||
"reset_log_1": "Курсор AI беше напълно нулиран и ограниченията за пробен период бяха заобиколени!",
|
||||
"reset_log_2": "Моля, рестартирайте системата си, за да влязат в сила промените.",
|
||||
"reset_log_3": "Ще трябва да преинсталирате Курсор AI и сега трябва да имате нов пробен период.",
|
||||
"reset_log_4": "За най-добри резултати, помислете за следното:",
|
||||
"reset_log_5": "Използвайте различен имейл адрес при регистрация за нов пробен период",
|
||||
"reset_log_6": "Ако е възможно, използвайте VPN за промяна на IP адреса си",
|
||||
"reset_log_7": "Изчистете бисквитките и кеша на браузъра си, преди да посетите уебсайта на Курсор AI",
|
||||
"reset_log_8": "Ако проблемите продължават, опитайте да инсталирате Курсор AI на друго място",
|
||||
"reset_log_9": "Ако срещнете проблеми, посетете Github Issue Tracker и създайте проблем на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Възникна неочаквана грешка: {error}",
|
||||
"report_issue": "Моля, докладвайте този проблем в Github Issue Tracker на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Процесът беше прекратен от потребителя. Излизане...",
|
||||
"return_to_main_menu": "Връщане към главното меню...",
|
||||
"process_interrupted": "Процесът беше прекратен. Излизане...",
|
||||
"press_enter_to_return_to_main_menu": "Натиснете Enter, за да се върнете към главното меню...",
|
||||
"removing_known": "Премахване на известни файлове за пробен период/лиценз",
|
||||
"performing_deep_scan": "Извършване на дълбоко сканиране за допълнителни файлове за пробен период/лиценз",
|
||||
"found_additional_potential_license_trial_files": "Намерени са {count} допълнителни потенциални файлове за лиценз/пробен период",
|
||||
"checking_for_electron_localstorage_files": "Проверка на Electron localStorage файлове",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Няма намерени допълнителни файлове за лиценз/пробен период при дълбоко сканиране",
|
||||
"removing_electron_localstorage_files": "Премахване на Electron localStorage файлове",
|
||||
"electron_localstorage_files_removed": "Electron localStorage файлове бяха премахнати",
|
||||
"electron_localstorage_files_removal_error": "Грешка при премахване на Electron localStorage файлове: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Премахването на Electron localStorage файлове беше завършено"
|
||||
}
|
||||
}
|
||||
380
locales/de.json
Normal file
@@ -0,0 +1,380 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Verfügbare Optionen",
|
||||
"exit": "Programm Beenden",
|
||||
"reset": "Maschinen-ID Zurücksetzen",
|
||||
"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",
|
||||
"quit": "Cursor-Anwendung Schließen",
|
||||
"select_language": "Sprache Ändern",
|
||||
"input_choice": "Bitte Auswahl eingeben ({choices})",
|
||||
"invalid_choice": "Ungültige Auswahl. Bitte eine Nummer von {choices} eingeben",
|
||||
"program_terminated": "Programm wurde vom Benutzer beendet",
|
||||
"error_occurred": "Ein Fehler ist aufgetreten: {error}. Bitte erneut versuchen",
|
||||
"press_enter": "Drücken Sie Enter zum Beenden",
|
||||
"disable_auto_update": "Cursor Auto-Update Deaktivieren",
|
||||
"lifetime_access_enabled": "LEBENSLANGER ZUGRIFF AKTIVIERT",
|
||||
"totally_reset": "Cursor Vollständig Zurücksetzen",
|
||||
"outdate": "Veraltet",
|
||||
"temp_github_register": "Temporäre GitHub-Registrierung",
|
||||
"admin_required": "Ausführen als ausführbare Datei, Administratorrechte erforderlich.",
|
||||
"admin_required_continue": "Mit der aktuellen Version fortfahren...",
|
||||
"coming_soon": "Bald verfügbar"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Englisch",
|
||||
"zh_cn": "Vereinfachtes Chinesisch",
|
||||
"zh_tw": "Traditionelles Chinesisch",
|
||||
"vi": "Vietnamesisch",
|
||||
"nl": "Niederländisch",
|
||||
"de": "Deutsch",
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ru": "Russisch",
|
||||
"es": "Spanisch"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Beginne Cursor zu Beenden",
|
||||
"no_process": "Kein Laufender Cursor-Prozess",
|
||||
"terminating": "Beende Prozess {pid}",
|
||||
"waiting": "Warte auf Prozessende",
|
||||
"success": "Alle Cursor-Prozesse Beendet",
|
||||
"timeout": "Prozess-Timeout: {pids}",
|
||||
"error": "Fehler Aufgetreten: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Cursor Maschinen-ID Zurücksetzen Tool",
|
||||
"checking": "Konfigurationsdatei Überprüfen",
|
||||
"not_found": "Konfigurationsdatei Nicht Gefunden",
|
||||
"no_permission": "Kann Konfigurationsdatei Nicht Lesen oder Schreiben, Bitte Dateiberechtigungen Überprüfen",
|
||||
"reading": "Aktuelle Konfiguration Lesen",
|
||||
"creating_backup": "Konfigurations-Backup Erstellen",
|
||||
"backup_exists": "Backup-Datei Existiert Bereits, Backup-Schritt Überspringen",
|
||||
"generating": "Neue Maschinen-ID Generieren",
|
||||
"saving_json": "Neue Konfiguration in JSON Speichern",
|
||||
"success": "Maschinen-ID Erfolgreich Zurückgesetzt",
|
||||
"new_id": "Neue Maschinen-ID",
|
||||
"permission_error": "Berechtigungsfehler: {error}",
|
||||
"run_as_admin": "Bitte Versuchen Sie, Dieses Programm als Administrator Auszuführen",
|
||||
"process_error": "Zurücksetzungsprozessfehler: {error}",
|
||||
"updating_sqlite": "SQLite-Datenbank Aktualisieren",
|
||||
"updating_pair": "Schlüssel-Wert-Paar Aktualisieren",
|
||||
"sqlite_success": "SQLite-Datenbank Erfolgreich Aktualisiert",
|
||||
"sqlite_error": "SQLite-Datenbank Aktualisierung Fehlgeschlagen: {error}",
|
||||
"press_enter": "Drücken Sie Enter zum Fortfahren",
|
||||
"unsupported_os": "Nicht Unterstütztes Betriebssystem: {os}",
|
||||
"linux_path_not_found": "Linux-Pfad Nicht Gefunden",
|
||||
"updating_system_ids": "System-IDs Aktualisieren",
|
||||
"system_ids_updated": "System-IDs Erfolgreich Aktualisiert",
|
||||
"system_ids_update_failed": "System-IDs Aktualisierung Fehlgeschlagen: {error}",
|
||||
"windows_guid_updated": "Windows GUID Erfolgreich Aktualisiert",
|
||||
"windows_permission_denied": "Windows Berechtigung Verweigert",
|
||||
"windows_guid_update_failed": "Windows GUID Aktualisierung Fehlgeschlagen",
|
||||
"macos_uuid_updated": "macOS UUID Erfolgreich Aktualisiert",
|
||||
"plutil_command_failed": "plutil-Befehl Fehlgeschlagen",
|
||||
"start_patching": "Patching getMachineId Starten",
|
||||
"macos_uuid_update_failed": "macOS UUID Aktualisierung Fehlgeschlagen",
|
||||
"current_version": "Aktuelle Cursor-Version: {version}",
|
||||
"patch_completed": "Patching getMachineId Abgeschlossen",
|
||||
"patch_failed": "Patching getMachineId Fehlgeschlagen: {error}",
|
||||
"version_check_passed": "Cursor-Version Überprüfung Erfolgreich",
|
||||
"file_modified": "Datei Geändert",
|
||||
"version_less_than_0_45": "Cursor-Version < 0.45.0, Patching getMachineId Überspringen",
|
||||
"detecting_version": "Cursor-Version Erkennen",
|
||||
"patching_getmachineid": "Patching getMachineId",
|
||||
"version_greater_than_0_45": "Cursor-Version >= 0.45.0, Patching getMachineId",
|
||||
"permission_denied": "Berechtigung Verweigert: {error}",
|
||||
"backup_created": "Backup Erstellt",
|
||||
"update_success": "Update Erfolgreich",
|
||||
"update_failed": "Update Fehlgeschlagen: {error}",
|
||||
"windows_machine_guid_updated": "Windows Maschinen-GUID Erfolgreich Aktualisiert",
|
||||
"reading_package_json": "package.json Lesen {path}",
|
||||
"invalid_json_object": "Ungültiges JSON-Objekt",
|
||||
"no_version_field": "Kein Versionsfeld in package.json Gefunden",
|
||||
"version_field_empty": "Versionsfeld ist Leer",
|
||||
"invalid_version_format": "Ungültiges Versionsformat: {version}",
|
||||
"found_version": "Gefundene Version: {version}",
|
||||
"version_parse_error": "Versions-Parse-Fehler: {error}",
|
||||
"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"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registrierungstool",
|
||||
"start": "Registrierungsprozess Starten...",
|
||||
"handling_turnstile": "Sicherheitsüberprüfung Verarbeiten...",
|
||||
"retry_verification": "Überprüfung Erneut Versuchen...",
|
||||
"detect_turnstile": "Sicherheitsüberprüfung Überprüfen...",
|
||||
"verification_success": "Sicherheitsüberprüfung Erfolgreich",
|
||||
"starting_browser": "Browser Öffnen...",
|
||||
"form_success": "Formular Erfolgreich Eingereicht",
|
||||
"browser_started": "Browser Erfolgreich Geöffnet",
|
||||
"waiting_for_second_verification": "Warten auf E-Mail-Verifizierung...",
|
||||
"waiting_for_verification_code": "Warten auf Verifizierungscode...",
|
||||
"password_success": "Passwort Erfolgreich Eingestellt",
|
||||
"password_error": "Konnte Passwort Nicht Einstellen: {error}. Bitte Erneut Versuchen",
|
||||
"waiting_for_page_load": "Seite Laden...",
|
||||
"first_verification_passed": "Erste Überprüfung Erfolgreich",
|
||||
"mailbox": "E-Mail-Postfach Erfolgreich Geöffnet",
|
||||
"register_start": "Registrierung Starten",
|
||||
"form_submitted": "Formular Eingereicht, Überprüfung Starten...",
|
||||
"filling_form": "Formular Ausfüllen",
|
||||
"visiting_url": "URL Besuchen",
|
||||
"basic_info": "Grundlegende Informationen Eingereicht",
|
||||
"handle_turnstile": "Turnstile Verarbeiten",
|
||||
"no_turnstile": "Kein Turnstile Erkannt",
|
||||
"turnstile_passed": "Turnstile Erfolgreich",
|
||||
"verification_start": "Verifizierungscode Erhalten Starten",
|
||||
"verification_timeout": "Verifizierungscode Timeout",
|
||||
"verification_not_found": "Kein Verifizierungscode Gefunden",
|
||||
"try_get_code": "Versuchen | {attempt} Verifizierungscode Erhalten | Verbleibende Zeit: {time}s",
|
||||
"get_account": "Kontoinformationen Erhalten",
|
||||
"get_token": "Cursor-Sitzungstoken Erhalten",
|
||||
"token_success": "Token Erfolgreich Erhalten",
|
||||
"token_attempt": "Versuchen | {attempt} Mal Token zu Erhalten | Erneut Versuchen in {time}s",
|
||||
"token_max_attempts": "Maximale Versuche Erreicht ({max}) | Token Erhalten Fehlgeschlagen",
|
||||
"token_failed": "Token Erhalten Fehlgeschlagen: {error}",
|
||||
"account_error": "Kontoinformationen Erhalten Fehlgeschlagen: {error}",
|
||||
"press_enter": "Drücken Sie Enter zum Fortfahren",
|
||||
"browser_start": "Browser Starten",
|
||||
"open_mailbox": "Mailbox-Seite Öffnen",
|
||||
"email_error": "E-Mail-Adresse Erhalten Fehlgeschlagen",
|
||||
"setup_error": "E-Mail-Einrichtungsfehler: {error}",
|
||||
"start_getting_verification_code": "Verifizierungscode Erhalten Starten, Erneut Versuchen in 60s",
|
||||
"get_verification_code_timeout": "Verifizierungscode Erhalten Timeout",
|
||||
"get_verification_code_success": "Verifizierungscode Erfolgreich Erhalten",
|
||||
"try_get_verification_code": "Versuchen | {attempt} Verifizierungscode Erhalten | Verbleibende Zeit: {remaining_time}s",
|
||||
"verification_code_filled": "Verifizierungscode Ausgefüllt",
|
||||
"login_success_and_jump_to_settings_page": "Anmeldung Erfolgreich und zu Einstellungsseite Springen",
|
||||
"detect_login_page": "Anmeldeseite Erkennen, Anmeldung Starten...",
|
||||
"cursor_registration_completed": "Cursor-Registrierung Abgeschlossen!",
|
||||
"set_password": "Passwort Einstellen",
|
||||
"basic_info_submitted": "Grundlegende Informationen Eingereicht",
|
||||
"cursor_auth_info_updated": "Cursor-Authentifizierungsinformationen Aktualisiert",
|
||||
"cursor_auth_info_update_failed": "Cursor-Authentifizierungsinformationen Aktualisierung Fehlgeschlagen",
|
||||
"reset_machine_id": "Maschinen-ID Zurücksetzen",
|
||||
"account_info_saved": "Kontoinformationen Gespeichert",
|
||||
"save_account_info_failed": "Kontoinformationen Speichern Fehlgeschlagen",
|
||||
"get_email_address": "E-Mail-Adresse Erhalten",
|
||||
"update_cursor_auth_info": "Cursor-Authentifizierungsinformationen Aktualisieren",
|
||||
"register_process_error": "Registrierungsprozessfehler: {error}",
|
||||
"setting_password": "Passwort Einstellen",
|
||||
"manual_code_input": "Manuelle Code-Eingabe",
|
||||
"manual_email_input": "Manuelle E-Mail-Eingabe",
|
||||
"password": "Passwort",
|
||||
"first_name": "Vorname",
|
||||
"last_name": "Nachname",
|
||||
"exit_signal": "Exit-Signal",
|
||||
"email_address": "E-Mail-Adresse",
|
||||
"config_created": "Konfiguration Erstellt",
|
||||
"verification_failed": "Verifizierung Fehlgeschlagen",
|
||||
"verification_error": "Verifizierungsfehler: {error}",
|
||||
"config_option_added": "Konfigurationsoption Hinzugefügt: {option}",
|
||||
"config_updated": "Konfiguration Aktualisiert",
|
||||
"password_submitted": "Passwort Eingereicht",
|
||||
"total_usage": "Gesamtnutzung: {usage}",
|
||||
"setting_on_password": "Passwort Einstellen",
|
||||
"getting_code": "Verifizierungscode Erhalten, Erneut Versuchen in 60s"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Authentifizierungsmanager",
|
||||
"checking_auth": "Authentifizierungsdatei Überprüfen",
|
||||
"auth_not_found": "Authentifizierungsdatei Nicht Gefunden",
|
||||
"auth_file_error": "Authentifizierungsdateifehler: {error}",
|
||||
"reading_auth": "Authentifizierungsdatei Lesen",
|
||||
"updating_auth": "Authentifizierungsinformationen Aktualisieren",
|
||||
"auth_updated": "Authentifizierungsinformationen Erfolgreich Aktualisiert",
|
||||
"auth_update_failed": "Authentifizierungsinformationen Aktualisierung Fehlgeschlagen: {error}",
|
||||
"auth_file_created": "Authentifizierungsdatei Erstellt",
|
||||
"auth_file_create_failed": "Authentifizierungsdatei Erstellen Fehlgeschlagen: {error}",
|
||||
"press_enter": "Drücken Sie Enter zum Fortfahren",
|
||||
"reset_machine_id": "Maschinen-ID Zurücksetzen",
|
||||
"database_connection_closed": "Datenbankverbindung Geschlossen",
|
||||
"database_updated_successfully": "Datenbank Erfolgreich Aktualisiert",
|
||||
"connected_to_database": "Mit Datenbank Verbunden",
|
||||
"updating_pair": "Schlüssel-Wert-Paar Aktualisieren",
|
||||
"db_not_found": "Datenbankdatei Nicht Gefunden bei: {path}",
|
||||
"db_permission_error": "Kann Nicht auf Datenbankdatei Zugreifen. Bitte Berechtigungen Überprüfen",
|
||||
"db_connection_error": "Verbindung zur Datenbank Fehlgeschlagen: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Neue E-Mail Generieren",
|
||||
"blocked_domain": "Gesperrte Domain",
|
||||
"select_domain": "Zufällige Domain Auswählen",
|
||||
"copy_email": "E-Mail-Adresse Kopieren",
|
||||
"enter_mailbox": "Mailbox Betreten",
|
||||
"refresh_mailbox": "Mailbox Aktualisieren",
|
||||
"check_verification": "Verifizierungscode Überprüfen",
|
||||
"verification_found": "Verifizierungscode Gefunden",
|
||||
"verification_not_found": "Kein Verifizierungscode Gefunden",
|
||||
"browser_error": "Browsersteuerungsfehler: {error}",
|
||||
"navigation_error": "Navigationsfehler: {error}",
|
||||
"email_copy_error": "E-Mail-Kopierfehler: {error}",
|
||||
"mailbox_error": "Mailbox-Fehler: {error}",
|
||||
"token_saved_to_file": "Token Gespeichert in cursor_tokens.txt",
|
||||
"navigate_to": "Navigieren zu {url}",
|
||||
"generate_email_success": "E-Mail Erfolgreich Generiert",
|
||||
"select_email_domain": "E-Mail-Domain Auswählen",
|
||||
"select_email_domain_success": "E-Mail-Domain Erfolgreich Ausgewählt",
|
||||
"get_email_name": "E-Mail-Name Erhalten",
|
||||
"get_email_name_success": "E-Mail-Name Erfolgreich Erhalten",
|
||||
"get_email_address": "E-Mail-Adresse Erhalten",
|
||||
"get_email_address_success": "E-Mail-Adresse Erfolgreich Erhalten",
|
||||
"enter_mailbox_success": "Mailbox Erfolgreich Betreten",
|
||||
"found_verification_code": "Verifizierungscode Gefunden",
|
||||
"get_cursor_session_token": "Cursor-Sitzungstoken Erhalten",
|
||||
"get_cursor_session_token_success": "Cursor-Sitzungstoken Erfolgreich Erhalten",
|
||||
"get_cursor_session_token_failed": "Cursor-Sitzungstoken Erhalten Fehlgeschlagen",
|
||||
"save_token_failed": "Token Speichern Fehlgeschlagen",
|
||||
"database_updated_successfully": "Datenbank Erfolgreich Aktualisiert",
|
||||
"database_connection_closed": "Datenbankverbindung Geschlossen",
|
||||
"no_valid_verification_code": "Kein Gültiger Verifizierungscode"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Browser Starten",
|
||||
"visiting_site": "Besuche mail domains",
|
||||
"create_success": "E-Mail Erfolgreich Erstellt",
|
||||
"create_failed": "E-Mail Erstellen Fehlgeschlagen",
|
||||
"create_error": "E-Mail-Erstellungsfehler: {error}",
|
||||
"refreshing": "E-Mail Aktualisieren",
|
||||
"refresh_success": "E-Mail Erfolgreich Aktualisiert",
|
||||
"refresh_error": "E-Mail-Aktualisierungsfehler: {error}",
|
||||
"refresh_button_not_found": "Aktualisierungsknopf Nicht Gefunden",
|
||||
"verification_found": "Verifizierung Gefunden",
|
||||
"verification_not_found": "Verifizierung Nicht Gefunden",
|
||||
"verification_error": "Verifizierungsfehler: {error}",
|
||||
"verification_code_found": "Verifizierungscode Gefunden",
|
||||
"verification_code_not_found": "Verifizierungscode Nicht Gefunden",
|
||||
"verification_code_error": "Verifizierungscodefehler: {error}",
|
||||
"address": "E-Mail-Adresse",
|
||||
"all_domains_blocked": "Alle Domains Gesperrt, Service Wechseln",
|
||||
"no_available_domains_after_filtering": "Keine Verfügbaren Domains Nach Filterung",
|
||||
"switching_service": "Wechseln zu {service} Service",
|
||||
"domains_list_error": "Domains-Liste Erhalten Fehlgeschlagen: {error}",
|
||||
"failed_to_get_available_domains": "Verfügbare Domains Erhalten Fehlgeschlagen",
|
||||
"domains_excluded": "Ausgeschlossene Domains: {domains}",
|
||||
"failed_to_create_account": "Konto Erstellen Fehlgeschlagen",
|
||||
"account_creation_error": "Konto-Erstellungsfehler: {error}",
|
||||
"domain_blocked": "Domain Blocked: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Cursor Auto-Update Deaktivieren",
|
||||
"disable_success": "Auto-Update Deaktiviert Erfolgreich",
|
||||
"disable_failed": "Auto-Update Deaktivieren Fehlgeschlagen: {error}",
|
||||
"press_enter": "Drücken Sie Enter zum Fortfahren",
|
||||
"start_disable": "Auto-Update Deaktivieren Starten",
|
||||
"killing_processes": "Prozesse Töten",
|
||||
"processes_killed": "Prozesse Getötet",
|
||||
"removing_directory": "Verzeichnis Entfernen",
|
||||
"directory_removed": "Verzeichnis Entfernt",
|
||||
"creating_block_file": "Block-Datei Erstellen",
|
||||
"block_file_created": "Block-Datei Erstellt"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Updates prüfen...",
|
||||
"new_version_available": "Neue Version verfügbar! (Aktuell: {current}, Neueste: {latest})",
|
||||
"updating": "Zur neuesten Version aktualisieren. Das Programm wird automatisch neu starten.",
|
||||
"up_to_date": "Sie verwenden die neueste Version.",
|
||||
"check_failed": "Überprüfung auf Updates fehlgeschlagen: {error}",
|
||||
"continue_anyway": "Mit der aktuellen Version fortfahren...",
|
||||
"update_confirm": "Möchten Sie die neueste Version aktualisieren? (Y/n)",
|
||||
"update_skipped": "Update überspringen.",
|
||||
"invalid_choice": "Ungültige Auswahl. Bitte geben Sie 'Y' oder 'n' ein.",
|
||||
"development_version": "Entwickler-Version {current} > {latest}",
|
||||
"changelog_title": "Changelog"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Cursor Vollständig Zurücksetzen",
|
||||
"checking_config": "Konfigurationsdatei Überprüfen",
|
||||
"config_not_found": "Konfigurationsdatei Nicht Gefunden",
|
||||
"no_permission": "Kann Konfigurationsdatei Nicht Lesen oder Schreiben, Bitte Berechtigungen Überprüfen",
|
||||
"reading_config": "Aktuelle Konfiguration Lesen",
|
||||
"creating_backup": "Konfigurationsdatei Sichern",
|
||||
"backup_exists": "Backup-Datei bereits vorhanden, Sicherungsschritt überspringen",
|
||||
"generating_new_machine_id": "Neue Maschinen-ID Generieren",
|
||||
"saving_new_config": "Neue Konfiguration in JSON Speichern",
|
||||
"success": "Cursor Erfolgreich Zurückgesetzt",
|
||||
"error": "Cursor Zurücksetzen Fehlgeschlagen: {error}",
|
||||
"press_enter": "Drücken Sie Enter zum Beenden",
|
||||
"reset_machine_id": "Maschinen-ID Zurücksetzen",
|
||||
"database_connection_closed": "Datenbankverbindung Geschlossen",
|
||||
"database_updated_successfully": "Datenbank Erfolgreich Aktualisiert",
|
||||
"connected_to_database": "Mit Datenbank Verbunden",
|
||||
"updating_pair": "Schlüssel-Wert-Paar Aktualisieren",
|
||||
"db_not_found": "Datenbankdatei Nicht Gefunden bei: {path}",
|
||||
"db_permission_error": "Kann Nicht auf Datenbankdatei Zugreifen. Bitte Berechtigungen Überprüfen",
|
||||
"db_connection_error": "Verbindung zur Datenbank Fehlgeschlagen: {error}",
|
||||
"feature_title": "FEATURES",
|
||||
"feature_1": "Vollständige Entfernung von Cursor AI Einstellungen und Konfigurationen",
|
||||
"feature_2": "Entfernt alle zwischengespeicherten Daten, einschließlich AI-Verlauf und Prompts",
|
||||
"feature_3": "Maschinen-ID Zurücksetzen, um Trial-Erkennung zu umgehen",
|
||||
"feature_4": "Erstellt neue zufällige Maschinen-IDs",
|
||||
"feature_5": "Entfernt benutzerdefinierte Erweiterungen und Einstellungen",
|
||||
"feature_6": "Zurücksetzt Trial-Informationen und Aktivierungsdaten",
|
||||
"feature_7": "Tiefes Scannen für versteckte Lizenz- und Trial-bezogene Dateien",
|
||||
"feature_8": "Sichert nicht-Cursor-Dateien und Anwendungen",
|
||||
"feature_9": "Kompatibel mit Windows, macOS und Linux",
|
||||
"disclaimer_title": "Disclaimer",
|
||||
"disclaimer_1": "Dieses Tool wird alle Cursor AI Einstellungen,",
|
||||
"disclaimer_2": "Konfigurationen und zwischengespeicherte Daten löschen. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"disclaimer_3": "Ihre Code-Dateien werden NICHT beeinflusst, und das Tool ist so konzipiert,",
|
||||
"disclaimer_4": "nur Cursor AI Editor-Dateien und Trial-Erkennungsmechanismen zu zielen.",
|
||||
"disclaimer_5": "Andere Anwendungen auf Ihrem System werden NICHT beeinflusst.",
|
||||
"disclaimer_6": "Sie müssen Cursor AI erneut einrichten, nachdem Sie dieses Tool ausgeführt haben.",
|
||||
"disclaimer_7": "Verwenden Sie auf eigene Gefahr",
|
||||
"confirm_title": "Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"confirm_1": "Diese Aktion wird alle Cursor AI Einstellungen,",
|
||||
"confirm_2": "Konfigurationen und zwischengespeicherte Daten löschen. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"confirm_3": "Ihre Code-Dateien werden NICHT beeinflusst, und das Tool ist so konzipiert,",
|
||||
"confirm_4": "nur Cursor AI Editor-Dateien und Trial-Erkennungsmechanismen zu zielen.",
|
||||
"confirm_5": "Andere Anwendungen auf Ihrem System werden NICHT beeinflusst.",
|
||||
"confirm_6": "Sie müssen Cursor AI erneut einrichten, nachdem Sie dieses Tool ausgeführt haben.",
|
||||
"confirm_7": "Verwenden Sie auf eigene Gefahr",
|
||||
"invalid_choice": "Bitte geben Sie 'Y' oder 'n' ein",
|
||||
"skipped_for_safety": "Übersprungen für Sicherheit (nicht Cursor-bezogen): {path}",
|
||||
"deleted": "Gelöscht: {path}",
|
||||
"error_deleting": "Fehler beim Löschen von {path}: {error}",
|
||||
"not_found": "Datei nicht gefunden: {path}",
|
||||
"resetting_machine_id": "Maschinen-IDs zurücksetzen, um Trial-Erkennung zu umgehen...",
|
||||
"created_machine_id": "Neue Maschinen-ID erstellt: {path}",
|
||||
"error_creating_machine_id": "Fehler beim Erstellen der Maschinen-ID-Datei {path}: {error}",
|
||||
"error_searching": "Fehler beim Suchen nach Dateien in {path}: {error}",
|
||||
"created_extended_trial_info": "Neue erweiterte Trial-Informationen erstellt: {path}",
|
||||
"error_creating_trial_info": "Fehler beim Erstellen der Trial-Informationen-Datei {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Cursor AI Editor zurücksetzen... Bitte warten.",
|
||||
"reset_cancelled": "Reset abgebrochen. Ohne Änderungen verlassen.",
|
||||
"windows_machine_id_modification_skipped": "Windows Maschinen-ID-Änderung übersprungen: {error}",
|
||||
"linux_machine_id_modification_skipped": "Linux machine-id-Änderung übersprungen: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Hinweis: Vollständiges Zurücksetzen der Maschinen-ID kann erfordern, dass Sie als Administrator ausführen",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Hinweis: Vollständiges System-Maschinen-ID-Zurücksetzen kann sudo-Berechtigungen erfordern",
|
||||
"windows_registry_instructions": "📝 HINWEIS: Für vollständiges Zurücksetzen auf Windows müssen Sie möglicherweise auch die Registrierungseinträge bereinigen.",
|
||||
"windows_registry_instructions_2": " Führen Sie 'regedit' aus und suchen Sie nach Schlüsseln, die 'Cursor' oder 'CursorAI' enthalten, unter HKEY_CURRENT_USER\\Software\\ und löschen Sie sie.\n",
|
||||
"reset_log_1": "Cursor AI wurde vollständig zurückgesetzt und Trial-Erkennung umgangen!",
|
||||
"reset_log_2": "Bitte starten Sie Ihr System neu, um die Änderungen zu übernehmen.",
|
||||
"reset_log_3": "Sie müssen Cursor AI erneut installieren und sollten jetzt einen neuen Trial-Zeitraum haben.",
|
||||
"reset_log_4": "Für die besten Ergebnisse betrachten Sie auch:",
|
||||
"reset_log_5": "Verwenden Sie eine andere E-Mail-Adresse beim Registrieren für einen neuen Trial",
|
||||
"reset_log_6": "Wenn verfügbar, verwenden Sie einen VPN, um Ihre IP-Adresse zu ändern",
|
||||
"reset_log_7": "Löschen Sie Ihre Browser-Cookies und Cache vor dem Besuch der Cursor AI-Website",
|
||||
"reset_log_8": "Wenn Probleme bestehen, versuchen Sie, Cursor AI in einem anderen Speicherort zu installieren",
|
||||
"reset_log_9": "Wenn Sie irgendwelche Probleme haben, gehen Sie zu Github Issue Tracker und erstellen Sie ein Problem unter https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Ein unerwarteter Fehler ist aufgetreten: {error}",
|
||||
"report_issue": "Bitte melden Sie dieses Problem bei Github Issue Tracker unter https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Prozess von Benutzer unterbrochen. Beenden...",
|
||||
"return_to_main_menu": "Zurück zur Hauptseite...",
|
||||
"process_interrupted": "Prozess unterbrochen. Beenden...",
|
||||
"press_enter_to_return_to_main_menu": "Drücken Sie Enter, um zur Hauptseite zurückzukehren...",
|
||||
"removing_known": "Bekannte Trial/Lizenz-Dateien entfernen",
|
||||
"performing_deep_scan": "Tiefes Scannen nach zusätzlichen Trial/Lizenz-Dateien",
|
||||
"found_additional_potential_license_trial_files": "Gefundene {count} zusätzliche potentielle Lizenz/Trial-Dateien",
|
||||
"checking_for_electron_localstorage_files": "Überprüfen auf Electron localStorage-Dateien",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Keine zusätzlichen Lizenz/Trial-Dateien in tiefem Scan gefunden",
|
||||
"removing_electron_localstorage_files": "Electron localStorage-Dateien entfernen",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
585
locales/en.json
Normal file
@@ -0,0 +1,585 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Available Options",
|
||||
"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_manual": "Register Cursor with Custom Email",
|
||||
"quit": "Close Cursor Application",
|
||||
"select_language": "Change Language",
|
||||
"input_choice": "Please enter your choice ({choices})",
|
||||
"invalid_choice": "Invalid selection. Please enter a number from {choices}",
|
||||
"program_terminated": "Program was terminated by user",
|
||||
"error_occurred": "An error occurred: {error}. Please try again",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"disable_auto_update": "Disable Cursor Auto-Update",
|
||||
"lifetime_access_enabled": "LIFETIME ACCESS ENABLED",
|
||||
"totally_reset": "Totally Reset Cursor",
|
||||
"outdate": "Outdated",
|
||||
"temp_github_register": "Temporary GitHub Register",
|
||||
"admin_required": "Running as executable, administrator privileges required.",
|
||||
"admin_required_continue": "Continuing without administrator privileges.",
|
||||
"coming_soon": "Coming Soon",
|
||||
"fixed_soon": "Fixed Soon",
|
||||
"contribute": "Contribute to the Project",
|
||||
"config": "Show Config"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"bg": "Bulgarian",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start Quitting Cursor",
|
||||
"no_process": "No Running Cursor Process",
|
||||
"terminating": "Terminating Process {pid}",
|
||||
"waiting": "Waiting for Process to Exit",
|
||||
"success": "All Cursor Processes Closed",
|
||||
"timeout": "Process Timeout: {pids}",
|
||||
"error": "Error Occurred: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Cursor Machine ID Reset Tool",
|
||||
"checking": "Checking Config File",
|
||||
"not_found": "Config File Not Found",
|
||||
"no_permission": "Cannot Read or Write Config File, Please Check File Permissions",
|
||||
"reading": "Reading Current Config",
|
||||
"creating_backup": "Creating Config Backup",
|
||||
"backup_exists": "Backup File Already Exists, Skipping Backup Step",
|
||||
"generating": "Generating New Machine ID",
|
||||
"saving_json": "Saving New Config to JSON",
|
||||
"success": "Machine ID Reset Successfully",
|
||||
"new_id": "New Machine ID",
|
||||
"permission_error": "Permission Error: {error}",
|
||||
"run_as_admin": "Please Try Running This Program as Administrator",
|
||||
"process_error": "Reset Process Error: {error}",
|
||||
"updating_sqlite": "Updating SQLite Database",
|
||||
"updating_pair": "Updating Key-Value Pair",
|
||||
"sqlite_success": "SQLite Database Updated Successfully",
|
||||
"sqlite_error": "SQLite Database Update Failed: {error}",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"unsupported_os": "Unsupported OS: {os}",
|
||||
"linux_path_not_found": "Linux Path Not Found",
|
||||
"updating_system_ids": "Updating System IDs",
|
||||
"system_ids_updated": "System IDs Updated Successfully",
|
||||
"system_ids_update_failed": "System IDs Update Failed: {error}",
|
||||
"windows_guid_updated": "Windows GUID Updated Successfully",
|
||||
"windows_permission_denied": "Windows Permission Denied",
|
||||
"windows_guid_update_failed": "Windows GUID Update Failed",
|
||||
"macos_uuid_updated": "macOS UUID Updated Successfully",
|
||||
"plutil_command_failed": "plutil Command Failed",
|
||||
"start_patching": "Starting Patching getMachineId",
|
||||
"macos_uuid_update_failed": "macOS UUID Update Failed",
|
||||
"current_version": "Current Cursor Version: {version}",
|
||||
"patch_completed": "Patching getMachineId Completed",
|
||||
"patch_failed": "Patching getMachineId Failed: {error}",
|
||||
"version_check_passed": "Cursor Version Check Passed",
|
||||
"file_modified": "File Modified",
|
||||
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
|
||||
"detecting_version": "Detecting Cursor Version",
|
||||
"patching_getmachineid": "Patching getMachineId",
|
||||
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId",
|
||||
"permission_denied": "Permission Denied: {error}",
|
||||
"backup_created": "Backup Created",
|
||||
"update_success": "Update Success",
|
||||
"update_failed": "Update Failed: {error}",
|
||||
"windows_machine_guid_updated": "Windows Machine GUID Updated Successfully",
|
||||
"reading_package_json": "Reading package.json {path}",
|
||||
"invalid_json_object": "Invalid JSON Object",
|
||||
"no_version_field": "No Version Field Found in package.json",
|
||||
"version_field_empty": "Version Field is Empty",
|
||||
"invalid_version_format": "Invalid Version Format: {version}",
|
||||
"found_version": "Found Version: {version}",
|
||||
"version_parse_error": "Version Parse Error: {error}",
|
||||
"package_not_found": "Package.json Not Found: {path}",
|
||||
"check_version_failed": "Check Version Failed: {error}",
|
||||
"stack_trace": "Stack Trace",
|
||||
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0",
|
||||
"no_write_permission": "No Write Permission: {path}",
|
||||
"path_not_found": "Path Not Found: {path}",
|
||||
"modify_file_failed": "Modify File Failed: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine ID Updated Successfully",
|
||||
"update_windows_machine_id_failed": "Update Windows Machine ID Failed: {error}",
|
||||
"update_windows_machine_guid_failed": "Update Windows Machine GUID Failed: {error}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registration Tool",
|
||||
"start": "Starting registration process...",
|
||||
"handling_turnstile": "Processing security verification...",
|
||||
"retry_verification": "Retrying verification...",
|
||||
"detect_turnstile": "Checking security verification...",
|
||||
"verification_success": "Security verification successful",
|
||||
"starting_browser": "Opening browser...",
|
||||
"form_success": "Form submitted successfully",
|
||||
"browser_started": "Browser opened successfully",
|
||||
"waiting_for_second_verification": "Waiting for email verification...",
|
||||
"waiting_for_verification_code": "Waiting for verification code...",
|
||||
"password_success": "Password set successfully",
|
||||
"password_error": "Could not set password: {error}. Please try again",
|
||||
"waiting_for_page_load": "Loading page...",
|
||||
"first_verification_passed": "Initial verification successful",
|
||||
"mailbox": "Successfully accessed email inbox",
|
||||
"register_start": "Start Register",
|
||||
"form_submitted": "Form Submitted, Start Verification...",
|
||||
"filling_form": "Fill Form",
|
||||
"visiting_url": "Visiting URL",
|
||||
"basic_info": "Basic Info Submitted",
|
||||
"handle_turnstile": "Handle Turnstile",
|
||||
"no_turnstile": "Not Detect Turnstile",
|
||||
"turnstile_passed": "Turnstile Passed",
|
||||
"verification_start": "Start Getting Verification Code",
|
||||
"verification_timeout": "Get Verification Code Timeout",
|
||||
"verification_not_found": "No Verification Code Found",
|
||||
"try_get_code": "Try | {attempt} Get Verification Code | Time Remaining: {time}s",
|
||||
"get_account": "Getting Account Info",
|
||||
"get_token": "Get Cursor Session Token",
|
||||
"token_success": "Get Token Success",
|
||||
"token_attempt": "Try | {attempt} times to get Token | Will retry in {time}s",
|
||||
"token_max_attempts": "Reach Max Attempts ({max}) | Failed to get Token",
|
||||
"token_failed": "Get Token Failed: {error}",
|
||||
"account_error": "Get Account Info Failed: {error}",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"browser_start": "Starting Browser",
|
||||
"open_mailbox": "Opening Mailbox Page",
|
||||
"email_error": "Failed to Get Email Address",
|
||||
"setup_error": "Email Setup Error: {error}",
|
||||
"start_getting_verification_code": "Start Getting Verification Code, Will Try in 60s",
|
||||
"get_verification_code_timeout": "Get Verification Code Timeout",
|
||||
"get_verification_code_success": "Get Verification Code Success",
|
||||
"try_get_verification_code": "Try | {attempt} Get Verification Code | Time Remaining: {remaining_time}s",
|
||||
"verification_code_filled": "Verification Code Filled",
|
||||
"login_success_and_jump_to_settings_page": "Login Success and Jump to Settings Page",
|
||||
"detect_login_page": "Detect Login Page, Start Login...",
|
||||
"cursor_registration_completed": "Cursor Registration Completed!",
|
||||
"set_password": "Set Password",
|
||||
"basic_info_submitted": "Basic Info Submitted",
|
||||
"cursor_auth_info_updated": "Cursor Auth Info Updated",
|
||||
"cursor_auth_info_update_failed": "Cursor Auth Info Update Failed",
|
||||
"reset_machine_id": "Reset Machine ID",
|
||||
"account_info_saved": "Account Info Saved",
|
||||
"save_account_info_failed": "Save Account Info Failed",
|
||||
"get_email_address": "Get Email Address",
|
||||
"update_cursor_auth_info": "Update Cursor Auth Info",
|
||||
"register_process_error": "Register Process Error: {error}",
|
||||
"setting_password": "Setting Password",
|
||||
"manual_code_input": "Manual Code Input",
|
||||
"manual_email_input": "Manual Email Input",
|
||||
"password": "Password",
|
||||
"first_name": "First Name",
|
||||
"last_name": "Last Name",
|
||||
"exit_signal": "Exit Signal",
|
||||
"email_address": "Email Address",
|
||||
"config_created": "Config Created",
|
||||
"verification_failed": "Verification Failed",
|
||||
"verification_error": "Verification Error: {error}",
|
||||
"config_option_added": "Config Option Added: {option}",
|
||||
"config_updated": "Config Updated",
|
||||
"password_submitted": "Password Submitted",
|
||||
"total_usage": "Total Usage: {usage}",
|
||||
"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."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Auth Manager",
|
||||
"checking_auth": "Checking Auth File",
|
||||
"auth_not_found": "Auth File Not Found",
|
||||
"auth_file_error": "Auth File Error: {error}",
|
||||
"reading_auth": "Reading Auth File",
|
||||
"updating_auth": "Updating Auth Info",
|
||||
"auth_updated": "Auth Info Updated Successfully",
|
||||
"auth_update_failed": "Auth Info Update Failed: {error}",
|
||||
"auth_file_created": "Auth File Created",
|
||||
"auth_file_create_failed": "Auth File Create Failed: {error}",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"reset_machine_id": "Reset Machine ID",
|
||||
"database_connection_closed": "Database Connection Closed",
|
||||
"database_updated_successfully": "Database Updated Successfully",
|
||||
"connected_to_database": "Connected to Database",
|
||||
"updating_pair": "Updating Key-Value Pair",
|
||||
"db_not_found": "Database file not found at: {path}",
|
||||
"db_permission_error": "Cannot access database file. Please check permissions",
|
||||
"db_connection_error": "Failed to connect to database: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Generating New Email",
|
||||
"blocked_domain": "Blocked Domain",
|
||||
"select_domain": "Selecting Random Domain",
|
||||
"copy_email": "Copying Email Address",
|
||||
"enter_mailbox": "Entering Mailbox",
|
||||
"refresh_mailbox": "Refreshing Mailbox",
|
||||
"check_verification": "Checking Verification Code",
|
||||
"verification_found": "Verification Code Found",
|
||||
"verification_not_found": "No Verification Code Found",
|
||||
"browser_error": "Browser Control Error: {error}",
|
||||
"navigation_error": "Navigation Error: {error}",
|
||||
"email_copy_error": "Email Copy Error: {error}",
|
||||
"mailbox_error": "Mailbox Error: {error}",
|
||||
"token_saved_to_file": "Token Saved to cursor_tokens.txt",
|
||||
"navigate_to": "Navigating to {url}",
|
||||
"generate_email_success": "Generate Email Success",
|
||||
"select_email_domain": "Select Email Domain",
|
||||
"select_email_domain_success": "Select Email Domain Success",
|
||||
"get_email_name": "Get Email Name",
|
||||
"get_email_name_success": "Get Email Name Success",
|
||||
"get_email_address": "Get Email Address",
|
||||
"get_email_address_success": "Get Email Address Success",
|
||||
"enter_mailbox_success": "Enter Mailbox Success",
|
||||
"found_verification_code": "Found Verification Code",
|
||||
"get_cursor_session_token": "Get Cursor Session Token",
|
||||
"get_cursor_session_token_success": "Get Cursor Session Token Success",
|
||||
"get_cursor_session_token_failed": "Get Cursor Session Token Failed",
|
||||
"save_token_failed": "Save Token Failed",
|
||||
"database_updated_successfully": "Database Updated Successfully",
|
||||
"database_connection_closed": "Database Connection Closed",
|
||||
"no_valid_verification_code": "No Valid Verification Code"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Starting Browser",
|
||||
"visiting_site": "Visiting mail domains",
|
||||
"create_success": "Email Created Successfully",
|
||||
"create_failed": "Failed to Create Email",
|
||||
"create_error": "Email Creation Error: {error}",
|
||||
"refreshing": "Refreshing Email",
|
||||
"refresh_success": "Email Refreshed Successfully",
|
||||
"refresh_error": "Email Refresh Error: {error}",
|
||||
"refresh_button_not_found": "Refresh Button Not Found",
|
||||
"verification_found": "Verification Found",
|
||||
"verification_not_found": "Verification Not Found",
|
||||
"verification_error": "Verification Error: {error}",
|
||||
"verification_code_found": "Verification Code Found",
|
||||
"verification_code_not_found": "Verification Code Not Found",
|
||||
"verification_code_error": "Verification Code Error: {error}",
|
||||
"address": "Email Address",
|
||||
"all_domains_blocked": "All Domains Blocked, Switching Service",
|
||||
"no_available_domains_after_filtering": "No Available Domains After Filtering",
|
||||
"switching_service": "Switching to {service} Service",
|
||||
"domains_list_error": "Failed to Get Domains List: {error}",
|
||||
"failed_to_get_available_domains": "Failed to Get Available Domains",
|
||||
"domains_excluded": "Domains Excluded: {domains}",
|
||||
"failed_to_create_account": "Failed to Create Account",
|
||||
"account_creation_error": "Account Creation Error: {error}",
|
||||
"blocked_domains": "Blocked Domains: {domains}",
|
||||
"blocked_domains_loaded": "Blocked Domains Loaded: {count}",
|
||||
"blocked_domains_loaded_error": "Blocked Domains Loaded Error: {error}",
|
||||
"blocked_domains_loaded_success": "Blocked Domains Loaded Successfully",
|
||||
"blocked_domains_loaded_timeout": "Blocked Domains Loaded Timeout: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Blocked Domains Loaded Timeout Error: {error}",
|
||||
"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}",
|
||||
"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",
|
||||
"disable_success": "Auto Update Disabled Successfully",
|
||||
"disable_failed": "Disable Auto Update Failed: {error}",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"start_disable": "Start Disabling Auto Update",
|
||||
"killing_processes": "Killing Processes",
|
||||
"processes_killed": "Processes Killed",
|
||||
"removing_directory": "Removing Directory",
|
||||
"directory_removed": "Directory Removed",
|
||||
"creating_block_file": "Creating Block File",
|
||||
"block_file_created": "Block File Created",
|
||||
"clearing_update_yml": "Clearing update.yml file",
|
||||
"update_yml_cleared": "update.yml file cleared",
|
||||
"update_yml_not_found": "update.yml file not found",
|
||||
"clear_update_yml_failed": "Failed to clear update.yml file: {error}",
|
||||
"unsupported_os": "Unsupported OS: {system}",
|
||||
"remove_directory_failed": "Failed to remove directory: {error}",
|
||||
"create_block_file_failed": "Failed to create block file: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Checking for updates...",
|
||||
"new_version_available": "New version available! (Current: {current}, Latest: {latest})",
|
||||
"updating": "Updating to the latest version. The program will restart automatically.",
|
||||
"up_to_date": "You are using the latest version.",
|
||||
"check_failed": "Failed to check for updates: {error}",
|
||||
"continue_anyway": "Continuing with current version...",
|
||||
"update_confirm": "Do you want to update to the latest version? (Y/n)",
|
||||
"update_skipped": "Skipping update.",
|
||||
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
|
||||
"development_version": "Development Version {current} > {latest}",
|
||||
"changelog_title": "Changelog",
|
||||
"rate_limit_exceeded": "GitHub API rate limit exceeded. Skipping update check."
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Totally Reset Cursor",
|
||||
"checking_config": "Checking Config File",
|
||||
"config_not_found": "Config File Not Found",
|
||||
"no_permission": "Cannot Read or Write Config File, Please Check File Permissions",
|
||||
"reading_config": "Reading Current Config",
|
||||
"creating_backup": "Creating Config Backup",
|
||||
"backup_exists": "Backup File Already Exists, Skipping Backup Step",
|
||||
"generating_new_machine_id": "Generating New Machine ID",
|
||||
"saving_new_config": "Saving New Config to JSON",
|
||||
"success": "Cursor Reset Successfully",
|
||||
"error": "Cursor Reset Failed: {error}",
|
||||
"press_enter": "Press Enter to Exit",
|
||||
"reset_machine_id": "Reset Machine ID",
|
||||
"database_connection_closed": "Database Connection Closed",
|
||||
"database_updated_successfully": "Database Updated Successfully",
|
||||
"connected_to_database": "Connected to Database",
|
||||
"updating_pair": "Updating Key-Value Pair",
|
||||
"db_not_found": "Database file not found at: {path}",
|
||||
"db_permission_error": "Cannot access database file. Please check permissions",
|
||||
"db_connection_error": "Failed to connect to database: {error}",
|
||||
"feature_title": "FEATURES",
|
||||
"feature_1": "Complete removal of Cursor AI settings and configurations",
|
||||
"feature_2": "Clears all cached data including AI history and prompts",
|
||||
"feature_3": "Resets machine ID to bypass trial detection",
|
||||
"feature_4": "Creates new randomized machine identifiers",
|
||||
"feature_5": "Removes custom extensions and preferences",
|
||||
"feature_6": "Resets trial information and activation data",
|
||||
"feature_7": "Deep scan for hidden license and trial-related files",
|
||||
"feature_8": "Safely preserves non-Cursor files and applications",
|
||||
"feature_9": "Compatible with Windows, macOS, and Linux",
|
||||
"disclaimer_title": "DISCLAIMER",
|
||||
"disclaimer_1": "This tool will permanently delete all Cursor AI settings,",
|
||||
"disclaimer_2": "configurations, and cached data. This action cannot be undone.",
|
||||
"disclaimer_3": "Your code files will NOT be affected, and the tool is designed",
|
||||
"disclaimer_4": "to only target Cursor AI editor files and trial detection mechanisms.",
|
||||
"disclaimer_5": "Other applications on your system will not be affected.",
|
||||
"disclaimer_6": "You will need to set up Cursor AI again after running this tool.",
|
||||
"disclaimer_7": "Use at your own risk",
|
||||
"confirm_title": "Are you sure you want to proceed?",
|
||||
"confirm_1": "This action will delete all Cursor AI settings,",
|
||||
"confirm_2": "configurations, and cached data. This action cannot be undone.",
|
||||
"confirm_3": "Your code files will NOT be affected, and the tool is designed",
|
||||
"confirm_4": "to only target Cursor AI editor files and trial detection mechanisms.",
|
||||
"confirm_5": "Other applications on your system will not be affected.",
|
||||
"confirm_6": "You will need to set up Cursor AI again after running this tool.",
|
||||
"confirm_7": "Use at your own risk",
|
||||
"invalid_choice": "Please enter 'Y' or 'n'",
|
||||
"skipped_for_safety": "Skipped for safety (not Cursor-related): {path}",
|
||||
"deleted": "Deleted: {path}",
|
||||
"error_deleting": "Error deleting {path}: {error}",
|
||||
"not_found": "File not found: {path}",
|
||||
"resetting_machine_id": "Resetting machine identifiers to bypass trial detection...",
|
||||
"created_machine_id": "Created new machine ID: {path}",
|
||||
"error_creating_machine_id": "Error creating machine ID file {path}: {error}",
|
||||
"error_searching": "Error searching for files in {path}: {error}",
|
||||
"created_extended_trial_info": "Created new extended trial info: {path}",
|
||||
"error_creating_trial_info": "Error creating trial info file {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Resetting Cursor AI Editor... Please wait.",
|
||||
"reset_cancelled": "Reset cancelled. Exiting without making any changes.",
|
||||
"windows_machine_id_modification_skipped": "Windows machine ID modification skipped: {error}",
|
||||
"linux_machine_id_modification_skipped": "Linux machine-id modification skipped: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Note: Complete machine ID reset may require running as administrator",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Note: Complete system machine-id reset may require sudo privileges",
|
||||
"windows_registry_instructions": "📝 NOTE: For complete reset on Windows, you might also need to clean registry entries.",
|
||||
"windows_registry_instructions_2": " Run 'regedit' and search for keys containing 'Cursor' or 'CursorAI' under HKEY_CURRENT_USER\\Software\\ and delete them.\n",
|
||||
"reset_log_1": "Cursor AI has been fully reset and trial detection bypassed!",
|
||||
"reset_log_2": "Please restart your system for changes to take effect.",
|
||||
"reset_log_3": "You will need to reinstall Cursor AI and should now have a fresh trial period.",
|
||||
"reset_log_4": "For best results, consider also:",
|
||||
"reset_log_5": "Use a different email address when registering for a new trial",
|
||||
"reset_log_6": "If available, use a VPN to change your IP address",
|
||||
"reset_log_7": "Clear your browser cookies and cache before visiting Cursor AI's website",
|
||||
"reset_log_8": "If issues persist, try installing Cursor AI in a different location",
|
||||
"reset_log_9": "If you encounter any issues, go to Github Issue Tracker and create an issue at https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "An unexpected error occurred: {error}",
|
||||
"report_issue": "Please report this issue to Github Issue Tracker at https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Process interrupted by user. Exiting...",
|
||||
"return_to_main_menu": "Returning to main menu...",
|
||||
"process_interrupted": "Process interrupted. Exiting...",
|
||||
"press_enter_to_return_to_main_menu": "Press Enter to return to main menu...",
|
||||
"removing_known": "Removing known trial/license files",
|
||||
"performing_deep_scan": "Performing deep scan for additional trial/license files",
|
||||
"found_additional_potential_license_trial_files": "Found {count} additional potential license/trial files",
|
||||
"checking_for_electron_localstorage_files": "Checking for Electron localStorage files",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "No additional license/trial files found in deep scan",
|
||||
"removing_electron_localstorage_files": "Removing Electron localStorage files",
|
||||
"electron_localstorage_files_removed": "Electron localStorage files removed",
|
||||
"electron_localstorage_files_removal_error": "Error removing Electron localStorage files: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage files removal completed",
|
||||
"warning_title": "WARNING",
|
||||
"warning_1": "This action will delete all Cursor AI settings,",
|
||||
"warning_2": "configurations, and cached data. This action cannot be undone.",
|
||||
"warning_3": "Your code files will NOT be affected, and the tool is designed",
|
||||
"warning_4": "to only target Cursor AI editor files and trial detection mechanisms.",
|
||||
"warning_5": "Other applications on your system will not be affected.",
|
||||
"warning_6": "You will need to set up Cursor AI again after running this tool.",
|
||||
"warning_7": "Use at your own risk",
|
||||
"removed": "Removed: {path}",
|
||||
"failed_to_reset_machine_guid": "Failed to reset machine GUID",
|
||||
"failed_to_remove": "Failed to remove: {path}",
|
||||
"failed_to_delete_file": "Failed to delete file: {path}",
|
||||
"failed_to_delete_directory": "Failed to delete directory: {path}",
|
||||
"failed_to_delete_file_or_directory": "Failed to delete file or directory: {path}",
|
||||
"deep_scanning": "Performing deep scan for additional trial/license files",
|
||||
"resetting_cursor": "Resetting Cursor AI Editor... Please wait.",
|
||||
"completed_in": "Completed in {time} seconds",
|
||||
"cursor_reset_completed": "Cursor AI Editor has been fully reset and trial detection bypassed!",
|
||||
"cursor_reset_failed": "Cursor AI Editor reset failed: {error}",
|
||||
"cursor_reset_cancelled": "Cursor AI Editor reset cancelled. Exiting without making any changes.",
|
||||
"operation_cancelled": "Operation cancelled. Exiting without making any changes."
|
||||
},
|
||||
"github_register": {
|
||||
"title": "GitHub + Cursor AI Registration Automation",
|
||||
"features_header": "Features",
|
||||
"feature1": "Generates a temporary email using 1secmail.",
|
||||
"feature2": "Registers a new GitHub account with random credentials.",
|
||||
"feature3": "Verifies the GitHub email automatically.",
|
||||
"feature4": "Logs into Cursor AI using GitHub authentication.",
|
||||
"feature5": "Resets the machine ID to bypass trial detection.",
|
||||
"feature6": "Saves all credentials to a file.",
|
||||
"warnings_header": "Warnings",
|
||||
"warning1": "This script automates account creation, which may violate GitHub/Cursor terms of service.",
|
||||
"warning2": "Requires internet access and administrative privileges.",
|
||||
"warning3": "CAPTCHA or additional verification may interrupt automation.",
|
||||
"warning4": "Use responsibly and at your own risk.",
|
||||
"confirm": "Are you sure you want to proceed?",
|
||||
"invalid_choice": "Invalid choice. Please enter 'yes' or 'no'",
|
||||
"cancelled": "Operation cancelled",
|
||||
"program_terminated": "Program terminated by user",
|
||||
"starting_automation": "Starting automation...",
|
||||
"github_username": "GitHub Username",
|
||||
"github_password": "GitHub Password",
|
||||
"email_address": "Email Address",
|
||||
"credentials_saved": "These credentials have been saved to github_cursor_accounts.txt",
|
||||
"completed_successfully": "GitHub + Cursor registration completed successfully!",
|
||||
"registration_encountered_issues": "GitHub + Cursor registration encountered issues.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Check browser windows for manual intervention or try again later."
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "Subscription",
|
||||
"trial_remaining": "Remaining Pro Trial",
|
||||
"days": "days",
|
||||
"subscription_not_found": "Subscription information not found",
|
||||
"email_not_found": "Email not found",
|
||||
"failed_to_get_account": "Failed to get account information",
|
||||
"config_not_found": "Configuration not found.",
|
||||
"failed_to_get_usage": "Failed to get usage information",
|
||||
"failed_to_get_subscription": "Failed to get subscription information",
|
||||
"failed_to_get_email": "Failed to get email address",
|
||||
"failed_to_get_token": "Failed to get token",
|
||||
"failed_to_get_account_info": "Failed to get account information",
|
||||
"title": "Account Information",
|
||||
"email": "Email",
|
||||
"token": "Token",
|
||||
"usage": "Usage",
|
||||
"subscription_type": "Subscription Type",
|
||||
"remaining_trial": "Remaining Trial",
|
||||
"days_remaining": "Days Remaining",
|
||||
"premium": "Premium",
|
||||
"pro": "Pro",
|
||||
"pro_trial": "Pro Trial",
|
||||
"team": "Team",
|
||||
"enterprise": "Enterprise",
|
||||
"free": "Free",
|
||||
"active": "Active",
|
||||
"inactive": "Inactive",
|
||||
"premium_usage": "Premium Usage",
|
||||
"basic_usage": "Basic Usage",
|
||||
"usage_not_found": "Usage not found",
|
||||
"lifetime_access_enabled": "Lifetime Access Enabled"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "Configuration not available",
|
||||
"configuration": "Configuration",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"config_directory": "Config Directory",
|
||||
"neither_cursor_nor_cursor_directory_found": "Neither Cursor nor Cursor directory found in {config_base}",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Please make sure Cursor is installed and has been run at least once",
|
||||
"storage_directory_not_found": "Storage directory not found: {storage_dir}",
|
||||
"storage_file_found": "Storage file found: {storage_path}",
|
||||
"file_size": "File size: {size} bytes",
|
||||
"file_permissions": "File permissions: {permissions}",
|
||||
"file_owner": "File owner: {owner}",
|
||||
"file_group": "File group: {group}",
|
||||
"error_getting_file_stats": "Error getting file stats: {error}",
|
||||
"permission_denied": "Permission denied: {storage_path}",
|
||||
"try_running": "Try running: {command}",
|
||||
"and": "And",
|
||||
"storage_file_is_empty": "Storage file is empty: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "The file might be corrupted, please reinstall Cursor",
|
||||
"storage_file_not_found": "Storage file not found: {storage_path}",
|
||||
"error_checking_linux_paths": "Error checking Linux paths: {error}",
|
||||
"config_option_added": "Config option added: {option}",
|
||||
"config_updated": "Config updated",
|
||||
"config_created": "Config created: {config_file}",
|
||||
"config_setup_error": "Error setting up config: {error}",
|
||||
"storage_file_is_valid_and_contains_data": "Storage file is valid and contains data",
|
||||
"error_reading_storage_file": "Error reading storage file: {error}",
|
||||
"also_checked": "Also checked {path}"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Authentication button not found",
|
||||
"authentication_failed": "Authentication failed: {error}",
|
||||
"found_cookies": "Found {count} cookies",
|
||||
"token_extraction_error": "Token extraction error: {error}",
|
||||
"authentication_successful": "Authentication successful - Email: {email}",
|
||||
"missing_authentication_data": "Missing authentication data: {data}",
|
||||
"failed_to_delete_account": "Failed to delete account: {error}",
|
||||
"invalid_authentication_type": "Invalid authentication type",
|
||||
"auth_update_success": "Auth update success",
|
||||
"browser_closed": "Browser closed",
|
||||
"auth_update_failed": "Auth update failed",
|
||||
"google_start": "Google start",
|
||||
"github_start": "Github start",
|
||||
"usage_count": "Usage count: {usage}",
|
||||
"account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
|
||||
"starting_new_authentication_process": "Starting new authentication process...",
|
||||
"failed_to_delete_expired_account": "Failed to delete expired account",
|
||||
"could_not_check_usage_count": "Could not check usage count: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"could_not_find_email": "Could not find email: {error}",
|
||||
"could_not_find_usage_count": "Could not find usage count: {error}",
|
||||
"already_on_settings_page": "Already on settings page!",
|
||||
"failed_to_extract_auth_info": "Failed to extract auth info: {error}",
|
||||
"no_chrome_profiles_found": "No Chrome profiles found, using Default",
|
||||
"found_default_chrome_profile": "Found Default Chrome profile",
|
||||
"using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
|
||||
"error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
|
||||
"initializing_browser_setup": "Initializing browser setup...",
|
||||
"detected_platform": "Detected platform: {platform}",
|
||||
"running_as_root_warning": "Running as root is not recommended for browser automation",
|
||||
"consider_running_without_sudo": "Consider running the script without sudo",
|
||||
"no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
|
||||
"supported_browsers": "Supported browsers for {platform}",
|
||||
"using_browser_profile": "Using browser profile: {profile}",
|
||||
"starting_browser": "Starting browser at: {path}",
|
||||
"browser_setup_completed": "Browser setup completed successfully",
|
||||
"browser_setup_failed": "Browser setup failed: {error}",
|
||||
"try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
|
||||
"redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Starting Google authentication...",
|
||||
"starting_github_authentication": "Starting GitHub authentication...",
|
||||
"waiting_for_authentication": "Waiting for authentication...",
|
||||
"page_changed_checking_auth": "Page changed, checking auth...",
|
||||
"status_check_error": "Status check error: {error}",
|
||||
"authentication_timeout": "Authentication timeout",
|
||||
"account_is_still_valid": "Account is still valid (Usage: {usage})",
|
||||
"starting_re_authentication_process": "Starting re-authentication process...",
|
||||
"starting_new_google_authentication": "Starting new Google authentication...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
|
||||
"navigating_to_authentication_page": "Navigating to authentication page...",
|
||||
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
|
||||
"found_browser_data_directory": "Found browser data directory: {path}",
|
||||
"authentication_successful_getting_account_info": "Authentication successful, getting account info...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}"
|
||||
}
|
||||
}
|
||||
443
locales/es.json
Normal file
@@ -0,0 +1,443 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Opciones Disponibles",
|
||||
"exit": "Salir del Programa",
|
||||
"reset": "Restablecer ID de Máquina",
|
||||
"register": "Registrar Nueva Cuenta de Cursor",
|
||||
"register_google": "Registrarse con Cuenta de Google",
|
||||
"register_github": "Registrarse con Cuenta de GitHub",
|
||||
"register_manual": "Registrar Cursor con Correo Personalizado",
|
||||
"quit": "Cerrar Aplicación Cursor",
|
||||
"select_language": "Cambiar Idioma",
|
||||
"input_choice": "Por favor, ingrese su elección ({choices})",
|
||||
"invalid_choice": "Selección inválida. Por favor ingrese un número de {choices}",
|
||||
"program_terminated": "El programa fue terminado por el usuario",
|
||||
"error_occurred": "Ocurrió un error: {error}. Por favor intente de nuevo",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"disable_auto_update": "Deshabilitar Actualización Automática de Cursor",
|
||||
"lifetime_access_enabled": "ACCESO DE POR VIDA ACTIVADO",
|
||||
"totally_reset": "Restablecer Cursor Completamente",
|
||||
"outdate": "Desactualizado",
|
||||
"temp_github_register": "Registro Temporal de GitHub",
|
||||
"admin_required": "Ejecutando como ejecutable, se requieren privilegios de administrador.",
|
||||
"admin_required_continue": "Continuando sin privilegios de administrador.",
|
||||
"coming_soon": "Próximamente",
|
||||
"fixed_soon": "Arreglado Pronto"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Inglés",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamita",
|
||||
"nl": "Holandés",
|
||||
"de": "Alemán",
|
||||
"fr": "Francés",
|
||||
"pt": "Portugués",
|
||||
"ru": "Ruso",
|
||||
"tr": "Turco",
|
||||
"bg": "Búlgaro",
|
||||
"es": "Español"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Comenzando a Cerrar Cursor",
|
||||
"no_process": "No Hay Procesos de Cursor en Ejecución",
|
||||
"terminating": "Terminando Proceso {pid}",
|
||||
"waiting": "Esperando que el Proceso Termine",
|
||||
"success": "Todos los Procesos de Cursor Cerrados",
|
||||
"timeout": "Tiempo de Espera Agotado: {pids}",
|
||||
"error": "Ocurrió un Error: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Herramienta de Restablecimiento de ID de Máquina de Cursor",
|
||||
"checking": "Verificando Archivo de Configuración",
|
||||
"not_found": "Archivo de Configuración No Encontrado",
|
||||
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
|
||||
"reading": "Leyendo Configuración Actual",
|
||||
"creating_backup": "Creando Copia de Seguridad de la Configuración",
|
||||
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
|
||||
"generating": "Generando Nuevo ID de Máquina",
|
||||
"saving_json": "Guardando Nueva Configuración en JSON",
|
||||
"success": "ID de Máquina Restablecido Exitosamente",
|
||||
"new_id": "Nuevo ID de Máquina",
|
||||
"permission_error": "Error de Permisos: {error}",
|
||||
"run_as_admin": "Por Favor Intente Ejecutar Este Programa como Administrador",
|
||||
"process_error": "Error en el Proceso de Restablecimiento: {error}",
|
||||
"updating_sqlite": "Actualizando Base de Datos SQLite",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"sqlite_success": "Base de Datos SQLite Actualizada Exitosamente",
|
||||
"sqlite_error": "Falló la Actualización de la Base de Datos SQLite: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"unsupported_os": "Sistema Operativo No Soportado: {os}",
|
||||
"linux_path_not_found": "Ruta de Linux No Encontrada",
|
||||
"updating_system_ids": "Actualizando IDs del Sistema",
|
||||
"system_ids_updated": "IDs del Sistema Actualizados Exitosamente",
|
||||
"system_ids_update_failed": "Falló la Actualización de IDs del Sistema: {error}",
|
||||
"windows_guid_updated": "GUID de Windows Actualizado Exitosamente",
|
||||
"windows_permission_denied": "Permisos de Windows Denegados",
|
||||
"windows_guid_update_failed": "Falló la Actualización del GUID de Windows",
|
||||
"macos_uuid_updated": "UUID de macOS Actualizado Exitosamente",
|
||||
"plutil_command_failed": "Falló el Comando plutil",
|
||||
"start_patching": "Iniciando Parcheo de getMachineId",
|
||||
"macos_uuid_update_failed": "Falló la Actualización del UUID de macOS",
|
||||
"current_version": "Versión Actual de Cursor: {version}",
|
||||
"patch_completed": "Parcheo de getMachineId Completado",
|
||||
"patch_failed": "Falló el Parcheo de getMachineId: {error}",
|
||||
"version_check_passed": "Verificación de Versión de Cursor Exitosa",
|
||||
"file_modified": "Archivo Modificado",
|
||||
"version_less_than_0_45": "Versión de Cursor < 0.45.0, Omitiendo Parcheo de getMachineId",
|
||||
"detecting_version": "Detectando Versión de Cursor",
|
||||
"patching_getmachineid": "Parcheando getMachineId",
|
||||
"version_greater_than_0_45": "Versión de Cursor >= 0.45.0, Parcheando getMachineId",
|
||||
"permission_denied": "Permiso Denegado: {error}",
|
||||
"backup_created": "Copia de Seguridad Creada",
|
||||
"update_success": "Actualización Exitosa",
|
||||
"update_failed": "Falló la Actualización: {error}",
|
||||
"windows_machine_guid_updated": "GUID de Máquina Windows Actualizado Exitosamente",
|
||||
"reading_package_json": "Leyendo package.json {path}",
|
||||
"invalid_json_object": "Objeto JSON Inválido",
|
||||
"no_version_field": "No se Encontró el Campo de Versión en package.json",
|
||||
"version_field_empty": "El Campo de Versión está Vacío",
|
||||
"invalid_version_format": "Formato de Versión Inválido: {version}",
|
||||
"found_version": "Versión Encontrada: {version}",
|
||||
"version_parse_error": "Error al Analizar Versión: {error}",
|
||||
"package_not_found": "Package.json No Encontrado: {path}",
|
||||
"check_version_failed": "Falló la Verificación de Versión: {error}",
|
||||
"stack_trace": "Traza de la Pila",
|
||||
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0"
|
||||
|
||||
},
|
||||
"register": {
|
||||
"title": "Herramienta de Registro de Cursor",
|
||||
"start": "Iniciando proceso de registro...",
|
||||
"handling_turnstile": "Procesando verificación de seguridad...",
|
||||
"retry_verification": "Reintentando verificación...",
|
||||
"detect_turnstile": "Comprobando verificación de seguridad...",
|
||||
"verification_success": "Verificación de seguridad exitosa",
|
||||
"starting_browser": "Abriendo navegador...",
|
||||
"form_success": "Formulario enviado exitosamente",
|
||||
"browser_started": "Navegador abierto exitosamente",
|
||||
"waiting_for_second_verification": "Esperando verificación por correo electrónico...",
|
||||
"waiting_for_verification_code": "Esperando código de verificación...",
|
||||
"password_success": "Contraseña establecida exitosamente",
|
||||
"password_error": "No se pudo establecer la contraseña: {error}. Por favor intente de nuevo",
|
||||
"waiting_for_page_load": "Cargando página...",
|
||||
"first_verification_passed": "Verificación inicial exitosa",
|
||||
"mailbox": "Acceso exitoso a la bandeja de entrada",
|
||||
"register_start": "Iniciar Registro",
|
||||
"form_submitted": "Formulario Enviado, Iniciando Verificación...",
|
||||
"filling_form": "Rellenando Formulario",
|
||||
"visiting_url": "Visitando URL",
|
||||
"basic_info": "Información Básica Enviada",
|
||||
"handle_turnstile": "Manejar Turnstile",
|
||||
"no_turnstile": "No se Detectó Turnstile",
|
||||
"turnstile_passed": "Turnstile Superado",
|
||||
"verification_start": "Comenzar a Obtener Código de Verificación",
|
||||
"verification_timeout": "Tiempo de Espera Agotado para Código de Verificación",
|
||||
"verification_not_found": "No se Encontró Código de Verificación",
|
||||
"try_get_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {time}s",
|
||||
"get_account": "Obteniendo Información de la Cuenta",
|
||||
"get_token": "Obtener Token de Sesión de Cursor",
|
||||
"token_success": "Token Obtenido Exitosamente",
|
||||
"token_attempt": "Intento | {attempt} veces para obtener Token | Se reintentará en {time}s",
|
||||
"token_max_attempts": "Alcanzado Máximo de Intentos ({max}) | No se pudo obtener Token",
|
||||
"token_failed": "Falló al Obtener Token: {error}",
|
||||
"account_error": "Falló al Obtener Información de la Cuenta: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"browser_start": "Iniciando Navegador",
|
||||
"open_mailbox": "Abriendo Página de Correo",
|
||||
"email_error": "Falló al Obtener Dirección de Correo",
|
||||
"setup_error": "Error de Configuración de Correo: {error}",
|
||||
"start_getting_verification_code": "Comenzando a Obtener Código de Verificación, Se Intentará en 60s",
|
||||
"get_verification_code_timeout": "Tiempo de Espera Agotado para Código de Verificación",
|
||||
"get_verification_code_success": "Código de Verificación Obtenido Exitosamente",
|
||||
"try_get_verification_code": "Intento | {attempt} Obtener Código de Verificación | Tiempo Restante: {remaining_time}s",
|
||||
"verification_code_filled": "Código de Verificación Completado",
|
||||
"login_success_and_jump_to_settings_page": "Inicio de Sesión Exitoso y Salto a Página de Configuración",
|
||||
"detect_login_page": "Página de Inicio de Sesión Detectada, Iniciando Sesión...",
|
||||
"cursor_registration_completed": "¡Registro de Cursor Completado!",
|
||||
"set_password": "Establecer Contraseña",
|
||||
"basic_info_submitted": "Información Básica Enviada",
|
||||
"cursor_auth_info_updated": "Información de Autenticación de Cursor Actualizada",
|
||||
"cursor_auth_info_update_failed": "Falló la Actualización de Información de Autenticación de Cursor",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"account_info_saved": "Información de Cuenta Guardada",
|
||||
"save_account_info_failed": "Falló al Guardar Información de Cuenta",
|
||||
"get_email_address": "Obtener Dirección de Correo",
|
||||
"update_cursor_auth_info": "Actualizar Información de Autenticación de Cursor",
|
||||
"register_process_error": "Error en el Proceso de Registro: {error}",
|
||||
"setting_password": "Estableciendo Contraseña",
|
||||
"manual_code_input": "Entrada Manual de Código",
|
||||
"manual_email_input": "Entrada Manual de Correo",
|
||||
"password": "Contraseña",
|
||||
"first_name": "Nombre",
|
||||
"last_name": "Apellido",
|
||||
"exit_signal": "Señal de Salida",
|
||||
"email_address": "Dirección de Correo",
|
||||
"config_created": "Configuración Creada",
|
||||
"verification_failed": "Verificación Fallida",
|
||||
"verification_error": "Error de Verificación: {error}",
|
||||
"config_option_added": "Opción de Configuración Añadida: {option}",
|
||||
"config_updated": "Configuración Actualizada",
|
||||
"password_submitted": "Contraseña Enviada",
|
||||
"total_usage": "Uso Total: {usage}",
|
||||
"setting_on_password": "Estableciendo Contraseña",
|
||||
"getting_code": "Obteniendo Código de Verificación, Se Intentará en 60s",
|
||||
"human_verify_error": "No se puede verificar que el usuario es humano. Reintentando...",
|
||||
"max_retries_reached": "Se alcanzó el máximo de intentos. Registro fallido."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Administrador de Autenticación de Cursor",
|
||||
"checking_auth": "Verificando Archivo de Autenticación",
|
||||
"auth_not_found": "Archivo de Autenticación No Encontrado",
|
||||
"auth_file_error": "Error en Archivo de Autenticación: {error}",
|
||||
"reading_auth": "Leyendo Archivo de Autenticación",
|
||||
"updating_auth": "Actualizando Información de Autenticación",
|
||||
"auth_updated": "Información de Autenticación Actualizada Exitosamente",
|
||||
"auth_update_failed": "Falló la Actualización de Información de Autenticación: {error}",
|
||||
"auth_file_created": "Archivo de Autenticación Creado",
|
||||
"auth_file_create_failed": "Falló la Creación del Archivo de Autenticación: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"connected_to_database": "Conectado a la Base de Datos",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
|
||||
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
|
||||
"db_connection_error": "Falló la conexión a la base de datos: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Generando Nuevo Correo",
|
||||
"blocked_domain": "Dominio Bloqueado",
|
||||
"select_domain": "Seleccionando Dominio Aleatorio",
|
||||
"copy_email": "Copiando Dirección de Correo",
|
||||
"enter_mailbox": "Entrando al Buzón de Correo",
|
||||
"refresh_mailbox": "Actualizando Buzón de Correo",
|
||||
"check_verification": "Verificando Código de Verificación",
|
||||
"verification_found": "Código de Verificación Encontrado",
|
||||
"verification_not_found": "No se Encontró Código de Verificación",
|
||||
"browser_error": "Error de Control del Navegador: {error}",
|
||||
"navigation_error": "Error de Navegación: {error}",
|
||||
"email_copy_error": "Error al Copiar Correo: {error}",
|
||||
"mailbox_error": "Error en el Buzón de Correo: {error}",
|
||||
"token_saved_to_file": "Token Guardado en cursor_tokens.txt",
|
||||
"navigate_to": "Navegando a {url}",
|
||||
"generate_email_success": "Generación de Correo Exitosa",
|
||||
"select_email_domain": "Seleccionar Dominio de Correo",
|
||||
"select_email_domain_success": "Selección de Dominio de Correo Exitosa",
|
||||
"get_email_name": "Obtener Nombre de Correo",
|
||||
"get_email_name_success": "Nombre de Correo Obtenido Exitosamente",
|
||||
"get_email_address": "Obtener Dirección de Correo",
|
||||
"get_email_address_success": "Dirección de Correo Obtenida Exitosamente",
|
||||
"enter_mailbox_success": "Entrada al Buzón de Correo Exitosa",
|
||||
"found_verification_code": "Código de Verificación Encontrado",
|
||||
"get_cursor_session_token": "Obtener Token de Sesión de Cursor",
|
||||
"get_cursor_session_token_success": "Token de Sesión de Cursor Obtenido Exitosamente",
|
||||
"get_cursor_session_token_failed": "Falló al Obtener Token de Sesión de Cursor",
|
||||
"save_token_failed": "Falló al Guardar Token",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"no_valid_verification_code": "No Hay Código de Verificación Válido"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Iniciando Navegador",
|
||||
"visiting_site": "Visitando dominios de correo",
|
||||
"create_success": "Correo Creado Exitosamente",
|
||||
"create_failed": "Falló al Crear Correo",
|
||||
"create_error": "Error en la Creación del Correo: {error}",
|
||||
"refreshing": "Actualizando Correo",
|
||||
"refresh_success": "Correo Actualizado Exitosamente",
|
||||
"refresh_error": "Error al Actualizar Correo: {error}",
|
||||
"refresh_button_not_found": "Botón de Actualización No Encontrado",
|
||||
"verification_found": "Verificación Encontrada",
|
||||
"verification_not_found": "Verificación No Encontrada",
|
||||
"verification_error": "Error de Verificación: {error}",
|
||||
"verification_code_found": "Código de Verificación Encontrado",
|
||||
"verification_code_not_found": "Código de Verificación No Encontrado",
|
||||
"verification_code_error": "Error en el Código de Verificación: {error}",
|
||||
"address": "Dirección de Correo",
|
||||
"all_domains_blocked": "Todos los Dominios Bloqueados, Cambiando Servicio",
|
||||
"no_available_domains_after_filtering": "No Hay Dominios Disponibles Después del Filtrado",
|
||||
"switching_service": "Cambiando al Servicio {service}",
|
||||
"domains_list_error": "Falló al Obtener Lista de Dominios: {error}",
|
||||
"failed_to_get_available_domains": "Falló al Obtener Dominios Disponibles",
|
||||
"domains_excluded": "Dominios Excluidos: {domains}",
|
||||
"failed_to_create_account": "Falló al Crear Cuenta",
|
||||
"account_creation_error": "Error en la Creación de la Cuenta: {error}",
|
||||
"blocked_domains": "Dominios Bloqueados: {domains}",
|
||||
"blocked_domains_loaded": "Dominios Bloqueados Cargados: {count}",
|
||||
"blocked_domains_loaded_error": "Error al Cargar Dominios Bloqueados: {error}",
|
||||
"blocked_domains_loaded_success": "Dominios Bloqueados Cargados Exitosamente",
|
||||
"blocked_domains_loaded_timeout": "Tiempo de Espera Agotado para Cargar Dominios Bloqueados: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Error de Tiempo de Espera al Cargar Dominios Bloqueados: {error}",
|
||||
"available_domains_loaded": "Dominios Disponibles Cargados: {count}",
|
||||
"domains_filtered": "Dominios Filtrados: {count}",
|
||||
"trying_to_create_email": "Intentando crear correo: {email}",
|
||||
"domain_blocked": "Dominio Bloqueado: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Deshabilitar Actualización Automática de Cursor",
|
||||
"disable_success": "Actualización Automática Deshabilitada Exitosamente",
|
||||
"disable_failed": "Falló al Deshabilitar Actualización Automática: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"start_disable": "Comenzar a Deshabilitar Actualización Automática",
|
||||
"killing_processes": "Terminando Procesos",
|
||||
"processes_killed": "Procesos Terminados",
|
||||
"removing_directory": "Eliminando Directorio",
|
||||
"directory_removed": "Directorio Eliminado",
|
||||
"creating_block_file": "Creando Archivo de Bloqueo",
|
||||
"block_file_created": "Archivo de Bloqueo Creado"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Buscando actualizaciones...",
|
||||
"new_version_available": "¡Nueva versión disponible! (Actual: {current}, Última: {latest})",
|
||||
"updating": "Actualizando a la última versión. El programa se reiniciará automáticamente.",
|
||||
"up_to_date": "Está utilizando la última versión.",
|
||||
"check_failed": "Falló al verificar actualizaciones: {error}",
|
||||
"continue_anyway": "Continuando con la versión actual...",
|
||||
"update_confirm": "¿Desea actualizar a la última versión? (Y/n)",
|
||||
"update_skipped": "Omitiendo actualización.",
|
||||
"invalid_choice": "Elección inválida. Por favor ingrese 'Y' o 'n'.",
|
||||
"development_version": "Versión de Desarrollo {current} > {latest}",
|
||||
"changelog_title": "Registro de Cambios"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Restablecer Cursor Completamente",
|
||||
"checking_config": "Verificando Archivo de Configuración",
|
||||
"config_not_found": "Archivo de Configuración No Encontrado",
|
||||
"no_permission": "No se Puede Leer o Escribir el Archivo de Configuración, Verifique los Permisos",
|
||||
"reading_config": "Leyendo Configuración Actual",
|
||||
"creating_backup": "Creando Copia de Seguridad de la Configuración",
|
||||
"backup_exists": "El Archivo de Respaldo ya Existe, Omitiendo Paso de Respaldo",
|
||||
"generating_new_machine_id": "Generando Nuevo ID de Máquina",
|
||||
"saving_new_config": "Guardando Nueva Configuración en JSON",
|
||||
"success": "Cursor Restablecido Exitosamente",
|
||||
"error": "Falló el Restablecimiento de Cursor: {error}",
|
||||
"press_enter": "Presione Enter para Salir",
|
||||
"reset_machine_id": "Restablecer ID de Máquina",
|
||||
"database_connection_closed": "Conexión a la Base de Datos Cerrada",
|
||||
"database_updated_successfully": "Base de Datos Actualizada Exitosamente",
|
||||
"connected_to_database": "Conectado a la Base de Datos",
|
||||
"updating_pair": "Actualizando Par Clave-Valor",
|
||||
"db_not_found": "Archivo de base de datos no encontrado en: {path}",
|
||||
"db_permission_error": "No se puede acceder al archivo de base de datos. Verifique los permisos",
|
||||
"db_connection_error": "Falló la conexión a la base de datos: {error}",
|
||||
"feature_title": "CARACTERÍSTICAS",
|
||||
"feature_1": "Eliminación completa de configuraciones y ajustes de Cursor AI",
|
||||
"feature_2": "Limpia todos los datos en caché incluyendo historial de IA y peticiones",
|
||||
"feature_3": "Restablece el ID de máquina para evitar la detección de prueba",
|
||||
"feature_4": "Crea nuevos identificadores de máquina aleatorios",
|
||||
"feature_5": "Elimina extensiones personalizadas y preferencias",
|
||||
"feature_6": "Restablece información de prueba y datos de activación",
|
||||
"feature_7": "Escaneo profundo de archivos ocultos relacionados con licencias y pruebas",
|
||||
"feature_8": "Preserva de forma segura archivos y aplicaciones que no son de Cursor",
|
||||
"feature_9": "Compatible con Windows, macOS y Linux",
|
||||
"disclaimer_title": "AVISO IMPORTANTE",
|
||||
"disclaimer_1": "Esta herramienta eliminará permanentemente todas las configuraciones de Cursor AI,",
|
||||
"disclaimer_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"disclaimer_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"disclaimer_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"disclaimer_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"disclaimer_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"disclaimer_7": "Use bajo su propio riesgo",
|
||||
"confirm_title": "¿Está seguro de que desea continuar?",
|
||||
"confirm_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
|
||||
"confirm_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"confirm_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"confirm_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"confirm_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"confirm_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"confirm_7": "Use bajo su propio riesgo",
|
||||
"invalid_choice": "Por favor ingrese 'Y' o 'n'",
|
||||
"skipped_for_safety": "Omitido por seguridad (no relacionado con Cursor): {path}",
|
||||
"deleted": "Eliminado: {path}",
|
||||
"error_deleting": "Error al eliminar {path}: {error}",
|
||||
"not_found": "Archivo no encontrado: {path}",
|
||||
"resetting_machine_id": "Restableciendo identificadores de máquina para evitar la detección de prueba...",
|
||||
"created_machine_id": "Creado nuevo ID de máquina: {path}",
|
||||
"error_creating_machine_id": "Error al crear archivo de ID de máquina {path}: {error}",
|
||||
"error_searching": "Error al buscar archivos en {path}: {error}",
|
||||
"created_extended_trial_info": "Creada nueva información de prueba extendida: {path}",
|
||||
"error_creating_trial_info": "Error al crear archivo de información de prueba {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Restableciendo Editor Cursor AI... Por favor espere.",
|
||||
"reset_cancelled": "Restablecimiento cancelado. Saliendo sin realizar cambios.",
|
||||
"windows_machine_id_modification_skipped": "Modificación de ID de máquina de Windows omitida: {error}",
|
||||
"linux_machine_id_modification_skipped": "Modificación de machine-id de Linux omitida: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Nota: El restablecimiento completo del ID de máquina puede requerir ejecutar como administrador",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Nota: El restablecimiento completo del machine-id del sistema puede requerir privilegios sudo",
|
||||
"windows_registry_instructions": "📝 NOTA: Para un restablecimiento completo en Windows, es posible que también deba limpiar entradas del registro.",
|
||||
"windows_registry_instructions_2": " Ejecute 'regedit' y busque claves que contengan 'Cursor' o 'CursorAI' bajo HKEY_CURRENT_USER\\Software\\ y elimínelas.",
|
||||
"reset_log_1": "¡Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
|
||||
"reset_log_2": "Por favor reinicie su sistema para que los cambios surtan efecto.",
|
||||
"reset_log_3": "Necesitará reinstalar Cursor AI y ahora debería tener un nuevo período de prueba.",
|
||||
"reset_log_4": "Para mejores resultados, considere también:",
|
||||
"reset_log_5": "Usar una dirección de correo diferente al registrarse para una nueva prueba",
|
||||
"reset_log_6": "Si está disponible, usar una VPN para cambiar su dirección IP",
|
||||
"reset_log_7": "Limpiar las cookies y caché de su navegador antes de visitar el sitio web de Cursor AI",
|
||||
"reset_log_8": "Si los problemas persisten, intente instalar Cursor AI en una ubicación diferente",
|
||||
"reset_log_9": "Si encuentra algún problema, vaya al Rastreador de Problemas de Github y cree un problema en https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Ocurrió un error inesperado: {error}",
|
||||
"report_issue": "Por favor reporte este problema al Rastreador de Problemas de Github en https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Proceso interrumpido por el usuario. Saliendo...",
|
||||
"return_to_main_menu": "Volviendo al menú principal...",
|
||||
"process_interrupted": "Proceso interrumpido. Saliendo...",
|
||||
"press_enter_to_return_to_main_menu": "Presione Enter para volver al menú principal...",
|
||||
"removing_known": "Eliminando archivos conocidos de prueba/licencia",
|
||||
"performing_deep_scan": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
|
||||
"found_additional_potential_license_trial_files": "Se encontraron {count} archivos potenciales adicionales de licencia/prueba",
|
||||
"checking_for_electron_localstorage_files": "Verificando archivos de localStorage de Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "No se encontraron archivos adicionales de licencia/prueba en el escaneo profundo",
|
||||
"removing_electron_localstorage_files": "Eliminando archivos de localStorage de Electron",
|
||||
"electron_localstorage_files_removed": "Archivos de localStorage de Electron eliminados",
|
||||
"electron_localstorage_files_removal_error": "Error al eliminar archivos de localStorage de Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Eliminación de archivos de localStorage de Electron completada",
|
||||
"warning_title": "ADVERTENCIA",
|
||||
"warning_1": "Esta acción eliminará todas las configuraciones de Cursor AI,",
|
||||
"warning_2": "ajustes y datos en caché. Esta acción no se puede deshacer.",
|
||||
"warning_3": "Sus archivos de código NO se verán afectados, y la herramienta está diseñada",
|
||||
"warning_4": "para dirigirse solo a archivos del editor Cursor AI y mecanismos de detección de prueba.",
|
||||
"warning_5": "Otras aplicaciones en su sistema no se verán afectadas.",
|
||||
"warning_6": "Necesitará configurar Cursor AI nuevamente después de ejecutar esta herramienta.",
|
||||
"warning_7": "Use bajo su propio riesgo",
|
||||
"removed": "Eliminado: {path}",
|
||||
"failed_to_reset_machine_guid": "Falló al restablecer GUID de máquina",
|
||||
"failed_to_remove": "Falló al eliminar: {path}",
|
||||
"failed_to_delete_file": "Falló al eliminar archivo: {path}",
|
||||
"failed_to_delete_directory": "Falló al eliminar directorio: {path}",
|
||||
"failed_to_delete_file_or_directory": "Falló al eliminar archivo o directorio: {path}",
|
||||
"deep_scanning": "Realizando escaneo profundo para archivos adicionales de prueba/licencia",
|
||||
"resetting_cursor": "Restableciendo Editor Cursor AI... Por favor espere.",
|
||||
"completed_in": "Completado en {time} segundos",
|
||||
"cursor_reset_completed": "¡El Editor Cursor AI ha sido completamente restablecido y se ha evitado la detección de prueba!",
|
||||
"cursor_reset_failed": "Falló el restablecimiento del Editor Cursor AI: {error}",
|
||||
"cursor_reset_cancelled": "Restablecimiento del Editor Cursor AI cancelado. Saliendo sin realizar cambios.",
|
||||
"operation_cancelled": "Operación cancelada. Saliendo sin realizar cambios."
|
||||
},
|
||||
"github_register": {
|
||||
"title": "Automatización de Registro de GitHub + Cursor AI",
|
||||
"features_header": "Características",
|
||||
"feature1": "Genera un correo temporal usando 1secmail.",
|
||||
"feature2": "Registra una nueva cuenta de GitHub con credenciales aleatorias.",
|
||||
"feature3": "Verifica el correo de GitHub automáticamente.",
|
||||
"feature4": "Inicia sesión en Cursor AI usando autenticación de GitHub.",
|
||||
"feature5": "Restablece el ID de máquina para evitar la detección de prueba.",
|
||||
"feature6": "Guarda todas las credenciales en un archivo.",
|
||||
"warnings_header": "Advertencias",
|
||||
"warning1": "Este script automatiza la creación de cuentas, lo que puede violar los términos de servicio de GitHub/Cursor.",
|
||||
"warning2": "Requiere acceso a internet y privilegios administrativos.",
|
||||
"warning3": "CAPTCHA o verificación adicional pueden interrumpir la automatización.",
|
||||
"warning4": "Use responsablemente y bajo su propio riesgo.",
|
||||
"confirm": "¿Está seguro de que desea continuar?",
|
||||
"invalid_choice": "Elección inválida. Por favor ingrese 'yes' o 'no'",
|
||||
"cancelled": "Operación cancelada",
|
||||
"program_terminated": "Programa terminado por el usuario",
|
||||
"starting_automation": "Iniciando automatización...",
|
||||
"github_username": "Nombre de Usuario de GitHub",
|
||||
"github_password": "Contraseña de GitHub",
|
||||
"email_address": "Dirección de Correo",
|
||||
"credentials_saved": "Estas credenciales han sido guardadas en github_cursor_accounts.txt",
|
||||
"completed_successfully": "¡Registro de GitHub + Cursor completado exitosamente!",
|
||||
"registration_encountered_issues": "El registro de GitHub + Cursor encontró problemas.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Revise las ventanas del navegador para intervención manual o intente nuevamente más tarde."
|
||||
}
|
||||
}
|
||||
378
locales/fr.json
Normal file
@@ -0,0 +1,378 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Options Disponibles",
|
||||
"exit": "Quitter le Programme",
|
||||
"reset": "Réinitialiser l'ID Machine",
|
||||
"register": "Enregistrer un Nouveau Compte Cursor",
|
||||
"register_google": "S'inscrire avec un Compte Google",
|
||||
"register_github": "S'inscrire avec un Compte GitHub",
|
||||
"register_manual": "Enregistrer Cursor avec un E-mail Personnalisé",
|
||||
"quit": "Fermer l'Application Cursor",
|
||||
"select_language": "Changer de Langue",
|
||||
"input_choice": "Veuillez entrer votre choix ({choices})",
|
||||
"invalid_choice": "Sélection invalide. Veuillez entrer un numéro de {choices}",
|
||||
"program_terminated": "Programme terminé par l'utilisateur",
|
||||
"error_occurred": "Une erreur s'est produite : {error}. Veuillez réessayer",
|
||||
"press_enter": "Appuyez sur Entrée pour quitter",
|
||||
"disable_auto_update": "Désactiver la Mise à Jour Automatique de Cursor",
|
||||
"lifetime_access_enabled": "ACCÈS À VIE ACTIVÉ",
|
||||
"totally_reset": "Réinitialisation Complète de Cursor",
|
||||
"outdate": "Obsolete",
|
||||
"temp_github_register": "Inscription GitHub temporaire",
|
||||
"coming_soon": "Bientôt"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Anglais",
|
||||
"zh_cn": "Chinois simplifié",
|
||||
"zh_tw": "Chinois traditionnel",
|
||||
"vi": "Vietnamien",
|
||||
"nl": "Néerlandais",
|
||||
"de": "Allemand",
|
||||
"fr": "Français",
|
||||
"pt": "Portugais",
|
||||
"ru": "Russe",
|
||||
"es": "Espagnol"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Début de la Fermeture de Cursor",
|
||||
"no_process": "Aucun Processus Cursor en Cours",
|
||||
"terminating": "Arrêt du Processus {pid}",
|
||||
"waiting": "En Attente de la Fin du Processus",
|
||||
"success": "Tous les Processus Cursor sont Fermés",
|
||||
"timeout": "Délai d'Attente du Processus : {pids}",
|
||||
"error": "Erreur Survenue : {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Outil de Réinitialisation de l'ID Machine de Cursor",
|
||||
"checking": "Vérification du Fichier de Configuration",
|
||||
"not_found": "Fichier de Configuration Non Trouvé",
|
||||
"no_permission": "Impossible de Lire ou d'Écrire le Fichier de Configuration, Veuillez Vérifier les Permissions",
|
||||
"reading": "Lecture de la Configuration Actuelle",
|
||||
"creating_backup": "Création d'une Sauvegarde de la Configuration",
|
||||
"backup_exists": "Fichier de Sauvegarde Existe Déjà, Étape de Sauvegarde Ignorée",
|
||||
"generating": "Génération d'un Nouvel ID Machine",
|
||||
"saving_json": "Sauvegarde de la Nouvelle Configuration en JSON",
|
||||
"success": "ID Machine Réinitialisé avec Succès",
|
||||
"new_id": "Nouvel ID Machine",
|
||||
"permission_error": "Erreur de Permission : {error}",
|
||||
"run_as_admin": "Veuillez Essayer d'Exécuter ce Programme en tant qu'Administrateur",
|
||||
"process_error": "Erreur de Processus de Réinitialisation : {error}",
|
||||
"updating_sqlite": "Mise à Jour de la Base de Données SQLite",
|
||||
"updating_pair": "Mise à Jour de la Paire Clé-Valeur",
|
||||
"sqlite_success": "Base de Données SQLite Mise à Jour avec Succès",
|
||||
"sqlite_error": "Échec de la Mise à Jour de la Base de Données SQLite : {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour Continuer",
|
||||
"unsupported_os": "Système d'Exploitation Non Pris en Charge : {os}",
|
||||
"linux_path_not_found": "Chemin Linux Non Trouvé",
|
||||
"updating_system_ids": "Mise à Jour des IDs Système",
|
||||
"system_ids_updated": "IDs Système Mis à Jour avec Succès",
|
||||
"system_ids_update_failed": "Échec de la Mise à Jour des IDs Système : {error}",
|
||||
"windows_guid_updated": "GUID Windows Mis à Jour avec Succès",
|
||||
"windows_permission_denied": "Permission Windows Refusée",
|
||||
"windows_guid_update_failed": "Échec de la Mise à Jour du GUID Windows",
|
||||
"macos_uuid_updated": "UUID macOS Mis à Jour avec Succès",
|
||||
"plutil_command_failed": "Commande plutil Échouée",
|
||||
"start_patching": "Démarrage du Patching de getMachineId",
|
||||
"macos_uuid_update_failed": "Échec de la Mise à Jour de l'UUID macOS",
|
||||
"current_version": "Version Actuelle de Cursor : {version}",
|
||||
"patch_completed": "Patching de getMachineId Terminé",
|
||||
"patch_failed": "Échec du Patching de getMachineId : {error}",
|
||||
"version_check_passed": "Vérification de la Version de Cursor Réussie",
|
||||
"file_modified": "Fichier Modifié",
|
||||
"version_less_than_0_45": "Version de Cursor < 0.45.0, Patching de getMachineId Ignoré",
|
||||
"detecting_version": "Détection de la Version de Cursor",
|
||||
"patching_getmachineid": "Patching de getMachineId",
|
||||
"version_greater_than_0_45": "Version de Cursor >= 0.45.0, Patching de getMachineId",
|
||||
"permission_denied": "Permission Refusée : {error}",
|
||||
"backup_created": "Sauvegarde Créée",
|
||||
"update_success": "Mise à Jour Réussie",
|
||||
"update_failed": "Échec de la Mise à Jour : {error}",
|
||||
"windows_machine_guid_updated": "GUID de la Machine Windows Mis à Jour avec Succès",
|
||||
"reading_package_json": "Lecture du package.json {path}",
|
||||
"invalid_json_object": "Objet JSON Invalide",
|
||||
"no_version_field": "Aucun Champ de Version Trouvé dans le package.json",
|
||||
"version_field_empty": "Champ de Version Vide",
|
||||
"invalid_version_format": "Format de Version Invalide : {version}",
|
||||
"found_version": "Version Trouvée : {version}",
|
||||
"version_parse_error": "Erreur d'Analyse de la Version : {error}",
|
||||
"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"
|
||||
},
|
||||
"register": {
|
||||
"title": "Outil d'Enregistrement de Cursor",
|
||||
"start": "Démarrage du Processus d'Enregistrement...",
|
||||
"handling_turnstile": "Traitement de la Vérification de Sécurité...",
|
||||
"retry_verification": "Nouvelle Tentative de Vérification...",
|
||||
"detect_turnstile": "Vérification de la Sécurité...",
|
||||
"verification_success": "Vérification de Sécurité Réussie",
|
||||
"starting_browser": "Ouverture du Navigateur...",
|
||||
"form_success": "Formulaire Soumis avec Succès",
|
||||
"browser_started": "Navigateur Ouvert avec Succès",
|
||||
"waiting_for_second_verification": "En Attente de la Vérification par E-mail...",
|
||||
"waiting_for_verification_code": "En Attente du Code de Vérification...",
|
||||
"password_success": "Mot de Passe Défini avec Succès",
|
||||
"password_error": "Impossible de Définir le Mot de Passe : {error}. Veuillez Réessayer",
|
||||
"waiting_for_page_load": "Chargement de la Page...",
|
||||
"first_verification_passed": "Première Vérification Réussie",
|
||||
"mailbox": "Boîte de Réception Accédée avec Succès",
|
||||
"register_start": "Démarrer l'Enregistrement",
|
||||
"form_submitted": "Formulaire Soumis, Démarrage de la Vérification...",
|
||||
"filling_form": "Remplissage du Formulaire",
|
||||
"visiting_url": "Visite de l'URL",
|
||||
"basic_info": "Informations de Base Soumises",
|
||||
"handle_turnstile": "Traitement du Tourniquet",
|
||||
"no_turnstile": "Aucun Tourniquet Détecté",
|
||||
"turnstile_passed": "Tourniquet Réussi",
|
||||
"verification_start": "Démarrage de l'Obtention du Code de Vérification",
|
||||
"verification_timeout": "Délai d'Attente du Code de Vérification",
|
||||
"verification_not_found": "Aucun Code de Vérification Trouvé",
|
||||
"try_get_code": "Essayer | {attempt} d'Obtenir le Code de Vérification | Temps Restant : {time}s",
|
||||
"get_account": "Obtention des Informations du Compte",
|
||||
"get_token": "Obtention du Jeton de Session Cursor",
|
||||
"token_success": "Jeton Obtenu avec Succès",
|
||||
"token_attempt": "Essayer | {attempt} fois d'Obtenir le Jeton | Nouvelle Tentative dans {time}s",
|
||||
"token_max_attempts": "Nombre Maximum de Tentatives Atteint ({max}) | Échec de l'Obtention du Jeton",
|
||||
"token_failed": "Échec de l'Obtention du Jeton : {error}",
|
||||
"account_error": "Échec de l'Obtention des Informations du Compte : {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour Continuer",
|
||||
"browser_start": "Démarrage du Navigateur",
|
||||
"open_mailbox": "Ouverture de la Page de la Boîte de Réception",
|
||||
"email_error": "Échec de l'Obtention de l'Adresse E-mail",
|
||||
"setup_error": "Erreur de Configuration de l'E-mail : {error}",
|
||||
"start_getting_verification_code": "Démarrage de l'Obtention du Code de Vérification, Nouvelle Tentative dans 60s",
|
||||
"get_verification_code_timeout": "Délai d'Attente de l'Obtention du Code de Vérification",
|
||||
"get_verification_code_success": "Code de Vérification Obtenu avec Succès",
|
||||
"try_get_verification_code": "Essayer | {attempt} d'Obtenir le Code de Vérification | Temps Restant : {remaining_time}s",
|
||||
"verification_code_filled": "Code de Vérification Rempli",
|
||||
"login_success_and_jump_to_settings_page": "Connexion Réussie et Accès à la Page des Paramètres",
|
||||
"detect_login_page": "Détection de la Page de Connexion, Démarrage de la Connexion...",
|
||||
"cursor_registration_completed": "Enregistrement de Cursor Terminé!",
|
||||
"set_password": "Définir le Mot de Passe",
|
||||
"basic_info_submitted": "Informations de Base Soumises",
|
||||
"cursor_auth_info_updated": "Informations d'Authentification de Cursor Mises à Jour",
|
||||
"cursor_auth_info_update_failed": "Échec de la Mise à Jour des Informations d'Authentification de Cursor",
|
||||
"reset_machine_id": "Réinitialiser l'ID Machine",
|
||||
"account_info_saved": "Informations du Compte Enregistrées",
|
||||
"save_account_info_failed": "Échec de l'Enregistrement des Informations du Compte",
|
||||
"get_email_address": "Obtenir l'Adresse E-mail",
|
||||
"update_cursor_auth_info": "Mettre à Jour les Informations d'Authentification de Cursor",
|
||||
"register_process_error": "Erreur du Processus d'Enregistrement : {error}",
|
||||
"setting_password": "Définir le Mot de Passe",
|
||||
"manual_code_input": "Saisie Manuelle du Code",
|
||||
"manual_email_input": "Saisie Manuelle de l'E-mail",
|
||||
"password": "Mot de Passe",
|
||||
"first_name": "Prénom",
|
||||
"last_name": "Nom de Famille",
|
||||
"exit_signal": "Signal de Sortie",
|
||||
"email_address": "Adresse E-mail",
|
||||
"config_created": "Configuration Créée",
|
||||
"verification_failed": "Échec de la Vérification",
|
||||
"verification_error": "Erreur de Vérification : {error}",
|
||||
"config_option_added": "Option de Configuration Ajoutée : {option}",
|
||||
"config_updated": "Configuration Mise à Jour",
|
||||
"password_submitted": "Mot de Passe Soumis",
|
||||
"total_usage": "Utilisation Totale : {usage}",
|
||||
"setting_on_password": "Définir le Mot de Passe",
|
||||
"getting_code": "Obtention du Code de Vérification, Nouvelle Tentative dans 60s"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Gestionnaire d'Authentification de Cursor",
|
||||
"checking_auth": "Vérification du Fichier d'Authentification",
|
||||
"auth_not_found": "Fichier d'Authentification Non Trouvé",
|
||||
"auth_file_error": "Erreur du Fichier d'Authentification : {error}",
|
||||
"reading_auth": "Lecture du Fichier d'Authentification",
|
||||
"updating_auth": "Mise à Jour des Informations d'Authentification",
|
||||
"auth_updated": "Informations d'Authentification Mises à Jour avec Succès",
|
||||
"auth_update_failed": "Échec de la Mise à Jour des Informations d'Authentification : {error}",
|
||||
"auth_file_created": "Fichier d'Authentification Créé",
|
||||
"auth_file_create_failed": "Échec de la Création du Fichier d'Authentification : {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour Continuer",
|
||||
"reset_machine_id": "Réinitialiser l'ID Machine",
|
||||
"database_connection_closed": "Connexion à la Base de Données Fermée",
|
||||
"database_updated_successfully": "Base de Données Mise à Jour avec Succès",
|
||||
"connected_to_database": "Connecté à la Base de Données",
|
||||
"updating_pair": "Mise à Jour de la Paire Clé-Valeur",
|
||||
"db_not_found": "Fichier de Base de Données Non Trouvé à : {path}",
|
||||
"db_permission_error": "Impossible d'Accéder au Fichier de Base de Données. Veuillez Vérifier les Permissions",
|
||||
"db_connection_error": "Échec de la Connexion à la Base de Données : {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Générer un Nouvel E-mail",
|
||||
"blocked_domain": "Domaine Bloqué",
|
||||
"select_domain": "Sélectionner un Domaine Aléatoire",
|
||||
"copy_email": "Copier l'Adresse E-mail",
|
||||
"enter_mailbox": "Entrer dans la Boîte de Réception",
|
||||
"refresh_mailbox": "Actualiser la Boîte de Réception",
|
||||
"check_verification": "Vérifier le Code de Vérification",
|
||||
"verification_found": "Code de Vérification Trouvé",
|
||||
"verification_not_found": "Aucun Code de Vérification Trouvé",
|
||||
"browser_error": "Erreur de Contrôle du Navigateur : {error}",
|
||||
"navigation_error": "Erreur de Navigation : {error}",
|
||||
"email_copy_error": "Erreur de Copie de l'E-mail : {error}",
|
||||
"mailbox_error": "Erreur de la Boîte de Réception : {error}",
|
||||
"token_saved_to_file": "Jeton Enregistré dans cursor_tokens.txt",
|
||||
"navigate_to": "Naviguer vers {url}",
|
||||
"generate_email_success": "E-mail Généré avec Succès",
|
||||
"select_email_domain": "Sélectionner le Domaine de l'E-mail",
|
||||
"select_email_domain_success": "Domaine de l'E-mail Sélectionné avec Succès",
|
||||
"get_email_name": "Obtenir le Nom de l'E-mail",
|
||||
"get_email_name_success": "Nom de l'E-mail Obtenu avec Succès",
|
||||
"get_email_address": "Obtenir l'Adresse E-mail",
|
||||
"get_email_address_success": "Adresse E-mail Obtenue avec Succès",
|
||||
"enter_mailbox_success": "Entrée dans la Boîte de Réception Réussie",
|
||||
"found_verification_code": "Code de Vérification Trouvé",
|
||||
"get_cursor_session_token": "Obtenir le Jeton de Session Cursor",
|
||||
"get_cursor_session_token_success": "Jeton de Session Cursor Obtenu avec Succès",
|
||||
"get_cursor_session_token_failed": "Échec de l'Obtention du Jeton de Session Cursor",
|
||||
"save_token_failed": "Échec de l'Enregistrement du Jeton",
|
||||
"database_updated_successfully": "Base de Données Mise à Jour avec Succès",
|
||||
"database_connection_closed": "Connexion à la Base de Données Fermée",
|
||||
"no_valid_verification_code": "Aucun Code de Vérification Valide"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Démarrage du Navigateur",
|
||||
"visiting_site": "Visite de mail domains",
|
||||
"create_success": "E-mail Créé avec Succès",
|
||||
"create_failed": "Échec de la Création de l'E-mail",
|
||||
"create_error": "Erreur de Création de l'E-mail : {error}",
|
||||
"refreshing": "Actualisation de l'E-mail",
|
||||
"refresh_success": "E-mail Actualisé avec Succès",
|
||||
"refresh_error": "Erreur d'Actualisation de l'E-mail : {error}",
|
||||
"refresh_button_not_found": "Bouton d'Actualisation Non Trouvé",
|
||||
"verification_found": "Vérification Trouvée",
|
||||
"verification_not_found": "Vérification Non Trouvée",
|
||||
"verification_error": "Erreur de Vérification : {error}",
|
||||
"verification_code_found": "Code de Vérification Trouvé",
|
||||
"verification_code_not_found": "Code de Vérification Non Trouvé",
|
||||
"verification_code_error": "Erreur de Code de Vérification : {error}",
|
||||
"address": "Adresse E-mail",
|
||||
"all_domains_blocked": "Tous les Domaines Sont Bloqués, Changement de Service",
|
||||
"no_available_domains_after_filtering": "Aucun Domaine Disponible Après Filtrage",
|
||||
"switching_service": "Changement vers le Service {service}",
|
||||
"domains_list_error": "Échec de l'Obtention de la Liste des Domaines : {error}",
|
||||
"failed_to_get_available_domains": "Échec de l'Obtention des Domaines Disponibles",
|
||||
"domains_excluded": "Domaines Exclus : {domains}",
|
||||
"failed_to_create_account": "Échec de la Création du Compte",
|
||||
"account_creation_error": "Erreur de Création du Compte : {error}",
|
||||
"domain_blocked": "Domaine Bloqué : {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Désactivation de la Mise à Jour Automatique de Cursor",
|
||||
"disable_success": "Mise à Jour Automatique Désactivée avec Succès",
|
||||
"disable_failed": "Échec de la Désactivation de la Mise à Jour Automatique : {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour Continuer",
|
||||
"start_disable": "Démarrage de la Désactivation de la Mise à Jour Automatique",
|
||||
"killing_processes": "Tuer les Processus",
|
||||
"processes_killed": "Processus Tuer",
|
||||
"removing_directory": "Suppression du Dossier",
|
||||
"directory_removed": "Dossier Supprimé",
|
||||
"creating_block_file": "Création du Fichier de Blocage",
|
||||
"block_file_created": "Fichier de Blocage Créé"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Vérification des mises à jour...",
|
||||
"new_version_available": "Nouvelle version disponible! (Version actuelle: {current}, Version la plus récente: {latest})",
|
||||
"updating": "Mise à jour vers la version la plus récente. Le programme redémarrera automatiquement.",
|
||||
"up_to_date": "Vous utilisez la version la plus récente.",
|
||||
"check_failed": "Échec de la vérification des mises à jour: {error}",
|
||||
"continue_anyway": "Continuer avec la version actuelle...",
|
||||
"update_confirm": "Voulez-vous mettre à jour vers la version la plus récente? (O/n)",
|
||||
"update_skipped": "Mise à jour ignorée.",
|
||||
"invalid_choice": "Choix invalide. Veuillez entrer 'O' ou 'n'.",
|
||||
"development_version": "Version de Développement {current} > {latest}",
|
||||
"changelog_title": "Journal des modifications"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Réinitialiser Complètement Cursor",
|
||||
"checking_config": "Vérification du Fichier de Configuration",
|
||||
"config_not_found": "Fichier de Configuration Non Trouvé",
|
||||
"no_permission": "Impossible de Lire ou d'Écrire le Fichier de Configuration, Veuillez Vérifier les Permissions du Fichier",
|
||||
"reading_config": "Lecture de la Configuration Actuelle",
|
||||
"creating_backup": "Création de la Sauvegarde de la Configuration",
|
||||
"backup_exists": "Fichier de Sauvegarde Déjà Existant, Passer à la Sauvegarde",
|
||||
"generating_new_machine_id": "Génération d'un Nouvel ID Machine",
|
||||
"saving_new_config": "Enregistrement de la Nouvelle Configuration dans JSON",
|
||||
"success": "Réinitialisation de Cursor Réussie",
|
||||
"error": "Réinitialisation de Cursor Échouée: {error}",
|
||||
"press_enter": "Appuyez sur Entrée pour Continuer",
|
||||
"reset_machine_id": "Réinitialiser l'ID Machine",
|
||||
"database_connection_closed": "Connexion à la Base de Données Fermée",
|
||||
"database_updated_successfully": "Base de Données Mise à Jour avec Succès",
|
||||
"connected_to_database": "Connecté à la Base de Données",
|
||||
"updating_pair": "Updating Key-Value Pair",
|
||||
"db_not_found": "Database file not found at: {path}",
|
||||
"db_permission_error": "Impossible d'Accéder au Fichier de Base de Données. Veuillez Vérifier les Permissions",
|
||||
"db_connection_error": "Échec de la Connexion à la Base de Données : {error}",
|
||||
"feature_title": "Fonctionnalités",
|
||||
"feature_1": "Suppression complète des paramètres et configurations de Cursor AI",
|
||||
"feature_2": "Efface tous les données mises en cache, y compris l'historique et les prompts",
|
||||
"feature_3": "Réinitialise l'ID Machine pour contourner la détection de la période d'essai",
|
||||
"feature_4": "Crée de nouveaux identifiants de machine aléatoires",
|
||||
"feature_5": "Supprime les extensions personnalisées et les préférences",
|
||||
"feature_6": "Réinitialise les informations de la période d'essai et les données d'activation",
|
||||
"feature_7": "Analyse approfondie pour les fichiers cachés liés à la licence et à la période d'essai",
|
||||
"feature_8": "Sauvegarde sécurisée des fichiers non liés à Cursor et applications",
|
||||
"feature_9": "Compatible avec Windows, macOS et Linux",
|
||||
"disclaimer_title": "DISCLAIMER",
|
||||
"disclaimer_1": "Cet outil supprimera définitivement tous les paramètres et configurations de Cursor AI,",
|
||||
"disclaimer_2": "configurations, et les données mises en cache. Cette action ne peut pas être annulée.",
|
||||
"disclaimer_3": "Vos fichiers de code ne seront PAS affectés, et l'outil est conçu",
|
||||
"disclaimer_4": "pour ne cibler que les fichiers de l'éditeur Cursor AI et les mécanismes de détection de la période d'essai.",
|
||||
"disclaimer_5": "Les autres applications sur votre système ne seront PAS affectées.",
|
||||
"disclaimer_6": "Vous devrez régler Cursor AI à nouveau après avoir exécuté cet outil.",
|
||||
"disclaimer_7": "Utilisez à vos risques et périls",
|
||||
"confirm_title": "Êtes-vous sûr de vouloir continuer?",
|
||||
"confirm_1": "Cette action supprimera tous les paramètres et configurations de Cursor AI,",
|
||||
"confirm_2": "configurations, et les données mises en cache. Cette action ne peut pas être annulée.",
|
||||
"confirm_3": "Vos fichiers de code ne seront PAS affectés, et l'outil est conçu",
|
||||
"confirm_4": "pour ne cibler que les fichiers de l'éditeur Cursor AI et les mécanismes de détection de la période d'essai.",
|
||||
"confirm_5": "Les autres applications sur votre système ne seront PAS affectées.",
|
||||
"confirm_6": "Vous devrez régler Cursor AI à nouveau après avoir exécuté cet outil.",
|
||||
"confirm_7": "Utilisez à vos risques et périls",
|
||||
"invalid_choice": "Veuillez entrer 'O' ou 'n'",
|
||||
"skipped_for_safety": "Passé pour la sécurité (non lié à Cursor): {path}",
|
||||
"deleted": "Supprimé: {path}",
|
||||
"error_deleting": "Erreur de suppression de {path}: {error}",
|
||||
"not_found": "Fichier non trouvé: {path}",
|
||||
"resetting_machine_id": "Réinitialisation des identifiants de machine pour contourner la détection de la période d'essai...",
|
||||
"created_machine_id": "Créé un nouvel ID machine: {path}",
|
||||
"error_creating_machine_id": "Erreur de création du fichier ID machine {path}: {error}",
|
||||
"error_searching": "Erreur de recherche dans {path}: {error}",
|
||||
"created_extended_trial_info": "Créé un nouveau fichier d'informations de période d'essai étendue: {path}",
|
||||
"error_creating_trial_info": "Erreur de création du fichier d'informations de période d'essai: {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Réinitialisation de l'éditeur Cursor AI... Veuillez patienter.",
|
||||
"reset_cancelled": "Réinitialisation annulée. Exiting sans faire de modifications.",
|
||||
"windows_machine_id_modification_skipped": "Modification de l'ID machine Windows ignorée: {error}",
|
||||
"linux_machine_id_modification_skipped": "Modification de l'ID machine Linux ignorée: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Note: Réinitialisation complète de l'ID machine peut nécessiter d'exécuter en tant qu'administrateur",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Note: Réinitialisation complète de l'ID machine peut nécessiter des privilèges sudo",
|
||||
"windows_registry_instructions": "📝 NOTE: Pour la réinitialisation complète sur Windows, vous devrez peut-être également nettoyer les entrées du registre.",
|
||||
"windows_registry_instructions_2": " Exécutez 'regedit' et recherchez les clés contenant 'Cursor' ou 'CursorAI' sous HKEY_CURRENT_USER\\Software\\ et supprimez-les.\n",
|
||||
"reset_log_1": "Cursor AI a été complètement réinitialisé et la détection de la période d'essai a été contournée!",
|
||||
"reset_log_2": "Veuillez redémarrer votre système pour que les modifications prennent effet.",
|
||||
"reset_log_3": "Vous devrez réinstaller Cursor AI et devriez maintenant avoir une période d'essai fraîche.",
|
||||
"reset_log_4": "Pour les meilleurs résultats, considérez également:",
|
||||
"reset_log_5": "Utilisez une autre adresse e-mail lors de l'inscription pour une nouvelle période d'essai",
|
||||
"reset_log_6": "Si disponible, utilisez un VPN pour changer votre adresse IP",
|
||||
"reset_log_7": "Nettoyez les cookies et le cache de votre navigateur avant de visiter le site web de Cursor AI",
|
||||
"reset_log_8": "Si les problèmes persistent, essayez d'installer Cursor AI dans un autre emplacement",
|
||||
"reset_log_9": "Si vous rencontrez des problèmes, allez au suivi des problèmes Github et créez un problème à https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Une erreur inattendue est survenue: {error}",
|
||||
"report_issue": "Veuillez signaler ce problème au suivi des problèmes Github à https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Processus interrompu par l'utilisateur. Exiting...",
|
||||
"return_to_main_menu": "Retour au menu principal...",
|
||||
"process_interrupted": "Processus interrompu. Exiting...",
|
||||
"press_enter_to_return_to_main_menu": "Appuyez sur Entrée pour retourner au menu principal...",
|
||||
"removing_known": "Suppression des fichiers de période d'essai/licence connus",
|
||||
"performing_deep_scan": "Exécution d'une analyse approfondie pour les fichiers de période d'essai/licence supplémentaires",
|
||||
"found_additional_potential_license_trial_files": "Trouvé {count} fichiers de période d'essai/licence supplémentaires potentiels",
|
||||
"checking_for_electron_localstorage_files": "Vérification des fichiers localStorage Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Aucun fichier de licence/période d'essai supplémentaire trouvé dans l'analyse approfondie",
|
||||
"removing_electron_localstorage_files": "Suppression des fichiers localStorage Electron",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
378
locales/nl.json
Normal file
@@ -0,0 +1,378 @@
|
||||
{
|
||||
"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}",
|
||||
"invalid_choice": "Ongeldige selectie. Voer een nummer in uit {choices}.",
|
||||
"program_terminated": "Programma is beëindigd door de gebruiker",
|
||||
"error_occurred": "Er is een fout opgetreden: {error}. Probeer het opnieuw.",
|
||||
"press_enter": "Druk op Enter om door te gaan.",
|
||||
"disable_auto_update": "Cursor automatische updates uitschakelen",
|
||||
"lifetime_access_enabled": "Levenslange toegang ingeschakeld",
|
||||
"totally_reset": "Cursor volledig resetten",
|
||||
"outdate": "Verouderd",
|
||||
"temp_github_register": "Tijdelijke GitHub-registratie",
|
||||
"coming_soon": "Binnenkort"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Engels",
|
||||
"zh_cn": "Vereenvoudigd Chinees",
|
||||
"zh_tw": "Traditioneel Chinees",
|
||||
"vi": "Vietnamees",
|
||||
"nl": "Nederlands",
|
||||
"de": "Duits",
|
||||
"fr": "Frans",
|
||||
"pt": "Portugees",
|
||||
"ru": "Russisch",
|
||||
"es": "Spaans"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start met afsluiten van Cursor",
|
||||
"no_process": "Geen actief Cursor-proces",
|
||||
"terminating": "Proces beëindigen {pid}",
|
||||
"waiting": "Wachten tot het proces is afgesloten",
|
||||
"success": "Alle Cursor-processen gesloten",
|
||||
"timeout": "Proces time-out: {pids}",
|
||||
"error": "Fout opgetreden: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Cursor Machine-ID Reset Tool",
|
||||
"checking": "Configuratiebestand controleren",
|
||||
"not_found": "Configuratiebestand niet gevonden",
|
||||
"no_permission": "Kan configuratiebestand niet lezen of schrijven, controleer de bestandsrechten",
|
||||
"reading": "Huidige configuratie lezen",
|
||||
"creating_backup": "Configuratieback-up maken",
|
||||
"backup_exists": "Back-upbestand bestaat al, back-upstap overslaan",
|
||||
"generating": "Nieuwe Machine-ID genereren",
|
||||
"saving_json": "Nieuwe configuratie opslaan naar JSON",
|
||||
"success": "Machine-ID succesvol gereset",
|
||||
"new_id": "Nieuwe Machine-ID",
|
||||
"permission_error": "Toestemmingsfout: {error}",
|
||||
"run_as_admin": "Probeer dit programma als beheerder uit te voeren",
|
||||
"process_error": "Resetprocesfout: {error}",
|
||||
"updating_sqlite": "SQLite-database bijwerken",
|
||||
"updating_pair": "Sleutel-waarde paar bijwerken",
|
||||
"sqlite_success": "SQLite-database succesvol bijgewerkt",
|
||||
"sqlite_error": "SQLite-database bijwerken mislukt: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan",
|
||||
"unsupported_os": "Niet-ondersteund besturingssysteem: {os}",
|
||||
"linux_path_not_found": "Linux-pad niet gevonden",
|
||||
"updating_system_ids": "Systeem-ID's bijwerken",
|
||||
"system_ids_updated": "Systeem-ID's succesvol bijgewerkt",
|
||||
"system_ids_update_failed": "Systeem-ID's bijwerken mislukt: {error}",
|
||||
"windows_guid_updated": "Windows GUID succesvol bijgewerkt",
|
||||
"windows_permission_denied": "Windows toestemming geweigerd",
|
||||
"windows_guid_update_failed": "Windows GUID bijwerken mislukt",
|
||||
"macos_uuid_updated": "macOS UUID succesvol bijgewerkt",
|
||||
"plutil_command_failed": "plutil-opdracht mislukt",
|
||||
"start_patching": "Patching getMachineId starten",
|
||||
"macos_uuid_update_failed": "macOS UUID bijwerken mislukt",
|
||||
"current_version": "Huidige Cursor-versie: {version}",
|
||||
"patch_completed": "Patching getMachineId voltooid",
|
||||
"patch_failed": "Patching getMachineId mislukt: {error}",
|
||||
"version_check_passed": "Cursor-versiecontrole geslaagd",
|
||||
"file_modified": "Bestand gewijzigd",
|
||||
"version_less_than_0_45": "Cursor-versie < 0.45.0, patching getMachineId overslaan",
|
||||
"detecting_version": "Cursor-versie detecteren",
|
||||
"patching_getmachineid": "Patching getMachineId",
|
||||
"version_greater_than_0_45": "Cursor-versie >= 0.45.0, patching getMachineId",
|
||||
"permission_denied": "Toestemming geweigerd: {error}",
|
||||
"backup_created": "Back-up gemaakt",
|
||||
"update_success": "Update geslaagd",
|
||||
"update_failed": "Update mislukt: {error}",
|
||||
"windows_machine_guid_updated": "Windows Machine GUID succesvol bijgewerkt",
|
||||
"reading_package_json": "package.json lezen {path}",
|
||||
"invalid_json_object": "Ongeldig JSON-object",
|
||||
"no_version_field": "Geen versieveld gevonden in package.json",
|
||||
"version_field_empty": "Versieveld is leeg",
|
||||
"invalid_version_format": "Ongeldig versieformaat: {version}",
|
||||
"found_version": "Gevonden versie: {version}",
|
||||
"version_parse_error": "Versie parse-fout: {error}",
|
||||
"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"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registratietool",
|
||||
"start": "Registratieproces starten...",
|
||||
"handling_turnstile": "Beveiligingsverificatie verwerken...",
|
||||
"retry_verification": "Verificatie opnieuw proberen...",
|
||||
"detect_turnstile": "Beveiligingsverificatie controleren...",
|
||||
"verification_success": "Beveiligingsverificatie geslaagd",
|
||||
"starting_browser": "Browser openen...",
|
||||
"form_success": "Formulier succesvol ingediend",
|
||||
"browser_started": "Browser succesvol geopend",
|
||||
"waiting_for_second_verification": "Wachten op e-mailverificatie...",
|
||||
"waiting_for_verification_code": "Wachten op verificatiecode...",
|
||||
"password_success": "Wachtwoord succesvol ingesteld",
|
||||
"password_error": "Kon wachtwoord niet instellen: {error}. Probeer het opnieuw",
|
||||
"waiting_for_page_load": "Pagina laden...",
|
||||
"first_verification_passed": "Eerste verificatie geslaagd",
|
||||
"mailbox": "E-mailinbox succesvol geopend",
|
||||
"register_start": "Registratie starten",
|
||||
"form_submitted": "Formulier ingediend, verificatie starten...",
|
||||
"filling_form": "Formulier invullen",
|
||||
"visiting_url": "URL bezoeken",
|
||||
"basic_info": "Basisinformatie ingediend",
|
||||
"handle_turnstile": "Turnstile verwerken",
|
||||
"no_turnstile": "Geen Turnstile gedetecteerd",
|
||||
"turnstile_passed": "Turnstile geslaagd",
|
||||
"verification_start": "Verificatiecode verkrijgen starten",
|
||||
"verification_timeout": "Verificatiecode time-out",
|
||||
"verification_not_found": "Geen verificatiecode gevonden",
|
||||
"try_get_code": "Probeer | {attempt} Verificatiecode verkrijgen | Tijd over: {time}s",
|
||||
"get_account": "Accountinformatie verkrijgen",
|
||||
"get_token": "Cursor-sessietoken verkrijgen",
|
||||
"token_success": "Token succesvol verkregen",
|
||||
"token_attempt": "Probeer | {attempt} keer om token te verkrijgen | Opnieuw proberen in {time}s",
|
||||
"token_max_attempts": "Maximale pogingen bereikt ({max}) | Token verkrijgen mislukt",
|
||||
"token_failed": "Token verkrijgen mislukt: {error}",
|
||||
"account_error": "Accountinformatie verkrijgen mislukt: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan",
|
||||
"browser_start": "Browser starten",
|
||||
"open_mailbox": "Mailboxpagina openen",
|
||||
"email_error": "E-mailadres verkrijgen mislukt",
|
||||
"setup_error": "E-mailinstelling fout: {error}",
|
||||
"start_getting_verification_code": "Verificatiecode verkrijgen starten, opnieuw proberen in 60s",
|
||||
"get_verification_code_timeout": "Verificatiecode verkrijgen time-out",
|
||||
"get_verification_code_success": "Verificatiecode succesvol verkregen",
|
||||
"try_get_verification_code": "Probeer | {attempt} Verificatiecode verkrijgen | Tijd over: {remaining_time}s",
|
||||
"verification_code_filled": "Verificatiecode ingevuld",
|
||||
"login_success_and_jump_to_settings_page": "Inloggen geslaagd en naar instellingenpagina springen",
|
||||
"detect_login_page": "Inlogpagina detecteren, inloggen starten...",
|
||||
"cursor_registration_completed": "Cursor-registratie voltooid!",
|
||||
"set_password": "Wachtwoord instellen",
|
||||
"basic_info_submitted": "Basisinformatie ingediend",
|
||||
"cursor_auth_info_updated": "Cursor-authenticatie-informatie bijgewerkt",
|
||||
"cursor_auth_info_update_failed": "Cursor-authenticatie-informatie bijwerken mislukt",
|
||||
"reset_machine_id": "Machine-ID resetten",
|
||||
"account_info_saved": "Accountinformatie opgeslagen",
|
||||
"save_account_info_failed": "Accountinformatie opslaan mislukt",
|
||||
"get_email_address": "E-mailadres verkrijgen",
|
||||
"update_cursor_auth_info": "Cursor-authenticatie-informatie bijwerken",
|
||||
"register_process_error": "Registratieprocesfout: {error}",
|
||||
"setting_password": "Wachtwoord instellen",
|
||||
"manual_code_input": "Handmatige code-invoer",
|
||||
"manual_email_input": "Handmatige e-mailinvoer",
|
||||
"password": "Wachtwoord",
|
||||
"first_name": "Voornaam",
|
||||
"last_name": "Achternaam",
|
||||
"exit_signal": "Exit-signaal",
|
||||
"email_address": "E-mailadres",
|
||||
"config_created": "Configuratie aangemaakt",
|
||||
"verification_failed": "Verificatie mislukt",
|
||||
"verification_error": "Verificatiefout: {error}",
|
||||
"config_option_added": "Configuratieoptie toegevoegd: {option}",
|
||||
"config_updated": "Configuratie bijgewerkt",
|
||||
"password_submitted": "Wachtwoord ingediend",
|
||||
"total_usage": "Totaal gebruik: {usage}",
|
||||
"setting_on_password": "Wachtwoord instellen",
|
||||
"getting_code": "Verificatiecode verkrijgen, opnieuw proberen in 60s"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Authenticatiebeheer",
|
||||
"checking_auth": "Authenticatiebestand controleren",
|
||||
"auth_not_found": "Authenticatiebestand niet gevonden",
|
||||
"auth_file_error": "Authenticatiebestandfout: {error}",
|
||||
"reading_auth": "Authenticatiebestand lezen",
|
||||
"updating_auth": "Authenticatie-informatie bijwerken",
|
||||
"auth_updated": "Authenticatie-informatie succesvol bijgewerkt",
|
||||
"auth_update_failed": "Authenticatie-informatie bijwerken mislukt: {error}",
|
||||
"auth_file_created": "Authenticatiebestand aangemaakt",
|
||||
"auth_file_create_failed": "Authenticatiebestand aanmaken mislukt: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan",
|
||||
"reset_machine_id": "Machine-ID resetten",
|
||||
"database_connection_closed": "Databaseverbinding gesloten",
|
||||
"database_updated_successfully": "Database succesvol bijgewerkt",
|
||||
"connected_to_database": "Verbonden met database",
|
||||
"updating_pair": "Sleutel-waarde paar bijwerken",
|
||||
"db_not_found": "Databasebestand niet gevonden op: {path}",
|
||||
"db_permission_error": "Kan geen toegang krijgen tot databasebestand. Controleer de rechten",
|
||||
"db_connection_error": "Verbinding met database mislukt: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Nieuw e-mailadres genereren",
|
||||
"blocked_domain": "Geblokkeerd domein",
|
||||
"select_domain": "Willekeurig domein selecteren",
|
||||
"copy_email": "E-mailadres kopiëren",
|
||||
"enter_mailbox": "Mailbox betreden",
|
||||
"refresh_mailbox": "Mailbox vernieuwen",
|
||||
"check_verification": "Verificatiecode controleren",
|
||||
"verification_found": "Verificatiecode gevonden",
|
||||
"verification_not_found": "Geen verificatiecode gevonden",
|
||||
"browser_error": "Browserbesturingsfout: {error}",
|
||||
"navigation_error": "Navigatiefout: {error}",
|
||||
"email_copy_error": "E-mailkopieerfout: {error}",
|
||||
"mailbox_error": "Mailboxfout: {error}",
|
||||
"token_saved_to_file": "Token opgeslagen in cursor_tokens.txt",
|
||||
"navigate_to": "Navigeren naar {url}",
|
||||
"generate_email_success": "E-mailadres succesvol gegenereerd",
|
||||
"select_email_domain": "E-maildomein selecteren",
|
||||
"select_email_domain_success": "E-maildomein succesvol geselecteerd",
|
||||
"get_email_name": "E-mailnaam verkrijgen",
|
||||
"get_email_name_success": "E-mailnaam succesvol verkregen",
|
||||
"get_email_address": "E-mailadres verkrijgen",
|
||||
"get_email_address_success": "E-mailadres succesvol verkregen",
|
||||
"enter_mailbox_success": "Mailbox succesvol betreden",
|
||||
"found_verification_code": "Verificatiecode gevonden",
|
||||
"get_cursor_session_token": "Cursor-sessietoken verkrijgen",
|
||||
"get_cursor_session_token_success": "Cursor-sessietoken succesvol verkregen",
|
||||
"get_cursor_session_token_failed": "Cursor-sessietoken verkrijgen mislukt",
|
||||
"save_token_failed": "Token opslaan mislukt",
|
||||
"database_updated_successfully": "Database succesvol bijgewerkt",
|
||||
"database_connection_closed": "Databaseverbinding gesloten",
|
||||
"no_valid_verification_code": "Geen geldige verificatiecode"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Browser starten",
|
||||
"visiting_site": "Bezoek mail domains",
|
||||
"create_success": "E-mail succesvol aangemaakt",
|
||||
"create_failed": "E-mail aanmaken mislukt",
|
||||
"create_error": "E-mail aanmaakfout: {error}",
|
||||
"refreshing": "E-mail vernieuwen",
|
||||
"refresh_success": "E-mail succesvol vernieuwd",
|
||||
"refresh_error": "E-mail vernieuwingsfout: {error}",
|
||||
"refresh_button_not_found": "Vernieuwknop niet gevonden",
|
||||
"verification_found": "Verificatie gevonden",
|
||||
"verification_not_found": "Verificatie niet gevonden",
|
||||
"verification_error": "Verificatiefout: {error}",
|
||||
"verification_code_found": "Verificatiecode gevonden",
|
||||
"verification_code_not_found": "Verificatiecode niet gevonden",
|
||||
"verification_code_error": "Verificatiecodefout: {error}",
|
||||
"address": "E-mailadres",
|
||||
"all_domains_blocked": "Alle domeinen geblokkeerd, service wisselen",
|
||||
"no_available_domains_after_filtering": "Geen beschikbare domeinen na filteren",
|
||||
"switching_service": "Wisselen naar {service} service",
|
||||
"domains_list_error": "Domeinenlijst verkrijgen mislukt: {error}",
|
||||
"failed_to_get_available_domains": "Verkrijgen van beschikbare domeinen mislukt",
|
||||
"domains_excluded": "Uitgesloten domeinen: {domains}",
|
||||
"failed_to_create_account": "Account aanmaken mislukt",
|
||||
"account_creation_error": "Account aanmaakfout: {error}",
|
||||
"domain_blocked": "Domein geblokkeerd: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Cursor automatische update uitschakelen",
|
||||
"disable_success": "Automatische update is uitgeschakeld",
|
||||
"disable_failed": "Automatische update uitschakelen mislukt: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan",
|
||||
"start_disable": "Automatische update uitschakelen starten",
|
||||
"killing_processes": "Processen verwijderen",
|
||||
"processes_killed": "Processen verwijderd",
|
||||
"removing_directory": "Map verwijderen",
|
||||
"directory_removed": "Map verwijderd",
|
||||
"creating_block_file": "Blokkeerbestand aanmaken",
|
||||
"block_file_created": "Blokkeerbestand aangemaakt"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Controleren op updates...",
|
||||
"new_version_available": "Er is een nieuwe versie beschikbaar! (Huidige versie: {current}, Laatste versie: {latest})",
|
||||
"updating": "Aan het bijwerken naar de nieuwste versie. Het programma zal automatisch herstart worden.",
|
||||
"up_to_date": "U gebruikt de nieuwste versie.",
|
||||
"check_failed": "Controle op updates mislukt: {error}",
|
||||
"continue_anyway": "Doorgaan met de huidige versie...",
|
||||
"update_confirm": "Wil je de nieuwste versie gebruiken? (Y/n)",
|
||||
"update_skipped": "Update overgeslagen.",
|
||||
"invalid_choice": "Ongeldige keuze. Voer 'Y' of 'n' in.",
|
||||
"development_version": "Ontwikkelversie {current} > {latest}",
|
||||
"changelog_title": "Wijzigingslogboek"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Cursor volledig herstellen",
|
||||
"checking_config": "Configuratiebestand controleren",
|
||||
"config_not_found": "Configuratiebestand niet gevonden",
|
||||
"no_permission": "Kan geen toegang krijgen tot configuratiebestand. Controleer de rechten",
|
||||
"reading_config": "Huidige configuratie lezen",
|
||||
"creating_backup": "Configuratie-back-up aanmaken",
|
||||
"backup_exists": "Back-up bestand bestaat, back-up stap overgeslagen",
|
||||
"generating_new_machine_id": "Nieuwe machine-ID genereren",
|
||||
"saving_new_config": "Nieuwe configuratie opslaan als JSON",
|
||||
"success": "Cursor succesvol hersteld",
|
||||
"error": "Cursor herstellen mislukt: {error}",
|
||||
"press_enter": "Druk op Enter om door te gaan",
|
||||
"reset_machine_id": "Machine-ID resetten",
|
||||
"database_connection_closed": "Databaseverbinding gesloten",
|
||||
"database_updated_successfully": "Database succesvol bijgewerkt",
|
||||
"connected_to_database": "Verbonden met database",
|
||||
"updating_pair": "Sleutel-waarde paar bijwerken",
|
||||
"db_not_found": "Databasebestand niet gevonden op: {path}",
|
||||
"db_permission_error": "Kan geen toegang krijgen tot databasebestand. Controleer de rechten",
|
||||
"db_connection_error": "Verbinding met database mislukt: {error}",
|
||||
"feature_title": "Functiebeschrijving",
|
||||
"feature_1": "Compleet verwijderen van Cursor AI-instellingen en configuratie",
|
||||
"feature_2": "Alle cachegegevens, inclusief AI-geschiedenis en prompts",
|
||||
"feature_3": "Machine-ID resetten om de proefperiode te omzeilen",
|
||||
"feature_4": "Nieuwe willekeurige machine-ID maken",
|
||||
"feature_5": "Aangepaste extensies en voorkeuren verwijderen",
|
||||
"feature_6": "Proefperiode- en activatiegegevens resetten",
|
||||
"feature_7": "Diepe scan voor verborgen licentie- en proefperiodebestanden",
|
||||
"feature_8": "Beveiligde niet-Cursor-bestanden en -toepassingen behouden",
|
||||
"feature_9": "Compatibel met Windows, macOS en Linux",
|
||||
"disclaimer_title": "Disclaimer",
|
||||
"disclaimer_1": "Deze tool verwijdert alle Cursor AI-instellingen,",
|
||||
"disclaimer_2": "configuratie en cachegegevens. Deze actie is niet ongedaan te maken.",
|
||||
"disclaimer_3": "Uw codebestanden **worden niet** beïnvloed, de tool is bedoeld om",
|
||||
"disclaimer_4": "Alleen gericht op Cursor AI-editorbestanden en proefperiodecontrolemechanisme.",
|
||||
"disclaimer_5": "Andere systemtoepassingen worden niet beïnvloed.",
|
||||
"disclaimer_6": "Na het uitvoeren van deze tool moet u Cursor AI opnieuw instellen.",
|
||||
"disclaimer_7": "U accepteert de risico's zelf.",
|
||||
"confirm_title": "Weet u zeker dat u wilt doorgaan?",
|
||||
"confirm_1": "Deze actie verwijdert alle Cursor AI-instellingen,",
|
||||
"confirm_2": "configuratie en cachegegevens. Deze actie is niet ongedaan te maken.",
|
||||
"confirm_3": "Uw codebestanden **worden niet** beïnvloed, de tool is bedoeld om",
|
||||
"confirm_4": "Alleen gericht op Cursor AI-editorbestanden en proefperiodecontrolemechanisme.",
|
||||
"confirm_5": "Andere systemtoepassingen worden niet beïnvloed.",
|
||||
"confirm_6": "Na het uitvoeren van deze tool moet u Cursor AI opnieuw instellen.",
|
||||
"confirm_7": "U accepteert de risico's zelf.",
|
||||
"invalid_choice": "Ongeldige keuze. Voer 'Y' of 'n' in.",
|
||||
"skipped_for_safety": "Uit veiligheidsoverwegingen overgeslagen (niet Cursor-gerelateerd): {path}",
|
||||
"deleted": "Verwijderd: {path}",
|
||||
"error_deleting": "Verwijdering van {path} mislukt: {error}",
|
||||
"not_found": "Bestand niet gevonden: {path}",
|
||||
"resetting_machine_id": "Machine-ID resetten om proefperiode te omzeilen...",
|
||||
"created_machine_id": "Nieuwe machine-ID gemaakt: {path}",
|
||||
"error_creating_machine_id": "Machine-ID-bestand {path} aanmaken mislukt: {error}",
|
||||
"error_searching": "Fout bij het zoeken naar bestand {path}: {error}",
|
||||
"created_extended_trial_info": "Nieuwe uitgebreide proefperiode-informatie gemaakt: {path}",
|
||||
"error_creating_trial_info": "Fout bij het aanmaken van proefperiode-informatiebestand {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Cursor AI-editor resetten... Wacht even.",
|
||||
"reset_cancelled": "Reset geannuleerd, geen wijzigingen aangebracht.",
|
||||
"windows_machine_id_modification_skipped": "Windows machine-ID-aanpassing overgeslagen: {error}",
|
||||
"linux_machine_id_modification_skipped": "Linux machine-id-aanpassing overgeslagen: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Opmerking: Compleet machine-ID-reset kan vereisen dat u als beheerder uitvoert",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Opmerking: Compleet systeem machine-ID-reset kan vereisen dat u sudo-rechten hebt",
|
||||
"windows_registry_instructions": "📝 Opmerking: Compleet machine-ID-reset kan vereisen dat u als beheerder uitvoert",
|
||||
"windows_registry_instructions_2": " Run 'regedit' en zoek naar de sleutels 'Cursor' of 'CursorAI' in HKEY_CURRENT_USER\\Software\\ en verwijder ze.\n",
|
||||
"reset_log_1": "Cursor AI is volledig hersteld en heeft de proefperiode omzeild!",
|
||||
"reset_log_2": "Start het systeem opnieuw om de wijzigingen te activeren.",
|
||||
"reset_log_3": "U moet Cursor AI opnieuw installeren, er zou nu een nieuwe proefperiode moeten zijn.",
|
||||
"reset_log_4": "Voor de beste resultaten raden we aan ook:",
|
||||
"reset_log_5": "Nieuwe proefperiode registreren met een ander e-mailadres",
|
||||
"reset_log_6": "Als mogelijk, VPN gebruiken om IP-adres te wijzigen",
|
||||
"reset_log_7": "Voordat u naar de Cursor AI-website gaat, verwijdert u de cookies en cache van uw browser",
|
||||
"reset_log_8": "Als het nog steeds niet werkt, probeert u Cursor AI op een andere locatie te installeren",
|
||||
"reset_log_9": "Als u eventuele problemen ondervindt, stuurt u een probleem naar de Github Issue Tracker: https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Onverwachte fout: {error}",
|
||||
"report_issue": "Rapporteer dit probleem op de Github Issue Tracker: https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Gebruiker heeft proces afgebroken, afsluiten...",
|
||||
"return_to_main_menu": "Terug naar hoofdmenu...",
|
||||
"process_interrupted": "Proces afgebroken, afsluiten...",
|
||||
"press_enter_to_return_to_main_menu": "Druk op Enter om terug te gaan naar het hoofdmenu...",
|
||||
"removing_known": "Het verwijderen van bekende proefperiode- en licentiebestanden...",
|
||||
"performing_deep_scan": "Een diepe scan wordt uitgevoerd om andere proefperiode- en licentiebestanden te vinden...",
|
||||
"found_additional_potential_license_trial_files": "Er zijn {count} andere potentiële proefperiode- en licentiebestanden gevonden",
|
||||
"checking_for_electron_localstorage_files": "Controleren op Electron localStorage-bestanden...",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Geen andere proefperiode- of licentiebestanden gevonden in diepe scan",
|
||||
"removing_electron_localstorage_files": "Het verwijderen van Electron localStorage-bestanden...",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
387
locales/pt.json
Normal file
@@ -0,0 +1,387 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Opções Disponíveis",
|
||||
"exit": "Sair do Programa",
|
||||
"reset": "Redefinir ID da Máquina",
|
||||
"register": "Registrar Nova Conta no Cursor",
|
||||
"register_google": "Registrar com Conta do Google",
|
||||
"register_github": "Registrar com Conta do GitHub",
|
||||
"register_manual": "Registrar Cursor com E-mail Personalizado",
|
||||
"quit": "Fechar Cursor",
|
||||
"select_language": "Alterar Idioma",
|
||||
"input_choice": "Por favor, insira sua escolha ({choices})",
|
||||
"invalid_choice": "Seleção inválida. Insira um número de {choices}",
|
||||
"program_terminated": "Programa encerrado pelo usuário",
|
||||
"error_occurred": "Ocorreu um erro: {error}. Por favor, tente novamente",
|
||||
"press_enter": "Pressione Enter para Sair",
|
||||
"disable_auto_update": "Desativar Atualização Automática do Cursor",
|
||||
"lifetime_access_enabled": "ACESSO VITALÍCIO HABILITADO",
|
||||
"totally_reset": "Redefinir Cursor Completamente",
|
||||
"outdate": "Obsoleto",
|
||||
"temp_github_register": "Registro temporário do GitHub",
|
||||
"coming_soon": "Em breve"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Inglês",
|
||||
"zh_cn": "Chinês Simplificado",
|
||||
"zh_tw": "Chinês Tradicional",
|
||||
"vi": "Vietnamita",
|
||||
"nl": "Holandês",
|
||||
"de": "Alemão",
|
||||
"fr": "Francês",
|
||||
"pt": "Português do Brasil",
|
||||
"ru": "Russo",
|
||||
"es": "Espanhol"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Iniciando fechamento do Cursor",
|
||||
"no_process": "Nenhum processo do Cursor em execução",
|
||||
"terminating": "Encerrando processo {pid}",
|
||||
"waiting": "Aguardando o processo ser finalizado",
|
||||
"success": "Todos os processos do Cursor foram encerrados",
|
||||
"timeout": "Tempo limite do processo: {pids}",
|
||||
"error": "Ocorreu um erro: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Ferramenta de Redefinição de ID da Máquina",
|
||||
"checking": "Verificando arquivo de configuração",
|
||||
"not_found": "Arquivo de configuração não encontrado",
|
||||
"no_permission": "Não é possível ler ou escrever no arquivo de configuração. Verifique as permissões do arquivo",
|
||||
"reading": "Lendo configuração atual",
|
||||
"creating_backup": "Criando backup da configuração",
|
||||
"backup_exists": "Arquivo de backup já existe, pulando etapa de backup",
|
||||
"generating": "Gerando novo ID da máquina",
|
||||
"saving_json": "Salvando nova configuração no JSON",
|
||||
"success": "ID da Máquina redefinido com sucesso",
|
||||
"new_id": "Novo ID da Máquina",
|
||||
"permission_error": "Erro de permissão: {error}",
|
||||
"run_as_admin": "Tente executar este programa como Administrador",
|
||||
"process_error": "Erro no processo de redefinição: {error}",
|
||||
"updating_sqlite": "Atualizando banco de dados SQLite",
|
||||
"updating_pair": "Atualizando chave-valor",
|
||||
"sqlite_success": "Banco de dados SQLite atualizado com sucesso",
|
||||
"sqlite_error": "Falha na atualização do banco de dados SQLite: {error}",
|
||||
"press_enter": "Pressione Enter para sair",
|
||||
"unsupported_os": "Sistema operacional não suportado: {os}",
|
||||
"linux_path_not_found": "Caminho do Linux não encontrado",
|
||||
"updating_system_ids": "Atualizando IDs do sistema",
|
||||
"system_ids_updated": "IDs do sistema atualizados com sucesso",
|
||||
"system_ids_update_failed": "Falha na atualização dos IDs do sistema: {error}",
|
||||
"windows_guid_updated": "GUID do Windows atualizado com sucesso",
|
||||
"windows_permission_denied": "Permissão negada no Windows",
|
||||
"windows_guid_update_failed": "Falha na atualização do GUID do Windows",
|
||||
"macos_uuid_updated": "UUID do macOS atualizado com sucesso",
|
||||
"plutil_command_failed": "Falha no comando plutil",
|
||||
"start_patching": "Iniciando correção de getMachineId",
|
||||
"macos_uuid_update_failed": "Falha na atualização do UUID do macOS",
|
||||
"current_version": "Versão atual do Cursor: {version}",
|
||||
"patch_completed": "Correção de getMachineId concluída",
|
||||
"patch_failed": "Falha na correção de getMachineId: {error}",
|
||||
"version_check_passed": "Verificação de versão do Cursor aprovada",
|
||||
"file_modified": "Arquivo modificado",
|
||||
"version_less_than_0_45": "Versão do Cursor < 0.45.0, pulando correção de getMachineId",
|
||||
"detecting_version": "Detectando versão do Cursor",
|
||||
"patching_getmachineid": "Corrigindo getMachineId",
|
||||
"version_greater_than_0_45": "Versão do Cursor >= 0.45.0, corrigindo getMachineId",
|
||||
"permission_denied": "Permissão negada: {error}",
|
||||
"backup_created": "Backup criado",
|
||||
"update_success": "Atualização concluída com sucesso",
|
||||
"update_failed": "Falha na atualização: {error}",
|
||||
"windows_machine_guid_updated": "GUID da máquina do Windows atualizado com sucesso",
|
||||
"reading_package_json": "Lendo package.json {path}",
|
||||
"invalid_json_object": "Objeto JSON inválido",
|
||||
"no_version_field": "Campo de versão não encontrado no package.json",
|
||||
"version_field_empty": "Campo de versão está vazio",
|
||||
"invalid_version_format": "Formato de versão inválido: {version}",
|
||||
"found_version": "Versão encontrada: {version}",
|
||||
"version_parse_error": "Erro ao analisar versão: {error}",
|
||||
"package_not_found": "Package.json não encontrado: {path}",
|
||||
"check_version_failed": "Falha ao verificar versão: {error}",
|
||||
"stack_trace": "Rastreamento de pilha",
|
||||
"version_too_low": "Versão do Cursor muito baixa: {version} < 0.45.0"
|
||||
},
|
||||
"register": {
|
||||
"title": "Ferramenta de Registro do Cursor",
|
||||
"start": "Iniciando o processo de registro...",
|
||||
"handling_turnstile": "Processando verificação de segurança...",
|
||||
"retry_verification": "Tentando novamente a verificação...",
|
||||
"detect_turnstile": "Verificando validação de segurança...",
|
||||
"verification_success": "Verificação de segurança bem-sucedida",
|
||||
"starting_browser": "Abrindo navegador...",
|
||||
"form_success": "Formulário enviado com sucesso",
|
||||
"browser_started": "Navegador aberto com sucesso",
|
||||
"waiting_for_second_verification": "Aguardando verificação por e-mail...",
|
||||
"waiting_for_verification_code": "Aguardando código de verificação...",
|
||||
"password_success": "Senha definida com sucesso",
|
||||
"password_error": "Não foi possível definir a senha: {error}. Por favor, tente novamente",
|
||||
"waiting_for_page_load": "Carregando página...",
|
||||
"first_verification_passed": "Verificação inicial bem-sucedida",
|
||||
"mailbox": "Caixa de entrada de e-mail acessada com sucesso",
|
||||
"register_start": "Iniciar Registro",
|
||||
"form_submitted": "Formulário Enviado, Iniciando Verificação...",
|
||||
"filling_form": "Preenchendo Formulário",
|
||||
"visiting_url": "Visitando URL",
|
||||
"basic_info": "Informações básicas enviadas",
|
||||
"handle_turnstile": "Processar Turnstile",
|
||||
"no_turnstile": "Turnstile Não Detectado",
|
||||
"turnstile_passed": "Turnstile Passado",
|
||||
"verification_start": "Iniciando Obtenção do Código de Verificação",
|
||||
"verification_timeout": "Tempo Esgotado para Obter Código de Verificação",
|
||||
"verification_not_found": "Nenhum Código de Verificação Encontrado",
|
||||
"try_get_code": "Tentativa | {attempt} Obter Código de Verificação | Tempo Restante: {time}s",
|
||||
"get_account": "Obtendo Informações da Conta",
|
||||
"get_token": "Obtendo Token da Sessão do Cursor",
|
||||
"token_success": "Token Obtido com Sucesso",
|
||||
"token_attempt": "Tentativa | {attempt} de obter o Token | Tentando novamente em {time}s",
|
||||
"token_max_attempts": "Número máximo de tentativas atingido ({max}) | Falha ao obter o Token",
|
||||
"token_failed": "Falha ao Obter Token: {error}",
|
||||
"account_error": "Falha ao Obter Informações da Conta: {error}",
|
||||
"press_enter": "Pressione Enter para sair",
|
||||
"browser_start": "Iniciando Navegador",
|
||||
"open_mailbox": "Abrindo Página da Caixa de Entrada",
|
||||
"email_error": "Falha ao obter endereço de e-mail",
|
||||
"setup_error": "Erro de configuração do e-mail: {error}",
|
||||
"start_getting_verification_code": "Iniciando obtenção do código de verificação, tentará em 60s",
|
||||
"get_verification_code_timeout": "Tempo Esgotado para Obter Código de Verificação",
|
||||
"get_verification_code_success": "Código de Verificação Obtido com Sucesso",
|
||||
"try_get_verification_code": "Tentativa | {attempt} Obter Código de Verificação | Tempo Restante: {remaining_time}s",
|
||||
"verification_code_filled": "Código de Verificação Preenchido",
|
||||
"login_success_and_jump_to_settings_page": "Login bem-sucedido, indo para a página de configurações",
|
||||
"detect_login_page": "Página de login detectada, iniciando login...",
|
||||
"cursor_registration_completed": "Registro do Cursor Concluído!",
|
||||
"set_password": "Definir Senha",
|
||||
"basic_info_submitted": "Informações Básicas Enviadas",
|
||||
"cursor_auth_info_updated": "Informações de Autenticação do Cursor Atualizadas",
|
||||
"cursor_auth_info_update_failed": "Falha ao Atualizar Informações de Autenticação do Cursor",
|
||||
"reset_machine_id": "Reiniciar ID da Máquina",
|
||||
"account_info_saved": "Informações da Conta Salvas",
|
||||
"save_account_info_failed": "Falha ao Salvar Informações da Conta",
|
||||
"get_email_address": "Obtendo Endereço de E-mail",
|
||||
"update_cursor_auth_info": "Atualizar Informações de Autenticação do Cursor",
|
||||
"register_process_error": "Erro no Processo de Registro: {error}",
|
||||
"setting_password": "Configurando Senha",
|
||||
"manual_code_input": "Inserção Manual do Código",
|
||||
"manual_email_input": "Inserção Manual de E-mail",
|
||||
"password": "Senha",
|
||||
"first_name": "Nome",
|
||||
"last_name": "Sobrenome",
|
||||
"exit_signal": "Sinal para Sair",
|
||||
"email_address": "Endereço de E-mail",
|
||||
"config_created": "Configuração Criada",
|
||||
"verification_failed": "Falha na Verificação",
|
||||
"verification_error": "Erro de Verificação: {error}",
|
||||
"config_option_added": "Opção de Configuração Adicionada: {option}",
|
||||
"config_updated": "Configuração Atualizada",
|
||||
"password_submitted": "Senha Enviada",
|
||||
"total_usage": "Uso Total: {usage}",
|
||||
"setting_on_password": "Configurando Senha",
|
||||
"getting_code": "Obtendo Código de Verificação, Tentará em 60s"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Gerenciador de Autenticação do Cursor",
|
||||
"checking_auth": "Verificando arquivo de autenticação",
|
||||
"auth_not_found": "Arquivo de autenticação não encontrado",
|
||||
"auth_file_error": "Erro no arquivo de autenticação: {error}",
|
||||
"reading_auth": "Lendo arquivo de autenticação",
|
||||
"updating_auth": "Atualizando informações de autenticação",
|
||||
"auth_updated": "Informações de autenticação atualizadas com sucesso",
|
||||
"auth_update_failed": "Falha ao atualizar informações de autenticação: {error}",
|
||||
"auth_file_created": "Arquivo de autenticação criado",
|
||||
"auth_file_create_failed": "Falha ao criar arquivo de autenticação: {error}",
|
||||
"press_enter": "Pressione Enter para sair",
|
||||
"reset_machine_id": "Redefinir ID da máquina",
|
||||
"database_connection_closed": "Conexão com o banco de dados fechada",
|
||||
"database_updated_successfully": "Banco de dados atualizado com sucesso",
|
||||
"connected_to_database": "Conectado ao banco de dados",
|
||||
"updating_pair": "Atualizando par chave-valor",
|
||||
"db_not_found": "Arquivo do banco de dados não encontrado em: {path}",
|
||||
"db_permission_error": "Não é possível acessar o arquivo do banco de dados. Verifique as permissões",
|
||||
"db_connection_error": "Falha ao conectar ao banco de dados: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Gerando novo e-mail",
|
||||
"blocked_domain": "Domínio bloqueado",
|
||||
"select_domain": "Selecionando domínio aleatório",
|
||||
"copy_email": "Copiando endereço de e-mail",
|
||||
"enter_mailbox": "Entrando na caixa de entrada",
|
||||
"refresh_mailbox": "Atualizando caixa de entrada",
|
||||
"check_verification": "Verificando código de verificação",
|
||||
"verification_found": "Código de verificação encontrado",
|
||||
"verification_not_found": "Nenhum código de verificação encontrado",
|
||||
"browser_error": "Erro no controle do navegador: {error}",
|
||||
"navigation_error": "Erro de navegação: {error}",
|
||||
"email_copy_error": "Erro ao copiar e-mail: {error}",
|
||||
"mailbox_error": "Erro na caixa de entrada: {error}",
|
||||
"token_saved_to_file": "Token salvo em cursor_tokens.txt",
|
||||
"navigate_to": "Navegando para {url}",
|
||||
"generate_email_success": "E-mail gerado com sucesso",
|
||||
"select_email_domain": "Selecionar domínio de e-mail",
|
||||
"select_email_domain_success": "Domínio de e-mail selecionado com sucesso",
|
||||
"get_email_name": "Obtendo nome do e-mail",
|
||||
"get_email_name_success": "Nome do e-mail obtido com sucesso",
|
||||
"get_email_address": "Obtendo endereço de e-mail",
|
||||
"get_email_address_success": "Endereço de e-mail obtido com sucesso",
|
||||
"enter_mailbox_success": "Entrada na caixa de entrada bem-sucedida",
|
||||
"found_verification_code": "Código de verificação encontrado",
|
||||
"get_cursor_session_token": "Obtendo token da sessão do Cursor",
|
||||
"get_cursor_session_token_success": "Token da sessão do Cursor obtido com sucesso",
|
||||
"get_cursor_session_token_failed": "Falha ao obter token da sessão do Cursor",
|
||||
"save_token_failed": "Falha ao salvar o token",
|
||||
"database_updated_successfully": "Banco de dados atualizado com sucesso",
|
||||
"database_connection_closed": "Conexão com o banco de dados fechada",
|
||||
"no_valid_verification_code": "Nenhum código de verificação válido"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Iniciando navegador",
|
||||
"visiting_site": "Visitando domínios de e-mail",
|
||||
"create_success": "E-mail criado com sucesso",
|
||||
"create_failed": "Falha ao criar e-mail",
|
||||
"create_error": "Erro ao criar e-mail: {error}",
|
||||
"refreshing": "Atualizando e-mail",
|
||||
"refresh_success": "E-mail atualizado com sucesso",
|
||||
"refresh_error": "Erro ao atualizar e-mail: {error}",
|
||||
"refresh_button_not_found": "Botão de atualização não encontrado",
|
||||
"verification_found": "Verificação encontrada",
|
||||
"verification_not_found": "Verificação não encontrada",
|
||||
"verification_error": "Erro na verificação: {error}",
|
||||
"verification_code_found": "Código de verificação encontrado",
|
||||
"verification_code_not_found": "Código de verificação não encontrado",
|
||||
"verification_code_error": "Erro no código de verificação: {error}",
|
||||
"address": "Endereço de e-mail",
|
||||
"all_domains_blocked": "Todos os domínios bloqueados, alternando serviço",
|
||||
"no_available_domains_after_filtering": "Nenhum domínio disponível após filtragem",
|
||||
"switching_service": "Alternando para o serviço {service}",
|
||||
"domains_list_error": "Falha ao obter lista de domínios: {error}",
|
||||
"failed_to_get_available_domains": "Falha ao obter domínios disponíveis",
|
||||
"domains_excluded": "Domínios excluídos: {domains}",
|
||||
"failed_to_create_account": "Falha ao criar conta",
|
||||
"account_creation_error": "Erro na criação da conta: {error}",
|
||||
"blocked_domains": "Domínios bloqueados: {domains}",
|
||||
"blocked_domains_loaded": "Domínios bloqueados carregados: {count}",
|
||||
"blocked_domains_loaded_error": "Erro ao carregar domínios bloqueados: {error}",
|
||||
"blocked_domains_loaded_success": "Domínios bloqueados carregados com sucesso",
|
||||
"blocked_domains_loaded_timeout": "Tempo esgotado ao carregar domínios bloqueados: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Erro de tempo esgotado ao carregar domínios bloqueados: {error}",
|
||||
"available_domains_loaded": "Domínios disponíveis carregados: {count}",
|
||||
"domains_filtered": "Domínios filtrados: {count}",
|
||||
"trying_to_create_email": "Tentando criar e-mail: {email}",
|
||||
"domain_blocked": "Domínio bloqueado: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Desativar atualização automática do Cursor",
|
||||
"disable_success": "Atualização automática desativada com sucesso",
|
||||
"disable_failed": "Falha ao desativar atualização automática: {error}",
|
||||
"press_enter": "Pressione Enter para sair",
|
||||
"start_disable": "Iniciando desativação da atualização automática",
|
||||
"killing_processes": "Finalizando processos",
|
||||
"processes_killed": "Processos finalizados",
|
||||
"removing_directory": "Removendo diretório",
|
||||
"directory_removed": "Diretório removido",
|
||||
"creating_block_file": "Criando arquivo de bloqueio",
|
||||
"block_file_created": "Arquivo de bloqueio criado"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Verificando atualizações...",
|
||||
"new_version_available": "Nova versão disponível! (Atual: {current}, Última: {latest})",
|
||||
"updating": "Atualizando para a última versão. O programa será reiniciado automaticamente.",
|
||||
"up_to_date": "Você está usando a versão mais recente.",
|
||||
"check_failed": "Falha ao verificar atualizações: {error}",
|
||||
"continue_anyway": "Continuando com a versão atual...",
|
||||
"update_confirm": "Deseja atualizar para a última versão? (Y/n)",
|
||||
"update_skipped": "Atualização ignorada.",
|
||||
"invalid_choice": "Escolha inválida. Por favor, digite 'Y' ou 'n'.",
|
||||
"development_version": "Versão de desenvolvimento {current} > {latest}",
|
||||
"changelog_title": "Registro de alterações"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Redefinir Cursor Completamente",
|
||||
"checking_config": "Verificando Arquivo de Configuração",
|
||||
"config_not_found": "Arquivo de Configuração Não Encontrado",
|
||||
"no_permission": "Não é possível Ler ou Escrever o Arquivo de Configuração, Verifique as Permissões do Arquivo",
|
||||
"reading_config": "Lendo Configuração Atual",
|
||||
"creating_backup": "Criando Backup da Configuração",
|
||||
"backup_exists": "Arquivo de Backup Já Existe, Pulando Etapa de Backup",
|
||||
"generating_new_machine_id": "Gerando Novo ID da Máquina",
|
||||
"saving_new_config": "Salvando Nova Configuração no JSON",
|
||||
"success": "Cursor Redefinido com Sucesso",
|
||||
"error": "Falha ao Redefinir Cursor: {error}",
|
||||
"press_enter": "Pressione Enter para Sair",
|
||||
"reset_machine_id": "Redefinir ID da Máquina",
|
||||
"database_connection_closed": "Conexão com o Banco de Dados Fechada",
|
||||
"database_updated_successfully": "Banco de Dados Atualizado com Sucesso",
|
||||
"connected_to_database": "Conectado ao Banco de Dados",
|
||||
"updating_pair": "Atualizando Par Chave-Valor",
|
||||
"db_not_found": "Arquivo de banco de dados não encontrado em: {path}",
|
||||
"db_permission_error": "Não é possível acessar o arquivo do banco de dados. Verifique as permissões",
|
||||
"db_connection_error": "Falha ao conectar ao banco de dados: {error}",
|
||||
"feature_title": "RECURSOS",
|
||||
"feature_1": "Remoção completa das configurações e preferências do Cursor AI",
|
||||
"feature_2": "Limpa todos os dados em cache, incluindo histórico e prompts de IA",
|
||||
"feature_3": "Redefine o ID da máquina para contornar a detecção de período de teste",
|
||||
"feature_4": "Cria novos identificadores de máquina aleatórios",
|
||||
"feature_5": "Remove extensões e preferências personalizadas",
|
||||
"feature_6": "Redefine informações de período de teste e dados de ativação",
|
||||
"feature_7": "Varredura profunda por arquivos ocultos relacionados à licença e período de teste",
|
||||
"feature_8": "Preserva com segurança arquivos e aplicativos não relacionados ao Cursor",
|
||||
"feature_9": "Compatível com Windows, macOS e Linux",
|
||||
"disclaimer_title": "AVISO",
|
||||
"disclaimer_1": "Esta ferramenta excluirá permanentemente todas as configurações,",
|
||||
"disclaimer_2": "preferências e dados em cache do Cursor AI. Essa ação não pode ser desfeita.",
|
||||
"disclaimer_3": "Seus arquivos de código NÃO serão afetados, e a ferramenta é projetada",
|
||||
"disclaimer_4": "para atingir somente os arquivos do editor Cursor AI e mecanismos de detecção de teste.",
|
||||
"disclaimer_5": "Outros aplicativos em seu sistema não serão afetados.",
|
||||
"disclaimer_6": "Será necessário configurar o Cursor AI novamente após executar esta ferramenta.",
|
||||
"disclaimer_7": "Use por sua conta e risco",
|
||||
"confirm_title": "Tem certeza que deseja prosseguir?",
|
||||
"confirm_1": "Esta ação excluirá todas as configurações do Cursor AI,",
|
||||
"confirm_2": "preferências e dados em cache. Essa ação não pode ser desfeita.",
|
||||
"confirm_3": "Seus arquivos de código NÃO serão afetados, e a ferramenta é projetada",
|
||||
"confirm_4": "para atingir somente os arquivos do editor Cursor AI e mecanismos de detecção de teste.",
|
||||
"confirm_5": "Outros aplicativos em seu sistema não serão afetados.",
|
||||
"confirm_6": "Será necessário configurar o Cursor AI novamente após executar esta ferramenta.",
|
||||
"confirm_7": "Use por sua conta e risco",
|
||||
"invalid_choice": "Por favor, digite 'Y' ou 'n'",
|
||||
"skipped_for_safety": "Ignorado por segurança (não relacionado ao Cursor): {path}",
|
||||
"deleted": "Excluído: {path}",
|
||||
"error_deleting": "Erro ao excluir {path}: {error}",
|
||||
"not_found": "Arquivo não encontrado: {path}",
|
||||
"resetting_machine_id": "Redefinindo identificadores da máquina para contornar a detecção de período de teste...",
|
||||
"created_machine_id": "Novo ID da máquina criado: {path}",
|
||||
"error_creating_machine_id": "Erro ao criar arquivo de ID da máquina {path}: {error}",
|
||||
"error_searching": "Erro ao procurar arquivos em {path}: {error}",
|
||||
"created_extended_trial_info": "Novas informações de período de teste criadas: {path}",
|
||||
"error_creating_trial_info": "Erro ao criar arquivo de informações de teste {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Redefinindo Editor Cursor AI... Por favor, aguarde.",
|
||||
"reset_cancelled": "Redefinição cancelada. Saindo sem realizar alterações.",
|
||||
"windows_machine_id_modification_skipped": "Modificação de ID da máquina no Windows ignorada: {error}",
|
||||
"linux_machine_id_modification_skipped": "Modificação do machine-id do Linux ignorada: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Nota: Redefinir totalmente o ID da máquina pode exigir a execução como administrador",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Nota: Redefinir totalmente o machine-id do sistema pode exigir privilégios sudo",
|
||||
"windows_registry_instructions": "📝 NOTA: Para uma redefinição completa no Windows, talvez você precise também limpar entradas do registro.",
|
||||
"windows_registry_instructions_2": " Execute 'regedit', pesquise chaves contendo 'Cursor' ou 'CursorAI' em HKEY_CURRENT_USER\\Software\\ e exclua-as.\n",
|
||||
"reset_log_1": "Cursor AI foi completamente redefinido e a detecção de teste foi contornada!",
|
||||
"reset_log_2": "Por favor, reinicie o sistema para que as alterações tenham efeito.",
|
||||
"reset_log_3": "Você precisará reinstalar o Cursor AI e deverá ter um novo período de teste disponível.",
|
||||
"reset_log_4": "Para melhores resultados, considere também:",
|
||||
"reset_log_5": "Utilizar um endereço de e-mail diferente ao registrar um novo período de teste",
|
||||
"reset_log_6": "Se disponível, utilizar uma VPN para alterar seu endereço IP",
|
||||
"reset_log_7": "Limpar cookies e cache do navegador antes de acessar o site do Cursor AI",
|
||||
"reset_log_8": "Se os problemas persistirem, tente instalar o Cursor AI em outro local",
|
||||
"reset_log_9": "Se encontrar problemas, abra uma issue no Github em https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Ocorreu um erro inesperado: {error}",
|
||||
"report_issue": "Por favor, relate este problema no Github em https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Processo interrompido pelo usuário. Saindo...",
|
||||
"return_to_main_menu": "Retornando ao menu principal...",
|
||||
"process_interrupted": "Processo interrompido. Saindo...",
|
||||
"press_enter_to_return_to_main_menu": "Pressione Enter para retornar ao menu principal...",
|
||||
"removing_known": "Removendo arquivos conhecidos de teste/licença",
|
||||
"performing_deep_scan": "Realizando varredura profunda por arquivos adicionais de teste/licença",
|
||||
"found_additional_potential_license_trial_files": "{count} arquivos adicionais de licença/teste potencialmente encontrados",
|
||||
"checking_for_electron_localstorage_files": "Verificando arquivos localStorage do Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Nenhum arquivo adicional de licença/teste encontrado na varredura profunda",
|
||||
"removing_electron_localstorage_files": "Removendo arquivos localStorage do Electron",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
387
locales/ru.json
Normal file
@@ -0,0 +1,387 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Доступные опции",
|
||||
"exit": "Выйти из программы",
|
||||
"reset": "Сбросить ID машины",
|
||||
"register": "Зарегистрировать новый аккаунт Cursor",
|
||||
"register_google": "Зарегистрироваться через Google",
|
||||
"register_github": "Зарегистрироваться через GitHub",
|
||||
"register_manual": "Зарегистрировать Cursor используя свою почту",
|
||||
"quit": "Закрыть приложение Cursor",
|
||||
"select_language": "Выбрать язык",
|
||||
"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",
|
||||
"coming_soon": "Скоро"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Английский",
|
||||
"zh_cn": "Упрощенный китайский",
|
||||
"zh_tw": "Традиционный китайский",
|
||||
"vi": "Вьетнамский",
|
||||
"nl": "Нидерландский",
|
||||
"de": "Немецкий",
|
||||
"fr": "Французский",
|
||||
"pt": "Бразильский португальский",
|
||||
"ru": "Русский",
|
||||
"es": "Испанский"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Начало закрытия Cursor",
|
||||
"no_process": "Нет запущенных процессов Cursor",
|
||||
"terminating": "Завершение процесса {pid}",
|
||||
"waiting": "Ожидание завершения процесса",
|
||||
"success": "Все процессы Cursor закрыты",
|
||||
"timeout": "Таймаут процесса: {pids}",
|
||||
"error": "Произошла ошибка: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Инструмент сброса ID машины Cursor",
|
||||
"checking": "Проверка конфигурационного файла",
|
||||
"not_found": "Конфигурационный файл не найден",
|
||||
"no_permission": "Невозможно прочитать или записать конфигурационный файл, проверьте права доступа",
|
||||
"reading": "Чтение текущей конфигурации",
|
||||
"creating_backup": "Создание резервной копии конфигурации",
|
||||
"backup_exists": "Резервный файл уже существует, пропускаем шаг резервного копирования",
|
||||
"generating": "Генерация нового ID машины",
|
||||
"saving_json": "Сохранение новой конфигурации в JSON",
|
||||
"success": "ID машины успешно сброшен",
|
||||
"new_id": "Новый ID машины",
|
||||
"permission_error": "Ошибка прав доступа: {error}",
|
||||
"run_as_admin": "Пожалуйста, запустите программу от имени администратора",
|
||||
"process_error": "Ошибка процесса сброса: {error}",
|
||||
"updating_sqlite": "Обновление базы данных SQLite",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"sqlite_success": "База данных SQLite успешно обновлена",
|
||||
"sqlite_error": "Ошибка обновления базы данных SQLite: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"unsupported_os": "Неподдерживаемая ОС: {os}",
|
||||
"linux_path_not_found": "Путь Linux не найден",
|
||||
"updating_system_ids": "Обновление системных ID",
|
||||
"system_ids_updated": "Системные ID успешно обновлены",
|
||||
"system_ids_update_failed": "Ошибка обновления системных ID: {error}",
|
||||
"windows_guid_updated": "Windows GUID успешно обновлен",
|
||||
"windows_permission_denied": "Отказано в доступе Windows",
|
||||
"windows_guid_update_failed": "Ошибка обновления Windows GUID",
|
||||
"macos_uuid_updated": "macOS UUID успешно обновлен",
|
||||
"plutil_command_failed": "Ошибка команды plutil",
|
||||
"start_patching": "Начало патчинга getMachineId",
|
||||
"macos_uuid_update_failed": "Ошибка обновления macOS UUID",
|
||||
"current_version": "Текущая версия 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"
|
||||
},
|
||||
"register": {
|
||||
"title": "Инструмент регистрации Cursor",
|
||||
"start": "Запуск процесса регистрации...",
|
||||
"handling_turnstile": "Обработка проверки безопасности...",
|
||||
"retry_verification": "Повторная попытка проверки...",
|
||||
"detect_turnstile": "Проверка безопасности...",
|
||||
"verification_success": "Проверка безопасности успешна",
|
||||
"starting_browser": "Открытие браузера...",
|
||||
"form_success": "Форма успешно отправлена",
|
||||
"browser_started": "Браузер успешно открыт",
|
||||
"waiting_for_second_verification": "Ожидание проверки email...",
|
||||
"waiting_for_verification_code": "Ожидание кода подтверждения...",
|
||||
"password_success": "Пароль успешно установлен",
|
||||
"password_error": "Не удалось установить пароль: {error}. Пожалуйста, попробуйте снова",
|
||||
"waiting_for_page_load": "Загрузка страницы...",
|
||||
"first_verification_passed": "Первичная проверка успешна",
|
||||
"mailbox": "Успешный доступ к почтовому ящику",
|
||||
"register_start": "Начать регистрацию",
|
||||
"form_submitted": "Форма отправлена, начало проверки...",
|
||||
"filling_form": "Заполнение формы",
|
||||
"visiting_url": "Переход по URL",
|
||||
"basic_info": "Основная информация отправлена",
|
||||
"handle_turnstile": "Обработка 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": "Не удалось получить email адрес",
|
||||
"setup_error": "Ошибка настройки email: {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": "Сброс ID машины",
|
||||
"account_info_saved": "Информация об аккаунте сохранена",
|
||||
"save_account_info_failed": "Ошибка сохранения информации об аккаунте",
|
||||
"get_email_address": "Получение email адреса",
|
||||
"update_cursor_auth_info": "Обновление информации авторизации Cursor",
|
||||
"register_process_error": "Ошибка процесса регистрации: {error}",
|
||||
"setting_password": "Установка пароля",
|
||||
"manual_code_input": "Ручной ввод кода",
|
||||
"manual_email_input": "Ручной ввод email",
|
||||
"password": "Пароль",
|
||||
"first_name": "Имя",
|
||||
"last_name": "Фамилия",
|
||||
"exit_signal": "Сигнал выхода",
|
||||
"email_address": "Email адрес",
|
||||
"config_created": "Конфигурация создана",
|
||||
"verification_failed": "Проверка не пройдена",
|
||||
"verification_error": "Ошибка проверки: {error}",
|
||||
"config_option_added": "Опция конфигурации добавлена: {option}",
|
||||
"config_updated": "Конфигурация обновлена",
|
||||
"password_submitted": "Пароль отправлен",
|
||||
"total_usage": "Общее использование: {usage}",
|
||||
"setting_on_password": "Установка пароля",
|
||||
"getting_code": "Получение кода подтверждения, попытка через 60с"
|
||||
},
|
||||
"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": "Сброс 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": "Генерация нового email",
|
||||
"blocked_domain": "Заблокированный домен",
|
||||
"select_domain": "Выбор случайного домена",
|
||||
"copy_email": "Копирование email адреса",
|
||||
"enter_mailbox": "Вход в почтовый ящик",
|
||||
"refresh_mailbox": "Обновление почтового ящика",
|
||||
"check_verification": "Проверка кода подтверждения",
|
||||
"verification_found": "Код подтверждения найден",
|
||||
"verification_not_found": "Код подтверждения не найден",
|
||||
"browser_error": "Ошибка управления браузером: {error}",
|
||||
"navigation_error": "Ошибка навигации: {error}",
|
||||
"email_copy_error": "Ошибка копирования email: {error}",
|
||||
"mailbox_error": "Ошибка почтового ящика: {error}",
|
||||
"token_saved_to_file": "Токен сохранен в cursor_tokens.txt",
|
||||
"navigate_to": "Переход на {url}",
|
||||
"generate_email_success": "Email успешно сгенерирован",
|
||||
"select_email_domain": "Выбор домена email",
|
||||
"select_email_domain_success": "Домен email успешно выбран",
|
||||
"get_email_name": "Получение имени email",
|
||||
"get_email_name_success": "Имя email успешно получено",
|
||||
"get_email_address": "Получение email адреса",
|
||||
"get_email_address_success": "Email адрес успешно получен",
|
||||
"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": "Email успешно создан",
|
||||
"create_failed": "Не удалось создать email",
|
||||
"create_error": "Ошибка создания email: {error}",
|
||||
"refreshing": "Обновление email",
|
||||
"refresh_success": "Email успешно обновлен",
|
||||
"refresh_error": "Ошибка обновления email: {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": "Email адрес",
|
||||
"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: {email}",
|
||||
"domain_blocked": "Домен заблокирован: {domain}"
|
||||
},
|
||||
"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": "Файл блокировки создан"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Проверка обновлений...",
|
||||
"new_version_available": "Доступна новая версия! (Текущая: {current}, Последняя: {latest})",
|
||||
"updating": "Обновление до последней версии. Программа перезапустится автоматически.",
|
||||
"up_to_date": "У вас установлена последняя версия.",
|
||||
"check_failed": "Не удалось проверить обновления: {error}",
|
||||
"continue_anyway": "Продолжение работы с текущей версией...",
|
||||
"update_confirm": "Хотите обновить до последней версии? (Y/n)",
|
||||
"update_skipped": "Обновление пропущено.",
|
||||
"invalid_choice": "Неверный выбор. Пожалуйста, введите 'Y' или 'n'.",
|
||||
"development_version": "Версия разработки {current} > {latest}",
|
||||
"changelog_title": "Список изменений"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Полный сброс Cursor",
|
||||
"checking_config": "Проверка конфигурационного файла",
|
||||
"config_not_found": "Конфигурационный файл не найден",
|
||||
"no_permission": "Невозможно прочитать или записать конфигурационный файл, проверьте права доступа",
|
||||
"reading_config": "Чтение текущей конфигурации",
|
||||
"creating_backup": "Создание резервной копии конфигурации",
|
||||
"backup_exists": "Резервный файл уже существует, пропускаем шаг резервного копирования",
|
||||
"generating_new_machine_id": "Генерация нового ID машины",
|
||||
"saving_new_config": "Сохранение новой конфигурации в JSON",
|
||||
"success": "Cursor успешно сброшен",
|
||||
"error": "Ошибка сброса Cursor: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"reset_machine_id": "Сброс ID машины",
|
||||
"database_connection_closed": "Соединение с базой данных закрыто",
|
||||
"database_updated_successfully": "База данных успешно обновлена",
|
||||
"connected_to_database": "Подключено к базе данных",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"db_not_found": "Файл базы данных не найден по пути: {path}",
|
||||
"db_permission_error": "Невозможно получить доступ к файлу базы данных. Проверьте права доступа",
|
||||
"db_connection_error": "Ошибка подключения к базе данных: {error}",
|
||||
"feature_title": "ФУНКЦИИ",
|
||||
"feature_1": "Полное удаление настроек и конфигураций Cursor AI",
|
||||
"feature_2": "Очистка всех кэшированных данных, включая историю AI и промпты",
|
||||
"feature_3": "Сброс ID машины для обхода обнаружения пробной версии",
|
||||
"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": "Создан новый ID машины: {path}",
|
||||
"error_creating_machine_id": "Ошибка создания файла ID машины {path}: {error}",
|
||||
"error_searching": "Ошибка поиска файлов в {path}: {error}",
|
||||
"created_extended_trial_info": "Создана новая расширенная информация о пробной версии: {path}",
|
||||
"error_creating_trial_info": "Ошибка создания файла информации о пробной версии {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Сброс редактора Cursor AI... Пожалуйста, подождите.",
|
||||
"reset_cancelled": "Сброс отменен. Выход без внесения изменений.",
|
||||
"windows_machine_id_modification_skipped": "Изменение ID машины Windows пропущено: {error}",
|
||||
"linux_machine_id_modification_skipped": "Изменение machine-id Linux пропущено: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Примечание: полный сброс ID машины может потребовать запуска от имени администратора",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Примечание: полный сброс системного machine-id может потребовать прав sudo",
|
||||
"windows_registry_instructions": "📝 ПРИМЕЧАНИЕ: Для полного сброса в Windows может потребоваться очистка записей реестра.",
|
||||
"windows_registry_instructions_2": " Запустите 'regedit' и найдите ключи, содержащие 'Cursor' или 'CursorAI' в HKEY_CURRENT_USER\\Software\\ и удалите их.\n",
|
||||
"reset_log_1": "Cursor AI полностью сброшен и обнаружение пробной версии обойдено!",
|
||||
"reset_log_2": "Пожалуйста, перезагрузите систему для применения изменений.",
|
||||
"reset_log_3": "Вам нужно будет переустановить Cursor AI, и теперь у вас должен быть новый пробный период.",
|
||||
"reset_log_4": "Для лучших результатов рекомендуется также:",
|
||||
"reset_log_5": "Использовать другой email адрес при регистрации нового пробного периода",
|
||||
"reset_log_6": "Если возможно, использовать VPN для изменения IP адреса",
|
||||
"reset_log_7": "Очистить куки и кэш браузера перед посещением сайта Cursor AI",
|
||||
"reset_log_8": "Если проблемы сохраняются, попробуйте установить Cursor AI в другое место",
|
||||
"reset_log_9": "Если вы столкнулись с проблемами, перейдите на Github Issue Tracker и создайте issue на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Произошла непредвиденная ошибка: {error}",
|
||||
"report_issue": "Пожалуйста, сообщите об этой проблеме на Github Issue Tracker на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Процесс прерван пользователем. Выход...",
|
||||
"return_to_main_menu": "Возврат в главное меню...",
|
||||
"process_interrupted": "Процесс прерван. Выход...",
|
||||
"press_enter_to_return_to_main_menu": "Нажмите Enter для возврата в главное меню...",
|
||||
"removing_known": "Удаление известных файлов лицензии/пробной версии",
|
||||
"performing_deep_scan": "Выполнение глубокого поиска дополнительных файлов лицензии/пробной версии",
|
||||
"found_additional_potential_license_trial_files": "Найдено {count} дополнительных потенциальных файлов лицензии/пробной версии",
|
||||
"checking_for_electron_localstorage_files": "Проверка файлов localStorage Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Дополнительные файлы лицензии/пробной версии не найдены при глубоком поиске",
|
||||
"removing_electron_localstorage_files": "Удаление файлов localStorage Electron",
|
||||
"electron_localstorage_files_removed": "Файлы localStorage Electron удалены",
|
||||
"electron_localstorage_files_removal_error": "Ошибка удаления файлов localStorage Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Удаление файлов localStorage Electron завершено"
|
||||
}
|
||||
}
|
||||
390
locales/tr.json
Normal file
@@ -0,0 +1,390 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Mevcut Seçenekler",
|
||||
"exit": "Programdan Çık",
|
||||
"reset": "Makine Kimliğini Sıfırla",
|
||||
"register": "Yeni Cursor Hesabı Kaydet",
|
||||
"register_google": "Google Hesabı ile Kayıt Ol",
|
||||
"register_github": "GitHub Hesabı ile Kayıt Ol",
|
||||
"register_manual": "Cursor'ı Özel E-posta ile Kaydet",
|
||||
"quit": "Cursor Uygulamasını Kapat",
|
||||
"select_language": "Dili Değiştir",
|
||||
"input_choice": "Lütfen seçiminizi girin ({choices})",
|
||||
"invalid_choice": "Geçersiz seçim. Lütfen {choices} arasından bir sayı girin",
|
||||
"program_terminated": "Program kullanıcı tarafından sonlandırıldı",
|
||||
"error_occurred": "Bir hata oluştu: {error}. Lütfen tekrar deneyin",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"disable_auto_update": "Cursor Otomatik Güncellemeyi Devre Dışı Bırak",
|
||||
"lifetime_access_enabled": "ÖMÜR BOYU ERİŞİM ETKİNLEŞTİRİLDİ",
|
||||
"totally_reset": "Cursor'ı Tamamen Sıfırla",
|
||||
"outdate": "güncel değil",
|
||||
"temp_github_register": "Geçici GitHub Kaydı",
|
||||
"coming_soon": "Yakında"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"es": "Spanish"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Cursor'dan Çıkış Başlatılıyor",
|
||||
"no_process": "Çalışan Cursor İşlemi Yok",
|
||||
"terminating": "İşlem Sonlandırılıyor {pid}",
|
||||
"waiting": "İşlemin Çıkması Bekleniyor",
|
||||
"success": "Tüm Cursor İşlemleri Kapatıldı",
|
||||
"timeout": "İşlem Zaman Aşımı: {pids}",
|
||||
"error": "Hata Oluştu: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Cursor Makine Kimliği Sıfırlama Aracı",
|
||||
"checking": "Yapılandırma Dosyası Kontrol Ediliyor",
|
||||
"not_found": "Yapılandırma Dosyası Bulunamadı",
|
||||
"no_permission": "Yapılandırma Dosyası Okunamıyor veya Yazılamıyor, Lütfen Dosya İzinlerini Kontrol Edin",
|
||||
"reading": "Mevcut Yapılandırma Okunuyor",
|
||||
"creating_backup": "Yapılandırma Yedeği Oluşturuluyor",
|
||||
"backup_exists": "Yedek Dosya Zaten Mevcut, Yedekleme Adımı Atlanıyor",
|
||||
"generating": "Yeni Makine Kimliği Oluşturuluyor",
|
||||
"saving_json": "Yeni Yapılandırma JSON'a Kaydediliyor",
|
||||
"success": "Makine Kimliği Başarıyla Sıfırlandı",
|
||||
"new_id": "Yeni Makine Kimliği",
|
||||
"permission_error": "İzin Hatası: {error}",
|
||||
"run_as_admin": "Lütfen Bu Programı Yönetici Olarak Çalıştırmayı Deneyin",
|
||||
"process_error": "Sıfırlama İşlemi Hatası: {error}",
|
||||
"updating_sqlite": "SQLite Veritabanı Güncelleniyor",
|
||||
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
|
||||
"sqlite_success": "SQLite Veritabanı Başarıyla Güncellendi",
|
||||
"sqlite_error": "SQLite Veritabanı Güncellemesi Başarısız: {error}",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"unsupported_os": "Desteklenmeyen İşletim Sistemi: {os}",
|
||||
"linux_path_not_found": "Linux Yolu Bulunamadı",
|
||||
"updating_system_ids": "Sistem Kimlikleri Güncelleniyor",
|
||||
"system_ids_updated": "Sistem Kimlikleri Başarıyla Güncellendi",
|
||||
"system_ids_update_failed": "Sistem Kimlikleri Güncellemesi Başarısız: {error}",
|
||||
"windows_guid_updated": "Windows GUID Başarıyla Güncellendi",
|
||||
"windows_permission_denied": "Windows İzni Reddedildi",
|
||||
"windows_guid_update_failed": "Windows GUID Güncellemesi Başarısız",
|
||||
"macos_uuid_updated": "macOS UUID Başarıyla Güncellendi",
|
||||
"plutil_command_failed": "plutil Komutu Başarısız",
|
||||
"start_patching": "getMachineId Yamalanması Başlatılıyor",
|
||||
"macos_uuid_update_failed": "macOS UUID Güncellemesi Başarısız",
|
||||
"current_version": "Mevcut Cursor Sürümü: {version}",
|
||||
"patch_completed": "getMachineId Yamalama Tamamlandı",
|
||||
"patch_failed": "getMachineId Yamalama Başarısız: {error}",
|
||||
"version_check_passed": "Cursor Sürüm Kontrolü Geçildi",
|
||||
"file_modified": "Dosya Değiştirildi",
|
||||
"version_less_than_0_45": "Cursor Sürümü < 0.45.0, getMachineId Yamalama Atlanıyor",
|
||||
"detecting_version": "Cursor Sürümü Tespit Ediliyor",
|
||||
"patching_getmachineid": "getMachineId Yamalanıyor",
|
||||
"version_greater_than_0_45": "Cursor Sürümü >= 0.45.0, getMachineId Yamalanıyor",
|
||||
"permission_denied": "İzin Reddedildi: {error}",
|
||||
"backup_created": "Yedek Oluşturuldu",
|
||||
"update_success": "Güncelleme Başarılı",
|
||||
"update_failed": "Güncelleme Başarısız: {error}",
|
||||
"windows_machine_guid_updated": "Windows Makine GUID'si Başarıyla Güncellendi",
|
||||
"reading_package_json": "package.json Okunuyor {path}",
|
||||
"invalid_json_object": "Geçersiz JSON Nesnesi",
|
||||
"no_version_field": "package.json İçinde Sürüm Alanı Bulunamadı",
|
||||
"version_field_empty": "Sürüm Alanı Boş",
|
||||
"invalid_version_format": "Geçersiz Sürüm Formatı: {version}",
|
||||
"found_version": "Sürüm Bulundu: {version}",
|
||||
"version_parse_error": "Sürüm Ayrıştırma Hatası: {error}",
|
||||
"package_not_found": "Package.json Bulunamadı: {path}",
|
||||
"check_version_failed": "Sürüm Kontrolü Başarısız: {error}",
|
||||
"stack_trace": "Yığın İzleme",
|
||||
"version_too_low": "Cursor Sürümü Çok Düşük: {version} < 0.45.0"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Kayıt Aracı",
|
||||
"start": "Kayıt işlemi başlatılıyor...",
|
||||
"handling_turnstile": "Güvenlik doğrulaması işleniyor...",
|
||||
"retry_verification": "Doğrulama tekrar deneniyor...",
|
||||
"detect_turnstile": "Güvenlik doğrulaması kontrol ediliyor...",
|
||||
"verification_success": "Güvenlik doğrulaması başarılı",
|
||||
"starting_browser": "Tarayıcı açılıyor...",
|
||||
"form_success": "Form başarıyla gönderildi",
|
||||
"browser_started": "Tarayıcı başarıyla açıldı",
|
||||
"waiting_for_second_verification": "E-posta doğrulaması bekleniyor...",
|
||||
"waiting_for_verification_code": "Doğrulama kodu bekleniyor...",
|
||||
"password_success": "Şifre başarıyla ayarlandı",
|
||||
"password_error": "Şifre ayarlanamadı: {error}. Lütfen tekrar deneyin",
|
||||
"waiting_for_page_load": "Sayfa yükleniyor...",
|
||||
"first_verification_passed": "İlk doğrulama başarılı",
|
||||
"mailbox": "E-posta gelen kutusuna başarıyla erişildi",
|
||||
"register_start": "Kayıt Başlat",
|
||||
"form_submitted": "Form Gönderildi, Doğrulama Başlatılıyor...",
|
||||
"filling_form": "Form Dolduruluyor",
|
||||
"visiting_url": "URL Ziyaret Ediliyor",
|
||||
"basic_info": "Temel Bilgiler Gönderildi",
|
||||
"handle_turnstile": "Turnstile İşleniyor",
|
||||
"no_turnstile": "Turnstile Algılanmadı",
|
||||
"turnstile_passed": "Turnstile Geçildi",
|
||||
"verification_start": "Doğrulama Kodu Alma Başlatılıyor",
|
||||
"verification_timeout": "Doğrulama Kodu Alma Zaman Aşımı",
|
||||
"verification_not_found": "Doğrulama Kodu Bulunamadı",
|
||||
"try_get_code": "Deneme | {attempt} Doğrulama Kodu Al | Kalan Süre: {time}s",
|
||||
"get_account": "Hesap Bilgileri Alınıyor",
|
||||
"get_token": "Cursor Oturum Jetonu Alınıyor",
|
||||
"token_success": "Jeton Başarıyla Alındı",
|
||||
"token_attempt": "Deneme | {attempt} kez Jeton alma denemesi | {time}s içinde tekrar denenecek",
|
||||
"token_max_attempts": "Maksimum Deneme Sayısına Ulaşıldı ({max}) | Jeton alınamadı",
|
||||
"token_failed": "Jeton Alma Başarısız: {error}",
|
||||
"account_error": "Hesap Bilgisi Alma Başarısız: {error}",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"browser_start": "Tarayıcı Başlatılıyor",
|
||||
"open_mailbox": "Posta Kutusu Sayfası Açılıyor",
|
||||
"email_error": "E-posta Adresi Alınamadı",
|
||||
"setup_error": "E-posta Kurulum Hatası: {error}",
|
||||
"start_getting_verification_code": "Doğrulama Kodu Alma Başlatılıyor, 60 saniye içinde denenecek",
|
||||
"get_verification_code_timeout": "Doğrulama Kodu Alma Zaman Aşımı",
|
||||
"get_verification_code_success": "Doğrulama Kodu Alma Başarılı",
|
||||
"try_get_verification_code": "Deneme | {attempt} Doğrulama Kodu Al | Kalan Süre: {remaining_time}s",
|
||||
"verification_code_filled": "Doğrulama Kodu Dolduruldu",
|
||||
"login_success_and_jump_to_settings_page": "Giriş Başarılı ve Ayarlar Sayfasına Yönlendiriliyor",
|
||||
"detect_login_page": "Giriş Sayfası Algılandı, Giriş Başlatılıyor...",
|
||||
"cursor_registration_completed": "Cursor Kaydı Tamamlandı!",
|
||||
"set_password": "Şifre Belirle",
|
||||
"basic_info_submitted": "Temel Bilgiler Gönderildi",
|
||||
"cursor_auth_info_updated": "Cursor Kimlik Bilgileri Güncellendi",
|
||||
"cursor_auth_info_update_failed": "Cursor Kimlik Bilgileri Güncellemesi Başarısız",
|
||||
"reset_machine_id": "Makine Kimliğini Sıfırla",
|
||||
"account_info_saved": "Hesap Bilgileri Kaydedildi",
|
||||
"save_account_info_failed": "Hesap Bilgilerini Kaydetme Başarısız",
|
||||
"get_email_address": "E-posta Adresi Al",
|
||||
"update_cursor_auth_info": "Cursor Kimlik Bilgilerini Güncelle",
|
||||
"register_process_error": "Kayıt İşlemi Hatası: {error}",
|
||||
"setting_password": "Şifre Ayarlanıyor",
|
||||
"manual_code_input": "Manuel Kod Girişi",
|
||||
"manual_email_input": "Manuel E-posta Girişi",
|
||||
"password": "Şifre",
|
||||
"first_name": "Ad",
|
||||
"last_name": "Soyad",
|
||||
"exit_signal": "Çıkış Sinyali",
|
||||
"email_address": "E-posta Adresi",
|
||||
"config_created": "Yapılandırma Oluşturuldu",
|
||||
"verification_failed": "Doğrulama Başarısız",
|
||||
"verification_error": "Doğrulama Hatası: {error}",
|
||||
"config_option_added": "Yapılandırma Seçeneği Eklendi: {option}",
|
||||
"config_updated": "Yapılandırma Güncellendi",
|
||||
"password_submitted": "Şifre Gönderildi",
|
||||
"total_usage": "Toplam Kullanım: {usage}",
|
||||
"setting_on_password": "Şifre Ayarlanıyor",
|
||||
"getting_code": "Doğrulama Kodu Alınıyor, 60 saniye içinde denenecek",
|
||||
"human_verify_error": "Kullanıcının insan olduğu doğrulanamıyor. Tekrar deneniyor...",
|
||||
"max_retries_reached": "Maksimum deneme sayısına ulaşıldı. Kayıt başarısız."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Kimlik Yöneticisi",
|
||||
"checking_auth": "Kimlik Dosyası Kontrol Ediliyor",
|
||||
"auth_not_found": "Kimlik Dosyası Bulunamadı",
|
||||
"auth_file_error": "Kimlik Dosyası Hatası: {error}",
|
||||
"reading_auth": "Kimlik Dosyası Okunuyor",
|
||||
"updating_auth": "Kimlik Bilgileri Güncelleniyor",
|
||||
"auth_updated": "Kimlik Bilgileri Başarıyla Güncellendi",
|
||||
"auth_update_failed": "Kimlik Bilgileri Güncellemesi Başarısız: {error}",
|
||||
"auth_file_created": "Kimlik Dosyası Oluşturuldu",
|
||||
"auth_file_create_failed": "Kimlik Dosyası Oluşturma Başarısız: {error}",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"reset_machine_id": "Makine Kimliğini Sıfırla",
|
||||
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
|
||||
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
|
||||
"connected_to_database": "Veritabanına Bağlanıldı",
|
||||
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
|
||||
"db_not_found": "Veritabanı dosyası bulunamadı: {path}",
|
||||
"db_permission_error": "Veritabanı dosyasına erişilemiyor. Lütfen izinleri kontrol edin",
|
||||
"db_connection_error": "Veritabanına bağlantı başarısız: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Yeni E-posta Oluşturuluyor",
|
||||
"blocked_domain": "Engellenmiş Alan Adı",
|
||||
"select_domain": "Rastgele Alan Adı Seçiliyor",
|
||||
"copy_email": "E-posta Adresi Kopyalanıyor",
|
||||
"enter_mailbox": "Posta Kutusuna Giriliyor",
|
||||
"refresh_mailbox": "Posta Kutusu Yenileniyor",
|
||||
"check_verification": "Doğrulama Kodu Kontrol Ediliyor",
|
||||
"verification_found": "Doğrulama Kodu Bulundu",
|
||||
"verification_not_found": "Doğrulama Kodu Bulunamadı",
|
||||
"browser_error": "Tarayıcı Kontrol Hatası: {error}",
|
||||
"navigation_error": "Gezinme Hatası: {error}",
|
||||
"email_copy_error": "E-posta Kopyalama Hatası: {error}",
|
||||
"mailbox_error": "Posta Kutusu Hatası: {error}",
|
||||
"token_saved_to_file": "Jeton cursor_tokens.txt dosyasına kaydedildi",
|
||||
"navigate_to": "{url} adresine gidiliyor",
|
||||
"generate_email_success": "E-posta Oluşturma Başarılı",
|
||||
"select_email_domain": "E-posta Alan Adı Seç",
|
||||
"select_email_domain_success": "E-posta Alan Adı Seçimi Başarılı",
|
||||
"get_email_name": "E-posta Adı Al",
|
||||
"get_email_name_success": "E-posta Adı Alma Başarılı",
|
||||
"get_email_address": "E-posta Adresi Al",
|
||||
"get_email_address_success": "E-posta Adresi Alma Başarılı",
|
||||
"enter_mailbox_success": "Posta Kutusuna Giriş Başarılı",
|
||||
"found_verification_code": "Doğrulama Kodu Bulundu",
|
||||
"get_cursor_session_token": "Cursor Oturum Jetonu Al",
|
||||
"get_cursor_session_token_success": "Cursor Oturum Jetonu Alma Başarılı",
|
||||
"get_cursor_session_token_failed": "Cursor Oturum Jetonu Alma Başarısız",
|
||||
"save_token_failed": "Jeton Kaydetme Başarısız",
|
||||
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
|
||||
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
|
||||
"no_valid_verification_code": "Geçerli Doğrulama Kodu Yok"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Tarayıcı Başlatılıyor",
|
||||
"visiting_site": "E-posta alan adları ziyaret ediliyor",
|
||||
"create_success": "E-posta Başarıyla Oluşturuldu",
|
||||
"create_failed": "E-posta Oluşturma Başarısız",
|
||||
"create_error": "E-posta Oluşturma Hatası: {error}",
|
||||
"refreshing": "E-posta Yenileniyor",
|
||||
"refresh_success": "E-posta Başarıyla Yenilendi",
|
||||
"refresh_error": "E-posta Yenileme Hatası: {error}",
|
||||
"refresh_button_not_found": "Yenileme Düğmesi Bulunamadı",
|
||||
"verification_found": "Doğrulama Bulundu",
|
||||
"verification_not_found": "Doğrulama Bulunamadı",
|
||||
"verification_error": "Doğrulama Hatası: {error}",
|
||||
"verification_code_found": "Doğrulama Kodu Bulundu",
|
||||
"verification_code_not_found": "Doğrulama Kodu Bulunamadı",
|
||||
"verification_code_error": "Doğrulama Kodu Hatası: {error}",
|
||||
"address": "E-posta Adresi",
|
||||
"all_domains_blocked": "Tüm Alan Adları Engellendi, Servis Değiştiriliyor",
|
||||
"no_available_domains_after_filtering": "Filtrelemeden Sonra Kullanılabilir Alan Adı Yok",
|
||||
"switching_service": "{service} Servisine Geçiliyor",
|
||||
"domains_list_error": "Alan Adları Listesi Alınamadı: {error}",
|
||||
"failed_to_get_available_domains": "Kullanılabilir Alan Adları Alınamadı",
|
||||
"domains_excluded": "Hariç Tutulan Alan Adları: {domains}",
|
||||
"failed_to_create_account": "Hesap Oluşturma Başarısız",
|
||||
"account_creation_error": "Hesap Oluşturma Hatası: {error}",
|
||||
"blocked_domains": "Engellenen Alan Adları: {domains}",
|
||||
"blocked_domains_loaded": "Engellenen Alan Adları Yüklendi: {count}",
|
||||
"blocked_domains_loaded_error": "Engellenen Alan Adları Yükleme Hatası: {error}",
|
||||
"blocked_domains_loaded_success": "Engellenen Alan Adları Başarıyla Yüklendi",
|
||||
"blocked_domains_loaded_timeout": "Engellenen Alan Adları Yükleme Zaman Aşımı: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Engellenen Alan Adları Yükleme Zaman Aşımı Hatası: {error}",
|
||||
"available_domains_loaded": "Kullanılabilir Alan Adları Yüklendi: {count}",
|
||||
"domains_filtered": "Filtrelenen Alan Adları: {count}",
|
||||
"trying_to_create_email": "E-posta oluşturulmaya çalışılıyor: {email}",
|
||||
"domain_blocked": "Alan Adı Engellendi: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Cursor Otomatik Güncellemeyi Devre Dışı Bırak",
|
||||
"disable_success": "Otomatik Güncelleme Başarıyla Devre Dışı Bırakıldı",
|
||||
"disable_failed": "Otomatik Güncellemeyi Devre Dışı Bırakma Başarısız: {error}",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"start_disable": "Otomatik Güncellemeyi Devre Dışı Bırakma Başlatılıyor",
|
||||
"killing_processes": "İşlemler Sonlandırılıyor",
|
||||
"processes_killed": "İşlemler Sonlandırıldı",
|
||||
"removing_directory": "Dizin Kaldırılıyor",
|
||||
"directory_removed": "Dizin Kaldırıldı",
|
||||
"creating_block_file": "Engelleme Dosyası Oluşturuluyor",
|
||||
"block_file_created": "Engelleme Dosyası Oluşturuldu"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Güncellemeler kontrol ediliyor...",
|
||||
"new_version_available": "Yeni sürüm mevcut! (Mevcut: {current}, En son: {latest})",
|
||||
"updating": "En son sürüme güncelleniyor. Program otomatik olarak yeniden başlatılacak.",
|
||||
"up_to_date": "En son sürümü kullanıyorsunuz.",
|
||||
"check_failed": "Güncellemeler kontrol edilemedi: {error}",
|
||||
"continue_anyway": "Mevcut sürümle devam ediliyor...",
|
||||
"update_confirm": "En son sürüme güncellemek istiyor musunuz? (Y/n)",
|
||||
"update_skipped": "Güncelleme atlanıyor.",
|
||||
"invalid_choice": "Geçersiz seçim. Lütfen 'Y' veya 'n' girin.",
|
||||
"development_version": "Geliştirme Sürümü {current} > {latest}",
|
||||
"changelog_title": "Değişiklik günlüğü"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Cursor'ı Tamamen Sıfırla",
|
||||
"checking_config": "Yapılandırma Dosyası Kontrol Ediliyor",
|
||||
"config_not_found": "Yapılandırma Dosyası Bulunamadı",
|
||||
"no_permission": "Yapılandırma Dosyası Okunamıyor veya Yazılamıyor, Lütfen Dosya İzinlerini Kontrol Edin",
|
||||
"reading_config": "Mevcut Yapılandırma Okunuyor",
|
||||
"creating_backup": "Yapılandırma Yedeği Oluşturuluyor",
|
||||
"backup_exists": "Yedek Dosya Zaten Mevcut, Yedekleme Adımı Atlanıyor",
|
||||
"generating_new_machine_id": "Yeni Makine Kimliği Oluşturuluyor",
|
||||
"saving_new_config": "Yeni Yapılandırma JSON'a Kaydediliyor",
|
||||
"success": "Cursor Başarıyla Sıfırlandı",
|
||||
"error": "Cursor Sıfırlama Başarısız: {error}",
|
||||
"press_enter": "Çıkmak için Enter'a Basın",
|
||||
"reset_machine_id": "Makine Kimliğini Sıfırla",
|
||||
"database_connection_closed": "Veritabanı Bağlantısı Kapatıldı",
|
||||
"database_updated_successfully": "Veritabanı Başarıyla Güncellendi",
|
||||
"connected_to_database": "Veritabanına Bağlanıldı",
|
||||
"updating_pair": "Anahtar-Değer Çifti Güncelleniyor",
|
||||
"db_not_found": "Veritabanı dosyası bulunamadı: {path}",
|
||||
"db_permission_error": "Veritabanı dosyasına erişilemiyor. Lütfen izinleri kontrol edin",
|
||||
"db_connection_error": "Veritabanına bağlantı başarısız: {error}",
|
||||
"feature_title": "ÖZELLİKLER",
|
||||
"feature_1": "Cursor AI ayarları ve yapılandırmalarının tamamen kaldırılması",
|
||||
"feature_2": "AI geçmişi ve komutları dahil tüm önbelleğe alınmış verileri temizler",
|
||||
"feature_3": "Deneme süresini aşma tespitini atlatmak için makine kimliğini sıfırlar",
|
||||
"feature_4": "Rastgele yeni makine tanımlayıcıları oluşturur",
|
||||
"feature_5": "Özel uzantıları ve tercihleri kaldırır",
|
||||
"feature_6": "Deneme süresi bilgilerini ve aktivasyon verilerini sıfırlar",
|
||||
"feature_7": "Gizli lisans ve deneme süresiyle ilgili dosyalar için derin tarama",
|
||||
"feature_8": "Cursor olmayan dosyaları ve uygulamaları güvenle korur",
|
||||
"feature_9": "Windows, macOS ve Linux ile uyumludur",
|
||||
"disclaimer_title": "YASAL UYARI",
|
||||
"disclaimer_1": "Bu araç, tüm Cursor AI ayarlarını,",
|
||||
"disclaimer_2": "yapılandırmalarını ve önbelleğe alınmış verileri kalıcı olarak silecektir. Bu işlem geri alınamaz.",
|
||||
"disclaimer_3": "Kod dosyalarınız ETKİLENMEYECEK ve bu araç",
|
||||
"disclaimer_4": "yalnızca Cursor AI editör dosyalarını ve deneme süresi algılama mekanizmalarını hedef almak için tasarlanmıştır.",
|
||||
"disclaimer_5": "Sisteminizde bulunan diğer uygulamalar etkilenmeyecektir.",
|
||||
"disclaimer_6": "Bu aracı çalıştırdıktan sonra Cursor AI'yi yeniden kurmanız gerekecektir.",
|
||||
"disclaimer_7": "Kullanım sorumluluğu size aittir",
|
||||
"confirm_title": "Devam etmek istediğinizden emin misiniz?",
|
||||
"confirm_1": "Bu işlem, tüm Cursor AI ayarlarını,",
|
||||
"confirm_2": "yapılandırmalarını ve önbelleğe alınmış verileri silecektir. Bu işlem geri alınamaz.",
|
||||
"confirm_3": "Kod dosyalarınız ETKİLENMEYECEK ve bu araç",
|
||||
"confirm_4": "yalnızca Cursor AI editör dosyalarını ve deneme süresi algılama mekanizmalarını hedef almak için tasarlanmıştır.",
|
||||
"confirm_5": "Sisteminizde bulunan diğer uygulamalar etkilenmeyecektir.",
|
||||
"confirm_6": "Bu aracı çalıştırdıktan sonra Cursor AI'yi yeniden kurmanız gerekecektir.",
|
||||
"confirm_7": "Kullanım sorumluluğu size aittir",
|
||||
"invalid_choice": "Lütfen 'Y' veya 'n' girin",
|
||||
"skipped_for_safety": "Güvenlik için atlandı (Cursor ile ilgili değil): {path}",
|
||||
"deleted": "Silindi: {path}",
|
||||
"error_deleting": "{path} silinirken hata: {error}",
|
||||
"not_found": "Dosya bulunamadı: {path}",
|
||||
"resetting_machine_id": "Deneme süresi algılamasını atlatmak için makine tanımlayıcıları sıfırlanıyor...",
|
||||
"created_machine_id": "Yeni makine kimliği oluşturuldu: {path}",
|
||||
"error_creating_machine_id": "Makine kimliği dosyası oluşturulurken hata {path}: {error}",
|
||||
"error_searching": "{path} içindeki dosyalar aranırken hata: {error}",
|
||||
"created_extended_trial_info": "Yeni genişletilmiş deneme bilgisi oluşturuldu: {path}",
|
||||
"error_creating_trial_info": "Deneme bilgisi dosyası oluşturulurken hata {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Cursor AI Editor sıfırlanıyor... Lütfen bekleyin.",
|
||||
"reset_cancelled": "Sıfırlama iptal edildi. Herhangi bir değişiklik yapmadan çıkılıyor.",
|
||||
"windows_machine_id_modification_skipped": "Windows makine kimliği değişikliği atlandı: {error}",
|
||||
"linux_machine_id_modification_skipped": "Linux machine-id değişikliği atlandı: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Not: Tam makine kimliği sıfırlaması yönetici olarak çalıştırmayı gerektirebilir",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Not: Tam sistem machine-id sıfırlaması sudo ayrıcalıkları gerektirebilir",
|
||||
"windows_registry_instructions": "📝 NOT: Windows'ta tam sıfırlama için kayıt defteri girdilerini de temizlemeniz gerekebilir.",
|
||||
"windows_registry_instructions_2": " 'regedit' çalıştırın ve HKEY_CURRENT_USER\\Software\\ altında 'Cursor' veya 'CursorAI' içeren anahtarları arayıp silin.\n",
|
||||
"reset_log_1": "Cursor AI tamamen sıfırlandı ve deneme süresi algılaması atlatıldı!",
|
||||
"reset_log_2": "Değişikliklerin etkili olması için lütfen sisteminizi yeniden başlatın.",
|
||||
"reset_log_3": "Cursor AI'yi yeniden kurmanız gerekecek ve şimdi yeni bir deneme süreniz olmalı.",
|
||||
"reset_log_4": "En iyi sonuçlar için şunları da düşünün:",
|
||||
"reset_log_5": "Yeni bir deneme süresi için kaydolurken farklı bir e-posta adresi kullanın",
|
||||
"reset_log_6": "Mümkünse, IP adresinizi değiştirmek için VPN kullanın",
|
||||
"reset_log_7": "Cursor AI'nin web sitesini ziyaret etmeden önce tarayıcı çerezlerinizi ve önbelleği temizleyin",
|
||||
"reset_log_8": "Sorunlar devam ederse, Cursor AI'yi farklı bir konuma kurmayı deneyin",
|
||||
"reset_log_9": "Herhangi bir sorunla karşılaşırsanız, Github Sorun Takibine gidin ve https://github.com/yeongpin/cursor-free-vip/issues adresinde bir sorun oluşturun",
|
||||
"unexpected_error": "Beklenmeyen bir hata oluştu: {error}",
|
||||
"report_issue": "Lütfen bu sorunu https://github.com/yeongpin/cursor-free-vip/issues adresindeki Github Sorun Takibine bildirin",
|
||||
"keyboard_interrupt": "İşlem kullanıcı tarafından kesildi. Çıkılıyor...",
|
||||
"return_to_main_menu": "Ana menüye dönülüyor...",
|
||||
"process_interrupted": "İşlem kesildi. Çıkılıyor...",
|
||||
"press_enter_to_return_to_main_menu": "Ana menüye dönmek için Enter'a basın...",
|
||||
"removing_known": "Bilinen deneme/lisans dosyaları kaldırılıyor",
|
||||
"performing_deep_scan": "Ek deneme/lisans dosyaları için derin tarama yapılıyor",
|
||||
"found_additional_potential_license_trial_files": "{count} ek potansiyel lisans/deneme dosyası bulundu",
|
||||
"checking_for_electron_localstorage_files": "Electron localStorage dosyaları kontrol ediliyor",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Derin taramada ek lisans/deneme dosyası bulunamadı",
|
||||
"removing_electron_localstorage_files": "Electron localStorage dosyaları kaldırılıyor",
|
||||
"electron_localstorage_files_removed": "Electron localStorage dosyaları kaldırıldı",
|
||||
"electron_localstorage_files_removal_error": "Electron localStorage dosyaları kaldırılırken hata: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage dosyaları kaldırma işlemi tamamlandı"
|
||||
}
|
||||
}
|
||||
385
locales/vi.json
Normal file
@@ -0,0 +1,385 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Các Tùy Chọn Khả Dụng",
|
||||
"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_manual": "Đăng Ký Cursor Với Email Tùy Chỉnh",
|
||||
"quit": "Đóng Ứng Dụng Cursor",
|
||||
"select_language": "Thay Đổi Ngôn Ngữ",
|
||||
"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",
|
||||
"error_occurred": "Đã xảy ra lỗi: {error}. Vui lòng thử lại",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"disable_auto_update": "Tắt tự động cập nhật Cursor",
|
||||
"lifetime_access_enabled": "ĐÃ BẬT TRUY CẬP TRỌN ĐỜI",
|
||||
"totally_reset": "Đặt lại hoàn toàn Cursor",
|
||||
"outdate": "Quá cũ",
|
||||
"temp_github_register": "Đăng ký GitHub tạm thời",
|
||||
"coming_soon": "Sắp ra mắt"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Tiếng Anh",
|
||||
"zh_cn": "Tiếng Trung Giản Thể",
|
||||
"zh_tw": "Tiếng Trung Phồn Thể",
|
||||
"vi": "Tiếng Việt",
|
||||
"nl": "Tiếng Hà Lan",
|
||||
"de": "Tiếng Đức",
|
||||
"fr": "Tiếng Pháp",
|
||||
"pt": "Tiếng Bồ Đào Nha",
|
||||
"ru": "Tiếng Nga",
|
||||
"es": "Tiếng Tây Ban Nha"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Bắt Đầu Thoát Cursor",
|
||||
"no_process": "Không Có Tiến Trình Cursor Đang Chạy",
|
||||
"terminating": "Đang Chấm Dứt Tiến Trình {pid}",
|
||||
"waiting": "Đang Chờ Tiến Trình Thoát",
|
||||
"success": "Tất Cả Tiến Trình Cursor Đã Đóng",
|
||||
"timeout": "Tiến Trình Hết Thời Gian: {pids}",
|
||||
"error": "Đã Xảy Ra Lỗi: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Công Cụ Đặt Lại ID Máy Cursor",
|
||||
"checking": "Đang Kiểm Tra Tệp Cấu Hình",
|
||||
"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": "Đ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": "Đang Tạo ID Máy Mới",
|
||||
"saving_json": "Đang Lưu Cấu Hình Mới Vào JSON",
|
||||
"success": "Đặt Lại ID Máy Thành Công",
|
||||
"new_id": "ID Máy Mới",
|
||||
"permission_error": "Lỗi Quyền: {error}",
|
||||
"run_as_admin": "Vui Lòng Thử Chạy Chương Trình Này Với Quyền Quản Trị",
|
||||
"process_error": "Lỗi Quá Trình Đặt Lại: {error}",
|
||||
"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_success": "Cập Nhật Cơ Sở Dữ Liệu SQLite Thành Công",
|
||||
"sqlite_error": "Cập Nhật Cơ Sở Dữ Liệu SQLite Thất Bại: {error}",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {os}",
|
||||
"linux_path_not_found": "Không Tìm Thấy Đường Dẫn Linux",
|
||||
"updating_system_ids": "Đang Cập Nhật ID Hệ Thống",
|
||||
"system_ids_updated": "Cập Nhật ID Hệ Thống Thành Công",
|
||||
"system_ids_update_failed": "Cập Nhật ID Hệ Thống Thất Bại: {error}",
|
||||
"windows_guid_updated": "Cập Nhật GUID Windows Thành Công",
|
||||
"windows_permission_denied": "Windows Từ Chối Quyền",
|
||||
"windows_guid_update_failed": "Cập Nhật GUID Windows Thất Bại",
|
||||
"macos_uuid_updated": "Cập Nhật UUID macOS Thành Công",
|
||||
"plutil_command_failed": "Lệnh plutil Thất Bại",
|
||||
"start_patching": "Bắt Đầu Vá getMachineId",
|
||||
"macos_uuid_update_failed": "Cập Nhật UUID macOS Thất Bại",
|
||||
"current_version": "Phiên Bản Cursor Hiện Tại: {version}",
|
||||
"patch_completed": "Vá getMachineId Hoàn Tất",
|
||||
"patch_failed": "Vá getMachineId Thất Bại: {error}",
|
||||
"version_check_passed": "Kiểm Tra Phiên Bản Cursor Đã Qua",
|
||||
"file_modified": "Tệp Đã Được Sửa Đổi",
|
||||
"version_less_than_0_45": "Phiên Bản Cursor < 0.45.0, Bỏ Qua Vá getMachineId",
|
||||
"detecting_version": "Đang Phát Hiện Phiên Bản Cursor",
|
||||
"patching_getmachineid": "Đang Vá getMachineId",
|
||||
"version_greater_than_0_45": "Phiên Bản Cursor >= 0.45.0, Đang Vá getMachineId",
|
||||
"permission_denied": "Từ Chối Quyền: {error}",
|
||||
"backup_created": "Đã Tạo Bản Sao Lưu",
|
||||
"update_success": "Cập Nhật Thành Công",
|
||||
"update_failed": "Cập Nhật Thất Bại: {error}",
|
||||
"windows_machine_guid_updated": "Cập Nhật GUID Máy Windows Thành Công",
|
||||
"reading_package_json": "Đang Đọc package.json {path}",
|
||||
"invalid_json_object": "Đối Tượng JSON Không Hợp Lệ",
|
||||
"no_version_field": "Không Tìm Thấy Trường Phiên Bản Trong package.json",
|
||||
"version_field_empty": "Trường Phiên Bản Trống",
|
||||
"invalid_version_format": "Định Dạng Phiên Bản Không Hợp Lệ: {version}",
|
||||
"found_version": "Đã Tìm Thấy Phiên Bản: {version}",
|
||||
"version_parse_error": "Lỗi Phân Tích Phiên Bản: {error}",
|
||||
"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"
|
||||
},
|
||||
"register": {
|
||||
"title": "Công Cụ Đăng Ký Cursor",
|
||||
"start": "Bắt đầu quá trình đăng ký...",
|
||||
"handling_turnstile": "Đang xử lý xác minh bảo mật...",
|
||||
"retry_verification": "Đang thử lại xác minh...",
|
||||
"detect_turnstile": "Đang kiểm tra xác minh bảo mật...",
|
||||
"verification_success": "Xác minh bảo mật thành công",
|
||||
"starting_browser": "Đang mở trình duyệt...",
|
||||
"form_success": "Biểu mẫu đã được gửi thành công",
|
||||
"browser_started": "Trình duyệt đã được mở thành công",
|
||||
"waiting_for_second_verification": "Đang chờ xác minh email...",
|
||||
"waiting_for_verification_code": "Đang chờ mã xác minh...",
|
||||
"password_success": "Đặt mật khẩu thành công",
|
||||
"password_error": "Không thể đặt mật khẩu: {error}. Vui lòng thử lại",
|
||||
"waiting_for_page_load": "Đang tải trang...",
|
||||
"first_verification_passed": "Xác minh ban đầu thành công",
|
||||
"mailbox": "Đã truy cập hộp thư đến thành công",
|
||||
"register_start": "Bắt Đầu Đăng Ký",
|
||||
"form_submitted": "Biểu Mẫu Đã Gửi, Bắt Đầu Xác Minh...",
|
||||
"filling_form": "Điền Biểu Mẫu",
|
||||
"visiting_url": "Đang Truy Cập URL",
|
||||
"basic_info": "Thông Tin Cơ Bản Đã Gửi",
|
||||
"handle_turnstile": "Xử Lý Turnstile",
|
||||
"no_turnstile": "Không Phát Hiện Turnstile",
|
||||
"turnstile_passed": "Đã Vượt Qua Turnstile",
|
||||
"verification_start": "Bắt Đầu Lấy Mã Xác Minh",
|
||||
"verification_timeout": "Lấy Mã Xác Minh Hết Thời Gian",
|
||||
"verification_not_found": "Không Tìm Thấy Mã Xác Minh",
|
||||
"try_get_code": "Thử | {attempt} Lấy Mã Xác Minh | Thời Gian Còn Lại: {time}s",
|
||||
"get_account": "Đang Lấy Thông Tin Tài Khoản",
|
||||
"get_token": "Lấy Token Phiên Cursor",
|
||||
"token_success": "Lấy Token Thành Công",
|
||||
"token_attempt": "Thử | {attempt} lần để lấy Token | Sẽ thử lại sau {time}s",
|
||||
"token_max_attempts": "Đạt Số Lần Thử Tối Đa ({max}) | Không Thể Lấy Token",
|
||||
"token_failed": "Lấy Token Thất Bại: {error}",
|
||||
"account_error": "Lấy Thông Tin Tài Khoản Thất Bại: {error}",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"browser_start": "Đang Khởi Động Trình Duyệt",
|
||||
"open_mailbox": "Đang Mở Trang Hộp Thư",
|
||||
"email_error": "Không Thể Lấy Địa Chỉ Email",
|
||||
"setup_error": "Lỗi Thiết Lập Email: {error}",
|
||||
"start_getting_verification_code": "Bắt Đầu Lấy Mã Xác Minh, Sẽ Thử Trong 60s",
|
||||
"get_verification_code_timeout": "Lấy Mã Xác Minh Hết Thời Gian",
|
||||
"get_verification_code_success": "Lấy Mã Xác Minh Thành Công",
|
||||
"try_get_verification_code": "Thử | {attempt} Lấy Mã Xác Minh | Thời Gian Còn Lại: {remaining_time}s",
|
||||
"verification_code_filled": "Đã Điền Mã Xác Minh",
|
||||
"login_success_and_jump_to_settings_page": "Đăng Nhập Thành Công và Chuyển Đến Trang Cài Đặt",
|
||||
"detect_login_page": "Phát Hiện Trang Đăng Nhập, Bắt Đầu Đăng Nhập...",
|
||||
"cursor_registration_completed": "Đăng Ký Cursor Hoàn Tất!",
|
||||
"set_password": "Đặt Mật Khẩu",
|
||||
"basic_info_submitted": "Thông Tin Cơ Bản Đã Gửi",
|
||||
"cursor_auth_info_updated": "Thông Tin Xác Thực Cursor Đã Cập Nhật",
|
||||
"cursor_auth_info_update_failed": "Cập Nhật Thông Tin Xác Thực Cursor Thất Bại",
|
||||
"reset_machine_id": "Đặt Lại ID Máy",
|
||||
"account_info_saved": "Thông Tin Tài Khoản Đã Lưu",
|
||||
"save_account_info_failed": "Lưu Thông Tin Tài Khoản Thất Bại",
|
||||
"get_email_address": "Lấy Địa Chỉ Email",
|
||||
"update_cursor_auth_info": "Cập Nhật Thông Tin Xác Thực Cursor",
|
||||
"register_process_error": "Lỗi Quá Trình Đăng Ký: {error}",
|
||||
"setting_password": "Đang Đặt Mật Khẩu",
|
||||
"manual_code_input": "Nhập Mã Thủ Công",
|
||||
"manual_email_input": "Nhập Email Thủ Công",
|
||||
"password": "Mật Khẩu",
|
||||
"first_name": "Tên",
|
||||
"last_name": "Họ",
|
||||
"exit_signal": "Tín Hiệu Thoát",
|
||||
"email_address": "Địa Chỉ Email",
|
||||
"config_created": "Đã Tạo Cấu Hình",
|
||||
"verification_failed": "Xác Minh Thất Bại",
|
||||
"verification_error": "Lỗi Xác Minh: {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",
|
||||
"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"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Trình 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}",
|
||||
"reading_auth": "Đang Đọc Tệp Xác Thực",
|
||||
"updating_auth": "Đang Cập Nhật Thông Tin Xác Thực",
|
||||
"auth_updated": "Cập Nhật Thông Tin Xác Thực Thành Công",
|
||||
"auth_update_failed": "Cập Nhật Thông Tin Xác Thực Thất Bại: {error}",
|
||||
"auth_file_created": "Đã Tạo Tệp Xác Thực",
|
||||
"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_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": "Không thể kết nối đến cơ sở dữ liệu: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Đang Tạo Email Mới",
|
||||
"blocked_domain": "Tên Miền Bị Chặn",
|
||||
"select_domain": "Đang Chọn Tên Miền Ngẫu Nhiên",
|
||||
"copy_email": "Đang Sao Chép Địa Chỉ Email",
|
||||
"enter_mailbox": "Đang Vào Hộp Thư",
|
||||
"refresh_mailbox": "Đang Làm Mới Hộp Thư",
|
||||
"check_verification": "Đang Kiểm Tra Mã Xác Minh",
|
||||
"verification_found": "Đã Tìm Thấy Mã Xác Minh",
|
||||
"verification_not_found": "Không Tìm Thấy Mã Xác Minh",
|
||||
"browser_error": "Lỗi Điều Khiển Trình Duyệt: {error}",
|
||||
"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",
|
||||
"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",
|
||||
"select_email_domain_success": "Chọn Tên Miền Email Thành Công",
|
||||
"get_email_name": "Lấy Tên Email",
|
||||
"get_email_name_success": "Lấy Tên Email Thành Công",
|
||||
"get_email_address": "Lấy Địa Chỉ Email",
|
||||
"get_email_address_success": "Lấy Địa Chỉ Email Thành Công",
|
||||
"enter_mailbox_success": "Vào Hộp Thư Thành Công",
|
||||
"found_verification_code": "Đã Tìm Thấy Mã Xác Minh",
|
||||
"get_cursor_session_token": "Lấy Token Phiên Cursor",
|
||||
"get_cursor_session_token_success": "Lấy Token Phiên Cursor Thành Công",
|
||||
"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",
|
||||
"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",
|
||||
"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}",
|
||||
"refreshing": "Đang Làm Mới Email",
|
||||
"refresh_success": "Làm Mới Email Thành Công",
|
||||
"refresh_error": "Lỗi Làm Mới Email: {error}",
|
||||
"refresh_button_not_found": "Không Tìm Thấy Nút Làm Mới",
|
||||
"verification_found": "Đã Tìm Thấy Xác Minh",
|
||||
"verification_not_found": "Không Tìm Thấy Xác Minh",
|
||||
"verification_error": "Lỗi Xác Minh: {error}",
|
||||
"verification_code_found": "Đã Tìm Thấy Mã Xác Minh",
|
||||
"verification_code_not_found": "Không Tìm Thấy Mã Xác Minh",
|
||||
"verification_code_error": "Lỗi Mã Xác Minh: {error}",
|
||||
"address": "Địa Chỉ Email",
|
||||
"all_domains_blocked": "Tất Cả Tên Miền Bị Chặn, Đang Chuyển Dịch Vụ",
|
||||
"no_available_domains_after_filtering": "Không Có Tên Miền Khả Dụng Sau Khi Lọc",
|
||||
"switching_service": "Đang Chuyển Sang Dịch Vụ {service}",
|
||||
"domains_list_error": "Không Thể Lấy Danh Sách Tên Miền: {error}",
|
||||
"failed_to_get_available_domains": "Không Thể Lấy Tên Miền Khả Dụng",
|
||||
"domains_excluded": "Tên Miền Bị Loại Trừ: {domains}",
|
||||
"failed_to_create_account": "Không Thể Tạo Tài Khoản",
|
||||
"account_creation_error": "Lỗi Tạo Tài Khoản: {error}",
|
||||
"blocked_domains": "Tên Miền Bị Chặn: {domains}",
|
||||
"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_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}"
|
||||
},
|
||||
"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_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",
|
||||
"killing_processes": "Đang Kết Thúc Các Tiến Trình",
|
||||
"processes_killed": "Đã Kết Thúc Các Tiến Trình",
|
||||
"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"
|
||||
},
|
||||
"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'.",
|
||||
"development_version": "Phiên Bản Phát Triển {current} > {latest}",
|
||||
"changelog_title": "Nhật ký thay đổi"
|
||||
},
|
||||
"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ị",
|
||||
"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}",
|
||||
"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_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_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_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",
|
||||
"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_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",
|
||||
"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}",
|
||||
"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ử...",
|
||||
"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}",
|
||||
"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",
|
||||
"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",
|
||||
"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...",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
581
locales/zh_cn.json
Normal file
@@ -0,0 +1,581 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "可用选项",
|
||||
"exit": "退出程序",
|
||||
"reset": "重置机器标识",
|
||||
"register": "注册新 Cursor 账号",
|
||||
"register_google": "使用 Google 账号注册",
|
||||
"register_github": "使用 GitHub 账号注册",
|
||||
"register_manual": "使用自定义邮箱注册",
|
||||
"quit": "关闭 Cursor 应用",
|
||||
"select_language": "更改语言",
|
||||
"input_choice": "请输入您的选择 ({choices})",
|
||||
"invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
|
||||
"program_terminated": "程序已被用户终止",
|
||||
"error_occurred": "发生错误:{error},请重试",
|
||||
"press_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": "显示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英语",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁体中文",
|
||||
"vi": "越南语",
|
||||
"nl": "荷兰语",
|
||||
"de": "德语",
|
||||
"fr": "法语",
|
||||
"pt": "葡萄牙语",
|
||||
"ru": "俄语",
|
||||
"tr": "土耳其语",
|
||||
"bg": "保加利亚语",
|
||||
"es": "西班牙语"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "开始退出 Cursor",
|
||||
"no_process": "未发现运行中的 Cursor 进程",
|
||||
"terminating": "正在终止进程 {pid}",
|
||||
"waiting": "等待进程退出",
|
||||
"success": "所有 Cursor 进程已正常关闭",
|
||||
"timeout": "以下进程未能在规定时间内关闭: {pids}",
|
||||
"error": "关闭 Cursor 进程时发生错误: {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": "按回车键退出",
|
||||
"updating_system_ids": "更新系统ID",
|
||||
"system_ids_updated": "系统ID更新成功",
|
||||
"system_ids_update_failed": "系统ID更新失败: {error}",
|
||||
"unsupported_os": "不支持的操作系统: {os}",
|
||||
"linux_path_not_found": "Linux路径未找到",
|
||||
"windows_guid_updated": "Windows GUID更新成功",
|
||||
"windows_permission_denied": "Windows权限拒绝",
|
||||
"windows_guid_update_failed": "Windows GUID更新失败",
|
||||
"macos_uuid_updated": "macOS UUID更新成功",
|
||||
"plutil_command_failed": "plutil命令失败",
|
||||
"macos_uuid_update_failed": "macOS UUID更新失败",
|
||||
"start_patching": "开始修补getMachineId",
|
||||
"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机器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机器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows机器ID失败: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows机器GUID失败: {error}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor 注册工具",
|
||||
"start": "正在启动注册流程...",
|
||||
"browser_started": "浏览器已成功打开",
|
||||
"password_success": "密码设置成功",
|
||||
"password_error": "无法设置密码:{error},请重试",
|
||||
"waiting_for_page_load": "页面加载中...",
|
||||
"mailbox": "已成功访问邮箱",
|
||||
"waiting_for_second_verification": "等待邮箱验证...",
|
||||
"waiting_for_verification_code": "等待验证码...",
|
||||
"first_verification_passed": "初始验证通过",
|
||||
"register_start": "开始注册流程",
|
||||
"form_submitted": "表单已提交,开始验证...",
|
||||
"filling_form": "填写注册信息",
|
||||
"visiting_url": "访问URL",
|
||||
"basic_info": "基本信息提交完成",
|
||||
"handling_turnstile": "正在处理安全验证...",
|
||||
"retry_verification": "正在重试验证...",
|
||||
"detect_turnstile": "正在检查安全验证...",
|
||||
"verification_success": "安全验证通过",
|
||||
"starting_browser": "正在打开浏览器...",
|
||||
"form_success": "表单提交成功",
|
||||
"handle_turnstile": "处理 Turnstile 验证",
|
||||
"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}",
|
||||
"press_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": "注册完成!",
|
||||
"set_password": "设置密码",
|
||||
"basic_info_submitted": "基本信息提交完成",
|
||||
"cursor_auth_info_updated": "Cursor 认证信息更新成功",
|
||||
"cursor_auth_info_update_failed": "Cursor 认证信息更新失败",
|
||||
"reset_machine_id": "重置机器ID",
|
||||
"account_info_saved": "账户信息已保存",
|
||||
"save_account_info_failed": "保存账户信息失败",
|
||||
"get_email_address": "获取邮箱地址",
|
||||
"register_process_error": "注册流程错误: {error}",
|
||||
"update_cursor_auth_info": "更新Cursor认证信息",
|
||||
"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秒内尝试..."
|
||||
},
|
||||
"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": "按回车键退出",
|
||||
"connected_to_database": "已连接到数据库",
|
||||
"database_updated_successfully": "数据库更新成功",
|
||||
"database_connection_closed": "数据库连接已关闭",
|
||||
"updating_pair": "更新键值对",
|
||||
"db_not_found": "未找到数据库文件:{path}",
|
||||
"db_permission_error": "无法访问数据库文件,请检查权限",
|
||||
"db_connection_error": "连接数据库失败:{error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "生成新邮箱",
|
||||
"select_domain": "选择随机域名",
|
||||
"copy_email": "复制邮箱地址",
|
||||
"enter_mailbox": "进入邮箱",
|
||||
"blocked_domain": "被屏蔽的域名",
|
||||
"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": "Token已保存到 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 Session Token",
|
||||
"get_cursor_session_token_success": "获取Cursor Session Token成功",
|
||||
"get_cursor_session_token_failed": "获取Cursor Session Token失败",
|
||||
"save_token_failed": "保存Token失败",
|
||||
"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": "获取可用域名失败",
|
||||
"blocked_domains_loaded": "加载了 {count} 个被屏蔽的域名",
|
||||
"domains_excluded": "排除了 {domains} 个被屏蔽的域名",
|
||||
"failed_to_create_account": "创建账户失败",
|
||||
"account_creation_error": "账户创建错误: {error}",
|
||||
"blocked_domains": "被屏蔽的域名: {domains}",
|
||||
"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": "按回车键退出",
|
||||
"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}"
|
||||
},
|
||||
"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": "正在生成新的机器 ID",
|
||||
"saving_new_config": "正在将新配置保存为 JSON",
|
||||
"success": "Cursor 重置成功",
|
||||
"error": "Cursor 重置失败:{error}",
|
||||
"press_enter": "按回车键退出",
|
||||
"reset_machine_id": "重置机器 ID",
|
||||
"database_connection_closed": "数据库连接已关闭",
|
||||
"database_updated_successfully": "数据库更新成功",
|
||||
"connected_to_database": "已连接到数据库",
|
||||
"updating_pair": "正在更新键值对",
|
||||
"db_not_found": "未找到数据库文件,路径:{path}",
|
||||
"db_permission_error": "无法访问数据库文件,请检查权限",
|
||||
"db_connection_error": "连接数据库失败:{error}",
|
||||
"feature_title": "功能介绍",
|
||||
"feature_1": "完全移除 Cursor AI 设置和配置",
|
||||
"feature_2": "清除所有缓存数据,包括 AI 历史记录和提示",
|
||||
"feature_3": "重置机器 ID 以绕过试用检测",
|
||||
"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": "正在重置机器 ID 以绕过试用检测...",
|
||||
"created_machine_id": "已创建新的机器 ID:{path}",
|
||||
"error_creating_machine_id": "创建机器 ID 文件 {path} 时出错:{error}",
|
||||
"error_searching": "在 {path} 搜索文件时出错:{error}",
|
||||
"created_extended_trial_info": "已创建新的扩展试用信息:{path}",
|
||||
"error_creating_trial_info": "创建试用信息文件 {path} 时出错:{error}",
|
||||
"resetting_cursor_ai_editor": "正在重置 Cursor AI 编辑器... 请稍候。",
|
||||
"reset_cancelled": "重置已取消,未进行任何更改。",
|
||||
"windows_machine_id_modification_skipped": "跳过 Windows 机器 ID 修改:{error}",
|
||||
"linux_machine_id_modification_skipped": "跳过 Linux machine-id 修改:{error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "注意:完整的机器 ID 重置可能需要以管理员身份运行",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "注意:完整的系统 machine-id 重置可能需要 sudo 权限",
|
||||
"windows_registry_instructions": "📝 注意:在 Windows 上进行完整重置,您可能还需要清理注册表项。",
|
||||
"windows_registry_instructions_2": " 运行 'regedit' 并搜索 HKEY_CURRENT_USER\\Software\\ 下包含 'Cursor' 或 'CursorAI' 的键并删除它们。\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 网站前清除浏览器的 Cookie 和缓存",
|
||||
"reset_log_8": "如果仍有问题,尝试将 Cursor AI 安装到不同位置",
|
||||
"reset_log_9": "如遇到任何问题,请到 Github Issue Tracker 提交问题:https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "发生意外错误:{error}",
|
||||
"report_issue": "请在 Github Issue Tracker 报告此问题:https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "用户中断进程,正在退出...",
|
||||
"return_to_main_menu": "返回主菜单...",
|
||||
"process_interrupted": "进程已中断,正在退出...",
|
||||
"press_enter_to_return_to_main_menu": "按回车键返回主菜单...",
|
||||
"removing_known": "正在移除已知的试用/授权文件",
|
||||
"performing_deep_scan": "正在进行深度扫描以查找其他试用/授权文件",
|
||||
"found_additional_potential_license_trial_files": "发现 {count} 个其他潜在的试用/授权文件",
|
||||
"checking_for_electron_localstorage_files": "正在检查 Electron localStorage 文件",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "深度扫描中未发现其他试用/授权文件",
|
||||
"removing_electron_localstorage_files": "正在移除 Electron localStorage 文件",
|
||||
"electron_localstorage_files_removed": "Electron localStorage 文件已移除",
|
||||
"electron_localstorage_files_removal_error": "移除 Electron localStorage 文件时出错:{error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage 文件移除完成",
|
||||
"warning_title": "警告",
|
||||
"warning_1": "此操作将删除所有 Cursor AI 设置、",
|
||||
"warning_2": "配置和缓存数据。此操作无法撤销。",
|
||||
"warning_3": "您的代码文件 **不会** 受到影响,工具设计为",
|
||||
"warning_4": "仅针对 Cursor AI 编辑器文件和试用检测机制。",
|
||||
"warning_5": "系统中的其他应用程序不会受到影响。",
|
||||
"warning_6": "运行此工具后,您需要重新设置 Cursor AI。",
|
||||
"warning_7": "请自行承担风险",
|
||||
"removed": "已删除:{path}",
|
||||
"failed_to_reset_machine_guid": "无法重置机器 GUID",
|
||||
"failed_to_remove": "无法删除:{path}",
|
||||
"failed_to_delete_file": "无法删除文件:{path}",
|
||||
"failed_to_delete_directory": "无法删除目录:{path}",
|
||||
"failed_to_delete_file_or_directory": "无法删除文件或目录:{path}",
|
||||
"deep_scanning": "正在进行深度扫描以查找其他试用/授权文件",
|
||||
"resetting_cursor": "正在重置 Cursor AI 编辑器... 请稍候。",
|
||||
"completed_in": "完成时间:{time} 秒",
|
||||
"cursor_reset_completed": "Cursor AI 编辑器已完全重置且绕过试用检测!",
|
||||
"cursor_reset_failed": "Cursor AI 编辑器重置失败:{error}",
|
||||
"cursor_reset_cancelled": "Cursor AI 编辑器重置已取消,未进行任何更改。",
|
||||
"operation_cancelled": "操作已取消,未进行任何更改。"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "GitHub + Cursor AI 注册自动化",
|
||||
"features_header": "功能",
|
||||
"feature1": "使用 1secmail 生成临时邮箱",
|
||||
"feature2": "使用随机凭证注册新的 GitHub 账户",
|
||||
"feature3": "自动验证 GitHub 邮箱",
|
||||
"feature4": "使用 GitHub 认证登录 Cursor AI",
|
||||
"feature5": "重置机器 ID 以绕过试用检测",
|
||||
"feature6": "保存所有凭证到文件",
|
||||
"warnings_header": "警告",
|
||||
"warning1": "此脚本自动化账户创建,可能违反 GitHub/Cursor 服务条款",
|
||||
"warning2": "需要互联网访问和管理员权限",
|
||||
"warning3": "CAPTCHA 或额外验证可能会中断自动化",
|
||||
"warning4": "请负责任地使用,风险自负",
|
||||
"confirm": "您确定要继续吗?",
|
||||
"invalid_choice": "无效选择。请输入 'yes' 或 'no'",
|
||||
"cancelled": "操作已取消",
|
||||
"program_terminated": "程序已由用户终止",
|
||||
"starting_automation": "开始自动化...",
|
||||
"github_username": "GitHub 用户名",
|
||||
"github_password": "GitHub 密码",
|
||||
"email_address": "邮箱地址",
|
||||
"credentials_saved": "这些凭证已保存到 github_cursor_accounts.txt",
|
||||
"completed_successfully": "GitHub + Cursor 注册成功",
|
||||
"registration_encountered_issues": "GitHub + Cursor 注册遇到问题",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "检查浏览器窗口进行手动干预或稍后再试"
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "订阅",
|
||||
"trial_remaining": "剩余试用",
|
||||
"days": "天",
|
||||
"subscription_not_found": "订阅信息未找到",
|
||||
"email_not_found": "邮箱未找到",
|
||||
"failed_to_get_account": "获取账户信息失败",
|
||||
"config_not_found": "配置未找到。",
|
||||
"failed_to_get_usage": "获取使用信息失败",
|
||||
"failed_to_get_subscription": "获取订阅信息失败",
|
||||
"failed_to_get_email": "获取邮箱地址失败",
|
||||
"failed_to_get_token": "获取 token 失败",
|
||||
"failed_to_get_account_info": "获取账户信息失败",
|
||||
"title": "账户信息",
|
||||
"email": "邮箱",
|
||||
"token": "Token",
|
||||
"usage": "使用量",
|
||||
"subscription_type": "订阅类型",
|
||||
"remaining_trial": "剩余试用",
|
||||
"days_remaining": "剩余天数",
|
||||
"premium": "高级",
|
||||
"pro": "专业",
|
||||
"pro_trial": "专业试用",
|
||||
"team": "团队",
|
||||
"enterprise": "企业",
|
||||
"free": "免费",
|
||||
"active": "活跃",
|
||||
"inactive": "非活跃",
|
||||
"premium_usage": "高级使用量",
|
||||
"basic_usage": "基础使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久访问已启用"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
"configuration": "配置",
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"config_directory": "配置目录",
|
||||
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目录",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "请确保 Cursor 已安装并至少运行一次",
|
||||
"storage_directory_not_found": "未找到存储目录",
|
||||
"storage_file_found": "找到存储文件",
|
||||
"file_size": "文件大小",
|
||||
"file_permissions": "文件权限",
|
||||
"file_owner": "文件所有者",
|
||||
"file_group": "文件组",
|
||||
"error_getting_file_stats": "获取文件统计信息时出错",
|
||||
"permission_denied": "权限拒绝",
|
||||
"try_running": "尝试运行: {command}",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "存储文件为空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已损坏,请重新安装 Cursor",
|
||||
"storage_file_not_found": "未找到存储文件",
|
||||
"error_checking_linux_paths": "检查 Linux 路径时出错",
|
||||
"config_option_added": "添加配置选项",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已创建",
|
||||
"config_setup_error": "配置设置错误",
|
||||
"storage_file_is_valid_and_contains_data": "存储文件有效且包含数据",
|
||||
"error_reading_storage_file": "读取存储文件时出错",
|
||||
"also_checked": "也检查了 {path}"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到认证按钮",
|
||||
"authentication_failed": "认证失败: {error}",
|
||||
"found_cookies": "找到 {count} 个 Cookie",
|
||||
"token_extraction_error": "Token 提取错误: {error}",
|
||||
"authentication_successful": "认证成功 - 邮箱: {email}",
|
||||
"missing_authentication_data": "缺少认证数据: {data}",
|
||||
"failed_to_delete_account": "删除账户失败: {error}",
|
||||
"invalid_authentication_type": "无效的认证类型",
|
||||
"auth_update_success": "认证更新成功",
|
||||
"browser_closed": "浏览器已关闭",
|
||||
"auth_update_failed": "认证更新失败",
|
||||
"google_start": "Google 开始",
|
||||
"github_start": "Github 开始",
|
||||
"usage_count": "使用次数: {usage}",
|
||||
"account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "开始新的认证过程...",
|
||||
"failed_to_delete_expired_account": "删除过期账户失败",
|
||||
"could_not_check_usage_count": "无法检查使用次数: {error}",
|
||||
"found_email": "找到邮箱: {email}",
|
||||
"could_not_find_email": "未找到邮箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用次数: {error}",
|
||||
"already_on_settings_page": "已处于设置页面",
|
||||
"failed_to_extract_auth_info": "提取认证信息失败: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
|
||||
"found_default_chrome_profile": "找到默认 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化浏览器设置...",
|
||||
"detected_platform": "检测平台: {platform}",
|
||||
"running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
|
||||
"consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
|
||||
"no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的浏览器: {platform}",
|
||||
"using_browser_profile": "使用浏览器配置文件: {profile}",
|
||||
"starting_browser": "正在启动浏览器: {path}",
|
||||
"browser_setup_completed": "浏览器设置完成",
|
||||
"browser_setup_failed": "浏览器设置失败: {error}",
|
||||
"try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "开始 Google 认证...",
|
||||
"starting_github_authentication": "开始 Github 认证...",
|
||||
"waiting_for_authentication": "等待认证...",
|
||||
"page_changed_checking_auth": "页面改变, 检查认证...",
|
||||
"status_check_error": "状态检查错误: {error}",
|
||||
"authentication_timeout": "认证超时",
|
||||
"account_is_still_valid": "账户仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "开始重新认证过程...",
|
||||
"starting_new_google_authentication": "开始新的 Google 认证...",
|
||||
"failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
|
||||
"navigating_to_authentication_page": "正在导航到认证页面...",
|
||||
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
|
||||
"found_browser_data_directory": "找到浏览器数据目录: {path}",
|
||||
"authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}"
|
||||
}
|
||||
|
||||
}
|
||||
560
locales/zh_tw.json
Normal file
@@ -0,0 +1,560 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "可用選項",
|
||||
"exit": "退出程式",
|
||||
"reset": "重置機器識別碼",
|
||||
"register": "註冊新 Cursor 帳號",
|
||||
"register_manual": "使用自訂郵箱註冊",
|
||||
"quit": "關閉 Cursor 應用程式",
|
||||
"select_language": "變更語言",
|
||||
"input_choice": "請輸入您的選擇 ({choices})",
|
||||
"invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
|
||||
"program_terminated": "程式已被使用者終止",
|
||||
"error_occurred": "發生錯誤:{error},請重試",
|
||||
"press_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": "顯示配置"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英文",
|
||||
"zh_cn": "簡體中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "越南文",
|
||||
"nl": "荷蘭文",
|
||||
"de": "德文",
|
||||
"fr": "法文",
|
||||
"pt": "葡萄牙文",
|
||||
"ru": "俄文",
|
||||
"tr": "土耳其文",
|
||||
"bg": "保加利亞文",
|
||||
"es": "西班牙文"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "開始退出 Cursor",
|
||||
"no_process": "未發現運行中的 Cursor 進程",
|
||||
"terminating": "正在終止進程 {pid}",
|
||||
"waiting": "等待進程退出",
|
||||
"success": "所有 Cursor 進程已正常關閉",
|
||||
"timeout": "以下進程未能在規定時間內關閉: {pids}",
|
||||
"error": "關閉 Cursor 進程時發生錯誤: {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": "按回車鍵退出",
|
||||
"updating_system_ids": "更新系統ID",
|
||||
"system_ids_updated": "系統ID更新成功",
|
||||
"system_ids_update_failed": "系統ID更新失敗: {error}",
|
||||
"unsupported_os": "不支持的操作系統: {os}",
|
||||
"linux_path_not_found": "Linux路徑未找到",
|
||||
"windows_guid_updated": "Windows GUID更新成功",
|
||||
"windows_permission_denied": "Windows權限拒絕",
|
||||
"windows_guid_update_failed": "Windows GUID更新失敗",
|
||||
"macos_uuid_updated": "macOS UUID更新成功",
|
||||
"plutil_command_failed": "plutil命令失敗",
|
||||
"macos_uuid_update_failed": "macOS UUID更新失敗",
|
||||
"start_patching": "開始修補getMachineId",
|
||||
"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機器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機器ID更新成功",
|
||||
"update_windows_machine_id_failed": "更新Windows機器ID失敗: {error}",
|
||||
"update_windows_machine_guid_failed": "更新Windows機器GUID失敗: {error}"
|
||||
},
|
||||
|
||||
"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": "已成功存取郵箱",
|
||||
"visiting_url": "訪問URL",
|
||||
"register_start": "開始註冊流程",
|
||||
"form_submitted": "表單已提交,開始驗證...",
|
||||
"filling_form": "填寫註冊信息",
|
||||
"basic_info": "基本信息提交完成",
|
||||
"handle_turnstile": "處理 Turnstile 驗證",
|
||||
"no_turnstile": "未檢測到 Turnstile 驗證",
|
||||
"turnstile_passed": "驗證通過",
|
||||
"verification_start": "開始獲取驗證碼",
|
||||
"verification_code_filled": "驗證碼填寫完成",
|
||||
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
|
||||
"detect_login_page": "檢測到登錄頁面,開始登錄...",
|
||||
"cursor_registration_completed": "註冊完成!",
|
||||
"set_password": "設置密碼",
|
||||
"basic_info_submitted": "基本信息提交完成",
|
||||
"cursor_auth_info_updated": "Cursor 認證信息更新成功",
|
||||
"cursor_auth_info_update_failed": "Cursor 認證信息更新失敗",
|
||||
"reset_machine_id": "重置機器ID",
|
||||
"account_info_saved": "賬戶信息已保存",
|
||||
"save_account_info_failed": "保存賬戶信息失敗",
|
||||
"get_email_address": "獲取郵箱地址",
|
||||
"register_process_error": "註冊流程錯誤: {error}",
|
||||
"update_cursor_auth_info": "更新Cursor認證信息",
|
||||
"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秒內嘗試..."
|
||||
},
|
||||
"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": "按回車鍵退出",
|
||||
"connected_to_database": "已連接到數據庫",
|
||||
"database_updated_successfully": "數據庫更新成功",
|
||||
"database_connection_closed": "數據庫連接已關閉",
|
||||
"updating_pair": "更新鍵值對",
|
||||
"db_not_found": "未找到數據庫文件:{path}",
|
||||
"db_permission_error": "無法訪問數據庫文件,請檢查權限",
|
||||
"db_connection_error": "連接數據庫失敗:{error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "生成新郵箱",
|
||||
"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": "Token已保存到 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 Session Token",
|
||||
"get_cursor_session_token_success": "獲取Cursor Session Token成功",
|
||||
"get_cursor_session_token_failed": "獲取Cursor Session Token失敗",
|
||||
"save_token_failed": "保存Token失敗",
|
||||
"blocked_domain": "被屏蔽的域名",
|
||||
"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": "加載被屏蔽的域名: {domains}",
|
||||
"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": "按回車鍵退出",
|
||||
"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}"
|
||||
},
|
||||
"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": "正在生成新的機器 ID",
|
||||
"saving_new_config": "正在將新配置保存到 JSON",
|
||||
"success": "Cursor 重置成功",
|
||||
"error": "Cursor 重置失敗:{error}",
|
||||
"press_enter": "按 Enter 鍵退出",
|
||||
"reset_machine_id": "重置機器 ID",
|
||||
"database_connection_closed": "資料庫連線已關閉",
|
||||
"database_updated_successfully": "資料庫更新成功",
|
||||
"connected_to_database": "已連接到資料庫",
|
||||
"updating_pair": "正在更新鍵值對",
|
||||
"db_not_found": "未找到資料庫檔案,路徑:{path}",
|
||||
"db_permission_error": "無法訪問資料庫檔案,請檢查權限",
|
||||
"db_connection_error": "連接資料庫失敗:{error}",
|
||||
"feature_title": "功能特色",
|
||||
"feature_1": "完全移除 Cursor AI 設定與配置",
|
||||
"feature_2": "清除所有快取資料,包括 AI 歷史與提示",
|
||||
"feature_3": "重置機器 ID 以繞過試用偵測",
|
||||
"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": "正在重置機器 ID 以繞過試用偵測...",
|
||||
"created_machine_id": "已建立新的機器 ID:{path}",
|
||||
"error_creating_machine_id": "建立機器 ID 檔案 {path} 時出錯:{error}",
|
||||
"error_searching": "在 {path} 搜尋檔案時出錯:{error}",
|
||||
"created_extended_trial_info": "已建立新的擴展試用資訊:{path}",
|
||||
"error_creating_trial_info": "建立試用資訊檔案 {path} 時出錯:{error}",
|
||||
"resetting_cursor_ai_editor": "正在重置 Cursor AI 編輯器... 請稍候。",
|
||||
"reset_cancelled": "重置已取消,未進行任何更改。",
|
||||
"windows_machine_id_modification_skipped": "跳過 Windows 機器 ID 修改:{error}",
|
||||
"linux_machine_id_modification_skipped": "跳過 Linux machine-id 修改:{error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "注意:完整的機器 ID 重置可能需要以管理員身份執行",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "注意:完整的系統 machine-id 重置可能需要 sudo 權限",
|
||||
"windows_registry_instructions": "📝 注意:在 Windows 上進行完整重置,您可能還需要清理登錄檔項目。",
|
||||
"windows_registry_instructions_2": " 執行 'regedit' 並搜尋 HKEY_CURRENT_USER\\Software\\ 下包含 'Cursor' 或 'CursorAI' 的鍵並刪除它們。\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 網站前清除瀏覽器的 Cookie 與快取",
|
||||
"reset_log_8": "若仍有問題,嘗試將 Cursor AI 安裝到不同位置",
|
||||
"reset_log_9": "若遇到任何問題,請到 Github Issue Tracker 提交問題:https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "發生非預期錯誤:{error}",
|
||||
"report_issue": "請在 Github Issue Tracker 回報此問題:https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "使用者中斷流程,正在退出...",
|
||||
"return_to_main_menu": "返回主選單...",
|
||||
"process_interrupted": "流程已中斷,正在退出...",
|
||||
"press_enter_to_return_to_main_menu": "按 Enter 鍵返回主選單...",
|
||||
"removing_known": "正在移除已知的試用/授權檔案",
|
||||
"performing_deep_scan": "正在進行深度掃描以查找其他試用/授權檔案",
|
||||
"found_additional_potential_license_trial_files": "找到 {count} 個其他潛在試用/授權檔案",
|
||||
"checking_for_electron_localstorage_files": "正在檢查 Electron localStorage 檔案",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "深度掃描中未發現其他試用/授權檔案",
|
||||
"removing_electron_localstorage_files": "正在移除 Electron localStorage 檔案",
|
||||
"electron_localstorage_files_removed": "已移除 Electron localStorage 檔案",
|
||||
"electron_localstorage_files_removal_error": "移除 Electron localStorage 檔案時出錯:{error}",
|
||||
"removing_electron_localstorage_files_completed": "Electron localStorage 檔案移除完成",
|
||||
"warning_title": "警告",
|
||||
"warning_1": "此操作將刪除所有 Cursor AI 設定、",
|
||||
"warning_2": "配置與快取資料。此操作無法還原。",
|
||||
"warning_3": "您的程式碼檔案將 **不會** 受到影響,且此工具僅針對",
|
||||
"warning_4": "Cursor AI 編輯器檔案與試用偵測機制。",
|
||||
"warning_5": "系統中的其他應用程式不會受到影響。",
|
||||
"warning_6": "執行此工具後,您需要重新設定 Cursor AI。",
|
||||
"warning_7": "請自行承擔風險",
|
||||
"removed": "已刪除:{path}",
|
||||
"failed_to_reset_machine_guid": "無法重置機器 GUID",
|
||||
"failed_to_remove": "無法刪除:{path}",
|
||||
"failed_to_delete_file": "無法刪除檔案:{path}",
|
||||
"failed_to_delete_directory": "無法刪除目錄:{path}",
|
||||
"failed_to_delete_file_or_directory": "無法刪除檔案或目錄:{path}",
|
||||
"deep_scanning": "正在進行深度掃描以查找其他試用/授權檔案",
|
||||
"resetting_cursor": "正在重置 Cursor AI 編輯器... 請稍候。",
|
||||
"completed_in": "完成時間:{time} 秒",
|
||||
"cursor_reset_completed": "Cursor AI 編輯器已完全重置且繞過試用偵測!",
|
||||
"cursor_reset_failed": "Cursor AI 編輯器重置失敗:{error}",
|
||||
"cursor_reset_cancelled": "Cursor AI 編輯器重置已取消,未進行任何更改。",
|
||||
"operation_cancelled": "操作已取消,未進行任何更改。"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "GitHub + Cursor AI 注册自动化",
|
||||
"features_header": "功能",
|
||||
"feature1": "使用 1secmail 生成临时邮箱",
|
||||
"feature2": "使用随机凭证注册新的 GitHub 账户",
|
||||
"feature3": "自动验证 GitHub 邮箱",
|
||||
"feature4": "使用 GitHub 认证登录 Cursor AI",
|
||||
"feature5": "重置机器 ID 以绕过试用检测",
|
||||
"feature6": "保存所有凭证到文件",
|
||||
"warnings_header": "警告",
|
||||
"warning1": "此脚本自动化账户创建,可能违反 GitHub/Cursor 服务条款",
|
||||
"warning2": "需要互联网访问和管理员权限",
|
||||
"warning3": "CAPTCHA 或额外验证可能会中断自动化",
|
||||
"warning4": "请负责任地使用,风险自负",
|
||||
"confirm": "您确定要继续吗?",
|
||||
"invalid_choice": "无效选择。请输入 'yes' 或 'no'",
|
||||
"cancelled": "操作已取消",
|
||||
"program_terminated": "程序已由用户终止",
|
||||
"starting_automation": "開始自動化...",
|
||||
"github_username": "GitHub 用戶名",
|
||||
"github_password": "GitHub 密碼",
|
||||
"email_address": "郵箱地址",
|
||||
"credentials_saved": "這些憑證已保存到 github_cursor_accounts.txt",
|
||||
"completed_successfully": "GitHub + Cursor 註冊成功",
|
||||
"registration_encountered_issues": "GitHub + Cursor 註冊遇到問題",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "檢查瀏覽器視窗進行手動干預或稍後再試"
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "訂閱",
|
||||
"trial_remaining": "剩餘試用",
|
||||
"days": "天",
|
||||
"subscription_not_found": "訂閱信息未找到",
|
||||
"email_not_found": "郵箱未找到",
|
||||
"failed_to_get_account": "獲取帳戶信息失敗",
|
||||
"config_not_found": "配置未找到。",
|
||||
"failed_to_get_usage": "獲取使用信息失敗",
|
||||
"failed_to_get_subscription": "獲取訂閱信息失敗",
|
||||
"failed_to_get_email": "獲取郵箱地址失敗",
|
||||
"failed_to_get_token": "獲取 token 失敗",
|
||||
"failed_to_get_account_info": "獲取帳戶信息失敗",
|
||||
"title": "帳戶信息",
|
||||
"email": "郵箱",
|
||||
"token": "Token",
|
||||
"usage": "使用量",
|
||||
"subscription_type": "訂閱類型",
|
||||
"remaining_trial": "剩餘試用",
|
||||
"days_remaining": "剩餘天數",
|
||||
"premium": "高級",
|
||||
"pro": "專業",
|
||||
"pro_trial": "專業試用",
|
||||
"team": "團隊",
|
||||
"enterprise": "企業",
|
||||
"free": "免費",
|
||||
"active": "活躍",
|
||||
"inactive": "非活躍",
|
||||
"premium_usage": "高級使用量",
|
||||
"basic_usage": "基礎使用量",
|
||||
"usage_not_found": "使用量未找到",
|
||||
"lifetime_access_enabled": "永久訪問已啟用"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "配置未找到。",
|
||||
"configuration": "配置",
|
||||
"enabled": "已啟用",
|
||||
"disabled": "已禁用",
|
||||
"config_directory": "配置目錄",
|
||||
"neither_cursor_nor_cursor_directory_found": "未找到 Cursor 或 Cursor 目錄",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "請確保 Cursor 已安裝並至少運行一次",
|
||||
"storage_directory_not_found": "未找到儲存目錄",
|
||||
"storage_file_found": "找到儲存文件",
|
||||
"file_size": "文件大小",
|
||||
"file_permissions": "文件權限",
|
||||
"file_owner": "文件所有者",
|
||||
"file_group": "文件組",
|
||||
"error_getting_file_stats": "獲取文件統計信息時出錯",
|
||||
"permission_denied": "權限拒絕",
|
||||
"try_running": "嘗試運行: {command}",
|
||||
"and": "和",
|
||||
"storage_file_is_empty": "儲存文件為空",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "文件可能已損壞,請重新安裝 Cursor",
|
||||
"storage_file_not_found": "未找到儲存文件",
|
||||
"error_checking_linux_paths": "檢查 Linux 路徑時出錯",
|
||||
"config_option_added": "添加配置選項",
|
||||
"config_updated": "配置更新",
|
||||
"config_created": "配置已創建",
|
||||
"config_setup_error": "配置設置錯誤",
|
||||
"storage_file_is_valid_and_contains_data": "儲存文件有效且包含數據",
|
||||
"error_reading_storage_file": "讀取儲存文件時出錯",
|
||||
"also_checked": "也檢查了 {path}"
|
||||
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到認證按鈕",
|
||||
"authentication_failed": "認證失敗: {error}",
|
||||
"found_cookies": "找到 {count} 個 Cookie",
|
||||
"token_extraction_error": "Token 提取錯誤: {error}",
|
||||
"authentication_successful": "認證成功 - 郵箱: {email}",
|
||||
"missing_authentication_data": "缺少認證數據: {data}",
|
||||
"failed_to_delete_account": "刪除帳戶失敗: {error}",
|
||||
"invalid_authentication_type": "無效的認證類型",
|
||||
"auth_update_success": "認證更新成功",
|
||||
"browser_closed": "瀏覽器已關閉",
|
||||
"auth_update_failed": "認證更新失敗",
|
||||
"google_start": "Google 開始",
|
||||
"github_start": "Github 開始",
|
||||
"usage_count": "使用量: {usage}",
|
||||
"account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "開始新的認證過程...",
|
||||
"failed_to_delete_expired_account": "刪除過期帳戶失敗",
|
||||
"could_not_check_usage_count": "無法檢查使用量: {error}",
|
||||
"found_email": "找到郵箱: {email}",
|
||||
"could_not_find_email": "未找到郵箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用量: {erro r}",
|
||||
"already_on_settings_page": "已處於設置頁面",
|
||||
"failed_to_extract_auth_info": "提取認證信息失敗: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
|
||||
"found_default_chrome_profile": "找到默認 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化瀏覽器設置...",
|
||||
"detected_platform": "檢測平台: {platform}",
|
||||
"running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
|
||||
"consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
|
||||
"no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的瀏覽器: {platform}",
|
||||
"using_browser_profile": "使用瀏覽器配置文件: {profile}",
|
||||
"starting_browser": "正在啟動瀏覽器: {path}",
|
||||
"browser_setup_completed": "瀏覽器設置完成成功",
|
||||
"browser_setup_failed": "瀏覽器設置失敗: {error}",
|
||||
"try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_github_authentication": "開始 Github 認證...",
|
||||
"waiting_for_authentication": "等待認證...",
|
||||
"page_changed_checking_auth": "頁面改變, 檢查認證...",
|
||||
"status_check_error": "狀態檢查錯誤: {error}",
|
||||
"authentication_timeout": "認證超時",
|
||||
"account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "開始重新認證過程...",
|
||||
"starting_new_google_authentication": "開始新的 Google 認證...",
|
||||
"failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
|
||||
"navigating_to_authentication_page": "正在導航到認證頁面...",
|
||||
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
|
||||
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
|
||||
"authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
|
||||
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed": "瀏覽器啟動失敗: {error}"
|
||||
}
|
||||
}
|
||||
98
logo.py
@@ -1,25 +1,101 @@
|
||||
from colorama import Fore, Style, init
|
||||
# 初始化 colorama
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
|
||||
# Get the current script directory
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
# Build the full path to the .env file
|
||||
env_path = os.path.join(current_dir, '.env')
|
||||
|
||||
# Load environment variables, specifying the .env file path
|
||||
load_dotenv(env_path)
|
||||
# Get the version number, using the default value if not found
|
||||
version = os.getenv('VERSION', '1.0.0')
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
CURSOR_LOGO = f"""
|
||||
{Fore.CYAN}
|
||||
# get terminal width
|
||||
def get_terminal_width():
|
||||
try:
|
||||
columns, _ = shutil.get_terminal_size()/2
|
||||
return columns
|
||||
except:
|
||||
return 80 # default width
|
||||
|
||||
# center display text (not handling Chinese characters)
|
||||
def center_multiline_text(text, handle_chinese=False):
|
||||
width = get_terminal_width()
|
||||
lines = text.split('\n')
|
||||
centered_lines = []
|
||||
|
||||
for line in lines:
|
||||
# calculate actual display width (remove ANSI color codes)
|
||||
clean_line = line
|
||||
for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Fore.BLUE, Style.RESET_ALL]:
|
||||
clean_line = clean_line.replace(color, '')
|
||||
|
||||
# remove all ANSI escape sequences to get the actual length
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
clean_line = ansi_escape.sub('', clean_line)
|
||||
|
||||
# calculate display width
|
||||
if handle_chinese:
|
||||
# consider Chinese characters occupying two positions
|
||||
display_width = 0
|
||||
for char in clean_line:
|
||||
if ord(char) > 127: # non-ASCII characters
|
||||
display_width += 2
|
||||
else:
|
||||
display_width += 1
|
||||
else:
|
||||
# not handling Chinese characters
|
||||
display_width = len(clean_line)
|
||||
|
||||
# calculate the number of spaces to add
|
||||
padding = max(0, (width - display_width) // 2)
|
||||
centered_lines.append(' ' * padding + line)
|
||||
|
||||
return '\n'.join(centered_lines)
|
||||
|
||||
# original LOGO text
|
||||
LOGO_TEXT = f"""{Fore.CYAN}
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██████╔╝██████╔╝██║ ██║
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═══╝ ██╔══██╗██║ ██║
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██║ ██║╚██████╔╝
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
||||
{Fore.YELLOW}
|
||||
Pro Version Activator
|
||||
{Fore.GREEN}
|
||||
Author: Pin Studios | yeongpin
|
||||
{Style.RESET_ALL}
|
||||
"""
|
||||
{Style.RESET_ALL}"""
|
||||
|
||||
DESCRIPTION_TEXT = f"""{Fore.YELLOW}
|
||||
Pro Version Activator v{version}{Fore.GREEN}
|
||||
Author: Pin Studios (yeongpin)"""
|
||||
|
||||
CONTRIBUTORS_TEXT = f"""{Fore.BLUE}
|
||||
Contributors:
|
||||
BasaiCorp aliensb handwerk2016 Nigel1992
|
||||
UntaDotMy RenjiYuusei imbajin ahmed98Osama
|
||||
bingoohuang mALIk-sHAHId MFaiqKhan httpmerak
|
||||
muhammedfurkan plamkatawe
|
||||
"""
|
||||
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
||||
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
|
||||
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
|
||||
|
||||
# center display LOGO and DESCRIPTION
|
||||
CURSOR_LOGO = center_multiline_text(LOGO_TEXT, handle_chinese=False)
|
||||
CURSOR_DESCRIPTION = center_multiline_text(DESCRIPTION_TEXT, handle_chinese=False)
|
||||
CURSOR_CONTRIBUTORS = center_multiline_text(CONTRIBUTORS_TEXT, handle_chinese=False)
|
||||
CURSOR_OTHER_INFO = center_multiline_text(OTHER_INFO_TEXT, handle_chinese=True)
|
||||
|
||||
def print_logo():
|
||||
print(CURSOR_LOGO)
|
||||
|
||||
print(CURSOR_DESCRIPTION)
|
||||
# print(CURSOR_CONTRIBUTORS)
|
||||
print(CURSOR_OTHER_INFO)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print_logo()
|
||||
print_logo()
|
||||
|
||||
616
main.py
@@ -1,12 +1,28 @@
|
||||
# main.py
|
||||
# This script allows the user to choose which script to run.
|
||||
from logo import print_logo
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from logo import print_logo, version
|
||||
from colorama import Fore, Style, init
|
||||
import locale
|
||||
import platform
|
||||
import requests
|
||||
import subprocess
|
||||
from config import get_config
|
||||
import shutil
|
||||
import re
|
||||
|
||||
# 初始化colorama
|
||||
# Only import windll on Windows systems
|
||||
if platform.system() == 'Windows':
|
||||
import ctypes
|
||||
# Only import windll on Windows systems
|
||||
from ctypes import windll
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
# Define emoji and color constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
@@ -16,58 +32,598 @@ EMOJI = {
|
||||
"RESET": "🔄",
|
||||
"MENU": "📋",
|
||||
"ARROW": "➜",
|
||||
"LANG": "🌐",
|
||||
"UPDATE": "🔄",
|
||||
"ADMIN": "🔐",
|
||||
"AIRDROP": "💰",
|
||||
"ROCKET": "🚀",
|
||||
"STAR": "⭐",
|
||||
"SUN": "🌟",
|
||||
"CONTRIBUTE": "🤝",
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
# Function to check if running as frozen executable
|
||||
def is_frozen():
|
||||
"""Check if the script is running as a frozen executable."""
|
||||
return getattr(sys, 'frozen', False)
|
||||
|
||||
# Function to check admin privileges (Windows only)
|
||||
def is_admin():
|
||||
"""Check if the script is running with admin privileges (Windows only)."""
|
||||
if platform.system() == 'Windows':
|
||||
try:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
||||
except Exception:
|
||||
return False
|
||||
# Always return True for non-Windows to avoid changing behavior
|
||||
return True
|
||||
|
||||
# Function to restart with admin privileges
|
||||
def run_as_admin():
|
||||
"""Restart the current script with admin privileges (Windows only)."""
|
||||
if platform.system() != 'Windows':
|
||||
return False
|
||||
|
||||
try:
|
||||
args = [sys.executable] + sys.argv
|
||||
|
||||
# Request elevation via ShellExecute
|
||||
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} Requesting administrator privileges...{Style.RESET_ALL}")
|
||||
ctypes.windll.shell32.ShellExecuteW(None, "runas", args[0], " ".join('"' + arg + '"' for arg in args[1:]), None, 1)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to restart with admin privileges: {e}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
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.load_translations()
|
||||
|
||||
def detect_system_language(self):
|
||||
"""Detect system language and return corresponding language code"""
|
||||
try:
|
||||
system = platform.system()
|
||||
|
||||
if system == 'Windows':
|
||||
return self._detect_windows_language()
|
||||
else:
|
||||
return self._detect_unix_language()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Failed to detect system language: {e}{Style.RESET_ALL}")
|
||||
return 'en'
|
||||
|
||||
def _detect_windows_language(self):
|
||||
"""Detect language on Windows systems"""
|
||||
try:
|
||||
# Ensure we are on Windows
|
||||
if platform.system() != 'Windows':
|
||||
return 'en'
|
||||
|
||||
# Get keyboard layout
|
||||
user32 = ctypes.windll.user32
|
||||
hwnd = user32.GetForegroundWindow()
|
||||
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')
|
||||
except:
|
||||
return self._detect_unix_language()
|
||||
|
||||
def _detect_unix_language(self):
|
||||
"""Detect language on Unix-like systems (Linux, macOS)"""
|
||||
try:
|
||||
# Get the system locale
|
||||
system_locale = locale.getdefaultlocale()[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'
|
||||
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')
|
||||
|
||||
if not os.path.exists(locales_dir):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
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}")
|
||||
|
||||
def get(self, key, **kwargs):
|
||||
"""Get translated text with fallback support"""
|
||||
try:
|
||||
# Try current language
|
||||
result = self._get_translation(self.current_language, key)
|
||||
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
|
||||
except Exception:
|
||||
return key
|
||||
|
||||
def _get_translation(self, lang_code, key):
|
||||
"""Get translation for a specific language"""
|
||||
try:
|
||||
keys = key.split('.')
|
||||
value = self.translations.get(lang_code, {})
|
||||
for k in keys:
|
||||
if isinstance(value, dict):
|
||||
value = value.get(k, key)
|
||||
else:
|
||||
return key
|
||||
return value
|
||||
except Exception:
|
||||
return key
|
||||
|
||||
def set_language(self, lang_code):
|
||||
"""Set current language with validation"""
|
||||
if lang_code in self.translations:
|
||||
self.current_language = lang_code
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_available_languages(self):
|
||||
"""Get list of available languages"""
|
||||
return list(self.translations.keys())
|
||||
|
||||
# Create translator instance
|
||||
translator = Translator()
|
||||
|
||||
def print_menu():
|
||||
"""打印菜单选项"""
|
||||
print(f"\n{Fore.CYAN}{EMOJI['MENU']} Available Options | 可用选项:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} Exit Program | 退出程序")
|
||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} Reset Machine Manual | 重置机器标识")
|
||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} Register Cursor | 注册 Cursor")
|
||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} Quit Cursor | 退出 Cursor")
|
||||
# 在这里添加更多选项
|
||||
"""Print menu options"""
|
||||
try:
|
||||
config = get_config()
|
||||
if config.getboolean('Utils', 'enabled_account_info'):
|
||||
import cursor_acc_info
|
||||
cursor_acc_info.display_account_info(translator)
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.account_info_error', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
|
||||
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
|
||||
print(f"{Fore.YELLOW}{'─' * 70}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{'─' * 110}{Style.RESET_ALL}")
|
||||
|
||||
# Get terminal width
|
||||
try:
|
||||
terminal_width = shutil.get_terminal_size().columns
|
||||
except:
|
||||
terminal_width = 80 # Default width
|
||||
|
||||
# Define all menu items
|
||||
menu_items = {
|
||||
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
|
||||
1: f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}",
|
||||
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})",
|
||||
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
|
||||
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
|
||||
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
|
||||
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
|
||||
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
|
||||
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
|
||||
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
|
||||
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
|
||||
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}"
|
||||
}
|
||||
|
||||
# Automatically calculate the number of menu items in the left and right columns
|
||||
total_items = len(menu_items)
|
||||
left_column_count = (total_items + 1) // 2 # The number of options displayed on the left (rounded up)
|
||||
|
||||
# Build left and right columns of menus
|
||||
sorted_indices = sorted(menu_items.keys())
|
||||
left_menu = [menu_items[i] for i in sorted_indices[:left_column_count]]
|
||||
right_menu = [menu_items[i] for i in sorted_indices[left_column_count:]]
|
||||
|
||||
# Calculate the maximum display width of left menu items
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
|
||||
def get_display_width(s):
|
||||
"""Calculate the display width of a string, considering Chinese characters and emojis"""
|
||||
# Remove ANSI color codes
|
||||
clean_s = ansi_escape.sub('', s)
|
||||
width = 0
|
||||
for c in clean_s:
|
||||
# Chinese characters and some emojis occupy two character widths
|
||||
if ord(c) > 127:
|
||||
width += 2
|
||||
else:
|
||||
width += 1
|
||||
return width
|
||||
|
||||
max_left_width = 0
|
||||
for item in left_menu:
|
||||
width = get_display_width(item)
|
||||
max_left_width = max(max_left_width, width)
|
||||
|
||||
# Set the starting position of right menu
|
||||
fixed_spacing = 4 # Fixed spacing
|
||||
right_start = max_left_width + fixed_spacing
|
||||
|
||||
# Calculate the number of spaces needed for right menu items
|
||||
spaces_list = []
|
||||
for i in range(len(left_menu)):
|
||||
if i < len(left_menu):
|
||||
left_item = left_menu[i]
|
||||
left_width = get_display_width(left_item)
|
||||
spaces = right_start - left_width
|
||||
spaces_list.append(spaces)
|
||||
|
||||
# Print menu items
|
||||
max_rows = max(len(left_menu), len(right_menu))
|
||||
|
||||
for i in range(max_rows):
|
||||
# Print left menu items
|
||||
if i < len(left_menu):
|
||||
left_item = left_menu[i]
|
||||
print(left_item, end='')
|
||||
|
||||
# Use pre-calculated spaces
|
||||
spaces = spaces_list[i]
|
||||
else:
|
||||
# If left side has no items, print only spaces
|
||||
spaces = right_start
|
||||
print('', end='')
|
||||
|
||||
# Print right menu items
|
||||
if i < len(right_menu):
|
||||
print(' ' * spaces + right_menu[i])
|
||||
else:
|
||||
print() # Change line
|
||||
if translator.current_language == 'zh_cn' or translator.current_language == 'zh_tw':
|
||||
print(f"{Fore.YELLOW}{'─' * 70}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{'─' * 110}{Style.RESET_ALL}")
|
||||
|
||||
def select_language():
|
||||
"""Language selection menu"""
|
||||
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
|
||||
languages = translator.get_available_languages()
|
||||
for i, lang in enumerate(languages):
|
||||
lang_name = translator.get(f"languages.{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)])
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except (ValueError, IndexError):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_latest_version():
|
||||
"""Check if current version matches the latest release 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
|
||||
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
|
||||
|
||||
# Check if response is successful
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"GitHub API returned status code {response.status_code}")
|
||||
|
||||
response_data = response.json()
|
||||
if "tag_name" not in response_data:
|
||||
raise Exception("No version tag found in GitHub response")
|
||||
|
||||
latest_version = response_data["tag_name"].lstrip('v')
|
||||
|
||||
# Validate version format
|
||||
if not latest_version:
|
||||
raise Exception("Invalid version format received")
|
||||
|
||||
# Parse versions for proper comparison
|
||||
def parse_version(version_str):
|
||||
"""Parse version string into tuple for proper comparison"""
|
||||
try:
|
||||
return tuple(map(int, version_str.split('.')))
|
||||
except ValueError:
|
||||
# Fallback to string comparison if parsing fails
|
||||
return version_str
|
||||
|
||||
current_version_tuple = parse_version(version)
|
||||
latest_version_tuple = parse_version(latest_version)
|
||||
|
||||
# Compare versions properly
|
||||
is_newer_version_available = False
|
||||
if isinstance(current_version_tuple, tuple) and isinstance(latest_version_tuple, tuple):
|
||||
is_newer_version_available = current_version_tuple < latest_version_tuple
|
||||
else:
|
||||
# Fallback to string comparison
|
||||
is_newer_version_available = version != latest_version
|
||||
|
||||
if is_newer_version_available:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
|
||||
|
||||
# get and show changelog
|
||||
try:
|
||||
changelog_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/CHANGELOG.md"
|
||||
changelog_response = requests.get(changelog_url, timeout=10)
|
||||
|
||||
if changelog_response.status_code == 200:
|
||||
changelog_content = changelog_response.text
|
||||
|
||||
# get latest version changelog
|
||||
latest_version_pattern = f"## v{latest_version}"
|
||||
changelog_sections = changelog_content.split("## v")
|
||||
|
||||
latest_changes = None
|
||||
for section in changelog_sections:
|
||||
if section.startswith(latest_version):
|
||||
latest_changes = section
|
||||
break
|
||||
|
||||
if latest_changes:
|
||||
print(f"\n{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{translator.get('updater.changelog_title')}:{Style.RESET_ALL}")
|
||||
|
||||
# show changelog content (max 10 lines)
|
||||
changes_lines = latest_changes.strip().split('\n')
|
||||
for i, line in enumerate(changes_lines[1:11]): # skip version number line, max 10 lines
|
||||
if line.strip():
|
||||
print(f"{Fore.WHITE}{line.strip()}{Style.RESET_ALL}")
|
||||
|
||||
# if changelog more than 10 lines, show ellipsis
|
||||
if len(changes_lines) > 11:
|
||||
print(f"{Fore.WHITE}...{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.CYAN}{'─' * 40}{Style.RESET_ALL}")
|
||||
except Exception as changelog_error:
|
||||
# get changelog failed
|
||||
pass
|
||||
|
||||
# Ask user if they want to update
|
||||
while True:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower()
|
||||
if choice in ['', 'y', 'yes']:
|
||||
break
|
||||
elif choice in ['n', 'no']:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.update_skipped')}{Style.RESET_ALL}")
|
||||
return
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Execute update command based on platform
|
||||
if platform.system() == 'Windows':
|
||||
update_command = 'irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex'
|
||||
subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', update_command], check=True)
|
||||
else:
|
||||
# For Linux/Mac, download and execute the install script
|
||||
install_script_url = 'https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh'
|
||||
|
||||
# First verify the script exists
|
||||
script_response = requests.get(install_script_url, timeout=5)
|
||||
if script_response.status_code != 200:
|
||||
raise Exception("Installation script not found")
|
||||
|
||||
# Save and execute the script
|
||||
with open('install.sh', 'wb') as f:
|
||||
f.write(script_response.content)
|
||||
|
||||
os.chmod('install.sh', 0o755) # Make executable
|
||||
subprocess.run(['./install.sh'], check=True)
|
||||
|
||||
# Clean up
|
||||
if os.path.exists('install.sh'):
|
||||
os.remove('install.sh')
|
||||
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.updating')}{Style.RESET_ALL}")
|
||||
sys.exit(0)
|
||||
|
||||
except Exception as update_error:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.update_failed', error=str(update_error))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}")
|
||||
return
|
||||
else:
|
||||
# If current version is newer or equal to latest version
|
||||
if current_version_tuple > latest_version_tuple:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.development_version', current=version, latest=latest_version)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.network_error', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.check_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
def main():
|
||||
# Check for admin privileges if running as executable on Windows only
|
||||
if platform.system() == 'Windows' and is_frozen() and not is_admin():
|
||||
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} {translator.get('menu.admin_required')}{Style.RESET_ALL}")
|
||||
if run_as_admin():
|
||||
sys.exit(0) # Exit after requesting admin privileges
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.admin_required_continue')}{Style.RESET_ALL}")
|
||||
|
||||
print_logo()
|
||||
|
||||
# Initialize configuration
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
if config.getboolean('Utils', 'enabled_update_check'):
|
||||
check_latest_version() # Add version check before showing menu
|
||||
print_menu()
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}Enter your choice (0-3) | 输入选择 (0-3): {Style.RESET_ALL}")
|
||||
choice_num = 12
|
||||
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']} Exiting program... | 正在退出程序...{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return # 直接返回,不等待按键
|
||||
return
|
||||
elif choice == "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run()
|
||||
break
|
||||
reset_machine_manual.run(translator)
|
||||
print_menu()
|
||||
elif choice == "2":
|
||||
import cursor_register
|
||||
cursor_register.main()
|
||||
break
|
||||
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()
|
||||
break
|
||||
quit_cursor.quit_cursor(translator)
|
||||
print_menu()
|
||||
elif choice == "8":
|
||||
if select_language():
|
||||
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']} Invalid choice. Please try again | 无效选择,请重试{Style.RESET_ALL}")
|
||||
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']} Program terminated by user | 程序被用户终止{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 # 直接返回,不等待按键
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} An error occurred | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
# 只有在执行完其他选项后才显示按键退出提示
|
||||
print(f"\n{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} Press Enter to Exit | 按回车键退出...{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
695
new_signup.py
Normal file
@@ -0,0 +1,695 @@
|
||||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||||
import time
|
||||
import os
|
||||
import signal
|
||||
import random
|
||||
from colorama import Fore, Style
|
||||
import configparser
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from config import get_config
|
||||
|
||||
# Add global variable at the beginning of the file
|
||||
_translator = None
|
||||
|
||||
# Add global variable to track our Chrome processes
|
||||
_chrome_process_ids = []
|
||||
|
||||
def cleanup_chrome_processes(translator=None):
|
||||
"""Clean only Chrome processes launched by this script"""
|
||||
global _chrome_process_ids
|
||||
|
||||
if not _chrome_process_ids:
|
||||
print("\nNo Chrome processes to clean...")
|
||||
return
|
||||
|
||||
print("\nCleaning Chrome processes launched by this script...")
|
||||
try:
|
||||
if os.name == 'nt':
|
||||
for pid in _chrome_process_ids:
|
||||
try:
|
||||
os.system(f'taskkill /F /PID {pid} /T 2>nul')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
for pid in _chrome_process_ids:
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except:
|
||||
pass
|
||||
_chrome_process_ids = [] # Reset the list after cleanup
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.cleanup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"清理进程时出错: {e}")
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
"""Handle Ctrl+C signal"""
|
||||
global _translator
|
||||
if _translator:
|
||||
print(f"{Fore.CYAN}{_translator.get('register.exit_signal')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n接收到退出信号,正在关闭...")
|
||||
cleanup_chrome_processes(_translator)
|
||||
os._exit(0)
|
||||
|
||||
def simulate_human_input(page, url, config, translator=None):
|
||||
"""Visit URL"""
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
|
||||
|
||||
# First visit blank page
|
||||
page.get('about:blank')
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
# Visit target page
|
||||
page.get(url)
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
|
||||
"""Fill signup form"""
|
||||
try:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n正在填写注册表单...")
|
||||
|
||||
# Fill first name
|
||||
first_name_input = page.ele("@name=first_name")
|
||||
if first_name_input:
|
||||
first_name_input.input(first_name)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Fill last name
|
||||
last_name_input = page.ele("@name=last_name")
|
||||
if last_name_input:
|
||||
last_name_input.input(last_name)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Fill email
|
||||
email_input = page.ele("@name=email")
|
||||
if email_input:
|
||||
email_input.input(email)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Click submit button
|
||||
submit_button = page.ele("@type=submit")
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(get_random_wait_time(config, 'submit_wait'))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.form_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Form filled successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error filling form: {e}")
|
||||
return False
|
||||
|
||||
def get_default_chrome_path():
|
||||
"""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":
|
||||
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_random_wait_time(config, timing_type='page_load_wait'):
|
||||
"""
|
||||
Get random wait time from config
|
||||
Args:
|
||||
config: ConfigParser object
|
||||
timing_type: Type of timing to get (page_load_wait, input_wait, submit_wait)
|
||||
Returns:
|
||||
float: Random wait time or fixed time
|
||||
"""
|
||||
try:
|
||||
if not config.has_section('Timing'):
|
||||
return random.uniform(0.1, 0.8) # Default value
|
||||
|
||||
if timing_type == 'random':
|
||||
min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
|
||||
max_time = float(config.get('Timing', 'max_random_time', fallback='0.8'))
|
||||
return random.uniform(min_time, max_time)
|
||||
|
||||
time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
|
||||
|
||||
# Check if it's a fixed time value
|
||||
if '-' not in time_value and ',' not in time_value:
|
||||
return float(time_value) # Return fixed time
|
||||
|
||||
# Process range time
|
||||
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
|
||||
return random.uniform(min_time, max_time)
|
||||
except:
|
||||
return random.uniform(0.1, 0.8) # Return default value when error
|
||||
|
||||
def setup_driver(translator=None):
|
||||
"""Setup browser driver"""
|
||||
global _chrome_process_ids
|
||||
|
||||
try:
|
||||
# Get config
|
||||
config = get_config(translator)
|
||||
|
||||
# Get Chrome path
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
|
||||
|
||||
if not chrome_path or not os.path.exists(chrome_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()
|
||||
|
||||
# Set browser options
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Set Chrome path
|
||||
co.set_browser_path(chrome_path)
|
||||
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Set random port
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
# Set random port
|
||||
co.auto_port()
|
||||
|
||||
# Use headless mode (must be set to False, simulate human operation)
|
||||
co.headless(False)
|
||||
|
||||
try:
|
||||
# Load extension
|
||||
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
|
||||
if os.path.exists(extension_path):
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
co.add_extension(extension_path)
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error loading extension: {e}")
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Starting browser...")
|
||||
|
||||
# Record Chrome processes before launching
|
||||
before_pids = []
|
||||
try:
|
||||
import psutil
|
||||
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Launch browser
|
||||
page = ChromiumPage(co)
|
||||
|
||||
# Wait a moment for Chrome to fully launch
|
||||
time.sleep(1)
|
||||
|
||||
# Record Chrome processes after launching and find new ones
|
||||
try:
|
||||
import psutil
|
||||
after_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
# Find new Chrome processes
|
||||
new_pids = [pid for pid in after_pids if pid not in before_pids]
|
||||
_chrome_process_ids.extend(new_pids)
|
||||
|
||||
if _chrome_process_ids:
|
||||
print(f"Tracking {len(_chrome_process_ids)} Chrome processes")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not track Chrome processes: {e}")
|
||||
|
||||
return config, page
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error setting up browser: {e}")
|
||||
raise
|
||||
|
||||
def handle_turnstile(page, config, translator=None):
|
||||
"""Handle Turnstile verification"""
|
||||
try:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.handling_turnstile')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\nHandling Turnstile verification...")
|
||||
|
||||
# from config
|
||||
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
|
||||
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
|
||||
|
||||
# Parse random time range
|
||||
try:
|
||||
min_time, max_time = map(float, random_time_str.split('-'))
|
||||
except:
|
||||
min_time, max_time = 1, 3 # Default value
|
||||
|
||||
max_retries = 2
|
||||
retry_count = 0
|
||||
|
||||
while retry_count < max_retries:
|
||||
retry_count += 1
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.retry_verification', attempt=retry_count)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Attempt {retry_count} of verification...")
|
||||
|
||||
try:
|
||||
# Try to reset turnstile
|
||||
page.run_js("try { turnstile.reset() } catch(e) { }")
|
||||
time.sleep(turnstile_time) # from config
|
||||
|
||||
# Locate verification box element
|
||||
challenge_check = (
|
||||
page.ele("@id=cf-turnstile", timeout=2)
|
||||
.child()
|
||||
.shadow_root.ele("tag:iframe")
|
||||
.ele("tag:body")
|
||||
.sr("tag:input")
|
||||
)
|
||||
|
||||
if challenge_check:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.detect_turnstile')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Detected verification box...")
|
||||
|
||||
# from config
|
||||
time.sleep(random.uniform(min_time, max_time))
|
||||
challenge_check.click()
|
||||
time.sleep(turnstile_time) # from config
|
||||
|
||||
# check verification result
|
||||
if check_verification_success(page, translator):
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Verification successful!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Verification attempt failed: {e}")
|
||||
|
||||
# Check if verification has been successful
|
||||
if check_verification_success(page, translator):
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Verification successful!")
|
||||
return True
|
||||
|
||||
time.sleep(random.uniform(min_time, max_time))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Exceeded maximum retry attempts")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error in verification process: {e}")
|
||||
return False
|
||||
|
||||
def check_verification_success(page, translator=None):
|
||||
"""Check if verification is successful"""
|
||||
try:
|
||||
# Check if there is a subsequent form element, indicating verification has passed
|
||||
if (page.ele("@name=password", timeout=0.5) or
|
||||
page.ele("@name=email", timeout=0.5) or
|
||||
page.ele("@data-index=0", timeout=0.5) or
|
||||
page.ele("Account Settings", timeout=0.5)):
|
||||
return True
|
||||
|
||||
# Check if there is an error message
|
||||
error_messages = [
|
||||
'xpath://div[contains(text(), "Can\'t verify the user is human")]',
|
||||
'xpath://div[contains(text(), "Error: 600010")]',
|
||||
'xpath://div[contains(text(), "Please try again")]'
|
||||
]
|
||||
|
||||
for error_xpath in error_messages:
|
||||
if page.ele(error_xpath):
|
||||
return False
|
||||
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def generate_password(length=12):
|
||||
"""Generate random password"""
|
||||
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
||||
return ''.join(random.choices(chars, k=length))
|
||||
|
||||
def fill_password(page, password: str, config, translator=None):
|
||||
"""
|
||||
Fill password form
|
||||
"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else 'Setting password'}{Style.RESET_ALL}")
|
||||
|
||||
# Fill password
|
||||
password_input = page.ele("@name=password")
|
||||
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
|
||||
if password_input:
|
||||
password_input.input(password)
|
||||
|
||||
# Click submit button
|
||||
submit_button = page.ele("@type=submit")
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(get_random_wait_time(config, 'submit_wait'))
|
||||
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.password_submitted') if translator else 'Password submitted'}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.password_error', error=str(e)) if translator else f'Error setting password: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
return False
|
||||
|
||||
def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
|
||||
"""Handle verification code"""
|
||||
try:
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
|
||||
|
||||
# Check if using manual input verification code
|
||||
if hasattr(controller, 'get_verification_code') and email_tab is None: # Manual mode
|
||||
verification_code = controller.get_verification_code()
|
||||
if verification_code:
|
||||
# Fill verification code in registration page
|
||||
for i, digit in enumerate(verification_code):
|
||||
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
print(f"{translator.get('register.verification_success')}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, config, translator):
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
|
||||
|
||||
# Visit settings page
|
||||
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
|
||||
browser_tab.get("https://www.cursor.com/settings")
|
||||
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
|
||||
return True, browser_tab
|
||||
|
||||
return False, None
|
||||
|
||||
# Automatic verification code logic
|
||||
elif email_tab:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
|
||||
|
||||
# Use existing email_tab to refresh email
|
||||
email_tab.refresh_inbox()
|
||||
time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
|
||||
|
||||
# Check if there is a verification code email
|
||||
if email_tab.check_for_cursor_email():
|
||||
verification_code = email_tab.get_verification_code()
|
||||
if verification_code:
|
||||
# Fill verification code in registration page
|
||||
for i, digit in enumerate(verification_code):
|
||||
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, config, translator):
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
|
||||
|
||||
# Visit settings page
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
|
||||
browser_tab.get("https://www.cursor.com/settings")
|
||||
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
|
||||
return True, browser_tab
|
||||
|
||||
else:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("最后一次验证失败")
|
||||
return False, None
|
||||
|
||||
# Get verification code, set timeout
|
||||
verification_code = None
|
||||
max_attempts = 20
|
||||
retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
|
||||
start_time = time.time()
|
||||
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # This can be kept unchanged because it is a fixed value
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
# Check if timeout
|
||||
if time.time() - start_time > timeout:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_timeout')}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
verification_code = controller.get_verification_code()
|
||||
if verification_code:
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
remaining_time = int(timeout - (time.time() - start_time))
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
|
||||
|
||||
# Refresh email
|
||||
email_tab.refresh_inbox()
|
||||
time.sleep(retry_interval) # Use get_random_wait_time
|
||||
|
||||
if verification_code:
|
||||
# Fill verification code in registration page
|
||||
for i, digit in enumerate(verification_code):
|
||||
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, config, translator):
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
|
||||
|
||||
# Visit settings page
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
|
||||
browser_tab.get("https://www.cursor.com/settings")
|
||||
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
|
||||
|
||||
# Return success directly, let cursor_register.py handle account information acquisition
|
||||
return True, browser_tab
|
||||
|
||||
else:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def handle_sign_in(browser_tab, email, password, translator=None):
|
||||
"""Handle login process"""
|
||||
try:
|
||||
# Check if on login page
|
||||
sign_in_header = browser_tab.ele('xpath://h1[contains(text(), "Sign in")]')
|
||||
if not sign_in_header:
|
||||
return True # If not on login page, it means login is successful
|
||||
|
||||
print(f"{Fore.CYAN}检测到登录页面,开始登录...{Style.RESET_ALL}")
|
||||
|
||||
# Fill email
|
||||
email_input = browser_tab.ele('@name=email')
|
||||
if email_input:
|
||||
email_input.input(email)
|
||||
time.sleep(1)
|
||||
|
||||
# Click Continue
|
||||
continue_button = browser_tab.ele('xpath://button[contains(@class, "BrandedButton") and text()="Continue"]')
|
||||
if continue_button:
|
||||
continue_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# Handle Turnstile verification
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
# Fill password
|
||||
password_input = browser_tab.ele('@name=password')
|
||||
if password_input:
|
||||
password_input.input(password)
|
||||
time.sleep(1)
|
||||
|
||||
# Click Sign in
|
||||
sign_in_button = browser_tab.ele('xpath://button[@name="intent" and @value="password"]')
|
||||
if sign_in_button:
|
||||
sign_in_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
print(f"{Fore.GREEN}Login successful!{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}Login failed{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Login process error: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(email=None, password=None, first_name=None, last_name=None, email_tab=None, controller=None, translator=None):
|
||||
"""Main function, can receive account information, email tab, and translator"""
|
||||
global _translator
|
||||
global _chrome_process_ids
|
||||
_translator = translator # Save to global variable
|
||||
_chrome_process_ids = [] # Reset the process IDs list
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
page = None
|
||||
success = False
|
||||
try:
|
||||
config, page = setup_driver(translator)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
|
||||
|
||||
# Visit registration page
|
||||
url = "https://authenticator.cursor.sh/sign-up"
|
||||
|
||||
# Visit page
|
||||
simulate_human_input(page, url, config, translator)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
# If account information is not provided, generate random information
|
||||
if not all([email, password, first_name, last_name]):
|
||||
first_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
|
||||
last_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
|
||||
email = f"{first_name.lower()}{random.randint(100,999)}@example.com"
|
||||
password = generate_password()
|
||||
|
||||
# Save account information
|
||||
with open('test_accounts.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n{'='*50}\n")
|
||||
f.write(f"Email: {email}\n")
|
||||
f.write(f"Password: {password}\n")
|
||||
f.write(f"{'='*50}\n")
|
||||
|
||||
# Fill form
|
||||
if fill_signup_form(page, first_name, last_name, email, config, translator):
|
||||
if translator:
|
||||
print(f"\n{Fore.GREEN}✅ {translator.get('register.form_submitted')}{Style.RESET_ALL}")
|
||||
|
||||
# Handle first Turnstile verification
|
||||
if handle_turnstile(page, config, translator):
|
||||
if translator:
|
||||
print(f"\n{Fore.GREEN}✅ {translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# Fill password
|
||||
if fill_password(page, password, config, translator):
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
|
||||
|
||||
# Handle second Turnstile verification
|
||||
if handle_turnstile(page, config, translator):
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
|
||||
if handle_verification_code(page, email_tab, controller, config, translator):
|
||||
success = True
|
||||
return True, page
|
||||
else:
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.first_verification_failed') if translator else 'First verification failed'}{Style.RESET_ALL}")
|
||||
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生错误: {e}")
|
||||
return False, None
|
||||
finally:
|
||||
if page and not success: # Only clean up when failed
|
||||
try:
|
||||
page.quit()
|
||||
except:
|
||||
pass
|
||||
cleanup_chrome_processes(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main() # Run without parameters, use randomly generated information
|
||||
337
new_tempemail.py
Normal file
@@ -0,0 +1,337 @@
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
from colorama import Fore, Style, init
|
||||
import requests
|
||||
import random
|
||||
import string
|
||||
from utils import get_random_wait_time
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
class NewTempEmail:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
self.page = None
|
||||
self.setup_browser()
|
||||
|
||||
def get_blocked_domains(self):
|
||||
"""Get blocked domains list"""
|
||||
try:
|
||||
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
|
||||
response = requests.get(block_url, timeout=5)
|
||||
if response.status_code == 200:
|
||||
# Split text and remove empty lines
|
||||
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
return domains
|
||||
return self._load_local_blocked_domains()
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
|
||||
return self._load_local_blocked_domains()
|
||||
|
||||
def _load_local_blocked_domains(self):
|
||||
"""Load blocked domains from local file as fallback"""
|
||||
try:
|
||||
local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "block_domain.txt")
|
||||
if os.path.exists(local_path):
|
||||
with open(local_path, 'r', encoding='utf-8') as f:
|
||||
domains = [line.strip() for line in f.readlines() if line.strip()]
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.local_blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 已从本地加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
return domains
|
||||
else:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 本地被屏蔽域名文件不存在{Style.RESET_ALL}")
|
||||
return []
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 读取本地被屏蔽域名文件失败: {str(e)}{Style.RESET_ALL}")
|
||||
return []
|
||||
|
||||
def exclude_blocked_domains(self, domains):
|
||||
"""Exclude blocked domains"""
|
||||
if not self.blocked_domains:
|
||||
return domains
|
||||
|
||||
filtered_domains = []
|
||||
for domain in domains:
|
||||
if domain['domain'] not in self.blocked_domains:
|
||||
filtered_domains.append(domain)
|
||||
|
||||
excluded_count = len(domains) - len(filtered_domains)
|
||||
if excluded_count > 0:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||
|
||||
return filtered_domains
|
||||
|
||||
|
||||
def get_extension_block(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "PBlock")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "PBlock")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def setup_browser(self):
|
||||
"""设置浏览器"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.starting_browser')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在启动浏览器...{Style.RESET_ALL}")
|
||||
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Only use headless for non-OAuth operations
|
||||
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Check if DISPLAY is set when not in headless mode
|
||||
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
co.set_argument("--no-sandbox")
|
||||
co.set_argument("--disable-dev-shm-usage")
|
||||
co.set_argument("--disable-gpu")
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
# 加载 uBlock 插件
|
||||
try:
|
||||
extension_path = self.get_extension_block()
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
co.add_extension(extension_path)
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 加载插件失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
self.page = ChromiumPage(co)
|
||||
return True
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform == "linux":
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def create_email(self):
|
||||
"""create temporary email"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在访问 smailpro.com...{Style.RESET_ALL}")
|
||||
|
||||
# load blocked domains list
|
||||
self.blocked_domains = self.get_blocked_domains()
|
||||
|
||||
# visit website
|
||||
self.page.get("https://smailpro.com/")
|
||||
time.sleep(2)
|
||||
|
||||
# click create email button
|
||||
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
|
||||
if create_button:
|
||||
create_button.click()
|
||||
time.sleep(1)
|
||||
|
||||
# click Create button in popup
|
||||
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]')
|
||||
if modal_create_button:
|
||||
modal_create_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# get email address - modify selector
|
||||
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
|
||||
if email_div:
|
||||
email = email_div.text.strip()
|
||||
if '@' in email: # check if it's a valid email address
|
||||
# check if domain is blocked
|
||||
domain = email.split('@')[1]
|
||||
if self.blocked_domains and domain in self.blocked_domains:
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domain_blocked')}: {domain}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 域名已被屏蔽: {domain},尝试重新创建邮箱{Style.RESET_ALL}")
|
||||
# create email again
|
||||
return self.create_email()
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
|
||||
return email
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.create_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
"""close browser"""
|
||||
if self.page:
|
||||
self.page.quit()
|
||||
|
||||
def refresh_inbox(self):
|
||||
"""refresh inbox"""
|
||||
try:
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
|
||||
|
||||
# click refresh button
|
||||
refresh_button = self.page.ele('xpath://button[@id="refresh"]')
|
||||
if refresh_button:
|
||||
refresh_button.click()
|
||||
time.sleep(2) # wait for refresh to complete
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 邮箱刷新成功{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 未找到刷新按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 刷新邮箱出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_for_cursor_email(self):
|
||||
"""检查是否有 Cursor 的验证邮件"""
|
||||
try:
|
||||
# find verification email - use more accurate selector
|
||||
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]')
|
||||
if email_div:
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
|
||||
# use JavaScript to click element
|
||||
self.page.run_js('arguments[0].click()', email_div)
|
||||
time.sleep(2) # wait for email content to load
|
||||
return True
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 未找到验证邮件{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.verification_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 检查验证邮件出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_verification_code(self):
|
||||
"""获取验证码"""
|
||||
try:
|
||||
# find verification code element
|
||||
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]')
|
||||
if code_element:
|
||||
code = code_element.text.strip()
|
||||
if code.isdigit() and len(code) == 6:
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
|
||||
return code
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}⚠️ 未找到有效的验证码{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.verification_code_error')}: {str(e)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}❌ 获取验证码出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def main(translator=None):
|
||||
temp_email = NewTempEmail(translator)
|
||||
|
||||
try:
|
||||
email = temp_email.create_email()
|
||||
if email:
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}📧 {translator.get('email.address')}: {email}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
|
||||
|
||||
# test refresh function
|
||||
while True:
|
||||
if translator:
|
||||
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()
|
||||
else:
|
||||
choice = input("\n按 R 刷新邮箱,按 Q 退出: ").lower()
|
||||
if choice == 'r':
|
||||
temp_email.refresh_inbox()
|
||||
elif choice == 'q':
|
||||
break
|
||||
|
||||
finally:
|
||||
temp_email.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
918
oauth_auth.py
Normal file
@@ -0,0 +1,918 @@
|
||||
import os
|
||||
from colorama import Fore, Style, init
|
||||
import time
|
||||
import random
|
||||
import webbrowser
|
||||
import sys
|
||||
import json
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from cursor_auth import CursorAuth
|
||||
from utils import get_random_wait_time, get_default_chrome_path
|
||||
from config import get_config
|
||||
import platform
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'OAUTH': '🔑',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
class OAuthHandler:
|
||||
def __init__(self, translator=None, auth_type=None):
|
||||
self.translator = translator
|
||||
self.config = get_config(translator)
|
||||
self.auth_type = auth_type # make sure the auth_type is not None
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser = None
|
||||
|
||||
def _get_active_profile(self, user_data_dir):
|
||||
"""Find the existing default/active Chrome profile"""
|
||||
try:
|
||||
# List all profile directories
|
||||
profiles = []
|
||||
for item in os.listdir(user_data_dir):
|
||||
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
|
||||
profiles.append(item)
|
||||
|
||||
if not profiles:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.no_chrome_profiles_found') if self.translator else 'No Chrome profiles found, using Default'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
# First check if Default profile exists
|
||||
if 'Default' in profiles:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_default_chrome_profile') if self.translator else 'Found Default Chrome profile'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
# If no Default profile, check Local State for last used profile
|
||||
local_state_path = os.path.join(user_data_dir, 'Local State')
|
||||
if os.path.exists(local_state_path):
|
||||
with open(local_state_path, 'r', encoding='utf-8') as f:
|
||||
local_state = json.load(f)
|
||||
|
||||
# Get info about last used profile
|
||||
profile_info = local_state.get('profile', {})
|
||||
last_used = profile_info.get('last_used', '')
|
||||
info_cache = profile_info.get('info_cache', {})
|
||||
|
||||
# Try to find an active profile
|
||||
for profile in profiles:
|
||||
profile_path = profile.replace('\\', '/')
|
||||
if profile_path in info_cache:
|
||||
#print(f"{Fore.CYAN}{EMOJI['INFO']} Using existing Chrome profile: {profile}{Style.RESET_ALL}")
|
||||
return profile
|
||||
|
||||
# If no profile found in Local State, use the first available profile
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_first_available_chrome_profile', profile=profiles[0]) if self.translator else f'Using first available Chrome profile: {profiles[0]}'}{Style.RESET_ALL}")
|
||||
return profiles[0]
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.error_finding_chrome_profile', error=str(e)) if self.translator else f'Error finding Chrome profile, using Default: {str(e)}'}{Style.RESET_ALL}")
|
||||
return 'Default'
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup browser for OAuth flow using active profile"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific initialization
|
||||
platform_name = platform.system().lower()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
|
||||
|
||||
# Linux-specific checks
|
||||
if platform_name == 'linux':
|
||||
# Check if DISPLAY is set
|
||||
display = os.environ.get('DISPLAY')
|
||||
if not display:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check if running as root
|
||||
if os.geteuid() == 0:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.running_as_root_warning') if self.translator else 'Running as root is not recommended for browser automation'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.consider_running_without_sudo') if self.translator else 'Consider running the script without sudo'}{Style.RESET_ALL}")
|
||||
|
||||
# Kill existing browser processes
|
||||
self._kill_browser_processes()
|
||||
|
||||
# Get browser paths and user data directory
|
||||
user_data_dir = self._get_user_data_directory()
|
||||
chrome_path = self._get_browser_path()
|
||||
|
||||
if not chrome_path:
|
||||
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
|
||||
"- Windows: Google Chrome, Chromium\n" +
|
||||
"- macOS: Google Chrome, Chromium\n" +
|
||||
"- Linux: Google Chrome, Chromium, chromium-browser")
|
||||
|
||||
# Get active profile
|
||||
active_profile = self._get_active_profile(user_data_dir)
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_browser_profile', profile=active_profile) if self.translator else f'Using browser profile: {active_profile}'}{Style.RESET_ALL}")
|
||||
|
||||
# Configure browser options
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Never use headless mode for OAuth flows
|
||||
co.headless(False)
|
||||
|
||||
# Platform-specific options
|
||||
if os.name == 'linux':
|
||||
co.set_argument('--no-sandbox')
|
||||
co.set_argument('--disable-dev-shm-usage')
|
||||
co.set_argument('--disable-gpu')
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.using_chrome_profile_from', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
# Set paths and profile
|
||||
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||
co.set_argument(f'--profile-directory={active_profile}')
|
||||
|
||||
# Basic options
|
||||
co.set_argument('--no-first-run')
|
||||
co.set_argument('--no-default-browser-check')
|
||||
# co.auto_port()
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
|
||||
self.browser = ChromiumPage(co)
|
||||
|
||||
# Verify browser launched successfully
|
||||
if not self.browser:
|
||||
raise Exception("Failed to initialize browser instance")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
if "DevToolsActivePort file doesn't exist" in str(e):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
|
||||
elif "Chrome failed to start" in str(e):
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
if platform_name == 'linux':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _kill_browser_processes(self):
|
||||
"""Kill existing browser processes based on platform"""
|
||||
try:
|
||||
if os.name == 'nt': # Windows
|
||||
processes = ['chrome.exe', 'chromium.exe']
|
||||
for proc in processes:
|
||||
os.system(f'taskkill /f /im {proc} >nul 2>&1')
|
||||
else: # Linux/Mac
|
||||
processes = ['chrome', 'chromium', 'chromium-browser']
|
||||
for proc in processes:
|
||||
os.system(f'pkill -f {proc} >/dev/null 2>&1')
|
||||
|
||||
time.sleep(1) # Wait for processes to close
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
|
||||
|
||||
def _get_user_data_directory(self):
|
||||
"""Get the appropriate user data directory based on platform"""
|
||||
try:
|
||||
if os.name == 'nt': # Windows
|
||||
possible_paths = [
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
|
||||
]
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||
os.path.expanduser('~/Library/Application Support/Chromium')
|
||||
]
|
||||
else: # Linux
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/.config/google-chrome'),
|
||||
os.path.expanduser('~/.config/chromium'),
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser'
|
||||
]
|
||||
|
||||
# Try each possible path
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=path) if self.translator else f'Found browser data directory: {path}'}{Style.RESET_ALL}")
|
||||
return path
|
||||
|
||||
# Create temporary profile if no existing profile found
|
||||
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
|
||||
os.makedirs(temp_profile, exist_ok=True)
|
||||
return temp_profile
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def _get_browser_path(self):
|
||||
"""Get the browser executable path based on platform"""
|
||||
try:
|
||||
# Try default path first
|
||||
chrome_path = get_default_chrome_path()
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
return chrome_path
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific paths
|
||||
if os.name == 'nt': # Windows
|
||||
alt_paths = [
|
||||
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files\Chromium\Application\chrome.exe',
|
||||
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
|
||||
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
|
||||
]
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
alt_paths = [
|
||||
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
|
||||
]
|
||||
else: # Linux
|
||||
alt_paths = [
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser',
|
||||
'/usr/bin/chromium',
|
||||
'/snap/bin/chromium',
|
||||
'/usr/local/bin/chrome',
|
||||
'/usr/local/bin/chromium'
|
||||
]
|
||||
|
||||
# Try each alternative path
|
||||
for path in alt_paths:
|
||||
expanded_path = os.path.expanduser(path)
|
||||
if os.path.exists(expanded_path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
|
||||
return expanded_path
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
|
||||
"""Configure browser options based on platform"""
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||
co.set_argument(f'--profile-directory={active_profile}')
|
||||
|
||||
# Basic options
|
||||
co.set_argument('--no-first-run')
|
||||
co.set_argument('--no-default-browser-check')
|
||||
co.set_argument('--disable-gpu')
|
||||
|
||||
# Platform-specific options
|
||||
if sys.platform.startswith('linux'):
|
||||
co.set_argument('--no-sandbox')
|
||||
co.set_argument('--disable-dev-shm-usage')
|
||||
co.set_argument('--disable-setuid-sandbox')
|
||||
elif sys.platform == 'darwin':
|
||||
co.set_argument('--disable-gpu-compositing')
|
||||
elif os.name == 'nt':
|
||||
co.set_argument('--disable-features=TranslateUI')
|
||||
co.set_argument('--disable-features=RendererCodeIntegrity')
|
||||
|
||||
return co
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def handle_google_auth(self):
|
||||
"""Handle Google OAuth authentication"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
|
||||
|
||||
# Setup browser
|
||||
if not self.setup_browser():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
# Navigate to auth URL
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Look for Google auth button
|
||||
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 and auth_btn.is_displayed():
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not auth_btn:
|
||||
raise Exception("Could not find Google authentication button")
|
||||
|
||||
# Click the button and wait for page load
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Check if we're on account selection page
|
||||
if "accounts.google.com" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
if not auth_info:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
|
||||
return True, auth_info
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
try:
|
||||
if self.browser:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def _wait_for_auth(self):
|
||||
"""Wait for authentication to complete and extract auth info"""
|
||||
try:
|
||||
max_wait = 300 # 5 minutes
|
||||
start_time = time.time()
|
||||
check_interval = 2 # Check every 2 seconds
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
|
||||
|
||||
while time.time() - start_time < max_wait:
|
||||
try:
|
||||
# Check for authentication cookies
|
||||
cookies = self.browser.cookies()
|
||||
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
token = None
|
||||
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
if token:
|
||||
# Get email from settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
time.sleep(3)
|
||||
|
||||
email = None
|
||||
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('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
|
||||
except:
|
||||
email = "user@cursor.sh" # Fallback email
|
||||
|
||||
# Check usage count
|
||||
try:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', creating_new_account='creating new account') if self.translator else 'Account has reached maximum usage, creating new account...'}{Style.RESET_ALL}")
|
||||
|
||||
# Delete current account
|
||||
if self._delete_current_account():
|
||||
# Start new authentication based on auth type
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
|
||||
if self.auth_type == "google":
|
||||
return self.handle_google_auth()
|
||||
else: # github
|
||||
return self.handle_github_auth()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
return {"email": email, "token": token}
|
||||
|
||||
# Also check URL as backup
|
||||
if "cursor.com/settings" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
|
||||
|
||||
time.sleep(check_interval)
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def handle_github_auth(self):
|
||||
"""Handle GitHub OAuth authentication"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
|
||||
|
||||
# Setup browser
|
||||
if not self.setup_browser():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
# Navigate to auth URL
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Look for GitHub auth button
|
||||
selectors = [
|
||||
"//a[contains(@href,'GitHubOAuth')]",
|
||||
"//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
|
||||
"(//a[contains(@class,'auth-method-button')])[2]" # Second 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 and auth_btn.is_displayed():
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not auth_btn:
|
||||
raise Exception("Could not find GitHub authentication button")
|
||||
|
||||
# Click the button and wait for page load
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
if not auth_info:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
|
||||
return True, auth_info
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
try:
|
||||
if self.browser:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def _handle_oauth(self, auth_type):
|
||||
"""Handle OAuth authentication for both Google and GitHub
|
||||
|
||||
Args:
|
||||
auth_type (str): Type of authentication ('google' or 'github')
|
||||
"""
|
||||
try:
|
||||
if not self.setup_browser():
|
||||
return False, None
|
||||
|
||||
# Navigate to auth URL
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Set selectors based on auth type
|
||||
if auth_type == "google":
|
||||
selectors = [
|
||||
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
|
||||
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
|
||||
]
|
||||
else: # github
|
||||
selectors = [
|
||||
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
|
||||
]
|
||||
|
||||
# Wait for the button to be available
|
||||
auth_btn = None
|
||||
max_button_wait = 30 # 30 seconds
|
||||
button_start_time = time.time()
|
||||
|
||||
while time.time() - button_start_time < max_button_wait:
|
||||
for selector in selectors:
|
||||
try:
|
||||
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
|
||||
if auth_btn and auth_btn.is_displayed():
|
||||
break
|
||||
except:
|
||||
continue
|
||||
if auth_btn:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if auth_btn:
|
||||
# Click the button and wait for page load
|
||||
auth_btn.click()
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Check if we're on account selection page
|
||||
if auth_type == "google" and "accounts.google.com" in self.browser.url:
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
|
||||
|
||||
# Wait for authentication to complete
|
||||
max_wait = 300 # 5 minutes
|
||||
start_time = time.time()
|
||||
last_url = self.browser.url
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.checking_authentication_status') if self.translator else 'Checking authentication status...'}{Style.RESET_ALL}")
|
||||
|
||||
while time.time() - start_time < max_wait:
|
||||
try:
|
||||
# Check for authentication cookies
|
||||
cookies = self.browser.cookies()
|
||||
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
|
||||
# Navigate to settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
time.sleep(3) # Wait for settings page to load
|
||||
|
||||
# Get email from settings page
|
||||
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:
|
||||
actual_email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
|
||||
actual_email = "user@cursor.sh"
|
||||
|
||||
# Check usage count
|
||||
try:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
delete_js = """
|
||||
function deleteAccount() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch('https://www.cursor.com/api/dashboard/delete-account', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
resolve('Account deleted successfully');
|
||||
} else {
|
||||
reject('Failed to delete account: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject('Error: ' + error);
|
||||
});
|
||||
});
|
||||
}
|
||||
return deleteAccount();
|
||||
"""
|
||||
|
||||
try:
|
||||
result = self.browser.run_js(delete_js)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page and repeat authentication
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
|
||||
# Explicitly navigate to the authentication page
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Call handle_google_auth again to repeat the entire process
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
|
||||
return self.handle_google_auth()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Remove the browser stay open prompt and input wait
|
||||
return True, {"email": actual_email, "token": token}
|
||||
|
||||
# Also check URL as backup
|
||||
current_url = self.browser.url
|
||||
if "cursor.com/settings" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
cookies = self.browser.cookies()
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
if token:
|
||||
# Get email and check usage here too
|
||||
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:
|
||||
actual_email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
|
||||
actual_email = "user@cursor.sh"
|
||||
|
||||
# Check usage count
|
||||
try:
|
||||
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||
if usage_element:
|
||||
usage_text = usage_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if account is expired
|
||||
if usage_text.strip() == "150 / 150":
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
|
||||
|
||||
delete_js = """
|
||||
function deleteAccount() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch('https://www.cursor.com/api/dashboard/delete-account', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
resolve('Account deleted successfully');
|
||||
} else {
|
||||
reject('Failed to delete account: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject('Error: ' + error);
|
||||
});
|
||||
});
|
||||
}
|
||||
return deleteAccount();
|
||||
"""
|
||||
|
||||
try:
|
||||
result = self.browser.run_js(delete_js)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page and repeat authentication
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_re_authentication_process') if self.translator else 'Starting re-authentication process...'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
|
||||
# Explicitly navigate to the authentication page
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
# Call handle_google_auth again to repeat the entire process
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_google_authentication') if self.translator else 'Starting new Google authentication...'}{Style.RESET_ALL}")
|
||||
return self.handle_google_auth()
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account_or_re_authenticate', error=str(e)) if self.translator else f'Failed to delete account or re-authenticate: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Remove the browser stay open prompt and input wait
|
||||
return True, {"email": actual_email, "token": token}
|
||||
elif current_url != last_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
|
||||
last_url = current_url
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
continue
|
||||
time.sleep(1)
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
finally:
|
||||
if self.browser:
|
||||
self.browser.quit()
|
||||
|
||||
def _extract_auth_info(self):
|
||||
"""Extract authentication information after successful OAuth"""
|
||||
try:
|
||||
# Get cookies with retry
|
||||
max_retries = 3
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
cookies = self.browser.cookies()
|
||||
if cookies:
|
||||
break
|
||||
time.sleep(1)
|
||||
except:
|
||||
if attempt == max_retries - 1:
|
||||
raise
|
||||
time.sleep(1)
|
||||
|
||||
# Debug cookie information
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
|
||||
|
||||
email = None
|
||||
token = None
|
||||
|
||||
for cookie in cookies:
|
||||
name = cookie.get("name", "")
|
||||
if name == "WorkosCursorSessionToken":
|
||||
try:
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
|
||||
elif name == "cursor_email":
|
||||
email = cookie.get("value")
|
||||
|
||||
if email and token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
|
||||
return True, {"email": email, "token": token}
|
||||
else:
|
||||
missing = []
|
||||
if not email:
|
||||
missing.append("email")
|
||||
if not token:
|
||||
missing.append("token")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False, None
|
||||
|
||||
def _delete_current_account(self):
|
||||
"""Delete the current account using the API"""
|
||||
try:
|
||||
delete_js = """
|
||||
function deleteAccount() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch('https://www.cursor.com/api/dashboard/delete-account', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
resolve('Account deleted successfully');
|
||||
} else {
|
||||
reject('Failed to delete account: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject('Error: ' + error);
|
||||
});
|
||||
});
|
||||
}
|
||||
return deleteAccount();
|
||||
"""
|
||||
|
||||
result = self.browser.run_js(delete_js)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate back to auth page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(auth_type, translator=None):
|
||||
"""Main function to handle OAuth authentication
|
||||
|
||||
Args:
|
||||
auth_type (str): Type of authentication ('google' or 'github')
|
||||
translator: Translator instance for internationalization
|
||||
"""
|
||||
handler = OAuthHandler(translator, auth_type)
|
||||
|
||||
if auth_type.lower() == 'google':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
|
||||
success, auth_info = handler.handle_google_auth()
|
||||
elif auth_type.lower() == 'github':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
|
||||
success, auth_info = handler.handle_github_auth()
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if success and auth_info:
|
||||
# Update Cursor authentication
|
||||
auth_manager = CursorAuth(translator)
|
||||
if auth_manager.update_auth(
|
||||
email=auth_info["email"],
|
||||
access_token=auth_info["token"],
|
||||
refresh_token=auth_info["token"]
|
||||
):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
|
||||
# Close the browser after successful authentication
|
||||
if handler.browser:
|
||||
handler.browser.quit()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
|
||||
|
||||
return False
|
||||
@@ -1,33 +1,33 @@
|
||||
import psutil
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"PROCESS": "⚙️",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"PROCESS": "⚙️",
|
||||
"WAIT": "⏳"
|
||||
}
|
||||
|
||||
class CursorQuitter:
|
||||
def __init__(self, timeout=5):
|
||||
def __init__(self, timeout=5, translator=None):
|
||||
self.timeout = timeout
|
||||
self.translator = translator # Use the passed translator
|
||||
|
||||
def quit_cursor(self):
|
||||
"""温和地关闭 Cursor 进程"""
|
||||
"""Gently close Cursor processes"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} Start Quitting Cursor | 开始退出 Cursor...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.start')}...{Style.RESET_ALL}")
|
||||
cursor_processes = []
|
||||
|
||||
# 收集所有 Cursor 进程
|
||||
# Collect all Cursor processes
|
||||
for proc in psutil.process_iter(['pid', 'name']):
|
||||
try:
|
||||
if proc.info['name'].lower() in ['cursor.exe', 'cursor']:
|
||||
@@ -36,20 +36,20 @@ class CursorQuitter:
|
||||
continue
|
||||
|
||||
if not cursor_processes:
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} No Running Cursor Process | 未发现运行中的 Cursor 进程{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('quit_cursor.no_process')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
# 温和地请求进程终止
|
||||
# Gently request processes to terminate
|
||||
for proc in cursor_processes:
|
||||
try:
|
||||
if proc.is_running():
|
||||
print(f"{Fore.YELLOW}{EMOJI['PROCESS']} Terminating Process | 正在终止进程 {proc.pid}...{Style.RESET_ALL}")
|
||||
proc.terminate() # 发送终止信号
|
||||
print(f"{Fore.YELLOW}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.terminating', pid=proc.pid)}...{Style.RESET_ALL}")
|
||||
proc.terminate()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
# 等待进程自然终止
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for Process to Exit | 等待进程退出...{Style.RESET_ALL}")
|
||||
# Wait for processes to terminate naturally
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('quit_cursor.waiting')}...{Style.RESET_ALL}")
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < self.timeout:
|
||||
still_running = []
|
||||
@@ -61,28 +61,29 @@ class CursorQuitter:
|
||||
continue
|
||||
|
||||
if not still_running:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} All Cursor Processes Closed | 所有 Cursor 进程已正常关闭{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('quit_cursor.success')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
# 等待一小段时间再检查
|
||||
time.sleep(0.5)
|
||||
|
||||
# 如果超时后仍有进程在运行
|
||||
# If processes are still running after timeout
|
||||
if still_running:
|
||||
process_list = ", ".join([str(p.pid) for p in still_running])
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Process Timeout | 以下进程未能在规定时间内关闭: {process_list}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error Occurred | 关闭 Cursor 进程时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def quit_cursor(timeout=5):
|
||||
"""便捷函数,用于直接调用退出功能"""
|
||||
quitter = CursorQuitter(timeout)
|
||||
def quit_cursor(translator=None, timeout=5):
|
||||
"""Convenient function for directly calling the quit function"""
|
||||
quitter = CursorQuitter(timeout, translator)
|
||||
return quitter.quit_cursor()
|
||||
|
||||
if __name__ == "__main__":
|
||||
quit_cursor()
|
||||
# If run directly, use the default translator
|
||||
from main import translator as main_translator
|
||||
quit_cursor(main_translator)
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Recaptcha Blocker",
|
||||
"version": "1.0",
|
||||
"description": "Blocks reCAPTCHA from loading",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["*://yopmail.com/zh/wm*"],
|
||||
"js": ["script.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"webNavigation",
|
||||
"activeTab"
|
||||
],
|
||||
"host_permissions": [
|
||||
"*://yopmail.com/*",
|
||||
"*://*.google.com/*"
|
||||
]
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// Debug logging function
|
||||
function log(message) {
|
||||
console.log(`[reCAPTCHA Bypass] ${new Date().toISOString()}: ${message}`);
|
||||
}
|
||||
|
||||
// Function to get element by selector
|
||||
function qSelector(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
// Constants
|
||||
const MAX_ATTEMPTS = 1;
|
||||
const SELECTORS = {
|
||||
CHECK_BOX: ".recaptcha-checkbox-border",
|
||||
AUDIO_BUTTON: "#recaptcha-audio-button",
|
||||
PLAY_BUTTON: ".rc-audiochallenge-play-button .rc-button-default",
|
||||
AUDIO_SOURCE: "#audio-source",
|
||||
IMAGE_SELECT: "#rc-imageselect",
|
||||
RESPONSE_FIELD: ".rc-audiochallenge-response-field",
|
||||
AUDIO_ERROR_MESSAGE: ".rc-audiochallenge-error-message",
|
||||
AUDIO_RESPONSE: "#audio-response",
|
||||
RELOAD_BUTTON: "#recaptcha-reload-button",
|
||||
RECAPTCHA_STATUS: "#recaptcha-accessible-status",
|
||||
DOSCAPTCHA: ".rc-doscaptcha-body",
|
||||
VERIFY_BUTTON: "#recaptcha-verify-button"
|
||||
};
|
||||
|
||||
// Function to check if element is hidden
|
||||
function isHidden(el) {
|
||||
return (el.offsetParent === null);
|
||||
}
|
||||
|
||||
// Function to handle the bypass process
|
||||
async function bypassRecaptcha() {
|
||||
try {
|
||||
log('Starting bypass process...');
|
||||
log('Waiting 3 seconds before starting...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
let solved = false;
|
||||
let checkBoxClicked = false;
|
||||
let requestCount = 0;
|
||||
|
||||
const recaptchaInitialStatus = qSelector(SELECTORS.RECAPTCHA_STATUS) ?
|
||||
qSelector(SELECTORS.RECAPTCHA_STATUS).innerText : "";
|
||||
|
||||
log('Initial reCAPTCHA status: ' + recaptchaInitialStatus);
|
||||
|
||||
// Main bypass logic
|
||||
try {
|
||||
if (!checkBoxClicked && qSelector(SELECTORS.CHECK_BOX) &&
|
||||
!isHidden(qSelector(SELECTORS.CHECK_BOX))) {
|
||||
log('Clicking checkbox...');
|
||||
qSelector(SELECTORS.CHECK_BOX).click();
|
||||
checkBoxClicked = true;
|
||||
}
|
||||
|
||||
// Check if the captcha is solved
|
||||
if (qSelector(SELECTORS.RECAPTCHA_STATUS) &&
|
||||
(qSelector(SELECTORS.RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
|
||||
solved = true;
|
||||
log('SOLVED!');
|
||||
}
|
||||
|
||||
if (requestCount > MAX_ATTEMPTS) {
|
||||
log('Attempted Max Retries. Stopping the solver');
|
||||
solved = true;
|
||||
}
|
||||
|
||||
// Stop solving when Automated queries message is shown
|
||||
if (qSelector(SELECTORS.DOSCAPTCHA) &&
|
||||
qSelector(SELECTORS.DOSCAPTCHA).innerText.length > 0) {
|
||||
log('Automated Queries Detected');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
log(`Error in main bypass logic: ${err.message}`);
|
||||
}
|
||||
|
||||
log('Bypass process completed');
|
||||
|
||||
// If not solved, retry after delay
|
||||
if (!solved && requestCount < MAX_ATTEMPTS) {
|
||||
requestCount++;
|
||||
log(`Retrying... Attempt ${requestCount} of ${MAX_ATTEMPTS}`);
|
||||
setTimeout(bypassRecaptcha, 2000);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
log(`Bypass failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a MutationObserver to watch for reCAPTCHA elements
|
||||
log('Setting up MutationObserver...');
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === 1 && // Element node
|
||||
((node.tagName === 'SCRIPT' && node.src && node.src.includes('recaptcha')) ||
|
||||
(node.tagName === 'IFRAME' && node.src && node.src.includes('recaptcha')))) {
|
||||
log(`Detected new reCAPTCHA element: ${node.tagName} - ${node.src}`);
|
||||
bypassRecaptcha();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing
|
||||
observer.observe(document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
log('MutationObserver started');
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
log('Document still loading, waiting for DOMContentLoaded');
|
||||
document.addEventListener('DOMContentLoaded', bypassRecaptcha);
|
||||
} else {
|
||||
log('Document already loaded, starting bypass process');
|
||||
bypassRecaptcha();
|
||||
}
|
||||
@@ -5,4 +5,6 @@ requests
|
||||
psutil>=5.8.0
|
||||
pywin32; platform_system == "Windows"
|
||||
pyinstaller
|
||||
DrissionPage>=4.0.0
|
||||
DrissionPage>=4.0.0
|
||||
selenium
|
||||
webdriver_manager
|
||||
|
||||
@@ -5,85 +5,544 @@ import uuid
|
||||
import hashlib
|
||||
import shutil
|
||||
import sqlite3
|
||||
from logo import print_logo
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"RESET": "<EFBFBD><EFBFBD>",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
class MachineIDResetter:
|
||||
def __init__(self):
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
""" Get Cursor related paths"""
|
||||
system = platform.system()
|
||||
|
||||
# Read config file
|
||||
config = configparser.ConfigParser()
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
|
||||
# Default paths for different systems
|
||||
default_paths = {
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app")]
|
||||
}
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
if not config.has_section(section):
|
||||
config.add_section(section)
|
||||
|
||||
if system == "Darwin":
|
||||
config.set('MacPaths', 'cursor_path', default_paths["Darwin"])
|
||||
elif system == "Windows":
|
||||
config.set('WindowsPaths', 'cursor_path', default_paths["Windows"])
|
||||
elif system == "Linux":
|
||||
# For Linux, try to find the first existing path
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
config.set('LinuxPaths', 'cursor_path', path)
|
||||
break
|
||||
else:
|
||||
# If no path exists, use the first one as default
|
||||
config.set('LinuxPaths', 'cursor_path', default_paths["Linux"][0])
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
else:
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# Get path based on system
|
||||
if system == "Darwin":
|
||||
section = 'MacPaths'
|
||||
elif system == "Windows":
|
||||
section = 'WindowsPaths'
|
||||
elif system == "Linux":
|
||||
section = 'LinuxPaths'
|
||||
else:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
|
||||
raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
|
||||
|
||||
base_path = config.get(section, 'cursor_path')
|
||||
|
||||
# For Linux, try to find the first existing path if the configured one doesn't exist
|
||||
if system == "Linux" and not os.path.exists(base_path):
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
base_path = path
|
||||
# Update config with the found path
|
||||
config.set(section, 'cursor_path', path)
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
break
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
|
||||
|
||||
pkg_path = os.path.join(base_path, "package.json")
|
||||
main_path = os.path.join(base_path, "out/main.js")
|
||||
|
||||
# Check if files exist
|
||||
if not os.path.exists(pkg_path):
|
||||
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.main_not_found', path=main_path) if translator else f"找不到 main.js: {main_path}")
|
||||
|
||||
return (pkg_path, main_path)
|
||||
|
||||
# 判断操作系统
|
||||
def get_cursor_machine_id_path(translator=None) -> str:
|
||||
"""
|
||||
Get Cursor machineId file path based on operating system
|
||||
Returns:
|
||||
str: Path to machineId file
|
||||
"""
|
||||
# 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 sys.platform == "win32": # Windows
|
||||
if not config.has_section('WindowsPaths'):
|
||||
config.add_section('WindowsPaths')
|
||||
config.set('WindowsPaths', 'machine_id_path',
|
||||
os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"))
|
||||
return config.get('WindowsPaths', 'machine_id_path')
|
||||
|
||||
elif sys.platform == "linux": # Linux
|
||||
if not config.has_section('LinuxPaths'):
|
||||
config.add_section('LinuxPaths')
|
||||
config.set('LinuxPaths', 'machine_id_path',
|
||||
os.path.expanduser("~/.config/cursor/machineid"))
|
||||
return config.get('LinuxPaths', 'machine_id_path')
|
||||
|
||||
elif sys.platform == "darwin": # macOS
|
||||
if not config.has_section('MacPaths'):
|
||||
config.add_section('MacPaths')
|
||||
config.set('MacPaths', 'machine_id_path',
|
||||
os.path.expanduser("~/Library/Application Support/Cursor/machineId"))
|
||||
return config.get('MacPaths', 'machine_id_path')
|
||||
|
||||
else:
|
||||
raise OSError(f"Unsupported operating system: {sys.platform}")
|
||||
|
||||
# Save any changes to config file
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
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"])
|
||||
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"]
|
||||
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 version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
|
||||
"""Version number check"""
|
||||
version_pattern = r"^\d+\.\d+\.\d+$"
|
||||
try:
|
||||
if not re.match(version_pattern, version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def parse_version(ver: str) -> Tuple[int, ...]:
|
||||
return tuple(map(int, ver.split(".")))
|
||||
|
||||
current = parse_version(version)
|
||||
|
||||
if min_version and current < parse_version(min_version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_low', version=version, min_version=min_version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if max_version and current > parse_version(max_version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_high', version=version, max_version=max_version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_check_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_cursor_version(translator) -> bool:
|
||||
"""Check Cursor version"""
|
||||
try:
|
||||
pkg_path, _ = get_cursor_paths(translator)
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.reading_package_json', path=pkg_path)}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except UnicodeDecodeError:
|
||||
# If UTF-8 reading fails, try other encodings
|
||||
with open(pkg_path, "r", encoding="latin-1") as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not isinstance(data, dict):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if "version" not in data:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_version_field')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
version = str(data["version"]).strip()
|
||||
if not version:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_field_empty')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
|
||||
|
||||
# Check version format
|
||||
if not re.match(r"^\d+\.\d+\.\d+$", version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Compare versions
|
||||
try:
|
||||
current = tuple(map(int, version.split(".")))
|
||||
min_ver = (0, 45, 0) # Use tuple directly instead of string
|
||||
|
||||
if current >= min_ver:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.version_too_low', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except ValueError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_parse_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.package_not_found', path=pkg_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.stack_trace')}: {traceback.format_exc()}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
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()
|
||||
|
||||
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)'
|
||||
|
||||
CBadge_old_pattern = r'<div>Pro Trial'
|
||||
CBadge_new_pattern = r'<div>Pro'
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
shutil.copy2(file_path, backup_path)
|
||||
|
||||
# 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 modify_main_js(main_path: str, translator) -> bool:
|
||||
"""Modify main.js file"""
|
||||
try:
|
||||
original_stat = os.stat(main_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()
|
||||
|
||||
patterns = {
|
||||
r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}",
|
||||
r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(main_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():
|
||||
os.unlink(tmp_path)
|
||||
return False
|
||||
|
||||
def patch_cursor_get_machine_id(translator) -> bool:
|
||||
"""Patch Cursor getMachineId function"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
|
||||
|
||||
# Get paths
|
||||
pkg_path, main_path = get_cursor_paths(translator)
|
||||
|
||||
# Check file permissions
|
||||
for file_path in [pkg_path, main_path]:
|
||||
if not os.path.isfile(file_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
if not os.access(file_path, os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Get version number
|
||||
try:
|
||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||
version = json.load(f)["version"]
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.current_version', version=version)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check version
|
||||
if not version_check(version, min_version="0.45.0", translator=translator):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# Backup file
|
||||
backup_path = main_path + ".bak"
|
||||
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}")
|
||||
|
||||
# Modify file
|
||||
if not modify_main_js(main_path, translator):
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.patch_completed')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.patch_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
class MachineIDResetter:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
|
||||
# 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 not os.path.exists(config_file):
|
||||
raise FileNotFoundError(f"Config file not found: {config_file}")
|
||||
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# Check operating system
|
||||
if sys.platform == "win32": # Windows
|
||||
appdata = os.getenv("APPDATA")
|
||||
if appdata is None:
|
||||
raise EnvironmentError("APPDATA Environment Variable Not Set | APPDATA 环境变量未设置")
|
||||
self.db_path = os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "storage.json"
|
||||
)
|
||||
self.sqlite_path = os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
)
|
||||
raise EnvironmentError("APPDATA Environment Variable Not Set")
|
||||
|
||||
if not config.has_section('WindowsPaths'):
|
||||
config.add_section('WindowsPaths')
|
||||
config.set('WindowsPaths', 'storage_path', os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "storage.json"
|
||||
))
|
||||
config.set('WindowsPaths', 'sqlite_path', os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
))
|
||||
|
||||
self.db_path = config.get('WindowsPaths', 'storage_path')
|
||||
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
|
||||
|
||||
elif sys.platform == "darwin": # macOS
|
||||
self.db_path = os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/storage.json"
|
||||
))
|
||||
self.sqlite_path = os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||
))
|
||||
elif sys.platform == "linux": # Linux 和其他类Unix系统
|
||||
self.db_path = os.path.abspath(os.path.expanduser(
|
||||
"~/.config/Cursor/User/globalStorage/storage.json"
|
||||
))
|
||||
self.sqlite_path = os.path.abspath(os.path.expanduser(
|
||||
"~/.config/Cursor/User/globalStorage/state.vscdb"
|
||||
))
|
||||
if not config.has_section('MacPaths'):
|
||||
config.add_section('MacPaths')
|
||||
config.set('MacPaths', 'storage_path', os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/storage.json"
|
||||
)))
|
||||
config.set('MacPaths', 'sqlite_path', os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||
)))
|
||||
|
||||
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'):
|
||||
config.add_section('LinuxPaths')
|
||||
# Get actual user's home directory
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
|
||||
|
||||
config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/cursor/User/globalStorage/storage.json"
|
||||
)))
|
||||
config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/cursor/User/globalStorage/state.vscdb"
|
||||
)))
|
||||
|
||||
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}")
|
||||
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
|
||||
|
||||
# Save any changes to config file
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
|
||||
def generate_new_ids(self):
|
||||
"""生成新的机器ID"""
|
||||
# 生成新的UUID
|
||||
"""Generate new machine ID"""
|
||||
# Generate new UUID
|
||||
dev_device_id = str(uuid.uuid4())
|
||||
|
||||
# 生成新的machineId (64个字符的十六进制)
|
||||
# Generate new machineId (64 characters of hexadecimal)
|
||||
machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
|
||||
|
||||
# 生成新的macMachineId (128个字符的十六进制)
|
||||
# Generate new macMachineId (128 characters of hexadecimal)
|
||||
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
|
||||
|
||||
# 生成新的sqmId
|
||||
# Generate new sqmId
|
||||
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
|
||||
self.update_machine_id_file(dev_device_id)
|
||||
|
||||
return {
|
||||
"telemetry.devDeviceId": dev_device_id,
|
||||
"telemetry.macMachineId": mac_machine_id,
|
||||
"telemetry.machineId": machine_id,
|
||||
"telemetry.sqmId": sqm_id,
|
||||
"storage.serviceMachineId": dev_device_id, # 添加 storage.serviceMachineId
|
||||
"storage.serviceMachineId": dev_device_id, # Add storage.serviceMachineId
|
||||
}
|
||||
|
||||
def update_sqlite_db(self, new_ids):
|
||||
"""更新 SQLite 数据库中的机器ID"""
|
||||
"""Update machine ID in SQLite database"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Updating SQLite Database | 正在更新 SQLite 数据库...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.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,
|
||||
@@ -91,108 +550,240 @@ class MachineIDResetter:
|
||||
)
|
||||
""")
|
||||
|
||||
# 准备更新数据
|
||||
updates = [
|
||||
(key, value) for key, value in new_ids.items()
|
||||
]
|
||||
|
||||
# 使用参数化查询来避免 SQL 注入和处理长文本
|
||||
for key, value in updates:
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO ItemTable (key, value)
|
||||
VALUES (?, ?)
|
||||
""", (key, value))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN}Updating Key-Value Pair | 更新键值对: {key}{Style.RESET_ALL}")
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('reset.updating_pair')}: {key}{Style.RESET_ALL}")
|
||||
|
||||
# 提交更改并关闭连接
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} SQLite Database Updated Successfully | 数据库更新成功!{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.sqlite_success')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} SQLite Database Update Failed | 数据库更新失败: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.sqlite_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def reset_machine_ids(self):
|
||||
"""重置机器ID并备份原文件"""
|
||||
def update_system_ids(self, new_ids):
|
||||
"""Update system-level IDs"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking Config File | 正在检查配置文件...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self._update_windows_machine_guid()
|
||||
self._update_windows_machine_id()
|
||||
elif sys.platform == "darwin":
|
||||
self._update_macos_platform_uuid(new_ids)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.system_ids_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _update_windows_machine_guid(self):
|
||||
"""Update Windows MachineGuid"""
|
||||
try:
|
||||
import winreg
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Cryptography",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
new_guid = str(uuid.uuid4())
|
||||
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}")
|
||||
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}")
|
||||
raise
|
||||
|
||||
def _update_windows_machine_id(self):
|
||||
"""Update Windows MachineId in SQMClient registry"""
|
||||
try:
|
||||
import winreg
|
||||
# 1. Generate new GUID
|
||||
new_guid = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.new_machine_id')}: {new_guid}{Style.RESET_ALL}")
|
||||
|
||||
# 2. Open the registry key
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
except FileNotFoundError:
|
||||
# If the key does not exist, create it
|
||||
key = winreg.CreateKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient"
|
||||
)
|
||||
|
||||
# 3. Set MachineId value
|
||||
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_id_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
|
||||
def _update_macos_platform_uuid(self, new_ids):
|
||||
"""Update macOS Platform UUID"""
|
||||
try:
|
||||
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||
if os.path.exists(uuid_file):
|
||||
# Use sudo to execute plutil command
|
||||
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
|
||||
result = os.system(cmd)
|
||||
if result == 0:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
|
||||
else:
|
||||
raise Exception(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_macos_platform_uuid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def reset_machine_ids(self):
|
||||
"""Reset machine ID and backup original file"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
|
||||
|
||||
# 检查JSON文件是否存在
|
||||
if not os.path.exists(self.db_path):
|
||||
print(
|
||||
f"{Fore.RED}{EMOJI['ERROR']} Config File Not Found | 配置文件不存在: {self.db_path}{Style.RESET_ALL}"
|
||||
)
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.not_found')}: {self.db_path}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 检查文件权限
|
||||
if not os.access(self.db_path, os.R_OK | os.W_OK):
|
||||
print(
|
||||
f"{Fore.RED}{EMOJI['ERROR']} Cannot Read or Write Config File, Please Check File Permissions | 无法读写配置文件,请检查文件权限!{Style.RESET_ALL}"
|
||||
)
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.no_permission')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 读取现有配置
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} Reading Current Config | 读取当前配置...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.reading')}...{Style.RESET_ALL}")
|
||||
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']} Creating Config Backup | 创建配置备份: {backup_path}{Style.RESET_ALL}"
|
||||
)
|
||||
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']} Backup File Already Exists, Skipping Backup Step | 已存在备份文件,跳过备份步骤{Style.RESET_ALL}"
|
||||
)
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
|
||||
|
||||
# 生成新的ID
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} Generating New Machine ID | 生成新的机器标识...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
|
||||
# 更新配置
|
||||
# Update configuration file
|
||||
config.update(new_ids)
|
||||
|
||||
# 保存新配置到 JSON
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} Saving New Config to JSON | 保存新配置到 JSON...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
|
||||
with open(self.db_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=4)
|
||||
|
||||
# 更新 SQLite 数据库
|
||||
# Update SQLite database
|
||||
self.update_sqlite_db(new_ids)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID Reset Successfully | 机器标识重置成功!{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}New Machine ID | 新的机器标识:{Style.RESET_ALL}")
|
||||
# Update system IDs
|
||||
self.update_system_ids(new_ids)
|
||||
|
||||
|
||||
# Modify workbench.desktop.main.js
|
||||
workbench_path = get_workbench_cursor_path(self.translator)
|
||||
modify_workbench_js(workbench_path, self.translator)
|
||||
|
||||
# Check Cursor version and perform corresponding actions
|
||||
|
||||
greater_than_0_45 = check_cursor_version(self.translator)
|
||||
if greater_than_0_45:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0,{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
|
||||
patch_cursor_get_machine_id(self.translator)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
|
||||
for key, value in new_ids.items():
|
||||
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Permission Error | 权限错误: {str(e)}{Style.RESET_ALL}")
|
||||
print(
|
||||
f"{Fore.YELLOW}{EMOJI['INFO']} Please Try Running This Program as Administrator | 请尝试以管理员身份运行此程序{Style.RESET_ALL}"
|
||||
)
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_error', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Reset Process Error | 重置过程出错: {str(e)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run():
|
||||
"""Main function to be called from main.py"""
|
||||
def update_machine_id_file(self, machine_id: str) -> bool:
|
||||
"""
|
||||
Update machineId file with new machine_id
|
||||
Args:
|
||||
machine_id (str): New machine ID to write
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get the machineId file path
|
||||
machine_id_path = get_cursor_machine_id_path()
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
|
||||
|
||||
# Create backup if file exists
|
||||
if os.path.exists(machine_id_path):
|
||||
backup_path = machine_id_path + ".backup"
|
||||
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}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_creation_failed', error=str(e)) if self.translator else f'Could not create backup: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Write new machine ID to file
|
||||
with open(machine_id_path, "w", encoding="utf-8") as f:
|
||||
f.write(machine_id)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.update_success') if self.translator else 'Successfully updated machineId file'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Failed to update machineId file: {str(e)}"
|
||||
if self.translator:
|
||||
error_msg = self.translator.get('reset.update_failed', error=str(e))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
|
||||
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']} Cursor Machine ID Reset Tool | Cursor 机器标识重置工具{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
resetter = MachineIDResetter()
|
||||
resetter = MachineIDResetter(translator) # Correctly pass translator
|
||||
resetter.reset_machine_ids()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} Press Enter to Exit | 按回车键退出...")
|
||||
input(f"{EMOJI['INFO']} {translator.get('reset.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
@@ -1,9 +1,4 @@
|
||||
# 檢查是否是通過權限提升啟動的
|
||||
param(
|
||||
[switch]$Elevated
|
||||
)
|
||||
|
||||
# 設置顏色主題
|
||||
# set color theme
|
||||
$Theme = @{
|
||||
Primary = 'Cyan'
|
||||
Success = 'Green'
|
||||
@@ -22,20 +17,7 @@ $Logo = @"
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
||||
"@
|
||||
|
||||
# 進度條函數
|
||||
function Write-ProgressBar {
|
||||
param (
|
||||
[int]$Percent,
|
||||
[string]$Activity
|
||||
)
|
||||
$width = $Host.UI.RawUI.WindowSize.Width - 20
|
||||
$completed = [math]::Floor($width * ($Percent / 100))
|
||||
$remaining = $width - $completed
|
||||
$progressBar = "[" + ("█" * $completed) + ("-" * $remaining) + "]"
|
||||
Write-Host "`r$Activity $progressBar $Percent%" -NoNewline
|
||||
}
|
||||
|
||||
# 美化輸出函數
|
||||
# Beautiful Output Function
|
||||
function Write-Styled {
|
||||
param (
|
||||
[string]$Message,
|
||||
@@ -43,14 +25,14 @@ function Write-Styled {
|
||||
[string]$Prefix = "",
|
||||
[switch]$NoNewline
|
||||
)
|
||||
$emoji = switch ($Color) {
|
||||
$Theme.Success { "✅" }
|
||||
$Theme.Error { "❌" }
|
||||
$Theme.Warning { "⚠️" }
|
||||
default { "ℹ️" }
|
||||
$symbol = switch ($Color) {
|
||||
$Theme.Success { "[OK]" }
|
||||
$Theme.Error { "[X]" }
|
||||
$Theme.Warning { "[!]" }
|
||||
default { "[*]" }
|
||||
}
|
||||
|
||||
$output = if ($Prefix) { "$emoji $Prefix :: $Message" } else { "$emoji $Message" }
|
||||
$output = if ($Prefix) { "$symbol $Prefix :: $Message" } else { "$symbol $Message" }
|
||||
if ($NoNewline) {
|
||||
Write-Host $output -ForegroundColor $Color -NoNewline
|
||||
} else {
|
||||
@@ -58,62 +40,7 @@ function Write-Styled {
|
||||
}
|
||||
}
|
||||
|
||||
# 檢查管理員權限
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
||||
if (-NOT $isAdmin) {
|
||||
Write-Styled "需要管理員權限來安裝" -Color $Theme.Warning -Prefix "權限"
|
||||
Write-Styled "正在請求管理員權限..." -Color $Theme.Primary -Prefix "提升"
|
||||
|
||||
# 顯示操作選項
|
||||
Write-Host "`n選擇操作:" -ForegroundColor $Theme.Primary
|
||||
Write-Host "1. 請求管理員權限" -ForegroundColor $Theme.Info
|
||||
Write-Host "2. 退出程序" -ForegroundColor $Theme.Info
|
||||
|
||||
$choice = Read-Host "`n請輸入選項 (1-2)"
|
||||
|
||||
if ($choice -ne "1") {
|
||||
Write-Styled "安裝已取消" -Color $Theme.Warning -Prefix "取消"
|
||||
Write-Host "`n按任意鍵退出..." -ForegroundColor $Theme.Info
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit
|
||||
}
|
||||
|
||||
$pwshPath = if (Get-Command "pwsh" -ErrorAction SilentlyContinue) {
|
||||
(Get-Command "pwsh").Source
|
||||
} elseif (Test-Path "$env:ProgramFiles\PowerShell\7\pwsh.exe") {
|
||||
"$env:ProgramFiles\PowerShell\7\pwsh.exe"
|
||||
} else {
|
||||
"powershell.exe"
|
||||
}
|
||||
|
||||
try {
|
||||
$arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`" -Elevated -WindowStyle Normal"
|
||||
Start-Process -FilePath $pwshPath -Verb RunAs -ArgumentList $arguments
|
||||
Write-Host "`n請在新開啟的管理員權限視窗中繼續操作..." -ForegroundColor $Theme.Primary
|
||||
|
||||
# 等待用戶確認
|
||||
Write-Host "`n按任意鍵退出此窗口..." -ForegroundColor $Theme.Info
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit
|
||||
}
|
||||
catch {
|
||||
Write-Styled "無法獲取管理員權限" -Color $Theme.Error -Prefix "錯誤"
|
||||
Write-Styled "請以管理員身份運行 PowerShell 後重試" -Color $Theme.Warning -Prefix "提示"
|
||||
Write-Styled "您可以:" -Color $Theme.Info
|
||||
Write-Host "1. 右鍵點擊 PowerShell,選擇「以系統管理員身分執行」" -ForegroundColor $Theme.Info
|
||||
Write-Host "2. 然後重新運行此安裝程序" -ForegroundColor $Theme.Info
|
||||
Write-Host "`n按任意鍵退出..." -ForegroundColor $Theme.Info
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# 如果是提升權限後的窗口,等待一下確保窗口可見
|
||||
if ($Elevated) {
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
|
||||
# 獲取版本號函數
|
||||
# Get version number function
|
||||
function Get-LatestVersion {
|
||||
try {
|
||||
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest"
|
||||
@@ -122,101 +49,167 @@ function Get-LatestVersion {
|
||||
Assets = $latestRelease.assets
|
||||
}
|
||||
} catch {
|
||||
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "錯誤"
|
||||
throw "無法獲取最新版本信息"
|
||||
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error"
|
||||
throw "Cannot get latest version"
|
||||
}
|
||||
}
|
||||
|
||||
# 顯示 Logo
|
||||
# Show Logo
|
||||
Write-Host $Logo -ForegroundColor $Theme.Primary
|
||||
$releaseInfo = Get-LatestVersion
|
||||
$version = $releaseInfo.Version
|
||||
Write-Host "Version $version" -ForegroundColor $Theme.Info
|
||||
Write-Host "Created by YeongPin`n" -ForegroundColor $Theme.Info
|
||||
|
||||
# 設置 TLS 1.2
|
||||
# Set TLS 1.2
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# 創建臨時目錄
|
||||
$TmpDir = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString())
|
||||
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
|
||||
|
||||
# 清理函數
|
||||
function Cleanup {
|
||||
if (Test-Path $TmpDir) {
|
||||
Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# 主安裝函數
|
||||
# Main installation function
|
||||
function Install-CursorFreeVIP {
|
||||
Write-Styled "開始安裝 Cursor Free VIP" -Color $Theme.Primary -Prefix "安裝"
|
||||
|
||||
# 設置安裝目錄
|
||||
$InstallDir = "$env:ProgramFiles\CursorFreeVIP"
|
||||
if (!(Test-Path $InstallDir)) {
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
}
|
||||
Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download"
|
||||
|
||||
try {
|
||||
# 獲取最新版本
|
||||
Write-Styled "正在檢查最新版本..." -Color $Theme.Primary -Prefix "更新"
|
||||
# Get latest version
|
||||
Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update"
|
||||
$releaseInfo = Get-LatestVersion
|
||||
$version = $releaseInfo.Version
|
||||
Write-Styled "找到最新版本: $version" -Color $Theme.Success -Prefix "版本"
|
||||
Write-Styled "Found latest version: $version" -Color $Theme.Success -Prefix "Version"
|
||||
|
||||
# 查找對應的資源
|
||||
# Find corresponding resources
|
||||
$asset = $releaseInfo.Assets | Where-Object { $_.name -eq "CursorFreeVIP_${version}_windows.exe" }
|
||||
if (!$asset) {
|
||||
Write-Styled "找不到檔案: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "錯誤"
|
||||
Write-Styled "可用的檔案:" -Color $Theme.Warning -Prefix "資訊"
|
||||
Write-Styled "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error"
|
||||
Write-Styled "Available files:" -Color $Theme.Warning -Prefix "Info"
|
||||
$releaseInfo.Assets | ForEach-Object {
|
||||
Write-Styled "- $($_.name)" -Color $Theme.Info
|
||||
}
|
||||
throw "找不到對應的安裝檔案"
|
||||
throw "Cannot find target file"
|
||||
}
|
||||
|
||||
# 下載
|
||||
Write-Styled "正在下載..." -Color $Theme.Primary -Prefix "下載"
|
||||
# Check if Downloads folder already exists for the corresponding version
|
||||
$DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"
|
||||
$downloadPath = Join-Path $DownloadsPath "CursorFreeVIP_${version}_windows.exe"
|
||||
|
||||
if (Test-Path $downloadPath) {
|
||||
Write-Styled "Found existing installation file" -Color $Theme.Success -Prefix "Found"
|
||||
Write-Styled "Location: $downloadPath" -Color $Theme.Info -Prefix "Location"
|
||||
|
||||
# Check if running with administrator privileges
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
|
||||
if (-not $isAdmin) {
|
||||
Write-Styled "Requesting administrator privileges..." -Color $Theme.Warning -Prefix "Admin"
|
||||
|
||||
# Create new process with administrator privileges
|
||||
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$startInfo.FileName = $downloadPath
|
||||
$startInfo.UseShellExecute = $true
|
||||
$startInfo.Verb = "runas"
|
||||
|
||||
try {
|
||||
[System.Diagnostics.Process]::Start($startInfo)
|
||||
Write-Styled "Program started with admin privileges" -Color $Theme.Success -Prefix "Launch"
|
||||
return
|
||||
}
|
||||
catch {
|
||||
Write-Styled "Failed to start with admin privileges. Starting normally..." -Color $Theme.Warning -Prefix "Warning"
|
||||
Start-Process $downloadPath
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# If already running with administrator privileges, start directly
|
||||
Start-Process $downloadPath
|
||||
return
|
||||
}
|
||||
|
||||
Write-Styled "No existing installation file found, starting download..." -Color $Theme.Primary -Prefix "Download"
|
||||
|
||||
# Create WebClient and add progress event
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.Headers.Add("User-Agent", "PowerShell Script")
|
||||
|
||||
$downloadPath = Join-Path $TmpDir "CursorFreeVIP.exe"
|
||||
$webClient.DownloadFile($asset.browser_download_url, $downloadPath)
|
||||
|
||||
# 安裝
|
||||
Write-Styled "正在安裝到系統..." -Color $Theme.Primary -Prefix "安裝"
|
||||
Copy-Item -Path $downloadPath -Destination "$InstallDir\CursorFreeVIP.exe" -Force
|
||||
|
||||
# 添加到 PATH
|
||||
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
if ($currentPath -notlike "*$InstallDir*") {
|
||||
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$InstallDir", "Machine")
|
||||
|
||||
# Define progress variables
|
||||
$Global:downloadedBytes = 0
|
||||
$Global:totalBytes = 0
|
||||
$Global:lastProgress = 0
|
||||
$Global:lastBytes = 0
|
||||
$Global:lastTime = Get-Date
|
||||
|
||||
# Download progress event
|
||||
$eventId = [guid]::NewGuid()
|
||||
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
|
||||
$Global:downloadedBytes = $EventArgs.BytesReceived
|
||||
$Global:totalBytes = $EventArgs.TotalBytesToReceive
|
||||
$progress = [math]::Round(($Global:downloadedBytes / $Global:totalBytes) * 100, 1)
|
||||
|
||||
# Only update display when progress changes by more than 1%
|
||||
if ($progress -gt $Global:lastProgress + 1) {
|
||||
$Global:lastProgress = $progress
|
||||
$downloadedMB = [math]::Round($Global:downloadedBytes / 1MB, 2)
|
||||
$totalMB = [math]::Round($Global:totalBytes / 1MB, 2)
|
||||
|
||||
# Calculate download speed
|
||||
$currentTime = Get-Date
|
||||
$timeSpan = ($currentTime - $Global:lastTime).TotalSeconds
|
||||
if ($timeSpan -gt 0) {
|
||||
$bytesChange = $Global:downloadedBytes - $Global:lastBytes
|
||||
$speed = $bytesChange / $timeSpan
|
||||
|
||||
# Choose appropriate unit based on speed
|
||||
$speedDisplay = if ($speed -gt 1MB) {
|
||||
"$([math]::Round($speed / 1MB, 2)) MB/s"
|
||||
} elseif ($speed -gt 1KB) {
|
||||
"$([math]::Round($speed / 1KB, 2)) KB/s"
|
||||
} else {
|
||||
"$([math]::Round($speed, 2)) B/s"
|
||||
}
|
||||
|
||||
Write-Host "`rDownloading: $downloadedMB MB / $totalMB MB ($progress%) - $speedDisplay" -NoNewline -ForegroundColor Cyan
|
||||
|
||||
# Update last data
|
||||
$Global:lastBytes = $Global:downloadedBytes
|
||||
$Global:lastTime = $currentTime
|
||||
}
|
||||
}
|
||||
} | Out-Null
|
||||
|
||||
# Download completed event
|
||||
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -Action {
|
||||
Write-Host "`r" -NoNewline
|
||||
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
|
||||
Unregister-Event -SourceIdentifier $eventId
|
||||
} | Out-Null
|
||||
|
||||
# Start download
|
||||
$webClient.DownloadFileAsync([Uri]$asset.browser_download_url, $downloadPath)
|
||||
|
||||
# Wait for download to complete
|
||||
while ($webClient.IsBusy) {
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
|
||||
Write-Styled "安裝完成!" -Color $Theme.Success -Prefix "完成"
|
||||
Write-Styled "正在啟動程序..." -Color $Theme.Primary -Prefix "啟動"
|
||||
|
||||
# 運行程序
|
||||
Start-Process "$InstallDir\CursorFreeVIP.exe"
|
||||
Write-Styled "File location: $downloadPath" -Color $Theme.Info -Prefix "Location"
|
||||
Write-Styled "Starting program..." -Color $Theme.Primary -Prefix "Launch"
|
||||
|
||||
# Run program
|
||||
Start-Process $downloadPath
|
||||
}
|
||||
catch {
|
||||
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "錯誤"
|
||||
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error"
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
# 執行安裝
|
||||
# Execute installation
|
||||
try {
|
||||
Install-CursorFreeVIP
|
||||
}
|
||||
catch {
|
||||
Write-Styled "安裝失敗" -Color $Theme.Error -Prefix "錯誤"
|
||||
Write-Styled "Download failed" -Color $Theme.Error -Prefix "Error"
|
||||
Write-Styled $_.Exception.Message -Color $Theme.Error
|
||||
}
|
||||
finally {
|
||||
Cleanup
|
||||
Write-Host "`n按任意鍵退出..." -ForegroundColor $Theme.Info
|
||||
Write-Host "`nPress any key to exit..." -ForegroundColor $Theme.Info
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
}
|
||||
}
|
||||
|
||||
154
scripts/install.sh
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 顏色定義
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -22,7 +22,7 @@ EOF
|
||||
echo -e "${NC}"
|
||||
}
|
||||
|
||||
# 获取下载文件夹路径
|
||||
# Get download folder path
|
||||
get_downloads_dir() {
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo "$HOME/Downloads"
|
||||
@@ -36,59 +36,157 @@ get_downloads_dir() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 獲取最新版本
|
||||
# Get latest version
|
||||
get_latest_version() {
|
||||
echo -e "${CYAN}ℹ️ 正在檢查最新版本...${NC}"
|
||||
local latest_release
|
||||
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ 無法獲取最新版本信息${NC}"
|
||||
echo -e "${CYAN}ℹ️ Checking latest version...${NC}"
|
||||
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) || {
|
||||
echo -e "${RED}❌ Cannot get latest version information${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
|
||||
echo -e "${GREEN}✅ 找到最新版本: ${VERSION}${NC}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo -e "${RED}❌ Failed to parse version from GitHub API response:\n${latest_release}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
|
||||
}
|
||||
|
||||
# 檢測系統類型
|
||||
# Detect system type and architecture
|
||||
detect_os() {
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
OS="mac"
|
||||
# Detect macOS architecture
|
||||
ARCH=$(uname -m)
|
||||
if [[ "$ARCH" == "arm64" ]]; then
|
||||
OS="mac_arm64"
|
||||
echo -e "${CYAN}ℹ️ Detected macOS ARM64 architecture${NC}"
|
||||
else
|
||||
OS="mac_intel"
|
||||
echo -e "${CYAN}ℹ️ Detected macOS Intel architecture${NC}"
|
||||
fi
|
||||
elif [[ "$(uname)" == "Linux" ]]; then
|
||||
# Detect Linux architecture
|
||||
ARCH=$(uname -m)
|
||||
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
|
||||
OS="linux_arm64"
|
||||
echo -e "${CYAN}ℹ️ Detected Linux ARM64 architecture${NC}"
|
||||
else
|
||||
OS="linux_x64"
|
||||
echo -e "${CYAN}ℹ️ Detected Linux x64 architecture${NC}"
|
||||
fi
|
||||
else
|
||||
OS="linux"
|
||||
# Assume Windows
|
||||
OS="windows"
|
||||
echo -e "${CYAN}ℹ️ Detected Windows system${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 下載並安裝
|
||||
# Install and download
|
||||
install_cursor_free_vip() {
|
||||
local downloads_dir=$(get_downloads_dir)
|
||||
local binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
||||
local binary_path="${downloads_dir}/cursor-free-vip"
|
||||
local binary_path="${downloads_dir}/${binary_name}"
|
||||
local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
||||
|
||||
echo -e "${CYAN}ℹ️ 正在下載到 ${downloads_dir}...${NC}"
|
||||
# Check if file already exists
|
||||
if [ -f "${binary_path}" ]; then
|
||||
echo -e "${GREEN}✅ Found existing installation file${NC}"
|
||||
echo -e "${CYAN}ℹ️ Location: ${binary_path}${NC}"
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${YELLOW}⚠️ Requesting administrator privileges...${NC}"
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
echo -e "${CYAN}ℹ️ Starting program with sudo...${NC}"
|
||||
sudo chmod +x "${binary_path}"
|
||||
sudo "${binary_path}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ sudo not found, trying to run normally...${NC}"
|
||||
chmod +x "${binary_path}"
|
||||
"${binary_path}"
|
||||
fi
|
||||
else
|
||||
# Already running as root
|
||||
echo -e "${CYAN}ℹ️ Already running as root, starting program...${NC}"
|
||||
chmod +x "${binary_path}"
|
||||
"${binary_path}"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}ℹ️ No existing installation file found, starting download...${NC}"
|
||||
echo -e "${CYAN}ℹ️ Downloading to ${downloads_dir}...${NC}"
|
||||
echo -e "${CYAN}ℹ️ Download link: ${download_url}${NC}"
|
||||
|
||||
# Check if file exists
|
||||
if curl --output /dev/null --silent --head --fail "$download_url"; then
|
||||
echo -e "${GREEN}✅ File exists, starting download...${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Download link does not exist: ${download_url}${NC}"
|
||||
echo -e "${YELLOW}⚠️ Trying without architecture...${NC}"
|
||||
|
||||
# Try without architecture
|
||||
if [[ "$OS" == "mac_arm64" || "$OS" == "mac_intel" ]]; then
|
||||
OS="mac"
|
||||
binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
||||
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
||||
echo -e "${CYAN}ℹ️ New download link: ${download_url}${NC}"
|
||||
|
||||
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
|
||||
echo -e "${RED}❌ New download link does not exist${NC}"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "$OS" == "linux_x64" || "$OS" == "linux_arm64" ]]; then
|
||||
OS="linux"
|
||||
binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
||||
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
||||
echo -e "${CYAN}ℹ️ New download link: ${download_url}${NC}"
|
||||
|
||||
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
|
||||
echo -e "${RED}❌ New download link does not exist${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Download file
|
||||
if ! curl -L -o "${binary_path}" "$download_url"; then
|
||||
echo -e "${RED}❌ 下載失敗${NC}"
|
||||
echo -e "${RED}❌ Download failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}ℹ️ 正在設置執行權限...${NC}"
|
||||
chmod +x "${binary_path}"
|
||||
# Check downloaded file size
|
||||
local file_size=$(stat -f%z "${binary_path}" 2>/dev/null || stat -c%s "${binary_path}" 2>/dev/null)
|
||||
echo -e "${CYAN}ℹ️ Downloaded file size: ${file_size} bytes${NC}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ 安裝完成!${NC}"
|
||||
echo -e "${CYAN}ℹ️ 程序已下載到: ${binary_path}${NC}"
|
||||
echo -e "${CYAN}ℹ️ 正在啟動程序...${NC}"
|
||||
# If file is too small, it might be an error message
|
||||
if [ "$file_size" -lt 1000 ]; then
|
||||
echo -e "${YELLOW}⚠️ Warning: Downloaded file is too small, possibly not a valid executable file${NC}"
|
||||
echo -e "${YELLOW}⚠️ File content:${NC}"
|
||||
cat "${binary_path}"
|
||||
echo ""
|
||||
echo -e "${RED}❌ Download failed, please check version and operating system${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}ℹ️ Setting executable permissions...${NC}"
|
||||
if chmod +x "${binary_path}"; then
|
||||
echo -e "${GREEN}✅ Installation completed!${NC}"
|
||||
echo -e "${CYAN}ℹ️ Program downloaded to: ${binary_path}${NC}"
|
||||
echo -e "${CYAN}ℹ️ Starting program...${NC}"
|
||||
|
||||
# 直接运行程序
|
||||
# Run program directly
|
||||
"${binary_path}"
|
||||
else
|
||||
echo -e "${RED}❌ 安裝失敗${NC}"
|
||||
echo -e "${RED}❌ Installation failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主程序
|
||||
# Main program
|
||||
main() {
|
||||
print_logo
|
||||
get_latest_version
|
||||
@@ -96,5 +194,5 @@ main() {
|
||||
install_cursor_free_vip
|
||||
}
|
||||
|
||||
# 運行主程序
|
||||
main
|
||||
# Run main program
|
||||
main
|
||||
|
||||
789
totally_reset_cursor.py
Normal file
@@ -0,0 +1,789 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import uuid
|
||||
import hashlib
|
||||
import shutil
|
||||
import sqlite3
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "<EFBFBD><EFBFBD>",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
""" Get Cursor related paths"""
|
||||
system = platform.system()
|
||||
|
||||
# Read config file
|
||||
config = configparser.ConfigParser()
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
|
||||
# Default paths for different systems
|
||||
default_paths = {
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"Linux": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", os.path.expanduser("~/.local/share/cursor/resources/app")]
|
||||
}
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
if not config.has_section(section):
|
||||
config.add_section(section)
|
||||
|
||||
if system == "Darwin":
|
||||
config.set('MacPaths', 'cursor_path', default_paths["Darwin"])
|
||||
elif system == "Windows":
|
||||
config.set('WindowsPaths', 'cursor_path', default_paths["Windows"])
|
||||
elif system == "Linux":
|
||||
# For Linux, try to find the first existing path
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
config.set('LinuxPaths', 'cursor_path', path)
|
||||
break
|
||||
else:
|
||||
# If no path exists, use the first one as default
|
||||
config.set('LinuxPaths', 'cursor_path', default_paths["Linux"][0])
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
else:
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# Get path based on system
|
||||
if system == "Darwin":
|
||||
section = 'MacPaths'
|
||||
elif system == "Windows":
|
||||
section = 'WindowsPaths'
|
||||
elif system == "Linux":
|
||||
section = 'LinuxPaths'
|
||||
else:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
|
||||
raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
|
||||
|
||||
base_path = config.get(section, 'cursor_path')
|
||||
|
||||
# For Linux, try to find the first existing path if the configured one doesn't exist
|
||||
if system == "Linux" and not os.path.exists(base_path):
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
base_path = path
|
||||
# Update config with the found path
|
||||
config.set(section, 'cursor_path', path)
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
break
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
|
||||
|
||||
pkg_path = os.path.join(base_path, "package.json")
|
||||
main_path = os.path.join(base_path, "out/main.js")
|
||||
|
||||
# Check if files exist
|
||||
if not os.path.exists(pkg_path):
|
||||
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.main_not_found', path=main_path) if translator else f"找不到 main.js: {main_path}")
|
||||
|
||||
return (pkg_path, main_path)
|
||||
|
||||
def get_cursor_machine_id_path(translator=None) -> str:
|
||||
"""
|
||||
Get Cursor machineId file path based on operating system
|
||||
Returns:
|
||||
str: Path to machineId file
|
||||
"""
|
||||
# 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 sys.platform == "win32": # Windows
|
||||
if not config.has_section('WindowsPaths'):
|
||||
config.add_section('WindowsPaths')
|
||||
config.set('WindowsPaths', 'machine_id_path',
|
||||
os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"))
|
||||
return config.get('WindowsPaths', 'machine_id_path')
|
||||
|
||||
elif sys.platform == "linux": # Linux
|
||||
if not config.has_section('LinuxPaths'):
|
||||
config.add_section('LinuxPaths')
|
||||
config.set('LinuxPaths', 'machine_id_path',
|
||||
os.path.expanduser("~/.config/cursor/machineid"))
|
||||
return config.get('LinuxPaths', 'machine_id_path')
|
||||
|
||||
elif sys.platform == "darwin": # macOS
|
||||
if not config.has_section('MacPaths'):
|
||||
config.add_section('MacPaths')
|
||||
config.set('MacPaths', 'machine_id_path',
|
||||
os.path.expanduser("~/Library/Application Support/Cursor/machineId"))
|
||||
return config.get('MacPaths', 'machine_id_path')
|
||||
|
||||
else:
|
||||
raise OSError(f"Unsupported operating system: {sys.platform}")
|
||||
|
||||
# Save any changes to config file
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
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"])
|
||||
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"]
|
||||
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 version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
|
||||
"""Version number check"""
|
||||
version_pattern = r"^\d+\.\d+\.\d+$"
|
||||
try:
|
||||
if not re.match(version_pattern, version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def parse_version(ver: str) -> Tuple[int, ...]:
|
||||
return tuple(map(int, ver.split(".")))
|
||||
|
||||
current = parse_version(version)
|
||||
|
||||
if min_version and current < parse_version(min_version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_low', version=version, min_version=min_version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if max_version and current > parse_version(max_version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_high', version=version, max_version=max_version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_check_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_cursor_version(translator) -> bool:
|
||||
"""Check Cursor version"""
|
||||
try:
|
||||
pkg_path, _ = get_cursor_paths(translator)
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.reading_package_json', path=pkg_path)}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except UnicodeDecodeError:
|
||||
# If UTF-8 reading fails, try other encodings
|
||||
with open(pkg_path, "r", encoding="latin-1") as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not isinstance(data, dict):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if "version" not in data:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_version_field')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
version = str(data["version"]).strip()
|
||||
if not version:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_field_empty')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
|
||||
|
||||
# Check version format
|
||||
if not re.match(r"^\d+\.\d+\.\d+$", version):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Compare versions
|
||||
try:
|
||||
current = tuple(map(int, version.split(".")))
|
||||
min_ver = (0, 45, 0) # Use tuple directly instead of string
|
||||
|
||||
if current >= min_ver:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.version_too_low', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except ValueError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_parse_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.package_not_found', path=pkg_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.stack_trace')}: {traceback.format_exc()}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
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()
|
||||
|
||||
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)'
|
||||
|
||||
CBadge_old_pattern = r'<div>Pro Trial'
|
||||
CBadge_new_pattern = r'<div>Pro'
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
shutil.copy2(file_path, backup_path)
|
||||
|
||||
# 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 modify_main_js(main_path: str, translator) -> bool:
|
||||
"""Modify main.js file"""
|
||||
try:
|
||||
original_stat = os.stat(main_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()
|
||||
|
||||
patterns = {
|
||||
r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}",
|
||||
r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(main_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():
|
||||
os.unlink(tmp_path)
|
||||
return False
|
||||
|
||||
def patch_cursor_get_machine_id(translator) -> bool:
|
||||
"""Patch Cursor getMachineId function"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
|
||||
|
||||
# Get paths
|
||||
pkg_path, main_path = get_cursor_paths(translator)
|
||||
|
||||
# Check file permissions
|
||||
for file_path in [pkg_path, main_path]:
|
||||
if not os.path.isfile(file_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
if not os.access(file_path, os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Get version number
|
||||
try:
|
||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||
version = json.load(f)["version"]
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.current_version', version=version)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Check version
|
||||
if not version_check(version, min_version="0.45.0", translator=translator):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# Backup file
|
||||
backup_path = main_path + ".bak"
|
||||
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}")
|
||||
|
||||
# Modify file
|
||||
if not modify_main_js(main_path, translator):
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.patch_completed')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.patch_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
class MachineIDResetter:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
|
||||
# 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 not os.path.exists(config_file):
|
||||
raise FileNotFoundError(f"Config file not found: {config_file}")
|
||||
|
||||
config.read(config_file, encoding='utf-8')
|
||||
|
||||
# Check operating system
|
||||
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'):
|
||||
config.add_section('WindowsPaths')
|
||||
config.set('WindowsPaths', 'storage_path', os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "storage.json"
|
||||
))
|
||||
config.set('WindowsPaths', 'sqlite_path', os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
))
|
||||
|
||||
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'):
|
||||
config.add_section('MacPaths')
|
||||
config.set('MacPaths', 'storage_path', os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/storage.json"
|
||||
)))
|
||||
config.set('MacPaths', 'sqlite_path', os.path.abspath(os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||
)))
|
||||
|
||||
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'):
|
||||
config.add_section('LinuxPaths')
|
||||
# Get actual user's home directory
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
|
||||
|
||||
config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/cursor/User/globalStorage/storage.json"
|
||||
)))
|
||||
config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/cursor/User/globalStorage/state.vscdb"
|
||||
)))
|
||||
|
||||
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}")
|
||||
|
||||
# Save any changes to config file
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
config.write(f)
|
||||
|
||||
def generate_new_ids(self):
|
||||
"""Generate new machine ID"""
|
||||
# Generate new UUID
|
||||
dev_device_id = str(uuid.uuid4())
|
||||
|
||||
# Generate new machineId (64 characters of hexadecimal)
|
||||
machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
|
||||
|
||||
# Generate new macMachineId (128 characters of hexadecimal)
|
||||
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
|
||||
|
||||
# Generate new sqmId
|
||||
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
|
||||
self.update_machine_id_file(dev_device_id)
|
||||
|
||||
return {
|
||||
"telemetry.devDeviceId": dev_device_id,
|
||||
"telemetry.macMachineId": mac_machine_id,
|
||||
"telemetry.machineId": machine_id,
|
||||
"telemetry.sqmId": sqm_id,
|
||||
"storage.serviceMachineId": dev_device_id, # Add storage.serviceMachineId
|
||||
}
|
||||
|
||||
def update_sqlite_db(self, new_ids):
|
||||
"""Update machine ID in SQLite database"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.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
|
||||
)
|
||||
""")
|
||||
|
||||
updates = [
|
||||
(key, value) for key, value in new_ids.items()
|
||||
]
|
||||
|
||||
for key, value in updates:
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO ItemTable (key, value)
|
||||
VALUES (?, ?)
|
||||
""", (key, value))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('reset.updating_pair')}: {key}{Style.RESET_ALL}")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.sqlite_success')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.sqlite_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def update_system_ids(self, new_ids):
|
||||
"""Update system-level IDs"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self._update_windows_machine_guid()
|
||||
self._update_windows_machine_id()
|
||||
elif sys.platform == "darwin":
|
||||
self._update_macos_platform_uuid(new_ids)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.system_ids_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _update_windows_machine_guid(self):
|
||||
"""Update Windows MachineGuid"""
|
||||
try:
|
||||
import winreg
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Cryptography",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
new_guid = str(uuid.uuid4())
|
||||
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}")
|
||||
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}")
|
||||
raise
|
||||
|
||||
def _update_windows_machine_id(self):
|
||||
"""Update Windows MachineId in SQMClient registry"""
|
||||
try:
|
||||
import winreg
|
||||
# 1. Generate new GUID
|
||||
new_guid = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.new_machine_id')}: {new_guid}{Style.RESET_ALL}")
|
||||
|
||||
# 2. Open the registry key
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient",
|
||||
0,
|
||||
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
|
||||
)
|
||||
except FileNotFoundError:
|
||||
# If the key does not exist, create it
|
||||
key = winreg.CreateKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\SQMClient"
|
||||
)
|
||||
|
||||
# 3. Set MachineId value
|
||||
winreg.SetValueEx(key, "MachineId", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_id_updated')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_id_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
|
||||
def _update_macos_platform_uuid(self, new_ids):
|
||||
"""Update macOS Platform UUID"""
|
||||
try:
|
||||
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||
if os.path.exists(uuid_file):
|
||||
# Use sudo to execute plutil command
|
||||
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
|
||||
result = os.system(cmd)
|
||||
if result == 0:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
|
||||
else:
|
||||
raise Exception(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_macos_platform_uuid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
|
||||
def reset_machine_ids(self):
|
||||
"""Reset machine ID and backup original file"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
|
||||
|
||||
if not os.path.exists(self.db_path):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.not_found')}: {self.db_path}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
if not os.access(self.db_path, os.R_OK | os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.no_permission')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.reading')}...{Style.RESET_ALL}")
|
||||
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}")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
|
||||
# Update configuration file
|
||||
config.update(new_ids)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
|
||||
with open(self.db_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=4)
|
||||
|
||||
# Update SQLite database
|
||||
self.update_sqlite_db(new_ids)
|
||||
|
||||
# Update system IDs
|
||||
self.update_system_ids(new_ids)
|
||||
|
||||
|
||||
# Modify workbench.desktop.main.js
|
||||
workbench_path = get_workbench_cursor_path(self.translator)
|
||||
modify_workbench_js(workbench_path, self.translator)
|
||||
|
||||
# Check Cursor version and perform corresponding actions
|
||||
|
||||
greater_than_0_45 = check_cursor_version(self.translator)
|
||||
if greater_than_0_45:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0,{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
|
||||
patch_cursor_get_machine_id(self.translator)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
|
||||
for key, value in new_ids.items():
|
||||
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_error', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.run_as_admin')}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def update_machine_id_file(self, machine_id: str) -> bool:
|
||||
"""
|
||||
Update machineId file with new machine_id
|
||||
Args:
|
||||
machine_id (str): New machine ID to write
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get the machineId file path
|
||||
machine_id_path = get_cursor_machine_id_path()
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
|
||||
|
||||
# Create backup if file exists
|
||||
if os.path.exists(machine_id_path):
|
||||
backup_path = machine_id_path + ".backup"
|
||||
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}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_creation_failed', error=str(e)) if self.translator else f'Could not create backup: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Write new machine ID to file
|
||||
with open(machine_id_path, "w", encoding="utf-8") as f:
|
||||
f.write(machine_id)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.update_success') if self.translator else 'Successfully updated machineId file'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Failed to update machineId file: {str(e)}"
|
||||
if self.translator:
|
||||
error_msg = self.translator.get('reset.update_failed', error=str(e))
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
|
||||
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('reset.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
resetter = MachineIDResetter(translator) # Correctly pass translator
|
||||
resetter.reset_machine_ids()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('reset.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
@@ -1,69 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>uBlock — Your filters</title>
|
||||
|
||||
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="lib/codemirror/addon/hint/show-hint.css">
|
||||
<link rel="stylesheet" href="lib/codemirror/addon/search/matchesonscrollbar.css">
|
||||
|
||||
<link rel="stylesheet" href="css/themes/default.css">
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<link rel="stylesheet" href="css/fa-icons.css">
|
||||
<link rel="stylesheet" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" href="css/1p-filters.css">
|
||||
<link rel="stylesheet" href="css/codemirror.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
<div id="cloudWidget" class="hide" data-cloud-entry="myFiltersPane"></div>
|
||||
<p>
|
||||
<button id="userFiltersApply" class="preferred iconified" type="button" disabled><span class="fa-icon">check</span><span data-i18n="1pApplyChanges">_</span><span class="hover"></span></button>
|
||||
<button id="userFiltersRevert" class="iconified" type="button" disabled><span class="fa-icon">undo</span><span data-i18n="genericRevert">_</span><span class="hover"></span></button>
|
||||
 
|
||||
<button id="importUserFiltersFromFile" class="iconified" type="button"><span class="fa-icon">download-alt</span><span data-i18n="1pImport">_</span><span class="hover"></span></button>
|
||||
<button id="exportUserFiltersToFile" class="iconified" type="button"><span class="fa-icon">upload-alt</span><span data-i18n="1pExport">_</span><span class="hover"></span></button>
|
||||
</p>
|
||||
<p data-i18n="1pTrustWarning"></p>
|
||||
<div class="li"><label><span id="enableMyFilters" class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="1pEnableMyFiltersLabel"></span></label></div>
|
||||
<div id="trustMyFilters" class="li"><label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="1pTrustMyFiltersLabel"></span></label></div>
|
||||
</div>
|
||||
<div id="userFilters" class="codeMirrorContainer codeMirrorBreakAll cm-theme-override" spellcheck="false"></div>
|
||||
<div class="hidden">
|
||||
<input id="importFilePicker" type="file" accept="text/plain">
|
||||
</div>
|
||||
|
||||
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||
<script src="lib/codemirror/addon/comment/comment.js"></script>
|
||||
<script src="lib/codemirror/addon/display/panel.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/fold/foldcode.js"></script>
|
||||
<script src="lib/codemirror/addon/hint/show-hint.js"></script>
|
||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
||||
<script src="lib/diff/swatinem_diff.js"></script>
|
||||
<script src="lib/hsluv/hsluv-0.1.0.min.js"></script>
|
||||
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
|
||||
<script src="js/codemirror/search.js" type="module"></script>
|
||||
<script src="js/codemirror/search-thread.js"></script>
|
||||
|
||||
<script src="js/fa-icons.js" type="module"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/1p-filters.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,117 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>uBlock — Filter lists</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/themes/default.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/fa-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/3p-filters.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
|
||||
<div id="cloudWidget" class="hide" data-cloud-entry="tpFiltersPane"></div>
|
||||
<p id="actions">
|
||||
<button id="buttonApply" class="preferred disabled iconified dontshrink" type="button" data-i18n-title="3pApplyChanges"><span class="fa-icon">check</span><span data-i18n="3pApplyChanges">_</span><span class="hover"></span></button>
|
||||
<button id="buttonUpdate" class="preferred disabled iconified" type="button" data-i18n-title="3pUpdateNow"><span class="fa-icon">refresh</span><span data-i18n="3pUpdateNow">_</span><span class="hover"></span></button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="autoUpdate"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pAutoUpdatePrompt1"></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="suspendUntilListsAreLoaded"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pSuspendUntilListsAreLoaded"></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="parseCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pParseAllABPHideFiltersPrompt1"></span> <span class="fa-icon info" data-i18n-title="3pParseAllABPHideFiltersInfo">question-circle</span></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="ignoreGenericCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pIgnoreGenericCosmeticFilters"></span> <span class="fa-icon info" data-i18n-title="3pIgnoreGenericCosmeticFiltersInfo">question-circle</span></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
</div>
|
||||
|
||||
<div id="lists">
|
||||
<div class="rootstats expandable" data-key="*">
|
||||
<span class="fa-icon listExpander">angle-up</span><span id="listsOfBlockedHostsPrompt"></span>
|
||||
</div>
|
||||
<div class="searchfield"><input type="search" spellcheck="false" placeholder="" /><span class="fa-icon">search</span></div>
|
||||
<div class="listEntries"></div>
|
||||
<div class="li listEntry expandable" data-role="import">
|
||||
<span class="detailbar">
|
||||
<label><span class="fa-icon listExpander">angle-up</span><span class="listname" data-i18n="3pImport"></span></label>
|
||||
<a class="fa-icon info towiki" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">info-circle</a>
|
||||
</span>
|
||||
<textarea dir="ltr" spellcheck="false" placeholder="3pExternalListsHint"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="templates" style="display: none;">
|
||||
<div class="listEntries"></div>
|
||||
<div class="li listEntry" data-role="leaf">
|
||||
<span class="detailbar">
|
||||
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span class="listname forinput"></span>
|
||||
</span></label>
|
||||
<span class="iconbar"><!--
|
||||
--><a class="fa-icon content" href="#" type="text/plain" target="_blank" data-i18n-title="3pViewContent">eye-open</a><!--
|
||||
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
|
||||
--><span class="fa-icon remove">trash-o</span><!--
|
||||
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
|
||||
--><span class="fa-icon status unsecure" title="http">unlock-alt</span><!--
|
||||
--><span class="fa-icon status obsolete" data-i18n-title="3pExternalListObsolete">exclamation-triangle</span><!--
|
||||
--><span class="fa-icon status cache">clock-o</span><!--
|
||||
--><span class="fa-icon status updating" data-i18n-title="3pUpdating">spinner</span><!--
|
||||
--><span class="fa-icon status failed" data-i18n-title="3pNetworkError">unlink</span>
|
||||
</span>
|
||||
<span class="leafstats"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="li listEntry expandable" data-role="node">
|
||||
<span class="detailbar">
|
||||
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span class="listname forinput"></span></label>
|
||||
<span class="nodestats"></span>
|
||||
<span class="fa-icon listExpander">angle-up</span>
|
||||
<span class="iconbar"><!--
|
||||
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
|
||||
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
|
||||
--><span class="fa-icon status obsolete" data-i18n-title="3pExternalListObsolete">exclamation-triangle</span><!--
|
||||
--><span class="fa-icon status cache">clock-o</span><!--
|
||||
--><span class="fa-icon status updating" data-i18n-title="3pUpdating">spinner</span><!--
|
||||
--><span class="fa-icon status failed" data-i18n-title="3pNetworkError">unlink</span>
|
||||
</span>
|
||||
<span class="leafstats"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="li listEntry expandable" data-parent="root" data-role="node">
|
||||
<span class="detailbar">
|
||||
<label><span class="fa-icon listExpander">angle-up</span><span class="listname"></span></label>
|
||||
<span class="nodestats"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="lib/hsluv/hsluv-0.1.0.min.js"></script>
|
||||
|
||||
<script src="js/fa-icons.js" type="module"></script>
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/3p-filters.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/}
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{{project}} Copyright (C) {{year}} {{fullname}}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
{http://www.gnu.org/licenses/}.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
{http://www.gnu.org/philosophy/why-not-lgpl.html}.
|
||||