Merge branch 'main' of https://github.com/canmi21/cursor into pr/597

This commit is contained in:
Pin Studios 2025-04-15 10:24:08 +08:00
commit 4757f9777a
12 changed files with 249 additions and 201 deletions

View File

@ -1,10 +1,11 @@
pkgbase = cursor-free-vip-git pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
pkgver = 1.9.03.2.g43a58db pkgver = 1.9.04.10.g5863891
pkgrel = 1 pkgrel = 1
url = https://github.com/yeongpin/cursor-free-vip url = https://github.com/yeongpin/cursor-free-vip
arch = x86_64 arch = x86_64
license = MIT license = MIT
license = Attribution-NonCommercial-NoDerivatives 4.0 International
makedepends = git makedepends = git
makedepends = python makedepends = python
makedepends = pyinstaller makedepends = pyinstaller
@ -13,6 +14,8 @@ pkgbase = cursor-free-vip-git
depends = cursor-bin depends = cursor-bin
provides = cursor-free-vip provides = cursor-free-vip
source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git 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 sha256sums = SKIP
pkgname = cursor-free-vip-git pkgname = cursor-free-vip-git

4
.env
View File

@ -1,2 +1,2 @@
version=1.9.03 version=1.9.05
VERSION=1.9.03 VERSION=1.9.05

View File

@ -153,7 +153,7 @@ jobs:
- name: Build in ARM64 Docker container - name: Build in ARM64 Docker container
run: | run: |
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c " docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.10-slim bash -c "
apt-get update && apt-get install -y build-essential apt-get update && apt-get install -y build-essential
pip install --upgrade pip pip install --upgrade pip
pip install pyinstaller pip install pyinstaller

View File

@ -1,6 +1,20 @@
# Change Log # Change Log
## v1.9.03 ## 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. 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 | 修復一些問題 1. Hotfix: Some Issues | 修復一些問題
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題 2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出 3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出

View File

@ -2,17 +2,17 @@
# Contributor: Canmi (Canmi21) # Contributor: Canmi (Canmi21)
pkgname=cursor-free-vip-git pkgname=cursor-free-vip-git
pkgver=1.9.03.2.g43a58db pkgver=1.9.04.10.g5863891
pkgrel=1 pkgrel=1
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit" pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64') arch=('x86_64')
url="https://github.com/yeongpin/cursor-free-vip" url="https://github.com/yeongpin/cursor-free-vip"
license=('MIT') license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
depends=('python' 'cursor-bin') depends=('python' 'cursor-bin')
makedepends=('git' 'python' 'pyinstaller' 'uv') makedepends=('git' 'python' 'pyinstaller' 'uv')
provides=('cursor-free-vip') provides=('cursor-free-vip')
source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git") 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') sha256sums=('SKIP' 'SKIP')
pkgver() { pkgver() {
cd "$srcdir/cursor-free-vip" cd "$srcdir/cursor-free-vip"
@ -28,5 +28,7 @@ build() {
} }
package() { 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" install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
} }

View File

@ -7,10 +7,10 @@
<p align="center"> <p align="center">
[![Release](https://img.shields.io/github/v/release/yeongpin/cursor-free-vip?style=flat-square&logo=github&color=blue)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Release](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/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/) [![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/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers) [![Stars](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/stars/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a1)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Downloads](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/downloads/yeongpin/cursor-free-vip/total)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
<a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a> <a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a>
</p> </p>

View File

@ -60,18 +60,17 @@ def setup_config(translator=None):
'Browser': { 'Browser': {
'default_browser': 'chrome', 'default_browser': 'chrome',
'chrome_path': get_default_browser_path('chrome'), 'chrome_path': get_default_browser_path('chrome'),
'edge_path': get_default_browser_path('edge'),
'firefox_path': get_default_browser_path('firefox'),
'brave_path': get_default_browser_path('brave'),
'chrome_driver_path': get_default_driver_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'), 'edge_driver_path': get_default_driver_path('edge'),
'firefox_path': get_default_browser_path('firefox'),
'firefox_driver_path': get_default_driver_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'), 'brave_driver_path': get_default_driver_path('brave'),
'opera_path': get_default_browser_path('opera'), 'opera_path': get_default_browser_path('opera'),
'opera_driver_path': get_default_driver_path('opera') 'opera_driver_path': get_default_driver_path('opera'),
}, 'operagx_path': get_default_browser_path('operagx'),
'Chrome': { 'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
'chromepath': get_default_browser_path('chrome')
}, },
'Turnstile': { 'Turnstile': {
'handle_turnstile_time': '2', 'handle_turnstile_time': '2',

View File

@ -79,7 +79,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}")
return True return True
except Exception as e: except Exception as e:

View File

@ -247,11 +247,11 @@ class CursorGoogleAccountDeleter(OAuthHandler):
except: except:
# Try direct JavaScript input as fallback # Try direct JavaScript input as fallback
try: try:
self.browser.run_js(f""" self.browser.run_js(r"""
arguments[0].value = "Delete"; arguments[0].value = "Delete";
const event = new Event('input', {{ bubbles: true }}); const event = new Event('input', { bubbles: true });
arguments[0].dispatchEvent(event); arguments[0].dispatchEvent(event);
const changeEvent = new Event('change', {{ bubbles: true }}); const changeEvent = new Event('change', { bubbles: true });
arguments[0].dispatchEvent(changeEvent); arguments[0].dispatchEvent(changeEvent);
""", delete_input) """, delete_input)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")

284
main.py
View File

@ -110,18 +110,24 @@ class Translator:
threadid = user32.GetWindowThreadProcessId(hwnd, 0) threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
# Map language ID to our language codes # Map language ID to our language codes using match-case
language_map = { match layout_id:
0x0409: 'en', # English case 0x0409:
0x0404: 'zh_tw', # Traditional Chinese return 'en' # English
0x0804: 'zh_cn', # Simplified Chinese case 0x0404:
0x0422: 'vi', # Vietnamese return 'zh_tw' # Traditional Chinese
0x0419: 'ru', # Russian case 0x0804:
0x0415: 'tr', # Turkish return 'zh_cn' # Simplified Chinese
0x0402: 'bg', # Bulgarian case 0x0422:
} return 'vi' # Vietnamese
case 0x0419:
return language_map.get(layout_id, 'en') return 'ru' # Russian
case 0x0415:
return 'tr' # Turkish
case 0x0402:
return 'bg' # Bulgarian
case _:
return 'en' # Default to English
except: except:
return self._detect_unix_language() return self._detect_unix_language()
@ -136,53 +142,56 @@ class Translator:
system_locale = system_locale.lower() system_locale = system_locale.lower()
# Map locale to our language codes # Map locale to our language codes using match-case
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'): match system_locale:
return 'zh_tw' case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
elif system_locale.startswith('zh_cn'): return 'zh_tw'
return 'zh_cn' case s if s.startswith('zh_cn'):
elif system_locale.startswith('en'): return 'zh_cn'
return 'en' case s if s.startswith('en'):
elif system_locale.startswith('vi'): return 'en'
return 'vi' case s if s.startswith('vi'):
elif system_locale.startswith('nl'): return 'vi'
return 'nl' case s if s.startswith('nl'):
elif system_locale.startswith('de'): return 'nl'
return 'de' case s if s.startswith('de'):
elif system_locale.startswith('fr'): return 'de'
return 'fr' case s if s.startswith('fr'):
elif system_locale.startswith('pt'): return 'fr'
return 'pt' case s if s.startswith('pt'):
elif system_locale.startswith('ru'): return 'pt'
return 'ru' case s if s.startswith('ru'):
elif system_locale.startswith('tr'): return 'ru'
return 'tr' case s if s.startswith('tr'):
elif system_locale.startswith('bg'): return 'tr'
return 'bg' case s if s.startswith('bg'):
# Try to get language from LANG environment variable as fallback return 'bg'
env_lang = os.getenv('LANG', '').lower() case _:
if 'tw' in env_lang or 'hk' in env_lang: # Try to get language from LANG environment variable as fallback
return 'zh_tw' env_lang = os.getenv('LANG', '').lower()
elif 'cn' in env_lang: match env_lang:
return 'zh_cn' case s if 'tw' in s or 'hk' in s:
elif 'vi' in env_lang: return 'zh_tw'
return 'vi' case s if 'cn' in s:
elif 'nl' in env_lang: return 'zh_cn'
return 'nl' case s if 'vi' in s:
elif 'de' in env_lang: return 'vi'
return 'de' case s if 'nl' in s:
elif 'fr' in env_lang: return 'nl'
return 'fr' case s if 'de' in s:
elif 'pt' in env_lang: return 'de'
return 'pt' case s if 'fr' in s:
elif 'ru' in env_lang: return 'fr'
return 'ru' case s if 'pt' in s:
elif 'tr' in env_lang: return 'pt'
return 'tr' case s if 'ru' in s:
elif 'bg' in env_lang: return 'ru'
return 'bg' case s if 'tr' in s:
return 'tr'
return 'en' case s if 'bg' in s:
return 'bg'
case _:
return 'en'
except: except:
return 'en' return 'en'
@ -566,87 +575,88 @@ def main():
choice_num = 17 choice_num = 17
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}") choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0": match choice:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}") case "0":
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}") print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
return print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
elif choice == "1": return
import reset_machine_manual case "1":
reset_machine_manual.run(translator) import reset_machine_manual
print_menu() reset_machine_manual.run(translator)
elif choice == "2": print_menu()
import cursor_register case "2":
cursor_register.main(translator) import cursor_register
print_menu() cursor_register.main(translator)
elif choice == "3": print_menu()
import cursor_register_google case "3":
cursor_register_google.main(translator) import cursor_register_google
print_menu() cursor_register_google.main(translator)
elif choice == "4": print_menu()
import cursor_register_github case "4":
cursor_register_github.main(translator) import cursor_register_github
print_menu() cursor_register_github.main(translator)
elif choice == "5": print_menu()
import cursor_register_manual case "5":
cursor_register_manual.main(translator) import cursor_register_manual
print_menu() cursor_register_manual.main(translator)
elif choice == "6": print_menu()
import github_cursor_register case "6":
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}") import github_cursor_register
# github_cursor_register.main(translator) print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
print_menu() # github_cursor_register.main(translator)
elif choice == "7": print_menu()
import quit_cursor case "7":
quit_cursor.quit_cursor(translator) import quit_cursor
print_menu() quit_cursor.quit_cursor(translator)
elif choice == "8": print_menu()
if select_language(): case "8":
if select_language():
print_menu()
continue
case "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
case "10":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
case "11":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
case "12":
from config import print_config
print_config(get_config(), translator)
print_menu()
case "13":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
case "14":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
case "15":
import bypass_version
bypass_version.main(translator)
print_menu()
case "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
case "17":
import bypass_token_limit
bypass_token_limit.run(translator)
print_menu()
case _:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu() print_menu()
continue
elif choice == "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
elif choice == "10":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
elif choice == "11":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
elif choice == "12":
from config import print_config
print_config(get_config(), translator)
print_menu()
elif choice == "13":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
elif choice == "14":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
elif choice == "15":
import bypass_version
bypass_version.main(translator)
print_menu()
elif choice == "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
elif choice == "17":
import bypass_token_limit
bypass_token_limit.run(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
except KeyboardInterrupt: except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}") print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}") print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return return
except Exception as e: except Exception as e:

View File

@ -1,3 +1,4 @@
# oauth_auth.py
import os import os
from colorama import Fore, Style, init from colorama import Fore, Style, init
import time import time
@ -30,7 +31,7 @@ class OAuthHandler:
def __init__(self, translator=None, auth_type=None): def __init__(self, translator=None, auth_type=None):
self.translator = translator self.translator = translator
self.config = get_config(translator) self.config = get_config(translator)
self.auth_type = auth_type # make sure the auth_type is not None self.auth_type = auth_type
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None self.browser = None
self.selected_profile = None self.selected_profile = None
@ -176,10 +177,15 @@ class OAuthHandler:
browser_path = self._get_browser_path() browser_path = self._get_browser_path()
if not browser_path: if not browser_path:
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" + error_msg = (
"- Windows: Google Chrome, Chromium\n" + f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}" +
"- macOS: Google Chrome, Chromium\n" + "\n" +
"- Linux: Google Chrome, Chromium, chromium-browser") f"{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, google-chrome-stable"
)
raise Exception(error_msg)
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
@ -239,7 +245,7 @@ class OAuthHandler:
browser_processes = { browser_processes = {
'chrome': { 'chrome': {
'win': ['chrome.exe', 'chromium.exe'], 'win': ['chrome.exe', 'chromium.exe'],
'linux': ['chrome', 'chromium', 'chromium-browser'], 'linux': ['chrome', 'chromium', 'chromium-browser', 'google-chrome-stable'],
'mac': ['Chrome', 'Chromium'] 'mac': ['Chrome', 'Chromium']
}, },
'brave': { 'brave': {
@ -305,7 +311,8 @@ class OAuthHandler:
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'), 'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'), 'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'),
'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'), 'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'),
'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable') 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'),
'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable')
} }
elif sys.platform == 'darwin': # macOS elif sys.platform == 'darwin': # macOS
user_data_dirs = { user_data_dirs = {
@ -313,7 +320,8 @@ class OAuthHandler:
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'), 'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'), 'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'),
'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'), 'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'),
'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera') 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
} }
else: # Linux else: # Linux
user_data_dirs = { user_data_dirs = {
@ -321,7 +329,8 @@ class OAuthHandler:
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'), 'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/.config/microsoft-edge'), 'edge': os.path.expanduser('~/.config/microsoft-edge'),
'firefox': os.path.expanduser('~/.mozilla/firefox'), 'firefox': os.path.expanduser('~/.mozilla/firefox'),
'opera': os.path.expanduser('~/.config/opera') 'opera': os.path.expanduser('~/.config/opera'),
'operagx': os.path.expanduser('~/.config/opera-gx')
} }
# 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的 # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
@ -420,7 +429,12 @@ class OAuthHandler:
elif browser_type == 'firefox': elif browser_type == 'firefox':
possible_paths = ['/usr/bin/firefox'] possible_paths = ['/usr/bin/firefox']
else: # 默认为 Chrome else: # 默认为 Chrome
possible_paths = ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser'] possible_paths = [
'/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable
'/usr/bin/google-chrome',
'/usr/bin/chromium',
'/usr/bin/chromium-browser'
]
# 检查每个可能的路径 # 检查每个可能的路径
for path in possible_paths: for path in possible_paths:
@ -428,7 +442,7 @@ class OAuthHandler:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}")
return path return path
# 如果找不到指定浏览器,则尝试使用Chrome # 如果找不到指定浏览器,则尝试使用 Chrome
if browser_type != 'chrome': if browser_type != 'chrome':
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}")
return self._get_chrome_path() return self._get_chrome_path()
@ -438,29 +452,6 @@ class OAuthHandler:
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
return None return None
def _get_chrome_path(self):
"""Fallback method to get Chrome path"""
try:
if os.name == 'nt': # Windows
possible_paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'Application', 'chrome.exe')
]
elif sys.platform == 'darwin': # macOS
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
else: # Linux
possible_paths = ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser']
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_chrome_at', path=path) if self.translator else f'Found Chrome at: {path}'}{Style.RESET_ALL}")
return path
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_chrome_path', error=str(e)) if self.translator else f'Error finding Chrome path: {e}'}{Style.RESET_ALL}")
return None
def _configure_browser_options(self, browser_path, user_data_dir, active_profile): def _configure_browser_options(self, browser_path, user_data_dir, active_profile):
"""Configure browser options based on platform""" """Configure browser options based on platform"""
@ -473,6 +464,7 @@ class OAuthHandler:
co.set_argument('--no-first-run') co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check') co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu') co.set_argument('--disable-gpu')
co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
# Platform-specific options # Platform-specific options
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
@ -955,7 +947,8 @@ class OAuthHandler:
value = cookie.get("value", "") value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator) token = get_token_from_cookie(value, self.translator)
except Exception as e: except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}") error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
elif name == "cursor_email": elif name == "cursor_email":
email = cookie.get("value") email = cookie.get("value")
@ -968,11 +961,13 @@ class OAuthHandler:
missing.append("email") missing.append("email")
if not token: if not token:
missing.append("token") missing.append("token")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}") error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None return False, None
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}") error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None return False, None
def _delete_current_account(self): def _delete_current_account(self):
@ -1014,7 +1009,8 @@ class OAuthHandler:
return True return True
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}") error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False return False
def main(auth_type, translator=None): def main(auth_type, translator=None):

View File

@ -84,14 +84,24 @@ def get_default_browser_path(browser_type='chrome'):
r"C:\Program Files\Opera\opera.exe", r"C:\Program Files\Opera\opera.exe",
r"C:\Program Files (x86)\Opera\opera.exe", r"C:\Program Files (x86)\Opera\opera.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
] ]
for path in opera_paths: for path in opera_paths:
if os.path.exists(path): if os.path.exists(path):
return path return path
return opera_paths[0] # 返回第一个路径,即使它不存在 return opera_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'operagx':
# 尝试多个可能的 Opera GX 路径
operagx_paths = [
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
r"C:\Program Files\Opera GX\opera.exe",
r"C:\Program Files (x86)\Opera GX\opera.exe"
]
for path in operagx_paths:
if os.path.exists(path):
return path
return operagx_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'brave': elif browser_type == 'brave':
# Brave 浏览器的默认安装路径 # Brave 浏览器的默认安装路径
paths = [ paths = [
@ -115,6 +125,8 @@ def get_default_browser_path(browser_type='chrome'):
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
elif browser_type == 'opera': elif browser_type == 'opera':
return "/Applications/Opera.app/Contents/MacOS/Opera" return "/Applications/Opera.app/Contents/MacOS/Opera"
elif browser_type == 'operagx':
return "/Applications/Opera GX.app/Contents/MacOS/Opera"
else: # Linux else: # Linux
if browser_type == 'chrome': if browser_type == 'chrome':
@ -135,6 +147,18 @@ def get_default_browser_path(browser_type='chrome'):
return "/usr/bin/firefox" return "/usr/bin/firefox"
elif browser_type == 'opera': elif browser_type == 'opera':
return "/usr/bin/opera" return "/usr/bin/opera"
elif browser_type == 'operagx':
# 尝试常见的 Opera GX 路径
operagx_names = ["opera-gx"]
for name in operagx_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/opera-gx"
elif browser_type == 'brave': elif browser_type == 'brave':
# 尝试常见的 Brave 路径 # 尝试常见的 Brave 路径
brave_names = ["brave", "brave-browser"] brave_names = ["brave", "brave-browser"]