mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2026-01-03 09:19:27 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5fceb2624 | ||
|
|
45046002df | ||
|
|
6bb33cec4c | ||
|
|
66f6197e6d | ||
|
|
5514e47759 | ||
|
|
3a53d59d52 | ||
|
|
c2e5499f19 | ||
|
|
9a223756c5 | ||
|
|
9b5912357d |
116
CHANGELOG.md
116
CHANGELOG.md
@@ -1,31 +1,39 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v1.7.08
|
||||||
|
1. Add: Google OAuth Authentication | 增加 Google OAuth 認證
|
||||||
|
2. Add: GitHub OAuth Authentication | 增加 GitHub OAuth 認證
|
||||||
|
3. Add: Lifetime Access for OAuth Users | 增加 OAuth 用戶終身訪問權限
|
||||||
|
4. Add: OAuth Authentication Integration | 增加 OAuth 認證集成
|
||||||
|
5. Update: Menu System with OAuth Options | 更新菜單系統,添加 OAuth 選項
|
||||||
|
6. Add: Multi-language Support for OAuth | 增加 OAuth 多語言支持
|
||||||
|
|
||||||
## v1.7.07
|
## v1.7.07
|
||||||
1. Add: Vietnamese Language | 增加越南語言
|
1. Add: Vietnamese Language | 增加越南語言
|
||||||
2. Add: Admin Privilege Management for Windows Executable | 增加Windows可執行文件管理員權限
|
2. Add: Admin Privilege Management for Windows Executable | 增加 Windows 可執行文件管理員權限
|
||||||
3. Implement admin privilege detection for Windows platform | 實現Windows平台管理員權限檢測
|
3. Implement admin privilege detection for Windows platform | 實現 Windows 平台管理員權限檢測
|
||||||
4. Add functions to check and request admin rights when running as a frozen executable | 增加檢查和請求管理員權限的功能
|
4. Add functions to check and request admin rights when running as a frozen executable | 增加檢查和請求管理員權限的功能
|
||||||
5. Enhance startup process with admin privilege verification | 增強啟動過程中的管理員權限驗證
|
5. Enhance startup process with admin privilege verification | 增強啟動過程中的管理員權限驗證
|
||||||
6. Add new admin-related emoji to the EMOJI dictionary | 增加新的管理員相關表情符號到EMOJI字典
|
6. Add new admin-related emoji to the EMOJI dictionary | 增加新的管理員相關表情符號到 EMOJI 字典
|
||||||
7. Provide fallback mechanism for non-Windows platforms (macos and linux ) | 提供非Windows平台(macos和linux)的回退機制
|
7. Provide fallback mechanism for non-Windows platforms (macos and linux ) | 提供非 Windows 平台(macos 和 linux)的回退機制
|
||||||
These changes make the application more user-friendly by only requesting admin privileges when necessary (when running as an executable). | 這些改進使應用程序更易於使用,只在必要時(當作為可執行文件運行時)請求管理員權限。
|
These changes make the application more user-friendly by only requesting admin privileges when necessary (when running as an executable). | 這些改進使應用程序更易於使用,只在必要時(當作為可執行文件運行時)請求管理員權限。
|
||||||
|
|
||||||
## v1.7.06
|
## v1.7.06
|
||||||
1. Add: Update Confirm | 增加更新確認
|
1. Add: Update Confirm | 增加更新確認
|
||||||
2. Add: Update Skipped | 增加更新跳過
|
2. Add: Update Skipped | 增加更新跳過
|
||||||
3. Add: Invalid Choice | 增加無效選擇
|
3. Add: Invalid Choice | 增加無效選擇
|
||||||
4. Fix: Cursor Path | 修復Cursor路徑
|
4. Fix: Cursor Path | 修復 Cursor 路徑
|
||||||
5. Fix: Path Encoding | 修復路徑編碼
|
5. Fix: Path Encoding | 修復路徑編碼
|
||||||
6. Fix: Getting Verification Code | 修復獲取驗證碼
|
6. Fix: Getting Verification Code | 修復獲取驗證碼
|
||||||
7. Fix: Setting Password | 修復設置密碼
|
7. Fix: Setting Password | 修復設置密碼
|
||||||
8. Fix: Disable Auto Update | 修復禁用自動更新
|
8. Fix: Disable Auto Update | 修復禁用自動更新
|
||||||
9. Add Config.py | 增加Config.py
|
9. Add Config.py | 增加 Config.py
|
||||||
10. Add utils.py | 增加utils.py
|
10. Add utils.py | 增加 utils.py
|
||||||
11. Rebuild some logic | 重新構建一些邏輯
|
11. Rebuild some logic | 重新構建一些邏輯
|
||||||
|
|
||||||
|
|
||||||
## v1.7.05
|
## v1.7.05
|
||||||
1. Fix: Cursor Version Check | 修復Cursor版本檢查
|
1. Fix: Cursor Version Check | 修復 Cursor 版本檢查
|
||||||
2. Fix: Small Problem | 修復一些小問題
|
2. Fix: Small Problem | 修復一些小問題
|
||||||
|
|
||||||
|
|
||||||
@@ -36,10 +44,10 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
1. Hotfix: Small Problem | 修復一些小問題
|
1. Hotfix: Small Problem | 修復一些小問題
|
||||||
|
|
||||||
## v1.7.02
|
## v1.7.02
|
||||||
1. Fix: Cursor Path | 修復Cursor路徑
|
1. Fix: Cursor Path | 修復 Cursor 路徑
|
||||||
2. Add: Config File | 增加配置文件
|
2. Add: Config File | 增加配置文件
|
||||||
3. Remove: Workbench Cursor Path | 移除Workbench Cursor路徑
|
3. Remove: Workbench Cursor Path | 移除 Workbench Cursor 路徑
|
||||||
4. Remove: Cursor Main JS | 移除Cursor main.js
|
4. Remove: Cursor Main JS | 移除 Cursor main.js
|
||||||
|
|
||||||
## v1.7.01
|
## v1.7.01
|
||||||
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
|
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
|
||||||
@@ -54,10 +62,10 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
2. Add: Test some Bypass Code | 測試一些繞過代碼
|
2. Add: Test some Bypass Code | 測試一些繞過代碼
|
||||||
|
|
||||||
## v1.6.01
|
## v1.6.01
|
||||||
1. Fix: Cursor Auth | 修復Cursor Auth
|
1. Fix: Cursor Auth | 修復 Cursor Auth
|
||||||
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
|
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
|
||||||
3. Fix: Cursor Auth Error | 修復Cursor Auth錯誤
|
3. Fix: Cursor Auth Error | 修復 Cursor Auth 錯誤
|
||||||
4. Fix: Update Curl Faild | 修復更新Curl失敗
|
4. Fix: Update Curl Faild | 修復更新 Curl 失敗
|
||||||
|
|
||||||
## v1.5.03
|
## v1.5.03
|
||||||
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
|
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
|
||||||
@@ -80,8 +88,8 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
1. Add: Print Some Account Info | 增加打印一些賬號信息
|
1. Add: Print Some Account Info | 增加打印一些賬號信息
|
||||||
|
|
||||||
## v1.4.07
|
## v1.4.07
|
||||||
1. Add Removed break statements after each operation | 修改結束event後的break暫停應用
|
1. Add Removed break statements after each operation | 修改結束 event 後的 break 暫停應用
|
||||||
2. Added print_menu() calls to show the menu again | 添加print_menu()調用以再次顯示菜單
|
2. Added print_menu() calls to show the menu again | 添加 print_menu()調用以再次顯示菜單
|
||||||
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
|
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
|
||||||
|
|
||||||
## v1.4.06
|
## v1.4.06
|
||||||
@@ -96,7 +104,7 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
|
|
||||||
## v1.4.05
|
## v1.4.05
|
||||||
|
|
||||||
1. Fix: macOS Language Detection | 修復macOS語言檢測
|
1. Fix: macOS Language Detection | 修復 macOS 語言檢測
|
||||||
|
|
||||||
|
|
||||||
## v1.4.04
|
## v1.4.04
|
||||||
@@ -108,46 +116,46 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
|
|
||||||
## v1.4.03
|
## v1.4.03
|
||||||
|
|
||||||
1. Switch to API-based Registration System | 改用API註冊系統替代瀏覽器操作
|
1. Switch to API-based Registration System | 改用 API 註冊系統替代瀏覽器操作
|
||||||
2. Add Support for Latest Cursor Version | 增加支持最新版本Cursor
|
2. Add Support for Latest Cursor Version | 增加支持最新版本 Cursor
|
||||||
3. Enhance Translation System | 優化多語言翻譯系統
|
3. Enhance Translation System | 優化多語言翻譯系統
|
||||||
4. Add Database Connection Status Messages | 增加數據庫連接狀態提示
|
4. Add Database Connection Status Messages | 增加數據庫連接狀態提示
|
||||||
5. Improve Error Handling for Database Operations | 改進數據庫操作的錯誤處理
|
5. Improve Error Handling for Database Operations | 改進數據庫操作的錯誤處理
|
||||||
6. Add New API Integration | 新增API集成
|
6. Add New API Integration | 新增 API 集成
|
||||||
7. Optimize Performance and Stability | 優化性能和穩定性
|
7. Optimize Performance and Stability | 優化性能和穩定性
|
||||||
|
|
||||||
## v1.4.01
|
## v1.4.01
|
||||||
|
|
||||||
1. Add Disable Cursor Auto Upgrade | 增加禁用Cursor自動升級
|
1. Add Disable Cursor Auto Upgrade | 增加禁用 Cursor 自動升級
|
||||||
|
|
||||||
## v1.3.02
|
## v1.3.02
|
||||||
|
|
||||||
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
|
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
|
||||||
2. Add PayPal | 增加PayPal
|
2. Add PayPal | 增加 PayPal
|
||||||
3. Very Small Fix | 非常小的修復
|
3. Very Small Fix | 非常小的修復
|
||||||
4. Fix main.py option number | 修復main.py選項數量
|
4. Fix main.py option number | 修復 main.py 選項數量
|
||||||
|
|
||||||
## v1.3.01
|
## v1.3.01
|
||||||
|
|
||||||
1. Add Manual Email Input | 增加手動輸入郵箱地址
|
1. Add Manual Email Input | 增加手動輸入郵箱地址
|
||||||
2. Add Manual Code Input | 增加手動輸入驗證碼
|
2. Add Manual Code Input | 增加手動輸入驗證碼
|
||||||
3. Fix Cursor Options | 修復Cursor選項
|
3. Fix Cursor Options | 修復 Cursor 選項
|
||||||
|
|
||||||
|
|
||||||
## v1.2.02
|
## v1.2.02
|
||||||
|
|
||||||
1. Add PBlock | 增加PBlock
|
1. Add PBlock | 增加 PBlock
|
||||||
2. Remove uBlock0.chromium | 移除uBlock0.chromium
|
2. Remove uBlock0.chromium | 移除 uBlock0.chromium
|
||||||
3. Optimize the logic of the script | 優化腳本邏輯
|
3. Optimize the logic of the script | 優化腳本邏輯
|
||||||
4. Optimize Size | 優化大小
|
4. Optimize Size | 優化大小
|
||||||
|
|
||||||
|
|
||||||
## v1.2.01
|
## v1.2.01
|
||||||
|
|
||||||
1. Fix Cursor Cloudflare Human Verification Problem | 修復Cursor Cloudflare人機驗證問題
|
1. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
|
||||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||||
3. Change Mail Site | 改變郵箱網站
|
3. Change Mail Site | 改變郵箱網站
|
||||||
4. Fix Cursor Cloudflare Problem | 修復Cursor Cloudflare問題
|
4. Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
|
||||||
|
|
||||||
|
|
||||||
## v1.1.01
|
## v1.1.01
|
||||||
@@ -156,15 +164,15 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
1. Hot Fix Cursor Cloudflare Problem | 修復Cursor Cloudflare問題
|
1. Hot Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
|
||||||
2. Fix Cursor Cloudflare Human Verification Problem | 修復Cursor Cloudflare人機驗證問題
|
2. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
|
||||||
3. Remake signup logic | 重做註冊邏輯
|
3. Remake signup logic | 重做註冊邏輯
|
||||||
|
|
||||||
|
|
||||||
## v1.0.10
|
## v1.0.10
|
||||||
|
|
||||||
1. Hot Fix Mac Chrome Problem | 修復Mac Chrome問題
|
1. Hot Fix Mac Chrome Problem | 修復 Mac Chrome 問題
|
||||||
2. Fix Linux Start Donet Problem | 修復Linux啟動開發者問題
|
2. Fix Linux Start Donet Problem | 修復 Linux 啟動開發者問題
|
||||||
|
|
||||||
|
|
||||||
## v1.0.9
|
## v1.0.9
|
||||||
@@ -173,15 +181,15 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
1. Fixed New 0.45.x Version Reset Machine | 修復新0.45版本重置機器
|
1. Fixed New 0.45.x Version Reset Machine | 修復新 0.45 版本重置機器
|
||||||
2. Fix Locale Language | 修復多語言
|
2. Fix Locale Language | 修復多語言
|
||||||
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
||||||
4. Add Remake main.js | 重做main.js
|
4. Add Remake main.js | 重做 main.js
|
||||||
|
|
||||||
|
|
||||||
## v1.0.8
|
## v1.0.8
|
||||||
|
|
||||||
1. Fix New 0.45 Version Reset Machine | 修復新0.45版本重置機器
|
1. Fix New 0.45 Version Reset Machine | 修復新 0.45 版本重置機器
|
||||||
2. Fix Locale Language | 修復多語言
|
2. Fix Locale Language | 修復多語言
|
||||||
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
|
||||||
|
|
||||||
@@ -202,17 +210,17 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
|
|
||||||
## v1.0.6
|
## v1.0.6
|
||||||
|
|
||||||
1. Add Quit Cursor Option | 增加退出Cursor選項
|
1. Add Quit Cursor Option | 增加退出 Cursor 選項
|
||||||
2. Add Recaptcha Path Patch | 增加Recaptcha路徑修復
|
2. Add Recaptcha Path Patch | 增加 Recaptcha 路徑修復
|
||||||
3. Fix Admin Permission | 修復管理員權限問題
|
3. Fix Admin Permission | 修復管理員權限問題
|
||||||
4. Remove all need admin permission | 移除所有需要管理員權限
|
4. Remove all need admin permission | 移除所有需要管理員權限
|
||||||
|
|
||||||
|
|
||||||
## v1.0.5 - HotFix
|
## v1.0.5 - HotFix
|
||||||
|
|
||||||
1. Fix: Mac Browser Control | 修復Mac瀏覽器控制問題
|
1. Fix: Mac Browser Control | 修復 Mac 瀏覽器控制問題
|
||||||
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
||||||
3. Add Linux Support | 增加Linux支持
|
3. Add Linux Support | 增加 Linux 支持
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
|
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
|
||||||
</p>
|
</p>
|
||||||
@@ -220,7 +228,7 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
|
|
||||||
## v1.0.5
|
## v1.0.5
|
||||||
|
|
||||||
1. Remove MachineID | 移除機器碼ID
|
1. Remove MachineID | 移除機器碼 ID
|
||||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||||
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
||||||
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
||||||
@@ -231,16 +239,16 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
|
|
||||||
## v1.0.4
|
## v1.0.4
|
||||||
|
|
||||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||||
2. Fix Cloud Lame | 修復雲端慢速模式
|
2. Fix Cloud Lame | 修復雲端慢速模式
|
||||||
|
|
||||||
|
|
||||||
## v1.0.3
|
## v1.0.3
|
||||||
|
|
||||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||||
2. Add Manual Reset Machine | 增加手動重置機器
|
2. Add Manual Reset Machine | 增加手動重置機器
|
||||||
3. Add CDN Cloud Control WatchDog | 增加CDN雲端控制WatchDog
|
3. Add CDN Cloud Control WatchDog | 增加 CDN 雲端控制 WatchDog
|
||||||
4. Add Mac OS Support | 增加Mac OS支持
|
4. Add Mac OS Support | 增加 Mac OS 支持
|
||||||
5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊
|
5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/what_2025-01-13_13-32-54.png" alt="Why" width="400"/><br>
|
<img src="./images/what_2025-01-13_13-32-54.png" alt="Why" width="400"/><br>
|
||||||
@@ -252,21 +260,21 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
1. Fix: Some known issues | 修復了一些已知問題
|
1. Fix: Some known issues | 修復了一些已知問題
|
||||||
2. Add cloud control device code | 增加雲端控制設備碼
|
2. Add cloud control device code | 增加雲端控制設備碼
|
||||||
3. Cloud reset device code | 雲端重置設備碼
|
3. Cloud reset device code | 雲端重置設備碼
|
||||||
4. Remove official WatchDog monitoring | 移除官方WatchDog監控
|
4. Remove official WatchDog monitoring | 移除官方 WatchDog 監控
|
||||||
5. Remove Proxy official prompt | 移除Proxy 官方提示
|
5. Remove Proxy official prompt | 移除 Proxy 官方提示
|
||||||
6. Fix: Too Many Computer | 修復Too Many Computer 問題
|
6. Fix: Too Many Computer | 修復 Too Many Computer 問題
|
||||||
7. Fix Billing Issue | 修復計費問題
|
7. Fix Billing Issue | 修復計費問題
|
||||||
8. Fix: Cursor's configuration | 修復Cursor的配置問題
|
8. Fix: Cursor's configuration | 修復 Cursor 的配置問題
|
||||||
9. Fix cursor-slow mode | 修復cursor-slow模式
|
9. Fix cursor-slow mode | 修復 cursor-slow 模式
|
||||||
|
|
||||||
|
|
||||||
## v1.0.1
|
## v1.0.1
|
||||||
|
|
||||||
1. Fix: Reset machine ID | 修復了重置機器ID的問題
|
1. Fix: Reset machine ID | 修復了重置機器 ID 的問題
|
||||||
2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題
|
2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題
|
||||||
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為pro會員的問題
|
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為 pro 會員的問題
|
||||||
4. Fix: Real-time send Token request | 修復了 實時發送Token請求的問題
|
4. Fix: Real-time send Token request | 修復了 實時發送 Token 請求的問題
|
||||||
5. Fix: Reset Cursor's configuration | 修復了 重置Cursor的配置的問題
|
5. Fix: Reset Cursor's configuration | 修復了 重置 Cursor 的配置的問題
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -278,7 +286,7 @@ These changes make the application more user-friendly by only requesting admin p
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/pro_2025-01-11_00-51-07.png" alt="Cursor Pro Logo" width="400"/><br>
|
<img src="./images/pro_2025-01-11_00-51-07.png" alt="Cursor Pro Logo" width="400"/><br>
|
||||||
</p>
|
</p>
|
||||||
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下MachineID 聯繫作者
|
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下 MachineID 聯繫作者
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|||||||
63
README.md
63
README.md
@@ -1,4 +1,5 @@
|
|||||||
# ➤ Cursor Free VIP
|
# ➤ Cursor Free VIP
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
|
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
|
||||||
@@ -12,45 +13,50 @@
|
|||||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<h4>Support Latest 0.47.x Version | 支持最新0.47.x版本</h4>
|
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
|
||||||
|
|
||||||
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
This is a tool to automatically register, support Windows and macOS systems, complete Auth verification, and reset
|
||||||
|
Cursor's configuration.
|
||||||
|
|
||||||
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
|
<img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||||
|
|
||||||
##### 如果沒有Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
##### 如果沒有 Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||||
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
## 🔄 Change Log | 更新日志
|
## 🔄 Change Log | 更新日志
|
||||||
|
|
||||||
[Watch Change Log | 查看更新日志](CHANGELOG.md)
|
[Watch Change Log | 查看更新日志](CHANGELOG.md)
|
||||||
|
|
||||||
## ✨ Features | 功能特點
|
## ✨ Features | 功能特點
|
||||||
|
|
||||||
* Automatically register Cursor membership<br>自動註冊Cursor會員<br>
|
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
|
||||||
|
|
||||||
|
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
|
||||||
|
|
||||||
|
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
|
||||||
|
|
||||||
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
|
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
|
||||||
|
|
||||||
* Complete Auth verification<br>完成Auth驗證<br>
|
* Complete Auth verification<br>完成 Auth 驗證<br>
|
||||||
|
|
||||||
* Reset Cursor's configuration<br>重置Cursor的配置<br>
|
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
|
||||||
|
|
||||||
|
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
|
||||||
|
|
||||||
## 💻 System Support | 系統支持
|
## 💻 System Support | 系統支持
|
||||||
|
|
||||||
|Windows|x64|✅|macOS|Intel|✅|
|
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|
||||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
|
||||||
|Windows|x86|✅|macOS|Apple Silicon|✅|
|
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
|
||||||
|Linux|x64|✅|Linux|x86|✅|
|
| Linux | x64 | ✅ | Linux | x86 | ✅ |
|
||||||
|Linux|ARM64|✅|Linux|ARM64|✅|
|
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
|
||||||
|
|
||||||
## 👀 How to use | 如何使用
|
## 👀 How to use | 如何使用
|
||||||
|
|
||||||
@@ -58,28 +64,34 @@ This is a tool to automatically register , support Windows and macOS systems, co
|
|||||||
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
|
||||||
|
|
||||||
**Linux/macOS**
|
**Linux/macOS**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows**
|
**Windows**
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
||||||
|
|
||||||
**Linux/macOS**
|
**Linux/macOS**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
|
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows**
|
**Windows**
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
|
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
|
||||||
@@ -140,6 +152,7 @@ retry_interval = 8-12
|
|||||||
# Max Timeout | 最大超時時間
|
# Max Timeout | 最大超時時間
|
||||||
max_timeout = 160
|
max_timeout = 160
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
* Use administrator to run the script <br>請使用管理員身份運行腳本
|
* Use administrator to run the script <br>請使用管理員身份運行腳本
|
||||||
@@ -150,15 +163,11 @@ max_timeout = 160
|
|||||||
|
|
||||||
* Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款
|
* Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 🚨 Common Issues | 常見問題
|
## 🚨 Common Issues | 常見問題
|
||||||
|
|
||||||
|如果遇到權限問題,請確保:| 此腳本以管理員身份運行 |
|
| 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 |
|
||||||
|:---:|:---:|
|
|:--------------------------------------------------:|:------------------------------------------------:|
|
||||||
|If you encounter permission issues, please ensure: | This script is run with administrator privileges |
|
| If you encounter permission issues, please ensure: | This script is run with administrator privileges |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 🤩 Contribution | 貢獻
|
## 🤩 Contribution | 貢獻
|
||||||
|
|
||||||
@@ -170,12 +179,12 @@ max_timeout = 160
|
|||||||
</a>
|
</a>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
## 📩 Disclaimer | 免責聲明
|
## 📩 Disclaimer | 免責聲明
|
||||||
|
|
||||||
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
||||||
|
|
||||||
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
|
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne
|
||||||
|
by the user.
|
||||||
|
|
||||||
## 💰 Buy Me a Coffee | 請我喝杯咖啡
|
## 💰 Buy Me a Coffee | 請我喝杯咖啡
|
||||||
|
|
||||||
@@ -202,9 +211,5 @@ This tool is only for learning and research purposes, and any consequences arisi
|
|||||||
|
|
||||||
## 📝 License | 授權
|
## 📝 License | 授權
|
||||||
|
|
||||||
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
|
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
|
||||||
Please refer to the [LICENSE](LICENSE.md) file for details.
|
Please refer to the [LICENSE](LICENSE.md) file for details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
oakon.com
|
oakon.com
|
||||||
famamail.com
|
famamail.com
|
||||||
2925.com
|
2925.com
|
||||||
|
indigobook.com
|
||||||
|
teihu.com
|
||||||
|
|||||||
5
cursor_register_github.py
Normal file
5
cursor_register_github.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from oauth_auth import main as oauth_main
|
||||||
|
|
||||||
|
def main(translator=None):
|
||||||
|
"""Handle GitHub OAuth registration"""
|
||||||
|
oauth_main('github', translator)
|
||||||
5
cursor_register_google.py
Normal file
5
cursor_register_google.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from oauth_auth import main as oauth_main
|
||||||
|
|
||||||
|
def main(translator=None):
|
||||||
|
"""Handle Google OAuth registration"""
|
||||||
|
oauth_main('google', translator)
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
"exit": "Exit Program",
|
"exit": "Exit Program",
|
||||||
"reset": "Reset Machine ID",
|
"reset": "Reset Machine ID",
|
||||||
"register": "Register New Cursor Account",
|
"register": "Register New Cursor Account",
|
||||||
|
"register_google": "Register with Google Account",
|
||||||
|
"register_github": "Register with GitHub Account",
|
||||||
"register_manual": "Register Cursor with Custom Email",
|
"register_manual": "Register Cursor with Custom Email",
|
||||||
"quit": "Close Cursor Application",
|
"quit": "Close Cursor Application",
|
||||||
"select_language": "Change Language",
|
"select_language": "Change Language",
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
"exit": "退出程序",
|
"exit": "退出程序",
|
||||||
"reset": "重置机器标识",
|
"reset": "重置机器标识",
|
||||||
"register": "注册新 Cursor 账号",
|
"register": "注册新 Cursor 账号",
|
||||||
|
"register_google": "使用 Google 账号注册",
|
||||||
|
"register_github": "使用 GitHub 账号注册",
|
||||||
"register_manual": "使用自定义邮箱注册",
|
"register_manual": "使用自定义邮箱注册",
|
||||||
"quit": "关闭 Cursor 应用",
|
"quit": "关闭 Cursor 应用",
|
||||||
"select_language": "更改语言",
|
"select_language": "更改语言",
|
||||||
|
|||||||
28
main.py
28
main.py
@@ -219,10 +219,14 @@ def print_menu():
|
|||||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
||||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
||||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
|
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
|
||||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
print(f"{Fore.GREEN}3{Style.RESET_ALL}. 🌟 {translator.get('menu.register_google')}")
|
||||||
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
print(f"{Fore.YELLOW} ┗━━ 🔥 LIFETIME ACCESS ENABLED 🔥{Style.RESET_ALL}")
|
||||||
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
print(f"{Fore.GREEN}4{Style.RESET_ALL}. ⭐ {translator.get('menu.register_github')}")
|
||||||
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
|
print(f"{Fore.YELLOW} ┗━━ 🚀 LIFETIME ACCESS ENABLED 🚀{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
||||||
|
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
||||||
|
print(f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
||||||
|
print(f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
|
||||||
def select_language():
|
def select_language():
|
||||||
@@ -358,7 +362,7 @@ def main():
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-6')}: {Style.RESET_ALL}")
|
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-8')}: {Style.RESET_ALL}")
|
||||||
|
|
||||||
if choice == "0":
|
if choice == "0":
|
||||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||||
@@ -373,18 +377,26 @@ def main():
|
|||||||
cursor_register.main(translator)
|
cursor_register.main(translator)
|
||||||
print_menu()
|
print_menu()
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
|
import cursor_register_google
|
||||||
|
cursor_register_google.main(translator)
|
||||||
|
print_menu()
|
||||||
|
elif choice == "4":
|
||||||
|
import cursor_register_github
|
||||||
|
cursor_register_github.main(translator)
|
||||||
|
print_menu()
|
||||||
|
elif choice == "5":
|
||||||
import cursor_register_manual
|
import cursor_register_manual
|
||||||
cursor_register_manual.main(translator)
|
cursor_register_manual.main(translator)
|
||||||
print_menu()
|
print_menu()
|
||||||
elif choice == "4":
|
elif choice == "6":
|
||||||
import quit_cursor
|
import quit_cursor
|
||||||
quit_cursor.quit_cursor(translator)
|
quit_cursor.quit_cursor(translator)
|
||||||
print_menu()
|
print_menu()
|
||||||
elif choice == "5":
|
elif choice == "7":
|
||||||
if select_language():
|
if select_language():
|
||||||
print_menu()
|
print_menu()
|
||||||
continue
|
continue
|
||||||
elif choice == "6":
|
elif choice == "8":
|
||||||
import disable_auto_update
|
import disable_auto_update
|
||||||
disable_auto_update.run(translator)
|
disable_auto_update.run(translator)
|
||||||
print_menu()
|
print_menu()
|
||||||
|
|||||||
826
oauth_auth.py
Normal file
826
oauth_auth.py
Normal file
@@ -0,0 +1,826 @@
|
|||||||
|
import os
|
||||||
|
from colorama import Fore, Style, init
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import webbrowser
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||||
|
from cursor_auth import CursorAuth
|
||||||
|
from utils import get_random_wait_time, get_default_chrome_path
|
||||||
|
from config import get_config
|
||||||
|
import platform
|
||||||
|
|
||||||
|
# Initialize colorama
|
||||||
|
init()
|
||||||
|
|
||||||
|
# Define emoji constants
|
||||||
|
EMOJI = {
|
||||||
|
'START': '🚀',
|
||||||
|
'OAUTH': '🔑',
|
||||||
|
'SUCCESS': '✅',
|
||||||
|
'ERROR': '❌',
|
||||||
|
'WAIT': '⏳',
|
||||||
|
'INFO': 'ℹ️'
|
||||||
|
}
|
||||||
|
|
||||||
|
class OAuthHandler:
|
||||||
|
def __init__(self, translator=None):
|
||||||
|
self.translator = translator
|
||||||
|
self.config = get_config(translator)
|
||||||
|
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||||
|
self.browser = None
|
||||||
|
|
||||||
|
def _get_active_profile(self, user_data_dir):
|
||||||
|
"""Find the existing default/active Chrome profile"""
|
||||||
|
try:
|
||||||
|
# List all profile directories
|
||||||
|
profiles = []
|
||||||
|
for item in os.listdir(user_data_dir):
|
||||||
|
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
|
||||||
|
profiles.append(item)
|
||||||
|
|
||||||
|
if not profiles:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} No Chrome profiles found, using Default{Style.RESET_ALL}")
|
||||||
|
return 'Default'
|
||||||
|
|
||||||
|
# First check if Default profile exists
|
||||||
|
if 'Default' in profiles:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found Default Chrome profile{Style.RESET_ALL}")
|
||||||
|
return 'Default'
|
||||||
|
|
||||||
|
# If no Default profile, check Local State for last used profile
|
||||||
|
local_state_path = os.path.join(user_data_dir, 'Local State')
|
||||||
|
if os.path.exists(local_state_path):
|
||||||
|
with open(local_state_path, 'r', encoding='utf-8') as f:
|
||||||
|
local_state = json.load(f)
|
||||||
|
|
||||||
|
# Get info about last used profile
|
||||||
|
profile_info = local_state.get('profile', {})
|
||||||
|
last_used = profile_info.get('last_used', '')
|
||||||
|
info_cache = profile_info.get('info_cache', {})
|
||||||
|
|
||||||
|
# Try to find an active profile
|
||||||
|
for profile in profiles:
|
||||||
|
profile_path = profile.replace('\\', '/')
|
||||||
|
if profile_path in info_cache:
|
||||||
|
#print(f"{Fore.CYAN}{EMOJI['INFO']} Using existing Chrome profile: {profile}{Style.RESET_ALL}")
|
||||||
|
return profile
|
||||||
|
|
||||||
|
# If no profile found in Local State, use the first available profile
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Using first available Chrome profile: {profiles[0]}{Style.RESET_ALL}")
|
||||||
|
return profiles[0]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Error finding Chrome profile, using Default: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return 'Default'
|
||||||
|
|
||||||
|
def setup_browser(self):
|
||||||
|
"""Setup browser for OAuth flow using active profile"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Initializing browser setup...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Platform-specific initialization
|
||||||
|
platform_name = platform.system().lower()
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected platform: {platform_name}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Kill existing browser processes
|
||||||
|
self._kill_browser_processes()
|
||||||
|
|
||||||
|
# Get browser paths and user data directory
|
||||||
|
user_data_dir = self._get_user_data_directory()
|
||||||
|
chrome_path = self._get_browser_path()
|
||||||
|
|
||||||
|
if not chrome_path:
|
||||||
|
raise Exception(f"No compatible browser found. Please install Google Chrome or Chromium.\nSupported browsers for {platform_name}:\n" +
|
||||||
|
"- Windows: Google Chrome, Chromium\n" +
|
||||||
|
"- macOS: Google Chrome, Chromium\n" +
|
||||||
|
"- Linux: Google Chrome, Chromium, chromium-browser")
|
||||||
|
|
||||||
|
# Get active profile
|
||||||
|
active_profile = self._get_active_profile(user_data_dir)
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Using browser profile: {active_profile}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Configure browser options
|
||||||
|
co = self._configure_browser_options(chrome_path, user_data_dir, active_profile)
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting browser at: {chrome_path}{Style.RESET_ALL}")
|
||||||
|
self.browser = ChromiumPage(co)
|
||||||
|
|
||||||
|
# Verify browser launched successfully
|
||||||
|
if not self.browser:
|
||||||
|
raise Exception("Failed to initialize browser instance")
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Browser setup completed successfully{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Browser setup failed: {str(e)}{Style.RESET_ALL}")
|
||||||
|
if "DevToolsActivePort file doesn't exist" in str(e):
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running with administrator/root privileges{Style.RESET_ALL}")
|
||||||
|
elif "Chrome failed to start" in str(e):
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _kill_browser_processes(self):
|
||||||
|
"""Kill existing browser processes based on platform"""
|
||||||
|
try:
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
processes = ['chrome.exe', 'chromium.exe']
|
||||||
|
for proc in processes:
|
||||||
|
os.system(f'taskkill /f /im {proc} >nul 2>&1')
|
||||||
|
else: # Linux/Mac
|
||||||
|
processes = ['chrome', 'chromium', 'chromium-browser']
|
||||||
|
for proc in processes:
|
||||||
|
os.system(f'pkill -f {proc} >/dev/null 2>&1')
|
||||||
|
|
||||||
|
time.sleep(1) # Wait for processes to close
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Warning: Could not kill existing browser processes: {e}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
def _get_user_data_directory(self):
|
||||||
|
"""Get the appropriate user data directory based on platform"""
|
||||||
|
try:
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
possible_paths = [
|
||||||
|
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
|
||||||
|
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
|
||||||
|
]
|
||||||
|
elif sys.platform == 'darwin': # macOS
|
||||||
|
possible_paths = [
|
||||||
|
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||||
|
os.path.expanduser('~/Library/Application Support/Chromium')
|
||||||
|
]
|
||||||
|
else: # Linux
|
||||||
|
possible_paths = [
|
||||||
|
os.path.expanduser('~/.config/google-chrome'),
|
||||||
|
os.path.expanduser('~/.config/chromium'),
|
||||||
|
'/usr/bin/google-chrome',
|
||||||
|
'/usr/bin/chromium-browser'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Try each possible path
|
||||||
|
for path in possible_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found browser data directory: {path}{Style.RESET_ALL}")
|
||||||
|
return path
|
||||||
|
|
||||||
|
# Create temporary profile if no existing profile found
|
||||||
|
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Creating temporary profile at: {temp_profile}{Style.RESET_ALL}")
|
||||||
|
os.makedirs(temp_profile, exist_ok=True)
|
||||||
|
return temp_profile
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting user data directory: {e}{Style.RESET_ALL}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _get_browser_path(self):
|
||||||
|
"""Get the browser executable path based on platform"""
|
||||||
|
try:
|
||||||
|
# Try default path first
|
||||||
|
chrome_path = get_default_chrome_path()
|
||||||
|
if chrome_path and os.path.exists(chrome_path):
|
||||||
|
return chrome_path
|
||||||
|
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Searching for alternative browser installations...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Platform-specific paths
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
alt_paths = [
|
||||||
|
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
||||||
|
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
|
||||||
|
r'C:\Program Files\Chromium\Application\chrome.exe',
|
||||||
|
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
|
||||||
|
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
|
||||||
|
]
|
||||||
|
elif sys.platform == 'darwin': # macOS
|
||||||
|
alt_paths = [
|
||||||
|
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||||
|
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||||
|
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||||
|
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
|
||||||
|
]
|
||||||
|
else: # Linux
|
||||||
|
alt_paths = [
|
||||||
|
'/usr/bin/google-chrome',
|
||||||
|
'/usr/bin/chromium-browser',
|
||||||
|
'/usr/bin/chromium',
|
||||||
|
'/snap/bin/chromium',
|
||||||
|
'/usr/local/bin/chrome',
|
||||||
|
'/usr/local/bin/chromium'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Try each alternative path
|
||||||
|
for path in alt_paths:
|
||||||
|
expanded_path = os.path.expanduser(path)
|
||||||
|
if os.path.exists(expanded_path):
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found browser at: {expanded_path}{Style.RESET_ALL}")
|
||||||
|
return expanded_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Error finding browser path: {e}{Style.RESET_ALL}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
|
||||||
|
"""Configure browser options based on platform"""
|
||||||
|
try:
|
||||||
|
co = ChromiumOptions()
|
||||||
|
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||||
|
co.set_argument(f'--profile-directory={active_profile}')
|
||||||
|
|
||||||
|
# Basic options
|
||||||
|
co.set_argument('--no-first-run')
|
||||||
|
co.set_argument('--no-default-browser-check')
|
||||||
|
co.set_argument('--disable-gpu')
|
||||||
|
|
||||||
|
# Platform-specific options
|
||||||
|
if sys.platform.startswith('linux'):
|
||||||
|
co.set_argument('--no-sandbox')
|
||||||
|
co.set_argument('--disable-dev-shm-usage')
|
||||||
|
co.set_argument('--disable-setuid-sandbox')
|
||||||
|
elif sys.platform == 'darwin':
|
||||||
|
co.set_argument('--disable-gpu-compositing')
|
||||||
|
elif os.name == 'nt':
|
||||||
|
co.set_argument('--disable-features=TranslateUI')
|
||||||
|
co.set_argument('--disable-features=RendererCodeIntegrity')
|
||||||
|
|
||||||
|
return co
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Error configuring browser options: {e}{Style.RESET_ALL}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def handle_google_auth(self):
|
||||||
|
"""Handle Google OAuth authentication"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Setup browser
|
||||||
|
if not self.setup_browser():
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Navigate to auth URL
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
|
||||||
|
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||||
|
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Look for Google auth button
|
||||||
|
selectors = [
|
||||||
|
"//a[contains(@href,'GoogleOAuth')]",
|
||||||
|
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
|
||||||
|
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
|
||||||
|
]
|
||||||
|
|
||||||
|
auth_btn = None
|
||||||
|
for selector in selectors:
|
||||||
|
try:
|
||||||
|
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
|
||||||
|
if auth_btn and auth_btn.is_displayed():
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not auth_btn:
|
||||||
|
raise Exception("Could not find Google authentication button")
|
||||||
|
|
||||||
|
# Click the button and wait for page load
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting Google authentication...{Style.RESET_ALL}")
|
||||||
|
auth_btn.click()
|
||||||
|
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Check if we're on account selection page
|
||||||
|
if "accounts.google.com" in self.browser.url:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account to continue...{Style.RESET_ALL}")
|
||||||
|
try:
|
||||||
|
self.browser.run_js("""
|
||||||
|
alert('Please select your Google account to continue with Cursor authentication');
|
||||||
|
""")
|
||||||
|
except:
|
||||||
|
pass # Alert is optional
|
||||||
|
|
||||||
|
# Wait for authentication to complete
|
||||||
|
auth_info = self._wait_for_auth()
|
||||||
|
if not auth_info:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{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']} Authentication error: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if self.browser:
|
||||||
|
self.browser.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
def _wait_for_auth(self):
|
||||||
|
"""Wait for authentication to complete and extract auth info"""
|
||||||
|
try:
|
||||||
|
max_wait = 300 # 5 minutes
|
||||||
|
start_time = time.time()
|
||||||
|
check_interval = 2 # Check every 2 seconds
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for authentication (timeout: 5 minutes)...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
while time.time() - start_time < max_wait:
|
||||||
|
try:
|
||||||
|
# Check for authentication cookies
|
||||||
|
cookies = self.browser.cookies()
|
||||||
|
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||||
|
value = cookie.get("value", "")
|
||||||
|
token = None
|
||||||
|
|
||||||
|
if "::" in value:
|
||||||
|
token = value.split("::")[-1]
|
||||||
|
elif "%3A%3A" in value:
|
||||||
|
token = value.split("%3A%3A")[-1]
|
||||||
|
|
||||||
|
if token:
|
||||||
|
# Get email from settings page
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Authentication successful, getting account info...{Style.RESET_ALL}")
|
||||||
|
self.browser.get("https://www.cursor.com/settings")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
email = None
|
||||||
|
try:
|
||||||
|
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||||
|
if email_element:
|
||||||
|
email = email_element.text
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {email}{Style.RESET_ALL}")
|
||||||
|
except:
|
||||||
|
email = "user@cursor.sh" # Fallback email
|
||||||
|
|
||||||
|
# Check usage count
|
||||||
|
try:
|
||||||
|
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
|
||||||
|
if usage_element:
|
||||||
|
usage_text = usage_element.text
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Check if account is expired
|
||||||
|
if usage_text.strip() == "150 / 150":
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, creating new account...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Delete current account
|
||||||
|
if self._delete_current_account():
|
||||||
|
# Start new authentication
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
|
||||||
|
return self.handle_google_auth()
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete expired account{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not check usage count: {str(e)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
return {"email": email, "token": token}
|
||||||
|
|
||||||
|
# Also check URL as backup
|
||||||
|
if "cursor.com/settings" in self.browser.url:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected successful login{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Waiting for authentication... ({str(e)}){Style.RESET_ALL}")
|
||||||
|
|
||||||
|
time.sleep(check_interval)
|
||||||
|
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Error while waiting for authentication: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def handle_github_auth(self):
|
||||||
|
"""Handle GitHub OAuth authentication"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Setup browser
|
||||||
|
if not self.setup_browser():
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Navigate to auth URL
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} 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']} 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')}{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']} Authentication error: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if self.browser:
|
||||||
|
self.browser.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
def _handle_oauth(self, auth_type):
|
||||||
|
"""Handle OAuth authentication for both Google and GitHub
|
||||||
|
|
||||||
|
Args:
|
||||||
|
auth_type (str): Type of authentication ('google' or 'github')
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not self.setup_browser():
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Navigate to auth URL
|
||||||
|
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||||
|
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Set selectors based on auth type
|
||||||
|
if auth_type == "google":
|
||||||
|
selectors = [
|
||||||
|
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
|
||||||
|
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
|
||||||
|
]
|
||||||
|
else: # github
|
||||||
|
selectors = [
|
||||||
|
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Wait for the button to be available
|
||||||
|
auth_btn = None
|
||||||
|
max_button_wait = 30 # 30 seconds
|
||||||
|
button_start_time = time.time()
|
||||||
|
|
||||||
|
while time.time() - button_start_time < max_button_wait:
|
||||||
|
for selector in selectors:
|
||||||
|
try:
|
||||||
|
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
|
||||||
|
if auth_btn and auth_btn.is_displayed():
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
if auth_btn:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
if auth_btn:
|
||||||
|
# Click the button and wait for page load
|
||||||
|
auth_btn.click()
|
||||||
|
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Check if we're on account selection page
|
||||||
|
if auth_type == "google" and "accounts.google.com" in self.browser.url:
|
||||||
|
alert_js = """
|
||||||
|
alert('Please select your Google account manually to continue with Cursor authentication');
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.browser.run_js(alert_js)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Alert display failed: {str(e)}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account manually to continue with Cursor authentication...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Waiting for authentication to complete...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Wait for authentication to complete
|
||||||
|
max_wait = 300 # 5 minutes
|
||||||
|
start_time = time.time()
|
||||||
|
last_url = self.browser.url
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['WAIT']} Checking authentication status...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
while time.time() - start_time < max_wait:
|
||||||
|
try:
|
||||||
|
# Check for authentication cookies
|
||||||
|
cookies = self.browser.cookies()
|
||||||
|
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||||
|
value = cookie.get("value", "")
|
||||||
|
if "::" in value:
|
||||||
|
token = value.split("::")[-1]
|
||||||
|
elif "%3A%3A" in value:
|
||||||
|
token = value.split("%3A%3A")[-1]
|
||||||
|
|
||||||
|
if token:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful!{Style.RESET_ALL}")
|
||||||
|
# Navigate to settings page
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to settings page...{Style.RESET_ALL}")
|
||||||
|
self.browser.get("https://www.cursor.com/settings")
|
||||||
|
time.sleep(3) # Wait for settings page to load
|
||||||
|
|
||||||
|
# Get email from settings page
|
||||||
|
try:
|
||||||
|
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||||
|
if email_element:
|
||||||
|
actual_email = email_element.text
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} 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']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Check if account is expired
|
||||||
|
if usage_text.strip() == "150 / 150": # Changed back to actual condition
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
delete_js = """
|
||||||
|
function deleteAccount() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch('https://www.cursor.com/api/dashboard/delete-account', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
resolve('Account deleted successfully');
|
||||||
|
} else {
|
||||||
|
reject('Failed to delete account: ' + response.status);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject('Error: ' + error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return deleteAccount();
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.browser.run_js(delete_js)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Navigate back to auth page and repeat authentication
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Explicitly navigate to the authentication page
|
||||||
|
#self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||||
|
# time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Call handle_google_auth again to repeat the entire process
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
|
||||||
|
return self.handle_google_auth()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Remove the browser stay open prompt and input wait
|
||||||
|
return True, {"email": actual_email, "token": token}
|
||||||
|
|
||||||
|
# Also check URL as backup
|
||||||
|
current_url = self.browser.url
|
||||||
|
if "cursor.com/settings" in current_url:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Already on settings page!{Style.RESET_ALL}")
|
||||||
|
time.sleep(1)
|
||||||
|
cookies = self.browser.cookies()
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||||
|
value = cookie.get("value", "")
|
||||||
|
if "::" in value:
|
||||||
|
token = value.split("::")[-1]
|
||||||
|
elif "%3A%3A" in value:
|
||||||
|
token = value.split("%3A%3A")[-1]
|
||||||
|
if token:
|
||||||
|
# Get email and check usage here too
|
||||||
|
try:
|
||||||
|
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
|
||||||
|
if email_element:
|
||||||
|
actual_email = email_element.text
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} 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']} Usage count: {usage_text}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Check if account is expired
|
||||||
|
if usage_text.strip() == "150 / 150": # Changed back to actual condition
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
delete_js = """
|
||||||
|
function deleteAccount() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch('https://www.cursor.com/api/dashboard/delete-account', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
resolve('Account deleted successfully');
|
||||||
|
} else {
|
||||||
|
reject('Failed to delete account: ' + response.status);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject('Error: ' + error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return deleteAccount();
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.browser.run_js(delete_js)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Navigate back to auth page and repeat authentication
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Explicitly navigate to the authentication page
|
||||||
|
self.browser.get("https://authenticator.cursor.sh/sign-up")
|
||||||
|
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
|
||||||
|
|
||||||
|
# Call handle_google_auth again to repeat the entire process
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
|
||||||
|
return self.handle_google_auth()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Remove the browser stay open prompt and input wait
|
||||||
|
return True, {"email": actual_email, "token": token}
|
||||||
|
elif current_url != last_url:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} 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']} Status check error: {str(e)}{Style.RESET_ALL}")
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication button not found{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication failed: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
finally:
|
||||||
|
if self.browser:
|
||||||
|
self.browser.quit()
|
||||||
|
|
||||||
|
def _extract_auth_info(self):
|
||||||
|
"""Extract authentication information after successful OAuth"""
|
||||||
|
try:
|
||||||
|
# Get cookies with retry
|
||||||
|
max_retries = 3
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
cookies = self.browser.cookies()
|
||||||
|
if cookies:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
except:
|
||||||
|
if attempt == max_retries - 1:
|
||||||
|
raise
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Debug cookie information
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Found {len(cookies)} cookies{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
email = None
|
||||||
|
token = None
|
||||||
|
|
||||||
|
for cookie in cookies:
|
||||||
|
name = cookie.get("name", "")
|
||||||
|
if name == "WorkosCursorSessionToken":
|
||||||
|
try:
|
||||||
|
value = cookie.get("value", "")
|
||||||
|
if "::" in value:
|
||||||
|
token = value.split("::")[-1]
|
||||||
|
elif "%3A%3A" in value:
|
||||||
|
token = value.split("%3A%3A")[-1]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Token extraction error: {str(e)}{Style.RESET_ALL}")
|
||||||
|
elif name == "cursor_email":
|
||||||
|
email = cookie.get("value")
|
||||||
|
|
||||||
|
if email and token:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful - Email: {email}{Style.RESET_ALL}")
|
||||||
|
return True, {"email": email, "token": token}
|
||||||
|
else:
|
||||||
|
missing = []
|
||||||
|
if not email:
|
||||||
|
missing.append("email")
|
||||||
|
if not token:
|
||||||
|
missing.append("token")
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Missing authentication data: {', '.join(missing)}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to extract auth info: {str(e)}{Style.RESET_ALL}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if auth_type.lower() == 'google':
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.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')}{Style.RESET_ALL}")
|
||||||
|
success, auth_info = handler.handle_github_auth()
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} 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')}{Style.RESET_ALL}")
|
||||||
|
# Close the browser after successful authentication
|
||||||
|
if handler.browser:
|
||||||
|
handler.browser.quit()
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} Browser closed{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
return False
|
||||||
15
scripts/install.sh
Normal file → Executable file
15
scripts/install.sh
Normal file → Executable file
@@ -39,14 +39,17 @@ get_downloads_dir() {
|
|||||||
# Get latest version
|
# Get latest version
|
||||||
get_latest_version() {
|
get_latest_version() {
|
||||||
echo -e "${CYAN}ℹ️ Checking latest version...${NC}"
|
echo -e "${CYAN}ℹ️ Checking latest version...${NC}"
|
||||||
local latest_release
|
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) || {
|
||||||
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "${RED}❌ Cannot get latest version information${NC}"
|
echo -e "${RED}❌ Cannot get latest version information${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
|
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo -e "${RED}❌ Failed to parse version from GitHub API response:\n${latest_release}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
|
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +173,7 @@ install_cursor_free_vip() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${CYAN}ℹ️ Setting executable permissions...${NC}"
|
echo -e "${CYAN}ℹ️ Setting executable permissions...${NC}"
|
||||||
chmod +x "${binary_path}"
|
if chmod +x "${binary_path}"; then
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo -e "${GREEN}✅ Installation completed!${NC}"
|
echo -e "${GREEN}✅ Installation completed!${NC}"
|
||||||
echo -e "${CYAN}ℹ️ Program downloaded to: ${binary_path}${NC}"
|
echo -e "${CYAN}ℹ️ Program downloaded to: ${binary_path}${NC}"
|
||||||
echo -e "${CYAN}ℹ️ Starting program...${NC}"
|
echo -e "${CYAN}ℹ️ Starting program...${NC}"
|
||||||
|
|||||||
39
utils.py
39
utils.py
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
|
import random
|
||||||
|
|
||||||
def get_user_documents_path():
|
def get_user_documents_path():
|
||||||
"""Get user documents path"""
|
"""Get user documents path"""
|
||||||
@@ -29,4 +30,40 @@ def get_linux_cursor_path():
|
|||||||
]
|
]
|
||||||
|
|
||||||
# return the first path that exists
|
# return the first path that exists
|
||||||
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])
|
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])
|
||||||
|
|
||||||
|
def get_random_wait_time(config, timing_key):
|
||||||
|
"""Get random wait time based on configuration timing settings
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (dict): Configuration dictionary containing timing settings
|
||||||
|
timing_key (str): Key to look up in the timing settings
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Random wait time in seconds
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get timing value from config
|
||||||
|
timing = config.get('Timing', {}).get(timing_key)
|
||||||
|
if not timing:
|
||||||
|
# Default to 0.5-1.5 seconds if timing not found
|
||||||
|
return random.uniform(0.5, 1.5)
|
||||||
|
|
||||||
|
# Check if timing is a range (e.g., "0.5-1.5" or "0.5,1.5")
|
||||||
|
if isinstance(timing, str):
|
||||||
|
if '-' in timing:
|
||||||
|
min_time, max_time = map(float, timing.split('-'))
|
||||||
|
elif ',' in timing:
|
||||||
|
min_time, max_time = map(float, timing.split(','))
|
||||||
|
else:
|
||||||
|
# Single value, use it as both min and max
|
||||||
|
min_time = max_time = float(timing)
|
||||||
|
else:
|
||||||
|
# If timing is a number, use it as both min and max
|
||||||
|
min_time = max_time = float(timing)
|
||||||
|
|
||||||
|
return random.uniform(min_time, max_time)
|
||||||
|
|
||||||
|
except (ValueError, TypeError, AttributeError):
|
||||||
|
# Return default value if any error occurs
|
||||||
|
return random.uniform(0.5, 1.5)
|
||||||
Reference in New Issue
Block a user