mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-26 21:50:39 +03:00
Compare commits
382 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3dff87da9 | ||
|
|
861f258ce4 | ||
|
|
ab9202fe48 | ||
|
|
d60c46bac6 | ||
|
|
4757f9777a | ||
|
|
caecbd4c8d | ||
|
|
105b5d4517 | ||
|
|
f325690e32 | ||
|
|
3de2db74b2 | ||
|
|
6153041607 | ||
|
|
6eba95c055 | ||
|
|
21535104a6 | ||
|
|
b42b4b01b9 | ||
|
|
4b9c465dd5 | ||
|
|
b98059a476 | ||
|
|
56882f0663 | ||
|
|
0852472746 | ||
|
|
d867f5cfe9 | ||
|
|
e69e500e8d | ||
|
|
2f012b9dc5 | ||
|
|
d3c6bf227b | ||
|
|
f697b71755 | ||
|
|
5863891f4b | ||
|
|
7e0da4a0cb | ||
|
|
1cdb543ea9 | ||
|
|
5dad4f35a6 | ||
|
|
34a23a69a1 | ||
|
|
3cca2e3b17 | ||
|
|
ee287b91f2 | ||
|
|
96704e9f38 | ||
|
|
f541bc40b4 | ||
|
|
a730b145a1 | ||
|
|
1e72e59985 | ||
|
|
7d355f126c | ||
|
|
abcc3a84fa | ||
|
|
d8f1cbfc3b | ||
|
|
431550e4a9 | ||
|
|
dde25cba02 | ||
|
|
0466421823 | ||
|
|
aa4177d5ec | ||
|
|
75b17bb515 | ||
|
|
80a76b507a | ||
|
|
84b77e8b13 | ||
|
|
f471450e12 | ||
|
|
9f81f94957 | ||
|
|
b423779d04 | ||
|
|
5203634f0a | ||
|
|
1a73ec0a32 | ||
|
|
92263013f2 | ||
|
|
3edb69e831 | ||
|
|
271fc818b1 | ||
|
|
61803031dc | ||
|
|
9a56bc5f7e | ||
|
|
32fea2c82b | ||
|
|
45f10a6da8 | ||
|
|
a4692d11dc | ||
|
|
43a58db339 | ||
|
|
bc55000668 | ||
|
|
84358805fc | ||
|
|
82e2625dfe | ||
|
|
d5404e8f57 | ||
|
|
fb3e532058 | ||
|
|
a7c4631ea4 | ||
|
|
5b64e54e90 | ||
|
|
f667da64b3 | ||
|
|
26a8e8da28 | ||
|
|
3862176867 | ||
|
|
5adc598661 | ||
|
|
5e6651bb32 | ||
|
|
bdc606ce2d | ||
|
|
564e421288 | ||
|
|
dce359dc33 | ||
|
|
ff79fae77b | ||
|
|
db3a2032dc | ||
|
|
e2a33d178d | ||
|
|
42d97cfa87 | ||
|
|
c42d7d5422 | ||
|
|
c7a84ca59f | ||
|
|
4746af7ce9 | ||
|
|
9f51ba8128 | ||
|
|
9aa09c436e | ||
|
|
1e3e9c99eb | ||
|
|
3f9cbc3d08 | ||
|
|
12d46d5f18 | ||
|
|
6cb3ad79af | ||
|
|
6470c65f8b | ||
|
|
491b227486 | ||
|
|
96c0cd5274 | ||
|
|
60a438e618 | ||
|
|
6a25871366 | ||
|
|
b46a58bd23 | ||
|
|
fea2b88a8e | ||
|
|
63fe39f2c1 | ||
|
|
386ffa4568 | ||
|
|
9c66725caf | ||
|
|
16b6ba95e2 | ||
|
|
3424f49a57 | ||
|
|
07bed23848 | ||
|
|
c12c52269e | ||
|
|
8d279ce972 | ||
|
|
849ec5ea8d | ||
|
|
c48c35fd09 | ||
|
|
a361d2fe6d | ||
|
|
74be8a0a77 | ||
|
|
bce205b252 | ||
|
|
8fcf3b62cf | ||
|
|
eeaee073cf | ||
|
|
2f353da381 | ||
|
|
efd1417407 | ||
|
|
2cc7cce283 | ||
|
|
4c30afcd7f | ||
|
|
bfb2f5e95a | ||
|
|
35e01edf9c | ||
|
|
df9e88131b | ||
|
|
8a4d22103e | ||
|
|
1244d86994 | ||
|
|
a2833fcbd4 | ||
|
|
b3e7c101d3 | ||
|
|
0a779063bf | ||
|
|
c96433e446 | ||
|
|
5604f5073d | ||
|
|
3cad68be36 | ||
|
|
d8799cdf2e | ||
|
|
3e5c22b816 | ||
|
|
893c3f8a52 | ||
|
|
edb74e21be | ||
|
|
be52ce98a6 | ||
|
|
163748fd96 | ||
|
|
f888b8c90c | ||
|
|
3c12561997 | ||
|
|
80aab8741f | ||
|
|
2595c4c95d | ||
|
|
f18e65f391 | ||
|
|
671307b53b | ||
|
|
f58bb70d3a | ||
|
|
5bfe653a92 | ||
|
|
a66a0e5395 | ||
|
|
ea44218a8a | ||
|
|
fdc8317380 | ||
|
|
523daea54e | ||
|
|
5a4e32f3f4 | ||
|
|
d91485ec75 | ||
|
|
51cef9c2b2 | ||
|
|
6e6180e331 | ||
|
|
e2d7c1c496 | ||
|
|
a56b978669 | ||
|
|
9793b91bc7 | ||
|
|
735dd8c1eb | ||
|
|
e5d192efcb | ||
|
|
a4d5b0e467 | ||
|
|
d7b056b339 | ||
|
|
bd152be4e8 | ||
|
|
172d9fb4bb | ||
|
|
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 |
21
.SRCINFO
Normal file
21
.SRCINFO
Normal file
@@ -0,0 +1,21 @@
|
||||
pkgbase = cursor-free-vip-git
|
||||
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
|
||||
pkgver = 1.9.04.10.g5863891
|
||||
pkgrel = 1
|
||||
url = https://github.com/yeongpin/cursor-free-vip
|
||||
arch = x86_64
|
||||
license = MIT
|
||||
license = Attribution-NonCommercial-NoDerivatives 4.0 International
|
||||
makedepends = git
|
||||
makedepends = python
|
||||
makedepends = pyinstaller
|
||||
makedepends = uv
|
||||
depends = python
|
||||
depends = cursor-bin
|
||||
provides = cursor-free-vip
|
||||
source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git
|
||||
source = https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE
|
||||
sha256sums = SKIP
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = cursor-free-vip-git
|
||||
70
.github/ISSUE_TEMPLATE/cn_bug_report.yml
vendored
Normal file
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_feature_request.yml
vendored
Normal file
75
.github/ISSUE_TEMPLATE/cn_feature_request.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: 💡 功能建议
|
||||
description: 为这个项目提出新的想法
|
||||
title: "[功能建议]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您抽出时间提出新功能建议!
|
||||
请确保填写以下所有部分。
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: 功能描述
|
||||
description: 清晰简洁地描述您想要实现的功能。
|
||||
placeholder: "我希望能够..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem-solution
|
||||
attributes:
|
||||
label: 问题和解决方案
|
||||
description: 描述您试图解决的问题,以及这个功能将如何帮助解决。
|
||||
placeholder: "目前,当我尝试..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 考虑过的替代方案
|
||||
description: 描述您考虑过的任何替代解决方案或功能。
|
||||
placeholder: "我考虑过..."
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: 优先级
|
||||
description: 这个功能对您来说有多重要?
|
||||
options:
|
||||
- 高(对我的工作流程至关重要)
|
||||
- 中(会很有帮助)
|
||||
- 低(有更好)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: 影响范围
|
||||
description: 有多少用户会从这个功能中受益?
|
||||
options:
|
||||
- 所有用户
|
||||
- 大多数用户
|
||||
- 部分用户
|
||||
- 仅我个人
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: technical-details
|
||||
attributes:
|
||||
label: 技术细节
|
||||
description: 您想分享的任何技术考虑或实现细节。
|
||||
placeholder: "这个功能可以通过..."
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: 截图
|
||||
description: 如果适用,添加截图以帮助解释您的功能建议。
|
||||
placeholder: "您可以在此处拖放截图..."
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: 行为准则
|
||||
description: 提交此问题即表示您同意遵守本项目的行为准则
|
||||
options:
|
||||
- label: 我同意遵守本项目的行为准则
|
||||
required: true
|
||||
75
.github/ISSUE_TEMPLATE/cn_question.yml
vendored
Normal file
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
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_feature_request.yml
vendored
Normal file
75
.github/ISSUE_TEMPLATE/en_feature_request.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: 💡 Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "[Feature]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to suggest a new feature!
|
||||
Please make sure to fill out all the sections below.
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
description: A clear and concise description of what you want to happen.
|
||||
placeholder: "I would like to..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem-solution
|
||||
attributes:
|
||||
label: Problem & Solution
|
||||
description: Describe the problem you're trying to solve and how this feature would help.
|
||||
placeholder: "Currently, when I try to..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: Describe any alternative solutions or features you've considered.
|
||||
placeholder: "I've considered..."
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How important is this feature to you?
|
||||
options:
|
||||
- High (Critical for my workflow)
|
||||
- Medium (Would be very helpful)
|
||||
- Low (Nice to have)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: Impact
|
||||
description: How many users would benefit from this feature?
|
||||
options:
|
||||
- All users
|
||||
- Most users
|
||||
- Some users
|
||||
- Just me
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: technical-details
|
||||
attributes:
|
||||
label: Technical Details
|
||||
description: Any technical considerations or implementation details you'd like to share.
|
||||
placeholder: "The feature could be implemented by..."
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your feature.
|
||||
placeholder: "You can drag and drop screenshots here..."
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow this project's Code of Conduct
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
75
.github/ISSUE_TEMPLATE/en_question.yml
vendored
Normal file
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
|
||||
139
.github/workflows/build.yml
vendored
139
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
version:
|
||||
description: 'Version number (e.g. 1.0.9)'
|
||||
required: true
|
||||
default: '1.0.9-dev'
|
||||
default: '1.9.02'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
path: dist/*
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
|
||||
build-macos-arm64:
|
||||
needs: create-tag
|
||||
@@ -90,14 +90,15 @@ jobs:
|
||||
- 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/*
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
|
||||
build-linux:
|
||||
build-linux-x64:
|
||||
needs: create-tag
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -119,16 +120,55 @@ jobs:
|
||||
pip install pyinstaller
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Build Linux executable
|
||||
- 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 artifact
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CursorFreeVIP_${{ env.VERSION }}_linux
|
||||
path: dist/*
|
||||
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.10-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
|
||||
@@ -155,21 +195,25 @@ jobs:
|
||||
- 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/*
|
||||
|
||||
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
|
||||
create-release:
|
||||
needs: [build-windows, build-macos-arm64, build-linux, build-macos-intel]
|
||||
needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get version
|
||||
shell: bash
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
@@ -179,27 +223,72 @@ jobs:
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Prepare release files
|
||||
- name: Calculate SHA256 checksums
|
||||
run: |
|
||||
cd artifacts
|
||||
# 重命名文件为最终的可执行文件名
|
||||
mv CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP.exe CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
mv CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
mv CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_linux
|
||||
mv CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
# 删除空目录
|
||||
rm -rf */
|
||||
ls -la
|
||||
mkdir -p checksums
|
||||
for file in artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64 \
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
do
|
||||
if [ -f "$file" ]; then
|
||||
filename=$(basename $file)
|
||||
sha256sum "$file" | cut -d ' ' -f 1 > checksums/${filename}.sha256
|
||||
echo "${filename}: $(cat checksums/${filename}.sha256)" >> checksums/all_checksums.txt
|
||||
else
|
||||
echo "Warning: File $file not found"
|
||||
fi
|
||||
done
|
||||
cat checksums/all_checksums.txt
|
||||
|
||||
- name: Extract release notes from CHANGELOG
|
||||
run: |
|
||||
version_pattern="## v${{ env.VERSION }}"
|
||||
next_version_pattern="## v"
|
||||
|
||||
# Find the start line number of the current version
|
||||
start_line=$(grep -n "$version_pattern" CHANGELOG.md | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$start_line" ]; then
|
||||
echo "Error: Version ${{ env.VERSION }} not found in CHANGELOG.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find the line number of the next version
|
||||
next_version_line=$(tail -n +$((start_line + 1)) CHANGELOG.md | grep -n "$next_version_pattern" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ -z "$next_version_line" ]; then
|
||||
# If there's no next version, get to the end of the file
|
||||
changelog_content=$(tail -n +$start_line CHANGELOG.md)
|
||||
else
|
||||
# Extract content between current version and next version
|
||||
end_line=$((start_line + next_version_line - 1))
|
||||
changelog_content=$(sed -n "${start_line},${end_line}p" CHANGELOG.md)
|
||||
fi
|
||||
|
||||
# Create release notes file
|
||||
{
|
||||
echo "$changelog_content"
|
||||
echo ""
|
||||
echo "## SHA256 Checksums"
|
||||
cat checksums/all_checksums.txt
|
||||
} > release_notes.md
|
||||
|
||||
# Display release notes for debugging
|
||||
cat release_notes.md
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
body_path: release_notes.md
|
||||
files: |
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux
|
||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||
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:
|
||||
|
||||
53
.gitignore
vendored
Normal file
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
|
||||
453
CHANGELOG.md
453
CHANGELOG.md
@@ -1,26 +1,415 @@
|
||||
# Change Log
|
||||
|
||||
## v1.9.05
|
||||
1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。
|
||||
2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。
|
||||
3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤
|
||||
4. Sync AUR new version 1.9.04 | 同步 AUR 新版本 1.9.04
|
||||
5. Fix: missing license install on pkgbuild @michaeldavis246611119 mention here | 修復 pkgbuild 中缺少授權安裝 @michaeldavis246611119 提到這裡
|
||||
6. Fix: readme table | 修復 readme 表格
|
||||
7. Fix: google-chrome package name problem, add "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 | 修復 google-chrome 包名稱問題,添加 "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616
|
||||
8. Fix: exception error log | 修復異常錯誤日誌
|
||||
9. Fix: github oauth error [Bug]: #564 | 修復 github oauth 錯誤 [Bug]: #564
|
||||
10. Fix: ChromiumOptions.arguments type error: list object has no attribute 'get' | 修復 ChromiumOptions.arguments 類型錯誤:list 對象沒有屬性 'get'
|
||||
11. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.04
|
||||
1. Add: Opera GX Support | 添加 Opera GX 支持
|
||||
2. Same as v1.9.03 | 與 v1.9.03 相同
|
||||
3. Hotfix: Some Issues | 修復一些問題
|
||||
4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.03[Skip & Merge to v1.9.04]
|
||||
1. Hotfix: Some Issues | 修復一些問題
|
||||
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.02
|
||||
1. Add: Bypass Token Limit | 添加繞過 Token 限制
|
||||
2. Add: More Browser Support | 添加更多瀏覽器支持
|
||||
3. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
4. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
|
||||
5. Add config manual browser path | 添加配置手動選擇遊覽器路徑
|
||||
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
|
||||
6. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
7. Fix: Config File Path | 修復配置文件路徑
|
||||
8. Fix: window user permission | 修復 window 用戶權限
|
||||
9. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.01
|
||||
1. Add: Bypass Token Limit | 添加繞過 Token 限制
|
||||
2. Add: More Browser Support | 添加更多瀏覽器支持
|
||||
3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
|
||||
4. Add config manual browser path | 添加配置手動選擇遊覽器路徑
|
||||
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
|
||||
## v1.8.10
|
||||
1. Add: Check User Authorized | 添加檢查用戶授權
|
||||
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
|
||||
3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統
|
||||
4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑
|
||||
5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.09
|
||||
1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查
|
||||
2. Add:Bypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5)
|
||||
3. Add: Force Update Config | 添加強制更新配置
|
||||
4. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
5. Fix: Reset break | 修復重置中斷
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.08
|
||||
1. Add: Force Update Config | 添加強制更新配置
|
||||
2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
3. Fix: Google Auth & Github Auth JWT Problem | 修復 Google Auth & Github Auth JWT 問題
|
||||
4. Fix: Totally reset import & import * raw options problem | 修復 totally reset import & import * raw 選項問題
|
||||
5. Fix: reset.file_not_found problem | 修復 reset.file_not_found 問題
|
||||
6. Outdated: Bypass Cursor Version Check | 過期:繞過 Cursor 版本檢查
|
||||
7. Document: i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx"); | 文檔:i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx");
|
||||
8. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.07
|
||||
1. Add: Bypass Cursor Version Check | 添加繞過 Cursor 版本檢查
|
||||
2. Add: Multilanguage support for bypass | 添加繞過的多語言支持
|
||||
3. MSG: Free & free trial accounts can no longer use chat with premium models on Cursor Version 0.45 or less. Please upgrade to Pro or use Cursor Version 0.46 or later. Install Cursor at https://www.cursor.com/downloads or update from within the editor.
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.06
|
||||
1. Add: Google Account Deletion Feature | 添加 Google 账号删除功能
|
||||
2. Update: Menu with new account deletion option | 更新菜单添加账号删除选项
|
||||
3. Add: Multilanguage support for account deletion | 添加账号删除功能的多语言支持
|
||||
4. Fix: Improve usage limits check and tuple index error | 修复使用限制检查和元组索引错误
|
||||
5. Fix: bug in disable cursor auto update | 修复禁用 Cursor 自动更新的错误
|
||||
6. Fix: Linux-appimage | 修复 Linux-appimage 问题
|
||||
7. Add: Support for custom Cursor installation paths on Windows | 添加 Windows 系统下自定义 Cursor 安装路径支持
|
||||
8. Add: Chrome profile selection feature | 添加 Chrome 配置文件选择功能
|
||||
9. Fix: improve account usage limit detection | 修復賬號檢測
|
||||
10. Fix: For custom Chrome Installations | 修復自定義chrome遊覽器安裝
|
||||
|
||||
## v1.8.05
|
||||
1. Fix: Linux Path Not Found | 修復linuxpath問題
|
||||
2. Add: support for detecting both 150/150 and 50/50 usage limits | 添加偵測50 或者150的使用量
|
||||
3. Improve: usage parsing and validation | 檢測使用量
|
||||
|
||||
## v1.8.04
|
||||
1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py
|
||||
2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理
|
||||
3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.03
|
||||
1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測
|
||||
2. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## 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 Failed | 修復更新 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選項
|
||||
3. Fix Cursor Options | 修復 Cursor 選項
|
||||
|
||||
|
||||
## v1.2.02
|
||||
|
||||
1. Add PBlock | 增加PBlock
|
||||
2. Remove uBlock0.chromium | 移除uBlock0.chromium
|
||||
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人機驗證問題
|
||||
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問題
|
||||
4. Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
|
||||
|
||||
|
||||
## v1.1.01
|
||||
@@ -29,15 +418,15 @@
|
||||
<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人機驗證問題
|
||||
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啟動開發者問題
|
||||
1. Hot Fix Mac Chrome Problem | 修復 Mac Chrome 問題
|
||||
2. Fix Linux Start Donet Problem | 修復 Linux 啟動開發者問題
|
||||
|
||||
|
||||
## v1.0.9
|
||||
@@ -46,15 +435,15 @@
|
||||
<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版本重置機器
|
||||
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
|
||||
4. Add Remake main.js | 重做 main.js
|
||||
|
||||
|
||||
## v1.0.8
|
||||
|
||||
1. Fix New 0.45 Version Reset Machine | 修復新0.45版本重置機器
|
||||
1. Fix New 0.45 Version Reset Machine | 修復新 0.45 版本重置機器
|
||||
2. Fix Locale Language | 修復多語言
|
||||
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
||||
|
||||
@@ -75,17 +464,17 @@
|
||||
|
||||
## v1.0.6
|
||||
|
||||
1. Add Quit Cursor Option | 增加退出Cursor選項
|
||||
2. Add Recaptcha Path Patch | 增加Recaptcha路徑修復
|
||||
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瀏覽器控制問題
|
||||
1. Fix: Mac Browser Control | 修復 Mac 瀏覽器控制問題
|
||||
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
||||
3. Add Linux Support | 增加Linux支持
|
||||
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>
|
||||
@@ -93,7 +482,7 @@
|
||||
|
||||
## v1.0.5
|
||||
|
||||
1. Remove MachineID | 移除機器碼ID
|
||||
1. Remove MachineID | 移除機器碼 ID
|
||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
||||
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
||||
@@ -104,16 +493,16 @@
|
||||
|
||||
## v1.0.4
|
||||
|
||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||
2. Fix Cloud Lame | 修復雲端慢速模式
|
||||
|
||||
|
||||
## v1.0.3
|
||||
|
||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
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支持
|
||||
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>
|
||||
@@ -125,21 +514,21 @@
|
||||
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 問題
|
||||
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模式
|
||||
8. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||
9. Fix cursor-slow mode | 修復 cursor-slow 模式
|
||||
|
||||
|
||||
## v1.0.1
|
||||
|
||||
1. Fix: Reset machine ID | 修復了重置機器ID的問題
|
||||
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的配置的問題
|
||||
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為 pro 會員的問題
|
||||
4. Fix: Real-time send Token request | 修復了 實時發送 Token 請求的問題
|
||||
5. Fix: Reset Cursor's configuration | 修復了 重置 Cursor 的配置的問題
|
||||
|
||||
|
||||
|
||||
@@ -151,9 +540,9 @@
|
||||
<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 聯繫作者
|
||||
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>
|
||||
</p>
|
||||
|
||||
34
PKGBUILD
Normal file
34
PKGBUILD
Normal file
@@ -0,0 +1,34 @@
|
||||
# Maintainer: Canmi21 <9997200@qq.com>
|
||||
# Contributor: Canmi (Canmi21)
|
||||
|
||||
pkgname=cursor-free-vip-git
|
||||
pkgver=1.9.04.10.g5863891
|
||||
pkgrel=1
|
||||
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/yeongpin/cursor-free-vip"
|
||||
license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
|
||||
depends=('python' 'cursor-bin')
|
||||
makedepends=('git' 'python' 'pyinstaller' 'uv')
|
||||
provides=('cursor-free-vip')
|
||||
source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git" "https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE")
|
||||
sha256sums=('SKIP' 'SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "$srcdir/cursor-free-vip"
|
||||
git describe --tags --always | sed 's/^v//;s/-/./g'
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "$srcdir/cursor-free-vip"
|
||||
uv venv .venv
|
||||
source .venv/bin/activate
|
||||
uv pip install -r requirements.txt
|
||||
pyinstaller --clean --noconfirm --onefile main.py --name cursor-free-vip
|
||||
}
|
||||
|
||||
package() {
|
||||
install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
|
||||
install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license"
|
||||
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
|
||||
}
|
||||
230
README.md
230
README.md
@@ -1,116 +1,276 @@
|
||||
# ➤ 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">
|
||||
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||
<a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a>
|
||||
|
||||
</p>
|
||||
<h4>Support Latest 0.45.11 Version | 支持最新0.45.11版本</h4>
|
||||
|
||||
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
||||
<a href="https://trendshift.io/repositories/13425" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13425" alt="yeongpin%2Fcursor-free-vip | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
<br>
|
||||
<a href="https://www.buymeacoffee.com/yeongpin" target="_blank">
|
||||
<img src="https://img.buymeacoffee.com/button-api/?text=buy me a coffee&emoji=☕&slug=yeongpin&button_colour=ffda33&font_colour=000000&font_family=Bree&outline_colour=000000&coffee_colour=FFDD00&latest=2" width="160" height='55' alt="Buy Me a Coffee"/>
|
||||
</a>
|
||||
|
||||
這是一個自動化工具,自動註冊(除了Google驗證碼),支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
||||
|
||||
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
|
||||
|
||||
This tool registers accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes, resets and wipes Cursor data and hardware info.
|
||||
|
||||
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 和 Linux 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pronew_2025-02-13_15-01-32.png" alt="new" width="400"/><br>
|
||||
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
|
||||
</p>
|
||||
|
||||
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||
##### If you don't have browser, you can download it from
|
||||
|
||||
##### 如果沒有Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||
##### 如果沒有瀏覽器,可以從这里下載
|
||||
|
||||
[Google Chrome](https://www.google.com/intl/en_pk/chrome/) | [Opera](https://www.opera.com/download) | [Edge](https://www.microsoft.com/en-us/edge) | [Firefox](https://www.mozilla.org/en-US/firefox/new/) | [Brave](https://www.brave.com/download/)
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
## 🔄 Change Log | 更新日志
|
||||
|
||||
[Watch Change Log | 查看更新日志](CHANGELOG.md)
|
||||
|
||||
## ✨ Features | 功能特點
|
||||
|
||||
* Automatically register Cursor membership<br>自動註冊Cursor會員<br>
|
||||
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
|
||||
|
||||
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
|
||||
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
|
||||
|
||||
* Complete Auth verification<br>完成Auth驗證<br>
|
||||
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
|
||||
|
||||
* Reset Cursor's configuration<br>重置Cursor的配置<br>
|
||||
* Support Windows macOS and Linux systems<br>支持 Windows、macOS 和 Linux 系統<br>
|
||||
|
||||
* Complete Auth verification<br>完成 Auth 驗證<br>
|
||||
|
||||
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
|
||||
|
||||
* Delete Cursor Google Account<br>删除 Cursor Google 账号<br>
|
||||
|
||||
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
|
||||
|
||||
## 💻 System Support | 系統支持
|
||||
|
||||
|Windows|x64|✅|macOS|Intel|✅|
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Windows|x86|✅|macOS|Apple Silicon|✅|
|
||||
|Linux|x64|✅|Linux|x86|✅|
|
||||
|Linux|ARM64|✅|Linux|ARM64|✅|
|
||||
| Operating System | Architecture | Supported |
|
||||
|------------------|-------------------|-----------|
|
||||
| Windows | x64, x86 | ✅ |
|
||||
| macOS | Intel, Apple Silicon | ✅ |
|
||||
| Linux | x64, x86, ARM64 | ✅ |
|
||||
|
||||
## 👀 How to use | 如何使用
|
||||
|
||||
<details open>
|
||||
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
### **Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
**Windows**
|
||||
### **Archlinux**
|
||||
|
||||
Install via [AUR](https://aur.archlinux.org/packages/cursor-free-vip-git)
|
||||
|
||||
```bash
|
||||
yay -S cursor-free-vip-git
|
||||
```
|
||||
|
||||
### **Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
### **Linux/macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
|
||||
```
|
||||
|
||||
**Windows**
|
||||
### **Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
|
||||
```
|
||||
|
||||
</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 | 注意事項
|
||||
|
||||
📝 Config | 文件配置
|
||||
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
|
||||
<details>
|
||||
<summary><b>⭐ Config | 文件配置</b></summary>
|
||||
|
||||
```
|
||||
[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
|
||||
|
||||
[WindowsPaths]
|
||||
storage_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\storage.json
|
||||
sqlite_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\state.vscdb
|
||||
machine_id_path = C:\Users\yeongpin\AppData\Roaming\Cursor\machineId
|
||||
cursor_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app
|
||||
updater_path = C:\Users\yeongpin\AppData\Local\cursor-updater
|
||||
update_yml_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app-update.yml
|
||||
product_json_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app\product.json
|
||||
|
||||
[Browser]
|
||||
default_browser = opera
|
||||
chrome_path = C:\Program Files\Google\Chrome\Application\chrome.exe
|
||||
edge_path = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
|
||||
firefox_path = C:\Program Files\Mozilla Firefox\firefox.exe
|
||||
brave_path = C:\Program Files\BraveSoftware/Brave-Browser/Application/brave.exe
|
||||
chrome_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
edge_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\msedgedriver.exe
|
||||
firefox_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\geckodriver.exe
|
||||
brave_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
opera_path = C:\Users\yeongpin\AppData\Local\Programs\Opera\opera.exe
|
||||
opera_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
|
||||
[OAuth]
|
||||
show_selection_alert = False
|
||||
timeout = 120
|
||||
max_attempts = 3
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
* 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.
|
||||
|
||||
83
block_domain.txt
Normal file
83
block_domain.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
oakon.com
|
||||
famamail.com
|
||||
2925.com
|
||||
indigobook.com
|
||||
teihu.com
|
||||
raleigh-construction.com
|
||||
pastryofistanbul.com
|
||||
linshiyouxiang.net
|
||||
Mohmal.com
|
||||
pusmail.com
|
||||
questtechsystems.com
|
||||
ikomail.com
|
||||
ofanda.com
|
||||
pusmail.com
|
||||
ikomail.com
|
||||
mailpull.com
|
||||
drewzen.com
|
||||
begemail.com
|
||||
dugmail.com
|
||||
solerbe.net
|
||||
corhash.net
|
||||
mailshou.com
|
||||
0-mail.com
|
||||
1secmail.com
|
||||
20minutemail.com
|
||||
2prong.com
|
||||
33mail.com
|
||||
anonbox.net
|
||||
anonymbox.com
|
||||
boun.cr
|
||||
burnermail.io
|
||||
byom.de
|
||||
chammy.info
|
||||
cloud-mail.top
|
||||
cool.fr.nf
|
||||
crazymailing.com
|
||||
cuvox.de
|
||||
deadaddress.com
|
||||
dispostable.com
|
||||
dudmail.com
|
||||
emailondeck.com
|
||||
fakeinbox.com
|
||||
fakemailgenerator.com
|
||||
filzmail.com
|
||||
fizmail.com
|
||||
guerrillamail.com
|
||||
harakirimail.com
|
||||
hottempmail.com
|
||||
inboxbear.com
|
||||
inboxkitten.com
|
||||
incognitomail.org
|
||||
letthemeatspam.com
|
||||
maildrop.cc
|
||||
mailinator.com
|
||||
mailnesia.com
|
||||
mailsac.com
|
||||
mailtemp.net
|
||||
mailzilla.org
|
||||
mintemail.com
|
||||
moakt.com
|
||||
my10minutemail.com
|
||||
mytrashmail.com
|
||||
nospamfor.us
|
||||
nowmymail.com
|
||||
openmailbox.org
|
||||
privacyroot.com
|
||||
sharklasers.com
|
||||
spam4.me
|
||||
spamavert.com
|
||||
spambog.com
|
||||
spamex.com
|
||||
spamfree24.org
|
||||
spaml.com
|
||||
temp-mail.org
|
||||
tempmail.net
|
||||
tempmailaddress.com
|
||||
temporaryemail.net
|
||||
throwawayemail.com
|
||||
trash-mail.com
|
||||
trashmail.com
|
||||
trbvn.com
|
||||
yopmail.com
|
||||
zippymail.info
|
||||
95
browser.py
95
browser.py
@@ -1,95 +0,0 @@
|
||||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import random
|
||||
|
||||
|
||||
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)
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
|
||||
extension_block_path = self.get_extension_block()
|
||||
co.add_extension(extension_block_path)
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
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/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
# 基本设置
|
||||
co.set_pref("credentials_enable_service", False)
|
||||
|
||||
# 随机端口
|
||||
co.auto_port()
|
||||
|
||||
# 系统特定设置
|
||||
if sys.platform == "darwin": # macOS
|
||||
co.set_argument("--disable-gpu")
|
||||
co.set_argument("--no-sandbox")
|
||||
elif sys.platform == "win32": # Windows
|
||||
co.set_argument("--disable-software-rasterizer")
|
||||
|
||||
# 设置窗口大小
|
||||
window_width = random.randint(1024, 1920)
|
||||
window_height = random.randint(768, 1080)
|
||||
co.set_argument(f"--window-size={window_width},{window_height}")
|
||||
|
||||
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, "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 quit(self):
|
||||
"""关闭浏览器"""
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
30
build.bat
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
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
Normal file → Executable file
20
build.sh
Normal file → Executable file
@@ -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
|
||||
|
||||
19
build.spec
19
build.spec
@@ -29,15 +29,24 @@ a = Analysis(
|
||||
('cursor_auth.py', '.'),
|
||||
('reset_machine_manual.py', '.'),
|
||||
('cursor_register.py', '.'),
|
||||
('browser.py', '.'),
|
||||
('control.py', '.'),
|
||||
('.env', '.')
|
||||
('new_signup.py', '.'),
|
||||
('new_tempemail.py', '.'),
|
||||
('quit_cursor.py', '.'),
|
||||
('cursor_register_manual.py', '.'),
|
||||
('oauth_auth.py', '.'),
|
||||
('utils.py', '.'),
|
||||
('.env', '.'),
|
||||
('block_domain.txt', '.')
|
||||
],
|
||||
hiddenimports=[
|
||||
'cursor_auth',
|
||||
'reset_machine_manual',
|
||||
'browser',
|
||||
'control'
|
||||
'new_signup',
|
||||
'new_tempemail',
|
||||
'quit_cursor',
|
||||
'cursor_register_manual',
|
||||
'oauth_auth',
|
||||
'utils'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
||||
174
bypass_token_limit.py
Normal file
174
bypass_token_limit.py
Normal file
@@ -0,0 +1,174 @@
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
import tempfile
|
||||
import glob
|
||||
from colorama import Fore, Style, init
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Add extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
|
||||
if system not in paths_map:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if system == "Linux":
|
||||
for base in paths_map["Linux"]["bases"]:
|
||||
main_path = os.path.join(base, paths_map["Linux"]["main"])
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
|
||||
if os.path.exists(main_path):
|
||||
return main_path
|
||||
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
|
||||
|
||||
return main_path
|
||||
|
||||
|
||||
def modify_workbench_js(file_path: str, translator=None) -> bool:
|
||||
"""
|
||||
Modify file content
|
||||
"""
|
||||
try:
|
||||
# Save original file permissions
|
||||
original_stat = os.stat(file_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
# Create temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
|
||||
# Read original content
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux/Mac 通用按钮替换模式
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
shutil.move(tmp_path, file_path)
|
||||
|
||||
# Restore original permissions
|
||||
os.chmod(file_path, original_mode)
|
||||
if os.name != "nt": # Not Windows
|
||||
os.chown(file_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
return False
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('bypass_token_limit.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
modify_workbench_js(get_workbench_cursor_path(translator), translator)
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('bypass_token_limit.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
158
bypass_version.py
Normal file
158
bypass_version.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import platform
|
||||
import configparser
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
import sys
|
||||
import traceback
|
||||
from utils import get_user_documents_path
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'INFO': 'ℹ️',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WARNING': '⚠️',
|
||||
'FILE': '📄',
|
||||
'BACKUP': '💾',
|
||||
'RESET': '🔄',
|
||||
'VERSION': '🏷️'
|
||||
}
|
||||
|
||||
def get_product_json_path(translator=None):
|
||||
"""Get Cursor product.json path"""
|
||||
system = platform.system()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
if system == "Windows":
|
||||
localappdata = os.environ.get("LOCALAPPDATA")
|
||||
if not localappdata:
|
||||
raise OSError(translator.get('bypass.localappdata_not_found') if translator else "LOCALAPPDATA environment variable not found")
|
||||
|
||||
product_json_path = os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json")
|
||||
|
||||
# Check if path exists in config
|
||||
if 'WindowsPaths' in config and 'cursor_path' in config['WindowsPaths']:
|
||||
cursor_path = config.get('WindowsPaths', 'cursor_path')
|
||||
product_json_path = os.path.join(cursor_path, "product.json")
|
||||
|
||||
elif system == "Darwin": # macOS
|
||||
product_json_path = "/Applications/Cursor.app/Contents/Resources/app/product.json"
|
||||
|
||||
elif system == "Linux":
|
||||
# Try multiple common paths
|
||||
possible_paths = [
|
||||
"/opt/Cursor/resources/app/product.json",
|
||||
"/usr/share/cursor/resources/app/product.json",
|
||||
"/usr/lib/cursor/app/product.json"
|
||||
]
|
||||
|
||||
# Add extracted AppImage paths
|
||||
extracted_usr_paths = os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app/product.json")
|
||||
if os.path.exists(extracted_usr_paths):
|
||||
possible_paths.append(extracted_usr_paths)
|
||||
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
product_json_path = path
|
||||
break
|
||||
else:
|
||||
raise OSError(translator.get('bypass.product_json_not_found') if translator else "product.json not found in common Linux paths")
|
||||
|
||||
else:
|
||||
raise OSError(translator.get('bypass.unsupported_os', system=system) if translator else f"Unsupported operating system: {system}")
|
||||
|
||||
if not os.path.exists(product_json_path):
|
||||
raise OSError(translator.get('bypass.file_not_found', path=product_json_path) if translator else f"File not found: {product_json_path}")
|
||||
|
||||
return product_json_path
|
||||
|
||||
def compare_versions(version1, version2):
|
||||
"""Compare two version strings"""
|
||||
v1_parts = [int(x) for x in version1.split('.')]
|
||||
v2_parts = [int(x) for x in version2.split('.')]
|
||||
|
||||
for i in range(max(len(v1_parts), len(v2_parts))):
|
||||
v1 = v1_parts[i] if i < len(v1_parts) else 0
|
||||
v2 = v2_parts[i] if i < len(v2_parts) else 0
|
||||
if v1 < v2:
|
||||
return -1
|
||||
elif v1 > v2:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
def bypass_version(translator=None):
|
||||
"""Bypass Cursor version check by modifying product.json"""
|
||||
try:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('bypass.starting') if translator else 'Starting Cursor version bypass...'}{Style.RESET_ALL}")
|
||||
|
||||
# Get product.json path
|
||||
product_json_path = get_product_json_path(translator)
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {translator.get('bypass.found_product_json', path=product_json_path) if translator else f'Found product.json: {product_json_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check file permissions
|
||||
if not os.access(product_json_path, os.W_OK):
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.no_write_permission', path=product_json_path) if translator else f'No write permission for file: {product_json_path}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Read product.json
|
||||
try:
|
||||
with open(product_json_path, "r", encoding="utf-8") as f:
|
||||
product_data = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.read_failed', error=str(e)) if translator else f'Failed to read product.json: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Get current version
|
||||
current_version = product_data.get("version", "0.0.0")
|
||||
print(f"{Fore.CYAN}{EMOJI['VERSION']} {translator.get('bypass.current_version', version=current_version) if translator else f'Current version: {current_version}'}{Style.RESET_ALL}")
|
||||
|
||||
# Check if version needs to be modified
|
||||
if compare_versions(current_version, "0.46.0") < 0:
|
||||
# Create backup
|
||||
timestamp = time.strftime("%Y%m%d%H%M%S")
|
||||
backup_path = f"{product_json_path}.{timestamp}"
|
||||
shutil.copy2(product_json_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['BACKUP']} {translator.get('bypass.backup_created', path=backup_path) if translator else f'Backup created: {backup_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Modify version
|
||||
new_version = "0.48.7"
|
||||
product_data["version"] = new_version
|
||||
|
||||
# Save modified product.json
|
||||
try:
|
||||
with open(product_json_path, "w", encoding="utf-8") as f:
|
||||
json.dump(product_data, f, indent=2)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('bypass.version_updated', old=current_version, new=new_version) if translator else f'Version updated from {current_version} to {new_version}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.write_failed', error=str(e)) if translator else f'Failed to write product.json: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.no_update_needed', version=current_version) if translator else f'No update needed. Current version {current_version} is already >= 0.46.0'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.bypass_failed', error=str(e)) if translator else f'Version bypass failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.stack_trace') if translator else 'Stack trace'}: {traceback.format_exc()}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function"""
|
||||
return bypass_version(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
214
check_user_authorized.py
Normal file
214
check_user_authorized.py
Normal file
@@ -0,0 +1,214 @@
|
||||
import os
|
||||
import requests
|
||||
import time
|
||||
import hashlib
|
||||
import base64
|
||||
import struct
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"KEY": "🔑",
|
||||
"CHECK": "🔍"
|
||||
}
|
||||
|
||||
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
|
||||
"""Generate a SHA-256 hash of input + salt and return as hex"""
|
||||
hash_obj = hashlib.sha256()
|
||||
hash_obj.update((input_str + salt).encode('utf-8'))
|
||||
return hash_obj.hexdigest()
|
||||
|
||||
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
|
||||
"""Obfuscate bytes using the algorithm from utils.js"""
|
||||
t = 165
|
||||
for r in range(len(byte_array)):
|
||||
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
|
||||
t = byte_array[r]
|
||||
return byte_array
|
||||
|
||||
def generate_cursor_checksum(token: str, translator=None) -> str:
|
||||
"""Generate Cursor checksum from token using the algorithm"""
|
||||
try:
|
||||
# Clean the token
|
||||
clean_token = token.strip()
|
||||
|
||||
# Generate machineId and macMachineId
|
||||
machine_id = generate_hashed64_hex(clean_token, 'machineId')
|
||||
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
|
||||
|
||||
# Get timestamp and convert to byte array
|
||||
timestamp = int(time.time() * 1000) // 1000000
|
||||
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
|
||||
|
||||
# Obfuscate bytes and encode as base64
|
||||
obfuscated_bytes = obfuscate_bytes(byte_array)
|
||||
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
|
||||
|
||||
# Combine final checksum
|
||||
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
|
||||
return ""
|
||||
|
||||
def check_user_authorized(token: str, translator=None) -> bool:
|
||||
"""
|
||||
Check if the user is authorized with the given token
|
||||
|
||||
Args:
|
||||
token (str): The authorization token
|
||||
translator: Optional translator for internationalization
|
||||
|
||||
Returns:
|
||||
bool: True if authorized, False otherwise
|
||||
"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
|
||||
|
||||
# Clean the token
|
||||
if token and '%3A%3A' in token:
|
||||
token = token.split('%3A%3A')[1]
|
||||
elif token and '::' in token:
|
||||
token = token.split('::')[1]
|
||||
|
||||
# Remove any whitespace
|
||||
token = token.strip()
|
||||
|
||||
if not token or len(token) < 10: # Add a basic validation for token length
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
|
||||
|
||||
# Try to get usage info using the DashboardService API
|
||||
try:
|
||||
# Generate checksum
|
||||
checksum = generate_cursor_checksum(token, translator)
|
||||
|
||||
# Create request headers
|
||||
headers = {
|
||||
'accept-encoding': 'gzip',
|
||||
'authorization': f'Bearer {token}',
|
||||
'connect-protocol-version': '1',
|
||||
'content-type': 'application/proto',
|
||||
'user-agent': 'connect-es/1.6.1',
|
||||
'x-cursor-checksum': checksum,
|
||||
'x-cursor-client-version': '0.48.7',
|
||||
'x-cursor-timezone': 'Asia/Shanghai',
|
||||
'x-ghost-mode': 'false',
|
||||
'Host': 'api2.cursor.sh'
|
||||
}
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
|
||||
|
||||
# Make the request - this endpoint doesn't need a request body
|
||||
usage_response = requests.post(
|
||||
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
|
||||
headers=headers,
|
||||
data=b'', # Empty body
|
||||
timeout=10
|
||||
)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
if usage_response.status_code == 200:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
|
||||
return True
|
||||
elif usage_response.status_code == 401 or usage_response.status_code == 403:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
"""Run function to be called from main.py"""
|
||||
try:
|
||||
# Ask user if they want to get token from database or input manually
|
||||
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
|
||||
|
||||
token = None
|
||||
|
||||
# If user chooses database or default
|
||||
if not choice or choice == 'd':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Import functions from cursor_acc_info.py
|
||||
from cursor_acc_info import get_token
|
||||
|
||||
# Get token using the get_token function
|
||||
token = get_token()
|
||||
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
|
||||
except ImportError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# If token not found in database or user chooses manual input
|
||||
if not token:
|
||||
# Try to get token from environment
|
||||
token = os.environ.get('CURSOR_TOKEN')
|
||||
|
||||
# If not in environment, ask user to input
|
||||
if not token:
|
||||
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
|
||||
|
||||
# Check authorization
|
||||
is_authorized = check_user_authorized(token, translator)
|
||||
|
||||
if is_authorized:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
|
||||
|
||||
return is_authorized
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to check user authorization"""
|
||||
return run(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
367
config.py
Normal file
367
config.py
Normal file
@@ -0,0 +1,367 @@
|
||||
import os
|
||||
import sys
|
||||
import configparser
|
||||
from colorama import Fore, Style
|
||||
from utils import get_user_documents_path, get_linux_cursor_path, get_default_driver_path, get_default_browser_path
|
||||
import shutil
|
||||
import datetime
|
||||
|
||||
EMOJI = {
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"ERROR": "❌",
|
||||
"SUCCESS": "✅",
|
||||
"ADMIN": "🔒",
|
||||
"ARROW": "➡️",
|
||||
"USER": "👤",
|
||||
"KEY": "🔑",
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
# global config cache
|
||||
_config_cache = None
|
||||
|
||||
def setup_config(translator=None):
|
||||
"""Setup configuration file and return config object"""
|
||||
try:
|
||||
# get documents path
|
||||
docs_path = get_user_documents_path()
|
||||
if not docs_path or not os.path.exists(docs_path):
|
||||
# if documents path not found, use current directory
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.documents_path_not_found', fallback='Documents path not found, using current directory') if translator else 'Documents path not found, using current directory'}{Style.RESET_ALL}")
|
||||
docs_path = os.path.abspath('.')
|
||||
|
||||
# normalize path
|
||||
config_dir = os.path.normpath(os.path.join(docs_path, ".cursor-free-vip"))
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
|
||||
# create config directory, only print message when directory not exists
|
||||
dir_exists = os.path.exists(config_dir)
|
||||
try:
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not dir_exists: # only print message when directory not exists
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_dir_created', path=config_dir) if translator else f'Config directory created: {config_dir}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
# if cannot create directory, use temporary directory
|
||||
import tempfile
|
||||
temp_dir = os.path.normpath(os.path.join(tempfile.gettempdir(), ".cursor-free-vip"))
|
||||
temp_exists = os.path.exists(temp_dir)
|
||||
config_dir = temp_dir
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not temp_exists: # only print message when temporary directory not exists
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.using_temp_dir', path=config_dir, error=str(e)) if translator else f'Using temporary directory due to error: {config_dir} (Error: {str(e)})'}{Style.RESET_ALL}")
|
||||
|
||||
# create config object
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# Default configuration
|
||||
default_config = {
|
||||
'Browser': {
|
||||
'default_browser': 'chrome',
|
||||
'chrome_path': get_default_browser_path('chrome'),
|
||||
'chrome_driver_path': get_default_driver_path('chrome'),
|
||||
'edge_path': get_default_browser_path('edge'),
|
||||
'edge_driver_path': get_default_driver_path('edge'),
|
||||
'firefox_path': get_default_browser_path('firefox'),
|
||||
'firefox_driver_path': get_default_driver_path('firefox'),
|
||||
'brave_path': get_default_browser_path('brave'),
|
||||
'brave_driver_path': get_default_driver_path('brave'),
|
||||
'opera_path': get_default_browser_path('opera'),
|
||||
'opera_driver_path': get_default_driver_path('opera'),
|
||||
'operagx_path': get_default_browser_path('operagx'),
|
||||
'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
|
||||
},
|
||||
'Turnstile': {
|
||||
'handle_turnstile_time': '2',
|
||||
'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_force_update': 'False',
|
||||
'enabled_account_info': 'True'
|
||||
},
|
||||
'OAuth': {
|
||||
'show_selection_alert': False, # 默认不显示选择提示弹窗
|
||||
'timeout': 120,
|
||||
'max_attempts': 3
|
||||
},
|
||||
'Token': {
|
||||
'refresh_server': 'https://token.cursorpro.com.cn',
|
||||
'enable_refresh': True
|
||||
}
|
||||
}
|
||||
|
||||
# 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"),
|
||||
'product_json_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json")
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
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",
|
||||
'product_json_path': "/Applications/Cursor.app/Contents/Resources/app/product.json"
|
||||
}
|
||||
# Create storage directory
|
||||
os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True)
|
||||
|
||||
elif sys.platform == "linux":
|
||||
# Get the actual user's home directory, handling both sudo and normal cases
|
||||
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 "",
|
||||
'product_json_path': os.path.join(cursor_dir, "resources/app/product.json") 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 force_update_config(translator=None):
|
||||
"""
|
||||
Force update configuration file with latest defaults if update check is enabled.
|
||||
Args:
|
||||
translator: Translator instance
|
||||
Returns:
|
||||
ConfigParser instance or None if failed
|
||||
"""
|
||||
try:
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
current_time = datetime.datetime.now()
|
||||
|
||||
# If the config file exists, check if forced update is enabled
|
||||
if os.path.exists(config_file):
|
||||
# First, read the existing configuration
|
||||
existing_config = configparser.ConfigParser()
|
||||
existing_config.read(config_file, encoding='utf-8')
|
||||
# Check if "enabled_update_check" is True
|
||||
update_enabled = True # Default to True if not set
|
||||
if existing_config.has_section('Utils') and existing_config.has_option('Utils', 'enabled_force_update'):
|
||||
update_enabled = existing_config.get('Utils', 'enabled_force_update').strip().lower() in ('true', 'yes', '1', 'on')
|
||||
|
||||
if update_enabled:
|
||||
try:
|
||||
# Create a backup
|
||||
backup_file = f"{config_file}.bak.{current_time.strftime('%Y%m%d_%H%M%S')}"
|
||||
shutil.copy2(config_file, backup_file)
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.backup_created', path=backup_file) if translator else f'Backup created: {backup_file}'}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_enabled') if translator else 'Config file force update enabled'}{Style.RESET_ALL}")
|
||||
# Delete the original config file (forced update)
|
||||
os.remove(config_file)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_removed') if translator else 'Config file removed for forced update'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.backup_failed', error=str(e)) if translator else f'Failed to backup config: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_disabled', fallback='Config file force update disabled by configuration. Keeping existing config file.') if translator else 'Config file force update disabled by configuration. Keeping existing config file.'}{Style.RESET_ALL}")
|
||||
|
||||
# Generate a new (or updated) configuration if needed
|
||||
return setup_config(translator)
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.force_update_failed', error=str(e)) if translator else f'Force update config failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
global _config_cache
|
||||
if _config_cache is None:
|
||||
_config_cache = setup_config(translator)
|
||||
return _config_cache
|
||||
240
control.py
240
control.py
@@ -1,240 +0,0 @@
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji常量
|
||||
EMOJI = {
|
||||
'MAIL': '📧',
|
||||
'REFRESH': '🔄',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'INFO': 'ℹ️',
|
||||
'CODE': '📱'
|
||||
}
|
||||
|
||||
class BrowserControl:
|
||||
def __init__(self, browser, translator=None):
|
||||
self.browser = browser
|
||||
self.translator = translator # 保存translator
|
||||
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}{EMOJI['SUCCESS']} {self.translator.get('control.create_new_tab_success')}{Style.RESET_ALL}")
|
||||
return new_browser
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def switch_to_tab(self, browser):
|
||||
"""切换到指定浏览器窗口"""
|
||||
try:
|
||||
self.browser = browser
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.switch_tab_success')}{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.switch_tab_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_current_tab(self):
|
||||
"""获取当前标签页"""
|
||||
return self.browser
|
||||
|
||||
def wait_for_page_load(self, seconds=2):
|
||||
"""等待页面加载"""
|
||||
time.sleep(seconds)
|
||||
|
||||
def navigate_to(self, url):
|
||||
"""导航到指定URL"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.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}{EMOJI['ERROR']} {self.translator.get('control.browser_error', error=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}{EMOJI['SUCCESS']} {self.translator.get('control.found_verification_code')}: {verification_code}{Style.RESET_ALL}")
|
||||
return verification_code
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.no_valid_verification_code')}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_verification_code_error', 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}{EMOJI['ERROR']} {self.translator.get('control.verification_code_format_error')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.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}{EMOJI['SUCCESS']} {self.translator.get('control.verification_code_filled')}{Style.RESET_ALL}")
|
||||
|
||||
# 等待页面加载和登录完成
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.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}{EMOJI['ERROR']} {self.translator.get('control.get_token_failed')}...{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}{EMOJI['INFO']} {self.translator.get('control.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}{EMOJI['INFO']} {self.translator.get('control.account_usage_limit')}: {total_usage}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_account_usage_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
|
||||
# 切换回邮箱页面
|
||||
self.switch_to_tab(email_tab)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.fill_verification_code_failed', error=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}{EMOJI['INFO']} {self.translator.get('control.find_turnstile_verification_box')}...{Style.RESET_ALL}")
|
||||
verify_checkbox.click()
|
||||
time.sleep(2) # 等待验证完成
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.clicked_turnstile_verification_box')}{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.check_and_click_turnstile_failed', error=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}{EMOJI['INFO']} {self.translator.get('control.get_cursor_session_token')}...{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}{EMOJI['SUCCESS']} {self.translator.get('control.get_cursor_session_token_success')}: {token}{Style.RESET_ALL}")
|
||||
return token
|
||||
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.get_cursor_session_token_failed', attempts=attempts, retry_interval=retry_interval)}...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.reach_max_attempts', max_attempts=max_attempts)}{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_cookie_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.will_retry_in', retry_interval=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}{EMOJI['SUCCESS']} {self.translator.get('control.token_saved_to_file')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.save_token_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
552
cursor_acc_info.py
Normal file
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()
|
||||
@@ -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': '🔄',
|
||||
@@ -21,25 +22,68 @@ EMOJI = {
|
||||
class CursorAuth:
|
||||
def __init__(self, translator=None):
|
||||
self.translator = translator
|
||||
# 判断操作系统
|
||||
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"
|
||||
)
|
||||
|
||||
# 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()
|
||||
@@ -54,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} {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("""
|
||||
@@ -110,5 +157,3 @@ class CursorAuth:
|
||||
if conn:
|
||||
conn.close()
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")
|
||||
|
||||
|
||||
|
||||
@@ -2,18 +2,17 @@ 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
|
||||
from get_user_token import get_token_from_cookie
|
||||
|
||||
os.environ["PYTHONVERBOSE"] = "0"
|
||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
@@ -33,9 +32,8 @@ EMOJI = {
|
||||
class CursorRegistration:
|
||||
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"
|
||||
@@ -45,41 +43,51 @@ 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 Email"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# 使用 new_tempemail 创建临时邮箱,传入 translator
|
||||
# Create a temporary email using new_tempemail, passing translator
|
||||
from new_tempemail import NewTempEmail
|
||||
self.temp_email = NewTempEmail(self.translator) # 传入 translator
|
||||
self.temp_email = NewTempEmail(self.translator) # Pass translator
|
||||
|
||||
# 创建临时邮箱
|
||||
# Create a temporary email
|
||||
email_address = self.temp_email.create_email()
|
||||
if not email_address:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 保存邮箱地址和浏览器实例
|
||||
# Save email address
|
||||
self.email_address = email_address
|
||||
self.email_tab = self.temp_email # 传递 NewTempEmail 实例而不是 page
|
||||
self.controller = BrowserControl(self.temp_email.page, self.translator)
|
||||
self.email_tab = self.temp_email # Pass NewTempEmail instance
|
||||
|
||||
return True
|
||||
|
||||
@@ -93,10 +101,10 @@ class CursorRegistration:
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# 直接使用 new_signup.py 进行注册
|
||||
# Directly use new_signup.py to sign up
|
||||
from new_signup import main as new_signup_main
|
||||
|
||||
# 执行新的注册流程,传入 translator
|
||||
# Execute the new registration process, passing translator
|
||||
result, browser_tab = new_signup_main(
|
||||
email=self.email_address,
|
||||
password=self.password,
|
||||
@@ -108,11 +116,11 @@ class CursorRegistration:
|
||||
)
|
||||
|
||||
if result:
|
||||
# 使用返回的浏览器实例获取账户信息
|
||||
self.signup_tab = browser_tab # 保存浏览器实例
|
||||
# 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()
|
||||
@@ -127,7 +135,7 @@ class CursorRegistration:
|
||||
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()
|
||||
@@ -135,7 +143,7 @@ class CursorRegistration:
|
||||
pass
|
||||
|
||||
def _get_account_info(self):
|
||||
"""获取账户信息和 Token"""
|
||||
"""Get Account Information and Token"""
|
||||
try:
|
||||
self.signup_tab.get(self.settings_url)
|
||||
time.sleep(2)
|
||||
@@ -150,6 +158,7 @@ class CursorRegistration:
|
||||
if usage_ele:
|
||||
total_usage = usage_ele.text.split("/")[-1].strip()
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.total_usage', usage=total_usage)}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
|
||||
max_attempts = 30
|
||||
retry_interval = 2
|
||||
@@ -160,7 +169,7 @@ class CursorRegistration:
|
||||
cookies = self.signup_tab.cookies()
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
token = get_token_from_cookie(cookie["value"], self.translator)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
|
||||
self._save_account_info(token, total_usage)
|
||||
return True
|
||||
@@ -186,22 +195,22 @@ class CursorRegistration:
|
||||
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}")
|
||||
|
||||
# 重置机器ID
|
||||
# Reset machine ID
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
|
||||
resetter = MachineIDResetter(self.translator) # 创建实例时传入translator
|
||||
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法
|
||||
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")
|
||||
@@ -218,7 +227,7 @@ class CursorRegistration:
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""启动注册流程"""
|
||||
"""Start Registration Process"""
|
||||
try:
|
||||
if self.setup_email():
|
||||
if self.register_cursor():
|
||||
@@ -226,7 +235,7 @@ class CursorRegistration:
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
# 关闭邮箱标签页
|
||||
# Close email tab
|
||||
if hasattr(self, 'temp_email'):
|
||||
try:
|
||||
self.temp_email.close()
|
||||
@@ -234,7 +243,7 @@ class CursorRegistration:
|
||||
pass
|
||||
|
||||
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
|
||||
"""更新Cursor的认证信息的便捷函数"""
|
||||
"""Update Cursor Auth Info"""
|
||||
auth_manager = CursorAuth(translator=self.translator)
|
||||
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||
|
||||
|
||||
5
cursor_register_github.py
Normal file
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
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)
|
||||
@@ -2,18 +2,17 @@ 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
|
||||
from get_user_token import get_token_from_cookie
|
||||
|
||||
os.environ["PYTHONVERBOSE"] = "0"
|
||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
@@ -33,9 +32,8 @@ EMOJI = {
|
||||
class CursorRegistration:
|
||||
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.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||
@@ -44,32 +42,44 @@ class CursorRegistration:
|
||||
self.signup_tab = None
|
||||
self.email_tab = None
|
||||
|
||||
# 账号信息
|
||||
# Generate 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 Email"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else '请输入邮箱地址:'}")
|
||||
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" + f"{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@@ -77,9 +87,9 @@ class CursorRegistration:
|
||||
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 '请输入验证码:'}{Style.RESET_ALL}")
|
||||
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:
|
||||
@@ -93,31 +103,31 @@ class CursorRegistration:
|
||||
return None
|
||||
|
||||
def register_cursor(self):
|
||||
"""注册 Cursor"""
|
||||
"""Register Cursor"""
|
||||
browser_tab = None
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||
|
||||
# 直接使用 new_signup.py 进行注册
|
||||
# Use new_signup.py directly for registration
|
||||
from new_signup import main as new_signup_main
|
||||
|
||||
# 执行新的注册流程,传入 translator
|
||||
# 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, # 不需要邮箱标签页
|
||||
controller=self, # 传入 self 而不是 self.controller
|
||||
email_tab=None, # No email tab needed
|
||||
controller=self, # Pass self instead of self.controller
|
||||
translator=self.translator
|
||||
)
|
||||
|
||||
if result:
|
||||
# 使用返回的浏览器实例获取账户信息
|
||||
self.signup_tab = browser_tab # 保存浏览器实例
|
||||
# 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()
|
||||
@@ -132,7 +142,7 @@ class CursorRegistration:
|
||||
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()
|
||||
@@ -140,7 +150,7 @@ class CursorRegistration:
|
||||
pass
|
||||
|
||||
def _get_account_info(self):
|
||||
"""获取账户信息和 Token"""
|
||||
"""Get Account Information and Token"""
|
||||
try:
|
||||
self.signup_tab.get(self.settings_url)
|
||||
time.sleep(2)
|
||||
@@ -155,6 +165,7 @@ class CursorRegistration:
|
||||
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
|
||||
@@ -165,7 +176,7 @@ class CursorRegistration:
|
||||
cookies = self.signup_tab.cookies()
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
token = get_token_from_cookie(cookie["value"], self.translator)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
|
||||
self._save_account_info(token, total_usage)
|
||||
return True
|
||||
@@ -191,22 +202,22 @@ class CursorRegistration:
|
||||
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}")
|
||||
|
||||
# 重置机器ID
|
||||
# Reset machine ID
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
|
||||
resetter = MachineIDResetter(self.translator) # 创建实例时传入translator
|
||||
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法
|
||||
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")
|
||||
@@ -223,7 +234,7 @@ class CursorRegistration:
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""启动注册流程"""
|
||||
"""Start Registration Process"""
|
||||
try:
|
||||
if self.setup_email():
|
||||
if self.register_cursor():
|
||||
@@ -231,7 +242,7 @@ class CursorRegistration:
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
# 关闭邮箱标签页
|
||||
# Close email tab
|
||||
if hasattr(self, 'temp_email'):
|
||||
try:
|
||||
self.temp_email.close()
|
||||
@@ -239,7 +250,7 @@ class CursorRegistration:
|
||||
pass
|
||||
|
||||
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
|
||||
"""更新Cursor的认证信息的便捷函数"""
|
||||
"""Convenient function to update Cursor authentication information"""
|
||||
auth_manager = CursorAuth(translator=self.translator)
|
||||
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||
|
||||
|
||||
386
delete_cursor_google.py
Normal file
386
delete_cursor_google.py
Normal file
@@ -0,0 +1,386 @@
|
||||
from oauth_auth import OAuthHandler
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
import sys
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'DELETE': '🗑️',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
class CursorGoogleAccountDeleter(OAuthHandler):
|
||||
def __init__(self, translator=None):
|
||||
super().__init__(translator, auth_type='google')
|
||||
|
||||
def delete_google_account(self):
|
||||
"""Delete Cursor account using Google OAuth"""
|
||||
try:
|
||||
# Setup browser and select profile
|
||||
if not self.setup_browser():
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.starting_process') if self.translator else 'Starting account deletion process...'}{Style.RESET_ALL}")
|
||||
|
||||
# Navigate to Cursor auth page - using the same URL as in registration
|
||||
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||
time.sleep(2)
|
||||
|
||||
# Click Google auth button using same selectors as in registration
|
||||
selectors = [
|
||||
"//a[contains(@href,'GoogleOAuth')]",
|
||||
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
|
||||
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
|
||||
]
|
||||
|
||||
auth_btn = None
|
||||
for selector in selectors:
|
||||
try:
|
||||
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
|
||||
if auth_btn:
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not auth_btn:
|
||||
raise Exception(self.translator.get('account_delete.google_button_not_found') if self.translator else "Google login button not found")
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.logging_in') if self.translator else 'Logging in with Google...'}{Style.RESET_ALL}")
|
||||
auth_btn.click()
|
||||
|
||||
# Wait for authentication to complete using a more robust method
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('account_delete.waiting_for_auth', fallback='Waiting for Google authentication...')}{Style.RESET_ALL}")
|
||||
|
||||
# Dynamic wait for authentication
|
||||
max_wait_time = 120 # Increase maximum wait time to 120 seconds
|
||||
start_time = time.time()
|
||||
check_interval = 3 # Check every 3 seconds
|
||||
google_account_alert_shown = False # Track if we've shown the alert already
|
||||
|
||||
while time.time() - start_time < max_wait_time:
|
||||
current_url = self.browser.url
|
||||
|
||||
# If we're already on the settings or dashboard page, we're successful
|
||||
if "/dashboard" in current_url or "/settings" in current_url or "cursor.com" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.login_successful') if self.translator else 'Login successful'}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
# If we're on Google accounts page or accounts.google.com, wait for user selection
|
||||
if "accounts.google.com" in current_url:
|
||||
# Only show the alert once to avoid spamming
|
||||
if not google_account_alert_shown:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.select_google_account', fallback='Please select your Google account...')}{Style.RESET_ALL}")
|
||||
# Alert to indicate user action needed
|
||||
try:
|
||||
self.browser.run_js("""
|
||||
alert('Please select your Google account to continue with Cursor authentication');
|
||||
""")
|
||||
google_account_alert_shown = True # Mark that we've shown the alert
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# Sleep before checking again
|
||||
time.sleep(check_interval)
|
||||
else:
|
||||
# If the loop completed without breaking, it means we hit the timeout
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.auth_timeout', fallback='Authentication timeout, continuing anyway...')}{Style.RESET_ALL}")
|
||||
|
||||
# Check current URL to determine next steps
|
||||
current_url = self.browser.url
|
||||
|
||||
# If we're already on the settings page, no need to navigate
|
||||
if "/settings" in current_url and "cursor.com" in current_url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.already_on_settings', fallback='Already on settings page')}{Style.RESET_ALL}")
|
||||
# If we're on the dashboard or any Cursor page but not settings, navigate to settings
|
||||
elif "cursor.com" in current_url or "authenticator.cursor.sh" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.navigating_to_settings', fallback='Navigating to settings page...')}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
# If we're still on Google auth or somewhere else, try directly going to settings
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.login_redirect_failed', fallback='Login redirection failed, trying direct navigation...')}{Style.RESET_ALL}")
|
||||
self.browser.get("https://www.cursor.com/settings")
|
||||
|
||||
# Wait for the settings page to load
|
||||
time.sleep(3) # Reduced from 5 seconds
|
||||
|
||||
# First look for the email element to confirm we're logged in
|
||||
try:
|
||||
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||
if email_element:
|
||||
email = email_element.text
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.found_email', email=email, fallback=f'Found email: {email}')}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.email_not_found', error=str(e), fallback=f'Email not found: {str(e)}')}{Style.RESET_ALL}")
|
||||
|
||||
# Click on "Advanced" tab or dropdown - keep only the successful approach
|
||||
advanced_found = False
|
||||
|
||||
# Direct JavaScript querySelector approach that worked according to logs
|
||||
try:
|
||||
advanced_element_js = self.browser.run_js("""
|
||||
// Try to find the Advanced dropdown using querySelector with the exact classes
|
||||
let advancedElement = document.querySelector('div.mb-0.flex.cursor-pointer.items-center.text-xs:not([style*="display: none"])');
|
||||
|
||||
// If not found, try a more general approach
|
||||
if (!advancedElement) {
|
||||
const allDivs = document.querySelectorAll('div');
|
||||
for (const div of allDivs) {
|
||||
if (div.textContent.includes('Advanced') &&
|
||||
div.className.includes('mb-0') &&
|
||||
div.className.includes('flex') &&
|
||||
div.className.includes('cursor-pointer')) {
|
||||
advancedElement = div;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Click the element if found
|
||||
if (advancedElement) {
|
||||
advancedElement.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
""")
|
||||
|
||||
if advanced_element_js:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.advanced_tab_clicked', fallback='Found and clicked Advanced using direct JavaScript selector')}{Style.RESET_ALL}")
|
||||
advanced_found = True
|
||||
time.sleep(1) # Reduced from 2 seconds
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.advanced_tab_error', error=str(e), fallback='JavaScript querySelector approach failed: {str(e)}')}{Style.RESET_ALL}")
|
||||
|
||||
if not advanced_found:
|
||||
# Fallback to direct URL navigation which is faster and more reliable
|
||||
try:
|
||||
self.browser.get("https://www.cursor.com/settings?tab=advanced")
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('account_delete.direct_advanced_navigation', fallback='Trying direct navigation to advanced tab')}{Style.RESET_ALL}")
|
||||
advanced_found = True
|
||||
except:
|
||||
raise Exception(self.translator.get('account_delete.advanced_tab_not_found') if self.translator else "Advanced option not found after multiple attempts")
|
||||
|
||||
# Wait for dropdown/tab content to load
|
||||
time.sleep(2) # Reduced from 4 seconds
|
||||
|
||||
# Find and click the "Delete Account" button
|
||||
delete_button_found = False
|
||||
|
||||
# Simplified approach for delete button based on what worked
|
||||
delete_button_selectors = [
|
||||
'xpath://button[contains(., "Delete Account")]',
|
||||
'xpath://button[text()="Delete Account"]',
|
||||
'xpath://div[contains(text(), "Delete Account")]',
|
||||
'xpath://button[contains(text(), "Delete") and contains(text(), "Account")]'
|
||||
]
|
||||
|
||||
for selector in delete_button_selectors:
|
||||
try:
|
||||
delete_button = self.browser.ele(selector, timeout=2)
|
||||
if delete_button:
|
||||
delete_button.click()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.delete_button_clicked') if self.translator else 'Clicked on Delete Account button'}{Style.RESET_ALL}")
|
||||
delete_button_found = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not delete_button_found:
|
||||
raise Exception(self.translator.get('account_delete.delete_button_not_found') if self.translator else "Delete Account button not found")
|
||||
|
||||
# Wait for confirmation dialog to appear
|
||||
time.sleep(2)
|
||||
|
||||
# Check if we need to input "Delete" at all - some modals might not require it
|
||||
input_required = True
|
||||
try:
|
||||
# Try detecting if the DELETE button is already enabled
|
||||
delete_button_enabled = self.browser.run_js("""
|
||||
const buttons = Array.from(document.querySelectorAll('button'));
|
||||
const deleteButtons = buttons.filter(btn =>
|
||||
btn.textContent.trim() === 'DELETE' ||
|
||||
btn.textContent.trim() === 'Delete'
|
||||
);
|
||||
|
||||
if (deleteButtons.length > 0) {
|
||||
return !deleteButtons.some(btn => btn.disabled);
|
||||
}
|
||||
return false;
|
||||
""")
|
||||
|
||||
if delete_button_enabled:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} DELETE button appears to be enabled already. Input may not be required.{Style.RESET_ALL}")
|
||||
input_required = False
|
||||
except:
|
||||
pass
|
||||
|
||||
# Type "Delete" in the confirmation input - only if required
|
||||
delete_input_found = False
|
||||
|
||||
if input_required:
|
||||
# Try common selectors for the input field
|
||||
delete_input_selectors = [
|
||||
'xpath://input[@placeholder="Delete"]',
|
||||
'xpath://div[contains(@class, "modal")]//input',
|
||||
'xpath://input',
|
||||
'css:input'
|
||||
]
|
||||
|
||||
for selector in delete_input_selectors:
|
||||
try:
|
||||
delete_input = self.browser.ele(selector, timeout=3)
|
||||
if delete_input:
|
||||
delete_input.clear()
|
||||
delete_input.input("Delete")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete', fallback='Typed \"Delete\" in confirmation box')}{Style.RESET_ALL}")
|
||||
delete_input_found = True
|
||||
time.sleep(2)
|
||||
break
|
||||
except:
|
||||
# Try direct JavaScript input as fallback
|
||||
try:
|
||||
self.browser.run_js(r"""
|
||||
arguments[0].value = "Delete";
|
||||
const event = new Event('input', { bubbles: true });
|
||||
arguments[0].dispatchEvent(event);
|
||||
const changeEvent = new Event('change', { bubbles: true });
|
||||
arguments[0].dispatchEvent(changeEvent);
|
||||
""", delete_input)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
|
||||
delete_input_found = True
|
||||
time.sleep(2)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not delete_input_found:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.delete_input_not_found', fallback='Delete confirmation input not found, continuing anyway')}{Style.RESET_ALL}")
|
||||
time.sleep(2)
|
||||
|
||||
# Wait before clicking the final DELETE button
|
||||
time.sleep(2)
|
||||
|
||||
# Click on the final DELETE button
|
||||
confirm_button_found = False
|
||||
|
||||
# Use JavaScript approach for the DELETE button
|
||||
try:
|
||||
delete_button_js = self.browser.run_js("""
|
||||
// Try to find the DELETE button by exact text content
|
||||
const buttons = Array.from(document.querySelectorAll('button'));
|
||||
const deleteButton = buttons.find(btn =>
|
||||
btn.textContent.trim() === 'DELETE' ||
|
||||
btn.textContent.trim() === 'Delete'
|
||||
);
|
||||
|
||||
if (deleteButton) {
|
||||
console.log("Found DELETE button with JavaScript");
|
||||
deleteButton.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not found by text, try to find right-most button in the modal
|
||||
const modalButtons = Array.from(document.querySelectorAll('.relative button, [role="dialog"] button, .modal button, [aria-modal="true"] button'));
|
||||
|
||||
if (modalButtons.length > 1) {
|
||||
modalButtons.sort((a, b) => {
|
||||
const rectA = a.getBoundingClientRect();
|
||||
const rectB = b.getBoundingClientRect();
|
||||
return rectB.right - rectA.right;
|
||||
});
|
||||
|
||||
console.log("Clicking right-most button in modal");
|
||||
modalButtons[0].click();
|
||||
return true;
|
||||
} else if (modalButtons.length === 1) {
|
||||
console.log("Clicking single button found in modal");
|
||||
modalButtons[0].click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
""")
|
||||
|
||||
if delete_button_js:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Clicked DELETE button')}{Style.RESET_ALL}")
|
||||
confirm_button_found = True
|
||||
except:
|
||||
pass
|
||||
|
||||
if not confirm_button_found:
|
||||
# Fallback to simple selectors
|
||||
delete_button_selectors = [
|
||||
'xpath://button[text()="DELETE"]',
|
||||
'xpath://div[contains(@class, "modal")]//button[last()]'
|
||||
]
|
||||
|
||||
for selector in delete_button_selectors:
|
||||
try:
|
||||
delete_button = self.browser.ele(selector, timeout=2)
|
||||
if delete_button:
|
||||
delete_button.click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Account deleted successfully!')}{Style.RESET_ALL}")
|
||||
confirm_button_found = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not confirm_button_found:
|
||||
raise Exception(self.translator.get('account_delete.confirm_button_not_found') if self.translator else "Confirm button not found")
|
||||
|
||||
# Wait a moment to see the confirmation
|
||||
time.sleep(2)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('account_delete.error', error=str(e)) if self.translator else f'Error during account deletion: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
# Clean up browser
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to handle Google account deletion"""
|
||||
print(f"\n{Fore.CYAN}{EMOJI['START']} {translator.get('account_delete.title') if translator else 'Cursor Google Account Deletion Tool'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 50}{Style.RESET_ALL}")
|
||||
|
||||
deleter = CursorGoogleAccountDeleter(translator)
|
||||
|
||||
try:
|
||||
# Ask for confirmation
|
||||
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('account_delete.warning') if translator else 'WARNING: This will permanently delete your Cursor account. This action cannot be undone.'}{Style.RESET_ALL}")
|
||||
confirm = input(f"{Fore.RED} {translator.get('account_delete.confirm_prompt') if translator else 'Are you sure you want to proceed? (y/N): '}{Style.RESET_ALL}").lower()
|
||||
|
||||
if confirm != 'y':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.cancelled') if translator else 'Account deletion cancelled.'}{Style.RESET_ALL}")
|
||||
return
|
||||
|
||||
success = deleter.delete_google_account()
|
||||
|
||||
if success:
|
||||
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('account_delete.success') if translator else 'Your Cursor account has been successfully deleted!'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.failed') if translator else 'Account deletion process failed or was cancelled.'}{Style.RESET_ALL}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.interrupted') if translator else 'Account deletion process interrupted by user.'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
|
||||
finally:
|
||||
print(f"{Fore.YELLOW}{'─' * 50}{Style.RESET_ALL}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
268
disable_auto_update.py
Normal file
268
disable_auto_update.py
Normal file
@@ -0,0 +1,268 @@
|
||||
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"))
|
||||
self.product_json_path = config.get('WindowsPaths', 'product_json_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"))
|
||||
elif self.system == "Darwin":
|
||||
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
|
||||
self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml")
|
||||
self.product_json_path = config.get('MacPaths', 'product_json_path', fallback="/Applications/Cursor.app/Contents/Resources/app/product.json")
|
||||
elif self.system == "Linux":
|
||||
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
|
||||
self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml"))
|
||||
self.product_json_path = config.get('LinuxPaths', 'product_json_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/product.json"))
|
||||
else:
|
||||
# If configuration loading fails, use default paths
|
||||
self.updater_paths = {
|
||||
"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)
|
||||
|
||||
self.product_json_paths = {
|
||||
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"),
|
||||
"Darwin": "/Applications/Cursor.app/Contents/Resources/app/product.json",
|
||||
"Linux": os.path.expanduser("~/.config/cursor/resources/app/product.json")
|
||||
}
|
||||
self.product_json_path = self.product_json_paths.get(self.system)
|
||||
|
||||
def _remove_update_url(self):
|
||||
"""Remove update URL"""
|
||||
try:
|
||||
original_stat = os.stat(self.product_json_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
|
||||
with open(self.product_json_path, "r", encoding="utf-8") as product_json_file:
|
||||
content = product_json_file.read()
|
||||
|
||||
patterns = {
|
||||
r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
|
||||
r"https://api2.cursor.sh/updates": r"",
|
||||
r"http://cursorapi.com/updates": r"",
|
||||
}
|
||||
|
||||
for pattern, replacement in patterns.items():
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(self.product_json_path, self.product_json_path + ".old")
|
||||
shutil.move(tmp_path, self.product_json_path)
|
||||
|
||||
os.chmod(self.product_json_path, original_mode)
|
||||
if os.name != "nt":
|
||||
os.chown(self.product_json_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
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):
|
||||
try:
|
||||
if os.path.isdir(updater_path):
|
||||
shutil.rmtree(updater_path)
|
||||
else:
|
||||
os.remove(updater_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.directory_locked', path=updater_path) if self.translator else f'更新程序目录已被锁定,跳过删除: {updater_path}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
|
||||
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):
|
||||
try:
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('')
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已被锁定,跳过清空'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
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 阻止文件
|
||||
try:
|
||||
os.makedirs(os.path.dirname(updater_path), exist_ok=True)
|
||||
open(updater_path, 'w').close()
|
||||
|
||||
# 设置 updater_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{updater_path}"')
|
||||
else:
|
||||
os.chmod(updater_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.block_file_locked') if self.translator else '阻止文件已被锁定,跳过创建'}{Style.RESET_ALL}")
|
||||
|
||||
# 检查 update_yml_path
|
||||
update_yml_path = self.update_yml_path
|
||||
if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)):
|
||||
try:
|
||||
# 创建 update_yml_path 阻止文件
|
||||
with open(update_yml_path, 'w') as f:
|
||||
f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
|
||||
|
||||
# 设置 update_yml_path 为只读
|
||||
if self.system == "Windows":
|
||||
os.system(f'attrib +r "{update_yml_path}"')
|
||||
else:
|
||||
os.chmod(update_yml_path, 0o444) # 设置为只读
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_already_locked') if self.translator else '更新配置文件已被锁定,跳过修改'}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
|
||||
return True # 返回 True 以继续执行后续步骤
|
||||
|
||||
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. Remove update URL from product.json
|
||||
if not self._remove_update_url():
|
||||
return False
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||
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)
|
||||
112
get_user_token.py
Normal file
112
get_user_token.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from colorama import Fore, Style
|
||||
import os
|
||||
from config import get_config
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'OAUTH': '🔑',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
def refresh_token(token, translator=None):
|
||||
"""Refresh the token using the Chinese server API
|
||||
|
||||
Args:
|
||||
token (str): The full WorkosCursorSessionToken cookie value
|
||||
translator: Optional translator object
|
||||
|
||||
Returns:
|
||||
str: The refreshed access token or original token if refresh fails
|
||||
"""
|
||||
try:
|
||||
config = get_config(translator)
|
||||
# Get refresh_server URL from config or use default
|
||||
refresh_server = config.get('Token', 'refresh_server', fallback='https://token.cursorpro.com.cn')
|
||||
|
||||
# Ensure the token is URL encoded properly
|
||||
if '%3A%3A' not in token and '::' in token:
|
||||
# Replace :: with URL encoded version if needed
|
||||
token = token.replace('::', '%3A%3A')
|
||||
|
||||
# Make the request to the refresh server
|
||||
url = f"{refresh_server}/reftoken?token={token}"
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('token.refreshing') if translator else 'Refreshing token...'}{Style.RESET_ALL}")
|
||||
|
||||
response = requests.get(url, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') == 0 and data.get('msg') == "获取成功":
|
||||
access_token = data.get('data', {}).get('accessToken')
|
||||
days_left = data.get('data', {}).get('days_left', 0)
|
||||
expire_time = data.get('data', {}).get('expire_time', 'Unknown')
|
||||
|
||||
if access_token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('token.refresh_success', days=days_left, expire=expire_time) if translator else f'Token refreshed successfully! Valid for {days_left} days (expires: {expire_time})'}{Style.RESET_ALL}")
|
||||
return access_token
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('token.no_access_token') if translator else 'No access token in response'}{Style.RESET_ALL}")
|
||||
else:
|
||||
error_msg = data.get('msg', 'Unknown error')
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.refresh_failed', error=error_msg) if translator else f'Token refresh failed: {error_msg}'}{Style.RESET_ALL}")
|
||||
except json.JSONDecodeError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.invalid_response') if translator else 'Invalid JSON response from refresh server'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.server_error', status=response.status_code) if translator else f'Refresh server error: HTTP {response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.request_timeout') if translator else 'Request to refresh server timed out'}{Style.RESET_ALL}")
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.connection_error') if translator else 'Connection error to refresh server'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.unexpected_error', error=str(e)) if translator else f'Unexpected error during token refresh: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Return original token if refresh fails
|
||||
return token.split('%3A%3A')[-1] if '%3A%3A' in token else token.split('::')[-1] if '::' in token else token
|
||||
|
||||
def get_token_from_cookie(cookie_value, translator=None):
|
||||
"""Extract and process token from cookie value
|
||||
|
||||
Args:
|
||||
cookie_value (str): The WorkosCursorSessionToken cookie value
|
||||
translator: Optional translator object
|
||||
|
||||
Returns:
|
||||
str: The processed token
|
||||
"""
|
||||
try:
|
||||
# Try to refresh the token with the API first
|
||||
refreshed_token = refresh_token(cookie_value, translator)
|
||||
|
||||
# If refresh succeeded and returned a different token, use it
|
||||
if refreshed_token and refreshed_token != cookie_value:
|
||||
return refreshed_token
|
||||
|
||||
# If refresh failed or returned same token, use traditional extraction method
|
||||
if '%3A%3A' in cookie_value:
|
||||
return cookie_value.split('%3A%3A')[-1]
|
||||
elif '::' in cookie_value:
|
||||
return cookie_value.split('::')[-1]
|
||||
else:
|
||||
return cookie_value
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.extraction_error', error=str(e)) if translator else f'Error extracting token: {str(e)}'}{Style.RESET_ALL}")
|
||||
# Fall back to original behavior
|
||||
if '%3A%3A' in cookie_value:
|
||||
return cookie_value.split('%3A%3A')[-1]
|
||||
elif '::' in cookie_value:
|
||||
return cookie_value.split('::')[-1]
|
||||
else:
|
||||
return cookie_value
|
||||
701
github_cursor_register.py
Normal file
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/new_2025-02-27_10-42-44.png
Normal file
BIN
images/new_2025-02-27_10-42-44.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
BIN
images/new_2025-03-19_00-19-09.png
Normal file
BIN
images/new_2025-03-19_00-19-09.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
BIN
images/new_2025-03-22_19-53-10.png
Normal file
BIN
images/new_2025-03-22_19-53-10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
BIN
images/paypal.png
Normal file
BIN
images/paypal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
images/pro_2025-04-05_18-47-56.png
Normal file
BIN
images/pro_2025-04-05_18-47-56.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
BIN
images/provi-code.jpg
Normal file
BIN
images/provi-code.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
414
locales/bg.json
Normal file
414
locales/bg.json
Normal file
@@ -0,0 +1,414 @@
|
||||
{
|
||||
"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": "Очаквайте скоро",
|
||||
"fixed_soon": "Ще бъде поправено скоро",
|
||||
"contribute": "Принос към проекта",
|
||||
"config": "Покажи конфигурацията",
|
||||
"delete_google_account": "Изтрий Google акаунта на Cursor",
|
||||
"continue_prompt": "Продължи? (y/N): ",
|
||||
"operation_cancelled_by_user": "Операцията е отменена от потребителя",
|
||||
"exiting": "Излизане ......",
|
||||
"bypass_version_check": "Пропусни проверката на версията на Cursor",
|
||||
"check_user_authorized": "Провери оторизацията на потребителя",
|
||||
"select_chrome_profile": "Избери Chrome профил"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"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 файлове беше завършено"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Избор на Chrome Профил",
|
||||
"select_profile": "Изберете Chrome профил за използване:",
|
||||
"profile_list": "Налични профили:",
|
||||
"default_profile": "Профил по Подразбиране",
|
||||
"profile": "Профил {number}",
|
||||
"no_profiles": "Не са намерени Chrome профили",
|
||||
"error_loading": "Грешка при зареждане на Chrome профили: {error}",
|
||||
"profile_selected": "Избран профил: {profile}",
|
||||
"invalid_selection": "Невалиден избор. Моля, опитайте отново",
|
||||
"warning_chrome_close": "Предупреждение: Това ще затвори всички работещи Chrome процеси"
|
||||
}
|
||||
}
|
||||
411
locales/de.json
Normal file
411
locales/de.json
Normal file
@@ -0,0 +1,411 @@
|
||||
{
|
||||
"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",
|
||||
"select_chrome_profile": "Chrome-Profil Auswählen",
|
||||
"input_choice": "Bitte geben Sie Ihre Auswahl ein ({choices})",
|
||||
"invalid_choice": "Ungültige Auswahl. Bitte eine Nummer von {choices} eingeben",
|
||||
"program_terminated": "Programm wurde vom Benutzer beendet",
|
||||
"error_occurred": "Ein Fehler ist aufgetreten: {error}. Bitte erneut versuchen",
|
||||
"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",
|
||||
"fixed_soon": "Bald Behoben",
|
||||
"contribute": "Zum Projekt Beitragen",
|
||||
"config": "Konfiguration Anzeigen",
|
||||
"delete_google_account": "Cursor Google-Konto Löschen",
|
||||
"continue_prompt": "Fortfahren? (y/N): ",
|
||||
"operation_cancelled_by_user": "Vorgang vom Benutzer abgebrochen",
|
||||
"exiting": "Wird beendet ……",
|
||||
"bypass_version_check": "Cursor Versionsprüfung Überspringen",
|
||||
"check_user_authorized": "Benutzerautorisierung Prüfen"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Englisch",
|
||||
"zh_cn": "Vereinfachtes Chinesisch",
|
||||
"zh_tw": "Traditionelles Chinesisch",
|
||||
"vi": "Vietnamesisch",
|
||||
"nl": "Niederländisch",
|
||||
"de": "Deutsch",
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ru": "Russisch",
|
||||
"es": "Spanisch",
|
||||
"tr": "Türkisch",
|
||||
"bg": "Bulgarisch"
|
||||
},
|
||||
"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",
|
||||
"no_write_permission": "Keine Schreibberechtigung: {path}",
|
||||
"path_not_found": "Pfad Nicht Gefunden: {path}",
|
||||
"modify_file_failed": "Datei Ändern Fehlgeschlagen: {error}",
|
||||
"windows_machine_id_updated": "Windows Maschinen-ID Erfolgreich Aktualisiert",
|
||||
"update_windows_machine_id_failed": "Windows Maschinen-ID Aktualisierung Fehlgeschlagen: {error}",
|
||||
"update_windows_machine_guid_failed": "Windows Maschinen-GUID Aktualisierung Fehlgeschlagen: {error}",
|
||||
"file_not_found": "Datei Nicht Gefunden: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registrierungstool",
|
||||
"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"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome-Profil Auswahl",
|
||||
"select_profile": "Wählen Sie ein Chrome-Profil zum Verwenden:",
|
||||
"profile_list": "Verfügbare Profile:",
|
||||
"default_profile": "Standard-Profil",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Keine Chrome-Profile gefunden",
|
||||
"error_loading": "Fehler beim Laden der Chrome-Profile: {error}",
|
||||
"profile_selected": "Ausgewähltes Profil: {profile}",
|
||||
"invalid_selection": "Ungültige Auswahl. Bitte versuchen Sie es erneut",
|
||||
"warning_chrome_close": "Warnung: Dies wird alle laufenden Chrome-Prozesse beenden"
|
||||
}
|
||||
}
|
||||
632
locales/en.json
632
locales/en.json
@@ -2,21 +2,51 @@
|
||||
"menu": {
|
||||
"title": "Available Options",
|
||||
"exit": "Exit Program",
|
||||
"reset": "Reset Machine Manual",
|
||||
"register": "Register Cursor",
|
||||
"register_manual": "Register Cursor With Manual Email",
|
||||
"quit": "Quit Cursor",
|
||||
"select_language": "Select Language",
|
||||
"input_choice": "Enter your choice ({choices})",
|
||||
"invalid_choice": "Invalid choice. Please try again",
|
||||
"program_terminated": "Program terminated by user",
|
||||
"error_occurred": "An error occurred: {error}",
|
||||
"press_enter": "Press Enter to Exit"
|
||||
"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",
|
||||
"select_chrome_profile": "Select Chrome Profile",
|
||||
"input_choice": "Please enter your choice ({choices})",
|
||||
"invalid_choice": "Invalid selection. Please enter a number from {choices}",
|
||||
"program_terminated": "Program was terminated by user",
|
||||
"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",
|
||||
"delete_google_account": "Delete Cursor Google Account",
|
||||
"continue_prompt": "Continue? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operation cancelled by user",
|
||||
"exiting": "Exiting ……",
|
||||
"bypass_version_check": "Bypass Cursor Version Check",
|
||||
"check_user_authorized": "Check User Authorized",
|
||||
"bypass_token_limit": "Bypass Token Limit"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文"
|
||||
"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",
|
||||
@@ -63,25 +93,52 @@
|
||||
"patch_completed": "Patching getMachineId Completed",
|
||||
"patch_failed": "Patching getMachineId Failed: {error}",
|
||||
"version_check_passed": "Cursor Version Check Passed",
|
||||
"file_modified": "File Modified"
|
||||
"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}",
|
||||
"file_not_found": "File Not Found: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registration Tool",
|
||||
"start": "Starting Registration Process",
|
||||
"handling_turnstile": "Handling Turnstile",
|
||||
"retry_verification": "Retry Verification",
|
||||
"detect_turnstile": "Detect Turnstile",
|
||||
"verification_success": "Verification Success",
|
||||
"starting_browser": "Starting Browser",
|
||||
"form_success": "Form Success",
|
||||
"browser_started": "Browser Started",
|
||||
"waiting_for_second_verification": "Waiting for Second Verification",
|
||||
"waiting_for_verification_code": "Waiting for Verification Code",
|
||||
"password_success": "Password Set Successfully",
|
||||
"password_error": "Password Set Failed: {error}",
|
||||
"waiting_for_page_load": "Waiting for Page Load",
|
||||
"first_verification_passed": "First Verification Passed",
|
||||
"mailbox": "Successfully Entered Mailbox",
|
||||
"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",
|
||||
@@ -126,7 +183,31 @@
|
||||
"register_process_error": "Register Process Error: {error}",
|
||||
"setting_password": "Setting Password",
|
||||
"manual_code_input": "Manual Code Input",
|
||||
"manual_email_input": "Manual Email 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.",
|
||||
"browser_path_invalid": "{browser} path is invalid, using default path",
|
||||
"using_browser": "Using {browser} browser: {path}",
|
||||
"using_browser_profile": "Using {browser} profile from: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "Make sure {browser} is properly installed",
|
||||
"try_install_browser": "Try installing the browser with your package manager",
|
||||
"tracking_processes": "Tracking {count} {browser} processes",
|
||||
"no_new_processes_detected": "No new {browser} processes detected to track",
|
||||
"could_not_track_processes": "Could not track {browser} processes: {error}"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Auth Manager",
|
||||
@@ -144,7 +225,10 @@
|
||||
"database_connection_closed": "Database Connection Closed",
|
||||
"database_updated_successfully": "Database Updated Successfully",
|
||||
"connected_to_database": "Connected to Database",
|
||||
"updating_pair": "Updating Key-Value Pair"
|
||||
"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",
|
||||
@@ -181,7 +265,7 @@
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Starting Browser",
|
||||
"visiting_site": "Visiting smailpro.com",
|
||||
"visiting_site": "Visiting mail domains",
|
||||
"create_success": "Email Created Successfully",
|
||||
"create_failed": "Failed to Create Email",
|
||||
"create_error": "Email Creation Error: {error}",
|
||||
@@ -195,6 +279,490 @@
|
||||
"verification_code_found": "Verification Code Found",
|
||||
"verification_code_not_found": "Verification Code Not Found",
|
||||
"verification_code_error": "Verification Code Error: {error}",
|
||||
"address": "Email Address"
|
||||
"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}",
|
||||
"directory_locked": "Directory is locked: {path}",
|
||||
"yml_locked": "update.yml file is locked",
|
||||
"block_file_locked": "block file is locked",
|
||||
"yml_already_locked": "update.yml file is already locked",
|
||||
"block_file_already_locked": "block file is already locked",
|
||||
"block_file_locked_error": "Block file locked error: {error}",
|
||||
"yml_locked_error": "update.yml file locked error: {error}",
|
||||
"block_file_already_locked_error": "Block file already locked error: {error}",
|
||||
"yml_already_locked_error": "update.yml file already locked error: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Checking for updates...",
|
||||
"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.",
|
||||
"navigating_to_settings": "Navigating to settings page...",
|
||||
"already_on_settings": "Already on settings page",
|
||||
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
|
||||
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
|
||||
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Error finding Advanced tab: {error}",
|
||||
"advanced_tab_clicked": "Clicked on Advanced tab",
|
||||
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
|
||||
"delete_button_not_found": "Delete Account button not found after multiple attempts",
|
||||
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Error finding Delete button: {error}",
|
||||
"delete_button_clicked": "Clicked on Delete Account button",
|
||||
"found_danger_zone": "Found Danger Zone section",
|
||||
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
|
||||
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Error finding Delete input: {error}",
|
||||
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "GitHub + Cursor AI Registration Automation",
|
||||
"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",
|
||||
"token_not_found": "Token not found"
|
||||
},
|
||||
"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}",
|
||||
"backup_created": "Backup created: {path}",
|
||||
"config_removed": "Config file removed for forced update",
|
||||
"backup_failed": "Failed to backup config: {error}",
|
||||
"force_update_failed": "Force update config failed: {error}",
|
||||
"config_force_update_disabled": "Config file force update disabled , skipping forced update",
|
||||
"config_force_update_enabled": "Config file force update enabled , performing forced update",
|
||||
"documents_path_not_found": "Documents path not found, using current directory",
|
||||
"config_dir_created": "Config directory created: {path}",
|
||||
"using_temp_dir": "Using temporary directory due to error: {path} (Error: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Authentication button not found",
|
||||
"authentication_failed": "Authentication failed: {error}",
|
||||
"found_cookies": "Found {count} cookies",
|
||||
"token_extraction_error": "Token extraction error: {error}",
|
||||
"authentication_successful": "Authentication successful - Email: {email}",
|
||||
"missing_authentication_data": "Missing authentication data: {data}",
|
||||
"failed_to_delete_account": "Failed to delete account: {error}",
|
||||
"invalid_authentication_type": "Invalid authentication type",
|
||||
"auth_update_success": "Auth update success",
|
||||
"browser_closed": "Browser closed",
|
||||
"auth_update_failed": "Auth update failed",
|
||||
"google_start": "Google start",
|
||||
"github_start": "Github start",
|
||||
"usage_count": "Usage count: {usage}",
|
||||
"account_has_reached_maximum_usage": "Account has reached maximum usage, {deleting}",
|
||||
"starting_new_authentication_process": "Starting new authentication process...",
|
||||
"failed_to_delete_expired_account": "Failed to delete expired account",
|
||||
"could_not_check_usage_count": "Could not check usage count: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"could_not_find_email": "Could not find email: {error}",
|
||||
"could_not_find_usage_count": "Could not find usage count: {error}",
|
||||
"already_on_settings_page": "Already on settings page!",
|
||||
"failed_to_extract_auth_info": "Failed to extract auth info: {error}",
|
||||
"no_chrome_profiles_found": "No Chrome profiles found, using Default",
|
||||
"found_default_chrome_profile": "Found Default Chrome profile",
|
||||
"using_first_available_chrome_profile": "Using first available Chrome profile: {profile}",
|
||||
"error_finding_chrome_profile": "Error finding Chrome profile, using Default: {error}",
|
||||
"initializing_browser_setup": "Initializing browser setup...",
|
||||
"detected_platform": "Detected platform: {platform}",
|
||||
"running_as_root_warning": "Running as root is not recommended for browser automation",
|
||||
"consider_running_without_sudo": "Consider running the script without sudo",
|
||||
"no_compatible_browser_found": "No compatible browser found. Please install Google Chrome or Chromium.",
|
||||
"supported_browsers": "Supported browsers for {platform}",
|
||||
"using_browser_profile": "Using browser profile: {profile}",
|
||||
"starting_browser": "Starting browser at: {path}",
|
||||
"browser_setup_completed": "Browser setup completed successfully",
|
||||
"browser_setup_failed": "Browser setup failed: {error}",
|
||||
"try_running_without_sudo_admin": "Try running without sudo/administrator privileges",
|
||||
"redirecting_to_authenticator_cursor_sh": "Redirecting to authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Starting Google authentication...",
|
||||
"starting_github_authentication": "Starting GitHub authentication...",
|
||||
"waiting_for_authentication": "Waiting for authentication...",
|
||||
"page_changed_checking_auth": "Page changed, checking auth...",
|
||||
"status_check_error": "Status check error: {error}",
|
||||
"authentication_timeout": "Authentication timeout",
|
||||
"account_is_still_valid": "Account is still valid (Usage: {usage})",
|
||||
"starting_re_authentication_process": "Starting re-authentication process...",
|
||||
"starting_new_google_authentication": "Starting new Google authentication...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Failed to delete account or re-authenticate: {error}",
|
||||
"navigating_to_authentication_page": "Navigating to authentication page...",
|
||||
"please_select_your_google_account_to_continue": "Please select your Google account to continue...",
|
||||
"found_browser_data_directory": "Found browser data directory: {path}",
|
||||
"authentication_successful_getting_account_info": "Authentication successful, getting account info...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}",
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
|
||||
"user_data_dir_not_found": "{browser} user data directory not found at {path}, will try Chrome instead",
|
||||
"error_getting_user_data_directory": "Error getting user data directory: {error}",
|
||||
"warning_browser_close": "Warning: This will close all running {browser} processes",
|
||||
"killing_browser_processes": "Killing {browser} processes...",
|
||||
"profile_selection_error": "Error during profile selection: {error}",
|
||||
"using_configured_browser_path": "Using configured {browser} path: {path}",
|
||||
"browser_not_found_trying_chrome": "Could not find {browser}, trying Chrome instead",
|
||||
"found_chrome_at": "Found Chrome at: {path}",
|
||||
"found_browser_user_data_dir": "Found {browser} user data directory: {path}"
|
||||
},
|
||||
"browser_profile": {
|
||||
"title": "Browser Profile Selection",
|
||||
"select_profile": "Select {browser} profile to use:",
|
||||
"profile_list": "Available {browser} profiles:",
|
||||
"default_profile": "Default profile",
|
||||
"profile": "Profile {number}",
|
||||
"no_profiles": "No {browser} profiles found",
|
||||
"error_loading": "Error loading {browser} profiles: {error}",
|
||||
"profile_selected": "Selected profile: {profile}",
|
||||
"invalid_selection": "Invalid selection. Please try again."
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google Account Deletion Tool",
|
||||
"warning": "WARNING: This will permanently delete your Cursor account. This action cannot be undone.",
|
||||
"cancelled": "Account deletion cancelled.",
|
||||
"starting_process": "Starting account deletion process...",
|
||||
"google_button_not_found": "Google login button not found",
|
||||
"logging_in": "Logging in with Google...",
|
||||
"waiting_for_auth": "Waiting for Google authentication...",
|
||||
"login_successful": "Login successful",
|
||||
"unexpected_page": "Unexpected page after login: {url}",
|
||||
"trying_settings": "Trying to navigate to settings page...",
|
||||
"select_google_account": "Please select your Google account...",
|
||||
"auth_timeout": "Authentication timeout, continuing anyway...",
|
||||
"navigating_to_settings": "Navigating to settings page...",
|
||||
"already_on_settings": "Already on settings page",
|
||||
"login_redirect_failed": "Login redirection failed, trying direct navigation...",
|
||||
"advanced_tab_not_found": "Advanced tab not found after multiple attempts",
|
||||
"advanced_tab_retry": "Advanced tab not found, attempt {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Error finding Advanced tab: {error}",
|
||||
"advanced_tab_clicked": "Clicked on Advanced tab",
|
||||
"direct_advanced_navigation": "Trying direct navigation to advanced tab",
|
||||
"delete_button_not_found": "Delete Account button not found after multiple attempts",
|
||||
"delete_button_retry": "Delete button not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Error finding Delete button: {error}",
|
||||
"delete_button_clicked": "Clicked on Delete Account button",
|
||||
"found_danger_zone": "Found Danger Zone section",
|
||||
"delete_input_not_found": "Delete confirmation input not found after multiple attempts",
|
||||
"delete_input_retry": "Delete input not found, attempt {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Error finding Delete input: {error}",
|
||||
"delete_input_not_found_continuing": "Delete confirmation input not found, trying to continue anyway",
|
||||
"typed_delete": "Typed \"Delete\" in confirmation box",
|
||||
"confirm_button_not_found": "Confirm button not found after multiple attempts",
|
||||
"confirm_button_retry": "Confirm button not found, attempt {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "Error finding Confirm button: {error}",
|
||||
"account_deleted": "Account deleted successfully!",
|
||||
"error": "Error during account deletion: {error}",
|
||||
"success": "Your Cursor account has been successfully deleted!",
|
||||
"failed": "Account deletion process failed or was cancelled.",
|
||||
"interrupted": "Account deletion process interrupted by user.",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"found_email": "Found email: {email}",
|
||||
"email_not_found": "Email not found: {error}",
|
||||
"confirm_prompt": "Are you sure you want to proceed? (y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "Starting Cursor version bypass...",
|
||||
"found_product_json": "Found product.json: {path}",
|
||||
"no_write_permission": "No write permission for file: {path}",
|
||||
"read_failed": "Failed to read product.json: {error}",
|
||||
"current_version": "Current version: {version}",
|
||||
"backup_created": "Backup created: {path}",
|
||||
"version_updated": "Version updated from {old} to {new}",
|
||||
"write_failed": "Failed to write product.json: {error}",
|
||||
"no_update_needed": "No update needed. Current version {version} is already >= 0.46.0",
|
||||
"bypass_failed": "Version bypass failed: {error}",
|
||||
"stack_trace": "Stack trace",
|
||||
"localappdata_not_found": "LOCALAPPDATA environment variable not found",
|
||||
"product_json_not_found": "product.json not found in common Linux paths",
|
||||
"unsupported_os": "Unsupported operating system: {system}",
|
||||
"file_not_found": "File not found: {path}",
|
||||
"title": "Cursor Version Bypass Tool",
|
||||
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
|
||||
"menu_option": "Bypass Cursor Version Check"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "Checking authorization...",
|
||||
"token_source": "Get token from database or input manually? (d/m, default: d)",
|
||||
"getting_token_from_db": "Getting token from database...",
|
||||
"token_found_in_db": "Token found in database",
|
||||
"token_not_found_in_db": "Token not found in database",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
|
||||
"error_getting_token_from_db": "Error getting token from database: {error}",
|
||||
"enter_token": "Enter your Cursor token: ",
|
||||
"token_length": "Token length: {length} characters",
|
||||
"usage_response_status": "Usage response status: {response}",
|
||||
"unexpected_status_code": "Unexpected status code: {code}",
|
||||
"jwt_token_warning": "Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.",
|
||||
"invalid_token": "Invalid token",
|
||||
"user_authorized": "User is authorized",
|
||||
"user_unauthorized": "User is unauthorized",
|
||||
"request_timeout": "Request timed out",
|
||||
"connection_error": "Connection error",
|
||||
"check_error": "Error checking authorization: {error}",
|
||||
"authorization_successful": "Authorization successful!",
|
||||
"authorization_failed": "Authorization failed!",
|
||||
"operation_cancelled": "Operation cancelled by user",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"error_generating_checksum": "Error generating checksum: {error}",
|
||||
"checking_usage_information": "Checking usage information...",
|
||||
"check_usage_response": "Check usage response: {response}",
|
||||
"usage_response": "Usage response: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "Bypass Token Limit Tool",
|
||||
"description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit",
|
||||
"press_enter": "Press Enter to continue..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "Refreshing token...",
|
||||
"refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})",
|
||||
"no_access_token": "No access token in response",
|
||||
"refresh_failed": "Token refresh failed: {error}",
|
||||
"invalid_response": "Invalid JSON response from refresh server",
|
||||
"server_error": "Refresh server error: HTTP {status}",
|
||||
"request_timeout": "Request to refresh server timed out",
|
||||
"connection_error": "Connection error to refresh server",
|
||||
"unexpected_error": "Unexpected error during token refresh: {error}",
|
||||
"extraction_error": "Error extracting token: {error}"
|
||||
}
|
||||
}
|
||||
}
|
||||
469
locales/es.json
Normal file
469
locales/es.json
Normal file
@@ -0,0 +1,469 @@
|
||||
{
|
||||
"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",
|
||||
"contribute": "Contribuir al Proyecto",
|
||||
"config": "Mostrar Configuración",
|
||||
"delete_google_account": "Eliminar Cuenta Google de Cursor",
|
||||
"continue_prompt": "¿Continuar? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operación cancelada por el usuario",
|
||||
"exiting": "Saliendo ……",
|
||||
"bypass_version_check": "Omitir Verificación de Versión de Cursor",
|
||||
"check_user_authorized": "Verificar Usuario Autorizado"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Inglés",
|
||||
"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",
|
||||
"no_write_permission": "Sin Permiso de Escritura: {path}",
|
||||
"path_not_found": "Ruta No Encontrada: {path}",
|
||||
"modify_file_failed": "Falló la Modificación del Archivo: {error}",
|
||||
"windows_machine_id_updated": "ID de Máquina Windows Actualizado Exitosamente",
|
||||
"update_windows_machine_id_failed": "Falló la Actualización del ID de Máquina Windows: {error}",
|
||||
"update_windows_machine_guid_failed": "Falló la Actualización del GUID de Máquina Windows: {error}",
|
||||
"file_not_found": "Archivo No Encontrado: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Herramienta de Registro de Cursor",
|
||||
"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."
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Selección de Perfil de Chrome",
|
||||
"select_profile": "Seleccione un perfil de Chrome para usar:",
|
||||
"profile_list": "Perfiles disponibles:",
|
||||
"default_profile": "Perfil Predeterminado",
|
||||
"profile": "Perfil {number}",
|
||||
"no_profiles": "No se encontraron perfiles de Chrome",
|
||||
"error_loading": "Error al cargar perfiles de Chrome: {error}",
|
||||
"profile_selected": "Perfil seleccionado: {profile}",
|
||||
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
|
||||
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
|
||||
}
|
||||
}
|
||||
408
locales/fr.json
Normal file
408
locales/fr.json
Normal file
@@ -0,0 +1,408 @@
|
||||
{
|
||||
"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",
|
||||
"fixed_soon": "Bientôt Corrigé",
|
||||
"contribute": "Contribuer au Projet",
|
||||
"config": "Afficher la Configuration",
|
||||
"delete_google_account": "Supprimer le Compte Google Cursor",
|
||||
"continue_prompt": "Continuer ? (y/N) : ",
|
||||
"operation_cancelled_by_user": "Opération annulée par l'utilisateur",
|
||||
"exiting": "Fermeture ……",
|
||||
"bypass_version_check": "Ignorer la Vérification de Version de Cursor",
|
||||
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Anglais",
|
||||
"zh_cn": "Chinois simplifié",
|
||||
"zh_tw": "Chinois traditionnel",
|
||||
"vi": "Vietnamien",
|
||||
"nl": "Néerlandais",
|
||||
"de": "Allemand",
|
||||
"fr": "Français",
|
||||
"pt": "Portugais",
|
||||
"ru": "Russe",
|
||||
"es": "Espagnol",
|
||||
"tr": "Turc",
|
||||
"bg": "Bulgare"
|
||||
},
|
||||
"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",
|
||||
"no_write_permission": "Pas de Permission d'Écriture : {path}",
|
||||
"path_not_found": "Chemin Non Trouvé : {path}",
|
||||
"modify_file_failed": "Échec de la Modification du Fichier : {error}",
|
||||
"windows_machine_id_updated": "ID de la Machine Windows Mis à Jour avec Succès",
|
||||
"update_windows_machine_id_failed": "Échec de la Mise à Jour de l'ID de la Machine Windows : {error}",
|
||||
"update_windows_machine_guid_failed": "Échec de la Mise à Jour du GUID de la Machine Windows : {error}",
|
||||
"file_not_found": "Fichier Non Trouvé : {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Outil d'Enregistrement de Cursor",
|
||||
"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"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Sélection du Profil Chrome",
|
||||
"select_profile": "Sélectionnez un profil Chrome à utiliser :",
|
||||
"profile_list": "Profils disponibles :",
|
||||
"default_profile": "Profil par Défaut",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Aucun profil Chrome trouvé",
|
||||
"error_loading": "Erreur lors du chargement des profils Chrome : {error}",
|
||||
"profile_selected": "Profil sélectionné : {profile}",
|
||||
"invalid_selection": "Sélection invalide. Veuillez réessayer",
|
||||
"warning_chrome_close": "Attention : Cela fermera tous les processus Chrome en cours d'exécution"
|
||||
}
|
||||
}
|
||||
407
locales/nl.json
Normal file
407
locales/nl.json
Normal file
@@ -0,0 +1,407 @@
|
||||
{
|
||||
"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 Aangepaste E-mail",
|
||||
"quit": "Cursor Toepassing Sluiten",
|
||||
"select_language": "Taal Wijzigen",
|
||||
"select_chrome_profile": "Chrome Profiel Selecteren",
|
||||
"input_choice": "Voer uw keuze in ({choices})",
|
||||
"invalid_choice": "Ongeldige selectie. Voer een nummer in uit {choices}.",
|
||||
"program_terminated": "Programma is beëindigd door de gebruiker",
|
||||
"error_occurred": "Er is een fout opgetreden: {error}. Probeer het opnieuw.",
|
||||
"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",
|
||||
"fixed_soon": "Binnenkort Opgelost",
|
||||
"contribute": "Bijdragen aan het Project",
|
||||
"config": "Configuratie Weergeven",
|
||||
"delete_google_account": "Cursor Google Account Verwijderen",
|
||||
"continue_prompt": "Doorgaan? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operatie geannuleerd door gebruiker",
|
||||
"exiting": "Afsluiten ……",
|
||||
"bypass_version_check": "Cursor Versiecontrole Overslaan",
|
||||
"check_user_authorized": "Gebruikersautorisatie Controleren"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Engels",
|
||||
"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",
|
||||
"no_write_permission": "Geen schrijfrechten: {path}",
|
||||
"path_not_found": "Pad niet gevonden: {path}",
|
||||
"modify_file_failed": "Bestand wijzigen mislukt: {error}",
|
||||
"windows_machine_id_updated": "Windows Machine-ID succesvol bijgewerkt",
|
||||
"update_windows_machine_id_failed": "Windows Machine-ID bijwerken mislukt: {error}",
|
||||
"update_windows_machine_guid_failed": "Windows Machine GUID bijwerken mislukt: {error}",
|
||||
"file_not_found": "Bestand niet gevonden: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Cursor Registratietool",
|
||||
"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"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profiel Selectie",
|
||||
"select_profile": "Selecteer een Chrome profiel om te gebruiken:",
|
||||
"profile_list": "Beschikbare profielen:",
|
||||
"default_profile": "Standaard Profiel",
|
||||
"profile": "Profiel {number}",
|
||||
"no_profiles": "Geen Chrome profielen gevonden",
|
||||
"error_loading": "Fout bij laden van Chrome profielen: {error}",
|
||||
"profile_selected": "Geselecteerd profiel: {profile}",
|
||||
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
|
||||
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
|
||||
}
|
||||
}
|
||||
408
locales/pt.json
Normal file
408
locales/pt.json
Normal file
@@ -0,0 +1,408 @@
|
||||
{
|
||||
"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",
|
||||
"fixed_soon": "Será corrigido em breve",
|
||||
"contribute": "Contribuir para o Projeto",
|
||||
"config": "Mostrar Configuração",
|
||||
"delete_google_account": "Excluir Conta Google do Cursor",
|
||||
"continue_prompt": "Continuar? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operação cancelada pelo usuário",
|
||||
"exiting": "Saindo ......",
|
||||
"bypass_version_check": "Ignorar Verificação de Versão do Cursor",
|
||||
"check_user_authorized": "Verificar Autorização do Usuário"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Inglês",
|
||||
"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"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Seleção de Perfil do Chrome",
|
||||
"select_profile": "Selecione um perfil do Chrome para usar:",
|
||||
"profile_list": "Perfis disponíveis:",
|
||||
"default_profile": "Perfil Padrão",
|
||||
"profile": "Perfil {number}",
|
||||
"no_profiles": "Nenhum perfil do Chrome encontrado",
|
||||
"error_loading": "Erro ao carregar perfis do Chrome: {error}",
|
||||
"profile_selected": "Perfil selecionado: {profile}",
|
||||
"invalid_selection": "Seleção inválida. Por favor, tente novamente",
|
||||
"warning_chrome_close": "Aviso: Isso fechará todos os processos do Chrome em execução"
|
||||
}
|
||||
}
|
||||
409
locales/ru.json
Normal file
409
locales/ru.json
Normal file
@@ -0,0 +1,409 @@
|
||||
{
|
||||
"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": "Скоро",
|
||||
"fixed_soon": "Скоро будет исправлено",
|
||||
"contribute": "Внести вклад в проект",
|
||||
"config": "Показать конфигурацию",
|
||||
"delete_google_account": "Удалить Google аккаунт Cursor",
|
||||
"continue_prompt": "Продолжить? (y/N): ",
|
||||
"operation_cancelled_by_user": "Операция отменена пользователем",
|
||||
"exiting": "Выход ......",
|
||||
"bypass_version_check": "Пропустить проверку версии Cursor",
|
||||
"check_user_authorized": "Проверить авторизацию пользователя",
|
||||
"select_chrome_profile": "Выбрать профиль Chrome"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Английский",
|
||||
"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 завершено"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Выбор Профиля Chrome",
|
||||
"select_profile": "Выберите профиль Chrome для использования:",
|
||||
"profile_list": "Доступные профили:",
|
||||
"default_profile": "Профиль по умолчанию",
|
||||
"profile": "Профиль {number}",
|
||||
"no_profiles": "Профили Chrome не найдены",
|
||||
"error_loading": "Ошибка загрузки профилей Chrome: {error}",
|
||||
"profile_selected": "Выбран профиль: {profile}",
|
||||
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
|
||||
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
|
||||
}
|
||||
}
|
||||
414
locales/tr.json
Normal file
414
locales/tr.json
Normal file
@@ -0,0 +1,414 @@
|
||||
{
|
||||
"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",
|
||||
"select_chrome_profile": "Chrome Profilini Seç",
|
||||
"input_choice": "Lütfen seçiminizi girin ({choices})",
|
||||
"invalid_choice": "Geçersiz seçim. Lütfen {choices} arasından bir sayı girin",
|
||||
"program_terminated": "Program kullanıcı tarafından sonlandırıldı",
|
||||
"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ı",
|
||||
"admin_required": "Running as executable, administrator privileges required.",
|
||||
"admin_required_continue": "Continuing without administrator privileges.",
|
||||
"coming_soon": "Yakında",
|
||||
"fixed_soon": "Yakında Düzeltilecek",
|
||||
"contribute": "Projeye Katkıda Bulun",
|
||||
"config": "Yapılandırmayı Göster",
|
||||
"delete_google_account": "Cursor Google Hesabını Sil",
|
||||
"continue_prompt": "Devam et? (y/N): ",
|
||||
"operation_cancelled_by_user": "İşlem kullanıcı tarafından iptal edildi",
|
||||
"exiting": "Çıkılıyor ......",
|
||||
"bypass_version_check": "Cursor Sürüm Kontrolünü Atla",
|
||||
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"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ı"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profil Seçimi",
|
||||
"select_profile": "Kullanılacak Chrome profilini seçin:",
|
||||
"profile_list": "Mevcut profiller:",
|
||||
"default_profile": "Varsayılan Profil",
|
||||
"profile": "Profil {number}",
|
||||
"no_profiles": "Chrome profili bulunamadı",
|
||||
"error_loading": "Chrome profilleri yüklenirken hata: {error}",
|
||||
"profile_selected": "Seçilen profil: {profile}",
|
||||
"invalid_selection": "Geçersiz seçim. Lütfen tekrar deneyin",
|
||||
"warning_chrome_close": "Uyarı: Bu işlem tüm çalışan Chrome işlemlerini kapatacaktır"
|
||||
}
|
||||
}
|
||||
731
locales/vi.json
Normal file
731
locales/vi.json
Normal file
@@ -0,0 +1,731 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Các Tùy Chọn",
|
||||
"exit": "Thoát Chương Trình",
|
||||
"reset": "Đặt Lại ID Máy",
|
||||
"register": "Đăng Ký Tài Khoản Cursor Mới",
|
||||
"register_google": "Đăng Ký Bằng Tài Khoản Google",
|
||||
"register_github": "Đăng Ký Bằng Tài Khoản GitHub",
|
||||
"register_manual": "Đăng Ký Cursor Với Email Tùy Chỉnh",
|
||||
"quit": "Đóng Ứng Dụng Cursor",
|
||||
"select_language": "Thay Đổi Ngôn Ngữ",
|
||||
"select_chrome_profile": "Chọn Hồ Sơ Chrome",
|
||||
"input_choice": "Vui lòng nhập lựa chọn của bạn ({choices})",
|
||||
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ {choices}",
|
||||
"program_terminated": "Chương trình đã bị người dùng chấm dứt",
|
||||
"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",
|
||||
"admin_required": "Đang chạy dưới dạng tệp thực thi, yêu cầu quyền quản trị.",
|
||||
"admin_required_continue": "Tiếp tục mà không có quyền quản trị.",
|
||||
"coming_soon": "Sắp ra mắt",
|
||||
"fixed_soon": "Sẽ Sớm Được Sửa",
|
||||
"contribute": "Đóng Góp Cho Dự Án",
|
||||
"config": "Hiển Thị Cấu Hình",
|
||||
"delete_google_account": "Xóa Tài Khoản Google Cursor",
|
||||
"continue_prompt": "Tiếp tục? (y/N): ",
|
||||
"operation_cancelled_by_user": "Thao tác đã bị người dùng hủy",
|
||||
"exiting": "Đang thoát ……",
|
||||
"bypass_version_check": "Bỏ qua Kiểm tra Phiên bản Cursor",
|
||||
"check_user_authorized": "Kiểm tra Quyền Người dùng"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Tiếng Anh",
|
||||
"zh_cn": "Tiếng Trung Giản Thể",
|
||||
"zh_tw": "Tiếng Trung Phồn Thể",
|
||||
"vi": "Tiếng Việt",
|
||||
"tr": "Tiếng Thổ Nhĩ Kỳ",
|
||||
"bg": "Tiếng Bulgaria",
|
||||
"nl": "Tiếng Hà Lan",
|
||||
"de": "Tiếng Đức",
|
||||
"fr": "Tiếng Pháp",
|
||||
"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",
|
||||
"no_write_permission": "Không Có Quyền Ghi: {path}",
|
||||
"path_not_found": "Không Tìm Thấy Đường Dẫn: {path}",
|
||||
"modify_file_failed": "Sửa Đổi Tệp Thất Bại: {error}",
|
||||
"windows_machine_id_updated": "Cập Nhật ID Máy Windows Thành Công",
|
||||
"update_windows_machine_id_failed": "Cập Nhật ID Máy Windows Thất Bại: {error}",
|
||||
"update_windows_machine_guid_failed": "Cập Nhật GUID Máy Windows Thất Bại: {error}",
|
||||
"file_not_found": "Không Tìm Thấy Tệp: {path}"
|
||||
},
|
||||
"register": {
|
||||
"title": "Công Cụ Đăng Ký Cursor",
|
||||
"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",
|
||||
"human_verify_error": "Không thể xác minh người dùng. Đang thử lại...",
|
||||
"max_retries_reached": "Đã đạt số lần thử tối đa. Đăng ký thất bại."
|
||||
},
|
||||
"auth": {
|
||||
"title": "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": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
|
||||
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
|
||||
"connected_to_database": "Đã Kết Nối Đến Cơ Sở Dữ Liệu",
|
||||
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
|
||||
"db_not_found": "Không tìm thấy tệp cơ sở dữ liệu tại: {path}",
|
||||
"db_permission_error": "Không thể truy cập tệp cơ sở dữ liệu. Vui lòng kiểm tra quyền",
|
||||
"db_connection_error": "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 Đã Được Lưu Vào cursor_tokens.txt",
|
||||
"navigate_to": "Đang Điều Hướng Đến {url}",
|
||||
"generate_email_success": "Tạo Email Thành Công",
|
||||
"select_email_domain": "Chọn Tên Miền Email",
|
||||
"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": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
|
||||
"no_valid_verification_code": "Không Có Mã Xác Minh Hợp Lệ"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Đang Khởi Động Trình Duyệt",
|
||||
"visiting_site": "Đang Truy Cập Tên Miền Mail",
|
||||
"create_success": "Tạo Email Thành Công",
|
||||
"create_failed": "Tạo Email Thất Bại",
|
||||
"create_error": "Lỗi Tạo Email: {error}",
|
||||
"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": "Hết Thời Gian Tải Tên Miền Bị Chặn: {timeout}s",
|
||||
"blocked_domains_loaded_timeout_error": "Lỗi Hết Thời Gian Tải Tên Miền Bị Chặn: {error}",
|
||||
"available_domains_loaded": "Đã Tải Tên Miền Khả Dụng: {count}",
|
||||
"domains_filtered": "Đã Lọc Tên Miền: {count}",
|
||||
"trying_to_create_email": "Đang Thử Tạo Email: {email}",
|
||||
"domain_blocked": "Tên Miền Bị Chặn: {domain}",
|
||||
"using_chrome_profile": "Đang Sử Dụng Hồ Sơ Chrome từ: {user_data_dir}",
|
||||
"no_display_found": "Không Tìm Thấy Màn Hình. Đảm Bảo X Server Đang Chạy.",
|
||||
"try_export_display": "Thử: export DISPLAY=:0",
|
||||
"extension_load_error": "Lỗi Tải Tiện Ích Mở Rộng: {error}",
|
||||
"make_sure_chrome_chromium_is_properly_installed": "Đảm Bảo Chrome/Chromium Được Cài Đặt Đúng Cách",
|
||||
"try_install_chromium": "Thử: sudo apt install chromium-browser"
|
||||
},
|
||||
"update": {
|
||||
"title": "Tắt Tự Động Cập Nhật Cursor",
|
||||
"disable_success": "Đã Tắt Tự Động Cập Nhật Thành Công",
|
||||
"disable_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",
|
||||
"clearing_update_yml": "Đang Xóa Tệp update.yml",
|
||||
"update_yml_cleared": "Đã Xóa Tệp update.yml",
|
||||
"update_yml_not_found": "Không Tìm Thấy Tệp update.yml",
|
||||
"clear_update_yml_failed": "Xóa Tệp update.yml Thất Bại: {error}",
|
||||
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {system}",
|
||||
"remove_directory_failed": "Xóa Thư Mục Thất Bại: {error}",
|
||||
"create_block_file_failed": "Tạo Tệp Chặn Thất Bại: {error}",
|
||||
"directory_locked": "Thư Mục Bị Khóa: {path}",
|
||||
"yml_locked": "Tệp update.yml Bị Khóa",
|
||||
"block_file_locked": "Tệp Chặn Bị Khóa",
|
||||
"yml_already_locked": "Tệp update.yml Đã Bị Khóa",
|
||||
"block_file_already_locked": "Tệp Chặn Đã Bị Khóa",
|
||||
"block_file_locked_error": "Lỗi Khóa Tệp Chặn: {error}",
|
||||
"yml_locked_error": "Lỗi Khóa Tệp update.yml: {error}",
|
||||
"block_file_already_locked_error": "Lỗi Tệp Chặn Đã Bị Khóa: {error}",
|
||||
"yml_already_locked_error": "Lỗi Tệp update.yml Đã Bị Khóa: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Đang Kiểm Tra Cập Nhật...",
|
||||
"new_version_available": "Có phiên bản mới! (Hiện tại: {current}, Mới nhất: {latest})",
|
||||
"updating": "Đang cập nhật lên phiên bản mới nhất. Chương trình sẽ tự động khởi động lại.",
|
||||
"up_to_date": "Bạn đang sử dụng phiên bản mới nhất.",
|
||||
"check_failed": "Kiểm tra cập nhật thất bại: {error}",
|
||||
"continue_anyway": "Tiếp tục với phiên bản hiện tại...",
|
||||
"update_confirm": "Bạn có muốn cập nhật lên phiên bản mới nhất không? (Y/n)",
|
||||
"update_skipped": "Bỏ qua cập nhật.",
|
||||
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'Y' hoặc 'n'.",
|
||||
"development_version": "Phiên Bản Phát Triển {current} > {latest}",
|
||||
"changelog_title": "Nhật Ký Thay Đổi",
|
||||
"rate_limit_exceeded": "Đã vượt quá giới hạn API GitHub. Bỏ qua kiểm tra cập nhật."
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Đặt Lại Hoàn Toàn Cursor",
|
||||
"checking_config": "Đang Kiểm Tra Tệp Cấu Hình",
|
||||
"config_not_found": "Không Tìm Thấy Tệp Cấu Hình",
|
||||
"no_permission": "Không Thể Đọc Hoặc Ghi Tệp Cấu Hình, Vui Lòng Kiểm Tra Quyền Tệp",
|
||||
"reading_config": "Đang Đọc Cấu Hình Hiện Tại",
|
||||
"creating_backup": "Đang Tạo Bản Sao Lưu Cấu Hình",
|
||||
"backup_exists": "Tệp Sao Lưu Đã Tồn Tại, Bỏ Qua Bước Sao Lưu",
|
||||
"generating_new_machine_id": "Đang Tạo ID Máy Mới",
|
||||
"saving_new_config": "Đang Lưu Cấu Hình Mới Vào JSON",
|
||||
"success": "Đặt Lại Cursor Thành Công",
|
||||
"error": "Đặt Lại Cursor Thất Bại: {error}",
|
||||
"press_enter": "Nhấn Enter để Thoát",
|
||||
"reset_machine_id": "Đặt Lại ID Máy",
|
||||
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
|
||||
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
|
||||
"connected_to_database": "Đã Kết Nối Đến Cơ Sở Dữ Liệu",
|
||||
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
|
||||
"db_not_found": "Không tìm thấy tệp cơ sở dữ liệu tại: {path}",
|
||||
"db_permission_error": "Không thể truy cập tệp cơ sở dữ liệu. Vui lòng kiểm tra quyền",
|
||||
"db_connection_error": "Không thể kết nối đến cơ sở dữ liệu: {error}",
|
||||
"feature_title": "TÍNH NĂNG",
|
||||
"feature_1": "Xóa hoàn toàn cài đặt và cấu hình của Cursor AI",
|
||||
"feature_2": "Xóa tất cả dữ liệu đã lưu trong bộ nhớ cache bao gồm lịch sử AI và lời nhắc",
|
||||
"feature_3": "Đặt lại ID máy để bỏ qua phát hiện dùng thử",
|
||||
"feature_4": "Tạo định danh máy mới ngẫu nhiên",
|
||||
"feature_5": "Xóa tiện ích mở rộng và tùy chọn tùy chỉnh",
|
||||
"feature_6": "Đặt lại thông tin dùng thử và dữ liệu kích hoạt",
|
||||
"feature_7": "Quét sâu các tệp giấy phép và dùng thử ẩn",
|
||||
"feature_8": "Bảo toàn an toàn các tệp và ứng dụng không phải của Cursor",
|
||||
"feature_9": "Tương thích với Windows, macOS và Linux",
|
||||
"disclaimer_title": "TUYÊN BỐ MIỄN TRỪ",
|
||||
"disclaimer_1": "Công cụ này sẽ xóa vĩnh viễn tất cả cài đặt Cursor AI,",
|
||||
"disclaimer_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
|
||||
"disclaimer_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
|
||||
"disclaimer_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
|
||||
"disclaimer_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
|
||||
"disclaimer_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
|
||||
"disclaimer_7": "Sử dụng với rủi ro của riêng bạn",
|
||||
"confirm_title": "Bạn có chắc chắn muốn tiếp tục không?",
|
||||
"confirm_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
|
||||
"confirm_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
|
||||
"confirm_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
|
||||
"confirm_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
|
||||
"confirm_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
|
||||
"confirm_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
|
||||
"confirm_7": "Sử dụng với rủi ro của riêng bạn",
|
||||
"invalid_choice": "Vui lòng nhập 'Y' hoặc 'n'",
|
||||
"skipped_for_safety": "Đã bỏ qua vì an toàn (không liên quan đến Cursor): {path}",
|
||||
"deleted": "Đã Xóa: {path}",
|
||||
"error_deleting": "Lỗi xóa {path}: {error}",
|
||||
"not_found": "Không tìm thấy tệp: {path}",
|
||||
"resetting_machine_id": "Đang đặt lại định danh máy để bỏ qua phát hiện dùng thử...",
|
||||
"created_machine_id": "Đã tạo ID máy mới: {path}",
|
||||
"error_creating_machine_id": "Lỗi tạo tệp ID máy {path}: {error}",
|
||||
"error_searching": "Lỗi tìm kiếm tệp trong {path}: {error}",
|
||||
"created_extended_trial_info": "Đã tạo thông tin dùng thử mở rộng mới: {path}",
|
||||
"error_creating_trial_info": "Lỗi tạo tệp thông tin dùng thử {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
|
||||
"reset_cancelled": "Đã hủy đặt lại. Thoát mà không thay đổi gì.",
|
||||
"windows_machine_id_modification_skipped": "Đã bỏ qua sửa đổi ID máy Windows: {error}",
|
||||
"linux_machine_id_modification_skipped": "Đã bỏ qua sửa đổi machine-id Linux: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Lưu ý: Đặt lại ID máy hoàn toàn có thể yêu cầu chạy với quyền quản trị",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Lưu ý: Đặt lại machine-id hệ thống hoàn toàn có thể yêu cầu quyền sudo",
|
||||
"windows_registry_instructions": "📝 LƯU Ý: Để đặt lại hoàn toàn trên Windows, bạn có thể cần xóa các mục đăng ký.",
|
||||
"windows_registry_instructions_2": " Chạy 'regedit' và tìm kiếm các khóa chứa 'Cursor' hoặc 'CursorAI' trong HKEY_CURRENT_USER\\Software\\ và xóa chúng.",
|
||||
"reset_log_1": "Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
|
||||
"reset_log_2": "Vui lòng khởi động lại hệ thống để các thay đổi có hiệu lực.",
|
||||
"reset_log_3": "Bạn sẽ cần cài đặt lại Cursor AI và bây giờ sẽ có một giai đoạn dùng thử mới.",
|
||||
"reset_log_4": "Để có kết quả tốt nhất, hãy xem xét:",
|
||||
"reset_log_5": "Sử dụng một địa chỉ email khác khi đăng ký dùng thử mới",
|
||||
"reset_log_6": "Nếu có thể, sử dụng VPN để thay đổi địa chỉ IP của bạn",
|
||||
"reset_log_7": "Xóa cookie và bộ nhớ cache trình duyệt trước khi truy cập trang web Cursor AI",
|
||||
"reset_log_8": "Nếu vấn đề vẫn còn, hãy thử cài đặt Cursor AI ở một vị trí khác",
|
||||
"reset_log_9": "Nếu bạn gặp bất kỳ vấn đề nào, hãy truy cập Github Issue Tracker và tạo một vấn đề tại https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Đã xảy ra lỗi không mong đợi: {error}",
|
||||
"report_issue": "Vui lòng báo cáo vấn đề này tới Github Issue Tracker tại https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Quá trình bị người dùng ngắt. Đang thoát...",
|
||||
"return_to_main_menu": "Đang trở về menu chính...",
|
||||
"process_interrupted": "Quá trình bị ngắt. Đang thoát...",
|
||||
"press_enter_to_return_to_main_menu": "Nhấn Enter để trở về menu chính...",
|
||||
"removing_known": "Đang xóa các tệp dùng thử/giấy phép đã biết",
|
||||
"performing_deep_scan": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
|
||||
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp giấy phép/dùng thử tiềm năng bổ sung",
|
||||
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp localStorage của Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy tệp giấy phép/dùng thử bổ sung trong quét sâu",
|
||||
"removing_electron_localstorage_files": "Đang xóa các tệp localStorage của Electron",
|
||||
"electron_localstorage_files_removed": "Đã xóa các tệp localStorage của Electron",
|
||||
"electron_localstorage_files_removal_error": "Lỗi xóa các tệp localStorage của Electron: {error}",
|
||||
"electron_localstorage_files_removal_completed": "Hoàn tất xóa các tệp localStorage của Electron",
|
||||
"warning_title": "CẢNH BÁO",
|
||||
"warning_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
|
||||
"warning_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
|
||||
"warning_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
|
||||
"warning_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
|
||||
"warning_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
|
||||
"warning_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
|
||||
"warning_7": "Sử dụng với rủi ro của riêng bạn",
|
||||
"removed": "Đã Xóa: {path}",
|
||||
"failed_to_reset_machine_guid": "Không thể đặt lại GUID máy",
|
||||
"failed_to_remove": "Không thể xóa: {path}",
|
||||
"failed_to_delete_file": "Không thể xóa tệp: {path}",
|
||||
"failed_to_delete_directory": "Không thể xóa thư mục: {path}",
|
||||
"failed_to_delete_file_or_directory": "Không thể xóa tệp hoặc thư mục: {path}",
|
||||
"deep_scanning": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
|
||||
"resetting_cursor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
|
||||
"completed_in": "Hoàn thành trong {time} giây",
|
||||
"cursor_reset_completed": "Trình soạn thảo Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
|
||||
"cursor_reset_failed": "Đặt lại Trình soạn thảo Cursor AI thất bại: {error}",
|
||||
"cursor_reset_cancelled": "Đã hủy đặt lại Trình soạn thảo Cursor AI. Thoát mà không thay đổi gì.",
|
||||
"operation_cancelled": "Đã hủy thao tác. Thoát mà không thay đổi gì.",
|
||||
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
|
||||
"already_on_settings": "Đã ở trang cài đặt",
|
||||
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
|
||||
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
|
||||
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
|
||||
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
|
||||
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
|
||||
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
|
||||
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
|
||||
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
|
||||
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
|
||||
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
|
||||
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
|
||||
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục"
|
||||
},
|
||||
"github_register": {
|
||||
"title": "Tự Động Hóa Đăng Ký GitHub + Cursor AI",
|
||||
"features_header": "Tính Năng",
|
||||
"feature1": "Tạo email tạm thời sử dụng 1secmail.",
|
||||
"feature2": "Đăng ký tài khoản GitHub mới với thông tin ngẫu nhiên.",
|
||||
"feature3": "Tự động xác minh email GitHub.",
|
||||
"feature4": "Đăng nhập vào Cursor AI sử dụng xác thực GitHub.",
|
||||
"feature5": "Đặt lại ID máy để bỏ qua phát hiện dùng thử.",
|
||||
"feature6": "Lưu tất cả thông tin đăng nhập vào tệp.",
|
||||
"warnings_header": "Cảnh Báo",
|
||||
"warning1": "Script này tự động hóa việc tạo tài khoản, có thể vi phạm điều khoản dịch vụ của GitHub/Cursor.",
|
||||
"warning2": "Yêu cầu truy cập internet và quyền quản trị.",
|
||||
"warning3": "CAPTCHA hoặc xác minh bổ sung có thể làm gián đoạn tự động hóa.",
|
||||
"warning4": "Sử dụng có trách nhiệm và tự chịu rủi ro.",
|
||||
"confirm": "Bạn có chắc chắn muốn tiếp tục không?",
|
||||
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'yes' hoặc 'no'",
|
||||
"cancelled": "Đã hủy thao tác",
|
||||
"program_terminated": "Chương trình bị người dùng chấm dứt",
|
||||
"starting_automation": "Bắt đầu tự động hóa...",
|
||||
"github_username": "Tên Người Dùng GitHub",
|
||||
"github_password": "Mật Khẩu GitHub",
|
||||
"email_address": "Địa Chỉ Email",
|
||||
"credentials_saved": "Các thông tin đăng nhập này đã được lưu vào github_cursor_accounts.txt",
|
||||
"completed_successfully": "Đăng ký GitHub + Cursor hoàn tất thành công!",
|
||||
"registration_encountered_issues": "Đăng ký GitHub + Cursor gặp vấn đề.",
|
||||
"check_browser_windows_for_manual_intervention_or_try_again_later": "Kiểm tra cửa sổ trình duyệt để can thiệp thủ công hoặc thử lại sau."
|
||||
},
|
||||
"account_info": {
|
||||
"subscription": "Gói Đăng Ký",
|
||||
"trial_remaining": "Thời Gian Dùng Thử Pro Còn Lại",
|
||||
"days": "ngày",
|
||||
"subscription_not_found": "Không tìm thấy thông tin đăng ký",
|
||||
"email_not_found": "Không tìm thấy email",
|
||||
"failed_to_get_account": "Không thể lấy thông tin tài khoản",
|
||||
"config_not_found": "Không tìm thấy cấu hình.",
|
||||
"failed_to_get_usage": "Không thể lấy thông tin sử dụng",
|
||||
"failed_to_get_subscription": "Không thể lấy thông tin đăng ký",
|
||||
"failed_to_get_email": "Không thể lấy địa chỉ email",
|
||||
"failed_to_get_token": "Không thể lấy token",
|
||||
"failed_to_get_account_info": "Không thể lấy thông tin tài khoản",
|
||||
"title": "Thông Tin Tài Khoản",
|
||||
"email": "Email",
|
||||
"token": "Token",
|
||||
"usage": "Sử Dụng",
|
||||
"subscription_type": "Loại Đăng Ký",
|
||||
"remaining_trial": "Thời Gian Dùng Thử Còn Lại",
|
||||
"days_remaining": "Số Ngày Còn Lại",
|
||||
"premium": "Premium",
|
||||
"pro": "Pro",
|
||||
"pro_trial": "Dùng Thử Pro",
|
||||
"team": "Team",
|
||||
"enterprise": "Enterprise",
|
||||
"free": "Miễn Phí",
|
||||
"active": "Đang Hoạt Động",
|
||||
"inactive": "Không Hoạt Động",
|
||||
"premium_usage": "Sử Dụng Premium",
|
||||
"basic_usage": "Sử Dụng Cơ Bản",
|
||||
"usage_not_found": "Không tìm thấy thông tin sử dụng",
|
||||
"lifetime_access_enabled": "Đã Bật Truy Cập Trọn Đời",
|
||||
"token_not_found": "Không tìm thấy token"
|
||||
},
|
||||
"config": {
|
||||
"config_not_available": "Không có sẵn cấu hình",
|
||||
"configuration": "Cấu Hình",
|
||||
"enabled": "Đã Bật",
|
||||
"disabled": "Đã Tắt",
|
||||
"config_directory": "Thư Mục Cấu Hình",
|
||||
"neither_cursor_nor_cursor_directory_found": "Không tìm thấy Cursor hoặc thư mục Cursor trong {config_base}",
|
||||
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Vui lòng đảm bảo Cursor đã được cài đặt và đã chạy ít nhất một lần",
|
||||
"storage_directory_not_found": "Không tìm thấy thư mục lưu trữ: {storage_dir}",
|
||||
"storage_file_found": "Đã tìm thấy tệp lưu trữ: {storage_path}",
|
||||
"file_size": "Kích thước tệp: {size} bytes",
|
||||
"file_permissions": "Quyền tệp: {permissions}",
|
||||
"file_owner": "Chủ sở hữu tệp: {owner}",
|
||||
"file_group": "Nhóm tệp: {group}",
|
||||
"error_getting_file_stats": "Lỗi lấy thông tin tệp: {error}",
|
||||
"permission_denied": "Từ chối quyền: {storage_path}",
|
||||
"try_running": "Thử chạy: {command}",
|
||||
"and": "Và",
|
||||
"storage_file_is_empty": "Tệp lưu trữ trống: {storage_path}",
|
||||
"the_file_might_be_corrupted_please_reinstall_cursor": "Tệp có thể bị hỏng, vui lòng cài đặt lại Cursor",
|
||||
"storage_file_not_found": "Không tìm thấy tệp lưu trữ: {storage_path}",
|
||||
"error_checking_linux_paths": "Lỗi kiểm tra đường dẫn Linux: {error}",
|
||||
"config_option_added": "Đã thêm tùy chọn cấu hình: {option}",
|
||||
"config_updated": "Đã cập nhật cấu hình",
|
||||
"config_created": "Đã tạo cấu hình: {config_file}",
|
||||
"config_setup_error": "Lỗi thiết lập cấu hình: {error}",
|
||||
"storage_file_is_valid_and_contains_data": "Tệp lưu trữ hợp lệ và chứa dữ liệu",
|
||||
"error_reading_storage_file": "Lỗi đọc tệp lưu trữ: {error}",
|
||||
"also_checked": "Cũng đã kiểm tra {path}",
|
||||
"backup_created": "Đã tạo bản sao lưu: {path}",
|
||||
"config_removed": "Đã xóa tệp cấu hình để cập nhật bắt buộc",
|
||||
"backup_failed": "Sao lưu cấu hình thất bại: {error}",
|
||||
"force_update_failed": "Cập nhật bắt buộc cấu hình thất bại: {error}",
|
||||
"config_force_update_disabled": "Đã tắt cập nhật bắt buộc tệp cấu hình, bỏ qua cập nhật bắt buộc",
|
||||
"config_force_update_enabled": "Đã bật cập nhật bắt buộc tệp cấu hình, thực hiện cập nhật bắt buộc"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Không tìm thấy nút xác thực",
|
||||
"authentication_failed": "Xác thực thất bại: {error}",
|
||||
"found_cookies": "Đã tìm thấy {count} cookie",
|
||||
"token_extraction_error": "Lỗi trích xuất token: {error}",
|
||||
"authentication_successful": "Xác thực thành công - Email: {email}",
|
||||
"missing_authentication_data": "Thiếu dữ liệu xác thực: {data}",
|
||||
"failed_to_delete_account": "Không thể xóa tài khoản: {error}",
|
||||
"invalid_authentication_type": "Loại xác thực không hợp lệ",
|
||||
"auth_update_success": "Cập nhật xác thực thành công",
|
||||
"browser_closed": "Đã đóng trình duyệt",
|
||||
"auth_update_failed": "Cập nhật xác thực thất bại",
|
||||
"google_start": "Bắt đầu Google",
|
||||
"github_start": "Bắt đầu Github",
|
||||
"usage_count": "Số lần sử dụng: {usage}",
|
||||
"account_has_reached_maximum_usage": "Tài khoản đã đạt số lần sử dụng tối đa, {deleting}",
|
||||
"starting_new_authentication_process": "Bắt đầu quá trình xác thực mới...",
|
||||
"failed_to_delete_expired_account": "Không thể xóa tài khoản hết hạn",
|
||||
"could_not_check_usage_count": "Không thể kiểm tra số lần sử dụng: {error}",
|
||||
"found_email": "Đã tìm thấy email: {email}",
|
||||
"could_not_find_email": "Không thể tìm thấy email: {error}",
|
||||
"could_not_find_usage_count": "Không thể tìm thấy số lần sử dụng: {error}",
|
||||
"already_on_settings_page": "Đã ở trang cài đặt!",
|
||||
"failed_to_extract_auth_info": "Không thể trích xuất thông tin xác thực: {error}",
|
||||
"no_chrome_profiles_found": "Không tìm thấy hồ sơ Chrome, sử dụng Mặc định",
|
||||
"found_default_chrome_profile": "Đã tìm thấy hồ sơ Chrome Mặc định",
|
||||
"using_first_available_chrome_profile": "Sử dụng hồ sơ Chrome khả dụng đầu tiên: {profile}",
|
||||
"error_finding_chrome_profile": "Lỗi tìm hồ sơ Chrome, sử dụng Mặc định: {error}",
|
||||
"initializing_browser_setup": "Đang khởi tạo thiết lập trình duyệt...",
|
||||
"detected_platform": "Đã phát hiện nền tảng: {platform}",
|
||||
"running_as_root_warning": "Chạy với quyền root không được khuyến nghị cho tự động hóa trình duyệt",
|
||||
"consider_running_without_sudo": "Hãy xem xét chạy script mà không cần sudo",
|
||||
"no_compatible_browser_found": "Không tìm thấy trình duyệt tương thích. Vui lòng cài đặt Google Chrome hoặc Chromium.",
|
||||
"supported_browsers": "Trình duyệt được hỗ trợ cho {platform}",
|
||||
"using_browser_profile": "Đang sử dụng hồ sơ trình duyệt: {profile}",
|
||||
"starting_browser": "Đang khởi động trình duyệt tại: {path}",
|
||||
"browser_setup_completed": "Thiết lập trình duyệt hoàn tất thành công",
|
||||
"browser_setup_failed": "Thiết lập trình duyệt thất bại: {error}",
|
||||
"try_running_without_sudo_admin": "Thử chạy mà không cần quyền sudo/quản trị",
|
||||
"redirecting_to_authenticator_cursor_sh": "Đang chuyển hướng đến authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "Bắt đầu xác thực Google...",
|
||||
"starting_github_authentication": "Bắt đầu xác thực GitHub...",
|
||||
"waiting_for_authentication": "Đang chờ xác thực...",
|
||||
"page_changed_checking_auth": "Trang đã thay đổi, đang kiểm tra xác thực...",
|
||||
"status_check_error": "Lỗi kiểm tra trạng thái: {error}",
|
||||
"authentication_timeout": "Hết thời gian xác thực",
|
||||
"account_is_still_valid": "Tài khoản vẫn còn hợp lệ (Sử dụng: {usage})",
|
||||
"starting_re_authentication_process": "Bắt đầu quá trình xác thực lại...",
|
||||
"starting_new_google_authentication": "Bắt đầu xác thực Google mới...",
|
||||
"failed_to_delete_account_or_re_authenticate": "Không thể xóa tài khoản hoặc xác thực lại: {error}",
|
||||
"navigating_to_authentication_page": "Đang điều hướng đến trang xác thực...",
|
||||
"please_select_your_google_account_to_continue": "Vui lòng chọn tài khoản Google của bạn để tiếp tục...",
|
||||
"found_browser_data_directory": "Đã tìm thấy thư mục dữ liệu trình duyệt: {path}",
|
||||
"authentication_successful_getting_account_info": "Xác thực thành công, đang lấy thông tin tài khoản...",
|
||||
"warning_could_not_kill_existing_browser_processes": "Cảnh báo: Không thể kết thúc các tiến trình trình duyệt hiện có: {error}",
|
||||
"browser_failed_to_start": "Trình duyệt không thể khởi động: {error}",
|
||||
"browser_failed": "Trình duyệt không thể khởi động: {error}",
|
||||
"browser_failed_to_start_fallback": "Trình duyệt không thể khởi động: {error}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chọn Hồ Sơ Chrome",
|
||||
"select_profile": "Chọn một hồ sơ Chrome để sử dụng:",
|
||||
"profile_list": "Hồ sơ khả dụng:",
|
||||
"default_profile": "Hồ Sơ Mặc Định",
|
||||
"profile": "Hồ Sơ {number}",
|
||||
"no_profiles": "Không tìm thấy hồ sơ Chrome",
|
||||
"error_loading": "Lỗi tải hồ sơ Chrome: {error}",
|
||||
"profile_selected": "Đã chọn hồ sơ: {profile}",
|
||||
"invalid_selection": "Lựa chọn không hợp lệ. Vui lòng thử lại",
|
||||
"warning_chrome_close": "Cảnh báo: Điều này sẽ đóng tất cả các tiến trình Chrome đang chạy"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Công Cụ Xóa Tài Khoản Google Cursor",
|
||||
"warning": "CẢNH BÁO: Điều này sẽ xóa vĩnh viễn tài khoản Cursor của bạn. Hành động này không thể hoàn tác.",
|
||||
"cancelled": "Đã hủy xóa tài khoản.",
|
||||
"starting_process": "Bắt đầu quá trình xóa tài khoản...",
|
||||
"google_button_not_found": "Không tìm thấy nút đăng nhập Google",
|
||||
"logging_in": "Đang đăng nhập bằng Google...",
|
||||
"waiting_for_auth": "Đang chờ xác thực Google...",
|
||||
"login_successful": "Đăng nhập thành công",
|
||||
"unexpected_page": "Trang không mong đợi sau khi đăng nhập: {url}",
|
||||
"trying_settings": "Đang thử điều hướng đến trang cài đặt...",
|
||||
"select_google_account": "Vui lòng chọn tài khoản Google của bạn...",
|
||||
"auth_timeout": "Hết thời gian xác thực, vẫn tiếp tục...",
|
||||
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
|
||||
"already_on_settings": "Đã ở trang cài đặt",
|
||||
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
|
||||
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
|
||||
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
|
||||
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
|
||||
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
|
||||
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
|
||||
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
|
||||
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
|
||||
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
|
||||
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
|
||||
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
|
||||
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
|
||||
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục",
|
||||
"typed_delete": "Đã nhập \"Delete\" vào ô xác nhận",
|
||||
"confirm_button_not_found": "Không tìm thấy nút Xác nhận sau nhiều lần thử",
|
||||
"confirm_button_retry": "Không tìm thấy nút Xác nhận, lần thử {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "Lỗi tìm nút Xác nhận: {error}",
|
||||
"account_deleted": "Đã xóa tài khoản thành công!",
|
||||
"error": "Lỗi trong quá trình xóa tài khoản: {error}",
|
||||
"success": "Tài khoản Cursor của bạn đã được xóa thành công!",
|
||||
"failed": "Quá trình xóa tài khoản thất bại hoặc đã bị hủy.",
|
||||
"interrupted": "Quá trình xóa tài khoản bị người dùng ngắt.",
|
||||
"unexpected_error": "Lỗi không mong đợi: {error}",
|
||||
"found_email": "Đã tìm thấy email: {email}",
|
||||
"email_not_found": "Không tìm thấy email: {error}",
|
||||
"confirm_prompt": "Bạn có chắc chắn muốn tiếp tục không? (y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "Bắt đầu bỏ qua phiên bản Cursor...",
|
||||
"found_product_json": "Đã tìm thấy product.json: {path}",
|
||||
"no_write_permission": "Không có quyền ghi cho tệp: {path}",
|
||||
"read_failed": "Không thể đọc product.json: {error}",
|
||||
"current_version": "Phiên bản hiện tại: {version}",
|
||||
"backup_created": "Đã tạo bản sao lưu: {path}",
|
||||
"version_updated": "Đã cập nhật phiên bản từ {old} lên {new}",
|
||||
"write_failed": "Không thể ghi product.json: {error}",
|
||||
"no_update_needed": "Không cần cập nhật. Phiên bản hiện tại {version} đã >= 0.46.0",
|
||||
"bypass_failed": "Bỏ qua phiên bản thất bại: {error}",
|
||||
"stack_trace": "Dấu vết ngăn xếp",
|
||||
"localappdata_not_found": "Không tìm thấy biến môi trường LOCALAPPDATA",
|
||||
"product_json_not_found": "Không tìm thấy product.json trong các đường dẫn Linux thông thường",
|
||||
"unsupported_os": "Hệ điều hành không được hỗ trợ: {system}",
|
||||
"file_not_found": "Không tìm thấy tệp: {path}",
|
||||
"title": "Công Cụ Bỏ Qua Phiên Bản Cursor",
|
||||
"description": "Công cụ này sửa đổi product.json của Cursor để bỏ qua hạn chế phiên bản",
|
||||
"menu_option": "Bỏ Qua Kiểm Tra Phiên Bản Cursor"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "Đang kiểm tra quyền...",
|
||||
"token_source": "Lấy token từ cơ sở dữ liệu hay nhập thủ công? (d/m, mặc định: d)",
|
||||
"getting_token_from_db": "Đang lấy token từ cơ sở dữ liệu...",
|
||||
"token_found_in_db": "Đã tìm thấy token trong cơ sở dữ liệu",
|
||||
"token_not_found_in_db": "Không tìm thấy token trong cơ sở dữ liệu",
|
||||
"cursor_acc_info_not_found": "Không tìm thấy cursor_acc_info.py",
|
||||
"error_getting_token_from_db": "Lỗi lấy token từ cơ sở dữ liệu: {error}",
|
||||
"enter_token": "Nhập token Cursor của bạn: ",
|
||||
"token_length": "Độ dài token: {length} ký tự",
|
||||
"usage_response_status": "Trạng thái phản hồi sử dụng: {response}",
|
||||
"unexpected_status_code": "Mã trạng thái không mong đợi: {code}",
|
||||
"jwt_token_warning": "Token có vẻ ở định dạng JWT, nhưng kiểm tra API trả về mã trạng thái không mong đợi. Token có thể hợp lệ nhưng truy cập API bị hạn chế.",
|
||||
"invalid_token": "Token không hợp lệ",
|
||||
"user_authorized": "Người dùng được ủy quyền",
|
||||
"user_unauthorized": "Người dùng không được ủy quyền",
|
||||
"request_timeout": "Yêu cầu hết thời gian",
|
||||
"connection_error": "Lỗi kết nối",
|
||||
"check_error": "Lỗi kiểm tra quyền: {error}",
|
||||
"authorization_successful": "Ủy quyền thành công!",
|
||||
"authorization_failed": "Ủy quyền thất bại!",
|
||||
"operation_cancelled": "Thao tác bị người dùng hủy",
|
||||
"unexpected_error": "Lỗi không mong đợi: {error}",
|
||||
"error_generating_checksum": "Lỗi tạo checksum: {error}",
|
||||
"checking_usage_information": "Đang kiểm tra thông tin sử dụng...",
|
||||
"check_usage_response": "Phản hồi kiểm tra sử dụng: {response}",
|
||||
"usage_response": "Phản hồi sử dụng: {response}"
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,51 @@
|
||||
"menu": {
|
||||
"title": "可用选项",
|
||||
"exit": "退出程序",
|
||||
"reset": "重置机器标识",
|
||||
"register": "注册 Cursor",
|
||||
"register_manual": "手动指定邮箱注册 Cursor",
|
||||
"quit": "退出 Cursor",
|
||||
"select_language": "选择语言",
|
||||
"input_choice": "输入选择 ({choices})",
|
||||
"invalid_choice": "无效选择,请重试",
|
||||
"program_terminated": "程序被用户终止",
|
||||
"error_occurred": "发生错误: {error}",
|
||||
"press_enter": "按回车键退出"
|
||||
"reset": "重置机器ID",
|
||||
"register": "注册新的Cursor账户",
|
||||
"register_google": "使用Google账户注册",
|
||||
"register_github": "使用GitHub账户注册",
|
||||
"register_manual": "使用自定义邮箱注册Cursor",
|
||||
"quit": "关闭Cursor应用",
|
||||
"select_language": "更改语言",
|
||||
"select_chrome_profile": "选择Chrome配置文件",
|
||||
"input_choice": "请输入您的选择 ({choices})",
|
||||
"invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
|
||||
"program_terminated": "程序已被用户终止",
|
||||
"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": "显示配置",
|
||||
"delete_google_account": "删除 Cursor Google 账号",
|
||||
"continue_prompt": "继续?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被用户取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "绕过 Cursor 版本检查",
|
||||
"check_user_authorized": "检查用户授权",
|
||||
"bypass_token_limit": "绕过 Token 限制"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"en": "英语",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文"
|
||||
"zh_tw": "繁体中文",
|
||||
"vi": "越南语",
|
||||
"nl": "荷兰语",
|
||||
"de": "德语",
|
||||
"fr": "法语",
|
||||
"pt": "葡萄牙语",
|
||||
"ru": "俄语",
|
||||
"tr": "土耳其语",
|
||||
"bg": "保加利亚语",
|
||||
"es": "西班牙语"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "开始退出 Cursor",
|
||||
@@ -63,29 +93,56 @@
|
||||
"patch_completed": "getMachineId修补完成",
|
||||
"patch_failed": "getMachineId修补失败: {error}",
|
||||
"version_check_passed": "Cursor版本检查通过",
|
||||
"file_modified": "文件已修改"
|
||||
"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}",
|
||||
"file_not_found": "文件未找到: {path}"
|
||||
},
|
||||
"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": "第一阶段验证通过",
|
||||
"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": "处理 Turnstile 验证",
|
||||
"retry_verification": "重试验证",
|
||||
"detect_turnstile": "检测 Turnstile 验证",
|
||||
"verification_success": "验证成功",
|
||||
"starting_browser": "启动浏览器",
|
||||
"handling_turnstile": "正在处理安全验证...",
|
||||
"retry_verification": "正在重试验证...",
|
||||
"detect_turnstile": "正在检查安全验证...",
|
||||
"verification_success": "安全验证通过",
|
||||
"starting_browser": "正在打开浏览器...",
|
||||
"form_success": "表单提交成功",
|
||||
"handle_turnstile": "处理 Turnstile 验证",
|
||||
"no_turnstile": "未检测到 Turnstile 验证",
|
||||
@@ -126,7 +183,31 @@
|
||||
"update_cursor_auth_info": "更新Cursor认证信息",
|
||||
"setting_password": "设置密码",
|
||||
"manual_code_input": "手动输入验证码",
|
||||
"manual_email_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秒内尝试...",
|
||||
"browser_path_invalid": "{browser} 路径无效,使用默认路径",
|
||||
"using_browser": "正在使用 {browser} 浏览器: {path}",
|
||||
"using_browser_profile": "使用 {browser} 配置文件: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "确保 {browser} 已正确安装",
|
||||
"try_install_browser": "尝试使用包管理器安装浏览器",
|
||||
"tracking_processes": "正在跟踪 {count} 个 {browser} 进程",
|
||||
"no_new_processes_detected": "未检测到新的 {browser} 进程",
|
||||
"could_not_track_processes": "无法跟踪 {browser} 进程: {error}",
|
||||
"human_verify_error": "无法验证用户是人类,正在重试...",
|
||||
"max_retries_reached": "已达到最大重试次数,注册失败。"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor 认证管理器",
|
||||
@@ -143,7 +224,10 @@
|
||||
"connected_to_database": "已连接到数据库",
|
||||
"database_updated_successfully": "数据库更新成功",
|
||||
"database_connection_closed": "数据库连接已关闭",
|
||||
"updating_pair": "更新键值对"
|
||||
"updating_pair": "更新键值对",
|
||||
"db_not_found": "未找到数据库文件:{path}",
|
||||
"db_permission_error": "无法访问数据库文件,请检查权限",
|
||||
"db_connection_error": "连接数据库失败:{error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "生成新邮箱",
|
||||
@@ -178,7 +262,7 @@
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "启动浏览器",
|
||||
"visiting_site": "访问 smailpro.com",
|
||||
"visiting_site": "访问 邮箱服务网站",
|
||||
"create_success": "邮箱创建成功",
|
||||
"create_failed": "邮箱创建失败",
|
||||
"create_error": "邮箱创建错误: {error}",
|
||||
@@ -192,6 +276,473 @@
|
||||
"verification_code_found": "找到验证码",
|
||||
"verification_code_not_found": "未找到验证码",
|
||||
"verification_code_error": "验证码错误: {error}",
|
||||
"address": "邮箱地址"
|
||||
"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}",
|
||||
"directory_locked": "目录被锁定: {path}",
|
||||
"yml_locked": "update.yml 文件被锁定",
|
||||
"block_file_locked": "阻止文件被锁定",
|
||||
"yml_already_locked": "update.yml 文件已锁定",
|
||||
"block_file_already_locked": "阻止文件已锁定",
|
||||
"block_file_locked_error": "阻止文件锁定错误: {error}",
|
||||
"yml_locked_error": "update.yml 文件锁定错误: {error}",
|
||||
"block_file_already_locked_error": "阻止文件已锁定错误: {error}",
|
||||
"yml_already_locked_error": "update.yml 文件已锁定错误: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "检查更新...",
|
||||
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
|
||||
"updating": "正在更新到最新版本。程序将自动重启。",
|
||||
"up_to_date": "您使用的是最新版本。",
|
||||
"check_failed": "检查更新失败: {error}",
|
||||
"continue_anyway": "继续使用当前版本...",
|
||||
"update_confirm": "是否要更新到最新版本? (Y/n)",
|
||||
"update_skipped": "跳过更新。",
|
||||
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
|
||||
"development_version": "开发版本 {current} > {latest}",
|
||||
"changelog_title": "更新日志",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超过。跳过更新检查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
"checking_config": "正在检查配置文件",
|
||||
"config_not_found": "未找到配置文件",
|
||||
"no_permission": "无法读取或写入配置文件,请检查文件权限",
|
||||
"reading_config": "正在读取当前配置",
|
||||
"creating_backup": "正在创建配置备份",
|
||||
"backup_exists": "备份文件已存在,跳过备份步骤",
|
||||
"generating_new_machine_id": "正在生成新的机器 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": "永久访问已启用",
|
||||
"token_not_found": "Token 未找到"
|
||||
},
|
||||
"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}",
|
||||
"backup_created": "备份创建: {path}",
|
||||
"config_removed": "配置文件已删除用于强制更新",
|
||||
"backup_failed": "备份失败: {error}",
|
||||
"force_update_failed": "强制更新配置失败: {error}",
|
||||
"config_force_update_disabled": "配置文件强制更新已禁用,跳过强制更新",
|
||||
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新",
|
||||
"documents_path_not_found": "找不到文档路径,使用当前目录",
|
||||
"config_dir_created": "已创建配置目录: {path}",
|
||||
"using_temp_dir": "由于错误使用临时目录: {path} (错误: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到认证按钮",
|
||||
"authentication_failed": "认证失败: {error}",
|
||||
"found_cookies": "找到 {count} 个 Cookie",
|
||||
"token_extraction_error": "Token 提取错误: {error}",
|
||||
"authentication_successful": "认证成功 - 邮箱: {email}",
|
||||
"missing_authentication_data": "缺少认证数据: {data}",
|
||||
"failed_to_delete_account": "删除账户失败: {error}",
|
||||
"invalid_authentication_type": "无效的认证类型",
|
||||
"auth_update_success": "认证更新成功",
|
||||
"browser_closed": "浏览器已关闭",
|
||||
"auth_update_failed": "认证更新失败",
|
||||
"google_start": "Google 开始",
|
||||
"github_start": "Github 开始",
|
||||
"usage_count": "使用次数: {usage}",
|
||||
"account_has_reached_maximum_usage": "账户已达到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "开始新的认证过程...",
|
||||
"failed_to_delete_expired_account": "删除过期账户失败",
|
||||
"could_not_check_usage_count": "无法检查使用次数: {error}",
|
||||
"found_email": "找到邮箱: {email}",
|
||||
"could_not_find_email": "未找到邮箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用次数: {error}",
|
||||
"already_on_settings_page": "已处于设置页面",
|
||||
"failed_to_extract_auth_info": "提取认证信息失败: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默认配置文件",
|
||||
"found_default_chrome_profile": "找到默认 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一个可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默认配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化浏览器设置...",
|
||||
"detected_platform": "检测平台: {platform}",
|
||||
"running_as_root_warning": "以 root 运行不推荐用于浏览器自动化",
|
||||
"consider_running_without_sudo": "考虑不使用 sudo 运行脚本",
|
||||
"no_compatible_browser_found": "未找到兼容的浏览器。请安装 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的浏览器: {platform}",
|
||||
"using_browser_profile": "使用浏览器配置文件: {profile}",
|
||||
"starting_browser": "正在启动浏览器: {path}",
|
||||
"browser_setup_completed": "浏览器设置完成",
|
||||
"browser_setup_failed": "浏览器设置失败: {error}",
|
||||
"try_running_without_sudo_admin": "尝试不使用 sudo/管理员权限运行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_google_authentication": "开始 Google 认证...",
|
||||
"starting_github_authentication": "开始 Github 认证...",
|
||||
"waiting_for_authentication": "等待认证...",
|
||||
"page_changed_checking_auth": "页面改变, 检查认证...",
|
||||
"status_check_error": "状态检查错误: {error}",
|
||||
"authentication_timeout": "认证超时",
|
||||
"account_is_still_valid": "账户仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "开始重新认证过程...",
|
||||
"starting_new_google_authentication": "开始新的 Google 认证...",
|
||||
"failed_to_delete_account_or_re_authenticate": "删除账户或重新认证失败: {error}",
|
||||
"navigating_to_authentication_page": "正在导航到认证页面...",
|
||||
"please_select_your_google_account_to_continue": "请选择您的 Google 账户以继续...",
|
||||
"found_browser_data_directory": "找到浏览器数据目录: {path}",
|
||||
"authentication_successful_getting_account_info": "认证成功, 获取账户信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}",
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
|
||||
"user_data_dir_not_found": "{browser} 用户数据目录未找到:{path},将尝试使用 Chrome",
|
||||
"error_getting_user_data_directory": "获取用户数据目录出错:{error}",
|
||||
"warning_browser_close": "警告:这将关闭所有正在运行的 {browser} 进程",
|
||||
"killing_browser_processes": "正在关闭 {browser} 进程...",
|
||||
"profile_selection_error": "配置文件选择过程中出错: {error}",
|
||||
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
|
||||
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
|
||||
"found_chrome_at": "找到 Chrome: {path}",
|
||||
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}"
|
||||
},
|
||||
"browser_profile": {
|
||||
"title": "浏览器配置文件选择",
|
||||
"select_profile": "选择要使用的{browser}配置文件:",
|
||||
"profile_list": "可用{browser}配置文件:",
|
||||
"default_profile": "默认配置文件",
|
||||
"profile": "配置文件 {number}",
|
||||
"no_profiles": "未找到{browser}配置文件",
|
||||
"error_loading": "加载{browser}配置文件时出错:{error}",
|
||||
"profile_selected": "已选择配置文件:{profile}",
|
||||
"invalid_selection": "选择无效。请重试"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google 账号删除工具",
|
||||
"warning": "警告:这将永久删除您的 Cursor 账号。此操作无法撤销。",
|
||||
"cancelled": "账号删除已取消。",
|
||||
"starting_process": "开始账号删除过程...",
|
||||
"google_button_not_found": "未找到 Google 登录按钮",
|
||||
"logging_in": "正在使用 Google 登录...",
|
||||
"waiting_for_auth": "等待 Google 验证...",
|
||||
"login_successful": "登录成功",
|
||||
"unexpected_page": "登录后页面异常:{url}",
|
||||
"trying_settings": "尝试导航到设置页面...",
|
||||
"select_google_account": "请选择您的 Google 账号...",
|
||||
"auth_timeout": "认证超时,继续执行...",
|
||||
"navigating_to_settings": "正在导航到设置页面...",
|
||||
"already_on_settings": "已在设置页面",
|
||||
"login_redirect_failed": "登录重定向失败,尝试直接导航...",
|
||||
"advanced_tab_not_found": "多次尝试后未找到高级选项卡",
|
||||
"advanced_tab_retry": "未找到高级选项卡,尝试 {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "查找高级选项卡时出错:{error}",
|
||||
"advanced_tab_clicked": "已点击高级选项卡",
|
||||
"direct_advanced_navigation": "尝试直接导航到高级选项卡",
|
||||
"delete_button_not_found": "多次尝试后未找到删除账号按钮",
|
||||
"delete_button_retry": "未找到删除按钮,尝试 {attempt}/{max_attempts}",
|
||||
"delete_button_error": "查找删除按钮时出错:{error}",
|
||||
"delete_button_clicked": "已点击删除账号按钮",
|
||||
"delete_input_not_found": "多次尝试后未找到删除确认输入框",
|
||||
"delete_input_retry": "未找到删除输入框,尝试 {attempt}/{max_attempts}",
|
||||
"delete_input_error": "查找删除输入框时出错:{error}",
|
||||
"delete_input_not_found_continuing": "未找到删除确认输入框,尝试继续执行...",
|
||||
"typed_delete": "已在确认框中输入\"Delete\"",
|
||||
"confirm_button_not_found": "多次尝试后未找到确认按钮",
|
||||
"confirm_button_retry": "未找到确认按钮,尝试 {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "查找确认按钮时出错:{error}",
|
||||
"account_deleted": "账号删除成功!",
|
||||
"error": "账号删除过程中出错:{error}",
|
||||
"success": "您的 Cursor 账号已成功删除!",
|
||||
"failed": "账号删除过程失败或已取消。",
|
||||
"interrupted": "账号删除过程被用户中断。",
|
||||
"unexpected_error": "意外错误:{error}",
|
||||
"found_email": "找到邮箱:{email}",
|
||||
"email_not_found": "未找到邮箱: {error}",
|
||||
"found_danger_zone": "已找到危险区域部分",
|
||||
"confirm_prompt": "您确定要继续吗?(y/N): "
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "开始绕过 Cursor 版本限制...",
|
||||
"found_product_json": "找到 product.json: {path}",
|
||||
"no_write_permission": "没有写入权限: {path}",
|
||||
"read_failed": "读取 product.json 失败: {error}",
|
||||
"current_version": "当前版本: {version}",
|
||||
"backup_created": "备份创建: {path}",
|
||||
"version_updated": "版本从 {old} 更新到 {new}",
|
||||
"write_failed": "写入 product.json 失败: {error}",
|
||||
"no_update_needed": "不需要更新。当前版本 {version} 已 >= 0.46.0",
|
||||
"bypass_failed": "绕过版本限制失败: {error}",
|
||||
"stack_trace": "堆栈跟踪",
|
||||
"localappdata_not_found": "LOCALAPPDATA 环境变量未找到",
|
||||
"product_json_not_found": "product.json 未在常见 Linux 路径中找到",
|
||||
"unsupported_os": "不支持的操作系统: {system}",
|
||||
"file_not_found": "文件未找到: {path}",
|
||||
"title": "Cursor 版本绕过工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
|
||||
"menu_option": "绕过 Cursor 版本检查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "检查授权...",
|
||||
"token_source": "从数据库获取 token 或手动输入?(d/m, 默认: d)",
|
||||
"getting_token_from_db": "从数据库获取 token...",
|
||||
"token_found_in_db": "在数据库中找到 token",
|
||||
"token_not_found_in_db": "在数据库中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
|
||||
"enter_token": "请输入您的 Cursor token: ",
|
||||
"token_length": "token 长度: {length}",
|
||||
"usage_response_status": "使用情况响应状态: {response}",
|
||||
"unexpected_status_code": "意外状态码: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
|
||||
"invalid_token": "无效的 token",
|
||||
"user_authorized": "用户已授权",
|
||||
"user_unauthorized": "用户未授权",
|
||||
"request_timeout": "请求超时",
|
||||
"connection_error": "连接错误",
|
||||
"check_error": "检查授权时出错: {error}",
|
||||
"authorization_successful": "授权成功",
|
||||
"authorization_failed": "授权失败",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外错误: {error}",
|
||||
"error_generating_checksum": "生成校验和时出错: {error}",
|
||||
"checking_usage_information": "检查使用情况...",
|
||||
"check_usage_response": "检查使用情况响应: {response}",
|
||||
"usage_response": "使用情况响应: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "绕过 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制",
|
||||
"press_enter": "按回车键继续..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "正在刷新令牌...",
|
||||
"refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire})",
|
||||
"no_access_token": "响应中没有访问令牌",
|
||||
"refresh_failed": "令牌刷新失败: {error}",
|
||||
"invalid_response": "刷新服务器返回无效的 JSON 响应",
|
||||
"server_error": "刷新服务器错误: HTTP {status}",
|
||||
"request_timeout": "刷新服务器请求超时",
|
||||
"connection_error": "连接刷新服务器错误",
|
||||
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
|
||||
"extraction_error": "提取令牌时出错: {error}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,52 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "可用選項",
|
||||
"exit": "退出程序",
|
||||
"reset": "重置機器標識",
|
||||
"register": "註冊 Cursor",
|
||||
"register_manual": "手動指定郵箱註冊 Cursor",
|
||||
"quit": "退出 Cursor",
|
||||
"select_language": "選擇語言",
|
||||
"input_choice": "輸入選擇 ({choices})",
|
||||
"invalid_choice": "無效選擇,請重試",
|
||||
"program_terminated": "程序被用戶終止",
|
||||
"error_occurred": "發生錯誤: {error}",
|
||||
"press_enter": "按回車鍵退出"
|
||||
"exit": "退出程式",
|
||||
"reset": "重置機器ID",
|
||||
"register": "註冊新的Cursor帳戶",
|
||||
"register_google": "使用Google帳戶註冊",
|
||||
"register_github": "使用GitHub帳戶註冊",
|
||||
"register_manual": "使用自定義郵箱註冊Cursor",
|
||||
"quit": "關閉Cursor應用",
|
||||
"select_language": "更改語言",
|
||||
"select_chrome_profile": "選擇Chrome配置檔案",
|
||||
"input_choice": "請輸入您的選擇 ({choices})",
|
||||
"invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
|
||||
"program_terminated": "程式已被使用者終止",
|
||||
"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": "顯示配置",
|
||||
"delete_google_account": "刪除 Cursor Google 帳號",
|
||||
"continue_prompt": "繼續?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被使用者取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "繞過 Cursor 版本檢查",
|
||||
"check_user_authorized": "檢查用戶授權",
|
||||
"bypass_token_limit": "繞過 Token 限制"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文"
|
||||
"en": "英文",
|
||||
"zh_cn": "簡體中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "越南文",
|
||||
"nl": "荷蘭文",
|
||||
"de": "德文",
|
||||
"fr": "法文",
|
||||
"pt": "葡萄牙文",
|
||||
"ru": "俄文",
|
||||
"tr": "土耳其文",
|
||||
"bg": "保加利亞文",
|
||||
"es": "西班牙文"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "開始退出 Cursor",
|
||||
@@ -63,47 +93,72 @@
|
||||
"patch_completed": "getMachineId修補完成",
|
||||
"patch_failed": "getMachineId修補失敗: {error}",
|
||||
"version_check_passed": "Cursor版本檢查通過",
|
||||
"file_modified": "文件已修改"
|
||||
"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}",
|
||||
"file_not_found": "文件未找到: {path}"
|
||||
},
|
||||
|
||||
"register": {
|
||||
"title": "Cursor 註冊工具",
|
||||
"start": "開始註冊流程",
|
||||
"mailbox": "成功進入郵箱",
|
||||
"browser_started": "瀏覽器已啟動",
|
||||
"waiting_for_page_load": "等待頁面加載",
|
||||
"password_success": "密碼設置完成",
|
||||
"password_error": "密碼設置失敗: {error}",
|
||||
"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",
|
||||
"first_verification_passed": "第一階段驗證通過",
|
||||
"register_start": "開始註冊流程",
|
||||
"form_submitted": "表單已提交,開始驗證...",
|
||||
"waiting_for_second_verification": "等待第二階段驗證",
|
||||
"filling_form": "填寫註冊信息",
|
||||
"basic_info": "基本信息提交完成",
|
||||
"handle_turnstile": "處理 Turnstile 驗證",
|
||||
"no_turnstile": "未檢測到 Turnstile 驗證",
|
||||
"turnstile_passed": "驗證通過",
|
||||
"verification_start": "開始獲取驗證碼",
|
||||
"waiting_for_verification_code": "等待驗證碼",
|
||||
"handling_turnstile": "處理 Turnstile 驗證",
|
||||
"retry_verification": "重試驗證",
|
||||
"detect_turnstile": "檢測 Turnstile 驗證",
|
||||
"verification_success": "驗證成功",
|
||||
"starting_browser": "啟動瀏覽器",
|
||||
"form_success": "表單提交成功",
|
||||
"verification_timeout": "獲取驗證碼超時",
|
||||
"verification_not_found": "未找到驗證碼",
|
||||
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
|
||||
"get_account": "獲取賬戶信息",
|
||||
"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": "正在打開郵箱頁面",
|
||||
"account_error": "獲取帳戶信息失敗: {error}",
|
||||
"email_error": "獲取郵箱地址失敗",
|
||||
"setup_error": "郵箱設置出錯: {error}",
|
||||
"start_getting_verification_code": "開始獲取驗證碼,將在60秒內嘗試...",
|
||||
@@ -126,7 +181,21 @@
|
||||
"update_cursor_auth_info": "更新Cursor認證信息",
|
||||
"setting_password": "設置密碼",
|
||||
"manual_code_input": "手動輸入驗證碼",
|
||||
"manual_email_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 認證管理器",
|
||||
@@ -143,7 +212,10 @@
|
||||
"connected_to_database": "已連接到數據庫",
|
||||
"database_updated_successfully": "數據庫更新成功",
|
||||
"database_connection_closed": "數據庫連接已關閉",
|
||||
"updating_pair": "更新鍵值對"
|
||||
"updating_pair": "更新鍵值對",
|
||||
"db_not_found": "未找到數據庫文件:{path}",
|
||||
"db_permission_error": "無法訪問數據庫文件,請檢查權限",
|
||||
"db_connection_error": "連接數據庫失敗:{error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "生成新郵箱",
|
||||
@@ -178,7 +250,7 @@
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "啟動瀏覽器",
|
||||
"visiting_site": "訪問 smailpro.com",
|
||||
"visiting_site": "訪問 郵箱網站",
|
||||
"create_success": "郵箱創建成功",
|
||||
"create_failed": "郵箱創建失敗",
|
||||
"create_error": "郵箱創建錯誤: {error}",
|
||||
@@ -192,7 +264,450 @@
|
||||
"verification_code_found": "找到驗證碼",
|
||||
"verification_code_not_found": "未找到驗證碼",
|
||||
"verification_code_error": "驗證碼錯誤: {error}",
|
||||
"address": "郵箱地址"
|
||||
}
|
||||
|
||||
}
|
||||
"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}",
|
||||
"directory_locked": "目錄被鎖定: {path}",
|
||||
"yml_locked": "update.yml 文件被鎖定",
|
||||
"block_file_locked": "阻止文件被鎖定",
|
||||
"yml_already_locked": "update.yml 文件已鎖定",
|
||||
"block_file_already_locked": "阻止文件已鎖定",
|
||||
"block_file_locked_error": "阻止文件锁定错误: {error}",
|
||||
"yml_locked_error": "update.yml 文件锁定错误: {error}",
|
||||
"block_file_already_locked_error": "阻止文件已锁定错误: {error}",
|
||||
"yml_already_locked_error": "update.yml 文件已锁定错误: {error}"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "檢查更新...",
|
||||
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
|
||||
"updating": "正在更新到最新版本。程序將自動重啟。",
|
||||
"up_to_date": "您使用的是最新版本。",
|
||||
"check_failed": "檢查更新失敗: {error}",
|
||||
"continue_anyway": "繼續使用當前版本...",
|
||||
"update_confirm": "是否要更新到最新版本? (Y/n)",
|
||||
"update_skipped": "跳過更新。",
|
||||
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
|
||||
"development_version": "開發版本 {current} > {latest}",
|
||||
"changelog_title": "更新日誌",
|
||||
"rate_limit_exceeded": "GitHub API 速率限制超過。跳過更新檢查。"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "完全重置 Cursor",
|
||||
"checking_config": "正在檢查配置檔案",
|
||||
"config_not_found": "找不到配置檔案",
|
||||
"no_permission": "無法讀取或寫入配置檔案,請檢查檔案權限",
|
||||
"reading_config": "正在讀取當前配置",
|
||||
"creating_backup": "正在建立配置備份",
|
||||
"backup_exists": "備份檔案已存在,跳過備份步驟",
|
||||
"generating_new_machine_id": "正在生成新的機器 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": "永久訪問已啟用",
|
||||
"token_not_found": "Token 未找到"
|
||||
},
|
||||
"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}",
|
||||
"backup_created": "備份已創建: {path}",
|
||||
"config_removed": "配置文件已刪除用於強制更新",
|
||||
"backup_failed": "備份失敗: {error}",
|
||||
"force_update_failed": "強制更新配置失敗: {error}",
|
||||
"config_force_update_disabled": "配置文件強制更新已禁用,跳過強制更新",
|
||||
"config_force_update_enabled": "配置文件強制更新已啟用,正在執行強制更新"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到認證按鈕",
|
||||
"authentication_failed": "認證失敗: {error}",
|
||||
"found_cookies": "找到 {count} 個 Cookie",
|
||||
"token_extraction_error": "Token 提取錯誤: {error}",
|
||||
"authentication_successful": "認證成功 - 郵箱: {email}",
|
||||
"missing_authentication_data": "缺少認證數據: {data}",
|
||||
"failed_to_delete_account": "刪除帳戶失敗: {error}",
|
||||
"invalid_authentication_type": "無效的認證類型",
|
||||
"auth_update_success": "認證更新成功",
|
||||
"browser_closed": "瀏覽器已關閉",
|
||||
"auth_update_failed": "認證更新失敗",
|
||||
"google_start": "Google 開始",
|
||||
"github_start": "Github 開始",
|
||||
"usage_count": "使用量: {usage}",
|
||||
"account_has_reached_maximum_usage": "帳戶已達到最大使用量, {deleting}",
|
||||
"starting_new_authentication_process": "開始新的認證過程...",
|
||||
"failed_to_delete_expired_account": "刪除過期帳戶失敗",
|
||||
"could_not_check_usage_count": "無法檢查使用量: {error}",
|
||||
"found_email": "找到郵箱: {email}",
|
||||
"could_not_find_email": "未找到郵箱: {error}",
|
||||
"could_not_find_usage_count": "未找到使用量: {erro r}",
|
||||
"already_on_settings_page": "已處於設置頁面",
|
||||
"failed_to_extract_auth_info": "提取認證信息失敗: {error}",
|
||||
"no_chrome_profiles_found": "未找到 Chrome 配置文件, 使用默認配置文件",
|
||||
"found_default_chrome_profile": "找到默認 Chrome 配置文件",
|
||||
"using_first_available_chrome_profile": "使用第一個可用的 Chrome 配置文件: {profile}",
|
||||
"error_finding_chrome_profile": "找不到 Chrome 配置文件, 使用默認配置文件: {error}",
|
||||
"initializing_browser_setup": "初始化瀏覽器設置...",
|
||||
"detected_platform": "檢測平台: {platform}",
|
||||
"running_as_root_warning": "以 root 運行不推薦用於瀏覽器自動化",
|
||||
"consider_running_without_sudo": "考慮不使用 sudo 運行腳本",
|
||||
"no_compatible_browser_found": "未找到兼容的瀏覽器。請安裝 Google Chrome 或 Chromium。",
|
||||
"supported_browsers": "支持的瀏覽器: {platform}",
|
||||
"using_browser_profile": "使用瀏覽器配置文件: {profile}",
|
||||
"starting_browser": "正在啟動瀏覽器: {path}",
|
||||
"browser_setup_completed": "瀏覽器設置完成成功",
|
||||
"browser_setup_failed": "瀏覽器設置失敗: {error}",
|
||||
"try_running_without_sudo_admin": "嘗試不使用 sudo/管理員權限運行",
|
||||
"redirecting_to_authenticator_cursor_sh": "重定向到 authenticator.cursor.sh...",
|
||||
"starting_github_authentication": "開始 Github 認證...",
|
||||
"waiting_for_authentication": "等待認證...",
|
||||
"page_changed_checking_auth": "頁面改變, 檢查認證...",
|
||||
"status_check_error": "狀態檢查錯誤: {error}",
|
||||
"authentication_timeout": "認證超時",
|
||||
"account_is_still_valid": "帳戶仍然有效 (使用量: {usage})",
|
||||
"starting_re_authentication_process": "開始重新認證過程...",
|
||||
"starting_new_google_authentication": "開始新的 Google 認證...",
|
||||
"failed_to_delete_account_or_re_authenticate": "刪除帳戶或重新認證失敗: {error}",
|
||||
"navigating_to_authentication_page": "正在導航到認證頁面...",
|
||||
"please_select_your_google_account_to_continue": "請選擇您的 Google 帳戶以繼續...",
|
||||
"found_browser_data_directory": "找到瀏覽器數據目錄: {path}",
|
||||
"authentication_successful_getting_account_info": "認證成功, 獲取帳戶信息...",
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 無法殺死現有瀏覽器進程: {error}",
|
||||
"browser_failed_to_start": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed": "瀏覽器啟動失敗: {error}",
|
||||
"browser_failed_to_start_fallback": "瀏覽器啟動失敗: {error}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome配置檔案選擇",
|
||||
"select_profile": "選擇要使用的Chrome配置檔案:",
|
||||
"profile_list": "可用配置檔案:",
|
||||
"default_profile": "預設配置檔案",
|
||||
"profile": "配置檔案 {number}",
|
||||
"no_profiles": "未找到Chrome配置檔案",
|
||||
"error_loading": "載入Chrome配置檔案時出錯:{error}",
|
||||
"profile_selected": "已選擇配置檔案:{profile}",
|
||||
"invalid_selection": "選擇無效。請重試",
|
||||
"warning_chrome_close": "警告:這將關閉所有正在執行的Chrome程序"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google 帳號刪除工具",
|
||||
"warning": "警告:這將永久刪除您的 Cursor 帳號。此操作無法撤銷。",
|
||||
"cancelled": "帳號刪除已取消。",
|
||||
"starting_process": "開始帳號刪除過程...",
|
||||
"google_button_not_found": "未找到 Google 登錄按鈕",
|
||||
"logging_in": "正在使用 Google 登錄...",
|
||||
"waiting_for_auth": "等待 Google 驗證...",
|
||||
"login_successful": "登錄成功",
|
||||
"unexpected_page": "登錄後頁面異常:{url}",
|
||||
"trying_settings": "嘗試導航到設置頁面...",
|
||||
"select_google_account": "請選擇您的 Google 帳號...",
|
||||
"auth_timeout": "認證超時,繼續執行...",
|
||||
"navigating_to_settings": "正在導航到設置頁面...",
|
||||
"already_on_settings": "已在設置頁面",
|
||||
"login_redirect_failed": "登錄重定向失敗,嘗試直接導航...",
|
||||
"advanced_tab_not_found": "多次嘗試後未找到高級選項卡",
|
||||
"advanced_tab_retry": "未找到高級選項卡,嘗試 {attempt}/{max_attempts}",
|
||||
"advanced_tab_error": "查找高級選項卡時出錯:{error}",
|
||||
"advanced_tab_clicked": "已點擊高級選項卡",
|
||||
"direct_advanced_navigation": "嘗試直接導航到高級選項卡",
|
||||
"delete_button_not_found": "多次嘗試後未找到刪除帳號按鈕",
|
||||
"delete_button_retry": "未找到刪除按鈕,嘗試 {attempt}/{max_attempts}",
|
||||
"delete_button_error": "查找刪除按鈕時出錯:{error}",
|
||||
"delete_button_clicked": "已點擊刪除帳號按鈕",
|
||||
"delete_input_not_found": "多次嘗試後未找到刪除確認輸入框",
|
||||
"delete_input_retry": "未找到刪除輸入框,嘗試 {attempt}/{max_attempts}",
|
||||
"delete_input_error": "查找刪除輸入框時出錯:{error}",
|
||||
"delete_input_not_found_continuing": "未找到刪除確認輸入框,嘗試繼續執行...",
|
||||
"typed_delete": "已在確認框中輸入\"Delete\"",
|
||||
"confirm_button_not_found": "多次嘗試後未找到確認按鈕",
|
||||
"confirm_button_retry": "未找到確認按鈕,嘗試 {attempt}/{max_attempts}",
|
||||
"confirm_button_error": "查找確認按鈕時出錯:{error}",
|
||||
"account_deleted": "帳號刪除成功!",
|
||||
"error": "帳號刪除過程中出錯:{error}",
|
||||
"success": "您的 Cursor 帳號已成功刪除!",
|
||||
"failed": "帳號刪除過程失敗或已取消。",
|
||||
"interrupted": "帳號刪除過程被用戶中斷。",
|
||||
"unexpected_error": "意外錯誤:{error}",
|
||||
"found_email": "找到郵箱:{email}",
|
||||
"email_not_found": "未找到郵箱: {error}",
|
||||
"found_danger_zone": "已找到危險區域部分",
|
||||
"confirm_prompt": "您確定要繼續嗎?(y/N): ",
|
||||
"typed_delete_js": "已使用 JavaScript 輸入\"Delete\""
|
||||
},
|
||||
"bypass": {
|
||||
"starting": "開始繞過 Cursor 版本限制...",
|
||||
"found_product_json": "找到 product.json: {path}",
|
||||
"no_write_permission": "沒有寫入權限: {path}",
|
||||
"read_failed": "讀取 product.json 失敗: {error}",
|
||||
"current_version": "當前版本: {version}",
|
||||
"backup_created": "備份已創建: {path}",
|
||||
"version_updated": "版本從 {old} 更新到 {new}",
|
||||
"write_failed": "寫入 product.json 失敗: {error}",
|
||||
"no_update_needed": "不需要更新。當前版本 {version} 已 >= 0.46.0",
|
||||
"bypass_failed": "繞過版本限制失敗: {error}",
|
||||
"stack_trace": "堆疊跟踪",
|
||||
"localappdata_not_found": "LOCALAPPDATA 環境變量未找到",
|
||||
"product_json_not_found": "product.json 未在常見 Linux 路徑中找到",
|
||||
"unsupported_os": "不支持的操作系統: {system}",
|
||||
"file_not_found": "文件未找到: {path}",
|
||||
"title": "Cursor 版本繞過工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
|
||||
"menu_option": "繞過 Cursor 版本檢查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "檢查授權...",
|
||||
"token_source": "從資料庫獲取 token 或手動輸入?(d/m, 預設: d)",
|
||||
"getting_token_from_db": "從資料庫獲取 token...",
|
||||
"token_found_in_db": "在資料庫中找到 token",
|
||||
"token_not_found_in_db": "在資料庫中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"usage_response_status": "使用情況響應狀態: {response}",
|
||||
"unexpected_status_code": "意外狀態碼: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
|
||||
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
|
||||
"enter_token": "請輸入您的 Cursor token: ",
|
||||
"token_length": "token 長度: {length}",
|
||||
"invalid_token": "無效的 token",
|
||||
"user_authorized": "用戶已授權",
|
||||
"user_unauthorized": "用戶未授權",
|
||||
"request_timeout": "請求超時",
|
||||
"connection_error": "連接錯誤",
|
||||
"check_error": "檢查授權時出錯: {error}",
|
||||
"authorization_successful": "授權成功",
|
||||
"authorization_failed": "授權失敗",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外錯誤: {error}",
|
||||
"error_generating_checksum": "生成校驗和時出錯: {error}",
|
||||
"checking_usage_information": "檢查使用情況...",
|
||||
"check_usage_response": "檢查使用情況響應: {response}",
|
||||
"usage_response": "使用情況響應: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "繞過 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
|
||||
"press_enter": "按回車鍵繼續..."
|
||||
}
|
||||
}
|
||||
94
logo.py
94
logo.py
@@ -1,39 +1,101 @@
|
||||
from colorama import Fore, Style, init
|
||||
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__))
|
||||
# 構建.env文件的完整路徑
|
||||
# Build the full path to the .env file
|
||||
env_path = os.path.join(current_dir, '.env')
|
||||
|
||||
# 加載環境變量,指定.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')
|
||||
|
||||
# 初始化 colorama
|
||||
# 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 v{version}
|
||||
{Fore.GREEN}
|
||||
Author: Pin Studios | yeongpin
|
||||
{Fore.RED}
|
||||
Press 5 to change language | 按下 5 键切换语言
|
||||
{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 Lucaszmv
|
||||
"""
|
||||
OTHER_INFO_TEXT = f"""{Fore.YELLOW}
|
||||
Github: https://github.com/yeongpin/cursor-free-vip{Fore.RED}
|
||||
Press 8 to change language | 按下 8 键切换语言{Style.RESET_ALL}"""
|
||||
|
||||
# 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()
|
||||
|
||||
669
main.py
669
main.py
@@ -3,13 +3,26 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from logo import print_logo
|
||||
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, force_update_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": "💾",
|
||||
@@ -19,130 +32,636 @@ EMOJI = {
|
||||
"RESET": "🔄",
|
||||
"MENU": "📋",
|
||||
"ARROW": "➜",
|
||||
"LANG": "🌐"
|
||||
"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.current_language = 'zh_tw' # 默认语言
|
||||
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 load_translations(self):
|
||||
"""加载所有可用的翻译"""
|
||||
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
locales_dir = os.path.join(sys._MEIPASS, 'locales')
|
||||
def detect_system_language(self):
|
||||
"""Detect system language and return corresponding language code"""
|
||||
try:
|
||||
system = platform.system()
|
||||
|
||||
for file in os.listdir(locales_dir):
|
||||
if file.endswith('.json'):
|
||||
lang_code = file[:-5] # 移除 .json
|
||||
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
|
||||
self.translations[lang_code] = json.load(f)
|
||||
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 using match-case
|
||||
match layout_id:
|
||||
case 0x0409:
|
||||
return 'en' # English
|
||||
case 0x0404:
|
||||
return 'zh_tw' # Traditional Chinese
|
||||
case 0x0804:
|
||||
return 'zh_cn' # Simplified Chinese
|
||||
case 0x0422:
|
||||
return 'vi' # Vietnamese
|
||||
case 0x0419:
|
||||
return 'ru' # Russian
|
||||
case 0x0415:
|
||||
return 'tr' # Turkish
|
||||
case 0x0402:
|
||||
return 'bg' # Bulgarian
|
||||
case _:
|
||||
return 'en' # Default to English
|
||||
except:
|
||||
return self._detect_unix_language()
|
||||
|
||||
def _detect_unix_language(self):
|
||||
"""Detect language on Unix-like systems (Linux, macOS)"""
|
||||
try:
|
||||
# Get the system locale
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
system_locale = locale.getlocale()[0]
|
||||
if not system_locale:
|
||||
return 'en'
|
||||
|
||||
system_locale = system_locale.lower()
|
||||
|
||||
# Map locale to our language codes using match-case
|
||||
match system_locale:
|
||||
case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
|
||||
return 'zh_tw'
|
||||
case s if s.startswith('zh_cn'):
|
||||
return 'zh_cn'
|
||||
case s if s.startswith('en'):
|
||||
return 'en'
|
||||
case s if s.startswith('vi'):
|
||||
return 'vi'
|
||||
case s if s.startswith('nl'):
|
||||
return 'nl'
|
||||
case s if s.startswith('de'):
|
||||
return 'de'
|
||||
case s if s.startswith('fr'):
|
||||
return 'fr'
|
||||
case s if s.startswith('pt'):
|
||||
return 'pt'
|
||||
case s if s.startswith('ru'):
|
||||
return 'ru'
|
||||
case s if s.startswith('tr'):
|
||||
return 'tr'
|
||||
case s if s.startswith('bg'):
|
||||
return 'bg'
|
||||
case _:
|
||||
# Try to get language from LANG environment variable as fallback
|
||||
env_lang = os.getenv('LANG', '').lower()
|
||||
match env_lang:
|
||||
case s if 'tw' in s or 'hk' in s:
|
||||
return 'zh_tw'
|
||||
case s if 'cn' in s:
|
||||
return 'zh_cn'
|
||||
case s if 'vi' in s:
|
||||
return 'vi'
|
||||
case s if 'nl' in s:
|
||||
return 'nl'
|
||||
case s if 'de' in s:
|
||||
return 'de'
|
||||
case s if 'fr' in s:
|
||||
return 'fr'
|
||||
case s if 'pt' in s:
|
||||
return 'pt'
|
||||
case s if 'ru' in s:
|
||||
return 'ru'
|
||||
case s if 'tr' in s:
|
||||
return 'tr'
|
||||
case s if 'bg' in s:
|
||||
return 'bg'
|
||||
case _:
|
||||
return 'en'
|
||||
except:
|
||||
return 'en'
|
||||
|
||||
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(self.current_language, {})
|
||||
value = self.translations.get(lang_code, {})
|
||||
for k in keys:
|
||||
if isinstance(value, dict):
|
||||
value = value.get(k, key)
|
||||
else:
|
||||
return key # 如果中間值不是字典,返回原始key
|
||||
return value.format(**kwargs) if kwargs else value
|
||||
return key
|
||||
return value
|
||||
except Exception:
|
||||
return key # 出現任何錯誤時返回原始key
|
||||
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 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}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
|
||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
||||
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
||||
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{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')}",
|
||||
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
|
||||
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
|
||||
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
|
||||
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}",
|
||||
17: f"{Fore.GREEN}17{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit', fallback='Bypass Token Limit')}"
|
||||
}
|
||||
|
||||
# Automatically calculate the number of menu items in the left and right columns
|
||||
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('languages')
|
||||
for i, (code, name) in enumerate(languages.items()):
|
||||
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {name}")
|
||||
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='0-' + str(len(languages)-1))}: {Style.RESET_ALL}")
|
||||
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):
|
||||
lang_code = list(languages.keys())[int(choice)]
|
||||
translator.set_language(lang_code)
|
||||
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):
|
||||
pass
|
||||
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
return False
|
||||
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
|
||||
force_update_config(translator)
|
||||
|
||||
if config.getboolean('Utils', 'enabled_update_check'):
|
||||
check_latest_version() # Add version check before showing menu
|
||||
print_menu()
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-5')}: {Style.RESET_ALL}")
|
||||
choice_num = 17
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
|
||||
|
||||
if choice == "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
elif choice == "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run(translator)
|
||||
break
|
||||
elif choice == "2":
|
||||
import cursor_register
|
||||
cursor_register.main(translator)
|
||||
break
|
||||
elif choice == "3":
|
||||
import cursor_register_manual
|
||||
cursor_register_manual.main(translator)
|
||||
break
|
||||
elif choice == "4":
|
||||
import quit_cursor
|
||||
quit_cursor.quit_cursor(translator)
|
||||
break
|
||||
elif choice == "5":
|
||||
if select_language():
|
||||
match choice:
|
||||
case "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
case "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run(translator)
|
||||
print_menu()
|
||||
case "2":
|
||||
import cursor_register
|
||||
cursor_register.main(translator)
|
||||
print_menu()
|
||||
case "3":
|
||||
import cursor_register_google
|
||||
cursor_register_google.main(translator)
|
||||
print_menu()
|
||||
case "4":
|
||||
import cursor_register_github
|
||||
cursor_register_github.main(translator)
|
||||
print_menu()
|
||||
case "5":
|
||||
import cursor_register_manual
|
||||
cursor_register_manual.main(translator)
|
||||
print_menu()
|
||||
case "6":
|
||||
import github_cursor_register
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
|
||||
# github_cursor_register.main(translator)
|
||||
print_menu()
|
||||
case "7":
|
||||
import quit_cursor
|
||||
quit_cursor.quit_cursor(translator)
|
||||
print_menu()
|
||||
case "8":
|
||||
if select_language():
|
||||
print_menu()
|
||||
continue
|
||||
case "9":
|
||||
import disable_auto_update
|
||||
disable_auto_update.run(translator)
|
||||
print_menu()
|
||||
case "10":
|
||||
import totally_reset_cursor
|
||||
totally_reset_cursor.run(translator)
|
||||
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
case "11":
|
||||
import logo
|
||||
print(logo.CURSOR_CONTRIBUTORS)
|
||||
print_menu()
|
||||
case "12":
|
||||
from config import print_config
|
||||
print_config(get_config(), translator)
|
||||
print_menu()
|
||||
case "13":
|
||||
from oauth_auth import OAuthHandler
|
||||
oauth = OAuthHandler(translator)
|
||||
oauth._select_profile()
|
||||
print_menu()
|
||||
case "14":
|
||||
import delete_cursor_google
|
||||
delete_cursor_google.main(translator)
|
||||
print_menu()
|
||||
case "15":
|
||||
import bypass_version
|
||||
bypass_version.main(translator)
|
||||
print_menu()
|
||||
case "16":
|
||||
import check_user_authorized
|
||||
check_user_authorized.main(translator)
|
||||
print_menu()
|
||||
case "17":
|
||||
import bypass_token_limit
|
||||
bypass_token_limit.run(translator)
|
||||
print_menu()
|
||||
case _:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
continue
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
print(f"\n{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('menu.press_enter')}...{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
568
new_signup.py
568
new_signup.py
@@ -4,20 +4,41 @@ import os
|
||||
import signal
|
||||
import random
|
||||
from colorama import Fore, Style
|
||||
import configparser
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from config import get_config
|
||||
from utils import get_default_browser_path as utils_get_default_browser_path
|
||||
|
||||
# 在文件开头添加全局变量
|
||||
# Add global variable at the beginning of the file
|
||||
_translator = None
|
||||
|
||||
# Add global variable to track our Chrome processes
|
||||
_chrome_process_ids = []
|
||||
|
||||
def cleanup_chrome_processes(translator=None):
|
||||
"""清理所有Chrome相关进程"""
|
||||
print("\n正在清理Chrome进程...")
|
||||
"""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':
|
||||
os.system('taskkill /F /IM chrome.exe /T 2>nul')
|
||||
os.system('taskkill /F /IM chromedriver.exe /T 2>nul')
|
||||
for pid in _chrome_process_ids:
|
||||
try:
|
||||
os.system(f'taskkill /F /PID {pid} /T 2>nul')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
os.system('pkill -f chrome')
|
||||
os.system('pkill -f chromedriver')
|
||||
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}")
|
||||
@@ -25,7 +46,7 @@ def cleanup_chrome_processes(translator=None):
|
||||
print(f"清理进程时出错: {e}")
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
"""处理Ctrl+C信号"""
|
||||
"""Handle Ctrl+C signal"""
|
||||
global _translator
|
||||
if _translator:
|
||||
print(f"{Fore.CYAN}{_translator.get('register.exit_signal')}{Style.RESET_ALL}")
|
||||
@@ -34,106 +55,233 @@ def signal_handler(signum, frame):
|
||||
cleanup_chrome_processes(_translator)
|
||||
os._exit(0)
|
||||
|
||||
def simulate_human_input(page, url, translator=None):
|
||||
"""访问网址"""
|
||||
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}")
|
||||
else:
|
||||
print("正在访问网址...")
|
||||
|
||||
# 先访问空白页面
|
||||
# First visit blank page
|
||||
page.get('about:blank')
|
||||
time.sleep(random.uniform(1.0, 2.0))
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
# 访问目标页面
|
||||
# Visit target page
|
||||
page.get(url)
|
||||
time.sleep(random.uniform(2.0, 3.0)) # 等待页面加载
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
def fill_signup_form(page, first_name, last_name, email, translator=None):
|
||||
"""填写注册表单"""
|
||||
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(random.uniform(0.5, 1.0))
|
||||
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(random.uniform(0.5, 1.0))
|
||||
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(random.uniform(0.5, 1.0))
|
||||
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(random.uniform(2.0, 3.0))
|
||||
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("表单填写完成")
|
||||
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"填写表单时出错: {e}")
|
||||
print(f"Error filling form: {e}")
|
||||
return False
|
||||
|
||||
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):
|
||||
"""设置浏览器驱动"""
|
||||
co = ChromiumOptions()
|
||||
|
||||
# 使用无痕模式
|
||||
co.set_argument("--incognito")
|
||||
|
||||
# 设置随机端口
|
||||
co.auto_port()
|
||||
|
||||
# 使用有头模式(一定要设置为False,模擬人類操作)
|
||||
co.headless(False)
|
||||
"""Setup browser driver"""
|
||||
global _chrome_process_ids
|
||||
|
||||
try:
|
||||
# 加载插件
|
||||
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)
|
||||
# Get config
|
||||
config = get_config(translator)
|
||||
|
||||
# Get browser type and path
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
|
||||
|
||||
if not browser_path or not os.path.exists(browser_path):
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {browser_type} {translator.get('register.browser_path_invalid')}{Style.RESET_ALL}")
|
||||
browser_path = utils_get_default_browser_path(browser_type)
|
||||
|
||||
# For backward compatibility, also check Chrome path
|
||||
if browser_type == 'chrome':
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
browser_path = chrome_path
|
||||
|
||||
# Set browser options
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Set browser path
|
||||
co.set_browser_path(browser_path)
|
||||
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Set Linux specific options
|
||||
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)
|
||||
|
||||
# Log browser info
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🌐 {translator.get('register.using_browser')}: {browser_type} {browser_path}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Load extension
|
||||
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
|
||||
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
|
||||
browser_process_names = {
|
||||
'chrome': ['chrome', 'chromium'],
|
||||
'edge': ['msedge', 'edge'],
|
||||
'firefox': ['firefox'],
|
||||
'brave': ['brave', 'brave-browser']
|
||||
}
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
before_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Launch browser
|
||||
page = ChromiumPage(co)
|
||||
|
||||
# Wait a moment for browser to fully launch
|
||||
time.sleep(1)
|
||||
|
||||
# Record browser processes after launching and find new ones
|
||||
try:
|
||||
import psutil
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
after_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
# Find new browser processes
|
||||
new_pids = [pid for pid in after_pids if pid not in before_pids]
|
||||
_chrome_process_ids.extend(new_pids)
|
||||
|
||||
if _chrome_process_ids:
|
||||
print(f"{translator.get('register.tracking_processes', count=len(_chrome_process_ids), browser=browser_type)}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}Warning: {translator.get('register.no_new_processes_detected', browser=browser_type)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{translator.get('register.could_not_track_processes', browser=browser_type, error=str(e))}")
|
||||
|
||||
return config, page
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}❌ {translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"加载插件失败: {e}")
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("正在启动浏览器...")
|
||||
page = ChromiumPage(co)
|
||||
|
||||
return page
|
||||
print(f"Error setting up browser: {e}")
|
||||
raise
|
||||
|
||||
def handle_turnstile(page, translator=None):
|
||||
"""处理 Turnstile 验证"""
|
||||
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("\n正在处理 Turnstile 验证...")
|
||||
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
|
||||
@@ -143,14 +291,14 @@ def handle_turnstile(page, translator=None):
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.retry_verification', attempt=retry_count)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"第 {retry_count} 次尝试验证...")
|
||||
print(f"Attempt {retry_count} of verification...")
|
||||
|
||||
try:
|
||||
# 尝试重置 turnstile
|
||||
# Try to reset turnstile
|
||||
page.run_js("try { turnstile.reset() } catch(e) { }")
|
||||
time.sleep(2)
|
||||
time.sleep(turnstile_time) # from config
|
||||
|
||||
# 定位验证框元素
|
||||
# Locate verification box element
|
||||
challenge_check = (
|
||||
page.ele("@id=cf-turnstile", timeout=2)
|
||||
.child()
|
||||
@@ -163,61 +311,61 @@ def handle_turnstile(page, translator=None):
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔄 {translator.get('register.detect_turnstile')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("检测到验证框...")
|
||||
print("Detected verification box...")
|
||||
|
||||
# 随机延时后点击验证
|
||||
time.sleep(random.uniform(1, 3))
|
||||
# from config
|
||||
time.sleep(random.uniform(min_time, max_time))
|
||||
challenge_check.click()
|
||||
time.sleep(2)
|
||||
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("验证通过!")
|
||||
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"验证尝试失败: {e}")
|
||||
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("验证通过!")
|
||||
print("Verification successful!")
|
||||
return True
|
||||
|
||||
time.sleep(random.uniform(1, 2))
|
||||
time.sleep(random.uniform(min_time, max_time))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("超出最大重试次数")
|
||||
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"验证过程出错: {e}")
|
||||
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")]',
|
||||
@@ -233,114 +381,103 @@ def check_verification_success(page, translator=None):
|
||||
return False
|
||||
|
||||
def generate_password(length=12):
|
||||
"""生成随机密码"""
|
||||
"""Generate random password"""
|
||||
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
||||
return ''.join(random.choices(chars, k=length))
|
||||
|
||||
def fill_password(page, password, translator=None):
|
||||
"""填写密码"""
|
||||
def fill_password(page, password: str, config, translator=None):
|
||||
"""
|
||||
Fill password form
|
||||
"""
|
||||
try:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n正在设置密码...")
|
||||
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)
|
||||
time.sleep(random.uniform(0.5, 1.0))
|
||||
|
||||
submit_button = page.ele("@type=submit")
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(random.uniform(2.0, 3.0))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.password_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"密码设置完成: {password}")
|
||||
return True
|
||||
|
||||
# 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:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.password_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"设置密码时出错: {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, email, password, translator=None):
|
||||
"""处理验证码"""
|
||||
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}")
|
||||
else:
|
||||
print("\n等待并获取验证码...")
|
||||
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
|
||||
|
||||
# 检查是否使用手动输入验证码
|
||||
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
|
||||
# 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(random.uniform(0.1, 0.3))
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
print("验证码填写完成")
|
||||
time.sleep(3)
|
||||
print(f"{translator.get('register.verification_success')}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# 处理最后一次 Turnstile 验证
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, config, translator):
|
||||
if translator:
|
||||
print(f"{translator.get('register.verification_success')}")
|
||||
else:
|
||||
print("最后一次验证通过!")
|
||||
time.sleep(2)
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
|
||||
|
||||
# 访问设置页面
|
||||
print("访问设置页面...")
|
||||
# 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(3) # 等待页面加载
|
||||
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("等待验证码邮件...")
|
||||
time.sleep(5) # 等待验证码邮件
|
||||
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'))
|
||||
|
||||
# 使用已有的 email_tab 刷新邮箱
|
||||
# Use existing email_tab to refresh email
|
||||
email_tab.refresh_inbox()
|
||||
time.sleep(3)
|
||||
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(random.uniform(0.1, 0.3))
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("验证码填写完成")
|
||||
time.sleep(3)
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# 处理最后一次 Turnstile 验证
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
# 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}")
|
||||
else:
|
||||
print("最后一次验证通过!")
|
||||
time.sleep(2)
|
||||
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}")
|
||||
else:
|
||||
print("访问设置页面...")
|
||||
browser_tab.get("https://www.cursor.com/settings")
|
||||
time.sleep(3) # 等待页面加载
|
||||
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
|
||||
return True, browser_tab
|
||||
|
||||
else:
|
||||
@@ -350,81 +487,65 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
|
||||
print("最后一次验证失败")
|
||||
return False, None
|
||||
|
||||
# 获取验证码,设置超时
|
||||
# Get verification code, set timeout
|
||||
verification_code = None
|
||||
max_attempts = 20
|
||||
retry_interval = 10
|
||||
retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
|
||||
start_time = time.time()
|
||||
timeout = 160
|
||||
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}")
|
||||
else:
|
||||
print("开始获取验证码...")
|
||||
|
||||
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}")
|
||||
else:
|
||||
print("获取验证码超时...")
|
||||
break
|
||||
|
||||
verification_code = controller.get_verification_code()
|
||||
if verification_code:
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"成功获取验证码: {verification_code}")
|
||||
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}")
|
||||
else:
|
||||
print(f"第 {attempt + 1} 次尝试获取验证码,剩余时间: {remaining_time}秒...")
|
||||
|
||||
# 刷新邮箱
|
||||
# Refresh email
|
||||
email_tab.refresh_inbox()
|
||||
time.sleep(retry_interval)
|
||||
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(random.uniform(0.1, 0.3))
|
||||
time.sleep(get_random_wait_time(config, 'verification_code_input'))
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("验证码填写完成")
|
||||
time.sleep(3)
|
||||
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
|
||||
|
||||
# 处理最后一次 Turnstile 验证
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
# 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}")
|
||||
else:
|
||||
print("最后一次验证通过!")
|
||||
time.sleep(2)
|
||||
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}")
|
||||
else:
|
||||
print("访问设置页面...")
|
||||
browser_tab.get("https://www.cursor.com/settings")
|
||||
time.sleep(3) # 等待页面加载
|
||||
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
|
||||
|
||||
# 直接返回成功,让 cursor_register.py 处理账户信息获取
|
||||
# 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}")
|
||||
else:
|
||||
print("最后一次验证失败")
|
||||
return False, None
|
||||
|
||||
return False, None
|
||||
@@ -432,63 +553,63 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"处理验证码时出错: {e}")
|
||||
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 # 如果不是登录页面,说明已经登录成功
|
||||
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)
|
||||
|
||||
# 点击 Continue
|
||||
# Click Continue
|
||||
continue_button = browser_tab.ele('xpath://button[contains(@class, "BrandedButton") and text()="Continue"]')
|
||||
if continue_button:
|
||||
continue_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
# 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)
|
||||
|
||||
# 点击 Sign in
|
||||
# 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)
|
||||
|
||||
# 处理最后一次 Turnstile 验证
|
||||
# Handle last Turnstile verification
|
||||
if handle_turnstile(browser_tab, translator):
|
||||
print(f"{Fore.GREEN}登录成功!{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}Login successful!{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}登录失败{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}Login failed{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}登录过程出错: {str(e)}{Style.RESET_ALL}")
|
||||
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
|
||||
_translator = 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)
|
||||
@@ -496,80 +617,63 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
|
||||
page = None
|
||||
success = False
|
||||
try:
|
||||
page = setup_driver(translator)
|
||||
config, page = setup_driver(translator)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("浏览器已启动")
|
||||
|
||||
# 访问注册页面
|
||||
# Visit registration page
|
||||
url = "https://authenticator.cursor.sh/sign-up"
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n正在访问: {url}")
|
||||
|
||||
# 访问页面
|
||||
simulate_human_input(page, url, translator)
|
||||
# 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}")
|
||||
else:
|
||||
print("等待页面加载...")
|
||||
time.sleep(5)
|
||||
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")
|
||||
|
||||
# 填写表单
|
||||
if fill_signup_form(page, first_name, last_name, email, translator):
|
||||
# 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}")
|
||||
else:
|
||||
print("\n表单已提交,开始验证...")
|
||||
print(f"\n{Fore.GREEN}✅ {translator.get('register.form_submitted')}{Style.RESET_ALL}")
|
||||
|
||||
# 处理第一次 Turnstile 验证
|
||||
if handle_turnstile(page, translator):
|
||||
# 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}")
|
||||
else:
|
||||
print("\n第一阶段验证通过!")
|
||||
print(f"\n{Fore.GREEN}✅ {translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# 填写密码
|
||||
if fill_password(page, password, translator):
|
||||
# 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}")
|
||||
else:
|
||||
print("\n等待第二次验证...")
|
||||
time.sleep(2)
|
||||
|
||||
# 处理第二次 Turnstile 验证
|
||||
if handle_turnstile(page, 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}")
|
||||
else:
|
||||
print("\n开始处理验证码...")
|
||||
if handle_verification_code(page, email_tab, controller, email, password, 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 # 返回浏览器实例
|
||||
return True, page
|
||||
else:
|
||||
print("\n验证码处理失败")
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n第二次验证失败")
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n密码设置失败")
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n第一次验证失败")
|
||||
print(f"\n{Fore.RED}❌ {translator.get('register.first_verification_failed') if translator else 'First verification failed'}{Style.RESET_ALL}")
|
||||
|
||||
return False, None
|
||||
|
||||
@@ -577,7 +681,7 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
|
||||
print(f"发生错误: {e}")
|
||||
return False, None
|
||||
finally:
|
||||
if page and not success: # 只在失败时清理
|
||||
if page and not success: # Only clean up when failed
|
||||
try:
|
||||
page.quit()
|
||||
except:
|
||||
@@ -585,4 +689,4 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
|
||||
cleanup_chrome_processes(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main() # 直接运行时不传参数,使用随机生成的信息
|
||||
main() # Run without parameters, use randomly generated information
|
||||
203
new_tempemail.py
203
new_tempemail.py
@@ -3,8 +3,13 @@ import time
|
||||
import os
|
||||
import sys
|
||||
from colorama import Fore, Style, init
|
||||
import requests
|
||||
import random
|
||||
import string
|
||||
from config import get_config
|
||||
from utils import get_random_wait_time, get_default_browser_path as utils_get_default_browser_path
|
||||
|
||||
# 初始化 colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
class NewTempEmail:
|
||||
@@ -13,6 +18,72 @@ class NewTempEmail:
|
||||
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()
|
||||
@@ -34,16 +105,86 @@ class NewTempEmail:
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在启动浏览器...{Style.RESET_ALL}")
|
||||
|
||||
# 获取配置
|
||||
config = get_config(self.translator)
|
||||
|
||||
# 获取浏览器类型和路径
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
|
||||
|
||||
if not browser_path or not os.path.exists(browser_path):
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.browser_path_invalid', browser=browser_type) if self.translator else f'{browser_type} 路径无效,使用默认路径'}{Style.RESET_ALL}")
|
||||
browser_path = utils_get_default_browser_path(browser_type)
|
||||
|
||||
# 为了向后兼容,也检查 Chrome 路径
|
||||
if browser_type == 'chrome':
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
browser_path = chrome_path
|
||||
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
# 设置浏览器路径
|
||||
co.set_browser_path(browser_path)
|
||||
|
||||
# 记录浏览器信息
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}🌐 {self.translator.get('email.using_browser', browser=browser_type, path=browser_path) if self.translator else f'使用 {browser_type} 浏览器: {browser_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Only use headless for non-OAuth operations
|
||||
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
|
||||
co.set_argument("--headless=new")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Check if DISPLAY is set when not in headless mode
|
||||
if "--headless=new" not in co.arguments and not os.environ.get('DISPLAY'):
|
||||
print(f"{Fore.RED}❌ {self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
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 browser profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
|
||||
# 根据浏览器类型选择配置文件夹
|
||||
profile_dirs = {
|
||||
'chrome': os.path.join(actual_home, ".config", "google-chrome"),
|
||||
'brave': os.path.join(actual_home, ".config", "BraveSoftware", "Brave-Browser"),
|
||||
'edge': os.path.join(actual_home, ".config", "microsoft-edge"),
|
||||
'firefox': os.path.join(actual_home, ".mozilla", "firefox")
|
||||
}
|
||||
|
||||
user_data_dir = profile_dirs.get(browser_type, profile_dirs['chrome'])
|
||||
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_browser_profile', browser=browser_type, user_data_dir=user_data_dir) if self.translator else f'Using {browser_type} profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
# 根据浏览器类型设置扩展参数
|
||||
extension_args = {
|
||||
'chrome': "--allow-extensions-in-incognito",
|
||||
'brave': "--allow-extensions-in-brave-incognito", # Brave 可能使用不同的参数
|
||||
'edge': "--allow-extensions-in-incognito",
|
||||
'firefox': None # Firefox 可能使用不同的方式加载扩展
|
||||
}
|
||||
|
||||
extension_arg = extension_args.get(browser_type, "--allow-extensions-in-incognito")
|
||||
|
||||
# 加载 uBlock 插件
|
||||
try:
|
||||
extension_path = self.get_extension_block()
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
if extension_arg: # 如果有扩展参数
|
||||
co.set_argument(extension_arg)
|
||||
co.add_extension(extension_path)
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
@@ -58,37 +199,63 @@ class NewTempEmail:
|
||||
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":
|
||||
browser_install_suggestions = {
|
||||
'chrome': "sudo apt install chromium-browser 或 sudo apt install google-chrome-stable",
|
||||
'brave': "sudo apt install brave-browser",
|
||||
'edge': "sudo apt install microsoft-edge-stable",
|
||||
'firefox': "sudo apt install firefox"
|
||||
}
|
||||
|
||||
suggestion = browser_install_suggestions.get(browser_type, browser_install_suggestions['chrome'])
|
||||
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_browser_is_properly_installed', browser=browser_type) if self.translator else f'Make sure {browser_type} is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_browser') if self.translator else f'Try: {suggestion}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def create_email(self):
|
||||
"""创建临时邮箱"""
|
||||
"""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)
|
||||
|
||||
# 点击弹窗中的 Create 按钮
|
||||
# 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: # 验证是否是有效的邮箱地址
|
||||
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:
|
||||
@@ -108,23 +275,23 @@ class NewTempEmail:
|
||||
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) # 等待刷新完成
|
||||
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:
|
||||
@@ -147,16 +314,16 @@ class NewTempEmail:
|
||||
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}")
|
||||
# 使用 JavaScript 点击元素
|
||||
# use JavaScript to click element
|
||||
self.page.run_js('arguments[0].click()', email_div)
|
||||
time.sleep(2) # 等待邮件内容加载
|
||||
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}")
|
||||
@@ -174,7 +341,7 @@ class NewTempEmail:
|
||||
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()
|
||||
@@ -208,7 +375,7 @@ def main(translator=None):
|
||||
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()
|
||||
|
||||
1052
oauth_auth.py
Normal file
1052
oauth_auth.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,10 @@ from colorama import Fore, Style, init
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"PROCESS": "⚙️",
|
||||
"SUCCESS": "✅",
|
||||
@@ -19,15 +19,15 @@ EMOJI = {
|
||||
class CursorQuitter:
|
||||
def __init__(self, timeout=5, translator=None):
|
||||
self.timeout = timeout
|
||||
self.translator = translator # 使用传入的翻译器
|
||||
self.translator = translator # Use the passed translator
|
||||
|
||||
def quit_cursor(self):
|
||||
"""温和地关闭 Cursor 进程"""
|
||||
"""Gently close Cursor processes"""
|
||||
try:
|
||||
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']:
|
||||
@@ -39,7 +39,7 @@ class CursorQuitter:
|
||||
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():
|
||||
@@ -48,7 +48,7 @@ class CursorQuitter:
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
# 等待进程自然终止
|
||||
# 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:
|
||||
@@ -66,7 +66,7 @@ class CursorQuitter:
|
||||
|
||||
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']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}")
|
||||
@@ -79,11 +79,11 @@ class CursorQuitter:
|
||||
return False
|
||||
|
||||
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__":
|
||||
# 如果直接运行,使用默认翻译器
|
||||
# If run directly, use the default translator
|
||||
from main import translator as main_translator
|
||||
quit_cursor(main_translator)
|
||||
@@ -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
|
||||
|
||||
@@ -8,13 +8,19 @@ import sqlite3
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
import glob
|
||||
from colorama import Fore, Style, init
|
||||
from typing import Tuple
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# 初始化colorama
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# 定义emoji常量
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
@@ -22,50 +28,215 @@ EMOJI = {
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||
"""根据不同操作系统获取 Cursor 相关路径"""
|
||||
""" 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"), "/usr/lib/cursor/app/"]
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Look for extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
# Also check current directory for extraction without home path prefix
|
||||
current_dir_paths = glob.glob("squashfs-root/usr/share/cursor/resources/app")
|
||||
|
||||
# Add any found paths to the Linux paths list
|
||||
default_paths["Linux"].extend(extracted_usr_paths)
|
||||
default_paths["Linux"].extend(current_dir_paths)
|
||||
|
||||
# Print debug information
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Available paths found:{Style.RESET_ALL}")
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {path} (exists){Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {path} (not found){Style.RESET_ALL}")
|
||||
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
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()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"package": "package.json",
|
||||
"main": "out/main.js",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"base": os.path.join(
|
||||
os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"
|
||||
),
|
||||
"package": "package.json",
|
||||
"main": "out/main.js",
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
|
||||
"package": "package.json",
|
||||
"main": "out/main.js",
|
||||
},
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Add extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
|
||||
if system not in paths_map:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if system == "Linux":
|
||||
for base in paths_map["Linux"]["bases"]:
|
||||
pkg_path = os.path.join(base, paths_map["Linux"]["package"])
|
||||
if os.path.exists(pkg_path):
|
||||
return (pkg_path, os.path.join(base, paths_map["Linux"]["main"]))
|
||||
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
|
||||
main_path = os.path.join(base, paths_map["Linux"]["main"])
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
|
||||
if os.path.exists(main_path):
|
||||
return main_path
|
||||
|
||||
base_path = paths_map[system]["base"]
|
||||
return (
|
||||
os.path.join(base_path, paths_map[system]["package"]),
|
||||
os.path.join(base_path, paths_map[system]["main"]),
|
||||
)
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
|
||||
|
||||
return main_path
|
||||
|
||||
def 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):
|
||||
@@ -92,18 +263,141 @@ def version_check(version: str, min_version: str = "", max_version: str = "", tr
|
||||
return False
|
||||
|
||||
def check_cursor_version(translator) -> bool:
|
||||
"""检查 Cursor 版本"""
|
||||
"""Check Cursor version"""
|
||||
try:
|
||||
pkg_path, _ = get_cursor_paths(translator)
|
||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||
version = json.load(f)["version"]
|
||||
return version_check(version, min_version="0.45.0", translator=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()
|
||||
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux/Mac 通用按钮替换模式
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
shutil.move(tmp_path, file_path)
|
||||
|
||||
# Restore original permissions
|
||||
os.chmod(file_path, original_mode)
|
||||
if os.name != "nt": # Not Windows
|
||||
os.chown(file_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def modify_main_js(main_path: str, translator) -> bool:
|
||||
"""修改 main.js 文件"""
|
||||
"""Modify main.js file"""
|
||||
try:
|
||||
original_stat = os.stat(main_path)
|
||||
original_mode = original_stat.st_mode
|
||||
@@ -125,7 +419,10 @@ def modify_main_js(main_path: str, translator) -> bool:
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.old.{timestamp}"
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
@@ -142,14 +439,14 @@ def modify_main_js(main_path: str, translator) -> bool:
|
||||
return False
|
||||
|
||||
def patch_cursor_get_machine_id(translator) -> bool:
|
||||
"""修补 Cursor getMachineId 函数"""
|
||||
"""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}")
|
||||
@@ -158,7 +455,7 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
||||
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"]
|
||||
@@ -167,20 +464,21 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
||||
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_path = main_path + ".bak"
|
||||
# Backup file
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.bak.{timestamp}"
|
||||
if not os.path.exists(backup_path):
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# 修改文件
|
||||
# Modify file
|
||||
if not modify_main_js(main_path, translator):
|
||||
return False
|
||||
|
||||
@@ -195,58 +493,99 @@ 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")
|
||||
self.db_path = os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "storage.json"
|
||||
)
|
||||
self.sqlite_path = os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
)
|
||||
|
||||
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"
|
||||
))
|
||||
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
|
||||
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('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):
|
||||
"""生成新的机器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']} {self.translator.get('reset.updating_sqlite')}...{Style.RESET_ALL}")
|
||||
|
||||
@@ -281,12 +620,13 @@ class MachineIDResetter:
|
||||
return False
|
||||
|
||||
def update_system_ids(self, new_ids):
|
||||
"""更新系统级别的ID"""
|
||||
"""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)
|
||||
|
||||
@@ -297,7 +637,7 @@ class MachineIDResetter:
|
||||
return False
|
||||
|
||||
def _update_windows_machine_guid(self):
|
||||
"""更新Windows MachineGuid"""
|
||||
"""Update Windows MachineGuid"""
|
||||
try:
|
||||
import winreg
|
||||
key = winreg.OpenKey(
|
||||
@@ -309,32 +649,71 @@ class MachineIDResetter:
|
||||
new_guid = str(uuid.uuid4())
|
||||
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
print("Windows MachineGuid updated successfully")
|
||||
except PermissionError:
|
||||
print("Permission denied: Run as administrator to update Windows MachineGuid")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Failed to update Windows MachineGuid: {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):
|
||||
"""更新macOS Platform UUID"""
|
||||
"""Update macOS Platform UUID"""
|
||||
try:
|
||||
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||
if os.path.exists(uuid_file):
|
||||
# 使用sudo来执行plutil命令
|
||||
# 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("macOS Platform UUID updated successfully")
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
|
||||
else:
|
||||
raise Exception("Failed to execute plutil command")
|
||||
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"Failed to update macOS Platform UUID: {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):
|
||||
"""重置机器ID并备份原文件"""
|
||||
"""Reset machine ID and backup original file"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
|
||||
|
||||
@@ -350,36 +729,40 @@ class MachineIDResetter:
|
||||
with open(self.db_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
|
||||
backup_path = self.db_path + ".bak"
|
||||
if not os.path.exists(backup_path):
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{self.db_path}.bak.{timestamp}"
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
|
||||
# 更新配置文件
|
||||
# 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)
|
||||
|
||||
# 更新SQLite数据库
|
||||
# Update SQLite database
|
||||
self.update_sqlite_db(new_ids)
|
||||
|
||||
# 更新系统ID
|
||||
# Update system IDs
|
||||
self.update_system_ids(new_ids)
|
||||
|
||||
# 检查 Cursor 版本并执行相应的操作
|
||||
|
||||
# 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']} 检测到 Cursor 版本 >= 0.45.0,正在修补 getMachineId...{Style.RESET_ALL}")
|
||||
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']} Cursor 版本 < 0.45.0,跳过 getMachineId 修补{Style.RESET_ALL}")
|
||||
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}")
|
||||
@@ -396,13 +779,54 @@ class MachineIDResetter:
|
||||
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):
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{machine_id_path}.backup.{timestamp}"
|
||||
try:
|
||||
shutil.copy2(machine_id_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
|
||||
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) # 正確傳遞 translator
|
||||
resetter = MachineIDResetter(translator) # Correctly pass translator
|
||||
resetter.reset_machine_ids()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
@@ -410,4 +834,4 @@ def run(translator=None):
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
run(main_translator)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 設置顏色主題
|
||||
# set color theme
|
||||
$Theme = @{
|
||||
Primary = 'Cyan'
|
||||
Success = 'Green'
|
||||
@@ -17,7 +17,7 @@ $Logo = @"
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
||||
"@
|
||||
|
||||
# 美化輸出函數
|
||||
# Beautiful Output Function
|
||||
function Write-Styled {
|
||||
param (
|
||||
[string]$Message,
|
||||
@@ -40,7 +40,7 @@ function Write-Styled {
|
||||
}
|
||||
}
|
||||
|
||||
# 獲取版本號函數
|
||||
# Get version number function
|
||||
function Get-LatestVersion {
|
||||
try {
|
||||
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest"
|
||||
@@ -54,28 +54,28 @@ function Get-LatestVersion {
|
||||
}
|
||||
}
|
||||
|
||||
# 顯示 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
|
||||
|
||||
# 主安裝函數
|
||||
# Main installation function
|
||||
function Install-CursorFreeVIP {
|
||||
Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download"
|
||||
|
||||
try {
|
||||
# 獲取最新版本
|
||||
# Get latest version
|
||||
Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update"
|
||||
$releaseInfo = Get-LatestVersion
|
||||
$version = $releaseInfo.Version
|
||||
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 "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error"
|
||||
@@ -86,22 +86,114 @@ function Install-CursorFreeVIP {
|
||||
throw "Cannot find target file"
|
||||
}
|
||||
|
||||
# 下載到Downloads文件夾
|
||||
# Check if Downloads folder already exists for the corresponding version
|
||||
$DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"
|
||||
$downloadPath = Join-Path $DownloadsPath "CursorFreeVIP.exe"
|
||||
$downloadPath = Join-Path $DownloadsPath "CursorFreeVIP_${version}_windows.exe"
|
||||
|
||||
Write-Styled "Downloading to Downloads folder..." -Color $Theme.Primary -Prefix "Download"
|
||||
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")
|
||||
$webClient.DownloadFile($asset.browser_download_url, $downloadPath)
|
||||
|
||||
# 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 "Download completed!" -Color $Theme.Success -Prefix "Complete"
|
||||
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 "Error"
|
||||
@@ -109,7 +201,7 @@ function Install-CursorFreeVIP {
|
||||
}
|
||||
}
|
||||
|
||||
# 執行安裝
|
||||
# Execute installation
|
||||
try {
|
||||
Install-CursorFreeVIP
|
||||
}
|
||||
@@ -120,4 +212,4 @@ catch {
|
||||
finally {
|
||||
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
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
|
||||
|
||||
814
totally_reset_cursor.py
Normal file
814
totally_reset_cursor.py
Normal file
@@ -0,0 +1,814 @@
|
||||
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
|
||||
import glob
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"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 system == "Linux":
|
||||
# Look for extracted AppImage directories - with usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
# Check current directory for extraction without home path prefix
|
||||
current_dir_paths = glob.glob("squashfs-root/usr/share/cursor/resources/app")
|
||||
|
||||
# Add all paths to the Linux paths list
|
||||
default_paths["Linux"].extend(extracted_usr_paths)
|
||||
default_paths["Linux"].extend(current_dir_paths)
|
||||
|
||||
# Print debug info for troubleshooting
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Available paths found:{Style.RESET_ALL}")
|
||||
for path in default_paths["Linux"]:
|
||||
if os.path.exists(path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {path} (exists){Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {path} (not found){Style.RESET_ALL}")
|
||||
|
||||
# If config doesn't exist, create it with default paths
|
||||
if not os.path.exists(config_file):
|
||||
for section in ['MacPaths', 'WindowsPaths', 'LinuxPaths']:
|
||||
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 == "Linux":
|
||||
# Look for extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
# Check current directory for extraction
|
||||
current_dir_paths = glob.glob("squashfs-root/usr/share/cursor/resources/app")
|
||||
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
paths_map["Linux"]["bases"].extend(current_dir_paths)
|
||||
|
||||
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)
|
||||
225
utils.py
Normal file
225
utils.py
Normal file
@@ -0,0 +1,225 @@
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import random
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user documents path"""
|
||||
if platform.system() == "Windows":
|
||||
return os.path.expanduser("~\\Documents")
|
||||
else:
|
||||
return os.path.expanduser("~/Documents")
|
||||
|
||||
def get_default_driver_path(browser_type='chrome'):
|
||||
"""Get default driver path based on browser type"""
|
||||
browser_type = browser_type.lower()
|
||||
if browser_type == 'chrome':
|
||||
return get_default_chrome_driver_path()
|
||||
elif browser_type == 'edge':
|
||||
return get_default_edge_driver_path()
|
||||
elif browser_type == 'firefox':
|
||||
return get_default_firefox_driver_path()
|
||||
elif browser_type == 'brave':
|
||||
# Brave 使用 Chrome 的 driver
|
||||
return get_default_chrome_driver_path()
|
||||
else:
|
||||
# Default to Chrome if browser type is unknown
|
||||
return get_default_chrome_driver_path()
|
||||
|
||||
def get_default_chrome_driver_path():
|
||||
"""Get default Chrome driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver")
|
||||
else:
|
||||
return "/usr/local/bin/chromedriver"
|
||||
|
||||
def get_default_edge_driver_path():
|
||||
"""Get default Edge driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver")
|
||||
else:
|
||||
return "/usr/local/bin/msedgedriver"
|
||||
|
||||
def get_default_firefox_driver_path():
|
||||
"""Get default Firefox driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver")
|
||||
else:
|
||||
return "/usr/local/bin/geckodriver"
|
||||
|
||||
def get_default_brave_driver_path():
|
||||
"""Get default Brave driver path (uses Chrome driver)"""
|
||||
# Brave 浏览器基于 Chromium,所以使用相同的 chromedriver
|
||||
return get_default_chrome_driver_path()
|
||||
|
||||
def get_default_browser_path(browser_type='chrome'):
|
||||
"""Get default browser executable path"""
|
||||
browser_type = browser_type.lower()
|
||||
|
||||
if sys.platform == "win32":
|
||||
if browser_type == 'chrome':
|
||||
# 尝试在 PATH 中找到 Chrome
|
||||
try:
|
||||
import shutil
|
||||
chrome_in_path = shutil.which("chrome")
|
||||
if chrome_in_path:
|
||||
return chrome_in_path
|
||||
except:
|
||||
pass
|
||||
# 使用默认路径
|
||||
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
|
||||
elif browser_type == 'edge':
|
||||
return r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
||||
elif browser_type == 'firefox':
|
||||
return r"C:\Program Files\Mozilla Firefox\firefox.exe"
|
||||
elif browser_type == 'opera':
|
||||
# 尝试多个可能的 Opera 路径
|
||||
opera_paths = [
|
||||
r"C:\Program Files\Opera\opera.exe",
|
||||
r"C:\Program Files (x86)\Opera\opera.exe",
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
|
||||
]
|
||||
for path in opera_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return opera_paths[0] # 返回第一个路径,即使它不存在
|
||||
elif browser_type == 'operagx':
|
||||
# 尝试多个可能的 Opera GX 路径
|
||||
operagx_paths = [
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
|
||||
r"C:\Program Files\Opera GX\opera.exe",
|
||||
r"C:\Program Files (x86)\Opera GX\opera.exe"
|
||||
]
|
||||
for path in operagx_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return operagx_paths[0] # 返回第一个路径,即使它不存在
|
||||
elif browser_type == 'brave':
|
||||
# Brave 浏览器的默认安装路径
|
||||
paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe')
|
||||
]
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return paths[0] # 返回第一个路径,即使它不存在
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
if browser_type == 'chrome':
|
||||
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
elif browser_type == 'edge':
|
||||
return "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
elif browser_type == 'firefox':
|
||||
return "/Applications/Firefox.app/Contents/MacOS/firefox"
|
||||
elif browser_type == 'brave':
|
||||
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
||||
elif browser_type == 'opera':
|
||||
return "/Applications/Opera.app/Contents/MacOS/Opera"
|
||||
elif browser_type == 'operagx':
|
||||
return "/Applications/Opera GX.app/Contents/MacOS/Opera"
|
||||
|
||||
else: # Linux
|
||||
if browser_type == 'chrome':
|
||||
# 尝试多种可能的名称
|
||||
chrome_names = ["google-chrome", "chrome", "chromium", "chromium-browser"]
|
||||
for name in chrome_names:
|
||||
try:
|
||||
import shutil
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return path
|
||||
except:
|
||||
pass
|
||||
return "/usr/bin/google-chrome"
|
||||
elif browser_type == 'edge':
|
||||
return "/usr/bin/microsoft-edge"
|
||||
elif browser_type == 'firefox':
|
||||
return "/usr/bin/firefox"
|
||||
elif browser_type == 'opera':
|
||||
return "/usr/bin/opera"
|
||||
elif browser_type == 'operagx':
|
||||
# 尝试常见的 Opera GX 路径
|
||||
operagx_names = ["opera-gx"]
|
||||
for name in operagx_names:
|
||||
try:
|
||||
import shutil
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return path
|
||||
except:
|
||||
pass
|
||||
return "/usr/bin/opera-gx"
|
||||
elif browser_type == 'brave':
|
||||
# 尝试常见的 Brave 路径
|
||||
brave_names = ["brave", "brave-browser"]
|
||||
for name in brave_names:
|
||||
try:
|
||||
import shutil
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return path
|
||||
except:
|
||||
pass
|
||||
return "/usr/bin/brave-browser"
|
||||
|
||||
# 如果找不到指定的浏览器类型,则返回 Chrome 的路径
|
||||
return get_default_browser_path('chrome')
|
||||
|
||||
def get_linux_cursor_path():
|
||||
"""Get Linux Cursor path"""
|
||||
possible_paths = [
|
||||
"/opt/Cursor/resources/app",
|
||||
"/usr/share/cursor/resources/app",
|
||||
"/opt/cursor-bin/resources/app",
|
||||
"/usr/lib/cursor/resources/app",
|
||||
os.path.expanduser("~/.local/share/cursor/resources/app")
|
||||
]
|
||||
|
||||
# return the first path that exists
|
||||
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])
|
||||
|
||||
def get_random_wait_time(config, timing_key):
|
||||
"""Get random wait time based on configuration timing settings
|
||||
|
||||
Args:
|
||||
config (dict): Configuration dictionary containing timing settings
|
||||
timing_key (str): Key to look up in the timing settings
|
||||
|
||||
Returns:
|
||||
float: Random wait time in seconds
|
||||
"""
|
||||
try:
|
||||
# Get timing value from config
|
||||
timing = config.get('Timing', {}).get(timing_key)
|
||||
if not timing:
|
||||
# Default to 0.5-1.5 seconds if timing not found
|
||||
return random.uniform(0.5, 1.5)
|
||||
|
||||
# Check if timing is a range (e.g., "0.5-1.5" or "0.5,1.5")
|
||||
if isinstance(timing, str):
|
||||
if '-' in timing:
|
||||
min_time, max_time = map(float, timing.split('-'))
|
||||
elif ',' in timing:
|
||||
min_time, max_time = map(float, timing.split(','))
|
||||
else:
|
||||
# Single value, use it as both min and max
|
||||
min_time = max_time = float(timing)
|
||||
else:
|
||||
# If timing is a number, use it as both min and max
|
||||
min_time = max_time = float(timing)
|
||||
|
||||
return random.uniform(min_time, max_time)
|
||||
|
||||
except (ValueError, TypeError, AttributeError):
|
||||
# Return default value if any error occurs
|
||||
return random.uniform(0.5, 1.5)
|
||||
Reference in New Issue
Block a user