From 9f81f949574002aa084c19405929201efaeec52d Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 11:15:31 +0800
Subject: [PATCH 01/15] rm: duplicate content
---
README.md | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 1ca8d16..774b7dc 100644
--- a/README.md
+++ b/README.md
@@ -39,11 +39,11 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create

-##### If you don't have browser, you can download it from
-[Google Chrome](https://www.google.com/intl/en_pk/chrome/) or [Opera](https://www.opera.com/download) or [Edge](https://www.microsoft.com/en-us/edge) or [Firefox](https://www.mozilla.org/en-US/firefox/new/) or [Brave](https://www.brave.com/download/)
+##### If you don't have browser, you can download it from
-##### 如果沒有瀏覽器,可以從
-[Google Chrome](https://www.google.com/intl/en_pk/chrome/) 或 [Opera](https://www.opera.com/download) 或 [Edge](https://www.microsoft.com/en-us/edge) 或 [Firefox](https://www.mozilla.org/en-US/firefox/new/) 或 [Brave](https://www.brave.com/download/) 下載
+##### 如果沒有瀏覽器,可以從这里下載
+
+[Google Chrome](https://www.google.com/intl/en_pk/chrome/) or [Opera](https://www.opera.com/download) or [Edge](https://www.microsoft.com/en-us/edge) or [Firefox](https://www.mozilla.org/en-US/firefox/new/) or [Brave](https://www.brave.com/download/)
@@ -71,11 +71,11 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
## 💻 System Support | 系統支持
-| Windows | x64 | ✅ | macOS | Intel | ✅ |
-|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
-| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
-| Linux | x64 | ✅ | Linux | x86 | ✅ |
-| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
+| Operating System | Architecture | Supported |
+|------------------|-------------------|-----------|
+| Windows | x64, x86 | ✅ |
+| macOS | Intel, Apple Silicon | ✅ |
+| Linux | x64, x86, ARM64 | ✅ |
## 👀 How to use | 如何使用
From d3c6bf227bddb5d0006cb21f001acd85416061e2 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:00:58 +0800
Subject: [PATCH 02/15] sync: aur version
---
PKGBUILD | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/PKGBUILD b/PKGBUILD
index 97c5636..7bd7734 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,7 +2,7 @@
# Contributor: Canmi (Canmi21)
pkgname=cursor-free-vip-git
-pkgver=1.9.03.2.g43a58db
+pkgver=1.9.04
pkgrel=1
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64')
From 2f012b9dc5eea6866785ac2ffcd10023c9c9fac2 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:01:47 +0800
Subject: [PATCH 03/15] fix: missing LICENSE
---
PKGBUILD | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/PKGBUILD b/PKGBUILD
index 7bd7734..5ae8e8f 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -7,7 +7,7 @@ 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')
+license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
depends=('python' 'cursor-bin')
makedepends=('git' 'python' 'pyinstaller' 'uv')
provides=('cursor-free-vip')
From e69e500e8d65b24d948ceff43c1ae404b074b6d5 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:02:34 +0800
Subject: [PATCH 04/15] add: LICENSE install
---
PKGBUILD | 2 ++
1 file changed, 2 insertions(+)
diff --git a/PKGBUILD b/PKGBUILD
index 5ae8e8f..847b714 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -29,4 +29,6 @@ build() {
package() {
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
+ install -Dm644 "$srcdir/cursor-free-vip/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"
}
\ No newline at end of file
From d867f5cfe9730e309452c6021c6f23617ff63bdc Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:02:50 +0800
Subject: [PATCH 05/15] update: SRCINFO
---
.SRCINFO | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.SRCINFO b/.SRCINFO
index d143943..6ba16b7 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,10 +1,11 @@
pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
- pkgver = 1.9.03.2.g43a58db
+ pkgver = 1.9.04
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
From b98059a476d8b4b147b3b411b6dc1bcc619fad8f Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:39:20 +0800
Subject: [PATCH 06/15] fix: missspell
---
.SRCINFO | 2 +-
PKGBUILD | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.SRCINFO b/.SRCINFO
index 6ba16b7..f5b9776 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
pkgver = 1.9.04
- pkgrel = 1
+ pkgrel = 2
url = https://github.com/yeongpin/cursor-free-vip
arch = x86_64
license = MIT
diff --git a/PKGBUILD b/PKGBUILD
index 847b714..72cd8ca 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -3,7 +3,7 @@
pkgname=cursor-free-vip-git
pkgver=1.9.04
-pkgrel=1
+pkgrel=2
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64')
url="https://github.com/yeongpin/cursor-free-vip"
@@ -29,6 +29,6 @@ build() {
package() {
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
- install -Dm644 "$srcdir/cursor-free-vip/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
+ 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"
}
\ No newline at end of file
From 4b9c465dd53fd967e7b858b5ee2600b9d3cb3d65 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 21:49:54 +0800
Subject: [PATCH 07/15] fix: path
---
PKGBUILD | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/PKGBUILD b/PKGBUILD
index 72cd8ca..be2a618 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -28,7 +28,7 @@ build() {
}
package() {
- install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
- install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
+ install -Dm644 "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"
}
\ No newline at end of file
From b42b4b01b94f3a556c26125a390d6a5f6f128867 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 22:02:10 +0800
Subject: [PATCH 08/15] fix: fetch
---
.SRCINFO | 6 ++++--
PKGBUILD | 10 +++++-----
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/.SRCINFO b/.SRCINFO
index f5b9776..a732162 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
- pkgver = 1.9.04
- pkgrel = 2
+ pkgver = 1.9.04.10.g5863891
+ pkgrel = 1
url = https://github.com/yeongpin/cursor-free-vip
arch = x86_64
license = MIT
@@ -14,6 +14,8 @@ pkgbase = cursor-free-vip-git
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
diff --git a/PKGBUILD b/PKGBUILD
index be2a618..1b81dda 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,8 +2,8 @@
# Contributor: Canmi (Canmi21)
pkgname=cursor-free-vip-git
-pkgver=1.9.04
-pkgrel=2
+pkgver=1.9.04.10.g5863891
+pkgrel=1
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64')
url="https://github.com/yeongpin/cursor-free-vip"
@@ -11,8 +11,8 @@ 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")
-sha256sums=('SKIP')
+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"
@@ -28,7 +28,7 @@ build() {
}
package() {
- install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
+ 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"
}
\ No newline at end of file
From 21535104a6b60d4aea69a3f5e59d2c61ba0b9dd4 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 23:04:43 +0800
Subject: [PATCH 09/15] fix: google-chrome-stable on archlinx(linux), killall,
port, and debug
---
oauth_auth.py | 40 ++++++++++++----------------------------
1 file changed, 12 insertions(+), 28 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index 8a2185f..4313041 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -1,3 +1,4 @@
+# oauth_auth.py
import os
from colorama import Fore, Style, init
import time
@@ -30,7 +31,7 @@ class OAuthHandler:
def __init__(self, translator=None, auth_type=None):
self.translator = translator
self.config = get_config(translator)
- self.auth_type = auth_type # make sure the auth_type is not None
+ self.auth_type = auth_type
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.selected_profile = None
@@ -182,7 +183,7 @@ class OAuthHandler:
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, chromium-browser"
+ "- Linux: Google Chrome, Chromium, google-chrome-stable"
)
raise Exception(error_msg)
@@ -244,7 +245,7 @@ class OAuthHandler:
browser_processes = {
'chrome': {
'win': ['chrome.exe', 'chromium.exe'],
- 'linux': ['chrome', 'chromium', 'chromium-browser'],
+ 'linux': ['chrome', 'chromium', 'chromium-browser', 'google-chrome-stable'],
'mac': ['Chrome', 'Chromium']
},
'brave': {
@@ -428,7 +429,12 @@ class OAuthHandler:
elif browser_type == 'firefox':
possible_paths = ['/usr/bin/firefox']
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:
@@ -436,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}")
return path
- # 如果找不到指定浏览器,则尝试使用Chrome
+ # 如果找不到指定浏览器,则尝试使用 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}")
return self._get_chrome_path()
@@ -446,29 +452,6 @@ class OAuthHandler:
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
return None
-
- def _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):
"""Configure browser options based on platform"""
@@ -481,6 +464,7 @@ class OAuthHandler:
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
+ co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
# Platform-specific options
if sys.platform.startswith('linux'):
From 6eba95c0556d3772002b6fc25ff46be60e98f52a Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 23:05:10 +0800
Subject: [PATCH 10/15] fix: oauth logic, add more debug output
---
oauth_auth.py | 377 +++++++++++++++++++++++++++++---------------------
1 file changed, 220 insertions(+), 157 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index 4313041..0c518a3 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -6,6 +6,7 @@ import random
import webbrowser
import sys
import json
+import logging # Added for detailed logging
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_browser_path
@@ -16,6 +17,16 @@ from get_user_token import get_token_from_cookie
# Initialize colorama
init()
+# Set up logging
+logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.StreamHandler(sys.stdout)
+ ]
+)
+logger = logging.getLogger(__name__)
+
# Define emoji constants
EMOJI = {
'START': '🚀',
@@ -61,6 +72,7 @@ class OAuthHandler:
profiles.append((item, profile_names.get(item, item)))
return sorted(profiles)
except Exception as e:
+ logger.error(f"Error loading Chrome profiles: {str(e)}") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return []
@@ -73,7 +85,6 @@ class OAuthHandler:
browser_type_display = browser_type.capitalize()
if self.translator:
- # 动态使用浏览器类型
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('browser_profile.select_profile', browser=browser_type_display)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('browser_profile.profile_list', browser=browser_type_display)}{Style.RESET_ALL}")
else:
@@ -91,19 +102,15 @@ class OAuthHandler:
state_data = json.load(f)
profiles_data = state_data.get('profile', {}).get('info_cache', {})
- # Create a list of available profiles
profiles = []
for profile_id, profile_info in profiles_data.items():
name = profile_info.get('name', profile_id)
- # Mark the default profile
if profile_id.lower() == 'default':
name = f"{name} (Default)"
profiles.append((profile_id, name))
- # Sort profiles by name
profiles.sort(key=lambda x: x[1])
- # Show available profiles
if self.translator:
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}")
else:
@@ -112,7 +119,6 @@ class OAuthHandler:
for i, (profile_id, name) in enumerate(profiles, 1):
print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}")
- # Get user's choice
max_choice = len(profiles)
choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}")
@@ -142,19 +148,18 @@ class OAuthHandler:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
return self._select_profile()
else:
- # No Local State file, use Default profile
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('browser_profile.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
- # Error loading profiles, use Default profile
+ logger.error(f"Error loading profiles: {str(e)}") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
- # General error, use Default profile
+ logger.error(f"Profile selection error: {str(e)}") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
@@ -162,17 +167,16 @@ class OAuthHandler:
def setup_browser(self):
"""Setup browser for OAuth flow using selected profile"""
try:
+ logger.info("Initializing browser setup") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
- # Platform-specific initialization
platform_name = platform.system().lower()
+ logger.info(f"Detected platform: {platform_name}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
- # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
- # Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
browser_path = self._get_browser_path()
@@ -185,11 +189,12 @@ class OAuthHandler:
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, google-chrome-stable"
)
+ logger.error("No compatible browser found") # Enhanced logging
raise Exception(error_msg)
+ logger.info(f"Found browser data directory: {user_data_dir}") # Enhanced logging
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}")
- # Show warning about closing browser first - 使用动态提示
if self.translator:
warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type)
else:
@@ -199,31 +204,33 @@ class OAuthHandler:
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
if choice != 'y':
+ logger.info("Operation cancelled by user") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
- # Kill existing browser processes
self._kill_browser_processes()
- # Let user select a profile
if not self._select_profile():
+ logger.info("Operation cancelled by user during profile selection") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
- # Configure browser options
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
+ logger.info(f"Starting browser at: {browser_path}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
- # Verify browser launched successfully
if not self.browser:
- raise Exception(f"{self.translator.get('oauth.browser_failed_to_start', error=str(e)) if self.translator else 'Failed to initialize browser instance'}")
+ logger.error("Failed to initialize browser instance") # Enhanced logging
+ raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
+ logger.info("Browser setup completed successfully") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
+ logger.error(f"Browser setup failed: {str(e)}", exc_info=True) # Enhanced logging with stack trace
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
@@ -236,12 +243,10 @@ class OAuthHandler:
def _kill_browser_processes(self):
"""Kill existing browser processes based on platform and browser type"""
try:
- # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
- # 根据浏览器类型和平台定义要关闭的进程
browser_processes = {
'chrome': {
'win': ['chrome.exe', 'chromium.exe'],
@@ -270,7 +275,6 @@ class OAuthHandler:
}
}
- # 获取平台类型
if os.name == 'nt':
platform_type = 'win'
elif sys.platform == 'darwin':
@@ -278,34 +282,31 @@ class OAuthHandler:
else:
platform_type = 'linux'
- # 获取要关闭的进程列表
processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, [])
- if self.translator:
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
+ logger.info(f"Killing {browser_type} processes: {processes}") # Enhanced logging
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
- # 根据平台关闭进程
- if os.name == 'nt': # Windows
+ if os.name == 'nt':
for proc in processes:
os.system(f'taskkill /f /im {proc} >nul 2>&1')
- else: # Linux/Mac
+ else:
for proc in processes:
os.system(f'pkill -f {proc} >/dev/null 2>&1')
- time.sleep(1) # Wait for processes to close
+ time.sleep(1)
except Exception as e:
+ logger.warning(f"Could not kill browser processes: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the default user data directory based on browser type and platform"""
try:
- # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
- # 根据操作系统和浏览器类型获取用户数据目录
- if os.name == 'nt': # Windows
+ if os.name == 'nt':
user_data_dirs = {
'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'),
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
@@ -314,7 +315,7 @@ class OAuthHandler:
'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':
user_data_dirs = {
'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'),
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
@@ -323,7 +324,7 @@ class OAuthHandler:
'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
}
- else: # Linux
+ else:
user_data_dirs = {
'chrome': os.path.expanduser('~/.config/google-chrome'),
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
@@ -333,19 +334,20 @@ class OAuthHandler:
'operagx': os.path.expanduser('~/.config/opera-gx')
}
- # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
user_data_dir = user_data_dirs.get(browser_type)
if user_data_dir and os.path.exists(user_data_dir):
+ logger.info(f"Found {browser_type} user data directory: {user_data_dir}") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}")
return user_data_dir
else:
+ logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}")
- return user_data_dirs['chrome'] # 回退到 Chrome 目录
+ return user_data_dirs['chrome']
except Exception as e:
+ logger.error(f"Error getting user data directory: {str(e)}") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
- # 在出错时提供一个默认目录
if os.name == 'nt':
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
elif sys.platform == 'darwin':
@@ -356,26 +358,24 @@ class OAuthHandler:
def _get_browser_path(self):
"""Get appropriate browser path based on platform and selected browser type"""
try:
- # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
- # 首先检查配置中是否有明确指定的浏览器路径
browser_path = config.get('Browser', f'{browser_type}_path', fallback=None)
if browser_path and os.path.exists(browser_path):
+ logger.info(f"Using configured {browser_type} path: {browser_path}") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}")
return browser_path
- # 尝试获取默认路径
browser_path = get_default_browser_path(browser_type)
if browser_path and os.path.exists(browser_path):
return browser_path
+ logger.info("Searching for alternative browser installations") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
- # 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器
- if os.name == 'nt': # Windows
+ if os.name == 'nt':
possible_paths = []
if browser_type == 'brave':
possible_paths = [
@@ -400,16 +400,17 @@ class OAuthHandler:
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 GX', 'launcher.exe'),
- os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Oper
+a GX', 'opera.exe')
]
- else: # 默认为 Chrome
+ else:
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
+ elif sys.platform == 'darwin':
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser']
@@ -417,10 +418,10 @@ class OAuthHandler:
possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge']
elif browser_type == 'firefox':
possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox']
- else: # 默认为 Chrome
+ else:
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
- else: # Linux
+ else:
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave']
@@ -428,28 +429,29 @@ class OAuthHandler:
possible_paths = ['/usr/bin/microsoft-edge']
elif browser_type == 'firefox':
possible_paths = ['/usr/bin/firefox']
- else: # 默认为 Chrome
+ else:
possible_paths = [
- '/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable
+ '/usr/bin/google-chrome-stable',
'/usr/bin/google-chrome',
'/usr/bin/chromium',
'/usr/bin/chromium-browser'
]
- # 检查每个可能的路径
for path in possible_paths:
if os.path.exists(path):
+ logger.info(f"Found browser at: {path}") # Enhanced logging
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
- # 如果找不到指定浏览器,则尝试使用 Chrome
if browser_type != 'chrome':
+ logger.warning(f"Could not find {browser_type}, trying Chrome") # Enhanced logging
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 None
except Exception as e:
+ logger.error(f"Error finding browser path: {str(e)}", exc_info=True) # Enhanced logging
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
@@ -460,13 +462,11 @@ class OAuthHandler:
co.set_paths(browser_path=browser_path, user_data_path=user_data_dir)
co.set_argument(f'--profile-directory={active_profile}')
- # Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
- co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
+ co.set_argument('--remote-debugging-port=9222')
- # Platform-specific options
if sys.platform.startswith('linux'):
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
@@ -477,57 +477,67 @@ class OAuthHandler:
co.set_argument('--disable-features=TranslateUI')
co.set_argument('--disable-features=RendererCodeIntegrity')
+ logger.info("Browser options configured successfully") # Enhanced logging
return co
except Exception as e:
+ logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
+ logger.info("Starting Google OAuth authentication") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
- # Setup browser
if not self.setup_browser():
+ logger.error("Browser failed to initialize") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
- # Navigate to auth URL
try:
+ logger.info("Navigating to authentication page") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging
- # Look for Google auth button
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
- "(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
+ "(//a[contains(@class,'auth-method-button')])[1]"
]
auth_btn = None
for selector in selectors:
+ logger.debug(f"Trying selector: {selector}") # Enhanced logging
try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout
if auth_btn and auth_btn.is_displayed():
+ logger.info(f"Found Google auth button with selector: {selector}") # Enhanced logging
break
- except:
+ except Exception as e:
+ logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging
continue
if not auth_btn:
+ logger.error("Could not find Google authentication button") # Enhanced logging
raise Exception("Could not find Google authentication button")
- # Click the button and wait for page load
+ logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
- auth_btn.click()
+ try:
+ auth_btn.click()
+ except Exception as e:
+ logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging
+ self.browser.run_js("arguments[0].click();", auth_btn)
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- # Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
+ logger.info("On Google account selection page") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
- # 获取配置中是否启用 alert 选项
config = get_config(self.translator)
show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False)
@@ -538,43 +548,49 @@ class OAuthHandler:
alert('{alert_message}');
""")
except:
- pass # Alert is optional
+ logger.warning("Failed to display alert") # Enhanced logging
+ pass
- # Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
+ logger.error("Authentication timeout") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
+ logger.info("Google authentication successful") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
+ logger.error(f"Authentication error: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
+ logger.info("Browser closed") # Enhanced logging
except:
+ logger.warning("Failed to close browser") # Enhanced logging
pass
except Exception as e:
+ logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _wait_for_auth(self):
"""Wait for authentication to complete and extract auth info"""
try:
- max_wait = 300 # 5 minutes
+ max_wait = 300
start_time = time.time()
- check_interval = 2 # Check every 2 seconds
+ check_interval = 2
+ logger.info("Waiting for authentication (timeout: 5 minutes)") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
- # Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
@@ -582,7 +598,7 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- # Get email from settings page
+ logger.info("Authentication successful, getting account info") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
@@ -592,15 +608,17 @@ class OAuthHandler:
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
+ logger.info(f"Found email: {email}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
- email = "user@cursor.sh" # Fallback email
+ logger.warning("Could not find email, using fallback") # Enhanced logging
+ email = "user@cursor.sh"
- # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
+ logger.info(f"Usage count: {usage_text}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -615,136 +633,165 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
+ logger.info("Account has reached maximum usage, deleting") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
+ logger.info("Starting new authentication process") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
+ logger.error("Failed to delete expired account") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
+ logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
- # Also check URL as backup
if "cursor.com/settings" in self.browser.url:
+ logger.info("Detected successful login via URL") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Error during auth wait: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
+ logger.error("Authentication timeout") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
+ logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
"""Handle GitHub OAuth authentication"""
try:
+ logger.info("Starting GitHub OAuth authentication") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
- # Setup browser
if not self.setup_browser():
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
+ logger.error("Browser failed to initialize") # Enhanced logging
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
- # Navigate to auth URL
- try:
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
- self.browser.get("https://authenticator.cursor.sh/sign-up")
- time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
-
- # Look for GitHub auth button
- selectors = [
- "//a[contains(@href,'GitHubOAuth')]",
- "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
- "(//a[contains(@class,'auth-method-button')])[2]" # Second auth button as fallback
- ]
-
- auth_btn = None
- for selector in selectors:
- try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
- if auth_btn and auth_btn.is_displayed():
- break
- except:
- continue
-
- if not auth_btn:
- raise Exception("Could not find GitHub authentication button")
-
- # Click the button and wait for page load
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
- auth_btn.click()
- time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
-
- # Wait for authentication to complete
- auth_info = self._wait_for_auth()
- if not auth_info:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
- return False, None
-
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
- return True, auth_info
-
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
- return False, None
- finally:
+ # Retry navigation up to 3 times
+ max_retries = 3
+ for attempt in range(max_retries):
try:
- if self.browser:
- self.browser.quit()
- except:
- pass
+ logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") # Enhanced logging
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
+ self.browser.get("https://authenticator.cursor.sh/sign-up")
+ time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging
+
+ # Updated selectors for GitHub button
+ selectors = [
+ "//a[contains(@href,'GitHubOAuth')]", # Primary selector
+ "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", # Class-based
+ "(//a[contains(@class,'auth-method-button')])[2]", # Second button fallback
+ "//a[contains(text(),'GitHub')]", # Text-based fallback
+ "//a[@href='/auth/github']" # Additional fallback
+ ]
+
+ auth_btn = None
+ for selector in selectors:
+ logger.debug(f"Trying selector: {selector}") # Enhanced logging
+ try:
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout
+ if auth_btn and auth_btn.is_displayed():
+ logger.info(f"Found GitHub auth button with selector: {selector}") # Enhanced logging
+ break
+ except Exception as e:
+ logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging
+ continue
+
+ if not auth_btn:
+ logger.error("Could not find GitHub authentication button") # Enhanced logging
+ raise Exception("Could not find GitHub authentication button")
+
+ logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
+ try:
+ auth_btn.click()
+ except Exception as e:
+ logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging
+ self.browser.run_js("arguments[0].click();", auth_btn)
+ time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") # Enhanced logging
+
+ auth_info = self._wait_for_auth()
+ if not auth_info:
+ logger.error("Authentication timeout") # Enhanced logging
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
+ return False, None
+
+ logger.info("GitHub authentication successful") # Enhanced logging
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
+ return True, auth_info
+
+ except Exception as e:
+ logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) # Enhanced logging
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
+ if attempt < max_retries - 1:
+ logger.info("Retrying navigation") # Enhanced logging
+ time.sleep(2)
+ continue
+ return False, None
+ finally:
+ try:
+ if self.browser and attempt == max_retries - 1:
+ self.browser.quit()
+ logger.info("Browser closed") # Enhanced logging
+ except:
+ logger.warning("Failed to close browser") # Enhanced logging
+ pass
except Exception as e:
+ logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _handle_oauth(self, auth_type):
- """Handle OAuth authentication for both Google and GitHub
-
- Args:
- auth_type (str): Type of authentication ('google' or 'github')
- """
+ """Handle OAuth authentication for both Google and GitHub"""
try:
if not self.setup_browser():
return False, None
- # Navigate to auth URL
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ logger.info(f"Current URL in _handle_oauth: {self.browser.url}") # Enhanced logging
- # Set selectors based on auth type
if auth_type == "google":
selectors = [
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
]
- else: # github
+ else:
selectors = [
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
]
- # Wait for the button to be available
auth_btn = None
- max_button_wait = 30 # 30 seconds
+ max_button_wait = 30
button_start_time = time.time()
while time.time() - button_start_time < max_button_wait:
for selector in selectors:
+ logger.debug(f"Trying selector in _handle_oauth: {selector}") # Enhanced logging
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
+ logger.info(f"Found auth button in _handle_oauth: {selector}") # Enhanced logging
break
except:
continue
@@ -753,11 +800,10 @@ class OAuthHandler:
time.sleep(1)
if auth_btn:
- # Click the button and wait for page load
+ logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- # Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
@@ -765,13 +811,13 @@ class OAuthHandler:
alert('{alert_message}');
""")
except Exception as e:
+ logger.warning(f"Alert display failed: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
- # Wait for authentication to complete
- max_wait = 300 # 5 minutes
+ max_wait = 300
start_time = time.time()
last_url = self.browser.url
@@ -779,7 +825,6 @@ class OAuthHandler:
while time.time() - start_time < max_wait:
try:
- # Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
@@ -787,27 +832,28 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
+ logger.info("Authentication successful in _handle_oauth") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
- # Navigate to settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
- time.sleep(3) # Wait for settings page to load
+ time.sleep(3)
- # Get email from settings page
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
+ logger.info(f"Found email: {actual_email}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Could not find email: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
- # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
+ logger.info(f"Usage count: {usage_text}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -822,26 +868,30 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
+ logger.info("Account has reached maximum usage, deleting") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
+ logger.info("Starting new authentication process") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
+ logger.error("Failed to delete expired account") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
+ logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
- # Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
- # Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
+ logger.info("Already on settings page") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
@@ -850,21 +900,22 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- # Get email and check usage here too
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
+ logger.info(f"Found email: {actual_email}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Could not find email: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
- # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
+ logger.info(f"Usage count: {usage_text}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -879,49 +930,58 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
+ logger.info("Account has reached maximum usage, deleting") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
+ logger.info("Starting new authentication process") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
+ logger.error("Failed to delete expired account") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
+ logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
+ logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
- # Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
+ logger.info(f"Page changed to {current_url}") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
+ logger.warning(f"Status check error: {str(e)}") # Enhanced logging
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
+ logger.error("Authentication timeout") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
+ logger.error("Authentication button not found") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
+ logger.error(f"Authentication failed: {str(e)}", exc_info=True) # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
self.browser.quit()
+ logger.info("Browser closed in _handle_oauth") # Enhanced logging
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
try:
- # Get cookies with retry
max_retries = 3
for attempt in range(max_retries):
try:
@@ -934,7 +994,7 @@ class OAuthHandler:
raise
time.sleep(1)
- # Debug cookie information
+ logger.info(f"Found {len(cookies)} cookies") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
@@ -947,12 +1007,14 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
except Exception as e:
+ logger.error(f"Failed to extract token: {str(e)}") # Enhanced logging
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":
email = cookie.get("value")
if email and token:
+ logger.info(f"Authentication successful - Email: {email}") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
@@ -961,11 +1023,13 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
+ logger.error(f"Missing authentication data: {', '.join(missing)}") # Enhanced logging
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
except Exception as e:
+ logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) # Enhanced logging
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
@@ -999,9 +1063,9 @@ class OAuthHandler:
"""
result = self.browser.run_js(delete_js)
+ logger.info(f"Delete account result: {result}") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
- # Navigate back to auth page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -1009,17 +1073,14 @@ class OAuthHandler:
return True
except Exception as e:
+ logger.error(f"Failed to delete account: {str(e)}", exc_info=True) # Enhanced logging
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
def main(auth_type, translator=None):
- """Main function to handle OAuth authentication
-
- Args:
- auth_type (str): Type of authentication ('google' or 'github')
- translator: Translator instance for internationalization
- """
+ """Main function to handle OAuth authentication"""
+ logger.info(f"Starting OAuth main with auth_type: {auth_type}") # Enhanced logging
handler = OAuthHandler(translator, auth_type)
if auth_type.lower() == 'google':
@@ -1029,24 +1090,26 @@ def main(auth_type, translator=None):
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_github_auth()
else:
+ logger.error("Invalid authentication type") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
return False
if success and auth_info:
- # Update Cursor authentication
auth_manager = CursorAuth(translator)
if auth_manager.update_auth(
email=auth_info["email"],
access_token=auth_info["token"],
refresh_token=auth_info["token"]
):
+ logger.info("Auth update success") # Enhanced logging
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
- # Close the browser after successful authentication
if handler.browser:
handler.browser.quit()
+ logger.info("Browser closed") # Enhanced logging
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
return True
else:
+ logger.error("Auth update failed") # Enhanced logging
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
- return False
\ No newline at end of file
+ return False
\ No newline at end of file
From 61530416074f23f4abc595e3435495ceb14d39c2 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 23:22:56 +0800
Subject: [PATCH 11/15] fix: handle
---
oauth_auth.py | 291 ++++++++++++++++++++++----------------------------
1 file changed, 126 insertions(+), 165 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index 0c518a3..b15c71a 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -6,7 +6,7 @@ import random
import webbrowser
import sys
import json
-import logging # Added for detailed logging
+import logging
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_browser_path
@@ -72,14 +72,13 @@ class OAuthHandler:
profiles.append((item, profile_names.get(item, item)))
return sorted(profiles)
except Exception as e:
- logger.error(f"Error loading Chrome profiles: {str(e)}") # Enhanced logging
+ logger.error(f"Error loading Chrome profiles: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return []
def _select_profile(self):
"""Allow user to select a browser profile to use"""
try:
- # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type_display = browser_type.capitalize()
@@ -91,10 +90,8 @@ class OAuthHandler:
print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}")
print(f"Available {browser_type_display} profiles:")
- # Get the user data directory for the browser type
user_data_dir = self._get_user_data_directory()
- # Load available profiles from the selected browser type
try:
local_state_file = os.path.join(user_data_dir, "Local State")
if os.path.exists(local_state_file):
@@ -153,13 +150,13 @@ class OAuthHandler:
return True
except Exception as e:
- logger.error(f"Error loading profiles: {str(e)}") # Enhanced logging
+ logger.error(f"Error loading profiles: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
- logger.error(f"Profile selection error: {str(e)}") # Enhanced logging
+ logger.error(f"Profile selection error: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
@@ -167,11 +164,11 @@ class OAuthHandler:
def setup_browser(self):
"""Setup browser for OAuth flow using selected profile"""
try:
- logger.info("Initializing browser setup") # Enhanced logging
+ logger.info("Initializing browser setup")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
platform_name = platform.system().lower()
- logger.info(f"Detected platform: {platform_name}") # Enhanced logging
+ logger.info(f"Detected platform: {platform_name}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
config = get_config(self.translator)
@@ -189,10 +186,10 @@ class OAuthHandler:
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, google-chrome-stable"
)
- logger.error("No compatible browser found") # Enhanced logging
+ logger.error("No compatible browser found")
raise Exception(error_msg)
- logger.info(f"Found browser data directory: {user_data_dir}") # Enhanced logging
+ logger.info(f"Found browser data directory: {user_data_dir}")
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}")
if self.translator:
@@ -204,33 +201,33 @@ class OAuthHandler:
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
if choice != 'y':
- logger.info("Operation cancelled by user") # Enhanced logging
+ logger.info("Operation cancelled by user")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
self._kill_browser_processes()
if not self._select_profile():
- logger.info("Operation cancelled by user during profile selection") # Enhanced logging
+ logger.info("Operation cancelled by user during profile selection")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
- logger.info(f"Starting browser at: {browser_path}") # Enhanced logging
+ logger.info(f"Starting browser at: {browser_path}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
if not self.browser:
- logger.error("Failed to initialize browser instance") # Enhanced logging
+ logger.error("Failed to initialize browser instance")
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
- logger.info("Browser setup completed successfully") # Enhanced logging
+ logger.info("Browser setup completed successfully")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
- logger.error(f"Browser setup failed: {str(e)}", exc_info=True) # Enhanced logging with stack trace
+ logger.error(f"Browser setup failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
@@ -284,7 +281,7 @@ class OAuthHandler:
processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, [])
- logger.info(f"Killing {browser_type} processes: {processes}") # Enhanced logging
+ logger.info(f"Killing {browser_type} processes: {processes}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
if os.name == 'nt':
@@ -296,7 +293,7 @@ class OAuthHandler:
time.sleep(1)
except Exception as e:
- logger.warning(f"Could not kill browser processes: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not kill browser processes: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
@@ -337,16 +334,16 @@ class OAuthHandler:
user_data_dir = user_data_dirs.get(browser_type)
if user_data_dir and os.path.exists(user_data_dir):
- logger.info(f"Found {browser_type} user data directory: {user_data_dir}") # Enhanced logging
+ logger.info(f"Found {browser_type} user data directory: {user_data_dir}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}")
return user_data_dir
else:
- logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") # Enhanced logging
+ logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}")
return user_data_dirs['chrome']
except Exception as e:
- logger.error(f"Error getting user data directory: {str(e)}") # Enhanced logging
+ logger.error(f"Error getting user data directory: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
if os.name == 'nt':
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
@@ -364,7 +361,7 @@ class OAuthHandler:
browser_path = config.get('Browser', f'{browser_type}_path', fallback=None)
if browser_path and os.path.exists(browser_path):
- logger.info(f"Using configured {browser_type} path: {browser_path}") # Enhanced logging
+ logger.info(f"Using configured {browser_type} path: {browser_path}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}")
return browser_path
@@ -372,7 +369,7 @@ class OAuthHandler:
if browser_path and os.path.exists(browser_path):
return browser_path
- logger.info("Searching for alternative browser installations") # Enhanced logging
+ logger.info("Searching for alternative browser installations")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
if os.name == 'nt':
@@ -400,8 +397,7 @@ class OAuthHandler:
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 GX', 'launcher.exe'),
- os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Oper
-a GX', 'opera.exe')
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
]
else:
possible_paths = [
@@ -439,19 +435,19 @@ a GX', 'opera.exe')
for path in possible_paths:
if os.path.exists(path):
- logger.info(f"Found browser at: {path}") # Enhanced logging
+ logger.info(f"Found browser at: {path}")
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
if browser_type != 'chrome':
- logger.warning(f"Could not find {browser_type}, trying Chrome") # Enhanced logging
+ logger.warning(f"Could not find {browser_type}, trying 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}")
return self._get_chrome_path()
return None
except Exception as e:
- logger.error(f"Error finding browser path: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Error finding browser path: {str(e)}", exc_info=True)
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
@@ -477,31 +473,31 @@ a GX', 'opera.exe')
co.set_argument('--disable-features=TranslateUI')
co.set_argument('--disable-features=RendererCodeIntegrity')
- logger.info("Browser options configured successfully") # Enhanced logging
+ logger.info("Browser options configured successfully")
return co
except Exception as e:
- logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Error configuring browser options: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
- logger.info("Starting Google OAuth authentication") # Enhanced logging
+ logger.info("Starting Google OAuth authentication")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
if not self.setup_browser():
- logger.error("Browser failed to initialize") # Enhanced logging
+ logger.error("Browser failed to initialize")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
try:
- logger.info("Navigating to authentication page") # Enhanced logging
+ logger.info("Navigating to authentication page")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging
+ logger.info(f"Current URL after navigation: {self.browser.url}")
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
@@ -511,31 +507,31 @@ a GX', 'opera.exe')
auth_btn = None
for selector in selectors:
- logger.debug(f"Trying selector: {selector}") # Enhanced logging
+ logger.debug(f"Trying selector: {selector}")
try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5)
if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found Google auth button with selector: {selector}") # Enhanced logging
+ logger.info(f"Found Google auth button with selector: {selector}")
break
except Exception as e:
- logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging
+ logger.warning(f"Selector {selector} failed: {str(e)}")
continue
if not auth_btn:
- logger.error("Could not find Google authentication button") # Enhanced logging
+ logger.error("Could not find Google authentication button")
raise Exception("Could not find Google authentication button")
- logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
+ logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
try:
auth_btn.click()
except Exception as e:
- logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging
+ logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click")
self.browser.run_js("arguments[0].click();", auth_btn)
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
if "accounts.google.com" in self.browser.url:
- logger.info("On Google account selection page") # Enhanced logging
+ logger.info("On Google account selection page")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
config = get_config(self.translator)
@@ -548,34 +544,34 @@ a GX', 'opera.exe')
alert('{alert_message}');
""")
except:
- logger.warning("Failed to display alert") # Enhanced logging
+ logger.warning("Failed to display alert")
pass
auth_info = self._wait_for_auth()
if not auth_info:
- logger.error("Authentication timeout") # Enhanced logging
+ logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
- logger.info("Google authentication successful") # Enhanced logging
+ logger.info("Google authentication successful")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
- logger.error(f"Authentication error: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Authentication error: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
- logger.info("Browser closed") # Enhanced logging
+ logger.info("Browser closed")
except:
- logger.warning("Failed to close browser") # Enhanced logging
+ logger.warning("Failed to close browser")
pass
except Exception as e:
- logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Google OAuth failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
@@ -586,7 +582,7 @@ a GX', 'opera.exe')
start_time = time.time()
check_interval = 2
- logger.info("Waiting for authentication (timeout: 5 minutes)") # Enhanced logging
+ logger.info("Waiting for authentication (timeout: 5 minutes)")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
@@ -598,7 +594,7 @@ a GX', 'opera.exe')
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- logger.info("Authentication successful, getting account info") # Enhanced logging
+ logger.info("Authentication successful, getting account info")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
@@ -608,17 +604,17 @@ a GX', 'opera.exe')
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
- logger.info(f"Found email: {email}") # Enhanced logging
+ logger.info(f"Found email: {email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
- logger.warning("Could not find email, using fallback") # Enhanced logging
+ logger.warning("Could not find email, using fallback")
email = "user@cursor.sh"
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}") # Enhanced logging
+ logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -633,117 +629,115 @@ a GX', 'opera.exe')
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting") # Enhanced logging
+ logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process") # Enhanced logging
+ logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account") # Enhanced logging
+ logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
+ logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
if "cursor.com/settings" in self.browser.url:
- logger.info("Detected successful login via URL") # Enhanced logging
+ logger.info("Detected successful login via URL")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Error during auth wait: {str(e)}") # Enhanced logging
+ logger.warning(f"Error during auth wait: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
- logger.error("Authentication timeout") # Enhanced logging
+ logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
- logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
"""Handle GitHub OAuth authentication"""
try:
- logger.info("Starting GitHub OAuth authentication") # Enhanced logging
+ logger.info("Starting GitHub OAuth authentication")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
if not self.setup_browser():
- logger.error("Browser failed to initialize") # Enhanced logging
+ logger.error("Browser failed to initialize")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
- # Retry navigation up to 3 times
max_retries = 3
for attempt in range(max_retries):
try:
- logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") # Enhanced logging
+ logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging
+ logger.info(f"Current URL after navigation: {self.browser.url}")
- # Updated selectors for GitHub button
selectors = [
- "//a[contains(@href,'GitHubOAuth')]", # Primary selector
- "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", # Class-based
- "(//a[contains(@class,'auth-method-button')])[2]", # Second button fallback
- "//a[contains(text(),'GitHub')]", # Text-based fallback
- "//a[@href='/auth/github']" # Additional fallback
+ "//a[contains(@href,'GitHubOAuth')]",
+ "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
+ "(//a[contains(@class,'auth-method-button')])[2]",
+ "//a[contains(text(),'GitHub')]",
+ "//a[@href='/auth/github']"
]
auth_btn = None
for selector in selectors:
- logger.debug(f"Trying selector: {selector}") # Enhanced logging
+ logger.debug(f"Trying selector: {selector}")
try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5)
if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found GitHub auth button with selector: {selector}") # Enhanced logging
+ logger.info(f"Found GitHub auth button with selector: {selector}")
break
except Exception as e:
- logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging
+ logger.warning(f"Selector {selector} failed: {str(e)}")
continue
if not auth_btn:
- logger.error("Could not find GitHub authentication button") # Enhanced logging
+ logger.error("Could not find GitHub authentication button")
raise Exception("Could not find GitHub authentication button")
- logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
+ logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
try:
auth_btn.click()
except Exception as e:
- logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging
+ logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click")
self.browser.run_js("arguments[0].click();", auth_btn)
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") # Enhanced logging
+ logger.info(f"Current URL after clicking GitHub button: {self.browser.url}")
auth_info = self._wait_for_auth()
if not auth_info:
- logger.error("Authentication timeout") # Enhanced logging
+ logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
- logger.info("GitHub authentication successful") # Enhanced logging
+ logger.info("GitHub authentication successful")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
- logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
if attempt < max_retries - 1:
- logger.info("Retrying navigation") # Enhanced logging
+ logger.info("Retrying navigation")
time.sleep(2)
continue
return False, None
@@ -751,13 +745,13 @@ a GX', 'opera.exe')
try:
if self.browser and attempt == max_retries - 1:
self.browser.quit()
- logger.info("Browser closed") # Enhanced logging
+ logger.info("Browser closed")
except:
- logger.warning("Failed to close browser") # Enhanced logging
+ logger.warning("Failed to close browser")
pass
except Exception as e:
- logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
@@ -769,7 +763,7 @@ a GX', 'opera.exe')
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL in _handle_oauth: {self.browser.url}") # Enhanced logging
+ logger.info(f"Current URL in _handle_oauth: {self.browser.url}")
if auth_type == "google":
selectors = [
@@ -787,11 +781,11 @@ a GX', 'opera.exe')
while time.time() - button_start_time < max_button_wait:
for selector in selectors:
- logger.debug(f"Trying selector in _handle_oauth: {selector}") # Enhanced logging
+ logger.debug(f"Trying selector in _handle_oauth: {selector}")
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found auth button in _handle_oauth: {selector}") # Enhanced logging
+ logger.info(f"Found auth button in _handle_oauth: {selector}")
break
except:
continue
@@ -800,7 +794,7 @@ a GX', 'opera.exe')
time.sleep(1)
if auth_btn:
- logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging
+ logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
@@ -811,7 +805,7 @@ a GX', 'opera.exe')
alert('{alert_message}');
""")
except Exception as e:
- logger.warning(f"Alert display failed: {str(e)}") # Enhanced logging
+ logger.warning(f"Alert display failed: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
@@ -832,7 +826,7 @@ a GX', 'opera.exe')
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- logger.info("Authentication successful in _handle_oauth") # Enhanced logging
+ logger.info("Authentication successful in _handle_oauth")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
@@ -842,10 +836,10 @@ a GX', 'opera.exe')
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- logger.info(f"Found email: {actual_email}") # Enhanced logging
+ logger.info(f"Found email: {actual_email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not find email: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not find email: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
@@ -853,7 +847,7 @@ a GX', 'opera.exe')
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}") # Enhanced logging
+ logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -868,30 +862,30 @@ a GX', 'opera.exe')
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting") # Enhanced logging
+ logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process") # Enhanced logging
+ logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account") # Enhanced logging
+ logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
+ logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return True, {"email": actual_email, "token": token}
current_url = self.browser.url
if "cursor.com/settings" in current_url:
- logger.info("Already on settings page") # Enhanced logging
+ logger.info("Already on settings page")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
@@ -904,10 +898,10 @@ a GX', 'opera.exe')
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- logger.info(f"Found email: {actual_email}") # Enhanced logging
+ logger.info(f"Found email: {actual_email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not find email: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not find email: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
@@ -915,7 +909,7 @@ a GX', 'opera.exe')
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}") # Enhanced logging
+ logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -930,54 +924,54 @@ a GX', 'opera.exe')
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting") # Enhanced logging
+ logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process") # Enhanced logging
+ logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account") # Enhanced logging
+ logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging
+ logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging
+ logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
- logger.info(f"Page changed to {current_url}") # Enhanced logging
+ logger.info(f"Page changed to {current_url}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
- logger.warning(f"Status check error: {str(e)}") # Enhanced logging
+ logger.warning(f"Status check error: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
- logger.error("Authentication timeout") # Enhanced logging
+ logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
- logger.error("Authentication button not found") # Enhanced logging
+ logger.error("Authentication button not found")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
- logger.error(f"Authentication failed: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Authentication failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
self.browser.quit()
- logger.info("Browser closed in _handle_oauth") # Enhanced logging
+ logger.info("Browser closed in _handle_oauth")
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
@@ -994,7 +988,7 @@ a GX', 'opera.exe')
raise
time.sleep(1)
- logger.info(f"Found {len(cookies)} cookies") # Enhanced logging
+ logger.info(f"Found {len(cookies)} cookies")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
@@ -1007,14 +1001,14 @@ a GX', 'opera.exe')
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
except Exception as e:
- logger.error(f"Failed to extract token: {str(e)}") # Enhanced logging
+ logger.error(f"Failed to extract token: {str(e)}")
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":
email = cookie.get("value")
if email and token:
- logger.info(f"Authentication successful - Email: {email}") # Enhanced logging
+ logger.info(f"Authentication successful - Email: {email}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
@@ -1023,13 +1017,13 @@ a GX', 'opera.exe')
missing.append("email")
if not token:
missing.append("token")
- logger.error(f"Missing authentication data: {', '.join(missing)}") # Enhanced logging
+ logger.error(f"Missing authentication data: {', '.join(missing)}")
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
except Exception as e:
- logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) # Enhanced logging
+ logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True)
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
@@ -1062,54 +1056,21 @@ a GX', 'opera.exe')
return deleteAccount();
"""
+ logger.info("Attempting to delete account via API")
result = self.browser.run_js(delete_js)
- logger.info(f"Delete account result: {result}") # Enhanced logging
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
+ logger.info(f"Delete account result: {result}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.delete_account_success', result=result) if self.translator else f'Delete account result: {result}'}{Style.RESET_ALL}")
+ logger.info("Redirecting to authenticator.cursor.sh")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ logger.info(f"Navigated to sign-up page: {self.browser.url}")
return True
except Exception as e:
- logger.error(f"Failed to delete account: {str(e)}", exc_info=True) # Enhanced logging
- 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))
+ logger.error(f"Failed to delete account: {str(e)}", exc_info=True)
+ error_message = self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'
print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
- return False
-
-def main(auth_type, translator=None):
- """Main function to handle OAuth authentication"""
- logger.info(f"Starting OAuth main with auth_type: {auth_type}") # Enhanced logging
- handler = OAuthHandler(translator, auth_type)
-
- if auth_type.lower() == 'google':
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
- success, auth_info = handler.handle_google_auth()
- elif auth_type.lower() == 'github':
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
- success, auth_info = handler.handle_github_auth()
- else:
- logger.error("Invalid authentication type") # Enhanced logging
- print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
- return False
-
- if success and auth_info:
- auth_manager = CursorAuth(translator)
- if auth_manager.update_auth(
- email=auth_info["email"],
- access_token=auth_info["token"],
- refresh_token=auth_info["token"]
- ):
- logger.info("Auth update success") # Enhanced logging
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
- if handler.browser:
- handler.browser.quit()
- logger.info("Browser closed") # Enhanced logging
- print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
- return True
- else:
- logger.error("Auth update failed") # Enhanced logging
- print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
-
- return False
\ No newline at end of file
+ return False
\ No newline at end of file
From 3de2db74b2d7c38357986dba31670eebd8e0608b Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 23:32:49 +0800
Subject: [PATCH 12/15] fix: unexpected
---
oauth_auth.py | 404 ++++++++++++++++++++++++--------------------------
1 file changed, 190 insertions(+), 214 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index b15c71a..4313041 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -6,7 +6,6 @@ import random
import webbrowser
import sys
import json
-import logging
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_browser_path
@@ -17,16 +16,6 @@ from get_user_token import get_token_from_cookie
# Initialize colorama
init()
-# Set up logging
-logging.basicConfig(
- level=logging.DEBUG,
- format='%(asctime)s - %(levelname)s - %(message)s',
- handlers=[
- logging.StreamHandler(sys.stdout)
- ]
-)
-logger = logging.getLogger(__name__)
-
# Define emoji constants
EMOJI = {
'START': '🚀',
@@ -72,26 +61,29 @@ class OAuthHandler:
profiles.append((item, profile_names.get(item, item)))
return sorted(profiles)
except Exception as e:
- logger.error(f"Error loading Chrome profiles: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return []
def _select_profile(self):
"""Allow user to select a browser profile to use"""
try:
+ # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type_display = browser_type.capitalize()
if self.translator:
+ # 动态使用浏览器类型
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('browser_profile.select_profile', browser=browser_type_display)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('browser_profile.profile_list', browser=browser_type_display)}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}")
print(f"Available {browser_type_display} profiles:")
+ # Get the user data directory for the browser type
user_data_dir = self._get_user_data_directory()
+ # Load available profiles from the selected browser type
try:
local_state_file = os.path.join(user_data_dir, "Local State")
if os.path.exists(local_state_file):
@@ -99,15 +91,19 @@ class OAuthHandler:
state_data = json.load(f)
profiles_data = state_data.get('profile', {}).get('info_cache', {})
+ # Create a list of available profiles
profiles = []
for profile_id, profile_info in profiles_data.items():
name = profile_info.get('name', profile_id)
+ # Mark the default profile
if profile_id.lower() == 'default':
name = f"{name} (Default)"
profiles.append((profile_id, name))
+ # Sort profiles by name
profiles.sort(key=lambda x: x[1])
+ # Show available profiles
if self.translator:
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}")
else:
@@ -116,6 +112,7 @@ class OAuthHandler:
for i, (profile_id, name) in enumerate(profiles, 1):
print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}")
+ # Get user's choice
max_choice = len(profiles)
choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}")
@@ -145,18 +142,19 @@ class OAuthHandler:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
return self._select_profile()
else:
+ # No Local State file, use Default profile
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('browser_profile.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
- logger.error(f"Error loading profiles: {str(e)}")
+ # Error loading profiles, use Default profile
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
except Exception as e:
- logger.error(f"Profile selection error: {str(e)}")
+ # General error, use Default profile
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}")
self.selected_profile = "Default"
return True
@@ -164,16 +162,17 @@ class OAuthHandler:
def setup_browser(self):
"""Setup browser for OAuth flow using selected profile"""
try:
- logger.info("Initializing browser setup")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
+ # Platform-specific initialization
platform_name = platform.system().lower()
- logger.info(f"Detected platform: {platform_name}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
+ # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
+ # Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
browser_path = self._get_browser_path()
@@ -186,12 +185,11 @@ class OAuthHandler:
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, google-chrome-stable"
)
- logger.error("No compatible browser found")
raise Exception(error_msg)
- logger.info(f"Found browser data directory: {user_data_dir}")
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}")
+ # Show warning about closing browser first - 使用动态提示
if self.translator:
warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type)
else:
@@ -201,33 +199,31 @@ class OAuthHandler:
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
if choice != 'y':
- logger.info("Operation cancelled by user")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
+ # Kill existing browser processes
self._kill_browser_processes()
+ # Let user select a profile
if not self._select_profile():
- logger.info("Operation cancelled by user during profile selection")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
+ # Configure browser options
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
- logger.info(f"Starting browser at: {browser_path}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
+ # Verify browser launched successfully
if not self.browser:
- logger.error("Failed to initialize browser instance")
- raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
+ raise Exception(f"{self.translator.get('oauth.browser_failed_to_start', error=str(e)) if self.translator else 'Failed to initialize browser instance'}")
- logger.info("Browser setup completed successfully")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
- logger.error(f"Browser setup failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
@@ -240,10 +236,12 @@ class OAuthHandler:
def _kill_browser_processes(self):
"""Kill existing browser processes based on platform and browser type"""
try:
+ # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
+ # 根据浏览器类型和平台定义要关闭的进程
browser_processes = {
'chrome': {
'win': ['chrome.exe', 'chromium.exe'],
@@ -272,6 +270,7 @@ class OAuthHandler:
}
}
+ # 获取平台类型
if os.name == 'nt':
platform_type = 'win'
elif sys.platform == 'darwin':
@@ -279,31 +278,34 @@ class OAuthHandler:
else:
platform_type = 'linux'
+ # 获取要关闭的进程列表
processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, [])
- logger.info(f"Killing {browser_type} processes: {processes}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
+ if self.translator:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}")
- if os.name == 'nt':
+ # 根据平台关闭进程
+ if os.name == 'nt': # Windows
for proc in processes:
os.system(f'taskkill /f /im {proc} >nul 2>&1')
- else:
+ else: # Linux/Mac
for proc in processes:
os.system(f'pkill -f {proc} >/dev/null 2>&1')
- time.sleep(1)
+ time.sleep(1) # Wait for processes to close
except Exception as e:
- logger.warning(f"Could not kill browser processes: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the default user data directory based on browser type and platform"""
try:
+ # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
- if os.name == 'nt':
+ # 根据操作系统和浏览器类型获取用户数据目录
+ if os.name == 'nt': # Windows
user_data_dirs = {
'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'),
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
@@ -312,7 +314,7 @@ class OAuthHandler:
'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':
+ elif sys.platform == 'darwin': # macOS
user_data_dirs = {
'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'),
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
@@ -321,7 +323,7 @@ class OAuthHandler:
'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
}
- else:
+ else: # Linux
user_data_dirs = {
'chrome': os.path.expanduser('~/.config/google-chrome'),
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
@@ -331,20 +333,19 @@ class OAuthHandler:
'operagx': os.path.expanduser('~/.config/opera-gx')
}
+ # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
user_data_dir = user_data_dirs.get(browser_type)
if user_data_dir and os.path.exists(user_data_dir):
- logger.info(f"Found {browser_type} user data directory: {user_data_dir}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}")
return user_data_dir
else:
- logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}")
- return user_data_dirs['chrome']
+ return user_data_dirs['chrome'] # 回退到 Chrome 目录
except Exception as e:
- logger.error(f"Error getting user data directory: {str(e)}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
+ # 在出错时提供一个默认目录
if os.name == 'nt':
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
elif sys.platform == 'darwin':
@@ -355,24 +356,26 @@ class OAuthHandler:
def _get_browser_path(self):
"""Get appropriate browser path based on platform and selected browser type"""
try:
+ # 从配置中获取浏览器类型
config = get_config(self.translator)
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
browser_type = browser_type.lower()
+ # 首先检查配置中是否有明确指定的浏览器路径
browser_path = config.get('Browser', f'{browser_type}_path', fallback=None)
if browser_path and os.path.exists(browser_path):
- logger.info(f"Using configured {browser_type} path: {browser_path}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}")
return browser_path
+ # 尝试获取默认路径
browser_path = get_default_browser_path(browser_type)
if browser_path and os.path.exists(browser_path):
return browser_path
- logger.info("Searching for alternative browser installations")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
- if os.name == 'nt':
+ # 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器
+ if os.name == 'nt': # Windows
possible_paths = []
if browser_type == 'brave':
possible_paths = [
@@ -399,14 +402,14 @@ class OAuthHandler:
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
]
- else:
+ else: # 默认为 Chrome
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':
+ elif sys.platform == 'darwin': # macOS
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser']
@@ -414,10 +417,10 @@ class OAuthHandler:
possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge']
elif browser_type == 'firefox':
possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox']
- else:
+ else: # 默认为 Chrome
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
- else:
+ else: # Linux
possible_paths = []
if browser_type == 'brave':
possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave']
@@ -425,29 +428,28 @@ class OAuthHandler:
possible_paths = ['/usr/bin/microsoft-edge']
elif browser_type == 'firefox':
possible_paths = ['/usr/bin/firefox']
- else:
+ else: # 默认为 Chrome
possible_paths = [
- '/usr/bin/google-chrome-stable',
+ '/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable
'/usr/bin/google-chrome',
'/usr/bin/chromium',
'/usr/bin/chromium-browser'
]
+ # 检查每个可能的路径
for path in possible_paths:
if os.path.exists(path):
- logger.info(f"Found browser at: {path}")
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
+ # 如果找不到指定浏览器,则尝试使用 Chrome
if browser_type != 'chrome':
- logger.warning(f"Could not find {browser_type}, trying 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}")
return self._get_chrome_path()
return None
except Exception as e:
- logger.error(f"Error finding browser path: {str(e)}", exc_info=True)
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
@@ -458,11 +460,13 @@ class OAuthHandler:
co.set_paths(browser_path=browser_path, user_data_path=user_data_dir)
co.set_argument(f'--profile-directory={active_profile}')
+ # Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
- co.set_argument('--remote-debugging-port=9222')
+ co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口
+ # Platform-specific options
if sys.platform.startswith('linux'):
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
@@ -473,67 +477,57 @@ class OAuthHandler:
co.set_argument('--disable-features=TranslateUI')
co.set_argument('--disable-features=RendererCodeIntegrity')
- logger.info("Browser options configured successfully")
return co
except Exception as e:
- logger.error(f"Error configuring browser options: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
- logger.info("Starting Google OAuth authentication")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
+ # Setup browser
if not self.setup_browser():
- logger.error("Browser failed to initialize")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
+ # Navigate to auth URL
try:
- logger.info("Navigating to authentication page")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after navigation: {self.browser.url}")
+ # Look for Google auth button
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
- "(//a[contains(@class,'auth-method-button')])[1]"
+ "(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
- logger.debug(f"Trying selector: {selector}")
try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5)
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found Google auth button with selector: {selector}")
break
- except Exception as e:
- logger.warning(f"Selector {selector} failed: {str(e)}")
+ except:
continue
if not auth_btn:
- logger.error("Could not find Google authentication button")
raise Exception("Could not find Google authentication button")
- logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
+ # Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
- try:
- auth_btn.click()
- except Exception as e:
- logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click")
- self.browser.run_js("arguments[0].click();", auth_btn)
+ auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ # Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
- logger.info("On Google account selection page")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
+ # 获取配置中是否启用 alert 选项
config = get_config(self.translator)
show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False)
@@ -544,49 +538,43 @@ class OAuthHandler:
alert('{alert_message}');
""")
except:
- logger.warning("Failed to display alert")
- pass
+ pass # Alert is optional
+ # Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
- logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
- logger.info("Google authentication successful")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
- logger.error(f"Authentication error: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
- logger.info("Browser closed")
except:
- logger.warning("Failed to close browser")
pass
except Exception as e:
- logger.error(f"Google OAuth failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _wait_for_auth(self):
"""Wait for authentication to complete and extract auth info"""
try:
- max_wait = 300
+ max_wait = 300 # 5 minutes
start_time = time.time()
- check_interval = 2
+ check_interval = 2 # Check every 2 seconds
- logger.info("Waiting for authentication (timeout: 5 minutes)")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
+ # Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
@@ -594,7 +582,7 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- logger.info("Authentication successful, getting account info")
+ # Get email from settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
@@ -604,17 +592,15 @@ class OAuthHandler:
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
- logger.info(f"Found email: {email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
- logger.warning("Could not find email, using fallback")
- email = "user@cursor.sh"
+ email = "user@cursor.sh" # Fallback email
+ # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -629,163 +615,136 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
+ # Also check URL as backup
if "cursor.com/settings" in self.browser.url:
- logger.info("Detected successful login via URL")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Error during auth wait: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
- logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
- logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
"""Handle GitHub OAuth authentication"""
try:
- logger.info("Starting GitHub OAuth authentication")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
+ # Setup browser
if not self.setup_browser():
- logger.error("Browser failed to initialize")
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
- max_retries = 3
- for attempt in range(max_retries):
- try:
- logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page")
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
- self.browser.get("https://authenticator.cursor.sh/sign-up")
- time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after navigation: {self.browser.url}")
-
- selectors = [
- "//a[contains(@href,'GitHubOAuth')]",
- "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
- "(//a[contains(@class,'auth-method-button')])[2]",
- "//a[contains(text(),'GitHub')]",
- "//a[@href='/auth/github']"
- ]
-
- auth_btn = None
- for selector in selectors:
- logger.debug(f"Trying selector: {selector}")
- try:
- auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5)
- if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found GitHub auth button with selector: {selector}")
- break
- except Exception as e:
- logger.warning(f"Selector {selector} failed: {str(e)}")
- continue
-
- if not auth_btn:
- logger.error("Could not find GitHub authentication button")
- raise Exception("Could not find GitHub authentication button")
-
- logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
+ # Navigate to auth URL
+ try:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
+ self.browser.get("https://authenticator.cursor.sh/sign-up")
+ time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+
+ # Look for GitHub auth button
+ selectors = [
+ "//a[contains(@href,'GitHubOAuth')]",
+ "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
+ "(//a[contains(@class,'auth-method-button')])[2]" # Second auth button as fallback
+ ]
+
+ auth_btn = None
+ for selector in selectors:
try:
- auth_btn.click()
- except Exception as e:
- logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click")
- self.browser.run_js("arguments[0].click();", auth_btn)
- time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL after clicking GitHub button: {self.browser.url}")
-
- auth_info = self._wait_for_auth()
- if not auth_info:
- logger.error("Authentication timeout")
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
- return False, None
-
- logger.info("GitHub authentication successful")
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
- return True, auth_info
-
- except Exception as e:
- logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True)
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
- if attempt < max_retries - 1:
- logger.info("Retrying navigation")
- time.sleep(2)
- continue
- return False, None
- finally:
- try:
- if self.browser and attempt == max_retries - 1:
- self.browser.quit()
- logger.info("Browser closed")
+ auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
+ if auth_btn and auth_btn.is_displayed():
+ break
except:
- logger.warning("Failed to close browser")
- pass
+ continue
+
+ if not auth_btn:
+ raise Exception("Could not find GitHub authentication button")
+
+ # Click the button and wait for page load
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
+ auth_btn.click()
+ time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+
+ # Wait for authentication to complete
+ auth_info = self._wait_for_auth()
+ if not auth_info:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
+ return False, None
+
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
+ return True, auth_info
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
+ return False, None
+ finally:
+ try:
+ if self.browser:
+ self.browser.quit()
+ except:
+ pass
except Exception as e:
- logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _handle_oauth(self, auth_type):
- """Handle OAuth authentication for both Google and GitHub"""
+ """Handle OAuth authentication for both Google and GitHub
+
+ Args:
+ auth_type (str): Type of authentication ('google' or 'github')
+ """
try:
if not self.setup_browser():
return False, None
+ # Navigate to auth URL
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Current URL in _handle_oauth: {self.browser.url}")
+ # Set selectors based on auth type
if auth_type == "google":
selectors = [
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
]
- else:
+ else: # github
selectors = [
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
]
+ # Wait for the button to be available
auth_btn = None
- max_button_wait = 30
+ max_button_wait = 30 # 30 seconds
button_start_time = time.time()
while time.time() - button_start_time < max_button_wait:
for selector in selectors:
- logger.debug(f"Trying selector in _handle_oauth: {selector}")
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
- logger.info(f"Found auth button in _handle_oauth: {selector}")
break
except:
continue
@@ -794,10 +753,11 @@ class OAuthHandler:
time.sleep(1)
if auth_btn:
- logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}")
+ # Click the button and wait for page load
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ # Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
@@ -805,13 +765,13 @@ class OAuthHandler:
alert('{alert_message}');
""")
except Exception as e:
- logger.warning(f"Alert display failed: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
- max_wait = 300
+ # Wait for authentication to complete
+ max_wait = 300 # 5 minutes
start_time = time.time()
last_url = self.browser.url
@@ -819,6 +779,7 @@ class OAuthHandler:
while time.time() - start_time < max_wait:
try:
+ # Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
@@ -826,28 +787,27 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
- logger.info("Authentication successful in _handle_oauth")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
+ # Navigate to settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
- time.sleep(3)
+ time.sleep(3) # Wait for settings page to load
+ # Get email from settings page
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- logger.info(f"Found email: {actual_email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not find email: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
+ # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -862,30 +822,26 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
+ # Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
+ # Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
- logger.info("Already on settings page")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
@@ -894,22 +850,21 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
if token:
+ # Get email and check usage here too
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
- logger.info(f"Found email: {actual_email}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not find email: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
+ # Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- logger.info(f"Usage count: {usage_text}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
@@ -924,58 +879,49 @@ class OAuthHandler:
return False
if check_usage_limits(usage_text):
- logger.info("Account has reached maximum usage, deleting")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
- logger.info("Starting new authentication process")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
- logger.error("Failed to delete expired account")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
- logger.info(f"Account is still valid (Usage: {usage_text})")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- logger.warning(f"Could not check usage count: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
+ # Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
- logger.info(f"Page changed to {current_url}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
- logger.warning(f"Status check error: {str(e)}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
- logger.error("Authentication timeout")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
- logger.error("Authentication button not found")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
- logger.error(f"Authentication failed: {str(e)}", exc_info=True)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
self.browser.quit()
- logger.info("Browser closed in _handle_oauth")
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
try:
+ # Get cookies with retry
max_retries = 3
for attempt in range(max_retries):
try:
@@ -988,7 +934,7 @@ class OAuthHandler:
raise
time.sleep(1)
- logger.info(f"Found {len(cookies)} cookies")
+ # Debug cookie information
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
@@ -1001,14 +947,12 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
except Exception as e:
- logger.error(f"Failed to extract token: {str(e)}")
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":
email = cookie.get("value")
if email and token:
- logger.info(f"Authentication successful - Email: {email}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
@@ -1017,13 +961,11 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
- logger.error(f"Missing authentication data: {', '.join(missing)}")
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
except Exception as e:
- logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True)
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
@@ -1056,21 +998,55 @@ class OAuthHandler:
return deleteAccount();
"""
- logger.info("Attempting to delete account via API")
result = self.browser.run_js(delete_js)
- logger.info(f"Delete account result: {result}")
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.delete_account_success', result=result) if self.translator else f'Delete account result: {result}'}{Style.RESET_ALL}")
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
- logger.info("Redirecting to authenticator.cursor.sh")
+ # Navigate back to auth page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
- logger.info(f"Navigated to sign-up page: {self.browser.url}")
return True
except Exception as e:
- logger.error(f"Failed to delete account: {str(e)}", exc_info=True)
- error_message = self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'
+ 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
\ No newline at end of file
+ return False
+
+def main(auth_type, translator=None):
+ """Main function to handle OAuth authentication
+
+ Args:
+ auth_type (str): Type of authentication ('google' or 'github')
+ translator: Translator instance for internationalization
+ """
+ handler = OAuthHandler(translator, auth_type)
+
+ if auth_type.lower() == 'google':
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
+ success, auth_info = handler.handle_google_auth()
+ elif auth_type.lower() == 'github':
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
+ success, auth_info = handler.handle_github_auth()
+ else:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
+ return False
+
+ if success and auth_info:
+ # Update Cursor authentication
+ auth_manager = CursorAuth(translator)
+ if auth_manager.update_auth(
+ email=auth_info["email"],
+ access_token=auth_info["token"],
+ refresh_token=auth_info["token"]
+ ):
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
+ # Close the browser after successful authentication
+ if handler.browser:
+ handler.browser.quit()
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
+ return True
+ else:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
+
+ return False
\ No newline at end of file
From f325690e32466c950a90f3dfe873dfeeb98fd5de Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Mon, 14 Apr 2025 23:48:25 +0800
Subject: [PATCH 13/15] add: more output
---
oauth_auth.py | 125 +++++++-------------------------------------------
1 file changed, 16 insertions(+), 109 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index 4313041..d4ff098 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -676,7 +676,8 @@ class OAuthHandler:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
- except:
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}")
continue
if not auth_btn:
@@ -703,13 +704,13 @@ class OAuthHandler:
try:
if self.browser:
self.browser.quit()
- except:
- pass
-
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.quit_browser_error', error=str(e)) if self.translator else f'Error quitting browser: {str(e)}'}{Style.RESET_ALL}")
+
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
-
+
def _handle_oauth(self, auth_type):
"""Handle OAuth authentication for both Google and GitHub
@@ -746,7 +747,8 @@ class OAuthHandler:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
break
- except:
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}")
continue
if auth_btn:
break
@@ -808,115 +810,20 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
-
- def check_usage_limits(usage_str):
- try:
- parts = usage_str.split('/')
- if len(parts) != 2:
- return False
- current = int(parts[0].strip())
- limit = int(parts[1].strip())
- return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
- except:
- return False
-
- if check_usage_limits(usage_text):
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
- if self._delete_current_account():
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
- if self.auth_type == "google":
- return self.handle_google_auth()
- else:
- return self.handle_github_auth()
- else:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
- else:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_usage_count', usage=usage_text) if self.translator else f'Found usage count: {usage_text}'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
-
- # Remove the browser stay open prompt and input wait
- return True, {"email": actual_email, "token": token}
-
- # Also check URL as backup
- current_url = self.browser.url
- if "cursor.com/settings" in current_url:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
- time.sleep(1)
- cookies = self.browser.cookies()
- for cookie in cookies:
- if cookie.get("name") == "WorkosCursorSessionToken":
- value = cookie.get("value", "")
- token = get_token_from_cookie(value, self.translator)
- if token:
- # Get email and check usage here too
- try:
- email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
- if email_element:
- actual_email = email_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
- except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
- actual_email = "user@cursor.sh"
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
- # Check usage count
- try:
- usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
- if usage_element:
- usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
-
- def check_usage_limits(usage_str):
- try:
- parts = usage_str.split('/')
- if len(parts) != 2:
- return False
- current = int(parts[0].strip())
- limit = int(parts[1].strip())
- return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
- except:
- return False
-
- if check_usage_limits(usage_text):
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
- if self._delete_current_account():
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
- if self.auth_type == "google":
- return self.handle_google_auth()
- else:
- return self.handle_github_auth()
- else:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
- else:
- print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
- except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
-
- # Remove the browser stay open prompt and input wait
- return True, {"email": actual_email, "token": token}
- elif current_url != last_url:
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
- last_url = current_url
- time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ return True, actual_email
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
- time.sleep(1)
- continue
- time.sleep(1)
-
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_checking_cookies', error=str(e)) if self.translator else f'Error checking cookies: {str(e)}'}{Style.RESET_ALL}")
+ time.sleep(5)
+
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout during authentication'}{Style.RESET_ALL}")
return False, None
-
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
- return False, None
-
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e)) if self.translator else f'OAuth failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
- finally:
- if self.browser:
- self.browser.quit()
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
From 105b5d45178e543dcd5aa7d2ac26f8c246d3e815 Mon Sep 17 00:00:00 2001
From: Canmi <9997200@qq.com>
Date: Tue, 15 Apr 2025 00:00:14 +0800
Subject: [PATCH 14/15] fix: github ouath fail error
---
oauth_auth.py | 129 +++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 111 insertions(+), 18 deletions(-)
diff --git a/oauth_auth.py b/oauth_auth.py
index d4ff098..4313041 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -676,8 +676,7 @@ class OAuthHandler:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}")
+ except:
continue
if not auth_btn:
@@ -704,13 +703,13 @@ class OAuthHandler:
try:
if self.browser:
self.browser.quit()
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.quit_browser_error', error=str(e)) if self.translator else f'Error quitting browser: {str(e)}'}{Style.RESET_ALL}")
-
+ except:
+ pass
+
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
-
+
def _handle_oauth(self, auth_type):
"""Handle OAuth authentication for both Google and GitHub
@@ -747,8 +746,7 @@ class OAuthHandler:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
break
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}")
+ except:
continue
if auth_btn:
break
@@ -810,20 +808,115 @@ class OAuthHandler:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
- print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_usage_count', usage=usage_text) if self.translator else f'Found usage count: {usage_text}'}{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
+
+ def check_usage_limits(usage_str):
+ try:
+ parts = usage_str.split('/')
+ if len(parts) != 2:
+ return False
+ current = int(parts[0].strip())
+ limit = int(parts[1].strip())
+ return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
+ except:
+ return False
+
+ if check_usage_limits(usage_text):
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
+ if self._delete_current_account():
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
+ if self.auth_type == "google":
+ return self.handle_google_auth()
+ else:
+ return self.handle_github_auth()
+ else:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
+ else:
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}")
-
- return True, actual_email
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_checking_cookies', error=str(e)) if self.translator else f'Error checking cookies: {str(e)}'}{Style.RESET_ALL}")
- time.sleep(5)
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
+
+ # Remove the browser stay open prompt and input wait
+ return True, {"email": actual_email, "token": token}
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout during authentication'}{Style.RESET_ALL}")
+ # Also check URL as backup
+ current_url = self.browser.url
+ if "cursor.com/settings" in current_url:
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
+ time.sleep(1)
+ cookies = self.browser.cookies()
+ for cookie in cookies:
+ if cookie.get("name") == "WorkosCursorSessionToken":
+ value = cookie.get("value", "")
+ token = get_token_from_cookie(value, self.translator)
+ if token:
+ # Get email and check usage here too
+ try:
+ email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
+ if email_element:
+ actual_email = email_element.text
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
+ except Exception as e:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
+ actual_email = "user@cursor.sh"
+
+ # Check usage count
+ try:
+ usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
+ if usage_element:
+ usage_text = usage_element.text
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
+
+ def check_usage_limits(usage_str):
+ try:
+ parts = usage_str.split('/')
+ if len(parts) != 2:
+ return False
+ current = int(parts[0].strip())
+ limit = int(parts[1].strip())
+ return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
+ except:
+ return False
+
+ if check_usage_limits(usage_text):
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
+ if self._delete_current_account():
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
+ if self.auth_type == "google":
+ return self.handle_google_auth()
+ else:
+ return self.handle_github_auth()
+ else:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
+ else:
+ print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
+ except Exception as e:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
+
+ # Remove the browser stay open prompt and input wait
+ return True, {"email": actual_email, "token": token}
+ elif current_url != last_url:
+ print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
+ last_url = current_url
+ time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
+ except Exception as e:
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
+ time.sleep(1)
+ continue
+ time.sleep(1)
+
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
- except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e)) if self.translator else f'OAuth failed: {str(e)}'}{Style.RESET_ALL}")
+
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
+
+ except Exception as e:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
+ return False, None
+ finally:
+ if self.browser:
+ self.browser.quit()
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
From caecbd4c8d86fd5b75b5c96e9fcc4fa8870452b8 Mon Sep 17 00:00:00 2001
From: Pin Studios
Date: Tue, 15 Apr 2025 10:24:07 +0800
Subject: [PATCH 15/15] fix(readme): format download links for browsers
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 774b7dc..2991a4e 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
##### 如果沒有瀏覽器,可以從这里下載
-[Google Chrome](https://www.google.com/intl/en_pk/chrome/) or [Opera](https://www.opera.com/download) or [Edge](https://www.microsoft.com/en-us/edge) or [Firefox](https://www.mozilla.org/en-US/firefox/new/) or [Brave](https://www.brave.com/download/)
+[Google Chrome](https://www.google.com/intl/en_pk/chrome/) | [Opera](https://www.opera.com/download) | [Edge](https://www.microsoft.com/en-us/edge) | [Firefox](https://www.mozilla.org/en-US/firefox/new/) | [Brave](https://www.brave.com/download/)