``` ├── .SRCINFO ├── .env ├── .github/ ├── ISSUE_TEMPLATE/ ├── cn_bug_report.yml ├── cn_feature_request.yml ├── cn_question.yml ├── en_bug_report.yml ├── en_feature_request.yml ├── en_question.yml ├── workflows/ ├── build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── PKGBUILD ├── README.md ├── block_domain.txt ├── build.bat ├── build.mac.command ├── build.py ├── build.sh ├── build.spec ├── bypass_token_limit.py ├── bypass_version.py ├── check_user_authorized.py ├── config.py ├── cursor_acc_info.py ├── cursor_auth.py ├── cursor_register_manual.py ├── delete_cursor_google.py ├── disable_auto_update.py ├── email_tabs/ ├── email_tab_interface.py ├── tempmail_plus_tab.py ├── fill_missing_translations.py ├── get_user_token.py ├── images/ ├── cloudflare_2025-02-12_13-43-21.png ├── fix_2025-01-14_21-30-43.png ├── free_2025-01-14_14-59-15.png ├── locale_2025-01-15_13-40-08.png ├── logo.png ├── new107_2025-01-15_13-53-56.png ├── new_2025-02-27_10-42-44.png ├── new_2025-03-19_00-19-09.png ├── new_2025-03-22_19-53-10.png ├── pass_2025-02-08_21-48-36.png ├── paypal.png ├── pro_2025-01-11_00-50-40.png ├── pro_2025-01-11_00-51-07.png ├── pro_2025-01-11_16-24-03.png ├── pro_2025-01-11_22-33-09.gif ├── pro_2025-01-13_13-49-55.png ├── pro_2025-01-14_14-40-37.png ├── pro_2025-04-05_18-47-56.png ├── product_2025-04-16_10-40-21.png ├── pronew_2025-02-13_15-01-32.png ├── provi-code.jpg ├── what_2025-01-13_13-32-54.png ├── locales/ ├── ar.json ``` ## /.SRCINFO ```SRCINFO path="/.SRCINFO" pkgbase = cursor-free-vip-git pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit pkgver = 1.9.05 pkgrel = 1 url = https://github.com/yeongpin/cursor-free-vip arch = x86_64 license = MIT license = Attribution-NonCommercial-NoDerivatives 4.0 International makedepends = git makedepends = python makedepends = pyinstaller makedepends = uv depends = python depends = cursor-bin provides = cursor-free-vip source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git source = https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE sha256sums = SKIP sha256sums = SKIP pkgname = cursor-free-vip-git ``` ## /.env ```env path="/.env" version=1.11.02 VERSION=1.11.02 ``` ## /.github/ISSUE_TEMPLATE/cn_bug_report.yml ```yml path="/.github/ISSUE_TEMPLATE/cn_bug_report.yml" 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 ( 不是 Cursor AI 版本 ) 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: 任何能让我们对你所遇到的问题有更多了解的东西 ``` ## /.github/ISSUE_TEMPLATE/cn_feature_request.yml ```yml path="/.github/ISSUE_TEMPLATE/cn_feature_request.yml" 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 ``` ## /.github/ISSUE_TEMPLATE/cn_question.yml ```yml path="/.github/ISSUE_TEMPLATE/cn_question.yml" 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 ``` ## /.github/ISSUE_TEMPLATE/en_bug_report.yml ```yml path="/.github/ISSUE_TEMPLATE/en_bug_report.yml" 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 ( Not Cursor AI Version ) 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 ``` ## /.github/ISSUE_TEMPLATE/en_feature_request.yml ```yml path="/.github/ISSUE_TEMPLATE/en_feature_request.yml" 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 ``` ## /.github/ISSUE_TEMPLATE/en_question.yml ```yml path="/.github/ISSUE_TEMPLATE/en_question.yml" 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 ``` ## /.github/workflows/build.yml ```yml path="/.github/workflows/build.yml" name: Build Executables on: workflow_dispatch: inputs: use_env_version: description: 'Use version from .env file (yes/no)' required: true default: 'yes' version: description: 'Version number (only used if not using .env version)' required: false default: '' permissions: contents: write actions: write packages: write jobs: determine-version: runs-on: ubuntu-latest outputs: version: ${{ steps.set-version.outputs.version }} steps: - uses: actions/checkout@v2 - name: Get version from .env file id: env-version if: ${{ github.event.inputs.use_env_version == 'yes' }} run: | VERSION=$(grep "^version=" .env | cut -d'=' -f2) echo "ENV_VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Using version from .env file: $VERSION" - name: Use manual version id: manual-version if: ${{ github.event.inputs.use_env_version != 'yes' }} run: | echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT echo "Using manually entered version: ${{ github.event.inputs.version }}" - name: Set final version id: set-version run: | if [ "${{ github.event.inputs.use_env_version }}" == "yes" ]; then echo "version=${{ env.ENV_VERSION }}" >> $GITHUB_OUTPUT else echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT fi create-tag: needs: determine-version runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 # 获取所有标签 - name: Check if tag exists id: check_tag run: | if git ls-remote --tags origin | grep -q "refs/tags/v${{ needs.determine-version.outputs.version }}"; then echo "Tag v${{ needs.determine-version.outputs.version }} already exists, will use existing tag" echo "tag_exists=true" >> $GITHUB_OUTPUT else echo "Tag v${{ needs.determine-version.outputs.version }} does not exist, will create new tag" echo "tag_exists=false" >> $GITHUB_OUTPUT fi - name: Create Tag if not exists if: steps.check_tag.outputs.tag_exists == 'false' run: | git tag "v${{ needs.determine-version.outputs.version }}" git push origin "v${{ needs.determine-version.outputs.version }}" build-windows: needs: [determine-version, create-tag] runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Set version shell: bash run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Install dependencies run: | python -m pip install --upgrade pip pip install pyinstaller pip install -r requirements.txt - name: Build EXE run: | pyinstaller build.spec - name: Upload Windows artifact uses: actions/upload-artifact@v4 with: name: CursorFreeVIP_${{ env.VERSION }}_windows.exe path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe build-macos-arm64: needs: [determine-version, create-tag] runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Set version shell: bash run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Install dependencies run: | python -m pip install --upgrade pip pip install pyinstaller pip install -r requirements.txt - name: Build MacOS ARM executable run: | pyinstaller build.spec mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64" - name: Upload MacOS ARM artifact uses: actions/upload-artifact@v4 with: name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64 path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 build-linux-x64: needs: [determine-version, create-tag] runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Set version shell: bash run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Install dependencies run: | python -m pip install --upgrade pip pip install pyinstaller pip install -r requirements.txt - name: Build Linux x64 executable env: VERSION: ${{ env.VERSION }} run: | pyinstaller build.spec mv "dist/CursorFreeVIP_${{ env.VERSION }}_linux" "dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64" echo "Contents of dist directory:" ls -la dist/ - name: Upload Linux x64 artifact uses: actions/upload-artifact@v4 with: name: CursorFreeVIP_${{ env.VERSION }}_linux_x64 path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64 build-linux-arm64: needs: [determine-version, 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=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Build in ARM64 Docker container run: | docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.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: [determine-version, create-tag] runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Set version shell: bash run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Install dependencies run: | arch -x86_64 pip3 install --upgrade pip arch -x86_64 pip3 install pyinstaller arch -x86_64 pip3 install -r requirements.txt - name: Build MacOS Intel executable env: TARGET_ARCH: 'x86_64' VERSION: ${{ env.VERSION }} run: | arch -x86_64 python3 -m PyInstaller build.spec mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel" - name: Upload MacOS Intel artifact uses: actions/upload-artifact@v4 with: name: CursorFreeVIP_${{ env.VERSION }}_mac_intel path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel create-release: needs: [determine-version, build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel] runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@v2 - name: Get version shell: bash run: echo "VERSION=${{ needs.determine-version.outputs.version }}" >> $GITHUB_ENV - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Calculate SHA256 checksums run: | 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@v2 with: tag_name: v${{ env.VERSION }} body_path: release_notes.md files: | artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64 artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64 artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` ## /.gitignore ```gitignore path="/.gitignore" __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 ``` ## /CHANGELOG.md # Change Log ## v1.11.02 1. Fill: Missing Translations(ar, zh-cn, zh-tw, vi, nl, de, fr, pt, ru, tr, bg, es, ja, it) | 填補缺失的翻譯 2. Add: Japanese and Italian language support 3. Refactor: Account Generation with Faker and Update requirements.txt 4. Add: script to auto-translate missing keys in translation files | 增加 fill_missing_translations.py 自動翻譯缺失的翻譯鍵 5. Add: TempMailPlus Support, support temp email verification | 新增 TempMailPlus 配置,支持临时邮箱验证功能 6. Fix: Chrome user data directory permission problem on mac | 修復 Chrome 用戶數據目錄權限問題 on mac 7. Fix: Some Issues | 修復一些問題 ## v1.11.01 0. Must Update to this version to get full experience | 必須更新到此版本以獲取完整體驗 1. Restore: Some Main Code | 恢復一些主程式碼 2. Add: Arabic language | 增加阿拉伯語 3. Add: Language configuration saved setting | 增加語言配置保存設定 4. Add: Restore Machine ID from Backup | 增加從備份恢復機器ID 5. Add: Owned Website Check Version | 增加擁有網站檢查版本 6. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file 7. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path 8. Fix: Some Issues | 修復一些問題 ## v1.10.05 1. Remove block_domain.txt | 移除 block_domain.txt 2. Original Code In Github , If u afraid of virus, please clone the code and run locally | 原始碼在 Github 上,如果怕病毒,請複製原始碼並在本機運行 3. All Action using github workflow , not build myself , so i cant place virus in the file | 所有 Action 使用 github workflow ,不是我自己 build 的,所以我不會在文件中放置病毒 4. Fix: Some Issues | 修復一些問題 ## v1.10.04 1. Hotfix: Reset Process Error: cannot access local variable 'main_path' where it is not associated with a value on windows & macos | 修復在 Windows 和 macOS 上無法訪問局部變量 'main_path' 的問題 2. Fix: Some Issues | 修復一些問題 ## v1.10.03 1. Add: Manual Registration | 增加手動註冊 2. Only support your own Email | 只支持自己的Email 請勿使用Temp Email 註冊 註冊假賬號. 3. Fix: macOS 'bypass_version.py' get product_json_path from config_file | 修復 macOS 'bypass_version.py' 從 config_file 獲取 product_json_path 4. Fix: use cursor_path from config_file | 修復使用 cursor_path 從 config_file 5. Fix: Some Issues | 修復一些問題 ## v1.10.02 1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問 2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service 3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service 4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律 5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途 6. Use & Cherish | 切用且珍惜 7. Same as v1.10.01 | 與 v1.10.01 相同 8. Fix: reset machine ID no module name 'new_signup' | 修復機器 ID 重置 no module name 'new_signup' 9. Fix: Some Issues | 修復一些問題 ## v1.10.01 1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問 2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service 3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service 4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律 5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途 6. Use & Cherish | 切用且珍惜 7. Fix: Some Issues | 修復一些問題 ## v1.9.05 1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。 2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。 3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤 4. Sync AUR new version 1.9.04 | 同步 AUR 新版本 1.9.04 5. Fix: missing license install on pkgbuild @michaeldavis246611119 mention here | 修復 pkgbuild 中缺少授權安裝 @michaeldavis246611119 提到這裡 6. Fix: readme table | 修復 readme 表格 7. Fix: google-chrome package name problem, add "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 | 修復 google-chrome 包名稱問題,添加 "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 8. Fix: exception error log | 修復異常錯誤日誌 9. Fix: github oauth error [Bug]: #564 | 修復 github oauth 錯誤 [Bug]: #564 10. Fix: ChromiumOptions.arguments type error: list object has no attribute 'get' | 修復 ChromiumOptions.arguments 類型錯誤:list 對象沒有屬性 'get' 11. Fix: Some Issues | 修復一些問題 ## v1.9.04 1. Add: Opera GX Support | 添加 Opera GX 支持 2. Same as v1.9.03 | 與 v1.9.03 相同 3. Hotfix: Some Issues | 修復一些問題 4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題 5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出 6. Fix: Some Issues | 修復一些問題 ## v1.9.03[Skip & Merge to v1.9.04] 1. Hotfix: Some Issues | 修復一些問題 2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題 3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出 4. Fix: Some Issues | 修復一些問題 ## v1.9.02 1. Add: Bypass Token Limit | 添加繞過 Token 限制 2. Add: More Browser Support | 添加更多瀏覽器支持 3. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題 4. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox 5. Add config manual browser path | 添加配置手動選擇遊覽器路徑 5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇 6. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出 7. Fix: Config File Path | 修復配置文件路徑 8. Fix: window user permission | 修復 window 用戶權限 9. Fix: Some Issues | 修復一些問題 ## v1.9.01 1. Add: Bypass Token Limit | 添加繞過 Token 限制 2. Add: More Browser Support | 添加更多瀏覽器支持 3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox 4. Add config manual browser path | 添加配置手動選擇遊覽器路徑 5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇 6. Fix: Some Issues | 修復一些問題 ## v1.8.10 1. Add: Check User Authorized | 添加檢查用戶授權 2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base' 3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統 4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑 5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性 6. Fix: Some Issues | 修復一些問題 ## v1.8.09 1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查 2. Add:Bypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5) 3. Add: Force Update Config | 添加強制更新配置 4. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持 5. Fix: Reset break | 修復重置中斷 4. Fix: Some Issues | 修復一些問題 ## v1.8.08 1. Add: Force Update Config | 添加強制更新配置 2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持 3. Fix: Google Auth & Github Auth JWT Problem | 修復 Google Auth & Github Auth JWT 問題 4. Fix: Totally reset import & import * raw options problem | 修復 totally reset import & import * raw 選項問題 5. Fix: reset.file_not_found problem | 修復 reset.file_not_found 問題 6. Outdated: Bypass Cursor Version Check | 過期:繞過 Cursor 版本檢查 7. Document: i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx"); | 文檔:i.header.set("x-cursor-config-version", "UUID4-xxxxxx-xxxxxx-xxxxxx-xxxxxx"); 8. Fix: Some Issues | 修復一些問題 ## v1.8.07 1. Add: Bypass Cursor Version Check | 添加繞過 Cursor 版本檢查 2. Add: Multilanguage support for bypass | 添加繞過的多語言支持 3. MSG: Free & free trial accounts can no longer use chat with premium models on Cursor Version 0.45 or less. Please upgrade to Pro or use Cursor Version 0.46 or later. Install Cursor at https://www.cursor.com/downloads or update from within the editor. 4. Fix: Some Issues | 修復一些問題 ## v1.8.06 1. Add: Google Account Deletion Feature | 添加 Google 账号删除功能 2. Update: Menu with new account deletion option | 更新菜单添加账号删除选项 3. Add: Multilanguage support for account deletion | 添加账号删除功能的多语言支持 4. Fix: Improve usage limits check and tuple index error | 修复使用限制检查和元组索引错误 5. Fix: bug in disable cursor auto update | 修复禁用 Cursor 自动更新的错误 6. Fix: Linux-appimage | 修复 Linux-appimage 问题 7. Add: Support for custom Cursor installation paths on Windows | 添加 Windows 系统下自定义 Cursor 安装路径支持 8. Add: Chrome profile selection feature | 添加 Chrome 配置文件选择功能 9. Fix: improve account usage limit detection | 修復賬號檢測 10. Fix: For custom Chrome Installations | 修復自定義chrome遊覽器安裝 ## v1.8.05 1. Fix: Linux Path Not Found | 修復linuxpath問題 2. Add: support for detecting both 150/150 and 50/50 usage limits | 添加偵測50 或者150的使用量 3. Improve: usage parsing and validation | 檢測使用量 ## v1.8.04 1. Update totally_reset_cursor.py | 更新 totally_reset_cursor.py 2. Fix: improve Linux Chrome visibility and root user handling | 修復 Linux Chrome 可見性以及 root 用戶處理 3. Fix: improve Linux path handling and fix permission issues | 修復 Linux 路徑處理以及修復權限問題 4. Fix: Some Issues | 修復一些問題 ## v1.8.03 1. Fix: Improve Linux path handling and add case-insensitive Cursor directory detection | 修復Linux系統路徑錯誤以及添加cursor 路徑偵測 2. Fix: Some Issues | 修復一些問題 ## 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 選項 ## v1.2.02 1. Add PBlock | 增加 PBlock 2. Remove uBlock0.chromium | 移除 uBlock0.chromium 3. Optimize the logic of the script | 優化腳本邏輯 4. Optimize Size | 優化大小 ## v1.2.01 1. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題 2. Change to automatic registration account | 全面改為自動註冊賬號 3. Change Mail Site | 改變郵箱網站 4. Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題 ## v1.1.01

free

1. Hot Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題 2. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題 3. Remake signup logic | 重做註冊邏輯 ## v1.0.10 1. Hot Fix Mac Chrome Problem | 修復 Mac Chrome 問題 2. Fix Linux Start Donet Problem | 修復 Linux 啟動開發者問題 ## v1.0.9

free

1. Fixed New 0.45.x Version Reset Machine | 修復新 0.45 版本重置機器 2. Fix Locale Language | 修復多語言 3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊 4. Add Remake main.js | 重做 main.js ## v1.0.8 1. Fix New 0.45 Version Reset Machine | 修復新 0.45 版本重置機器 2. Fix Locale Language | 修復多語言 3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊 ## v1.0.7 - HotFix 1. Fix Reset Machine | 修復重置機器 2. Fix Locale Language | 修復多語言 ## v1.0.7 1. Add Locale Language Support | 增加多語言支持

locale

## v1.0.6 1. Add Quit Cursor Option | 增加退出 Cursor 選項 2. Add Recaptcha Path Patch | 增加 Recaptcha 路徑修復 3. Fix Admin Permission | 修復管理員權限問題 4. Remove all need admin permission | 移除所有需要管理員權限 ## v1.0.5 - HotFix 1. Fix: Mac Browser Control | 修復 Mac 瀏覽器控制問題 2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題 3. Add Linux Support | 增加 Linux 支持

fix

## v1.0.5 1. Remove MachineID | 移除機器碼 ID 2. Change to automatic registration account | 全面改為自動註冊賬號 3. Use your own exclusive new account | 使用自己獨享的新賬號 4. Fully automatic reset machine configuration | 全面自動化重置機器配置

Why

## v1.0.4 1. Fix: Cursor's configuration | 修復 Cursor 的配置問題 2. Fix Cloud Lame | 修復雲端慢速模式 ## v1.0.3 1. Fix: Cursor's configuration | 修復 Cursor 的配置問題 2. Add Manual Reset Machine | 增加手動重置機器 3. Add CDN Cloud Control WatchDog | 增加 CDN 雲端控制 WatchDog 4. Add Mac OS Support | 增加 Mac OS 支持 5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊

Why

## v1.0.2 1. Fix: Some known issues | 修復了一些已知問題 2. Add cloud control device code | 增加雲端控制設備碼 3. Cloud reset device code | 雲端重置設備碼 4. Remove official WatchDog monitoring | 移除官方 WatchDog 監控 5. Remove Proxy official prompt | 移除 Proxy 官方提示 6. Fix: Too Many Computer | 修復 Too Many Computer 問題 7. Fix Billing Issue | 修復計費問題 8. Fix: Cursor's configuration | 修復 Cursor 的配置問題 9. Fix cursor-slow mode | 修復 cursor-slow 模式 ## v1.0.1 1. Fix: Reset machine ID | 修復了重置機器 ID 的問題 2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題 3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為 pro 會員的問題 4. Fix: Real-time send Token request | 修復了 實時發送 Token 請求的問題 5. Fix: Reset Cursor's configuration | 修復了 重置 Cursor 的配置的問題 ## v1.0 1. Preview Image | 預覽圖

Cursor Pro Logo

Cursor Pro Logo

2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下 MachineID 聯繫作者

Cursor Pro Logo

## /LICENSE.md # Attribution-NonCommercial-NoDerivatives 4.0 International > *Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.* > > ### Using Creative Commons Public Licenses > > Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. > > * __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). > > * __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). ## Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. ### Section 1 – Definitions. a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. ### Section 2 – Scope. a. ___License grant.___ 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and B. produce and reproduce, but not Share, Adapted Material for NonCommercial purposes only. 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. __Term.__ The term of this Public License is specified in Section 6(a). 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 5. __Downstream recipients.__ A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. ___Other rights.___ 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. ### Section 3 – License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. ___Attribution.___ 1. If You Share the Licensed Material, You must: A. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. ### Section 4 – Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only and provided You do not Share Adapted Material; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. ### Section 5 – Disclaimer of Warranties and Limitation of Liability. a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. ### Section 6 – Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. ### Section 7 – Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. ### Section 8 – Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. > Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. > > Creative Commons may be contacted at [creativecommons.org](http://creativecommons.org). ## /PKGBUILD ``` path="/PKGBUILD" # Maintainer: Canmi21 <9997200@qq.com> # Contributor: Canmi (Canmi21) pkgname=cursor-free-vip-git pkgver=1.9.05 pkgrel=1 pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit" arch=('x86_64') url="https://github.com/yeongpin/cursor-free-vip" license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International') depends=('python' 'cursor-bin') makedepends=('git' 'python' 'pyinstaller' 'uv') provides=('cursor-free-vip') source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git" "https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE") sha256sums=('SKIP' 'SKIP') pkgver() { cd "$srcdir/cursor-free-vip" git describe --tags --always | sed 's/^v//;s/-/./g' } build() { cd "$srcdir/cursor-free-vip" uv venv .venv source .venv/bin/activate uv pip install -r requirements.txt pyinstaller --clean --noconfirm --onefile main.py --name cursor-free-vip } package() { install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license" install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license" install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip" } ``` ## /README.md # ➤ Cursor Free VIP

Cursor Pro Logo

[![Release](https://img.shields.io/endpoint?url=https://api.pinstudios.net/api/badges/release/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/) [![Stars](https://img.shields.io/endpoint?url=https://api.pinstudios.net/api/badges/stars/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/stargazers) [![Downloads](https://img.shields.io/endpoint?url=https://api.pinstudios.net/api/badges/downloads/yeongpin/cursor-free-vip/total)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee [Ask DeepWiki.com](https://deepwiki.com/yeongpin/cursor-free-vip)

yeongpin%2Fcursor-free-vip | Trendshift
Buy Me a Coffee

Support Latest 0.49.x Version | 支持最新 0.49.x 版本

This tool is for educational purposes, currently the repo does not violate any laws. Please support the original project. This tool will not generate any fake email accounts and OAuth access. Supports Windows, macOS and Linux. For optimal performance, run with privileges and always stay up to date. 這是一款用於學習和研究的工具,目前 repo 沒有違反任何法律。請支持原作者。 這款工具不會生成任何假的電子郵件帳戶和 OAuth 訪問。 支持 Windows、macOS 和 Linux。 對於最佳性能,請以管理員身份運行並始終保持最新。

new

## 🔄 Change Log | 更新日志 [Watch Change Log | 查看更新日志](CHANGELOG.md) ## ✨ Features | 功能特點 * Support Windows macOS and Linux systems
支持 Windows、macOS 和 Linux 系統
* Reset Cursor's configuration
重置 Cursor 的配置
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)
多語言支持(英文、简体中文、繁體中文、越南語)
## 💻 System Support | 系統支持 | Operating System | Architecture | Supported | |------------------|-------------------|-----------| | Windows | x64, x86 | ✅ | | macOS | Intel, Apple Silicon | ✅ | | Linux | x64, x86, ARM64 | ✅ | ## 👀 How to use | 如何使用
⭐ Auto Run Script | 腳本自動化運行 ### **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 ``` ### **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 ```
If you want to stop the script, please press Ctrl+C
要停止腳本,請按 Ctrl+C ## ❗ Note | 注意事項 📝 Config | 文件配置 `Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
⭐ Config | 文件配置 ``` [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 ```
* Use administrator privileges to run the script
請使用管理員身份運行腳本 * Confirm that Cursor is closed before running the script
請確保在運行腳本前已經關閉 Cursor
* This tool is only for learning and research purposes
此工具僅供學習和研究使用
* Please comply with the relevant software usage terms when using this tool
使用本工具時請遵守相關軟件使用條款 ## 🚨 Common Issues | 常見問題 | 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 | |:--------------------------------------------------:|:------------------------------------------------:| | 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!

## 📩 Disclaimer | 免責聲明 本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。
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 | 請我喝杯咖啡
buy_me_a_coffee
buy_me_a_coffee
## ⭐ Star History | 星星數
[![Star History Chart](https://api.star-history.com/svg?repos=yeongpin/cursor-free-vip&type=Date)](https://star-history.com/#yeongpin/cursor-free-vip&Date)
## 📝 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. ## /block_domain.txt 0-mail.com 10minemail.com 1secmail.com 20minutemail.com 2925.com 2prong.com 33mail.com abusemail.de afrobacon.com anonbox.net anonymbox.com antichef.com bareed.ws begemail.com boun.cr brefmail.com burnermail.io byom.de chammy.info cloud-mail.top cocovpn.com cool.fr.nf corhash.net crazymailing.com cuvox.de dayrep.com deadaddress.com discard.email dispostable.com drewzen.com dudmail.com dugmail.com emailondeck.com emailtemporario.com.br ephemail.net fakeinbox.com fakeinbox.org fakemailgenerator.com famamail.com fastmailbox.net filzmail.com fizmail.com getairmail.com getnada.com givmail.com guerrillamail.com gustr.com harakirimail.com hottempmail.com ikomail.com inboxbear.com inboxkitten.com incognitomail.org indigobook.com jetable.org kaspop.com letthemeatspam.com linshiyouxiang.net luxusmail.org mail-temp.com mail1a.de mailbucket.org mailcatch.com maildrop.cc mailexpire.com mailhazard.com mailimate.com mailin8r.com mailinator.com mailme.lv mailnesia.com mailnull.com mailpull.com mailsac.com mailshou.com mailtemp.net mailzilla.org meltmail.com mintemail.com moakt.com mohmal.com my10minutemail.com mycleaninbox.net mytrashmail.com no-spam.ws nomail.pw nospamfor.us notmailinator.com nowmymail.com oakon.com objectmail.com ofanda.com openmailbox.org owlpic.com pastryofistanbul.com privacyroot.com pusmail.com questtechsystems.com raleigh-construction.com rcpt.at safemail.link sendspamhere.com sharklasers.com shortmail.net solerbe.net spam4.me spamavert.com spambog.com spamdecoy.net spamex.com spamfree24.org spamgourmet.com spamhereplease.com spaml.com spamslicer.com spamsphere.com spamtroll.net teihu.com temp-mail.org tempmail.net tempmailaddress.com temporaryemail.net throwawayemail.com tmail.ws trash-mail.com trash2009.com trashdevil.com trashmail.com trashmail.de trbvn.com wegwerfadresse.org yepmail.net yopmail.com zippymail.info ## /build.bat ```bat path="/build.bat" @echo off 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 ⚠️ 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 ( goto RUN_BUILD ) ) ) :RUN_BUILD echo ℹ️ Starting virtual environment... call venv\Scripts\activate.bat if errorlevel 1 ( echo ❌ Failed to start virtual environment pause exit /b 1 ) :: Check and install missing dependencies echo ℹ️ Checking dependencies... for /f "tokens=1" %%i in (requirements.txt) do ( pip show %%i >nul 2>&1 || ( echo ℹ️ Installing %%i... pip install %%i ) ) echo ℹ️ Starting build... python build.py if errorlevel 1 ( echo ❌ Build failed pause exit /b 1 ) echo ✅ Completed! pause ``` ## /build.mac.command ```command path="/build.mac.command" #!/bin/bash export PYTHONWARNINGS=ignore::SyntaxWarning:DrissionPage # Get script directory cd "$(dirname "$0")" echo "Creating virtual environment..." # Check if virtual environment exists if [ ! -d "venv" ]; then python3 -m venv venv if [ $? -ne 0 ]; then echo "Failed to create virtual environment!" exit 1 fi fi # Activate virtual environment source venv/bin/activate # Install dependencies echo "Installing dependencies..." python -m pip install --upgrade pip pip install -r requirements.txt # Run build script echo "Starting build process..." python build.py # Keep window open echo "Build completed!" echo "Press any key to exit..." read -n 1 ``` ## /build.py ```py path="/build.py" import warnings import os import platform import subprocess import time import threading import shutil from logo import print_logo from dotenv import load_dotenv # Ignore specific warnings warnings.filterwarnings("ignore", category=SyntaxWarning) class LoadingAnimation: def __init__(self): self.is_running = False self.animation_thread = None def start(self, message="Building"): self.is_running = True self.animation_thread = threading.Thread(target=self._animate, args=(message,)) self.animation_thread.start() def stop(self): self.is_running = False if self.animation_thread: self.animation_thread.join() print("\r" + " " * 70 + "\r", end="", flush=True) def _animate(self, message): animation = "|/-\\" idx = 0 while self.is_running: print(f"\r{message} {animation[idx % len(animation)]}", end="", flush=True) idx += 1 time.sleep(0.1) def progress_bar(progress, total, prefix="", length=50): filled = int(length * progress // total) bar = "█" * filled + "░" * (length - filled) percent = f"{100 * progress / total:.1f}" print(f"\r{prefix} |{bar}| {percent}% Complete", end="", flush=True) if progress == total: print() def simulate_progress(message, duration=1.0, steps=20): print(f"\033[94m{message}\033[0m") for i in range(steps + 1): time.sleep(duration / steps) progress_bar(i, steps, prefix="Progress:", length=40) def build(): # Clean screen os.system("cls" if platform.system().lower() == "windows" else "clear") # Display logo print_logo() # 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📦 Building version: v{version}\033[0m") try: simulate_progress("Preparing build environment...", 0.5) loading = LoadingAnimation() loading.start("Building in progress") # Set output name based on system type system = platform.system().lower() if system == "windows": os_type = "windows" ext = ".exe" elif system == "linux": os_type = "linux" ext = "" else: # Darwin os_type = "mac" ext = "" 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}') os.system(build_command) loading.stop() if os.path.exists(output_path): print(f"\n\033[92m✅ Build completed!") print(f"📦 Executable file located: {output_path}\033[0m") else: 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❌ Build process error: {str(e)}\033[0m") return False return True if __name__ == "__main__": build() ``` ## /build.sh ```sh path="/build.sh" #!/bin/bash # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # 检查并安装必要的依赖 check_dependencies() { echo -e "${YELLOW}Checking system dependencies...${NC}" # 检查是否为 Ubuntu/Debian if [ -f /etc/debian_version ]; then # 检查并安装必要的包 PACKAGES="python3 python3-pip python3-venv" for pkg in $PACKAGES; do if ! dpkg -l | grep -q "^ii $pkg "; then echo -e "${YELLOW}Installing $pkg...${NC}" sudo apt-get update sudo apt-get install -y $pkg fi done else echo -e "${RED}Unsupported system, please install python3, pip3 and python3-venv manually${NC}" exit 1 fi } # 创建并激活虚拟环境 setup_venv() { echo -e "${GREEN}Creating virtual environment...${NC}" python3 -m venv venv echo -e "${GREEN}Starting virtual environment...${NC}" . ./venv/bin/activate || source ./venv/bin/activate } # 安装依赖 install_dependencies() { echo -e "${GREEN}Installing dependencies...${NC}" python3 -m pip install --upgrade pip pip3 install -r requirements.txt } # 构建程序 build_program() { echo -e "${GREEN}Starting build...${NC}" python3 build.py } # 清理 cleanup() { echo -e "${GREEN}Cleaning virtual environment...${NC}" deactivate 2>/dev/null || true rm -rf venv } # 主程序 main() { # 检查依赖 check_dependencies # 设置虚拟环境 setup_venv # 安装依赖 install_dependencies # 构建 build_program # 清理 cleanup echo -e "${GREEN}Completed!${NC}" echo "Press any key to exit..." # 使用兼容的方式读取输入 if [ "$(uname)" = "Linux" ]; then read dummy else read -n 1 fi } # 运行主程序 main ``` ## /build.spec ```spec path="/build.spec" # -*- mode: python ; coding: utf-8 -*- import os import platform from dotenv import load_dotenv # 加載環境變量獲取版本號 load_dotenv() version = os.getenv('VERSION', '1.0.0') # 根据系统类型设置输出名称 system = platform.system().lower() if system == "windows": os_type = "windows" elif system == "linux": os_type = "linux" else: # Darwin os_type = "mac" output_name = f"CursorFreeVIP_{version}_{os_type}" a = Analysis( ['main.py'], pathex=[], binaries=[], datas=[ ('locales', 'locales'), ('quit_cursor.py', '.'), ('utils.py', '.'), ('.env', '.') ], hiddenimports=[ 'quit_cursor', 'utils' ], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], noarchive=False, ) pyz = PYZ(a.pure) target_arch = os.environ.get('TARGET_ARCH', None) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name=output_name, # 使用动态生成的名称 debug=False, bootloader_ignore_signals=False, strip=False, upx=False, upx_exclude=[], runtime_tmpdir=None, console=True, disable_windowed_traceback=False, argv_emulation=True, # 对非Mac平台无影响 target_arch=target_arch, # 仅在需要时通过环境变量指定 codesign_identity=None, entitlements_file=None, icon=None ) ``` ## /bypass_token_limit.py ```py path="/bypass_token_limit.py" import os import shutil import platform import tempfile import glob from colorama import Fore, Style, init import configparser import sys from config import get_config from datetime import datetime # Initialize colorama init() # Define emoji constants EMOJI = { "FILE": "📄", "BACKUP": "💾", "SUCCESS": "✅", "ERROR": "❌", "INFO": "ℹ️", "RESET": "🔄", "WARNING": "⚠️", } def get_user_documents_path(): """Get user Documents folder path""" if sys.platform == "win32": return os.path.join(os.path.expanduser("~"), "Documents") elif sys.platform == "darwin": return os.path.join(os.path.expanduser("~"), "Documents") else: # Linux # Get actual user's home directory sudo_user = os.environ.get('SUDO_USER') if sudo_user: return os.path.join("/home", sudo_user, "Documents") return os.path.join(os.path.expanduser("~"), "Documents") def get_workbench_cursor_path(translator=None) -> str: """Get Cursor workbench.desktop.main.js path""" system = platform.system() # Read configuration config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip") config_file = os.path.join(config_dir, "config.ini") config = configparser.ConfigParser() if os.path.exists(config_file): config.read(config_file) paths_map = { "Darwin": { # macOS "base": "/Applications/Cursor.app/Contents/Resources/app", "main": "out/vs/workbench/workbench.desktop.main.js" }, "Windows": { "main": "out\\vs\\workbench\\workbench.desktop.main.js" }, "Linux": { "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"], "main": "out/vs/workbench/workbench.desktop.main.js" } } if system == "Linux": # Add extracted AppImage with correct usr structure extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app")) paths_map["Linux"]["bases"].extend(extracted_usr_paths) if system not in paths_map: raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}") if system == "Linux": for base in paths_map["Linux"]["bases"]: main_path = os.path.join(base, paths_map["Linux"]["main"]) print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}") if os.path.exists(main_path): return main_path if system == "Windows": base_path = config.get('WindowsPaths', 'cursor_path') elif system == "Darwin": base_path = paths_map[system]["base"] if config.has_section('MacPaths') and config.has_option('MacPaths', 'cursor_path'): base_path = config.get('MacPaths', 'cursor_path') else: # Linux # For Linux, we've already checked all bases in the loop above # If we're here, it means none of the bases worked, so we'll use the first one base_path = paths_map[system]["bases"][0] if config.has_section('LinuxPaths') and config.has_option('LinuxPaths', 'cursor_path'): base_path = config.get('LinuxPaths', 'cursor_path') main_path = os.path.join(base_path, paths_map[system]["main"]) if not os.path.exists(main_path): raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}") return main_path def modify_workbench_js(file_path: str, translator=None) -> bool: """ Modify file content """ try: # Save original file permissions original_stat = os.stat(file_path) original_mode = original_stat.st_mode original_uid = original_stat.st_uid original_gid = original_stat.st_gid # Create temporary file with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file: # Read original content with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file: content = main_file.read() patterns = { # 通用按钮替换模式 r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)', # Windows/Linux r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)', # Mac 通用按钮替换模式 r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)': r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)', # Badge 替换 r'
Pro Trial': r'
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("
You are currently signed in with .");': r'var DWr=ne("
You are currently signed in with .

Pro

");', # 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) ``` ## /bypass_version.py ```py path="/bypass_version.py" import os import json import shutil import platform import configparser import time from colorama import Fore, Style, init import sys import traceback from utils import get_user_documents_path # Initialize colorama init() # Define emoji constants EMOJI = { 'INFO': 'ℹ️', 'SUCCESS': '✅', 'ERROR': '❌', 'WARNING': '⚠️', 'FILE': '📄', 'BACKUP': '💾', 'RESET': '🔄', 'VERSION': '🏷️' } def get_product_json_path(translator=None): """Get Cursor product.json path""" system = platform.system() # Read configuration config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip") config_file = os.path.join(config_dir, "config.ini") config = configparser.ConfigParser() if os.path.exists(config_file): config.read(config_file) if system == "Windows": localappdata = os.environ.get("LOCALAPPDATA") if not localappdata: raise OSError(translator.get('bypass.localappdata_not_found') if translator else "LOCALAPPDATA environment variable not found") product_json_path = os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json") # Check if path exists in config if 'WindowsPaths' in config and 'cursor_path' in config['WindowsPaths']: cursor_path = config.get('WindowsPaths', 'cursor_path') product_json_path = os.path.join(cursor_path, "product.json") elif system == "Darwin": # macOS product_json_path = "/Applications/Cursor.app/Contents/Resources/app/product.json" if config.has_section('MacPaths') and config.has_option('MacPaths', 'product_json_path'): product_json_path = config.get('MacPaths', 'product_json_path') elif system == "Linux": # Try multiple common paths possible_paths = [ "/opt/Cursor/resources/app/product.json", "/usr/share/cursor/resources/app/product.json", "/usr/lib/cursor/app/product.json" ] # Add extracted AppImage paths extracted_usr_paths = os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app/product.json") if os.path.exists(extracted_usr_paths): possible_paths.append(extracted_usr_paths) for path in possible_paths: if os.path.exists(path): product_json_path = path break else: raise OSError(translator.get('bypass.product_json_not_found') if translator else "product.json not found in common Linux paths") else: raise OSError(translator.get('bypass.unsupported_os', system=system) if translator else f"Unsupported operating system: {system}") if not os.path.exists(product_json_path): raise OSError(translator.get('bypass.file_not_found', path=product_json_path) if translator else f"File not found: {product_json_path}") return product_json_path def compare_versions(version1, version2): """Compare two version strings""" v1_parts = [int(x) for x in version1.split('.')] v2_parts = [int(x) for x in version2.split('.')] for i in range(max(len(v1_parts), len(v2_parts))): v1 = v1_parts[i] if i < len(v1_parts) else 0 v2 = v2_parts[i] if i < len(v2_parts) else 0 if v1 < v2: return -1 elif v1 > v2: return 1 return 0 def bypass_version(translator=None): """Bypass Cursor version check by modifying product.json""" try: print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('bypass.starting') if translator else 'Starting Cursor version bypass...'}{Style.RESET_ALL}") # Get product.json path product_json_path = get_product_json_path(translator) print(f"{Fore.CYAN}{EMOJI['FILE']} {translator.get('bypass.found_product_json', path=product_json_path) if translator else f'Found product.json: {product_json_path}'}{Style.RESET_ALL}") # Check file permissions if not os.access(product_json_path, os.W_OK): print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.no_write_permission', path=product_json_path) if translator else f'No write permission for file: {product_json_path}'}{Style.RESET_ALL}") return False # Read product.json try: with open(product_json_path, "r", encoding="utf-8") as f: product_data = json.load(f) except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.read_failed', error=str(e)) if translator else f'Failed to read product.json: {str(e)}'}{Style.RESET_ALL}") return False # Get current version current_version = product_data.get("version", "0.0.0") print(f"{Fore.CYAN}{EMOJI['VERSION']} {translator.get('bypass.current_version', version=current_version) if translator else f'Current version: {current_version}'}{Style.RESET_ALL}") # Check if version needs to be modified if compare_versions(current_version, "0.46.0") < 0: # Create backup timestamp = time.strftime("%Y%m%d%H%M%S") backup_path = f"{product_json_path}.{timestamp}" shutil.copy2(product_json_path, backup_path) print(f"{Fore.GREEN}{EMOJI['BACKUP']} {translator.get('bypass.backup_created', path=backup_path) if translator else f'Backup created: {backup_path}'}{Style.RESET_ALL}") # Modify version new_version = "0.48.7" product_data["version"] = new_version # Save modified product.json try: with open(product_json_path, "w", encoding="utf-8") as f: json.dump(product_data, f, indent=2) print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('bypass.version_updated', old=current_version, new=new_version) if translator else f'Version updated from {current_version} to {new_version}'}{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.write_failed', error=str(e)) if translator else f'Failed to write product.json: {str(e)}'}{Style.RESET_ALL}") return False else: print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.no_update_needed', version=current_version) if translator else f'No update needed. Current version {current_version} is already >= 0.46.0'}{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('bypass.bypass_failed', error=str(e)) if translator else f'Version bypass failed: {str(e)}'}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('bypass.stack_trace') if translator else 'Stack trace'}: {traceback.format_exc()}{Style.RESET_ALL}") return False def main(translator=None): """Main function""" return bypass_version(translator) if __name__ == "__main__": main() ``` ## /check_user_authorized.py ```py path="/check_user_authorized.py" 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() ``` ## /config.py ```py path="/config.py" 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 }, 'Language': { 'current_language': '', # Set by local system detection if empty 'fallback_language': 'en', 'auto_update_languages': 'True', 'language_cache_dir': os.path.join(config_dir, "language_cache") } } # 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 "" } # Add tempmail_plus configuration default_config['TempMailPlus'] = { 'enabled': 'false', 'email': '', 'epin': '' } # 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 ``` ## /cursor_acc_info.py ```py path="/cursor_acc_info.py" 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() ``` ## /cursor_auth.py ```py path="/cursor_auth.py" import sqlite3 import os import sys from colorama import Fore, Style, init from config import get_config # Initialize colorama init() # Define emoji and color constants EMOJI = { 'DB': '🗄️', 'UPDATE': '🔄', 'SUCCESS': '✅', 'ERROR': '❌', 'WARN': '⚠️', 'INFO': 'ℹ️', 'FILE': '📄', 'KEY': '🔐' } class CursorAuth: def __init__(self, translator=None): self.translator = translator # Get configuration config = get_config(translator) if not config: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}") sys.exit(1) # Get path based on operating system try: if sys.platform == "win32": # Windows if not config.has_section('WindowsPaths'): raise ValueError("Windows paths not configured") self.db_path = config.get('WindowsPaths', 'sqlite_path') elif sys.platform == 'linux': # Linux if not config.has_section('LinuxPaths'): raise ValueError("Linux paths not configured") self.db_path = config.get('LinuxPaths', 'sqlite_path') elif sys.platform == 'darwin': # macOS if not config.has_section('MacPaths'): raise ValueError("macOS paths not configured") self.db_path = config.get('MacPaths', 'sqlite_path') else: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}") sys.exit(1) # Verify if the path exists if not os.path.exists(os.path.dirname(self.db_path)): raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}") except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}") sys.exit(1) # Check if the database file exists if not os.path.exists(self.db_path): print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path) if self.translator else f'Database not found: {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') if self.translator else 'Database 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') if self.translator else '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)) if self.translator else f'Database connection error: {str(e)}'}{Style.RESET_ALL}") return def update_auth(self, email=None, access_token=None, refresh_token=None, auth_type="Auth_0"): 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() cursor.execute(''' CREATE TABLE IF NOT EXISTS ItemTable ( key TEXT PRIMARY KEY, value TEXT ) ''') conn.commit() if sys.platform != "win32": 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') if self.translator else '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_type)) 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)) # 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(""" INSERT INTO ItemTable (key, value) VALUES (?, ?) """, (key, value)) else: cursor.execute(""" UPDATE ItemTable SET value = ? WHERE key = ? """, (value, key)) print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('auth.updating_pair') if self.translator else 'Updating key-value pair:'} {key.split('/')[-1]}...{Style.RESET_ALL}") cursor.execute("COMMIT") print(f"{EMOJI['SUCCESS']} {Fore.GREEN}{self.translator.get('auth.database_updated_successfully') if self.translator else 'Database updated successfully'}{Style.RESET_ALL}") return True except Exception as e: cursor.execute("ROLLBACK") raise e except sqlite3.Error as e: print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.database_error', error=str(e)) if self.translator else f'Database error: {str(e)}'}{Style.RESET_ALL}") return False except Exception as e: print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.an_error_occurred', error=str(e)) if self.translator else f'An error occurred: {str(e)}'}{Style.RESET_ALL}") return False finally: if conn: conn.close() print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed') if self.translator else 'Database connection closed'}{Style.RESET_ALL}") ``` ## /cursor_register_manual.py ```py path="/cursor_register_manual.py" import os from colorama import Fore, Style, init import time import random from faker import Faker from cursor_auth import CursorAuth from reset_machine_manual import MachineIDResetter from get_user_token import get_token_from_cookie from config import get_config os.environ["PYTHONVERBOSE"] = "0" os.environ["PYINSTALLER_VERBOSE"] = "0" # Initialize colorama init() # Define emoji constants EMOJI = { 'START': '🚀', 'FORM': '📝', 'VERIFY': '🔄', 'PASSWORD': '🔑', 'CODE': '📱', 'DONE': '✨', 'ERROR': '❌', 'WAIT': '⏳', 'SUCCESS': '✅', 'MAIL': '📧', 'KEY': '🔐', 'UPDATE': '🔄', 'INFO': 'ℹ️' } class CursorRegistration: def __init__(self, translator=None): self.translator = translator # Set to display mode os.environ['BROWSER_HEADLESS'] = 'False' self.browser = None self.controller = None self.sign_up_url = "https://authenticator.cursor.sh/sign-up" self.settings_url = "https://www.cursor.com/settings" self.email_address = None self.signup_tab = None self.email_tab = None # initialize Faker instance self.faker = Faker() # generate account information self.password = self._generate_password() self.first_name = self.faker.first_name() self.last_name = self.faker.last_name() # modify the first letter of the first name(keep the original function) new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") self.first_name = new_first_letter + self.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 password""" return self.faker.password(length=length, special_chars=True, digits=True, upper_case=True, lower_case=True) def setup_email(self): """Setup Email""" try: print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}") self.email_address = input().strip() if '@' not in self.email_address: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}") return False print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}") return False def get_verification_code(self): """Manually Get Verification Code""" try: print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}") code = input().strip() if not code.isdigit() or len(code) != 6: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}") return None return code except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}") return None def register_cursor(self): """Register Cursor""" browser_tab = None try: print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}") # Check if tempmail_plus is enabled config = get_config(self.translator) email_tab = None if config and config.has_section('TempMailPlus'): if config.getboolean('TempMailPlus', 'enabled'): email = config.get('TempMailPlus', 'email') epin = config.get('TempMailPlus', 'epin') if email and epin: from email_tabs.tempmail_plus_tab import TempMailPlusTab email_tab = TempMailPlusTab(email, epin) print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.using_tempmail_plus')}{Style.RESET_ALL}") # Use new_signup.py directly for registration from new_signup import main as new_signup_main # Execute new registration process, passing translator result, browser_tab = new_signup_main( email=self.email_address, password=self.password, first_name=self.first_name, last_name=self.last_name, email_tab=email_tab, # Pass email_tab if tempmail_plus is enabled controller=self, # Pass self instead of self.controller translator=self.translator ) if result: # Use the returned browser instance to get account information self.signup_tab = browser_tab # Save browser instance success = self._get_account_info() # Close browser after getting information if browser_tab: try: browser_tab.quit() except: pass return success return False except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}") return False finally: # Ensure browser is closed in any case if browser_tab: try: browser_tab.quit() except: pass def _get_account_info(self): """Get Account Information and Token""" try: self.signup_tab.get(self.settings_url) time.sleep(2) usage_selector = ( "css:div.col-span-2 > div > div > div > div > " "div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > " "span.font-mono.text-sm\\/\\[0\\.875rem\\]" ) usage_ele = self.signup_tab.ele(usage_selector) total_usage = "未知" if usage_ele: total_usage = usage_ele.text.split("/")[-1].strip() print(f"Total Usage: {total_usage}\n") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}") max_attempts = 30 retry_interval = 2 attempts = 0 while attempts < max_attempts: try: cookies = self.signup_tab.cookies() for cookie in cookies: if cookie.get("name") == "WorkosCursorSessionToken": token = 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 attempts += 1 if attempts < max_attempts: print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}") time.sleep(retry_interval) else: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}") except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}") attempts += 1 if attempts < max_attempts: print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}") time.sleep(retry_interval) return False except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}") return False def _save_account_info(self, token, total_usage): """Save Account Information to File""" try: # Update authentication information first print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}") if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token, auth_type="Auth_0"): print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}") else: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}") # Reset machine ID print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}") resetter = MachineIDResetter(self.translator) # Create instance with translator if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly raise Exception("Failed to reset machine ID") # Save account information to file with open('cursor_accounts.txt', 'a', encoding='utf-8') as f: f.write(f"\n{'='*50}\n") f.write(f"Email: {self.email_address}\n") f.write(f"Password: {self.password}\n") f.write(f"Token: {token}\n") f.write(f"Usage Limit: {total_usage}\n") f.write(f"{'='*50}\n") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}") return False def start(self): """Start Registration Process""" try: if self.setup_email(): if self.register_cursor(): print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}") return True return False finally: # Close email tab if hasattr(self, 'temp_email'): try: self.temp_email.close() except: pass def update_cursor_auth(self, email=None, access_token=None, refresh_token=None, auth_type="Auth_0"): """Convenient function to update Cursor authentication information""" auth_manager = CursorAuth(translator=self.translator) return auth_manager.update_auth(email, access_token, refresh_token, auth_type) def main(translator=None): """Main function to be called from main.py""" print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") registration = CursorRegistration(translator) registration.start() print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...") if __name__ == "__main__": from main import translator as main_translator main(main_translator) ``` ## /delete_cursor_google.py ```py path="/delete_cursor_google.py" 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() ``` ## /disable_auto_update.py ```py path="/disable_auto_update.py" 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) ``` ## /email_tabs/email_tab_interface.py ```py path="/email_tabs/email_tab_interface.py" from abc import ABC, abstractmethod class EmailTabInterface(ABC): """Email tab interface for handling email verification""" @abstractmethod def refresh_inbox(self) -> None: """Refresh the email inbox""" pass @abstractmethod def check_for_cursor_email(self) -> bool: """Check if there is a verification email from Cursor Returns: bool: True if verification email exists, False otherwise """ pass @abstractmethod def get_verification_code(self) -> str: """Get the verification code from the email Returns: str: The verification code if found, empty string otherwise """ pass ``` ## /email_tabs/tempmail_plus_tab.py ```py path="/email_tabs/tempmail_plus_tab.py" import requests import re from typing import Optional from .email_tab_interface import EmailTabInterface class TempMailPlusTab(EmailTabInterface): """Implementation of EmailTabInterface for tempmail.plus""" def __init__(self, email: str, epin: str): """Initialize TempMailPlusTab Args: email: The email address to check epin: The epin token for authentication """ self.email = email self.epin = epin self.base_url = "https://tempmail.plus/api" self.headers = { 'accept': 'application/json', 'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6', 'cache-control': 'no-cache', 'pragma': 'no-cache', 'referer': 'https://tempmail.plus/zh/', 'sec-ch-ua': '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', 'x-requested-with': 'XMLHttpRequest' } self.cookies = {'email': email} self._cached_mail_id = None # 缓存mail_id def refresh_inbox(self) -> None: """Refresh the email inbox""" pass def check_for_cursor_email(self) -> bool: """Check if there is a verification email from Cursor Returns: bool: True if verification email exists, False otherwise """ try: params = { 'email': self.email, 'epin': self.epin } response = requests.get( f"{self.base_url}/mails", params=params, headers=self.headers, cookies=self.cookies ) response.raise_for_status() data = response.json() if data.get('result') and data.get('mail_list'): for mail in data['mail_list']: if 'cursor.sh' in mail.get('from_mail', '') and mail.get('is_new') == True: self._cached_mail_id = mail.get('mail_id') # 缓存mail_id return True return False except Exception as e: print(f"检查Cursor邮件失败: {str(e)}") return False def get_verification_code(self) -> str: """Get the verification code from the email Returns: str: The verification code if found, empty string otherwise """ try: # 如果没有缓存的mail_id,先检查是否有新邮件 if not self._cached_mail_id: if not self.check_for_cursor_email(): return "" # 使用缓存的mail_id获取邮件内容 params = { 'email': self.email, 'epin': self.epin } response = requests.get( f"{self.base_url}/mails/{self._cached_mail_id}", params=params, headers=self.headers, cookies=self.cookies ) response.raise_for_status() data = response.json() if not data.get('result'): return "" # Extract verification code from text content using regex text = data.get('text', '') match = re.search(r'\n\n(\d{6})\n\n', text) if match: return match.group(1) return "" except Exception as e: print(f"获取验证码失败: {str(e)}") return "" ``` ## /fill_missing_translations.py ```py path="/fill_missing_translations.py" """ Compares two JSON translation files in /locales (e.g., en.json and ar.json). Finds keys missing in the target file, translates their values using Google Translate API (googletrans 4.0.2), and inserts the translations. Runs in parallel for speed and creates a backup of the target file. """ import json import sys import os from pathlib import Path import re from concurrent.futures import ThreadPoolExecutor, as_completed from colorama import init, Fore, Style import time import shutil import asyncio # Import googletrans with error handling try: from googletrans import Translator as GoogleTranslator GOOGLETRANS_AVAILABLE = True print(f"{Fore.GREEN}Using googletrans for translation.{Style.RESET_ALL}") except ImportError: GOOGLETRANS_AVAILABLE = False print(f"{Fore.YELLOW}googletrans library not found. Will use web scraping fallback method.{Style.RESET_ALL}") print(f"{Fore.YELLOW}To install googletrans: pip install googletrans==4.0.2{Style.RESET_ALL}") import requests init(autoreset=True) # Language code mapping to Google Translate language codes LANGUAGE_MAPPING = { "zh_cn": "zh-CN", # Simplified Chinese "zh_tw": "zh-TW", # Traditional Chinese "ar": "ar", # Arabic "bg": "bg", # Bulgarian "de": "de", # German "en": "en", # English "es": "es", # Spanish "fr": "fr", # French "it": "it", # Italian "ja": "ja", # Japanese "ko": "ko", # Korean "nl": "nl", # Dutch "pt": "pt", # Portuguese "ru": "ru", # Russian "tr": "tr", # Turkish "vi": "vi", # Vietnamese # Add more mappings as needed } # Recursively get all keys in the JSON as dot-separated paths def get_keys(d, prefix=''): keys = set() for k, v in d.items(): full_key = f"{prefix}.{k}" if prefix else k if isinstance(v, dict): keys |= get_keys(v, full_key) else: keys.add(full_key) return keys # Get value from nested dict by dot-separated path def get_by_path(d, path): for p in path.split('.'): d = d[p] return d # Set value in nested dict by dot-separated path def set_by_path(d, path, value): parts = path.split('.') for p in parts[:-1]: if p not in d: d[p] = {} d = d[p] d[parts[-1]] = value # Get Google Translate language code from file name def get_google_lang_code(file_lang): # Remove .json extension if present if file_lang.endswith('.json'): file_lang = file_lang[:-5] # Return mapped language code or the original if not in mapping return LANGUAGE_MAPPING.get(file_lang, file_lang) # Translate text using Google Translate API if available, otherwise fallback to web scraping def translate(text, source, target): # Map language codes to Google Translate format source_lang = get_google_lang_code(source) target_lang = get_google_lang_code(target) print(f"{Fore.CYAN}Translating from {source_lang} to {target_lang}{Style.RESET_ALL}") if GOOGLETRANS_AVAILABLE: try: # Use synchronous web scraping instead of async googletrans return translate_web_scraping(text, source_lang, target_lang) except Exception as e: print(Fore.YELLOW + f"Translation error: {e}. Trying alternative method.") return translate_web_scraping(text, source_lang, target_lang) else: return translate_web_scraping(text, source_lang, target_lang) # Fallback translation method using web scraping def translate_web_scraping(text, source, target): try: import requests # 使用更可靠的 Google 翻译 API URL url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl={source}&tl={target}&dt=t&q={requests.utils.quote(text)}" 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"} response = requests.get(url, headers=headers, timeout=10) if response.status_code == 200: # 解析 JSON 响应 result = response.json() # 提取翻译结果 translated_text = '' for sentence in result[0]: if len(sentence) > 0: translated_text += sentence[0] if translated_text: return translated_text else: print(Fore.RED + f"Translation not found for: {text}") return text else: print(Fore.RED + f"Request failed with status code {response.status_code} for: {text}") return text except Exception as e: print(Fore.RED + f"Web scraping translation error: {e}") return text # Process a single language file def process_language(en_filename, other_filename, create_backup=None): # Always use the /locales directory en_path = Path("locales") / en_filename other_path = Path("locales") / other_filename # Infer language code from filename (before .json) en_lang = Path(en_filename).stem other_lang = Path(other_filename).stem print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}Processing: {other_filename} (Translating to {get_google_lang_code(other_lang)}){Style.RESET_ALL}") print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}Reading source file: {en_path}{Style.RESET_ALL}") with open(en_path, encoding='utf-8') as f: en = json.load(f) print(f"{Fore.CYAN}Reading target file: {other_path}{Style.RESET_ALL}") try: with open(other_path, encoding='utf-8') as f: other = json.load(f) except FileNotFoundError: # If target file doesn't exist, create an empty one print(f"{Fore.YELLOW}Target file not found. Creating a new file.{Style.RESET_ALL}") other = {} except json.JSONDecodeError: # If target file is invalid JSON, create an empty one print(f"{Fore.YELLOW}Target file contains invalid JSON. Creating a new file.{Style.RESET_ALL}") other = {} en_keys = get_keys(en) other_keys = get_keys(other) missing = en_keys - other_keys print(f"{Fore.YELLOW}Found {len(missing)} missing keys{Style.RESET_ALL}") if not missing: print(f"{Fore.GREEN}No missing keys found. Translation is complete!{Style.RESET_ALL}") return True # Parallel translation using ThreadPoolExecutor with ThreadPoolExecutor(max_workers=3) as executor: # Further reduced workers for googletrans 4.0.2 future_to_key = { executor.submit(translate, get_by_path(en, key), en_lang, other_lang): key for key in missing } completed = 0 total = len(missing) for future in as_completed(future_to_key): key = future_to_key[future] value = get_by_path(en, key) try: translated = future.result() completed += 1 print(f"{Fore.CYAN}[{completed}/{total}] Translated [{key}]: '{value}' -> " + Fore.MAGENTA + f"'{translated}'") except Exception as exc: print(f"{Fore.RED}Error translating {key}: {exc}") translated = value set_by_path(other, key, translated) # Ask about backup if not specified if create_backup is None and os.path.exists(other_path): while True: backup_choice = input(f"{Fore.CYAN}Create backup file? (y/N): {Style.RESET_ALL}").lower() if backup_choice in ['y', 'yes']: create_backup = True break elif backup_choice in ['', 'n', 'no']: create_backup = False break else: print(f"{Fore.RED}Invalid choice. Please enter 'y' or 'n'.{Style.RESET_ALL}") # Create backup if requested and file exists if create_backup and os.path.exists(other_path): backup_path = other_path.with_suffix('.bak.json') shutil.copy2(other_path, backup_path) print(f"{Fore.GREEN}Backup created at {backup_path}{Style.RESET_ALL}") # Save the updated file with open(other_path, 'w', encoding='utf-8') as f: json.dump(other, f, ensure_ascii=False, indent=4) print(f"{Fore.GREEN}File updated: {other_path}{Style.RESET_ALL}") return True # Main function with interactive menu def main(): # Check if locales directory exists locales_dir = Path("locales") if not locales_dir.exists(): print(f"{Fore.YELLOW}Creating 'locales' directory...{Style.RESET_ALL}") locales_dir.mkdir(parents=True) # Get all JSON files in locales directory (excluding backup files) json_files = [f for f in os.listdir(locales_dir) if f.endswith('.json') and not f.endswith('.bak.json')] # Check if en.json exists (source file) if 'en.json' not in json_files: print(f"{Fore.RED}Error: 'en.json' not found in locales directory. This file is required as the source for translations.{Style.RESET_ALL}") return False # Get all target language files (excluding en.json) target_files = [f for f in json_files if f != 'en.json'] # Add option to create a new language file available_languages = list(LANGUAGE_MAPPING.keys()) if 'en' in available_languages: available_languages.remove('en') # Remove English as it's the source # Filter out languages that already have files existing_lang_codes = [f.split('.')[0] for f in target_files] available_languages = [lang for lang in available_languages if lang not in existing_lang_codes] # Display menu print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}Translation Tool - Select target language to update{Style.RESET_ALL}") print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.GREEN}0{Style.RESET_ALL}. Translate all existing language files") # List existing language files for i, file in enumerate(target_files, 1): lang_code = file.split('.')[0] google_lang = get_google_lang_code(lang_code) print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_code} ({google_lang})") # Option to create a new language file (only if there are available languages) next_option = len(target_files) + 1 if available_languages: print(f"\n{Fore.CYAN}Create new language file:{Style.RESET_ALL}") print(f"{Fore.GREEN}{next_option}{Style.RESET_ALL}. Create a new language file") max_choice = next_option else: max_choice = len(target_files) # Get user choice while True: try: choice = input(f"\n{Fore.CYAN}Enter your choice (0-{max_choice}): {Style.RESET_ALL}") if choice.strip() == '': print(f"{Fore.RED}Please enter a number.{Style.RESET_ALL}") continue choice = int(choice) if choice < 0 or choice > max_choice: print(f"{Fore.RED}Invalid choice. Please enter a number between 0 and {max_choice}.{Style.RESET_ALL}") continue break except ValueError: print(f"{Fore.RED}Invalid input. Please enter a number.{Style.RESET_ALL}") # Ask about backup for all files create_backup = None if choice == 0: while True: backup_choice = input(f"{Fore.CYAN}Create backup files? (y/N): {Style.RESET_ALL}").lower() if backup_choice in ['y', 'yes']: create_backup = True break elif backup_choice in ['', 'n', 'no']: create_backup = False break else: print(f"{Fore.RED}Invalid choice. Please enter 'y' or 'n'.{Style.RESET_ALL}") # Process selected language(s) if choice == 0: print(f"{Fore.CYAN}Translating all existing languages...{Style.RESET_ALL}") success_count = 0 for target_file in target_files: try: if process_language('en.json', target_file, create_backup): success_count += 1 except Exception as e: print(f"{Fore.RED}Error processing {target_file}: {str(e)}{Style.RESET_ALL}") print(f"\n{Fore.GREEN}Translation completed for {success_count} out of {len(target_files)} languages.{Style.RESET_ALL}") elif available_languages and choice == next_option: # Create a new language file print(f"\n{Fore.CYAN}Available languages:{Style.RESET_ALL}") for i, lang in enumerate(available_languages): google_lang = get_google_lang_code(lang) print(f"{Fore.GREEN}{i+1}{Style.RESET_ALL}. {lang} ({google_lang})") while True: try: lang_choice = input(f"\n{Fore.CYAN}Select language (1-{len(available_languages)}): {Style.RESET_ALL}") lang_choice = int(lang_choice) if lang_choice < 1 or lang_choice > len(available_languages): print(f"{Fore.RED}Invalid choice. Please enter a number between 1 and {len(available_languages)}.{Style.RESET_ALL}") continue selected_lang = available_languages[lang_choice-1] new_file = f"{selected_lang}.json" if new_file in json_files: print(f"{Fore.YELLOW}Warning: {new_file} already exists. It will be updated with missing translations.{Style.RESET_ALL}") process_language('en.json', new_file) print(f"\n{Fore.GREEN}Created and translated {new_file}.{Style.RESET_ALL}") break except ValueError: print(f"{Fore.RED}Invalid input. Please enter a number.{Style.RESET_ALL}") else: target_file = target_files[choice - 1] try: process_language('en.json', target_file) print(f"\n{Fore.GREEN}Translation completed for {target_file}.{Style.RESET_ALL}") except Exception as e: print(f"{Fore.RED}Error processing {target_file}: {str(e)}{Style.RESET_ALL}") return True if __name__ == "__main__": # If arguments are provided, use the old method if len(sys.argv) == 3: process_language(sys.argv[1], sys.argv[2]) else: # Otherwise, show the interactive menu main() ``` ## /get_user_token.py ```py path="/get_user_token.py" 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 ``` ## /images/cloudflare_2025-02-12_13-43-21.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/cloudflare_2025-02-12_13-43-21.png ## /images/fix_2025-01-14_21-30-43.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/fix_2025-01-14_21-30-43.png ## /images/free_2025-01-14_14-59-15.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/free_2025-01-14_14-59-15.png ## /images/locale_2025-01-15_13-40-08.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/locale_2025-01-15_13-40-08.png ## /images/logo.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/logo.png ## /images/new107_2025-01-15_13-53-56.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/new107_2025-01-15_13-53-56.png ## /images/new_2025-02-27_10-42-44.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/new_2025-02-27_10-42-44.png ## /images/new_2025-03-19_00-19-09.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/new_2025-03-19_00-19-09.png ## /images/new_2025-03-22_19-53-10.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/new_2025-03-22_19-53-10.png ## /images/pass_2025-02-08_21-48-36.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pass_2025-02-08_21-48-36.png ## /images/paypal.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/paypal.png ## /images/pro_2025-01-11_00-50-40.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-11_00-50-40.png ## /images/pro_2025-01-11_00-51-07.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-11_00-51-07.png ## /images/pro_2025-01-11_16-24-03.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-11_16-24-03.png ## /images/pro_2025-01-11_22-33-09.gif Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-11_22-33-09.gif ## /images/pro_2025-01-13_13-49-55.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-13_13-49-55.png ## /images/pro_2025-01-14_14-40-37.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-01-14_14-40-37.png ## /images/pro_2025-04-05_18-47-56.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pro_2025-04-05_18-47-56.png ## /images/product_2025-04-16_10-40-21.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/product_2025-04-16_10-40-21.png ## /images/pronew_2025-02-13_15-01-32.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/pronew_2025-02-13_15-01-32.png ## /images/provi-code.jpg Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/provi-code.jpg ## /images/what_2025-01-13_13-32-54.png Binary file available at https://raw.githubusercontent.com/yeongpin/cursor-free-vip/refs/heads/main/images/what_2025-01-13_13-32-54.png The content has been capped at 50000 tokens, and files over NaN bytes have been omitted. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.