diff --git a/.gitignore b/.gitignore index 27161b55..80d4dfaa 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,6 @@ coverage.txt *.tmp *~ *.bak -*.log \ No newline at end of file +*.log + +.cunzhi*/ \ No newline at end of file diff --git a/README.md b/README.md index c90d5371..f4fbf0f7 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,59 @@ [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE) [![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=flat-square&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers) -[🌟 English](README.md) | [🌏 中文](README_CN.md) +[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md) Cursor Logo + + > ⚠️ **IMPORTANT NOTICE** > > This tool currently supports: -> - ✅ Cursor v0.44.11 and below -> - ❌ Latest 0.45.x versions (temporarily unsupported) +> - ✅ Windows: Latest 1.0.x versions (Supported) +> - ✅ Mac/Linux: Latest 1.0.x versions (Supported, feedback welcome) > > Please check your Cursor version before using this tool. -> 💾 **Download Cursor v0.44.11** -> - [Download from Cursor Official](https://downloader.cursor.sh/builds/250103fqxdt5u9z/windows/nsis/x64) -> - [Download from ToDesktop](https://download.todesktop.com/230313mzl4w4u92/Cursor%20Setup%200.44.11%20-%20Build%20250103fqxdt5u9z-x64.exe) +
+📦 Version History & Downloads + +
+ +### 🌟 Latest Versions + +[View Full Version History]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file)) + +
+ + + +
+ +⚠️ **General Solutions for Cursor** +> 1. Close Cursor, log out of your account, and delete your account in the official website Settings (refresh IP node: Japan, Singapore, USA, Hong Kong, prioritizing low latency - not necessarily required but change if conditions allow; Windows users are recommended to refresh DNS cache: `ipconfig /flushdns`) +> Go to the Cursor official website to delete your current account +> Steps: User avatar -> Setting -> Advanced▼ in the bottom left -> Delete Account +> +> 2. Run the machine code refresh script, see the script address below, available in China +> +> 3. Re-register an account, log in, and open Cursor to resume normal use. +> +> 4. Alternative solution: If still unusable after step [**3**], or if you encounter problems such as account registration failure or inability to delete an account, this usually means your browser has been identified or restricted by the target website (risk control). In this case, try switching browsers, such as: Edge, Google Chrome, Firefox. (Or, consider using a browser that can modify or randomize browser fingerprint information). + + +--- + +⚠️ **MAC Address Modification Warning** +> +> For Mac users: This script includes a MAC address modification feature that will: +> - Modify your network interface's MAC address +> - Backup original MAC addresses before modification +> - This modification may temporarily affect network connectivity +> - You can skip this step when prompted during execution +>
🔒 Disable Auto-Update Feature @@ -49,11 +85,22 @@ Select `1` to automatically complete the disable operation. **macOS:** ```bash +# NOTE: As tested, this method only works for version 0.45.11 and below. # Close Cursor pkill -f "Cursor" -# Remove update directory and create blocking file -rm -rf ~/Library/Application\ Support/cursor-updater -touch ~/Library/Application\ Support/cursor-updater +# Replacing app-update.yml with a blank/read-only file +cd /Applications/Cursor.app/Contents/Resources +mv app-update.yml app-update.yml.bak +touch app-update.yml +chmod 444 app-update.yml + +# Go to Settings -> Application -> Update, set Mode to none. +# This must be done to prevent Cursor from checking for updates. + +# NOTE: The cursor-updater modification method may no longer be effective +# In any case, remove update directory and create blocking file +rm -rf ~/Library/Application\ Support/Caches/cursor-updater +touch ~/Library/Application\ Support/Caches/cursor-updater ``` **Linux:** @@ -103,6 +150,14 @@ Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx You've reached your trial request limit. ``` +#### Issue 4: Claude 3.7 High Load

Back To Top

+ +```text +High Load +We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the +'default' model, Claude 3.5 sonnet, another model, or try again in a few moments. +``` +

@@ -141,6 +196,17 @@ If the above solutions don't work, try: - Ensure network stability - Clear browser cache and retry +#### Solution 4: Claude 3.7 Access Issue (High Load) + +If you see the "High Load" message for Claude 3.7 Sonnet, this indicates Cursor is limiting free trial accounts from using the 3.7 model during certain times of the day. Try: + +1. Switch to a new account created with Gmail, possibly connecting through a different IP address +2. Try accessing during off-peak hours (typically 5-10 AM or 3-7 PM when restrictions are often lighter) +3. Consider upgrading to Pro for guaranteed access +4. Use Claude 3.5 Sonnet as a fallback option + +> Note: These access patterns may change as Cursor adjusts their resource allocation policies. + ### 💻 System Support @@ -173,6 +239,8 @@ If the above solutions don't work, try:
+ + ### 🚀 One-Click Solution
@@ -181,7 +249,8 @@ If the above solutions don't work, try: **macOS** ```bash -curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh | sudo bash +# Method two +curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh ``` **Linux** @@ -190,12 +259,21 @@ curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/ curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash ``` +> **Note for Linux users:** The script attempts to find your Cursor installation by checking common paths (`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`), using the `which cursor` command, and searching within `/usr`, `/opt`, and `$HOME/.local`. If Cursor is installed elsewhere or not found via these methods, the script may fail. Ensure Cursor is accessible via one of these standard locations or methods. + **Windows** ```powershell irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +**Windows (Enhanced Version)** + +```powershell +irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` +> Enhanced Cursor machine code modifier with dual-mode operation and trial reset functionality +
Run Success
@@ -208,7 +286,7 @@ irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/ **macOS** ```bash -curl -fsSL https://aizaozao.com/accelerate.php/c | sudo bash +curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh ``` **Linux** @@ -223,6 +301,13 @@ curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +**Windows (Enhanced Version)** + +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` +> Enhanced Cursor machine code modifier with dual-mode operation and trial reset functionality +
@@ -251,6 +336,11 @@ irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaoti irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +For the enhanced version: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + ##### Method 3: Using Search >![Search PowerShell](img/pwsh_1.png) > @@ -262,6 +352,11 @@ Enter the reset script in the administrator terminal: irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +For the enhanced version: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + ### 🔧 PowerShell Installation Guide If PowerShell is not installed on your system, you can install it using one of these methods: @@ -369,6 +464,40 @@ macOS/Linux users can try to locate similar `cursor-updater` directory in their - ✅ Error handling and recovery
+
+Registry Modification Notice + +> ⚠️ **Important: This tool modifies the Windows Registry** + +#### Modified Registry +- Path: `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` +- Key: `MachineGuid` + +#### Potential Impact +Modifying this registry key may affect: +- Windows system's unique device identification +- Device recognition and authorization status of certain software +- System features based on hardware identification + +#### Safety Measures +1. Automatic Backup + - Original value is automatically backed up before modification + - Backup location: `%APPDATA%\Cursor\User\globalStorage\backups` + - Backup file format: `MachineGuid.backup_YYYYMMDD_HHMMSS` + +2. Manual Recovery Steps + - Open Registry Editor (regedit) + - Navigate to: `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` + - Right-click on `MachineGuid` + - Select "Modify" + - Paste the value from backup file + +#### Important Notes +- Verify backup file existence before modification +- Use backup file to restore original value if needed +- Administrator privileges required for registry modification +
+ --- ### 📚 Recommended Reading @@ -378,6 +507,59 @@ macOS/Linux users can try to locate similar `cursor-updater` directory in their --- +## 💬 Feedback & Suggestions + +We value your feedback on the new enhanced script! If you've tried the `cursor_win_id_modifier.ps1` script, please share your experience: + +- 🐛 **Bug Reports**: Found any issues? Let us know! +- 💡 **Feature Suggestions**: Have ideas for improvements? +- ⭐ **Success Stories**: Share how the tool helped you! +- 🔧 **Technical Feedback**: Performance, compatibility, or usability insights + +Your feedback helps us improve the tool for everyone. Feel free to open an issue or contribute to the project! + +--- + +## Support + +
+If you find this helpful, consider buying me a spicy gluten snack (Latiao) as appreciation~ 💁☕️ + + + + + + + + + + +
+微信赞赏
+微信赞赏码
+要到饭咧?啊咧?啊咧?不给也没事~ 请随意打赏 +
+支付宝赞赏
+支付宝赞赏码
+如果觉得有帮助,来包辣条犒劳一下吧~ +
+Alipay
+Alipay
+1 Latiao = 1 AI thought cycle +
+WeChat
+WeChat
+二维码7天内(9月29日前前)有效,过期请加微信或者公众号`煎饼果子卷AI` +
+
+ +--- + ## ⭐ Project Stats
diff --git a/README_CN.md b/README_CN.md index 0ef89809..6c9c5580 100644 --- a/README_CN.md +++ b/README_CN.md @@ -6,77 +6,65 @@ [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE) [![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=flat-square&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers) -[🌟 English](README.md) | [🌏 中文](README_CN.md) +[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md) Cursor Logo -
+ > ⚠️ **重要提示** > > 本工具当前支持版本: -> - ✅ Cursor v0.44.11 及以下版本 -> - ❌ 最新的 0.45.x 版本(暂不支持) -> +> - ✅ Windows: 最新的 1.0.x 版本(已支持) +> - ✅ Mac/Linux: 最新的 1.0.x 版本(已支持,欢迎测试并反馈问题) + > 使用前请确认您的 Cursor 版本。 -> 💾 **下载 Cursor v0.44.11** -> - [从 Cursor 官方下载](https://downloader.cursor.sh/builds/250103fqxdt5u9z/windows/nsis/x64) -> - [从 ToDesktop 下载](https://download.todesktop.com/230313mzl4w4u92/Cursor%20Setup%200.44.11%20-%20Build%20250103fqxdt5u9z-x64.exe) +
+📦 版本历史与下载 -
-🔒 禁用自动更新功能 +
-> 为防止 Cursor 自动更新到不支持的新版本,您可以选择禁用自动更新功能。 -#### 方法一:使用内置脚本(推荐) +[查看完整版本历史]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file)) -在运行重置工具时,脚本会询问是否要禁用自动更新: -```text -[询问] 是否要禁用 Cursor 自动更新功能? -0) 否 - 保持默认设置 (按回车键) -1) 是 - 禁用自动更新 -``` +
-选择 `1` 即可自动完成禁用操作。 -#### 方法二:手动禁用 +
-**Windows:** -1. 关闭所有 Cursor 进程 -2. 删除目录:`%LOCALAPPDATA%\cursor-updater` -3. 在相同位置创建同名文件(不带扩展名) +⚠️ **Cursor通用解决方案** +> 1. 关闭Cursor、退出账号、官网Setting删除账号(刷新节点IP:日本、新加坡、 美国、香港,低延迟为主不一定需要但是有条件就换,Windows用户建议刷新DNS缓存:`ipconfig /flushdns`) +> 前往Cursor官网删除当前账号 +> 步骤:用户头像->Setting-左下角Advanced▼->Delete Account +> +> 2. 刷新机器码脚本,看下面脚本地址,国内可用 +> +> 3. 重新注册账号、登录、打开Cursor,即可恢复正常使用。 +> +> 4. 备用方案:如果步骤 [**3**] 后仍不可用,或者遇到注册账号失败、无法删除账号等问题,这通常意味着您的浏览器被目标网站识别或限制(风控)。此时,请尝试更换浏览器,例如:Edge、Google Chrome、Firefox。(或者,可以尝试使用能够修改或随机化浏览器指纹信息的浏览器)。 -**macOS:** -```bash -# 关闭 Cursor -pkill -f "Cursor" -# 删除更新目录并创建阻止文件 -rm -rf ~/Library/Application\ Support/cursor-updater -touch ~/Library/Application\ Support/cursor-updater -``` -**Linux:** -```bash -# 关闭 Cursor -pkill -f "Cursor" -# 删除更新目录并创建阻止文件 -rm -rf ~/.config/cursor-updater -touch ~/.config/cursor-updater -``` +关注大佬公众号:煎饼果子卷AI -> ⚠️ **注意:** 禁用自动更新后,需要手动下载并安装新版本。建议在确认新版本可用后再更新。 +--- -
+> ⚠️ **MAC地址修改警告** +> +> Mac用户请注意: 本脚本包含MAC地址修改功能,将会: +> - 修改您的网络接口MAC地址 +> - 在修改前备份原始MAC地址 +> - 此修改可能会暂时影响网络连接 +> - 执行过程中可以选择跳过此步骤 --- ### 📝 问题描述 -> 当您遇到以下任一提示时: +> 当您遇到以下任何消息时: -#### 问题一:试用账号限制

Back To Top

+#### 问题 1: 试用账号限制

跳转到顶部

```text Too many free trial accounts used on this machine. @@ -85,61 +73,82 @@ to prevent abuse. Please let us know if you believe this is a mistake. ``` -#### 问题二:API密钥限制

Back To Top

+#### 问题 2: API密钥限制

跳转到顶部

```text -❗[New Issue] +[New Issue] Composer relies on custom models that cannot be billed to an API key. Please disable API keys and use a Pro or Business subscription. Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ``` -#### 问题三:试用请求次数限制 +#### 问题 3: 试用请求限制 -> 这表示在 VIP 免费试用期间已达到使用次数限制: +> 这表明您在VIP免费试用期间已达到使用限制: ```text You've reached your trial request limit. ``` +#### 问题 4: Claude 3.7 高负载 (High Load)

跳转到顶部

+ +```text +High Load +We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the +'default' model, Claude 3.5 sonnet, another model, or try again in a few moments. +``` +
-

+

-#### 解决方案 :完全卸载 Cursor 并重新安装(API 密钥问题) +#### 解决方案:完全卸载Cursor并重新安装(API密钥问题) -1. 下载 [Geek.exe 卸载程序[免费]](https://geekuninstaller.com/download) -2. 完全卸载 Cursor 应用 -3. 重新安装 Cursor 应用 -4. 转到解决方案 1 +1. 下载 [Geek.exe 卸载工具[免费]](https://geekuninstaller.com/download) +2. 完全卸载Cursor应用 +3. 重新安装Cursor应用 +4. 继续执行解决方案1
-

+

> 临时解决方案: -#### 方案一:快速重置(推荐) +#### 解决方案 1: 快速重置(推荐) -1. 关闭 Cursor 应用 -2. 执行重置机器码脚本(见下方安装说明) -3. 重新打开 Cursor 即可继续使用 +1. 关闭Cursor应用 +2. 运行机器码重置脚本(见下方安装说明) +3. 重新打开Cursor继续使用 -#### 方案二:账号切换 +#### 解决方案 2: 切换账号 -1. 文件 -> Cursor Settings -> 注销当前账号 -2. 关闭 Cursor -3. 执行重置机器码脚本 -4. 使用新账号重新登录 +1. 文件 -> Cursor设置 -> 退出登录 +2. 关闭Cursor +3. 运行机器码重置脚本 +4. 使用新账号登录 -#### 方案三:网络优化 +#### 解决方案 3: 网络优化 -如果上述方案仍无法解决,可尝试: +如果上述解决方案不起作用,请尝试: -- 切换至低延迟节点(推荐区域:日本、新加坡、美国、香港) +- 切换到低延迟节点(推荐区域:日本、新加坡、美国、香港) - 确保网络稳定性 -- 清除浏览器缓存后重试 +- 清除浏览器缓存并重试 + +

+ +#### 解决方案 4: Claude 3.7 访问问题(High Load ) + +如果您看到Claude 3.7 Sonnet的"High Load"(高负载)消息,这表明Cursor在一天中某些时段限制免费试用账号使用3.7模型。请尝试: + +1. 使用Gmail邮箱创建新账号,可能需要通过不同IP地址连接 +2. 尝试在非高峰时段访问(通常在早上5-10点或下午3-7点之间限制较少) +3. 考虑升级到Pro版本获取保证访问权限 +4. 使用Claude 3.5 Sonnet作为备选方案 + +> 注意:随着Cursor调整资源分配策略,这些访问模式可能会发生变化。 ### 🚀 系统支持 @@ -169,6 +178,8 @@ You've reached your trial request limit. + + ### 🚀 一键解决方案
@@ -177,7 +188,7 @@ You've reached your trial request limit. **macOS** ```bash -curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh | sudo bash +curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh ``` **Linux** @@ -186,11 +197,20 @@ curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash ``` +> **Linux 用户请注意:** 该脚本通过检查常用路径(`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`)、使用 `which cursor` 命令以及在 `/usr`、`/opt` 和 `$HOME/.local` 目录内搜索,来尝试定位您的 Cursor 安装。如果 Cursor 安装在其他位置或通过这些方法无法找到,脚本可能会失败。请确保可以通过这些标准位置或方法之一访问到 Cursor。 + **Windows** ```powershell irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` + +**Windows (增强版)** + +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` +> 增强版Cursor机器码修改工具,支持双模式操作和试用重置功能
运行成功
@@ -222,6 +242,11 @@ irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaoti irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +增强版脚本: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + ##### 方法三:通过搜索启动 >![搜索 PowerShell](img/pwsh_1.png) > @@ -233,6 +258,11 @@ irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaoti irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex ``` +增强版脚本: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + ### 🔧 PowerShell 安装指南 如果您的系统没有安装 PowerShell,可以通过以下方法安装: @@ -298,6 +328,40 @@ winget install --id Microsoft.PowerShell --source winget ### 🔧 技术细节 +
+注册表修改说明 + +> ⚠️ **重要提示:本工具会修改系统注册表** + +#### 修改内容 +- 路径:`计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` +- 项目:`MachineGuid` + +#### 潜在影响 +修改此注册表项可能会影响: +- Windows 系统对设备的唯一标识 +- 某些软件的设备识别和授权状态 +- 基于硬件标识的系统功能 + +#### 安全措施 +1. 自动备份 + - 每次修改前会自动备份原始值 + - 备份保存在:`%APPDATA%\Cursor\User\globalStorage\backups` + - 备份文件格式:`MachineGuid.backup_YYYYMMDD_HHMMSS` + +2. 手动恢复方法 + - 打开注册表编辑器(regedit) + - 定位到:`计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` + - 右键点击 `MachineGuid` + - 选择"修改" + - 粘贴备份文件中的值 + +#### 注意事项 +- 建议在修改前先确认备份文件的存在 +- 如遇问题可通过备份文件恢复原始值 +- 必须以管理员权限运行才能修改注册表 +
+
配置文件 @@ -328,8 +392,28 @@ Windows 用户可以手动禁用自动更新功能: 2. 删除目录:`C:\Users\用户名\AppData\Local\cursor-updater` 3. 创建同名文件:`cursor-updater`(不带扩展名) -macOS/Linux 用户可以尝试在系统中找到类似的`cursor-updater`目录进行相同操作。 +Linux用户可以尝试在系统中找到类似的`cursor-updater`目录进行相同操作。 + +MacOS用户按照以下步骤操作: + +```bash +# 注意:经测试,此方法仅适用于0.45.11及以下版本,不支持0.46.*版本 +# 关闭所有 Cursor 进程 +pkill -f "Cursor" +# 备份app-update.yml并创建空的只读文件代替原文件 +cd /Applications/Cursor.app/Contents/Resources +mv app-update.yml app-update.yml.bak +touch app-update.yml +chmod 444 app-update.yml + +# 打开Cursor设置,将更新模式设置为"无",该步骤必须执行,否则Cursor依然会自动检查更新 +# 步骤:Settings -> Application -> Update, 将Mode设置为none + +# 注意: cursor-updater修改方法可能已失效。但为了以防万一,还是删除更新目录并创建阻止文件 +rm -rf ~/Library/Application\ Support/Caches/cursor-updater +touch ~/Library/Application\ Support/Caches/cursor-updater +```
@@ -340,8 +424,30 @@ macOS/Linux 用户可以尝试在系统中找到类似的`cursor-updater`目录 - ✅ 错误处理和恢复
+
+重置 Cursor 免费试用 + +### 使用 `cursor_free_trial_reset.sh` 脚本 + +#### macOS + +```bash +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh -o ./cursor_free_trial_reset.sh && sudo bash ./cursor_free_trial_reset.sh && rm ./cursor_free_trial_reset.sh +``` + +#### Linux +```bash +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh | sudo bash +``` +#### Windows + +```powershell +irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh | iex +``` + +
## 联系方式 @@ -355,8 +461,8 @@ macOS/Linux 用户可以尝试在系统中找到类似的`cursor-updater`目录 微信交流群
-微信群二维码
-7天内(1月15日前)有效,群满可以加公众号关注最新动态 +WeChat
+二维码7天内(9月29日前)有效,过期请加微信或者公众号`煎饼果子卷AI` 公众号
@@ -370,7 +476,7 @@ macOS/Linux 用户可以尝试在系统中找到类似的`cursor-updater`目录 支付宝赞赏
-支付宝赞赏码
+支付宝赞赏码
如果觉得有帮助,来包辣条犒劳一下吧~ @@ -386,6 +492,19 @@ macOS/Linux 用户可以尝试在系统中找到类似的`cursor-updater`目录 --- +## 💬 反馈与建议 + +我们非常重视您对新增强脚本的反馈!如果您已经尝试了 `cursor_win_id_modifier.ps1` 脚本,请分享您的使用体验: + +- 🐛 **错误报告**:发现任何问题?请告诉我们! +- 💡 **功能建议**:有改进想法? +- ⭐ **成功案例**:分享工具如何帮助到您! +- 🔧 **技术反馈**:性能、兼容性或易用性方面的见解 + +您的反馈帮助我们为所有人改进工具。欢迎提交issue或为项目做出贡献! + +--- + ## ⭐ 项目统计
diff --git a/README_JP.md b/README_JP.md new file mode 100644 index 00000000..d52008dd --- /dev/null +++ b/README_JP.md @@ -0,0 +1,587 @@ +# 🚀 Cursor 無料試用リセットツール + +
+ +[![Release](https://img.shields.io/github/v/release/yuaotian/go-cursor-help?style=flat-square&logo=github&color=blue)](https://github.com/yuaotian/go-cursor-help/releases/latest) +[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE) +[![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=flat-square&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers) + +[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md) + +Cursor Logo + + + +
+ +> ⚠️ **重要なお知らせ** +> +> このツールは現在以下のバージョンをサポートしています: +> - ✅ Windows: 最新の1.0.xバージョン(サポート済み) +> - ✅ Mac/Linux: 最新の1.0.xバージョン(サポート済み、フィードバック歓迎) +> +> このツールを使用する前に、Cursorのバージョンを確認してください。 + +
+📦 バージョン履歴とダウンロード + +
+ +### 🌟 最新バージョン + +[完全なバージョン履歴を見る]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file)) + +
+ +
+ +⚠️ **Cursorの一般的な解決策** +> 1. Cursorを閉じ、アカウントからログアウトし、公式サイトの設定からアカウントを削除します(IPノードを更新:日本、シンガポール、アメリカ、香港など、低遅延を優先。必須ではありませんが条件が整えば変更してください。Windowsユーザーの場合はDNSキャッシュの更新をお勧めします:`ipconfig /flushdns`) +> Cursor公式サイトで現在のアカウントを削除します +> 手順:ユーザーアイコン->設定->左下のAdvanced▼->Delete Account +> +> 2. マシンコードリセットスクリプトを実行します。下記のスクリプトアドレスを参照してください。 +> +> 3. アカウントを再登録し、ログインして、Cursorを開くと、正常に使用できるようになります。 +> +> 4. 代替案:ステップ[**3**]の後でもまだ使用できない場合、またはアカウント登録に失敗したり、アカウントを削除できないなどの問題が発生した場合、これは通常、ブラウザがターゲットサイトに識別または制限されている(リスク管理)ことを意味します。この場合、Edge、Google Chrome、Firefoxなど別のブラウザを試してみてください(または、ブラウザのフィンガープリント情報を変更またはランダム化できるブラウザの使用を検討してください)。 + + +--- + +⚠️ **MACアドレス変更警告** +> +> Macユーザーの皆様へ: このスクリプトにはMACアドレス変更機能が含まれています。以下の操作が行われます: +> - ネットワークインターフェースのMACアドレスを変更します +> - 変更前に元のMACアドレスをバックアップします +> - この変更により一時的にネットワーク接続が影響を受ける可能性があります +> - 実行中にこのステップをスキップすることができます +> + +
+🔒 自動更新機能の無効化 + +> Cursorがサポートされていない新しいバージョンに自動的に更新されるのを防ぐために、自動更新機能を無効にすることができます。 + +#### 方法1: 組み込みスクリプトを使用する(推奨) + +リセットツールを実行するとき、スクリプトは自動更新を無効にするかどうかを尋ねます: +```text +[質問] Cursorの自動更新機能を無効にしますか? +0) いいえ - デフォルト設定を維持(Enterキーを押す) +1) はい - 自動更新を無効にする +``` + +`1`を選択して無効化操作を自動的に完了します。 + +#### 方法2: 手動で無効化 + +**Windows:** +1. すべてのCursorプロセスを閉じます +2. ディレクトリを削除します: `%LOCALAPPDATA%\cursor-updater` +3. 同じ名前のファイルを作成します(拡張子なし) + +**macOS:** +```bash +# 注意: テスト済みでは、この方法はバージョン0.45.11およびそれ以前のバージョンでのみ機能します。 +# Cursorを閉じます +pkill -f "Cursor" +# app-update.ymlを空の読み取り専用ファイルに置き換えます +cd /Applications/Cursor.app/Contents/Resources +mv app-update.yml app-update.yml.bak +touch app-update.yml +chmod 444 app-update.yml + +# 設定 -> アプリケーション -> 更新、モードをnoneに設定します。 +# これを行わないと、Cursorは更新をチェックし続けます。 + +# 注意: cursor-updaterの変更方法はもはや有効ではないかもしれません +# いずれにせよ、更新ディレクトリを削除し、ブロックファイルを作成します +rm -rf ~/Library/Application\ Support/Caches/cursor-updater +touch ~/Library/Application\ Support/Caches/cursor-updater +``` + +**Linux:** +```bash +# Cursorを閉じます +pkill -f "Cursor" +# 更新ディレクトリを削除し、ブロックファイルを作成します +rm -rf ~/.config/cursor-updater +touch ~/.config/cursor-updater +``` + +> ⚠️ **注意:** 自動更新を無効にした後、新しいバージョンを手動でダウンロードしてインストールする必要があります。新しいバージョンが互換性があることを確認した後に更新することをお勧めします。 + +
+ +--- + +### 📝 説明 + +> これらのメッセージのいずれかに遭遇した場合: + +#### 問題1: 試用アカウント制限

Back To Top

+ +```text +Too many free trial accounts used on this machine. +Please upgrade to pro. We have this limit in place +to prevent abuse. Please let us know if you believe +this is a mistake. +``` + +#### 問題2: APIキー制限

Back To Top

+ +```text +[New Issue] + +Composer relies on custom models that cannot be billed to an API key. +Please disable API keys and use a Pro or Business subscription. +Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +``` + +#### 問題3: 試用リクエスト制限 + +> これは、VIP無料試用期間中に使用制限に達したことを示しています: + +```text +You've reached your trial request limit. +``` + +#### 問題4: Claude 3.7 高負荷

Back To Top

+ +```text +High Load +We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the +'default' model, Claude 3.5 sonnet, another model, or try again in a few moments. +``` + +
+ +

+ +#### 解決策 : Cursorを完全にアンインストールして再インストールする(APIキーの問題) + +1. [Geek.exeアンインストーラー[無料]](https://geekuninstaller.com/download)をダウンロードします +2. Cursorアプリを完全にアンインストールします +3. Cursorアプリを再インストールします +4. 解決策1を続行します + +
+ +

+ +> 一時的な解決策: + +#### 解決策1: クイックリセット(推奨) + +1. Cursorアプリケーションを閉じます +2. マシンコードリセットスクリプトを実行します(以下のインストール手順を参照) +3. Cursorを再度開いて使用を続けます + +#### 解決策2: アカウントの切り替え + +1. ファイル -> Cursor設定 -> サインアウト +2. Cursorを閉じます +3. マシンコードリセットスクリプトを実行します +4. 新しいアカウントでログインします + +#### 解決策3: ネットワークの最適化 + +上記の解決策が機能しない場合は、次のことを試してください: + +- 低遅延ノードに切り替えます(推奨地域:日本、シンガポール、米国、香港) +- ネットワークの安定性を確保します +- ブラウザのキャッシュをクリアして再試行します + +#### 解決策4: Claude 3.7 アクセス問題(高負荷) + +Claude 3.7 Sonnetの"High Load"メッセージが表示された場合、これはCursorが特定の時間帯に無料試用アカウントの3.7モデルの使用を制限していることを示しています。次のことを試してください: + +1. Gmailで作成した新しいアカウントに切り替えます。異なるIPアドレスを使用して接続することをお勧めします +2. 非ピーク時間帯にアクセスを試みます(通常、5-10 AMまたは3-7 PMの間に制限が少ないです) +3. Proにアップグレードしてアクセスを保証します +4. Claude 3.5 Sonnetを代替オプションとして使用します + +> 注意: Cursorがリソース配分ポリシーを調整するにつれて、これらのアクセスパターンは変更される可能性があります。 + +### 💻 システムサポート + + + + + + + +
+ +**Windows** ✅ + +- x64 (64ビット) +- x86 (32ビット) + + + +**macOS** ✅ + +- Intel (x64) +- Apple Silicon (M1/M2) + + + +**Linux** ✅ + +- x64 (64ビット) +- x86 (32ビット) +- ARM64 + +
+ + + +### 🚀 ワンクリックソリューション + +
+グローバルユーザー + +**macOS** + +```bash +# 方法2 +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh +``` + +**Linux** + +```bash +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash +``` + +**Windows** + +```powershell +irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +**Windows (強化版)** + +```powershell +irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` +> デュアルモード操作とトライアルリセット機能を備えた強化版Cursorマシンコード修正ツール + +
+Run Success +
+ +
+ +
+中国ユーザー(推奨) + +**macOS** + +```bash +curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh +``` + +**Linux** + +```bash +curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash +``` + +> **Linuxユーザーへの注意:** スクリプトは、一般的なパス(`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`)の確認、`which cursor` コマンドの使用、および `/usr`、`/opt`、`$HOME/.local` ディレクトリ内の検索によって、Cursor のインストールを見つけようとします。Cursorが他の場所にインストールされているか、これらの方法で見つからない場合、スクリプトは失敗する可能性があります。これらの標準的な場所または方法のいずれかを通じてCursorにアクセスできることを確認してください。 + +**Windows** + +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +**Windows (強化版)** + +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` +> デュアルモード操作とトライアルリセット機能を備えた強化版Cursorマシンコード修正ツール + +
+ +
+Windowsターミナルの実行と構成 + +#### Windowsで管理者ターミナルを開く方法: + +##### 方法1: Win + Xショートカットを使用する +```md +1. Win + Xキーの組み合わせを押します +2. メニューから次のオプションのいずれかを選択します: + - "Windows PowerShell (管理者)" + - "Windows Terminal (管理者)" + - "ターミナル (管理者)" + (Windowsのバージョンによってオプションが異なる場合があります) +``` + +##### 方法2: Win + R実行コマンドを使用する +```md +1. Win + Rキーの組み合わせを押します +2. 実行ダイアログにpowershellまたはpwshと入力します +3. Ctrl + Shift + Enterを押して管理者として実行します + または開いたウィンドウに次のように入力します: Start-Process pwsh -Verb RunAs +4. 管理者ターミナルにリセットスクリプトを入力します: + +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +強化版スクリプト: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +##### 方法3: 検索を使用する +>![PowerShellを検索](img/pwsh_1.png) +> +>検索ボックスにpwshと入力し、右クリックして「管理者として実行」を選択します +>![管理者として実行](img/pwsh_2.png) + +管理者ターミナルにリセットスクリプトを入力します: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +強化版スクリプト: +```powershell +irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex +``` + +### 🔧 PowerShellインストールガイド + +システムにPowerShellがインストールされていない場合は、次の方法でインストールできます: + +#### 方法1: Wingetを使用してインストール(推奨) + +1. コマンドプロンプトまたはPowerShellを開きます +2. 次のコマンドを実行します: +```powershell +winget install --id Microsoft.PowerShell --source winget +``` + +#### 方法2: 手動でインストール + +1. システムに適したインストーラーをダウンロードします: + - [PowerShell-7.4.6-win-x64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x64.msi)(64ビットシステム用) + - [PowerShell-7.4.6-win-x86.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x86.msi)(32ビットシステム用) + - [PowerShell-7.4.6-win-arm64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-arm64.msi)(ARM64システム用) + +2. ダウンロードしたインストーラーをダブルクリックし、インストールの指示に従います + +> 💡 問題が発生した場合は、[Microsoft公式インストールガイド](https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows)を参照してください + +
+ +#### Windowsインストール機能: + +- 🔍 PowerShell 7が利用可能な場合は自動的に検出して使用します +- 🛡️ UACプロンプトを介して管理者権限を要求します +- 📝 PS7が見つからない場合はWindows PowerShellにフォールバックします +- 💡 権限昇格に失敗した場合は手動の指示を提供します + +これで完了です!スクリプトは次のことを行います: + +1. ✨ ツールを自動的にインストールします +2. 🔄 Cursorの試用期間を即座にリセットします + +### 📦 手動インストール + +> [リリース](https://github.com/yuaotian/go-cursor-help/releases/latest)からシステムに適したファイルをダウンロードします + +
+Windowsパッケージ + +- 64ビット: `cursor-id-modifier_windows_x64.exe` +- 32ビット: `cursor-id-modifier_windows_x86.exe` +
+ +
+macOSパッケージ + +- Intel: `cursor-id-modifier_darwin_x64_intel` +- M1/M2: `cursor-id-modifier_darwin_arm64_apple_silicon` +
+ +
+Linuxパッケージ + +- 64ビット: `cursor-id-modifier_linux_x64` +- 32ビット: `cursor-id-modifier_linux_x86` +- ARM64: `cursor-id-modifier_linux_arm64` +
+ +### 🔧 技術的詳細 + +
+構成ファイル + +プログラムはCursorの`storage.json`構成ファイルを変更します。場所は次のとおりです: + +- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json` +- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json` +- Linux: `~/.config/Cursor/User/globalStorage/storage.json` +
+ +
+変更されたフィールド + +ツールは次の新しい一意の識別子を生成します: + +- `telemetry.machineId` +- `telemetry.macMachineId` +- `telemetry.devDeviceId` +- `telemetry.sqmId` +
+ +
+手動自動更新無効化 + +Windowsユーザーは自動更新機能を手動で無効にすることができます: + +1. すべてのCursorプロセスを閉じます +2. ディレクトリを削除します: `C:\Users\username\AppData\Local\cursor-updater` +3. 同じ名前のファイルを作成します: `cursor-updater`(拡張子なし) + +macOS/Linuxユーザーはシステム内で同様の`cursor-updater`ディレクトリを見つけて同じ操作を行うことができます。 + +
+ +
+安全機能 + +- ✅ 安全なプロセス終了 +- ✅ アトミックファイル操作 +- ✅ エラーハンドリングとリカバリ +
+ +
+レジストリ変更通知 + +> ⚠️ **重要: このツールはWindowsレジストリを変更します** + +#### 変更されたレジストリ +- パス: `コンピュータ\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` +- キー: `MachineGuid` + +#### 潜在的な影響 +このレジストリキーを変更すると、次のことに影響を与える可能性があります: +- Windowsシステムの一意のデバイス識別 +- 特定のソフトウェアのデバイス認識と認証状態 +- ハードウェア識別に基づくシステム機能 + +#### 安全対策 +1. 自動バックアップ + - 変更前に元の値が自動的にバックアップされます + - バックアップ場所: `%APPDATA%\Cursor\User\globalStorage\backups` + - バックアップファイル形式: `MachineGuid.backup_YYYYMMDD_HHMMSS` + +2. 手動復元手順 + - レジストリエディタ(regedit)を開きます + - 次の場所に移動します: `コンピュータ\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` + - `MachineGuid`を右クリックします + - 「修正」を選択します + - バックアップファイルの値を貼り付けます + +#### 重要な注意事項 +- 変更前にバックアップファイルの存在を確認します +- 必要に応じてバックアップファイルを使用して元の値を復元します +- レジストリの変更には管理者権限が必要です +
+ +--- + +### 📚 推奨読書 + +- [Cursorの問題収集と解決策](https://mp.weixin.qq.com/s/pnJrH7Ifx4WZvseeP1fcEA) +- [AIユニバーサル開発アシスタントプロンプトガイド](https://mp.weixin.qq.com/s/PRPz-qVkFJSgkuEKkTdzwg) + +--- + +## サポート + +
+このツールが役立つと感じた場合、スパイシーグルテンのおやつ(Latiao)を買っていただけると嬉しいです~ 💁☕️ + + + + + + + + + + +
+WeChat Pay
+WeChat Pay
+要到饭咧?啊咧?啊咧?不给也没事~ 请随意打赏 +
+Alipay
+Alipay
+如果觉得有帮助,来包辣条犒劳一下吧~ +
+Alipay
+Alipay
+1 Latiao = 1 AI thought cycle +
+WeChat
+WeChat
+二维码7天内(9月29日前前)有效,过期请加微信或者公众号`煎饼果子卷AI` +
+
+ +--- + +## 💬 フィードバック&提案 + +新しい強化スクリプトに関するフィードバックをお待ちしています!`cursor_win_id_modifier.ps1` スクリプトをお試しいただいた方は、ぜひご体験をお聞かせください: + +- 🐛 **バグレポート**:問題を発見されましたか?お知らせください! +- 💡 **機能提案**:改善のアイデアはありますか? +- ⭐ **成功事例**:ツールがどのようにお役に立ったかお聞かせください! +- 🔧 **技術的フィードバック**:パフォーマンス、互換性、使いやすさに関するご意見 + +皆様のフィードバックは、すべてのユーザーのためにツールを改善するのに役立ちます。お気軽にissueを開いたり、プロジェクトに貢献してください! + +--- + +## ⭐ プロジェクト統計 + +
+ +[![Star History Chart](https://api.star-history.com/svg?repos=yuaotian/go-cursor-help&type=Date)](https://star-history.com/#yuaotian/go-cursor-help&Date) + +![Repobeats analytics image](https://repobeats.axiom.co/api/embed/ddaa9df9a94b0029ec3fad399e1c1c4e75755477.svg "Repobeats analytics image") + +
+ +## 📄 ライセンス + +
+MITライセンス + +Copyright (c) 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +
diff --git a/cmd/cursor-id-modifier/main.go b/cmd/cursor-id-modifier/main.go index 89de5f3e..4ff9e0e0 100644 --- a/cmd/cursor-id-modifier/main.go +++ b/cmd/cursor-id-modifier/main.go @@ -11,13 +11,13 @@ import ( "runtime/debug" "strings" + "github.com/sirupsen/logrus" + "github.com/yuaotian/go-cursor-help/internal/config" "github.com/yuaotian/go-cursor-help/internal/lang" "github.com/yuaotian/go-cursor-help/internal/process" "github.com/yuaotian/go-cursor-help/internal/ui" "github.com/yuaotian/go-cursor-help/pkg/idgen" - - "github.com/sirupsen/logrus" ) // Global variables @@ -29,7 +29,15 @@ var ( ) func main() { - setupErrorRecovery() + // Place defer at the beginning of main to ensure it can catch panics from all subsequent function calls + defer func() { + if r := recover(); r != nil { + log.Errorf("Panic recovered: %v\n", r) + debug.PrintStack() + waitExit() + } + }() + handleFlags() setupLogger() @@ -73,16 +81,6 @@ func main() { } } -func setupErrorRecovery() { - defer func() { - if r := recover(); r != nil { - log.Errorf("Panic recovered: %v\n", r) - debug.PrintStack() - waitExit() - } - }() -} - func handleFlags() { flag.Parse() if *showVersion { diff --git a/img/alipay_scan_pay.jpg b/img/alipay_scan_pay.jpg new file mode 100644 index 00000000..eee8bbfc Binary files /dev/null and b/img/alipay_scan_pay.jpg differ diff --git a/img/etc.png b/img/etc.png new file mode 100644 index 00000000..078b1d8e Binary files /dev/null and b/img/etc.png differ diff --git a/img/qun-16.png b/img/qun-16.png new file mode 100644 index 00000000..af47faca Binary files /dev/null and b/img/qun-16.png differ diff --git a/img/qun-17.png b/img/qun-17.png new file mode 100644 index 00000000..7d84f765 Binary files /dev/null and b/img/qun-17.png differ diff --git a/img/qun-18.jpg b/img/qun-18.jpg new file mode 100644 index 00000000..929070e0 Binary files /dev/null and b/img/qun-18.jpg differ diff --git a/img/qun-19.jpg b/img/qun-19.jpg new file mode 100644 index 00000000..7c232f89 Binary files /dev/null and b/img/qun-19.jpg differ diff --git a/img/wx_group.png b/img/wx_group.png deleted file mode 100644 index 77ed143d..00000000 Binary files a/img/wx_group.png and /dev/null differ diff --git a/img/wx_group2.png b/img/wx_group2.png deleted file mode 100644 index b747b33a..00000000 Binary files a/img/wx_group2.png and /dev/null differ diff --git a/img/wx_group7.jpg b/img/wx_group7.jpg new file mode 100644 index 00000000..2a12f68c Binary files /dev/null and b/img/wx_group7.jpg differ diff --git a/img/zanzhu/twillot.png b/img/zanzhu/twillot.png new file mode 100644 index 00000000..459f6012 Binary files /dev/null and b/img/zanzhu/twillot.png differ diff --git a/process_cursor_links.py b/process_cursor_links.py new file mode 100644 index 00000000..29520643 --- /dev/null +++ b/process_cursor_links.py @@ -0,0 +1,375 @@ +import csv +from dataclasses import dataclass +from typing import List +import json + +@dataclass +class CursorVersion: + version: str + build_id: str + + def get_download_links(self) -> dict: + base_url = f"https://downloader.cursor.sh/builds/{self.build_id}" + return { + "windows": { + "x64": f"{base_url}/windows/nsis/x64", + "arm64": f"{base_url}/windows/nsis/arm64" + }, + "mac": { + "universal": f"{base_url}/mac/installer/universal", + "arm64": f"{base_url}/mac/installer/arm64", + "x64": f"{base_url}/mac/installer/x64" + }, + "linux": { + "x64": f"{base_url}/linux/appImage/x64" + } + } + +def parse_versions(data: str) -> List[CursorVersion]: + versions = [] + for line in data.strip().split('\n'): + if not line: + continue + version, build_id = line.strip().split(',') + versions.append(CursorVersion(version, build_id)) + return versions + +def generate_markdown(versions: List[CursorVersion]) -> str: + md = """# 🖥️ Windows + +## x64 +
+📦 Windows x64 安装包 + +| 版本 | 下载链接 | +|------|----------| +""" + + # Windows x64 + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['windows']['x64']}) |\n" + + md += """ +
+ +## ARM64 +
+📱 Windows ARM64 安装包 + +| 版本 | 下载链接 | +|------|----------| +""" + + # Windows ARM64 + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['windows']['arm64']}) |\n" + + md += """ +
+ +# 🍎 macOS + +## Universal +
+🎯 macOS Universal 安装包 + +| 版本 | 下载链接 | +|------|----------| +""" + + # macOS Universal + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['mac']['universal']}) |\n" + + md += """ +
+ +## ARM64 +
+💪 macOS ARM64 安装包 + +| 版本 | 下载链接 | +|------|----------| +""" + + # macOS ARM64 + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['mac']['arm64']}) |\n" + + md += """ +
+ +## Intel +
+💻 macOS Intel 安装包 + +| 版本 | 下载链接 | +|------|----------| +""" + + # macOS Intel + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['mac']['x64']}) |\n" + + md += """ +
+ +# 🐧 Linux + +## x64 +
+🎮 Linux x64 AppImage + +| 版本 | 下载链接 | +|------|----------| +""" + + # Linux x64 + for version in versions: + links = version.get_download_links() + md += f"| {version.version} | [下载]({links['linux']['x64']}) |\n" + + md += """ +
+ + +""" + return md + +def main(): + # 示例数据 + data = """ +0.45.11,250207y6nbaw5qc +0.45.10,250205buadkzpea +0.45.9,250202tgstl42dt +0.45.8,250201b44xw1x2k +0.45.7,250130nr6eorv84 +0.45.6,25013021lv9say3 +0.45.5,250128loaeyulq8 +0.45.4,250126vgr3vztvj +0.45.3,250124b0rcj0qql +0.45.2,250123mhituoa6o +0.45.1,2501213ljml5byg +0.45.0,250120dh9ezx9pg +0.44.11,250103fqxdt5u9z +0.44.10,250102ys80vtnud +0.44.9,2412268nc6pfzgo +0.44.8,241222ooktny8mh +0.44.7,2412219nhracv01 +0.44.6,2412214pmryneua +0.44.5,241220s3ux0e1tv +0.44.4,241219117fcvexy +0.44.3,241218sybfbogmq +0.44.2,241218ntls52u8v +0.44.0,2412187f9v0nffu +0.43.6,241206z7j6me2e2 +0.43.5,241127pdg4cnbu2 +0.43.4,241126w13goyvrs +0.43.3,2411246yqzx1jmm +0.43.1,241124gsiwb66nc +0.42.5,24111460bf2loz1 +0.42.4,2410291z3bdg1dy +0.42.3,241016kxu9umuir +0.42.2,2410127mj66lvaq +0.42.1,241011i66p9fuvm +0.42.0,241009fij7nohn5 +0.41.3,240925fkhcqg263 +0.41.2,240921llnho65ov +0.41.1,2409189xe3envg5 +0.40.4,2409052yfcjagw2 +0.40.3,240829epqamqp7h +0.40.2,240828c021k3aib +0.40.1,2408245thnycuzj +0.40.0,24082202sreugb2 +0.39.6,240819ih4ta2fye +0.39.5,240814y9rhzmu7h +0.39.4,240810elmeg3seq +0.39.3,2408092hoyaxt9m +0.39.2,240808phaxh4b5r +0.39.1,240807g919tr4ly +0.39.0,240802cdixtv9a6 +0.38.1,240725f0ti25os7 +0.38.0,240723790oxe4a2 +0.37.1,240714yrr3gmv3k +0.36.2,2407077n6pzboby +0.36.1,240706uekt2eaft +0.36.0,240703xqkjv5aqa +0.35.1,240621pc2f7rl8a +0.35.0,240608cv11mfsjl +0.34.6,240606kgzq24cfb +0.34.6,240605r495newcf +0.34.5,240602rq6xovt3a +0.34.4,2406014h0rgjghe +0.34.3,240529baisuyd2e +0.34.2,240528whh1qyo9h +0.34.1,24052838ygfselt +0.34.0,240527xus72jmkj +0.33.4,240511kb8wt1tms +0.33.3,2405103lx8342ta +0.33.2,240510dwmw395qe +0.33.1,2405039a9h2fqc9 +0.33.0,240503hyjsnhazo +0.32.8,240428d499o6zja +0.32.7,240427w5guozr0l +0.32.2,240417ab4wag7sx +0.32.1,2404152czor73fk +0.32.0,240412ugli06ue0 +0.31.3,240402rq154jw46 +0.31.1,240402pkwfm2ps6 +0.31.0,2404018j7z0xv2g +0.30.5,240327tmd2ozdc7 +0.30.4,240325dezy8ziab +0.30.3,2403229gtuhto9g +0.30.2,240322gzqjm3p0d +0.30.1,2403212w1ejubt8 +0.30.0,240320tpx86e7hk +0.29.1,2403027twmz0d1t +0.29.0,240301kpqvacw2h +0.28.1,240226tstim4evd +0.28.0,240224g2d7jazcq +0.27.4,240219qdbagglqz +0.27.3,240218dxhc6y8os +0.27.2,240216kkzl9nhxi +0.27.1,240215l4ooehnyl +0.27.0,240215at6ewkd59 +0.26.2,240212o6r9qxtcg +0.26.1,2402107t904hing +0.26.0,240210k8is5xr6v +0.25.3,240207aacboj1k8 +0.25.2,240206p3708uc9z +0.25.1,2402033t030rprh +0.25.0,240203kh86t91q8 +0.24.4,240129iecm3e33w +0.24.3,2401289dx79qsc0 +0.24.1,240127cad17436d +0.24.0,240126wp9irhmza +0.23.9,240124dsmraeml3 +0.23.8,240123fnn1hj1fg +0.23.7,240123xsfe7ywcv +0.23.6,240121m1740elox +0.23.5,2401215utj6tx6q +0.23.4,240121f4qy6ba2y +0.23.3,2401201und3ytom +0.23.2,240120an2k2hf1i +0.23.1,240119fgzxwudn9 +0.22.2,24011721vsch1l1 +0.22.1,2401083eyk8kmzc +0.22.0,240107qk62kvva3 +0.21.1,231230h0vi6srww +0.21.0,231229ezidnxiu3 +0.20.2,231219aksf83aad +0.20.1,231218ywfaxax09 +0.20.0,231216nsyfew5j1 +0.19.1,2312156z2ric57n +0.19.0,231214per9qal2p +0.18.8,2312098ffjr3ign +0.18.7,23120880aolip2i +0.18.6,231207ueqazwde8 +0.18.5,231206jzy2n2sbi +0.18.4,2312033zjv5fqai +0.18.3,231203k2vnkxq2m +0.18.1,23120176kaer07t +0.17.0,231127p7iyxn8rg +0.16.0,231116rek2xuq6a +0.15.5,231115a5mv63u9f +0.15.4,23111469e1i3xyi +0.15.3,231113b0yv3uqem +0.15.2,231113ah0kuf3pf +0.15.1,231111yanyyovap +0.15.0,231110mdkomczmw +0.14.1,231109xitrgihlk +0.14.0,231102m6tuamwbx +0.13.4,231029rso7pso8l +0.13.3,231025uihnjkh9v +0.13.2,231024w4iv7xlm6 +0.13.1,231022f3j0ubckv +0.13.0,231022ptw6i4j42 +0.12.3,231008c5ursm0oj""" + + versions = parse_versions(data) + + # 生成 Markdown 文件 + markdown_content = generate_markdown(versions) + with open('Cursor历史.md', 'w', encoding='utf-8') as f: + f.write(markdown_content) + + # 创建结果数据结构 + result = { + "versions": [] + } + + # 处理每个版本 + for version in versions: + version_info = { + "version": version.version, + "build_id": version.build_id, + "downloads": version.get_download_links() + } + result["versions"].append(version_info) + + # 保存为JSON文件 + with open('cursor_downloads.json', 'w', encoding='utf-8') as f: + json.dump(result, f, indent=2, ensure_ascii=False) + + # 同时生成CSV格式的下载链接 + with open('cursor_downloads.csv', 'w', newline='', encoding='utf-8') as f: + writer = csv.writer(f) + writer.writerow(['Version', 'Platform', 'Architecture', 'Download URL']) + + for version in versions: + links = version.get_download_links() + for platform, archs in links.items(): + for arch, url in archs.items(): + writer.writerow([version.version, platform, arch, url]) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/cursor_id_modifier.pot b/scripts/cursor_id_modifier.pot new file mode 100644 index 00000000..54f44177 --- /dev/null +++ b/scripts/cursor_id_modifier.pot @@ -0,0 +1,318 @@ +msgid "" +msgstr "" +"Project-Id-Version: cursor_id_modifier\n" +"POT-Creation-Date: 2025-04-25 12:00+0000\n" +"PO-Revision-Date: 2025-04-25 12:00+0000\n" +"Language-Team: None\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Error: No translation file found for domain 'cursor_id_modifier' in {}/zh_CN/LC_MESSAGES/" +msgstr "" + +msgid "========== Cursor ID modification tool log start {} ==========" +msgstr "" + +msgid "[INFO] {} {}" +msgstr "" + +msgid "[WARN] {} {}" +msgstr "" + +msgid "[ERROR] {} {}" +msgstr "" + +msgid "[DEBUG] {} {}" +msgstr "" + +msgid "[CMD] {} Executing command: {}" +msgstr "" + +msgid "[CMD] {}:" +msgstr "" + +msgid "Unable to get username" +msgstr "" + +msgid "Finding Cursor installation path..." +msgstr "" + +msgid "Found Cursor installation path: {}" +msgstr "" + +msgid "Found Cursor via which: {}" +msgstr "" + +msgid "Cursor executable not found, will try using config directory" +msgstr "" + +msgid "Found Cursor via search: {}" +msgstr "" + +msgid "Finding Cursor resource directory..." +msgstr "" + +msgid "Found Cursor resource directory: {}" +msgstr "" + +msgid "Found resource directory via binary path: {}" +msgstr "" + +msgid "Cursor resource directory not found" +msgstr "" + +msgid "Please run this script with sudo" +msgstr "" + +msgid "Example: sudo {}" +msgstr "" + +msgid "Checking Cursor processes..." +msgstr "" + +msgid "Getting process details for {}:" +msgstr "" + +msgid "No running Cursor processes found" +msgstr "" + +msgid "Found running Cursor processes" +msgstr "" + +msgid "Attempting to terminate Cursor processes..." +msgstr "" + +msgid "Attempting to forcefully terminate processes..." +msgstr "" + +msgid "Waiting for processes to terminate, attempt {}/{}..." +msgstr "" + +msgid "Cursor processes successfully terminated" +msgstr "" + +msgid "Unable to terminate Cursor processes after {} attempts" +msgstr "" + +msgid "Please manually terminate the processes and try again" +msgstr "" + +msgid "Configuration file does not exist, skipping backup" +msgstr "" + +msgid "Configuration backed up to: {}" +msgstr "" + +msgid "Backup failed" +msgstr "" + +msgid "File does not exist: {}" +msgstr "" + +msgid "Unable to modify file permissions: {}" +msgstr "" + +msgid "Generated temporary file is empty" +msgstr "" + +msgid "Unable to write to file: {}" +msgstr "" + +msgid "Machine code reset options" +msgstr "" + +msgid "Do you need to reset the machine code? (Usually, modifying JS files is sufficient):" +msgstr "" + +msgid "Don't reset - only modify JS files" +msgstr "" + +msgid "Reset - modify both config file and machine code" +msgstr "" + +msgid "[INPUT_DEBUG] Machine code reset option selected: {}" +msgstr "" + +msgid "You chose to reset the machine code" +msgstr "" + +msgid "Found existing configuration file: {}" +msgstr "" + +msgid "Setting new device and machine IDs..." +msgstr "" + +msgid "New device ID: {}" +msgstr "" + +msgid "New machine ID: {}" +msgstr "" + +msgid "Configuration file modified successfully" +msgstr "" + +msgid "Configuration file modification failed" +msgstr "" + +msgid "Configuration file not found, this is normal, skipping ID modification" +msgstr "" + +msgid "You chose not to reset the machine code, will only modify JS files" +msgstr "" + +msgid "Configuration processing completed" +msgstr "" + +msgid "Finding Cursor's JS files..." +msgstr "" + +msgid "Searching for JS files in resource directory: {}" +msgstr "" + +msgid "Found JS file: {}" +msgstr "" + +msgid "No JS files found in resource directory, trying other directories..." +msgstr "" + +msgid "Searching directory: {}" +msgstr "" + +msgid "No modifiable JS files found" +msgstr "" + +msgid "Found {} JS files to modify" +msgstr "" + +msgid "Starting to modify Cursor's JS files..." +msgstr "" + +msgid "Unable to find modifiable JS files" +msgstr "" + +msgid "Processing file: {}" +msgstr "" + +msgid "Unable to create backup for file: {}" +msgstr "" + +msgid "Found x-cursor-checksum setting code" +msgstr "" + +msgid "Successfully modified x-cursor-checksum setting code" +msgstr "" + +msgid "Failed to modify x-cursor-checksum setting code" +msgstr "" + +msgid "Found IOPlatformUUID keyword" +msgstr "" + +msgid "Successfully injected randomUUID call into a$ function" +msgstr "" + +msgid "Failed to modify a$ function" +msgstr "" + +msgid "Successfully injected randomUUID call into v5 function" +msgstr "" + +msgid "Failed to modify v5 function" +msgstr "" + +msgid "Completed universal modification" +msgstr "" + +msgid "File already contains custom injection code, skipping modification" +msgstr "" + +msgid "Completed most universal injection" +msgstr "" + +msgid "File has already been modified, skipping modification" +msgstr "" + +msgid "Failed to modify any JS files" +msgstr "" + +msgid "Successfully modified {} JS files" +msgstr "" + +msgid "Disabling Cursor auto-update..." +msgstr "" + +msgid "Found update configuration file: {}" +msgstr "" + +msgid "Disabled update configuration file: {}" +msgstr "" + +msgid "Found updater: {}" +msgstr "" + +msgid "Disabled updater: {}" +msgstr "" + +msgid "No update configuration files or updaters found" +msgstr "" + +msgid "Successfully disabled auto-update" +msgstr "" + +msgid "You selected: {}" +msgstr "" + +msgid "This script only supports Linux systems" +msgstr "" + +msgid "Script started..." +msgstr "" + +msgid "System information: {}" +msgstr "" + +msgid "Current user: {}" +msgstr "" + +msgid "System version information" +msgstr "" + +msgid "Cursor Linux startup tool" +msgstr "" + +msgid "Important notice" +msgstr "" + +msgid "This tool prioritizes modifying JS files, which is safer and more reliable" +msgstr "" + +msgid "Modifying Cursor JS files..." +msgstr "" + +msgid "JS files modified successfully!" +msgstr "" + +msgid "JS file modification failed, but configuration file modification may have succeeded" +msgstr "" + +msgid "If Cursor still indicates the device is disabled after restarting, please rerun this script" +msgstr "" + +msgid "Please restart Cursor to apply the new configuration" +msgstr "" + +msgid "Follow the WeChat public account [Pancake AI] to discuss more Cursor tips and AI knowledge (script is free, join the group via the public account for more tips and experts)" +msgstr "" + +msgid "Script execution completed" +msgstr "" + +msgid "========== Cursor ID modification tool log end {} ==========" +msgstr "" + +msgid "Detailed log saved to: {}" +msgstr "" + +msgid "If you encounter issues, please provide this log file to the developer for troubleshooting" +msgstr "" diff --git a/scripts/cursor_id_modifier.py b/scripts/cursor_id_modifier.py new file mode 100644 index 00000000..aad0bc87 --- /dev/null +++ b/scripts/cursor_id_modifier.py @@ -0,0 +1,1298 @@ +# -*- coding: utf-8 -*- +""" +AppImage instructions: +mkdir -p ~/Downloads/Cursor +cd ~/Downloads/Cursor +cd Cursor && ./Cursor-1.0.5-x86_64.AppImage --appimage-extract +mkdir -p ~/.local +rsync -rt ~/Downloads/Cursor/squashfs-root/usr/ ~/.local +# ^ copy the subfolders not usr itself, so the resulting executable should be ~/.local/bin/cursor +""" +import subprocess +import os + +SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__)) +# repo_dir = os.path.dirname(SCRIPTS_DIR) +repo_dir = SCRIPTS_DIR +locales_dir = os.path.join(repo_dir, 'locales') +t_domain = "cursor_id_modifier" + +def compile_messages(): + global _ + languages = ['en_US', 'zh_CN'] + for lang in languages: + lang_dir = os.path.join(locales_dir, lang, 'LC_MESSAGES') + if os.path.isdir(lang_dir): + # Change the directory to the LC_MESSAGES folder + os.chdir(lang_dir) + # Run msgfmt command + out_name = 'cursor_id_modifier.mo' + subprocess.run(['msgfmt', '-o', out_name, 'cursor_id_modifier.po'], check=True) + print(os.path.abspath(out_name)) + else: + print(f"Directory not found: {lang_dir}") + +import sys +import subprocess +import datetime +import tempfile +import shutil +import uuid +import hashlib +import re +import getpass +import time +import select +import tty +import termios +import signal +import json +from pathlib import Path +import glob +import pwd + +import gettext +import random + +# 尝试导入netifaces,如果不可用则使用系统命令 +# Try to import netifaces, use system commands if not available +try: + import netifaces + NETIFACES_AVAILABLE = True +except ImportError: + NETIFACES_AVAILABLE = False + log_warn = lambda x: print(f"[WARN] {x}") # 临时日志函数 + log_warn("netifaces library not found, will use system commands for network operations") + +# 设置语言环境 +# Set language environment + +# lang = 'zh' if '--en' not in sys.argv else 'en' +lang = 'en' + +assert os.path.isdir(locales_dir) +# gettext.bindtextdomain(t_domain, localedir=locales_dir) +if lang == 'zh': + translation = gettext.translation( + t_domain, + localedir=locales_dir, + languages=['en_US', 'zh_CN'], + ) +else: + translation = gettext.NullTranslations() +translation.install() +_ = translation.gettext + +# 设置错误处理 +# Set error handling +def set_error_handling(): + global _ + # 在 Python 中,我们使用 try/except 来处理错误,而不是 bash 的 set -e + # In Python, we use try/except to handle errors instead of bash's set -e + pass + +set_error_handling() + +# 定义日志文件路径 +# Define log file path +LOG_FILE = "/tmp/cursor_linux_id_modifier.log" + +# 初始化日志文件 +# Initialize log file +def initialize_log(): + with open(LOG_FILE, 'w') as f: + f.write(_("========== Cursor ID modification tool log start {} ==========").format(datetime.datetime.now()) + "\n") + os.chmod(LOG_FILE, 0o644) + +# 颜色定义 +# Color definitions +RED = '\033[0;31m' +GREEN = '\033[0;32m' +YELLOW = '\033[1;33m' +BLUE = '\033[0;34m' +NC = '\033[0m' # No Color + +# 日志函数 - 同时输出到终端和日志文件 +# Log functions - output to terminal and log file simultaneously +def log_info(message): + global _ + print(f"{GREEN}[INFO]{NC} {_(message)}") + with open(LOG_FILE, 'a') as f: + f.write(_("[INFO] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n") + +def log_warn(message): + global _ + print(f"{YELLOW}[WARN]{NC} {_(message)}") + with open(LOG_FILE, 'a') as f: + f.write(_("[WARN] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n") + +def log_error(message): + global _ + print(f"{RED}[ERROR]{NC} {_(message)}") + with open(LOG_FILE, 'a') as f: + f.write(_("[ERROR] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n") + +def log_debug(message): + global _ + print(f"{BLUE}[DEBUG]{NC} {_(message)}") + with open(LOG_FILE, 'a') as f: + f.write(_("[DEBUG] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n") + +# 记录命令输出到日志文件 +# Log command output to log file +def log_cmd_output(cmd, msg): + global _ + with open(LOG_FILE, 'a') as f: + f.write(_("[CMD] {} Executing command: {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), cmd) + "\n") + f.write(_("[CMD] {}:").format(_(msg)) + "\n") + process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + with open(LOG_FILE, 'a') as f: + for line in process.stdout: + print(line, end='') + f.write(line) + process.wait() + with open(LOG_FILE, 'a') as f: + f.write("\n") + +# 获取当前用户 +# Get current user +def get_current_user(): + global _ + if os.geteuid() == 0: + return os.environ.get('SUDO_USER', '') + return getpass.getuser() + +CURRENT_USER = get_current_user() +if not CURRENT_USER: + log_error(_("Unable to get username")) + sys.exit(1) + +# 定义Linux下的Cursor路径 +# Define Cursor paths on Linux +CURSOR_CONFIG_DIR = os.path.expanduser("~/.config/Cursor") +STORAGE_FILE = os.path.join(CURSOR_CONFIG_DIR, "User/globalStorage/storage.json") +BACKUP_DIR = os.path.join(CURSOR_CONFIG_DIR, "User/globalStorage/backups") + +# 可能的Cursor二进制路径 +# Possible Cursor binary paths +CURSOR_BIN_PATHS = [ + "/usr/bin/cursor", + "/usr/local/bin/cursor", + os.path.expanduser("~/.local/bin/cursor"), + "/opt/cursor/cursor", + "/snap/bin/cursor", +] + +# 找到Cursor安装路径 +# Find Cursor installation path +def find_cursor_path(): + global _ + log_info(_("Finding Cursor installation path...")) + + for path in CURSOR_BIN_PATHS: + if os.path.isfile(path): + log_info(_("Found Cursor installation path: {}").format(path)) + os.environ['CURSOR_PATH'] = path + return True + + # 尝试通过which命令定位 + # Try locating via which command + try: + result = subprocess.run(['which', 'cursor'], capture_output=True, text=True) + if result.returncode == 0: + os.environ['CURSOR_PATH'] = result.stdout.strip() + log_info(_("Found Cursor via which: {}").format(os.environ['CURSOR_PATH'])) + return True + except subprocess.CalledProcessError: + pass + + # 尝试查找可能的安装路径 + # Try finding possible installation paths + search_dirs = ['/usr', '/opt', os.path.expanduser('~/.local')] + for dir in search_dirs: + try: + for root, _1, files in os.walk(dir): + if 'cursor' in files: + path = os.path.join(root, 'cursor') + if os.access(path, os.X_OK): + os.environ['CURSOR_PATH'] = path + log_info(_("Found Cursor via search: {}").format(os.environ['CURSOR_PATH'])) + return True + except PermissionError: + continue + log_warn(_("Cursor executable not found, will try using config directory")) + return False + +# 查找并定位Cursor资源文件目录 +# Find and locate Cursor resource directory +def find_cursor_resources(): + global _ + log_info(_("Finding Cursor resource directory...")) + + # 可能的资源目录路径 + # Possible resource directory paths + resource_paths = [ + "/usr/lib/cursor", + "/usr/share/cursor", + "/opt/cursor", + os.path.expanduser("~/.local/share/cursor"), + ] + + for path in resource_paths: + if os.path.isdir(path): + log_info(_("Found Cursor resource directory: {}").format(path)) + os.environ['CURSOR_RESOURCES'] = path + return True + + # 如果有CURSOR_PATH,尝试从它推断 + # If CURSOR_PATH exists, try to infer from it + if os.environ.get('CURSOR_PATH'): + base_dir = os.path.dirname(os.environ['CURSOR_PATH']) + resource_dir = os.path.join(base_dir, 'resources') + if os.path.isdir(resource_dir): + os.environ['CURSOR_RESOURCES'] = resource_dir + log_info(_("Found resource directory via binary path: {}").format(os.environ['CURSOR_RESOURCES'])) + return True + + log_warn(_("Cursor resource directory not found")) + return False + +# 检查权限 +# Check permissions +def check_permissions(): + global _ + if os.geteuid() != 0: + log_error(_("Please run this script with sudo")) + print(_("Example: sudo {}").format(sys.argv[0])) + sys.exit(1) + +# 检查并关闭 Cursor 进程 +# Check and kill Cursor processes +def check_and_kill_cursor(): + global _ + log_info(_("Checking Cursor processes...")) + + attempt = 1 + max_attempts = 5 + + # 函数:获取进程详细信息 + # Function: Get process details + def get_process_details(process_name): + log_debug(_("Getting process details for {}:").format(process_name)) + try: + result = subprocess.run( + 'ps aux | grep -i "cursor" | grep -v grep | grep -v "cursor_id_modifier.py"', + shell=True, capture_output=True, text=True + ) + print(result.stdout) + except subprocess.CalledProcessError: + pass + + while attempt <= max_attempts: + # 使用更精确的匹配来获取 Cursor 进程,排除当前脚本和grep进程 + # Use more precise matching to get Cursor processes, excluding current script and grep + try: + result = subprocess.run( + 'ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_id_modifier.py" | awk \'{print $2}\'', + shell=True, capture_output=True, text=True + ) + CURSOR_PIDS = result.stdout.strip().split('\n') + CURSOR_PIDS = [pid for pid in CURSOR_PIDS if pid] + except subprocess.CalledProcessError: + CURSOR_PIDS = [] + + if not CURSOR_PIDS: + log_info(_("No running Cursor processes found")) + return True + + log_warn(_("Found running Cursor processes")) + get_process_details("cursor") + + log_warn(_("Attempting to terminate Cursor processes...")) + + for pid in CURSOR_PIDS: + try: + if attempt == max_attempts: + log_warn(_("Attempting to forcefully terminate processes...")) + os.kill(int(pid), signal.SIGKILL) + else: + os.kill(int(pid), signal.SIGTERM) + except (OSError, ValueError): + continue + + time.sleep(1) + + # 再次检查进程是否还在运行 + # Check again if processes are still running + try: + result = subprocess.run( + 'ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_id_modifier.py"', + shell=True, capture_output=True, text=True + ) + if not result.stdout.strip(): + log_info(_("Cursor processes successfully terminated")) + return True + except subprocess.CalledProcessError: + log_info(_("Cursor processes successfully terminated")) + return True + + log_warn(_("Waiting for processes to terminate, attempt {}/{}...").format(attempt, max_attempts)) + attempt += 1 + + log_error(_("Unable to terminate Cursor processes after {} attempts").format(max_attempts)) + get_process_details("cursor") + log_error(_("Please manually terminate the processes and try again")) + sys.exit(1) + +# 备份配置文件 +# Backup configuration file +def backup_config(): + global _ + if not os.path.isfile(STORAGE_FILE): + log_warn(_("Configuration file does not exist, skipping backup")) + return True + + os.makedirs(BACKUP_DIR, exist_ok=True) + backup_file = os.path.join(BACKUP_DIR, "storage.json.backup_{}".format(datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))) + + try: + shutil.copy(STORAGE_FILE, backup_file) + os.chmod(backup_file, 0o644) + os.chown(backup_file, pwd.getpwnam(CURRENT_USER).pw_uid, -1) + log_info(_("Configuration backed up to: {}").format(backup_file)) + except (OSError, shutil.Error): + log_error(_("Backup failed")) + sys.exit(1) + +# 生成随机 ID +# Generate random ID +def generate_random_id(): + global _ + # 生成32字节(64个十六进制字符)的随机数 + # Generate 32 bytes (64 hexadecimal characters) of random data + return hashlib.sha256(os.urandom(32)).hexdigest() + +# 生成随机 UUID +# Generate random UUID +def generate_uuid(): + global _ + # 在Linux上使用uuid模块生成UUID + # Use uuid module to generate UUID on Linux + try: + return str(uuid.uuid1()).lower() + except Exception: + # 备选方案:生成类似UUID的字符串 + # Fallback: Generate UUID-like string + rand_bytes = os.urandom(16) + rand_hex = rand_bytes.hex() + return f"{rand_hex[:8]}-{rand_hex[8:12]}-{rand_hex[12:16]}-{rand_hex[16:20]}-{rand_hex[20:]}" + +# 修改现有文件 +# Modify or add to configuration file +def modify_or_add_config(key, value, file): + global _ + if not os.path.isfile(file): + log_error(_("File does not exist: {}").format(file)) + return False + + # 确保文件可写 + # Ensure file is writable + try: + os.chmod(file, 0o644) + except OSError: + log_error(_("Unable to modify file permissions: {}").format(file)) + return False + + # 创建临时文件 + # Create temporary file + with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: + temp_path = temp_file.name + + # 读取原始文件 + # Read original file + with open(file, 'r') as f: + content = f.read() + + # 检查key是否存在 + # Check if key exists + if f'"{key}":' in content: + # key存在,执行替换 + # Key exists, perform replacement + pattern = f'"{key}":\\s*"[^"]*"' + replacement = f'"{key}": "{value}"' + new_content = re.sub(pattern, replacement, content) + else: + # key不存在,添加新的key-value对 + # Key does not exist, add new key-value pair + new_content = content.rstrip('}\n') + f',\n "{key}": "{value}"\n}}' + + # 写入临时文件 + # Write to temporary file + with open(temp_path, 'w') as f: + f.write(new_content) + + # 检查临时文件是否为空 + # Check if temporary file is empty + if os.path.getsize(temp_path) == 0: + log_error(_("Generated temporary file is empty")) + os.unlink(temp_path) + return False + + # 替换原文件 + # Replace original file + try: + shutil.move(temp_path, file) + except OSError: + log_error(_("Unable to write to file: {}").format(file)) + os.unlink(temp_path) + return False + + # 恢复文件权限 + # Restore file permissions + os.chmod(file, 0o444) + return True + +# 生成新的配置 +# Generate new configuration +def generate_new_config(): + global _ + print() + log_warn(_("Machine code reset options")) + + # 使用菜单选择函数询问用户是否重置机器码 + # Use menu selection function to ask user whether to reset machine code + reset_choice = select_menu_option( + _("Do you need to reset the machine code? (Usually, modifying JS files is sufficient):"), + [_("Don't reset - only modify JS files"), _("Reset - modify both config file and machine code")], + 0 + ) + + # 记录日志以便调试 + # Log for debugging + with open(LOG_FILE, 'a') as f: + f.write(_("[INPUT_DEBUG] Machine code reset option selected: {}").format(reset_choice) + "\n") + + # 处理用户选择 + # Handle user selection + if reset_choice == 1: + log_info(_("You chose to reset the machine code")) + + # 确保配置文件目录存在 + # Ensure configuration file directory exists + if os.path.isfile(STORAGE_FILE): + log_info(_("Found existing configuration file: {}").format(STORAGE_FILE)) + + # 备份现有配置 + # Backup existing configuration + backup_config() + + # 生成并设置新的设备ID + # Generate and set new device ID + new_device_id = generate_uuid() + new_machine_id = "auth0|user_{}".format(hashlib.sha256(os.urandom(16)).hexdigest()[:32]) + + log_info(_("Setting new device and machine IDs...")) + log_debug(_("New device ID: {}").format(new_device_id)) + log_debug(_("New machine ID: {}").format(new_machine_id)) + + # 修改配置文件 + # Modify configuration file + if (modify_or_add_config("deviceId", new_device_id, STORAGE_FILE) and + modify_or_add_config("machineId", new_machine_id, STORAGE_FILE)): + log_info(_("Configuration file modified successfully")) + else: + log_error(_("Configuration file modification failed")) + else: + log_warn(_("Configuration file not found, this is normal, skipping ID modification")) + else: + log_info(_("You chose not to reset the machine code, will only modify JS files")) + + # 确保配置文件目录存在 + # Ensure configuration file directory exists + if os.path.isfile(STORAGE_FILE): + log_info(_("Found existing configuration file: {}").format(STORAGE_FILE)) + + # 备份现有配置 + # Backup existing configuration + backup_config() + else: + log_warn(_("Configuration file not found, this is normal, skipping ID modification")) + + print() + log_info(_("Configuration processing completed")) + +# 查找Cursor的JS文件 +# Find Cursor's JS files +def find_cursor_js_files(): + global _ + log_info(_("Finding Cursor's JS files...")) + + js_files = [] + found = False + + # 如果找到了资源目录,在资源目录中搜索 + # If resource directory is found, search in it + if os.environ.get('CURSOR_RESOURCES'): + log_debug(_("Searching for JS files in resource directory: {}").format(os.environ['CURSOR_RESOURCES'])) + + # 在资源目录中递归搜索特定JS文件 + # Recursively search for specific JS files in resource directory + js_patterns = [ + "*/extensionHostProcess.js", + "*/main.js", + "*/cliProcessMain.js", + "*/app/out/vs/workbench/api/node/extensionHostProcess.js", + "*/app/out/main.js", + "*/app/out/vs/code/node/cliProcessMain.js", + ] + + for pattern in js_patterns: + try: + files = glob.glob(os.path.join(os.environ['CURSOR_RESOURCES'], pattern), recursive=True) + for file in files: + log_info(_("Found JS file: {}").format(file)) + js_files.append(file) + found = True + except Exception: + continue + + # 如果还没找到,尝试在/usr和$HOME目录下搜索 + # If not found, try searching in /usr and $HOME directories + if not found: + log_warn(_("No JS files found in resource directory, trying other directories...")) + + search_dirs = [ + "/usr/lib/cursor", + "/usr/share/cursor", + "/opt/cursor", + os.path.expanduser("~/.config/Cursor"), + os.path.expanduser("~/.local/share/cursor"), + ] + + for dir in search_dirs: + if os.path.isdir(dir): + log_debug(_("Searching directory: {}").format(dir)) + try: + for root, _1, files in os.walk(dir): + for file in files: + if file.endswith('.js'): + file_path = os.path.join(root, file) + with open(file_path, 'r', errors='ignore') as f: + content = f.read() + if "IOPlatformUUID" in content or "x-cursor-checksum" in content: + log_info(_("Found JS file: {}").format(file_path)) + js_files.append(file_path) + found = True + except Exception: + continue + + if not found: + log_error(_("No modifiable JS files found")) + return False, [] + + # 保存找到的文件列表到环境变量 + # Save found files to environment variable + os.environ['CURSOR_JS_FILES'] = json.dumps(js_files) + log_info(_("Found {} JS files to modify").format(len(js_files))) + return True, js_files + +# 修改Cursor的JS文件 +# Modify Cursor's JS files +def modify_cursor_js_files(): + global _ + log_info(_("Starting to modify Cursor's JS files...")) + + # 先查找需要修改的JS文件 + # First find JS files to modify + success, js_files = find_cursor_js_files() + if not success: + log_error(_("Unable to find modifiable JS files")) + return False + + modified_count = 0 + + for file in js_files: + log_info(_("Processing file: {}").format(file)) + + # 创建文件备份 + # Create file backup + backup_file = f"{file}.backup_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}" + try: + shutil.copy(file, backup_file) + except OSError: + log_error(_("Unable to create backup for file: {}").format(file)) + continue + + # 确保文件可写 + # Ensure file is writable + try: + os.chmod(file, 0o644) + except OSError: + log_error(_("Unable to modify file permissions: {}").format(file)) + continue + + # 读取文件内容 + # Read file content + with open(file, 'r', errors='ignore') as f: + content = f.read() + + # 检查文件内容并进行相应修改 + # Check file content and make appropriate modifications + if 'i.header.set("x-cursor-checksum' in content: + log_debug(_("Found x-cursor-checksum setting code")) + new_content = content.replace( + 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}/${e}`)', + 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}/${p}`)' + ) + if new_content != content: + with open(file, 'w') as f: + f.write(new_content) + log_info(_("Successfully modified x-cursor-checksum setting code")) + modified_count += 1 + else: + log_error(_("Failed to modify x-cursor-checksum setting code")) + shutil.copy(backup_file, file) + elif "IOPlatformUUID" in content: + log_debug(_("Found IOPlatformUUID keyword")) + if "function a$" in content and "return crypto.randomUUID()" not in content: + new_content = content.replace( + "function a$(t){switch", + "function a$(t){return crypto.randomUUID(); switch" + ) + if new_content != content: + with open(file, 'w') as f: + f.write(new_content) + log_debug(_("Successfully injected randomUUID call into a$ function")) + modified_count += 1 + else: + log_error(_("Failed to modify a$ function")) + shutil.copy(backup_file, file) + elif "async function v5" in content and "return crypto.randomUUID()" not in content: + new_content = content.replace( + "async function v5(t){let e=", + "async function v5(t){return crypto.randomUUID(); let e=" + ) + if new_content != content: + with open(file, 'w') as f: + f.write(new_content) + log_debug(_("Successfully injected randomUUID call into v5 function")) + modified_count += 1 + else: + log_error(_("Failed to modify v5 function")) + shutil.copy(backup_file, file) + else: + # 通用注入方法 + # Universal injection method + if "// Cursor ID 修改工具注入" not in content: + timestamp = datetime.datetime.now().strftime('%s') + datetime_s = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + inject_code = f""" +// Cursor ID 修改工具注入 - {datetime_s} +// 随机设备ID生成器注入 - {timestamp} +const randomDeviceId_{timestamp} = () => {{ + try {{ + return require('crypto').randomUUID(); + }} catch (e) {{ + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {{ + const r = Math.random() * 16 | 0; + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }}); + }} +}}; +""" + # NOTE: double {{ or }} is literal, so code matches: + old_bash_inject_code = """ +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) +// 随机设备ID生成器注入 - $(date +%s) +const randomDeviceId_$(date +%s) = () => { + try { + return require('crypto').randomUUID(); + } catch (e) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = Math.random() * 16 | 0; + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } +}; +""" + new_content = inject_code + content + new_content = new_content.replace(f"await v5(!1)", f"randomDeviceId_{timestamp}()") + new_content = new_content.replace(f"a$(t)", f"randomDeviceId_{timestamp}()") + with open(file, 'w') as f: + f.write(new_content) + log_debug(_("Completed universal modification")) + modified_count += 1 + else: + log_info(_("File already contains custom injection code, skipping modification")) + else: + # 未找到关键字,尝试通用方法 + # No keywords found, try universal method + if "return crypto.randomUUID()" not in content and "// Cursor ID 修改工具注入" not in content: + if "function t$()" in content or "async function y5" in content: + new_content = content + if "function t$()" in new_content: + new_content = new_content.replace( + "function t$(){", + 'function t$(){return "00:00:00:00:00:00";' + ) + if "async function y5" in new_content: + new_content = new_content.replace( + "async function y5(t){", + "async function y5(t){return crypto.randomUUID();" + ) + if new_content != content: + with open(file, 'w') as f: + f.write(new_content) + modified_count += 1 + else: + # 最通用的注入方法 + # Most universal injection method + new_uuid = generate_uuid() + machine_id = f"auth0|user_{hashlib.sha256(os.urandom(16)).hexdigest()[:32]}" + device_id = generate_uuid() + mac_machine_id = hashlib.sha256(os.urandom(32)).hexdigest() + timestamp = datetime.datetime.now().strftime('%s') + datetime_s = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + inject_universal_code = f""" +// Cursor ID 修改工具注入 - {datetime_s} +// 全局拦截设备标识符 - {timestamp} +const originalRequire_{timestamp} = require; +require = function(module) {{ + const result = originalRequire_{timestamp}(module); + if (module === 'crypto' && result.randomUUID) {{ + const originalRandomUUID_{timestamp} = result.randomUUID; + result.randomUUID = function() {{ + return '{new_uuid}'; + }}; + }} + return result; +}}; + +// 覆盖所有可能的系统ID获取函数 +global.getMachineId = function() {{ return '{machine_id}'; }}; +global.getDeviceId = function() {{ return '{device_id}'; }}; +global.macMachineId = '{mac_machine_id}'; +""" + # NOTE: Double {{ or }} is literal, so matches: + old_bash_inject_code = """ +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) +// 全局拦截设备标识符 - $(date +%s) +const originalRequire_$(date +%s) = require; +require = function(module) { + const result = originalRequire_$(date +%s)(module); + if (module === 'crypto' && result.randomUUID) { + const originalRandomUUID_$(date +%s) = result.randomUUID; + result.randomUUID = function() { + return '$new_uuid'; + }; + } + return result; +}; + +// 覆盖所有可能的系统ID获取函数 +global.getMachineId = function() { return '$machine_id'; }; +global.getDeviceId = function() { return '$device_id'; }; +global.macMachineId = '$mac_machine_id'; +""" + new_content = inject_universal_code + content + with open(file, 'w') as f: + f.write(new_content) + log_debug(_("Completed most universal injection")) + modified_count += 1 + else: + log_info(_("File has already been modified, skipping modification")) + + # 恢复文件权限 + # Restore file permissions + os.chmod(file, 0o444) + + if modified_count == 0: + log_error(_("Failed to modify any JS files")) + return False + + log_info(_("Successfully modified {} JS files").format(modified_count)) + return True + +# 获取网络接口列表 +# Get network interface list +def get_network_interfaces(): + global _ + if NETIFACES_AVAILABLE: + try: + # 使用netifaces库 + # Use netifaces library + interfaces = netifaces.interfaces() + # 过滤掉回环接口 + # Filter out loopback interfaces + return [iface for iface in interfaces if not iface.startswith('lo')] + except Exception: + pass + + # 如果netifaces不可用或失败,使用系统命令 + # If netifaces is not available or failed, use system commands + try: + result = subprocess.run(['ip', 'link', 'show'], capture_output=True, text=True) + interfaces = [] + for line in result.stdout.split('\n'): + if ': ' in line and 'state' in line.lower(): + iface_name = line.split(': ')[1].split('@')[0] + if not iface_name.startswith('lo'): + interfaces.append(iface_name) + return interfaces + except subprocess.CalledProcessError: + # 最后的备选方案 + # Last fallback option + try: + result = subprocess.run(['ls', '/sys/class/net/'], capture_output=True, text=True) + interfaces = result.stdout.strip().split('\n') + return [iface for iface in interfaces if not iface.startswith('lo')] + except subprocess.CalledProcessError: + log_error(_("Unable to get network interface list")) + return [] + +# 生成随机MAC地址 +# Generate random MAC address +def generate_random_mac(): + global _ + # 生成随机MAC地址,确保第一个字节的最低位为0(单播地址) + # Generate random MAC address, ensure the lowest bit of first byte is 0 (unicast address) + mac = [0x00, 0x16, 0x3e, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff)] + return ':'.join(map(lambda x: "%02x" % x, mac)) + +# 获取当前MAC地址 +# Get current MAC address +def get_current_mac(interface): + global _ + if NETIFACES_AVAILABLE: + try: + # 使用netifaces库 + # Use netifaces library + addrs = netifaces.ifaddresses(interface) + if netifaces.AF_LINK in addrs: + return addrs[netifaces.AF_LINK][0]['addr'] + except Exception: + pass + + # 使用系统命令获取MAC地址 + # Use system command to get MAC address + try: + result = subprocess.run(['cat', f'/sys/class/net/{interface}/address'], + capture_output=True, text=True) + if result.returncode == 0: + return result.stdout.strip() + except subprocess.CalledProcessError: + pass + + # 备选方案:使用ip命令 + # Fallback: use ip command + try: + result = subprocess.run(['ip', 'link', 'show', interface], + capture_output=True, text=True) + for line in result.stdout.split('\n'): + if 'link/ether' in line: + return line.split()[1] + except subprocess.CalledProcessError: + pass + + return None + +# 修改MAC地址 +# Modify MAC address +def modify_mac_address(): + global _ + log_info(_("Starting MAC address modification...")) + + # 获取网络接口列表 + # Get network interface list + interfaces = get_network_interfaces() + if not interfaces: + log_error(_("No network interfaces found")) + return False + + log_info(_("Found network interfaces: {}").format(', '.join(interfaces))) + + # 选择要修改的接口(通常选择第一个非回环接口) + # Select interface to modify (usually the first non-loopback interface) + target_interface = None + for iface in interfaces: + # 优先选择以太网接口 + # Prefer ethernet interfaces + if any(prefix in iface.lower() for prefix in ['eth', 'enp', 'ens', 'enx']): + target_interface = iface + break + + # 如果没有找到以太网接口,选择第一个可用接口 + # If no ethernet interface found, select first available interface + if not target_interface and interfaces: + target_interface = interfaces[0] + + if not target_interface: + log_error(_("No suitable network interface found")) + return False + + log_info(_("Selected network interface: {}").format(target_interface)) + + # 获取当前MAC地址 + # Get current MAC address + current_mac = get_current_mac(target_interface) + if current_mac: + log_info(_("Current MAC address: {}").format(current_mac)) + + # 备份当前MAC地址 + # Backup current MAC address + backup_file = os.path.join(BACKUP_DIR, f"original_mac_{target_interface}.txt") + try: + os.makedirs(BACKUP_DIR, exist_ok=True) + with open(backup_file, 'w') as f: + f.write(f"{target_interface}:{current_mac}\n") + log_info(_("Original MAC address backed up to: {}").format(backup_file)) + except OSError: + log_warn(_("Failed to backup original MAC address")) + else: + log_warn(_("Unable to get current MAC address")) + + # 生成新的MAC地址 + # Generate new MAC address + new_mac = generate_random_mac() + log_info(_("Generated new MAC address: {}").format(new_mac)) + + # 修改MAC地址 + # Modify MAC address + success = False + + # 方法1:使用ip命令 + # Method 1: Use ip command + try: + log_debug(_("Attempting to modify MAC address using ip command...")) + + # 先关闭接口 + # First bring down the interface + subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'down'], + check=True, capture_output=True) + + # 修改MAC地址 + # Modify MAC address + subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'address', new_mac], + check=True, capture_output=True) + + # 重新启用接口 + # Bring up the interface + subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'up'], + check=True, capture_output=True) + + success = True + log_info(_("Successfully modified MAC address using ip command")) + + except subprocess.CalledProcessError as e: + log_warn(_("Failed to modify MAC address using ip command: {}").format(str(e))) + + # 方法2:使用ifconfig命令(备选方案) + # Method 2: Use ifconfig command (fallback) + if not success: + try: + log_debug(_("Attempting to modify MAC address using ifconfig command...")) + + # 先关闭接口 + # First bring down the interface + subprocess.run(['ifconfig', target_interface, 'down'], + check=True, capture_output=True) + + # 修改MAC地址 + # Modify MAC address + subprocess.run(['ifconfig', target_interface, 'hw', 'ether', new_mac], + check=True, capture_output=True) + + # 重新启用接口 + # Bring up the interface + subprocess.run(['ifconfig', target_interface, 'up'], + check=True, capture_output=True) + + success = True + log_info(_("Successfully modified MAC address using ifconfig command")) + + except subprocess.CalledProcessError as e: + log_warn(_("Failed to modify MAC address using ifconfig command: {}").format(str(e))) + + # 验证MAC地址修改 + # Verify MAC address modification + if success: + time.sleep(2) # 等待网络接口稳定 + # Wait for network interface to stabilize + + new_current_mac = get_current_mac(target_interface) + if new_current_mac and new_current_mac.lower() == new_mac.lower(): + log_info(_("MAC address modification verified successfully")) + log_info(_("New MAC address: {}").format(new_current_mac)) + + # 保存新MAC地址信息 + # Save new MAC address information + new_mac_file = os.path.join(BACKUP_DIR, f"new_mac_{target_interface}.txt") + try: + with open(new_mac_file, 'w') as f: + f.write(f"{target_interface}:{new_current_mac}\n") + f.write(f"Modified at: {datetime.datetime.now()}\n") + log_info(_("New MAC address information saved to: {}").format(new_mac_file)) + except OSError: + log_warn(_("Failed to save new MAC address information")) + + return True + else: + log_error(_("MAC address modification verification failed")) + log_error(_("Expected: {}, Actual: {}").format(new_mac, new_current_mac)) + return False + else: + log_error(_("All MAC address modification methods failed")) + log_error(_("Please check if you have sufficient permissions or try running with sudo")) + return False + +# 禁用自动更新 +# Disable auto-update +def disable_auto_update(): + global _ + log_info(_("Disabling Cursor auto-update...")) + + # 查找可能的更新配置文件 + # Find possible update configuration files + update_configs = [ + os.path.join(CURSOR_CONFIG_DIR, "update-config.json"), + os.path.expanduser("~/.local/share/cursor/update-config.json"), + "/opt/cursor/resources/app-update.yml", + ] + + disabled = False + + for config in update_configs: + if os.path.isfile(config): + log_info(_("Found update configuration file: {}").format(config)) + try: + shutil.copy(config, f"{config}.bak") + with open(config, 'w') as f: + json.dump({"autoCheck": False, "autoDownload": False}, f) + os.chmod(config, 0o444) + log_info(_("Disabled update configuration file: {}").format(config)) + disabled = True + except (OSError, shutil.Error): + continue + + # 尝试查找updater可执行文件并禁用 + # Try to find and disable updater executable + updater_paths = [ + os.path.join(CURSOR_CONFIG_DIR, "updater"), + "/opt/cursor/updater", + "/usr/lib/cursor/updater", + ] + + for updater in updater_paths: + if os.path.exists(updater): + log_info(_("Found updater: {}").format(updater)) + try: + if os.path.isfile(updater): + shutil.move(updater, f"{updater}.bak") + else: + Path(f"{updater}.disabled").touch() + log_info(_("Disabled updater: {}").format(updater)) + disabled = True + except (OSError, shutil.Error): + continue + + if not disabled: + log_warn(_("No update configuration files or updaters found")) + else: + log_info(_("Successfully disabled auto-update")) + +# 新增:通用菜单选择函数 +# New: Universal menu selection function +def select_menu_option(prompt, options, default_index=0): + global _ + # 保存终端设置 + # Save terminal settings + old_settings = termios.tcgetattr(sys.stdin) + selected_index = default_index + + try: + # 设置终端为非缓冲模式 + # Set terminal to non-buffered mode + tty.setcbreak(sys.stdin.fileno()) + + # 显示提示信息 + # Display prompt + print(_(prompt)) + + # 第一次显示菜单 + # Display menu initially + for i, option in enumerate(options): + if i == selected_index: + print(f" {GREEN}►{NC} {_(option)}") + else: + print(f" {_(option)}") + + while True: + # 读取键盘输入 + # Read keyboard input + rlist, _1, _2 = select.select([sys.stdin], [], [], 0.1) + if rlist: + key = sys.stdin.read(1) + # 上箭头键 + # Up arrow key + if key == '\033': + next_char = sys.stdin.read(2) + if next_char == '[A' and selected_index > 0: + selected_index -= 1 + # 下箭头键 + # Down arrow key + elif next_char == '[B' and selected_index < len(options) - 1: + selected_index += 1 + # Enter键 + # Enter key + elif key == '\n': + print() + log_info(_("You selected: {}").format(options[selected_index])) + return selected_index + + # 清除当前菜单 + # Clear current menu + sys.stdout.write('\033[{}A\033[J'.format(len(options) + 1)) + sys.stdout.flush() + + # 重新显示提示和菜单 + # Redisplay prompt and menu + print(_(prompt)) + for i, option in enumerate(options): + if i == selected_index: + print(f" {GREEN}►{NC} {_(option)}") + else: + print(f" {_(option)}") + sys.stdout.flush() + + finally: + # 恢复终端设置 + # Restore terminal settings + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) + +# 主函数 +# Main function +def main(): + global _ + # 检查系统环境 + # Check system environment + if sys.platform != "linux": + log_error(_("This script only supports Linux systems")) + sys.exit(1) + + # 初始化日志文件 + # Initialize log file + initialize_log() + log_info(_("Script started...")) + + # 记录系统信息 + # Log system information + log_info(_("System information: {}").format(subprocess.getoutput("uname -a"))) + log_info(_("Current user: {}").format(CURRENT_USER)) + log_cmd_output( + "lsb_release -a 2>/dev/null || cat /etc/*release 2>/dev/null || cat /etc/issue", + _("System version information") + ) + + # 清除终端 + # Clear terminal + os.system('clear') + + # 显示 Logo + # Display Logo + print(""" + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ + ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ + ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ + ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ + """) + print(f"{BLUE}================================{NC}") + print(f"{GREEN} {_('Cursor Linux startup tool')} {NC}") + print(f"{BLUE}================================{NC}") + print() + print(f"{YELLOW}[{_('Important notice')}] {NC} {_('This tool prioritizes modifying JS files, which is safer and more reliable')}") + print() + + # 执行主要功能 + # Execute main functions + check_permissions() + find_cursor_path() + find_cursor_resources() + check_and_kill_cursor() + backup_config() + generate_new_config() + + # 修改JS文件 + # Modify JS files + log_info(_("Modifying Cursor JS files...")) + js_success = modify_cursor_js_files() + if js_success: + log_info(_("JS files modified successfully!")) + else: + log_warn(_("JS file modification failed, but configuration file modification may have succeeded")) + log_warn(_("If Cursor still indicates the device is disabled after restarting, please rerun this script")) + + # 修改MAC地址 + # Modify MAC address + log_info(_("Modifying system MAC address...")) + mac_success = modify_mac_address() + if mac_success: + log_info(_("MAC address modified successfully!")) + else: + log_warn(_("MAC address modification failed")) + log_warn(_("This may affect the effectiveness of device identification bypass")) + + # 禁用自动更新 + # Disable auto-update + disable_auto_update() + + # 显示修改结果总结 + # Display modification result summary + print() + print(f"{GREEN}================================{NC}") + print(f"{BLUE} {_('Modification Results Summary')} {NC}") + print(f"{GREEN}================================{NC}") + + if js_success: + print(f"{GREEN}✓{NC} {_('JS files modification: SUCCESS')}") + else: + print(f"{RED}✗{NC} {_('JS files modification: FAILED')}") + + if mac_success: + print(f"{GREEN}✓{NC} {_('MAC address modification: SUCCESS')}") + else: + print(f"{RED}✗{NC} {_('MAC address modification: FAILED')}") + + print(f"{GREEN}================================{NC}") + print() + + log_info(_("Please restart Cursor to apply the new configuration")) + + # 显示最后的提示信息 + # Display final prompt + print() + print(f"{GREEN}================================{NC}") + print(f"{YELLOW} {_('Follow the WeChat public account [Pancake AI] to discuss more Cursor tips and AI knowledge (script is free, join the group via the public account for more tips and experts)')} {NC}") + print("WeChat account: [煎饼果子卷AI]") + print(f"{GREEN}================================{NC}") + print() + + # 记录脚本完成信息 + # Log script completion information + log_info(_("Script execution completed")) + with open(LOG_FILE, 'a') as f: + f.write(_("========== Cursor ID modification tool log end {} ==========").format(datetime.datetime.now()) + "\n") + + # 显示日志文件位置 + # Display log file location + print() + log_info(_("Detailed log saved to: {}").format(LOG_FILE)) + print(_("If you encounter issues, please provide this log file to the developer for troubleshooting")) + print() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/git-actions.sh b/scripts/git-actions.sh new file mode 100755 index 00000000..632e3120 --- /dev/null +++ b/scripts/git-actions.sh @@ -0,0 +1,9 @@ +#!/bin/bash +REPO_DIR="$PWD" +LOCALES_DIR="$REPO_DIR/locales" +msginit -i cursor_id_modifier.pot -o $LOCALES_DIR/en_US/LC_MESSAGES/cursor_id_modifier.po -l en_US +for lang in en_US zh_CN; do + cd $LOCALES_DIR/$lang/LC_MESSAGES + msgfmt -o cursor_id_modifier.mo cursor_id_modifier.po +done + diff --git a/scripts/run/cursor_linux_id_modifier.sh b/scripts/run/cursor_linux_id_modifier.sh old mode 100644 new mode 100755 index 399885d6..2ba6938f --- a/scripts/run/cursor_linux_id_modifier.sh +++ b/scripts/run/cursor_linux_id_modifier.sh @@ -3,6 +3,15 @@ # 设置错误处理 set -e +# 定义日志文件路径 +LOG_FILE="/tmp/cursor_linux_id_modifier.log" + +# 初始化日志文件 +initialize_log() { + echo "========== Cursor ID 修改工具日志开始 $(date) ==========" > "$LOG_FILE" + chmod 644 "$LOG_FILE" +} + # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' @@ -10,21 +19,35 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color -# 日志函数 +# 日志函数 - 同时输出到终端和日志文件 log_info() { echo -e "${GREEN}[INFO]${NC} $1" + echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" + echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_error() { echo -e "${RED}[ERROR]${NC} $1" + echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_debug() { echo -e "${BLUE}[DEBUG]${NC} $1" + echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +# 记录命令输出到日志文件 +log_cmd_output() { + local cmd="$1" + local msg="$2" + echo "[CMD] $(date '+%Y-%m-%d %H:%M:%S') 执行命令: $cmd" >> "$LOG_FILE" + echo "[CMD] $msg:" >> "$LOG_FILE" + eval "$cmd" 2>&1 | tee -a "$LOG_FILE" + echo "" >> "$LOG_FILE" } # 获取当前用户 @@ -42,19 +65,276 @@ if [ -z "$CURRENT_USER" ]; then exit 1 fi -# 定义配置文件路径 (修改为 Linux 路径) -STORAGE_FILE="/home/$CURRENT_USER/.config/Cursor/User/globalStorage/storage.json" -BACKUP_DIR="/home/$CURRENT_USER/.config/Cursor/User/globalStorage/backups" +# 定义Linux下的Cursor路径 +CURSOR_CONFIG_DIR="$HOME/.config/Cursor" +STORAGE_FILE="$CURSOR_CONFIG_DIR/User/globalStorage/storage.json" +BACKUP_DIR="$CURSOR_CONFIG_DIR/User/globalStorage/backups" + +# --- 新增:安装相关变量 --- +APPIMAGE_SEARCH_DIR="/opt/CursorInstall" # AppImage 搜索目录,可按需修改 +APPIMAGE_PATTERN="Cursor-*.AppImage" # AppImage 文件名模式 +INSTALL_DIR="/opt/Cursor" # Cursor 最终安装目录 +ICON_PATH="/usr/share/icons/cursor.png" +DESKTOP_FILE="/usr/share/applications/cursor-cursor.desktop" +# --- 结束:安装相关变量 --- + +# 可能的Cursor二进制路径 - 添加了标准安装路径 +CURSOR_BIN_PATHS=( + "/usr/bin/cursor" + "/usr/local/bin/cursor" + "$INSTALL_DIR/cursor" # 添加标准安装路径 + "$HOME/.local/bin/cursor" + "/snap/bin/cursor" +) + +# 找到Cursor安装路径 +find_cursor_path() { + log_info "查找Cursor安装路径..." + + for path in "${CURSOR_BIN_PATHS[@]}"; do + if [ -f "$path" ] && [ -x "$path" ]; then # 确保文件存在且可执行 + log_info "找到Cursor安装路径: $path" + CURSOR_PATH="$path" + return 0 + fi + done + + # 尝试通过which命令定位 + if command -v cursor &> /dev/null; then + CURSOR_PATH=$(which cursor) + log_info "通过which找到Cursor: $CURSOR_PATH" + return 0 + fi + + # 尝试查找可能的安装路径 (限制搜索范围和类型) + local cursor_paths=$(find /usr /opt $HOME/.local -path "$INSTALL_DIR/cursor" -o -name "cursor" -type f -executable 2>/dev/null) + if [ -n "$cursor_paths" ]; then + # 优先选择标准安装路径 + local standard_path=$(echo "$cursor_paths" | grep "$INSTALL_DIR/cursor" | head -1) + if [ -n "$standard_path" ]; then + CURSOR_PATH="$standard_path" + else + CURSOR_PATH=$(echo "$cursor_paths" | head -1) + fi + log_info "通过查找找到Cursor: $CURSOR_PATH" + return 0 + fi + + log_warn "未找到Cursor可执行文件" + return 1 +} + +# 查找并定位Cursor资源文件目录 +find_cursor_resources() { + log_info "查找Cursor资源目录..." + + # 可能的资源目录路径 - 添加了标准安装目录 + local resource_paths=( + "$INSTALL_DIR" # 添加标准安装路径 + "/usr/lib/cursor" + "/usr/share/cursor" + "$HOME/.local/share/cursor" + ) + + for path in "${resource_paths[@]}"; do + if [ -d "$path/resources" ]; then # 检查是否存在 resources 子目录 + log_info "找到Cursor资源目录: $path" + CURSOR_RESOURCES="$path" + return 0 + fi + if [ -d "$path/app" ]; then # 有些版本可能直接是 app 目录 + log_info "找到Cursor资源目录 (app): $path" + CURSOR_RESOURCES="$path" + return 0 + fi + done + + # 如果有CURSOR_PATH,尝试从它推断 + if [ -n "$CURSOR_PATH" ]; then + local base_dir=$(dirname "$CURSOR_PATH") + # 检查常见的相对路径 + if [ -d "$base_dir/resources" ]; then + CURSOR_RESOURCES="$base_dir" + log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" + return 0 + elif [ -d "$base_dir/../resources" ]; then # 例如在 bin 目录内 + CURSOR_RESOURCES=$(realpath "$base_dir/..") + log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" + return 0 + elif [ -d "$base_dir/../lib/cursor/resources" ]; then # 另一种常见结构 + CURSOR_RESOURCES=$(realpath "$base_dir/../lib/cursor") + log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" + return 0 + fi + fi + + log_warn "未找到Cursor资源目录" + return 1 +} # 检查权限 check_permissions() { if [ "$EUID" -ne 0 ]; then - log_error "请使用 sudo 运行此脚本" + log_error "请使用 sudo 运行此脚本 (安装和修改系统文件需要权限)" echo "示例: sudo $0" exit 1 fi } +# --- 新增/重构:从本地 AppImage 安装 Cursor --- +install_cursor_appimage() { + log_info "开始尝试从本地 AppImage 安装 Cursor..." + local found_appimage_path="" + + # 确保搜索目录存在 + mkdir -p "$APPIMAGE_SEARCH_DIR" + + # 查找 AppImage 文件 + find_appimage() { + found_appimage_path=$(find "$APPIMAGE_SEARCH_DIR" -maxdepth 1 -name "$APPIMAGE_PATTERN" -print -quit) + if [ -z "$found_appimage_path" ]; then + return 1 + else + return 0 + fi + } + + if ! find_appimage; then + log_warn "在 '$APPIMAGE_SEARCH_DIR' 目录下未找到 '$APPIMAGE_PATTERN' 文件。" + # --- 新增:添加文件名格式提醒 --- + log_info "请确保 AppImage 文件名格式类似: Cursor-版本号-架构.AppImage (例如: Cursor-1.0.6-aarch64.AppImage 或 Cursor-x.y.z-x86_64.AppImage)" + # --- 结束:添加文件名格式提醒 --- + # 等待用户放置文件 + read -p $"请将 Cursor AppImage 文件放入 '$APPIMAGE_SEARCH_DIR' 目录,然后按 Enter 键继续..." + + # 再次查找 + if ! find_appimage; then + log_error "在 '$APPIMAGE_SEARCH_DIR' 中仍然找不到 '$APPIMAGE_PATTERN' 文件。安装中止。" + return 1 + fi + fi + + log_info "找到 AppImage 文件: $found_appimage_path" + local appimage_filename=$(basename "$found_appimage_path") + + # 进入搜索目录操作,避免路径问题 + local current_dir=$(pwd) + cd "$APPIMAGE_SEARCH_DIR" || { log_error "无法进入目录: $APPIMAGE_SEARCH_DIR"; return 1; } + + log_info "设置 '$appimage_filename' 可执行权限..." + chmod +x "$appimage_filename" || { + log_error "设置可执行权限失败: $appimage_filename" + cd "$current_dir" + return 1 + } + + log_info "解压 AppImage 文件 '$appimage_filename'..." + # 创建临时解压目录 + local extract_dir="squashfs-root" + rm -rf "$extract_dir" # 清理旧的解压目录(如果存在) + + # 执行解压,将输出重定向避免干扰 + if ./"$appimage_filename" --appimage-extract > /dev/null; then + log_info "AppImage 解压成功到 '$extract_dir'" + else + log_error "解压 AppImage 失败: $appimage_filename" + rm -rf "$extract_dir" # 清理失败的解压 + cd "$current_dir" + return 1 + fi + + # 检查解压后的预期目录结构 + local cursor_source_dir="" + if [ -d "$extract_dir/usr/share/cursor" ]; then + cursor_source_dir="$extract_dir/usr/share/cursor" + elif [ -d "$extract_dir" ]; then # 有些 AppImage 可能直接在根目录 + # 进一步检查是否存在关键文件/目录 + if [ -f "$extract_dir/cursor" ] && [ -d "$extract_dir/resources" ]; then + cursor_source_dir="$extract_dir" + fi + fi + + if [ -z "$cursor_source_dir" ]; then + log_error "解压后的目录 '$extract_dir' 中未找到预期的 Cursor 文件结构 (例如 'usr/share/cursor' 或直接包含 'cursor' 和 'resources')。" + rm -rf "$extract_dir" + cd "$current_dir" + return 1 + fi + log_info "找到 Cursor 源文件在: $cursor_source_dir" + + + log_info "安装 Cursor 到 '$INSTALL_DIR'..." + # 如果安装目录已存在,先删除 (确保全新安装) + if [ -d "$INSTALL_DIR" ]; then + log_warn "发现已存在的安装目录 '$INSTALL_DIR',将先移除..." + rm -rf "$INSTALL_DIR" || { log_error "移除旧安装目录失败: $INSTALL_DIR"; cd "$current_dir"; return 1; } + fi + + # 创建安装目录的父目录(如果需要)并设置权限 + mkdir -p "$(dirname "$INSTALL_DIR")" + + # 移动解压后的内容到安装目录 + if mv "$cursor_source_dir" "$INSTALL_DIR"; then + log_info "成功将文件移动到 '$INSTALL_DIR'" + # 确保安装目录及其内容归属当前用户(如果需要) + chown -R "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$INSTALL_DIR" || log_warn "设置 '$INSTALL_DIR' 文件所有权失败,可能需要手动调整" + chmod -R u+rwX,go+rX,go-w "$INSTALL_DIR" || log_warn "设置 '$INSTALL_DIR' 文件权限失败,可能需要手动调整" + else + log_error "移动文件到安装目录 '$INSTALL_DIR' 失败" + rm -rf "$extract_dir" # 确保清理 + rm -rf "$INSTALL_DIR" # 清理部分移动的文件 + cd "$current_dir" + return 1 + fi + + # 处理图标和桌面快捷方式 (从脚本执行的原始目录查找) + cd "$current_dir" # 返回原始目录查找图标等文件 + + local icon_source="./cursor.png" + local desktop_source="./cursor-cursor.desktop" + + if [ -f "$icon_source" ]; then + log_info "安装图标..." + mkdir -p "$(dirname "$ICON_PATH")" + cp "$icon_source" "$ICON_PATH" || log_warn "无法复制图标文件 '$icon_source' 到 '$ICON_PATH'" + chmod 644 "$ICON_PATH" || log_warn "设置图标文件权限失败: $ICON_PATH" + else + log_warn "图标文件 '$icon_source' 在脚本当前目录不存在,跳过图标安装。" + log_warn "请将 'cursor.png' 文件放置在脚本目录 '$current_dir' 下并重新运行安装(如果需要图标)。" + fi + + if [ -f "$desktop_source" ]; then + log_info "安装桌面快捷方式..." + mkdir -p "$(dirname "$DESKTOP_FILE")" + cp "$desktop_source" "$DESKTOP_FILE" || log_warn "无法创建桌面快捷方式 '$desktop_source' 到 '$DESKTOP_FILE'" + chmod 644 "$DESKTOP_FILE" || log_warn "设置桌面文件权限失败: $DESKTOP_FILE" + + # 更新桌面数据库 + log_info "更新桌面数据库..." + update-desktop-database "$(dirname "$DESKTOP_FILE")" &> /dev/null || log_warn "无法更新桌面数据库,快捷方式可能不会立即显示" + else + log_warn "桌面文件 '$desktop_source' 在脚本当前目录不存在,跳过快捷方式安装。" + log_warn "请将 'cursor-cursor.desktop' 文件放置在脚本目录 '$current_dir' 下并重新运行安装(如果需要快捷方式)。" + fi + + # 创建符号链接到 /usr/local/bin + log_info "创建命令行启动链接..." + ln -sf "$INSTALL_DIR/cursor" /usr/local/bin/cursor || log_warn "无法创建命令行链接 '/usr/local/bin/cursor'" + + # 清理临时文件 + log_info "清理临时文件..." + cd "$APPIMAGE_SEARCH_DIR" # 返回搜索目录清理 + rm -rf "$extract_dir" + log_info "正在删除原始 AppImage 文件: $found_appimage_path" + rm -f "$appimage_filename" # 删除 AppImage 文件 + + cd "$current_dir" # 确保返回最终目录 + + log_info "Cursor 安装成功!安装目录: $INSTALL_DIR" + return 0 +} +# --- 结束:安装函数 --- + # 检查并关闭 Cursor 进程 check_and_kill_cursor() { log_info "检查 Cursor 进程..." @@ -66,12 +346,12 @@ check_and_kill_cursor() { get_process_details() { local process_name="$1" log_debug "正在获取 $process_name 进程详细信息:" - ps aux | grep -E "/[C]ursor|[C]ursor$" || true + ps aux | grep -i "cursor" | grep -v grep | grep -v "cursor_linux_id_modifier.sh" } while [ $attempt -le $max_attempts ]; do - # 使用更精确的方式查找 Cursor 进程 - CURSOR_PIDS=$(ps aux | grep -E "/[C]ursor|[C]ursor$" | awk '{print $2}' || true) + # 使用更精确的匹配来获取 Cursor 进程,排除当前脚本和grep进程 + CURSOR_PIDS=$(ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_linux_id_modifier.sh" | awk '{print $2}' || true) if [ -z "$CURSOR_PIDS" ]; then log_info "未发现运行中的 Cursor 进程" @@ -79,49 +359,39 @@ check_and_kill_cursor() { fi log_warn "发现 Cursor 进程正在运行" - get_process_details "Cursor" + get_process_details "cursor" log_warn "尝试关闭 Cursor 进程..." - # 遍历每个 PID 并尝试终止 - for pid in $CURSOR_PIDS; do - if [ $attempt -eq $max_attempts ]; then - log_warn "尝试强制终止进程 PID: ${pid}..." - kill -9 "${pid}" 2>/dev/null || true - else - kill "${pid}" 2>/dev/null || true - fi - done + if [ $attempt -eq $max_attempts ]; then + log_warn "尝试强制终止进程..." + kill -9 $CURSOR_PIDS 2>/dev/null || true + else + kill $CURSOR_PIDS 2>/dev/null || true + fi - sleep 2 + sleep 1 - # 检查是否还有 Cursor 进程在运行 - if ! ps aux | grep -E "/[C]ursor|[C]ursor$" > /dev/null; then + # 再次检查进程是否还在运行,排除当前脚本和grep进程 + if ! ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_linux_id_modifier.sh" > /dev/null; then log_info "Cursor 进程已成功关闭" return 0 fi log_warn "等待进程关闭,尝试 $attempt/$max_attempts..." ((attempt++)) - sleep 1 done log_error "在 $max_attempts 次尝试后仍无法关闭 Cursor 进程" - get_process_details "Cursor" + get_process_details "cursor" log_error "请手动关闭进程后重试" exit 1 } # 备份配置文件 backup_config() { - # 检查文件权限 - if [ -f "$STORAGE_FILE" ] && [ ! -w "$STORAGE_FILE" ]; then - log_error "无法写入配置文件,请检查权限" - exit 1 - fi - if [ ! -f "$STORAGE_FILE" ]; then - log_warn "配置文件不存在,跳过备份" + log_warn "配置文件 '$STORAGE_FILE' 不存在,跳过备份" return 0 fi @@ -130,214 +400,767 @@ backup_config() { if cp "$STORAGE_FILE" "$backup_file"; then chmod 644 "$backup_file" - chown "$CURRENT_USER:$CURRENT_USER" "$backup_file" + # 确保备份文件归属正确用户 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$backup_file" || log_warn "设置备份文件所有权失败: $backup_file" log_info "配置已备份到: $backup_file" else - log_error "备份失败" + log_error "备份失败: $STORAGE_FILE" exit 1 fi + return 0 # 明确返回成功 } # 生成随机 ID generate_random_id() { - # Linux 可以使用 /dev/urandom - head -c 32 /dev/urandom | xxd -p + # 生成32字节(64个十六进制字符)的随机数 + openssl rand -hex 32 } # 生成随机 UUID generate_uuid() { - # Linux 使用 uuidgen 命令 - uuidgen | tr '[:upper:]' '[:lower:]' + # 在Linux上使用uuidgen生成UUID + if command -v uuidgen &> /dev/null; then + uuidgen | tr '[:upper:]' '[:lower:]' + else + # 备选方案:使用/proc/sys/kernel/random/uuid + if [ -f /proc/sys/kernel/random/uuid ]; then + cat /proc/sys/kernel/random/uuid + else + # 最后备选方案:使用openssl生成 + openssl rand -hex 16 | sed 's/\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)/\\1\\2\\3\\4-\\5\\6-\\7\\8-\\9\\10-\\11\\12\\13\\14\\15\\16/' + fi + fi } -# 生成新的配置 -generate_new_config() { - # 错误处理 - if ! command -v xxd &> /dev/null; then - log_error "未找到 xxd 命令,请安装 xxd" - exit 1 - fi +# 修改现有文件 +modify_or_add_config() { + local key="$1" + local value="$2" + local file="$3" - if ! command -v uuidgen &> /dev/null; then - log_error "未找到 uuidgen 命令,请安装 uuidgen" - exit 1 + if [ ! -f "$file" ]; then + log_error "配置文件不存在: $file" + return 1 fi - # 检查配置文件是否存在 - if [ ! -f "$STORAGE_FILE" ]; then - log_error "未找到配置文件: $STORAGE_FILE" - log_warn "请先安装并运行一次 Cursor 后再使用此脚本" - exit 1 - fi + # 确保文件对当前执行用户(root)可写 + chmod u+w "$file" || { + log_error "无法修改文件权限(写): $file" + return 1 + } - # 将 auth0|user_ 转换为字节数组的十六进制 - local prefix_hex=$(echo -n "auth0|user_" | xxd -p) - local random_part=$(generate_random_id) - local machine_id="${prefix_hex}${random_part}" + # 创建临时文件 + local temp_file=$(mktemp) - local mac_machine_id=$(generate_random_id) - local device_id=$(generate_uuid | tr '[:upper:]' '[:lower:]') - local sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}" + # 检查key是否存在 + if grep -q "\"$key\":[[:space:]]*\"[^\"]*\"" "$file"; then + # key存在,执行替换 (更精确的匹配) + sed "s/\\(\"$key\"\\):[[:space:]]*\"[^\"]*\"/\\1: \"$value\"/" "$file" > "$temp_file" || { + log_error "修改配置失败 (替换): $key in $file" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 + } + log_debug "已替换 key '$key' 在文件 '$file' 中" + elif grep -q "}" "$file"; then + # key不存在, 在最后一个 '}' 前添加新的key-value对 + # 注意:这种方式比较脆弱,如果 JSON 格式不标准或最后一行不是 '}' 会失败 + sed '$ s/}/,\n "'$key'\": "'$value'\"\n}/' "$file" > "$temp_file" || { + log_error "添加配置失败 (注入): $key to $file" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 + } + log_debug "已添加 key '$key' 到文件 '$file' 中" + else + log_error "无法确定如何添加配置: $key to $file (文件结构可能不标准)" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 + fi + + # 检查临时文件是否有效 + if [ ! -s "$temp_file" ]; then + log_error "修改或添加配置后生成的临时文件为空: $key in $file" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 + fi - # 增强的转义函数 - escape_sed_replacement() { - echo "$1" | sed -e 's/[\/&|]/\\&/g' # 转义 / & | 符号 + # 使用 cat 替换原文件内容 + cat "$temp_file" > "$file" || { + log_error "无法写入更新后的配置到文件: $file" + rm -f "$temp_file" + # 尝试恢复权限(如果失败也无大碍) + chmod u-w "$file" || true + return 1 } + + rm -f "$temp_file" + + # 设置所有者和基础权限(root执行时目标文件是用户家目录下的) + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "设置文件所有权失败: $file" + chmod 644 "$file" || log_warn "设置文件权限失败: $file" # 用户读写,组和其他读 + + return 0 +} + +# 生成新的配置 +generate_new_config() { + echo + log_warn "机器码重置选项" + + # 使用菜单选择函数询问用户是否重置机器码 + select_menu_option "是否需要重置机器码? (通常情况下,只修改js文件即可):" "不重置 - 仅修改js文件即可|重置 - 同时修改配置文件和机器码" 0 + reset_choice=$? + + # 记录日志以便调试 + echo "[INPUT_DEBUG] 机器码重置选项选择: $reset_choice" >> "$LOG_FILE" + + # 确保配置文件目录存在 + mkdir -p "$(dirname "$STORAGE_FILE")" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$(dirname "$STORAGE_FILE")" || log_warn "设置配置目录所有权失败: $(dirname "$STORAGE_FILE")" + chmod 755 "$(dirname "$STORAGE_FILE")" || log_warn "设置配置目录权限失败: $(dirname "$STORAGE_FILE")" - # 对变量进行转义处理 - machine_id_escaped=$(escape_sed_replacement "$machine_id") - mac_machine_id_escaped=$(escape_sed_replacement "$mac_machine_id") - device_id_escaped=$(escape_sed_replacement "$device_id") - sqm_id_escaped=$(escape_sed_replacement "$sqm_id") - - # 使用增强正则表达式和转义 - sed -i "s|\"telemetry\.machineId\": *\"[^\"]*\"|\"telemetry.machineId\": \"${machine_id_escaped}\"|" "$STORAGE_FILE" - sed -i "s|\"telemetry\.macMachineId\": *\"[^\"]*\"|\"telemetry.macMachineId\": \"${mac_machine_id_escaped}\"|" "$STORAGE_FILE" - sed -i "s|\"telemetry\.devDeviceId\": *\"[^\"]*\"|\"telemetry.devDeviceId\": \"${device_id_escaped}\"|" "$STORAGE_FILE" - sed -i "s|\"telemetry\.sqmId\": *\"[^\"]*\"|\"telemetry.sqmId\": \"${sqm_id_escaped}\"|" "$STORAGE_FILE" - - # 设置文件权限和所有者 - chmod 444 "$STORAGE_FILE" # 改为只读权限 - chown "$CURRENT_USER:$CURRENT_USER" "$STORAGE_FILE" - - # 验证权限设置 - if [ -w "$STORAGE_FILE" ]; then - log_warn "无法设置只读权限,尝试使用其他方法..." - # 在 Linux 上使用 chattr 命令设置不可修改属性 - if command -v chattr &> /dev/null; then - chattr +i "$STORAGE_FILE" 2>/dev/null || log_warn "chattr 设置失败" + # 处理用户选择 - 索引0对应"不重置"选项,索引1对应"重置"选项 + if [ "$reset_choice" = "1" ]; then + log_info "您选择了重置机器码" + + # 检查配置文件是否存在 + if [ -f "$STORAGE_FILE" ]; then + log_info "发现已有配置文件: $STORAGE_FILE" + + # 备份现有配置 + if ! backup_config; then # 如果备份失败,不继续修改 + log_error "配置文件备份失败,中止机器码重置。" + return 1 # 返回错误状态 + fi + + # 生成并设置新的设备ID + local new_device_id=$(generate_uuid) + local new_machine_id=$(generate_uuid) # 使用 UUID 作为 Machine ID 更常见 + + log_info "正在设置新的设备和机器ID..." + log_debug "新设备ID: $new_device_id" + log_debug "新机器ID: $new_machine_id" + + # 修改配置文件 + if modify_or_add_config "deviceId" "$new_device_id" "$STORAGE_FILE" && \ + modify_or_add_config "machineId" "$new_machine_id" "$STORAGE_FILE"; then + log_info "配置文件中的 deviceId 和 machineId 修改成功" + else + log_error "配置文件中的 deviceId 或 machineId 修改失败" + # 注意:即使失败,备份仍在,但配置文件可能已部分修改 + return 1 # 返回错误状态 + fi + else + log_warn "未找到配置文件 '$STORAGE_FILE',无法重置机器码。如果这是首次安装,这是正常的。" + # 即使文件不存在,也认为此步骤(不执行)是"成功"的,允许继续 fi else - log_info "成功设置文件只读权限" + log_info "您选择了不重置机器码,将仅修改js文件" + + # 检查配置文件是否存在并备份(如果存在) + if [ -f "$STORAGE_FILE" ]; then + log_info "发现已有配置文件: $STORAGE_FILE" + if ! backup_config; then + log_error "配置文件备份失败,中止操作。" + return 1 # 返回错误状态 + fi + else + log_warn "未找到配置文件 '$STORAGE_FILE',跳过备份。" + fi fi echo - log_info "已更新配置:" - log_debug "machineId: $machine_id" - log_debug "macMachineId: $mac_machine_id" - log_debug "devDeviceId: $device_id" - log_debug "sqmId: $sqm_id" + log_info "配置处理完成" + return 0 # 明确返回成功 } -# 显示文件树结构 -show_file_tree() { - local base_dir=$(dirname "$STORAGE_FILE") - echo - log_info "文件结构:" - echo -e "${BLUE}$base_dir${NC}" - echo "├── globalStorage" - echo "│ ├── storage.json (已修改)" - echo "│ └── backups" - - # 列出备份文件 - if [ -d "$BACKUP_DIR" ]; then - local backup_files=("$BACKUP_DIR"/*) - if [ ${#backup_files[@]} -gt 0 ] && [ -e "${backup_files[0]}" ]; then - for file in "${backup_files[@]}"; do - if [ -f "$file" ]; then - echo "│ └── $(basename "$file")" +# 查找Cursor的JS文件 +find_cursor_js_files() { + log_info "查找Cursor的JS文件..." + + local js_files=() + local found=false + + # 确保 CURSOR_RESOURCES 已设置 + if [ -z "$CURSOR_RESOURCES" ] || [ ! -d "$CURSOR_RESOURCES" ]; then + log_error "Cursor 资源目录未找到或无效 ($CURSOR_RESOURCES),无法查找 JS 文件。" + return 1 + fi + + log_debug "在资源目录中搜索JS文件: $CURSOR_RESOURCES" + + # 在资源目录中递归搜索特定JS文件 + # 注意:这些模式可能需要根据 Cursor 版本更新 + local js_patterns=( + "resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "resources/app/out/main.js" + "resources/app/out/vs/code/node/cliProcessMain.js" + # 添加其他可能的路径模式 + "app/out/vs/workbench/api/node/extensionHostProcess.js" # 如果资源目录是 app 的父目录 + "app/out/main.js" + "app/out/vs/code/node/cliProcessMain.js" + ) + + for pattern in "${js_patterns[@]}"; do + # 使用 find 在 CURSOR_RESOURCES 下查找完整路径 + local files=$(find "$CURSOR_RESOURCES" -path "*/$pattern" -type f 2>/dev/null) + if [ -n "$files" ]; then + while IFS= read -r file; do + # 检查文件是否已添加 + if [[ ! " ${js_files[@]} " =~ " ${file} " ]]; then + log_info "找到JS文件: $file" + js_files+=("$file") + found=true fi - done + done <<< "$files" + fi + done + + # 如果还没找到,尝试更通用的搜索(可能误报) + if [ "$found" = false ]; then + log_warn "在标准路径模式中未找到JS文件,尝试在资源目录 '$CURSOR_RESOURCES' 中进行更广泛的搜索..." + # 查找包含特定关键字的 JS 文件 + local files=$(find "$CURSOR_RESOURCES" -name "*.js" -type f -exec grep -lE 'IOPlatformUUID|x-cursor-checksum|getMachineId' {} \; 2>/dev/null) + if [ -n "$files" ]; then + while IFS= read -r file; do + if [[ ! " ${js_files[@]} " =~ " ${file} " ]]; then + log_info "通过关键字找到可能的JS文件: $file" + js_files+=("$file") + found=true + fi + done <<< "$files" else - echo "│ └── (空)" + log_warn "在资源目录 '$CURSOR_RESOURCES' 中通过关键字也未能找到 JS 文件。" fi fi - echo -} -# 显示公众号信息 -show_follow_info() { - echo - echo -e "${GREEN}================================${NC}" - echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识 ${NC}" - echo -e "${GREEN}================================${NC}" - echo + if [ "$found" = false ]; then + log_error "在资源目录 '$CURSOR_RESOURCES' 中未找到任何可修改的JS文件。" + log_error "请检查 Cursor 安装是否完整,或脚本中的 JS 路径模式是否需要更新。" + return 1 + fi + + # 去重(理论上上面的检查已经处理,但以防万一) + IFS=" " read -r -a CURSOR_JS_FILES <<< "$(echo "${js_files[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')" + + log_info "找到 ${#CURSOR_JS_FILES[@]} 个唯一的JS文件需要处理。" + return 0 } -# 修改 disable_auto_update 函数,在失败处理时添加手动教程 -disable_auto_update() { - echo - log_warn "是否要禁用 Cursor 自动更新功能?" - echo "0) 否 - 保持默认设置 (按回车键)" - echo "1) 是 - 禁用自动更新" - read -r choice - - if [ "$choice" = "1" ]; then - echo - log_info "正在处理自动更新..." - local updater_path="$HOME/.config/cursor-updater" - - # 定义手动设置教程 - show_manual_guide() { - echo - log_warn "自动设置失败,请尝试手动操作:" - echo -e "${YELLOW}手动禁用更新步骤:${NC}" - echo "1. 打开终端" - echo "2. 复制粘贴以下命令:" - echo -e "${BLUE}rm -rf \"$updater_path\" && touch \"$updater_path\" && chmod 444 \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}如果上述命令提示权限不足,请使用 sudo:${NC}" - echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}如果要添加额外保护(推荐),请执行:${NC}" - echo -e "${BLUE}sudo chattr +i \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}验证方法:${NC}" - echo "1. 运行命令:ls -l \"$updater_path\"" - echo "2. 确认文件权限为 r--r--r--" - echo "3. 运行命令:lsattr \"$updater_path\"" - echo "4. 确认有 'i' 属性(如果执行了 chattr 命令)" - echo - log_warn "完成后请重启 Cursor" - } +# 修改Cursor的JS文件 +modify_cursor_js_files() { + log_info "开始修改Cursor的JS文件..." + + # 先查找需要修改的JS文件 + if ! find_cursor_js_files; then + # find_cursor_js_files 内部会打印错误日志 + return 1 + fi + + if [ ${#CURSOR_JS_FILES[@]} -eq 0 ]; then + log_error "JS 文件列表为空,无法继续修改。" + return 1 + fi + + local modified_count=0 + local file_modification_status=() # 记录每个文件的修改状态 + + for file in "${CURSOR_JS_FILES[@]}"; do + log_info "处理文件: $file" - if [ -d "$updater_path" ]; then - rm -rf "$updater_path" 2>/dev/null || { - log_error "删除 cursor-updater 目录失败" - show_manual_guide - return 1 - } - log_info "成功删除 cursor-updater 目录" + if [ ! -f "$file" ]; then + log_error "文件不存在: $file,跳过处理。" + file_modification_status+=("'$file': Not Found") + continue fi - - touch "$updater_path" 2>/dev/null || { - log_error "创建阻止文件失败" - show_manual_guide - return 1 + + # 创建文件备份 + local backup_file="${file}.backup_$(date +%Y%m%d_%H%M%S)" + if ! cp "$file" "$backup_file"; then + log_error "无法创建文件备份: $file" + file_modification_status+=("'$file': Backup Failed") + continue + fi + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$backup_file" || log_warn "设置备份文件所有权失败: $backup_file" + chmod 444 "$backup_file" || log_warn "设置备份文件权限失败: $backup_file" + + + # 确保文件对当前执行用户(root)可写 + chmod u+w "$file" || { + log_error "无法修改文件权限(写): $file" + file_modification_status+=("'$file': Permission Error (Write)") + # 尝试恢复备份(如果可能) + cp "$backup_file" "$file" 2>/dev/null || true + continue } - if ! chmod 444 "$updater_path" 2>/dev/null || ! chown "$CURRENT_USER:$CURRENT_USER" "$updater_path" 2>/dev/null; then - log_error "设置文件权限失败" - show_manual_guide - return 1 - fi + local modification_applied=false + + # --- 开始尝试各种修改模式 --- - # 尝试设置不可修改属性 - if command -v chattr &> /dev/null; then - chattr +i "$updater_path" 2>/dev/null || { - log_warn "chattr 设置失败" - show_manual_guide - return 1 - } + # 模式1:精确修改 x-cursor-checksum (最常见的目标之一) + if grep -q 'i.header.set("x-cursor-checksum' "$file"; then + log_debug "找到 x-cursor-checksum 设置代码,尝试修改..." + # 使用更健壮的 sed,处理不同的空格和变量名可能性 + if sed -i -E 's/(i|[\w$]+)\.header\.set\("x-cursor-checksum",\s*e\s*===\s*void 0\s*\?\s*`\$\{p\}(\$\{t\})`\s*:\s*`\$\{p\}\2\/(\$\{e\})`/i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${p}`)/' "$file"; then + # 验证修改是否真的发生 (避免 sed 没匹配但返回0) + if ! grep -q 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${e}`)' "$file"; then + log_info "成功修改 x-cursor-checksum 设置代码" + modification_applied=true + else + log_warn "sed 命令执行成功,但似乎未修改 x-cursor-checksum (可能模式不匹配当前版本)" + fi + else + log_error "修改 x-cursor-checksum 设置代码失败 (sed 命令执行错误)" + fi fi - - # 验证设置是否成功 - if [ ! -f "$updater_path" ] || [ -w "$updater_path" ]; then - log_error "验证失败:文件权限设置可能未生效" - show_manual_guide - return 1 + + # 模式2:注入 randomUUID 到特定函数 (如果模式1未应用) + if [ "$modification_applied" = false ] && grep -q "IOPlatformUUID" "$file"; then + log_debug "未修改 checksum 或未找到,尝试注入 randomUUID..." + # 尝试注入 a$ 函数 + if grep -q "function a\$(" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then + if sed -i 's/function a\$(t){switch/function a\$(t){try { return require("crypto").randomUUID(); } catch(e){} switch/' "$file"; then + # 验证修改 + if grep -q "return require(\"crypto\").randomUUID()" "$file"; then + log_info "成功注入 randomUUID 调用到 a\$ 函数" + modification_applied=true + else + log_warn "sed 注入 a$ 失败(可能模式不匹配)" + fi + else + log_error "修改 a\$ 函数失败 (sed 命令执行错误)" + fi + # 尝试注入 v5 函数 (如果 a$ 没成功) + elif [ "$modification_applied" = false ] && grep -q "async function v5(" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then + if sed -i 's/async function v5(t){let e=/async function v5(t){try { return require("crypto").randomUUID(); } catch(e){} let e=/' "$file"; then + # 验证修改 + if grep -q "return require(\"crypto\").randomUUID()" "$file"; then + log_info "成功注入 randomUUID 调用到 v5 函数" + modification_applied=true + else + log_warn "sed 注入 v5 失败(可能模式不匹配)" + fi + else + log_error "修改 v5 函数失败 (sed 命令执行错误)" + fi + fi + fi + + # 模式3:通用注入 (如果上述模式都未应用,并且没有标记) + if [ "$modification_applied" = false ] && ! grep -q "// Cursor ID Modifier Injection" "$file"; then + log_debug "特定修改模式未生效或不适用,尝试通用注入..." + # 生成唯一标识符以避免冲突 + local timestamp=$(date +%s) + local new_uuid=$(generate_uuid) + local machine_id=$(generate_uuid) # 使用 UUID + local device_id=$(generate_uuid) + local mac_machine_id=$(openssl rand -hex 32) # 伪造 MAC 相关 ID + + # 创建注入代码块 + local inject_universal_code=" +// Cursor ID Modifier Injection - $timestamp +const originalRequire_$timestamp = typeof require === 'function' ? require : null; +if (originalRequire_$timestamp) { + require = function(module) { + try { + const result = originalRequire_$timestamp(module); + if (module === 'crypto' && result && result.randomUUID) { + const originalRandomUUID_$timestamp = result.randomUUID; + result.randomUUID = function() { return '$new_uuid'; }; + console.log('Cursor Modifier: Patched crypto.randomUUID'); + } + if (module === 'os' && result && result.networkInterfaces) { + const originalNI_$timestamp = result.networkInterfaces; + result.networkInterfaces = function() { return { lo: [{ address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' }]}; }; + console.log('Cursor Modifier: Patched os.networkInterfaces'); + } + return result; + } catch (e) { + console.error('Cursor Modifier: Error in require patch for module:', module, e); + // 如果原始 require 失败,可能需要返回一个空对象或抛出异常 + // 尝试调用原始 require,即使它可能已在 try 块中失败 + try { return originalRequire_$timestamp(module); } catch (innerE) { return {}; } + } + }; +} else { console.warn('Cursor Modifier: Original require not found.'); } + +// Override potential global functions or properties if they exist +try { if (typeof global !== 'undefined' && global.getMachineId) global.getMachineId = function() { return '$machine_id'; }; } catch(e){} +try { if (typeof global !== 'undefined' && global.getDeviceId) global.getDeviceId = function() { return '$device_id'; }; } catch(e){} +try { if (typeof global !== 'undefined' && global.macMachineId) global.macMachineId = '$mac_machine_id'; } catch(e){} +try { if (typeof process !== 'undefined' && process.env) process.env.VSCODE_MACHINE_ID = '$machine_id'; } catch(e){} + +console.log('Cursor Modifier: Universal patches applied (UUID: $new_uuid)'); +// End Cursor ID Modifier Injection - $timestamp + +" + # 将变量替换进代码 + inject_universal_code=${inject_universal_code//\$new_uuid/$new_uuid} + inject_universal_code=${inject_universal_code//\$machine_id/$machine_id} + inject_universal_code=${inject_universal_code//\$device_id/$device_id} + inject_universal_code=${inject_universal_code//\$mac_machine_id/$mac_machine_id} + inject_universal_code=${inject_universal_code//\$timestamp/$timestamp} # 确保时间戳替换 + + # 将代码注入到文件开头 + local temp_inject_file=$(mktemp) + echo "$inject_universal_code" > "$temp_inject_file" + cat "$file" >> "$temp_inject_file" + + if mv "$temp_inject_file" "$file"; then + log_info "完成通用注入修改" + modification_applied=true + else + log_error "通用注入失败 (无法移动临时文件)" + rm -f "$temp_inject_file" # 清理注入文件 + fi + elif [ "$modification_applied" = false ]; then + log_info "文件 '$file' 似乎已被修改过 (包含注入标记),跳过通用注入。" + # 即使未应用新修改,也认为"成功"处理(避免恢复备份) + modification_applied=true # 标记为已处理,防止恢复备份 + fi + + # --- 结束修改尝试 --- + + # 根据修改结果处理 + if [ "$modification_applied" = true ]; then + ((modified_count++)) + file_modification_status+=("'$file': Success") + # 恢复文件权限为只读 + chmod u-w,go-w "$file" || log_warn "设置文件只读权限失败: $file" + # 设置文件所有者 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "设置 JS 文件所有权失败: $file" + else + log_error "未能成功应用任何修改到文件: $file" + file_modification_status+=("'$file': Failed") + # 恢复备份 + log_info "正在从备份恢复文件: $file" + if cp "$backup_file" "$file"; then + chmod u-w,go-w "$file" || log_warn "恢复备份后设置只读权限失败: $file" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "恢复备份后设置所有权失败: $file" + else + log_error "从备份恢复文件失败: $file" + # 文件可能处于不确定状态 + fi fi - log_info "成功禁用自动更新" + # 清理备份文件 + rm -f "$backup_file" + + done # 文件循环结束 + + # 报告每个文件的状态 + log_info "JS 文件处理状态汇总:" + for status in "${file_modification_status[@]}"; do + log_info "- $status" + done + + if [ "$modified_count" -eq 0 ]; then + log_error "未能成功修改任何JS文件。请检查日志以获取详细信息。" + return 1 + fi + + log_info "成功修改或确认了 $modified_count 个JS文件。" + return 0 +} + +# 禁用自动更新 +disable_auto_update() { + log_info "正在尝试禁用 Cursor 自动更新..." + + # 查找可能的更新配置文件 + local update_configs=() + # 用户配置目录下的 + if [ -d "$CURSOR_CONFIG_DIR" ]; then + update_configs+=("$CURSOR_CONFIG_DIR/update-config.json") + update_configs+=("$CURSOR_CONFIG_DIR/settings.json") # 有些设置可能在这里 + fi + # 安装目录下的 (如果资源目录确定) + if [ -n "$CURSOR_RESOURCES" ] && [ -d "$CURSOR_RESOURCES" ]; then + update_configs+=("$CURSOR_RESOURCES/resources/app-update.yml") + update_configs+=("$CURSOR_RESOURCES/app-update.yml") # 可能的位置 + fi + # 标准安装目录下的 + if [ -d "$INSTALL_DIR" ]; then + update_configs+=("$INSTALL_DIR/resources/app-update.yml") + update_configs+=("$INSTALL_DIR/app-update.yml") + fi + # $HOME/.local/share + update_configs+=("$HOME/.local/share/cursor/update-config.json") + + + local disabled_count=0 + + # 处理 JSON 配置文件 + local json_config_pattern='update-config.json|settings.json' + for config in "${update_configs[@]}"; do + if [[ "$config" =~ $json_config_pattern ]] && [ -f "$config" ]; then + log_info "找到可能的更新配置文件: $config" + + # 备份 + cp "$config" "${config}.bak_$(date +%Y%m%d%H%M%S)" 2>/dev/null + + # 尝试修改 JSON (如果存在且是 settings.json) + if [[ "$config" == *settings.json ]]; then + # 尝试添加或修改 "update.mode": "none" + if grep -q '"update.mode"' "$config"; then + sed -i 's/"update.mode":[[:space:]]*"[^"]*"/"update.mode": "none"/' "$config" || log_warn "修改 settings.json 中的 update.mode 失败" + elif grep -q "}" "$config"; then # 尝试注入 + sed -i '$ s/}/,\n "update.mode": "none"\n}/' "$config" || log_warn "注入 update.mode 到 settings.json 失败" + else + log_warn "无法修改 settings.json 以禁用更新(结构未知)" + fi + # 确保权限正确 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$config" || log_warn "设置所有权失败: $config" + chmod 644 "$config" || log_warn "设置权限失败: $config" + ((disabled_count++)) + log_info "已尝试在 '$config' 中设置 'update.mode' 为 'none'" + elif [[ "$config" == *update-config.json ]]; then + # 直接覆盖 update-config.json + echo '{"autoCheck": false, "autoDownload": false}' > "$config" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$config" || log_warn "设置所有权失败: $config" + chmod 644 "$config" || log_warn "设置权限失败: $config" + ((disabled_count++)) + log_info "已覆盖更新配置文件: $config" + fi + fi + done + + # 处理 YAML 配置文件 + local yml_config_pattern='app-update.yml' + for config in "${update_configs[@]}"; do + if [[ "$config" =~ $yml_config_pattern ]] && [ -f "$config" ]; then + log_info "找到可能的更新配置文件: $config" + # 备份 + cp "$config" "${config}.bak_$(date +%Y%m%d%H%M%S)" 2>/dev/null + # 清空或修改内容 (简单起见,直接清空或写入禁用标记) + echo "# Automatic updates disabled by script $(date)" > "$config" + # echo "provider: generic" > "$config" # 或者尝试修改 provider + # echo "url: http://127.0.0.1" >> "$config" + chmod 444 "$config" # 设置为只读 + ((disabled_count++)) + log_info "已修改/清空更新配置文件: $config" + fi + done + + # 尝试查找updater可执行文件并禁用(重命名或移除权限) + local updater_paths=() + if [ -n "$CURSOR_RESOURCES" ] && [ -d "$CURSOR_RESOURCES" ]; then + updater_paths+=($(find "$CURSOR_RESOURCES" -name "updater" -type f -executable 2>/dev/null)) + updater_paths+=($(find "$CURSOR_RESOURCES" -name "CursorUpdater" -type f -executable 2>/dev/null)) # macOS 风格? + fi + if [ -d "$INSTALL_DIR" ]; then + updater_paths+=($(find "$INSTALL_DIR" -name "updater" -type f -executable 2>/dev/null)) + updater_paths+=($(find "$INSTALL_DIR" -name "CursorUpdater" -type f -executable 2>/dev/null)) + fi + updater_paths+=("$HOME/.config/Cursor/updater") # 旧位置? + + for updater in "${updater_paths[@]}"; do + if [ -f "$updater" ] && [ -x "$updater" ]; then + log_info "找到更新程序: $updater" + local bak_updater="${updater}.bak_$(date +%Y%m%d%H%M%S)" + if mv "$updater" "$bak_updater"; then + log_info "已重命名更新程序为: $bak_updater" + ((disabled_count++)) + else + log_warn "重命名更新程序失败: $updater,尝试移除执行权限..." + if chmod a-x "$updater"; then + log_info "已移除更新程序执行权限: $updater" + ((disabled_count++)) + else + log_error "无法禁用更新程序: $updater" + fi + fi + # elif [ -d "$updater" ]; then # 如果是目录,尝试禁用 + # log_info "找到更新程序目录: $updater" + # touch "${updater}.disabled_by_script" + # log_info "已标记禁用更新程序目录: $updater" + # ((disabled_count++)) + fi + done + + if [ "$disabled_count" -eq 0 ]; then + log_warn "未能找到或禁用任何已知的自动更新机制。" + log_warn "如果 Cursor 仍然自动更新,可能需要手动查找并禁用相关文件或设置。" else - log_info "保持默认设置,不进行更改" + log_info "成功禁用或尝试禁用了 $disabled_count 个自动更新相关的文件/程序。" fi + return 0 # 即使没找到,也认为函数执行成功 +} + +# 新增:通用菜单选择函数 +select_menu_option() { + local prompt="$1" + IFS='|' read -ra options <<< "$2" + local default_index=${3:-0} + local selected_index=$default_index + local key_input + local cursor_up=$'\e[A' # 更标准的 ANSI 码 + local cursor_down=$'\e[B' + local enter_key=$'\n' + + # 隐藏光标 + tput civis + # 清除可能存在的旧菜单行 (假设菜单最多 N 行) + local num_options=${#options[@]} + for ((i=0; i/dev/null || cat /etc/*release 2>/dev/null || cat /etc/issue" "系统版本信息" + clear - # 显示 CURSOR Logo + # 显示 Logo echo -e " ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ @@ -346,27 +1169,108 @@ main() { ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ " - echo -e "${BLUE}================================${NC}" - echo -e "${GREEN} Cursor ID 修改工具${NC}" - echo -e "${BLUE}================================${NC}" + echo -e "${BLUE}=====================================================${NC}" + echo -e "${GREEN} Cursor Linux 启动与修改工具(免费) ${NC}" + echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】 ${NC}" + echo -e "${YELLOW} 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${BLUE}=====================================================${NC}" echo - echo -e "${YELLOW}[重要提示]${NC} 本工具仅支持 Cursor v0.44.11 及以下版本" - echo -e "${YELLOW}[重要提示]${NC} 最新的 0.45.x 版本暂不支持" + echo -e "${GREEN} [小小广告] 出售CursorPro教育号一年质保三个月,有需要找我(86),WeChat:JavaRookie666 ${NC}" echo + echo -e "${YELLOW}[提示]${NC} 本工具旨在修改 Cursor 以解决可能的启动问题或设备限制。" + echo -e "${YELLOW}[提示]${NC} 它将优先修改 JS 文件,并可选择重置设备ID和禁用自动更新。" + echo -e "${YELLOW}[提示]${NC} 如果未找到 Cursor,将尝试从 '$APPIMAGE_SEARCH_DIR' 目录安装。" + echo + + # 查找 Cursor 路径 + if ! find_cursor_path; then + log_warn "系统中未找到现有的 Cursor 安装。" + select_menu_option "是否尝试从 '$APPIMAGE_SEARCH_DIR' 目录中的 AppImage 文件安装 Cursor?" "是,安装 Cursor|否,退出脚本" 0 + install_choice=$? + + if [ "$install_choice" -eq 0 ]; then + if ! install_cursor_appimage; then + log_error "Cursor 安装失败,请检查上面的日志。脚本将退出。" + exit 1 + fi + # 安装成功后,重新查找路径 + if ! find_cursor_path || ! find_cursor_resources; then + log_error "安装后仍然无法找到 Cursor 的可执行文件或资源目录。请检查 '$INSTALL_DIR' 和 '/usr/local/bin/cursor'。脚本退出。" + exit 1 + fi + log_info "Cursor 安装成功,继续执行修改步骤..." + else + log_info "用户选择不安装 Cursor,脚本退出。" + exit 0 + fi + else + # 如果找到了 Cursor,也要确保找到资源目录 + if ! find_cursor_resources; then + log_error "找到了 Cursor 可执行文件 ($CURSOR_PATH),但未能定位资源目录。" + log_error "无法继续修改 JS 文件。请检查 Cursor 安装是否完整。脚本退出。" + exit 1 + fi + log_info "发现已安装的 Cursor ($CURSOR_PATH),资源目录 ($CURSOR_RESOURCES)。" + fi + + # 到这里,Cursor 应该已安装并且路径已知 + + # 检查并关闭Cursor进程 + if ! check_and_kill_cursor; then + # check_and_kill_cursor 内部会记录错误并退出,但以防万一 + exit 1 + fi - check_permissions - check_and_kill_cursor - backup_config - generate_new_config + # 执行 Cursor 初始化清理 + # cursor_initialize_cleanup + + # 备份并处理配置文件 (机器码重置选项) + if ! generate_new_config; then + log_error "处理配置文件时出错,脚本中止。" + # 此处可能需要考虑是否回滚JS修改(如果已执行)?目前不回滚。 + exit 1 + fi + + # 修改JS文件 + log_info "正在修改 Cursor JS 文件..." + if ! modify_cursor_js_files; then + log_error "JS 文件修改过程中发生错误。" + log_warn "配置文件可能已被修改,但 JS 文件修改失败。" + log_warn "如果重启后 Cursor 行为异常或仍有问题,请检查日志并考虑手动恢复备份或重新运行脚本。" + # 决定是否继续执行禁用更新?通常建议继续 + # exit 1 # 或者选择退出 + else + log_info "JS 文件修改成功!" + fi + # 禁用自动更新 + if ! disable_auto_update; then + # disable_auto_update 内部会记录警告,不视为致命错误 + log_warn "尝试禁用自动更新时遇到问题(详见日志),但脚本将继续。" + fi + + log_info "所有修改步骤已完成!" + log_info "请启动 Cursor 以应用更改。" + + # 显示最后的提示信息 + echo + echo -e "${GREEN}=====================================================${NC}" + echo -e "${YELLOW} 请关注公众号【煎饼果子卷AI】获取更多技巧和交流 ${NC}" + echo -e "${GREEN}=====================================================${NC}" echo - log_info "操作完成!" - show_follow_info - show_file_tree - log_info "请重启 Cursor 以应用新的配置" - disable_auto_update + # 记录脚本完成信息 + log_info "脚本执行完成" + echo "========== Cursor ID 修改工具日志结束 $(date) ==========" >> "$LOG_FILE" + + # 显示日志文件位置 + echo + log_info "详细日志已保存到: $LOG_FILE" + echo "如遇问题请将此日志文件提供给开发者以协助排查" + echo } # 执行主函数 main + +exit 0 # 确保最后返回成功状态码 diff --git a/scripts/run/cursor_mac_free_trial_reset.sh b/scripts/run/cursor_mac_free_trial_reset.sh new file mode 100644 index 00000000..13f2a5b5 --- /dev/null +++ b/scripts/run/cursor_mac_free_trial_reset.sh @@ -0,0 +1,1401 @@ +#!/bin/bash + +# 设置错误处理 +set -e + +# 定义日志文件路径 +LOG_FILE="/tmp/cursor_free_trial_reset.log" + +# 初始化日志文件 +initialize_log() { + echo "========== Cursor Free Trial Reset Tool Log Start $(date) ==========" > "$LOG_FILE" + chmod 644 "$LOG_FILE" +} + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 - 同时输出到终端和日志文件 +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" + echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" + echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" + echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +log_debug() { + echo -e "${BLUE}[DEBUG]${NC} $1" + echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +# 记录命令输出到日志文件 +log_cmd_output() { + local cmd="$1" + local msg="$2" + echo "[CMD] $(date '+%Y-%m-%d %H:%M:%S') 执行命令: $cmd" >> "$LOG_FILE" + echo "[CMD] $msg:" >> "$LOG_FILE" + eval "$cmd" 2>&1 | tee -a "$LOG_FILE" + echo "" >> "$LOG_FILE" +} + +# 获取当前用户 +get_current_user() { + if [ "$EUID" -eq 0 ]; then + echo "$SUDO_USER" + else + echo "$USER" + fi +} + +CURRENT_USER=$(get_current_user) +if [ -z "$CURRENT_USER" ]; then + log_error "无法获取用户名" + exit 1 +fi + +# 定义配置文件路径 +STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" +BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" + +# 定义 Cursor 应用程序路径 +CURSOR_APP_PATH="/Applications/Cursor.app" + +# 🚀 新增 Cursor 防掉试用Pro删除文件夹功能 +remove_cursor_trial_folders() { + echo + log_info "🎯 [核心功能] 正在执行 Cursor 防掉试用Pro删除文件夹..." + log_info "📋 [说明] 此功能将删除指定的Cursor相关文件夹以重置试用状态" + echo + + # 定义需要删除的文件夹路径 + local folders_to_delete=( + "$HOME/Library/Application Support/Cursor" + "$HOME/.cursor" + ) + + log_info "📂 [检测] 将检查以下文件夹:" + for folder in "${folders_to_delete[@]}"; do + echo " 📁 $folder" + done + echo + + local deleted_count=0 + local skipped_count=0 + local error_count=0 + + # 删除指定文件夹 + for folder in "${folders_to_delete[@]}"; do + log_debug "🔍 [检查] 检查文件夹: $folder" + + if [ -d "$folder" ]; then + log_warn "⚠️ [警告] 发现文件夹存在,正在删除..." + if rm -rf "$folder"; then + log_info "✅ [成功] 已删除文件夹: $folder" + ((deleted_count++)) + else + log_error "❌ [错误] 删除文件夹失败: $folder" + ((error_count++)) + fi + else + log_warn "⏭️ [跳过] 文件夹不存在: $folder" + ((skipped_count++)) + fi + echo + done + + # 🔧 重要:深度修复权限问题 + log_info "🔧 [深度修复] 正在进行全面的权限修复..." + local cursor_support_dir="$HOME/Library/Application Support/Cursor" + local cursor_home_dir="$HOME/.cursor" + + # 创建完整的目录结构(包括Cursor可能需要的所有子目录) + local directories=( + "$cursor_support_dir" + "$cursor_support_dir/User" + "$cursor_support_dir/User/globalStorage" + "$cursor_support_dir/User/workspaceStorage" + "$cursor_support_dir/User/History" + "$cursor_support_dir/logs" + "$cursor_support_dir/CachedData" + "$cursor_support_dir/CachedExtensions" + "$cursor_support_dir/CachedExtensionVSIXs" + "$cursor_home_dir" + "$cursor_home_dir/extensions" + ) + + log_info "📁 [创建] 创建完整的目录结构..." + for dir in "${directories[@]}"; do + if mkdir -p "$dir" 2>/dev/null; then + log_debug "✅ 创建目录: $dir" + else + log_warn "⚠️ 创建目录失败: $dir" + fi + done + + # 设置递归权限(确保所有子目录都有正确权限) + log_info "🔐 [权限] 设置递归权限..." + chmod -R 755 "$cursor_support_dir" 2>/dev/null || true + chmod -R 755 "$cursor_home_dir" 2>/dev/null || true + + # 特别处理:确保当前用户拥有这些目录 + log_info "👤 [所有权] 确保目录所有权正确..." + chown -R "$(whoami)" "$cursor_support_dir" 2>/dev/null || true + chown -R "$(whoami)" "$cursor_home_dir" 2>/dev/null || true + + # 验证权限设置 + log_info "🔍 [验证] 验证权限设置..." + if [ -w "$cursor_support_dir" ] && [ -w "$cursor_home_dir" ]; then + log_info "✅ [成功] 权限验证通过" + else + log_warn "⚠️ [警告] 权限验证失败,可能仍存在问题" + fi + + # 🔍 权限诊断 + log_info "🔍 [诊断] 执行权限诊断..." + echo " 📁 目录权限检查:" + for dir in "${directories[@]}"; do + if [ -d "$dir" ]; then + local perms=$(ls -ld "$dir" | awk '{print $1, $3, $4}') + echo " ✅ $dir: $perms" + else + echo " ❌ $dir: 不存在" + fi + done + + # 🔍 权限诊断 + log_info "🔍 [诊断] 执行权限诊断..." + echo " 📁 目录权限检查:" + for dir in "${directories[@]}"; do + if [ -d "$dir" ]; then + local perms=$(ls -ld "$dir" | awk '{print $1, $3, $4}') + echo " ✅ $dir: $perms" + else + echo " ❌ $dir: 不存在" + fi + done + + log_info "✅ [完成] 深度权限修复完成" + echo + + # 显示操作统计 + log_info "📊 [统计] 操作完成统计:" + echo " ✅ 成功删除: $deleted_count 个文件夹" + echo " ⏭️ 跳过处理: $skipped_count 个文件夹" + echo " ❌ 删除失败: $error_count 个文件夹" + echo + + if [ $deleted_count -gt 0 ]; then + log_info "🎉 [完成] Cursor 防掉试用Pro文件夹删除完成!" + else + log_warn "🤔 [提示] 未找到需要删除的文件夹,可能已经清理过了" + fi + echo +} + +# 🔄 重启Cursor并等待配置文件生成 +restart_cursor_and_wait() { + echo + log_info "🔄 [重启] 正在重启Cursor以重新生成配置文件..." + + if [ -z "$CURSOR_PROCESS_PATH" ]; then + log_error "❌ [错误] 未找到Cursor进程信息,无法重启" + return 1 + fi + + log_info "📍 [路径] 使用路径: $CURSOR_PROCESS_PATH" + + if [ ! -f "$CURSOR_PROCESS_PATH" ]; then + log_error "❌ [错误] Cursor可执行文件不存在: $CURSOR_PROCESS_PATH" + return 1 + fi + + # 🔧 启动前最后一次权限确认 + log_info "🔧 [最终权限] 启动前最后一次权限确认..." + local cursor_support_dir="$HOME/Library/Application Support/Cursor" + local cursor_home_dir="$HOME/.cursor" + + # 再次确认完整目录结构存在 + local directories=( + "$cursor_support_dir" + "$cursor_support_dir/User" + "$cursor_support_dir/User/globalStorage" + "$cursor_support_dir/logs" + "$cursor_support_dir/CachedData" + "$cursor_home_dir" + "$cursor_home_dir/extensions" + ) + + for dir in "${directories[@]}"; do + mkdir -p "$dir" 2>/dev/null || true + done + + # 设置强制权限 + chmod -R 755 "$cursor_support_dir" 2>/dev/null || true + chmod -R 755 "$cursor_home_dir" 2>/dev/null || true + chown -R "$(whoami)" "$cursor_support_dir" 2>/dev/null || true + chown -R "$(whoami)" "$cursor_home_dir" 2>/dev/null || true + + # 启动Cursor + log_info "🚀 [启动] 正在启动Cursor..." + "$CURSOR_PROCESS_PATH" > /dev/null 2>&1 & + CURSOR_PID=$! + + log_info "⏳ [等待] 等待15秒让Cursor完全启动并生成配置文件..." + sleep 15 + + # 检查配置文件是否生成 + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + local max_wait=30 + local waited=0 + + while [ ! -f "$config_path" ] && [ $waited -lt $max_wait ]; do + log_info "⏳ [等待] 等待配置文件生成... ($waited/$max_wait 秒)" + sleep 1 + waited=$((waited + 1)) + done + + if [ -f "$config_path" ]; then + log_info "✅ [成功] 配置文件已生成: $config_path" + else + log_warn "⚠️ [警告] 配置文件未在预期时间内生成,继续执行..." + fi + + # 强制关闭Cursor + log_info "🔄 [关闭] 正在关闭Cursor以进行配置修改..." + if [ ! -z "$CURSOR_PID" ]; then + kill $CURSOR_PID 2>/dev/null || true + fi + + # 确保所有Cursor进程都关闭 + pkill -f "Cursor" 2>/dev/null || true + + log_info "✅ [完成] Cursor重启流程完成" + return 0 +} + +# 🛠️ 修改机器码配置 +modify_machine_code_config() { + echo + log_info "🛠️ [配置] 正在修改机器码配置..." + + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + + if [ ! -f "$config_path" ]; then + log_error "❌ [错误] 配置文件不存在: $config_path" + log_info "💡 [提示] 请手动启动Cursor一次,然后重新运行此脚本" + return 1 + fi + + # 生成新的ID + local MAC_MACHINE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]') + local UUID=$(uuidgen | tr '[:upper:]' '[:lower:]') + local MACHINE_ID="auth0|user_$(openssl rand -hex 32)" + local SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" + + log_info "🔧 [生成] 已生成新的设备标识符" + + # 备份原始配置 + local backup_dir="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" + mkdir -p "$backup_dir" + + local backup_name="storage.json.backup_$(date +%Y%m%d_%H%M%S)" + cp "$config_path" "$backup_dir/$backup_name" + log_info "💾 [备份] 已备份原配置: $backup_name" + + # 使用Python修改JSON配置(更可靠) + python3 -c " +import json +import sys + +try: + with open('$config_path', 'r', encoding='utf-8') as f: + config = json.load(f) + + config['telemetry.machineId'] = '$MACHINE_ID' + config['telemetry.macMachineId'] = '$MAC_MACHINE_ID' + config['telemetry.devDeviceId'] = '$UUID' + config['telemetry.sqmId'] = '$SQM_ID' + + with open('$config_path', 'w', encoding='utf-8') as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + print('SUCCESS') +except Exception as e: + print(f'ERROR: {e}') + sys.exit(1) +" 2>/dev/null + + if [ $? -eq 0 ]; then + log_info "✅ [成功] 机器码配置修改完成" + log_info "📋 [详情] 已更新以下标识符:" + echo " 🔹 machineId: ${MACHINE_ID:0:20}..." + echo " 🔹 macMachineId: $MAC_MACHINE_ID" + echo " 🔹 devDeviceId: $UUID" + echo " 🔹 sqmId: $SQM_ID" + return 0 + else + log_error "❌ [错误] 修改配置失败" + return 1 + fi +} + +# 检查权限 +check_permissions() { + if [ "$EUID" -ne 0 ]; then + log_error "请使用 sudo 运行此脚本" + echo "示例: sudo $0" + exit 1 + fi +} + +# 检查并关闭 Cursor 进程(保存进程信息) +check_and_kill_cursor() { + log_info "🔍 [检查] 检查 Cursor 进程..." + + local attempt=1 + local max_attempts=5 + + # 💾 保存Cursor进程路径 + CURSOR_PROCESS_PATH="/Applications/Cursor.app/Contents/MacOS/Cursor" + + # 函数:获取进程详细信息 + get_process_details() { + local process_name="$1" + log_debug "正在获取 $process_name 进程详细信息:" + ps aux | grep -i "/Applications/Cursor.app" | grep -v grep + } + + while [ $attempt -le $max_attempts ]; do + # 使用更精确的匹配来获取 Cursor 进程 + CURSOR_PIDS=$(ps aux | grep -i "/Applications/Cursor.app" | grep -v grep | awk '{print $2}') + + if [ -z "$CURSOR_PIDS" ]; then + log_info "💡 [提示] 未发现运行中的 Cursor 进程" + # 确认Cursor应用路径存在 + if [ -f "$CURSOR_PROCESS_PATH" ]; then + log_info "💾 [保存] 已保存Cursor路径: $CURSOR_PROCESS_PATH" + else + log_warn "⚠️ [警告] 未找到Cursor应用,请确认已安装" + fi + return 0 + fi + + log_warn "⚠️ [警告] 发现 Cursor 进程正在运行" + # 💾 保存进程信息 + log_info "💾 [保存] 已保存Cursor路径: $CURSOR_PROCESS_PATH" + get_process_details "cursor" + + log_warn "🔄 [操作] 尝试关闭 Cursor 进程..." + + if [ $attempt -eq $max_attempts ]; then + log_warn "💥 [强制] 尝试强制终止进程..." + kill -9 $CURSOR_PIDS 2>/dev/null || true + else + kill $CURSOR_PIDS 2>/dev/null || true + fi + + sleep 1 + + # 同样使用更精确的匹配来检查进程是否还在运行 + if ! ps aux | grep -i "/Applications/Cursor.app" | grep -v grep > /dev/null; then + log_info "✅ [成功] Cursor 进程已成功关闭" + return 0 + fi + + log_warn "⏳ [等待] 等待进程关闭,尝试 $attempt/$max_attempts..." + ((attempt++)) + done + + log_error "❌ [错误] 在 $max_attempts 次尝试后仍无法关闭 Cursor 进程" + get_process_details "cursor" + log_error "💥 [错误] 请手动关闭进程后重试" + exit 1 +} + +# 备份配置文件 +backup_config() { + if [ ! -f "$STORAGE_FILE" ]; then + log_warn "配置文件不存在,跳过备份" + return 0 + fi + + mkdir -p "$BACKUP_DIR" + local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)" + + if cp "$STORAGE_FILE" "$backup_file"; then + chmod 644 "$backup_file" + chown "$CURRENT_USER" "$backup_file" + log_info "配置已备份到: $backup_file" + else + log_error "备份失败" + exit 1 + fi +} + +# 生成随机 ID +generate_random_id() { + # 生成32字节(64个十六进制字符)的随机数 + openssl rand -hex 32 +} + +# 生成随机 UUID +generate_uuid() { + uuidgen | tr '[:upper:]' '[:lower:]' +} + +# 修改现有文件 +modify_or_add_config() { + local key="$1" + local value="$2" + local file="$3" + + if [ ! -f "$file" ]; then + log_error "文件不存在: $file" + return 1 + fi + + # 确保文件可写 + chmod 644 "$file" || { + log_error "无法修改文件权限: $file" + return 1 + } + + # 创建临时文件 + local temp_file=$(mktemp) + + # 检查key是否存在 + if grep -q "\"$key\":" "$file"; then + # key存在,执行替换 + sed "s/\"$key\":[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$file" > "$temp_file" || { + log_error "修改配置失败: $key" + rm -f "$temp_file" + return 1 + } + else + # key不存在,添加新的key-value对 + sed "s/}$/,\n \"$key\": \"$value\"\n}/" "$file" > "$temp_file" || { + log_error "添加配置失败: $key" + rm -f "$temp_file" + return 1 + } + fi + + # 检查临时文件是否为空 + if [ ! -s "$temp_file" ]; then + log_error "生成的临时文件为空" + rm -f "$temp_file" + return 1 + fi + + # 使用 cat 替换原文件内容 + cat "$temp_file" > "$file" || { + log_error "无法写入文件: $file" + rm -f "$temp_file" + return 1 + } + + rm -f "$temp_file" + + # 恢复文件权限 + chmod 444 "$file" + + return 0 +} + +# 生成新的配置 +generate_new_config() { + echo + log_warn "机器码处理" + + # 默认不重置机器码 + reset_choice=0 + + # 记录日志以便调试 + echo "[INPUT_DEBUG] 机器码重置选项: 不重置 (默认)" >> "$LOG_FILE" + + # 处理 - 默认为不重置 + log_info "默认不重置机器码,将仅修改js文件" + + # 确保配置文件目录存在 + if [ -f "$STORAGE_FILE" ]; then + log_info "发现已有配置文件: $STORAGE_FILE" + + # 备份现有配置(以防万一) + backup_config + else + log_warn "未找到配置文件,这是正常的,脚本将跳过ID修改" + fi + + echo + log_info "配置处理完成" +} + +# 清理 Cursor 之前的修改 +clean_cursor_app() { + log_info "尝试清理 Cursor 之前的修改..." + + # 如果存在备份,直接恢复备份 + local latest_backup="" + + # 查找最新的备份 + latest_backup=$(find /tmp -name "Cursor.app.backup_*" -type d -print 2>/dev/null | sort -r | head -1) + + if [ -n "$latest_backup" ] && [ -d "$latest_backup" ]; then + log_info "找到现有备份: $latest_backup" + log_info "正在恢复原始版本..." + + # 停止 Cursor 进程 + check_and_kill_cursor + + # 恢复备份 + sudo rm -rf "$CURSOR_APP_PATH" + sudo cp -R "$latest_backup" "$CURSOR_APP_PATH" + sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH" + sudo chmod -R 755 "$CURSOR_APP_PATH" + + log_info "已恢复原始版本" + return 0 + else + log_warn "未找到现有备份,尝试重新安装 Cursor..." + echo "您可以从 https://cursor.sh 下载并重新安装 Cursor" + echo "或者继续执行此脚本,将尝试修复现有安装" + + # 可以在这里添加重新下载和安装的逻辑 + return 1 + fi +} + +# 修改 Cursor 主程序文件(安全模式) +modify_cursor_app_files() { + log_info "正在安全修改 Cursor 主程序文件..." + log_info "详细日志将记录到: $LOG_FILE" + + # 先清理之前的修改 + clean_cursor_app + + # 验证应用是否存在 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log_error "未找到 Cursor.app,请确认安装路径: $CURSOR_APP_PATH" + return 1 + fi + + # 定义目标文件 - 将extensionHostProcess.js放在最前面优先处理 + local target_files=( + "${CURSOR_APP_PATH}/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "${CURSOR_APP_PATH}/Contents/Resources/app/out/main.js" + "${CURSOR_APP_PATH}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" + ) + + # 检查文件是否存在并且是否已修改 + local need_modification=false + local missing_files=false + + log_debug "检查目标文件..." + for file in "${target_files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "文件不存在: ${file/$CURSOR_APP_PATH\//}" + echo "[FILE_CHECK] 文件不存在: $file" >> "$LOG_FILE" + missing_files=true + continue + fi + + echo "[FILE_CHECK] 文件存在: $file ($(wc -c < "$file") 字节)" >> "$LOG_FILE" + + if ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then + log_info "文件需要修改: ${file/$CURSOR_APP_PATH\//}" + grep -n "IOPlatformUUID" "$file" | head -3 >> "$LOG_FILE" || echo "[FILE_CHECK] 未找到 IOPlatformUUID" >> "$LOG_FILE" + need_modification=true + break + else + log_info "文件已修改: ${file/$CURSOR_APP_PATH\//}" + fi + done + + # 如果所有文件都已修改或不存在,则退出 + if [ "$missing_files" = true ]; then + log_error "部分目标文件不存在,请确认 Cursor 安装是否完整" + return 1 + fi + + if [ "$need_modification" = false ]; then + log_info "所有目标文件已经被修改过,无需重复操作" + return 0 + fi + + # 创建临时工作目录 + local timestamp=$(date +%Y%m%d_%H%M%S) + local temp_dir="/tmp/cursor_reset_${timestamp}" + local temp_app="${temp_dir}/Cursor.app" + local backup_app="/tmp/Cursor.app.backup_${timestamp}" + + log_debug "创建临时目录: $temp_dir" + echo "[TEMP_DIR] 创建临时目录: $temp_dir" >> "$LOG_FILE" + + # 清理可能存在的旧临时目录 + if [ -d "$temp_dir" ]; then + log_info "清理已存在的临时目录..." + rm -rf "$temp_dir" + fi + + # 创建新的临时目录 + mkdir -p "$temp_dir" || { + log_error "无法创建临时目录: $temp_dir" + echo "[ERROR] 无法创建临时目录: $temp_dir" >> "$LOG_FILE" + return 1 + } + + # 备份原应用 + log_info "备份原应用..." + echo "[BACKUP] 开始备份: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE" + + cp -R "$CURSOR_APP_PATH" "$backup_app" || { + log_error "无法创建应用备份" + echo "[ERROR] 备份失败: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE" + rm -rf "$temp_dir" + return 1 + } + + echo "[BACKUP] 备份完成" >> "$LOG_FILE" + + # 复制应用到临时目录 + log_info "创建临时工作副本..." + echo "[COPY] 开始复制: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE" + + cp -R "$CURSOR_APP_PATH" "$temp_dir" || { + log_error "无法复制应用到临时目录" + echo "[ERROR] 复制失败: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE" + rm -rf "$temp_dir" "$backup_app" + return 1 + } + + echo "[COPY] 复制完成" >> "$LOG_FILE" + + # 确保临时目录的权限正确 + chown -R "$CURRENT_USER:staff" "$temp_dir" + chmod -R 755 "$temp_dir" + + # 移除签名(增强兼容性) + log_info "移除应用签名..." + echo "[CODESIGN] 移除签名: $temp_app" >> "$LOG_FILE" + + codesign --remove-signature "$temp_app" 2>> "$LOG_FILE" || { + log_warn "移除应用签名失败" + echo "[WARN] 移除签名失败: $temp_app" >> "$LOG_FILE" + } + + # 移除所有相关组件的签名 + local components=( + "$temp_app/Contents/Frameworks/Cursor Helper.app" + "$temp_app/Contents/Frameworks/Cursor Helper (GPU).app" + "$temp_app/Contents/Frameworks/Cursor Helper (Plugin).app" + "$temp_app/Contents/Frameworks/Cursor Helper (Renderer).app" + ) + + for component in "${components[@]}"; do + if [ -e "$component" ]; then + log_info "正在移除签名: $component" + codesign --remove-signature "$component" || { + log_warn "移除组件签名失败: $component" + } + fi + done + + # 修改目标文件 - 优先处理js文件 + local modified_count=0 + local files=( + "${temp_app}/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "${temp_app}/Contents/Resources/app/out/main.js" + "${temp_app}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" + ) + + for file in "${files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "文件不存在: ${file/$temp_dir\//}" + continue + fi + + log_debug "处理文件: ${file/$temp_dir\//}" + echo "[PROCESS] 开始处理文件: $file" >> "$LOG_FILE" + echo "[PROCESS] 文件大小: $(wc -c < "$file") 字节" >> "$LOG_FILE" + + # 输出文件部分内容到日志 + echo "[FILE_CONTENT] 文件头部 100 行:" >> "$LOG_FILE" + head -100 "$file" 2>/dev/null | grep -v "^$" | head -50 >> "$LOG_FILE" + echo "[FILE_CONTENT] ..." >> "$LOG_FILE" + + # 创建文件备份 + cp "$file" "${file}.bak" || { + log_error "无法创建文件备份: ${file/$temp_dir\//}" + echo "[ERROR] 无法创建文件备份: $file" >> "$LOG_FILE" + continue + } + + # 使用 sed 替换而不是字符串操作 + if [[ "$file" == *"extensionHostProcess.js"* ]]; then + log_debug "处理 extensionHostProcess.js 文件..." + echo "[PROCESS_DETAIL] 开始处理 extensionHostProcess.js 文件" >> "$LOG_FILE" + + # 检查是否包含目标代码 + if grep -q 'i.header.set("x-cursor-checksum' "$file"; then + log_debug "找到 x-cursor-checksum 设置代码" + echo "[FOUND] 找到 x-cursor-checksum 设置代码" >> "$LOG_FILE" + + # 记录匹配的行到日志 + grep -n 'i.header.set("x-cursor-checksum' "$file" >> "$LOG_FILE" + + # 执行特定的替换 + if sed -i.tmp 's/i\.header\.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${e}`)/i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${p}`)/' "$file"; then + log_info "成功修改 x-cursor-checksum 设置代码" + echo "[SUCCESS] 成功完成 x-cursor-checksum 设置代码替换" >> "$LOG_FILE" + # 记录修改后的行 + grep -n 'i.header.set("x-cursor-checksum' "$file" >> "$LOG_FILE" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 x-cursor-checksum 设置代码失败" + echo "[ERROR] 替换 x-cursor-checksum 设置代码失败" >> "$LOG_FILE" + cp "${file}.bak" "$file" + fi + else + log_warn "未找到 x-cursor-checksum 设置代码" + echo "[FILE_CHECK] 未找到 x-cursor-checksum 设置代码" >> "$LOG_FILE" + + # 记录文件部分内容到日志以便排查 + echo "[FILE_CONTENT] 文件中包含 'header.set' 的行:" >> "$LOG_FILE" + grep -n "header.set" "$file" | head -20 >> "$LOG_FILE" + + echo "[FILE_CONTENT] 文件中包含 'checksum' 的行:" >> "$LOG_FILE" + grep -n "checksum" "$file" | head -20 >> "$LOG_FILE" + fi + + echo "[PROCESS_DETAIL] 完成处理 extensionHostProcess.js 文件" >> "$LOG_FILE" + elif grep -q "IOPlatformUUID" "$file"; then + log_debug "找到 IOPlatformUUID 关键字" + echo "[FOUND] 找到 IOPlatformUUID 关键字" >> "$LOG_FILE" + grep -n "IOPlatformUUID" "$file" | head -5 >> "$LOG_FILE" + + # 定位 IOPlatformUUID 相关函数 + if grep -q "function a\$" "$file"; then + # 检查是否已经修改过 + if grep -q "return crypto.randomUUID()" "$file"; then + log_info "文件已经包含 randomUUID 调用,跳过修改" + ((modified_count++)) + continue + fi + + # 针对 main.js 中发现的代码结构进行修改 + if sed -i.tmp 's/function a\$(t){switch/function a\$(t){return crypto.randomUUID(); switch/' "$file"; then + log_debug "成功注入 randomUUID 调用到 a\$ 函数" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 a\$ 函数失败" + cp "${file}.bak" "$file" + fi + elif grep -q "async function v5" "$file"; then + # 检查是否已经修改过 + if grep -q "return crypto.randomUUID()" "$file"; then + log_info "文件已经包含 randomUUID 调用,跳过修改" + ((modified_count++)) + continue + fi + + # 替代方法 - 修改 v5 函数 + if sed -i.tmp 's/async function v5(t){let e=/async function v5(t){return crypto.randomUUID(); let e=/' "$file"; then + log_debug "成功注入 randomUUID 调用到 v5 函数" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 v5 函数失败" + cp "${file}.bak" "$file" + fi + else + # 检查是否已经注入了自定义代码 + if grep -q "// Cursor ID 修改工具注入" "$file"; then + log_info "文件已经包含自定义注入代码,跳过修改" + ((modified_count++)) + continue + fi + + # 使用更通用的注入方法 + log_warn "未找到具体函数,尝试使用通用修改方法" + inject_code=" +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) +// 随机设备ID生成器注入 - $(date +%s) +const randomDeviceId_$(date +%s) = () => { + try { + return require('crypto').randomUUID(); + } catch (e) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = Math.random() * 16 | 0; + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } +}; +" + # 将代码注入到文件开头 + echo "$inject_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + # 替换调用点 + sed -i.tmp 's/await v5(!1)/randomDeviceId_'"$(date +%s)"'()/g' "$file" + sed -i.tmp 's/a\$(t)/randomDeviceId_'"$(date +%s)"'()/g' "$file" + + log_debug "完成通用修改" + ((modified_count++)) + log_info "使用通用方法成功修改文件: ${file/$temp_dir\//}" + fi + else + # 未找到 IOPlatformUUID,可能是文件结构变化 + log_warn "未找到 IOPlatformUUID,尝试替代方法" + + # 检查是否已经注入或修改过 + if grep -q "return crypto.randomUUID()" "$file" || grep -q "// Cursor ID 修改工具注入" "$file"; then + log_info "文件已经被修改过,跳过修改" + ((modified_count++)) + continue + fi + + # 尝试找其他关键函数如 getMachineId 或 getDeviceId + if grep -q "function t\$()" "$file" || grep -q "async function y5" "$file"; then + log_debug "找到设备ID相关函数" + + # 修改 MAC 地址获取函数 + if grep -q "function t\$()" "$file"; then + sed -i.tmp 's/function t\$(){/function t\$(){return "00:00:00:00:00:00";/' "$file" + log_debug "修改 MAC 地址获取函数成功" + fi + + # 修改设备ID获取函数 + if grep -q "async function y5" "$file"; then + sed -i.tmp 's/async function y5(t){/async function y5(t){return crypto.randomUUID();/' "$file" + log_debug "修改设备ID获取函数成功" + fi + + ((modified_count++)) + log_info "使用替代方法成功修改文件: ${file/$temp_dir\//}" + else + # 最后尝试的通用方法 - 在文件顶部插入重写函数定义 + log_warn "未找到任何已知函数,使用最通用的方法" + + inject_universal_code=" +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) +// 全局拦截设备标识符 - $(date +%s) +const originalRequire_$(date +%s) = require; +require = function(module) { + const result = originalRequire_$(date +%s)(module); + if (module === 'crypto' && result.randomUUID) { + const originalRandomUUID_$(date +%s) = result.randomUUID; + result.randomUUID = function() { + return '${new_uuid}'; + }; + } + return result; +}; + +// 覆盖所有可能的系统ID获取函数 +global.getMachineId = function() { return '${machine_id}'; }; +global.getDeviceId = function() { return '${device_id}'; }; +global.macMachineId = '${mac_machine_id}'; +" + # 将代码注入到文件开头 + local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') + local machine_id="auth0|user_$(openssl rand -hex 16)" + local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + local mac_machine_id=$(openssl rand -hex 32) + + inject_universal_code=${inject_universal_code//\$\{new_uuid\}/$new_uuid} + inject_universal_code=${inject_universal_code//\$\{machine_id\}/$machine_id} + inject_universal_code=${inject_universal_code//\$\{device_id\}/$device_id} + inject_universal_code=${inject_universal_code//\$\{mac_machine_id\}/$mac_machine_id} + + echo "$inject_universal_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + log_debug "完成通用覆盖" + ((modified_count++)) + log_info "使用最通用方法成功修改文件: ${file/$temp_dir\//}" + fi + fi + + # 添加在关键操作后记录日志 + echo "[MODIFIED] 文件修改后内容:" >> "$LOG_FILE" + grep -n "return crypto.randomUUID()" "$file" | head -3 >> "$LOG_FILE" + + # 清理临时文件 + rm -f "${file}.tmp" "${file}.bak" + echo "[PROCESS] 文件处理完成: $file" >> "$LOG_FILE" + done + + if [ "$modified_count" -eq 0 ]; then + log_error "未能成功修改任何文件" + rm -rf "$temp_dir" + return 1 + fi + + # 重新签名应用(增加重试机制) + local max_retry=3 + local retry_count=0 + local sign_success=false + + while [ $retry_count -lt $max_retry ]; do + ((retry_count++)) + log_info "尝试签名 (第 $retry_count 次)..." + + # 使用更详细的签名参数 + if codesign --sign - --force --deep --preserve-metadata=entitlements,identifier,flags "$temp_app" 2>&1 | tee /tmp/codesign.log; then + # 验证签名 + if codesign --verify -vvvv "$temp_app" 2>/dev/null; then + sign_success=true + log_info "应用签名验证通过" + break + else + log_warn "签名验证失败,错误日志:" + cat /tmp/codesign.log + fi + else + log_warn "签名失败,错误日志:" + cat /tmp/codesign.log + fi + + sleep 1 + done + + if ! $sign_success; then + log_error "经过 $max_retry 次尝试仍无法完成签名" + log_error "请手动执行以下命令完成签名:" + echo -e "${BLUE}sudo codesign --sign - --force --deep '${temp_app}'${NC}" + echo -e "${YELLOW}操作完成后,请手动将应用复制到原路径:${NC}" + echo -e "${BLUE}sudo cp -R '${temp_app}' '/Applications/'${NC}" + log_info "临时文件保留在:${temp_dir}" + return 1 + fi + + # 替换原应用 + log_info "安装修改版应用..." + if ! sudo rm -rf "$CURSOR_APP_PATH" || ! sudo cp -R "$temp_app" "/Applications/"; then + log_error "应用替换失败,正在恢复..." + sudo rm -rf "$CURSOR_APP_PATH" + sudo cp -R "$backup_app" "$CURSOR_APP_PATH" + rm -rf "$temp_dir" "$backup_app" + return 1 + fi + + # 清理临时文件 + rm -rf "$temp_dir" "$backup_app" + + # 设置权限 + sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH" + sudo chmod -R 755 "$CURSOR_APP_PATH" + + log_info "Cursor 主程序文件修改完成!原版备份在: ${backup_app/$HOME/\~}" + return 0 +} + +# 显示文件树结构 +show_file_tree() { + local base_dir=$(dirname "$STORAGE_FILE") + echo + log_info "文件结构:" + echo -e "${BLUE}$base_dir${NC}" + echo "├── globalStorage" + echo "│ ├── storage.json (已修改)" + echo "│ └── backups" + + # 列出备份文件 + if [ -d "$BACKUP_DIR" ]; then + local backup_files=("$BACKUP_DIR"/*) + if [ ${#backup_files[@]} -gt 0 ]; then + for file in "${backup_files[@]}"; do + if [ -f "$file" ]; then + echo "│ └── $(basename "$file")" + fi + done + else + echo "│ └── (空)" + fi + fi + echo +} + +# 显示公众号信息 +show_follow_info() { + echo + echo -e "${GREEN}================================${NC}" + echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${GREEN}================================${NC}" + echo +} + +# 禁用自动更新 +disable_auto_update() { + local updater_path="$HOME/Library/Application Support/Caches/cursor-updater" + local app_update_yml="/Applications/Cursor.app/Contents/Resources/app-update.yml" + + echo + log_info "正在禁用 Cursor 自动更新..." + + # 备份并清空 app-update.yml + if [ -f "$app_update_yml" ]; then + log_info "备份并修改 app-update.yml..." + if ! sudo cp "$app_update_yml" "${app_update_yml}.bak" 2>/dev/null; then + log_warn "备份 app-update.yml 失败,继续执行..." + fi + + if sudo bash -c "echo '' > \"$app_update_yml\"" && \ + sudo chmod 444 "$app_update_yml"; then + log_info "成功禁用 app-update.yml" + else + log_error "修改 app-update.yml 失败,请手动执行以下命令:" + echo -e "${BLUE}sudo cp \"$app_update_yml\" \"${app_update_yml}.bak\"${NC}" + echo -e "${BLUE}sudo bash -c 'echo \"\" > \"$app_update_yml\"'${NC}" + echo -e "${BLUE}sudo chmod 444 \"$app_update_yml\"${NC}" + fi + else + log_warn "未找到 app-update.yml 文件" + fi + + # 同时也处理 cursor-updater + log_info "处理 cursor-updater..." + if sudo rm -rf "$updater_path" && \ + sudo touch "$updater_path" && \ + sudo chmod 444 "$updater_path"; then + log_info "成功禁用 cursor-updater" + else + log_error "禁用 cursor-updater 失败,请手动执行以下命令:" + echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}" + fi + + echo + log_info "验证方法:" + echo "1. 运行命令:ls -l \"$updater_path\"" + echo " 确认文件权限显示为:r--r--r--" + echo "2. 运行命令:ls -l \"$app_update_yml\"" + echo " 确认文件权限显示为:r--r--r--" + echo + log_info "完成后请重启 Cursor" +} + +# 新增恢复功能选项 +restore_feature() { + # 检查备份目录是否存在 + if [ ! -d "$BACKUP_DIR" ]; then + log_warn "备份目录不存在" + return 1 + fi + + # 使用 find 命令获取备份文件列表并存储到数组 + backup_files=() + while IFS= read -r file; do + [ -f "$file" ] && backup_files+=("$file") + done < <(find "$BACKUP_DIR" -name "*.backup_*" -type f 2>/dev/null | sort) + + # 检查是否找到备份文件 + if [ ${#backup_files[@]} -eq 0 ]; then + log_warn "未找到任何备份文件" + return 1 + fi + + echo + log_info "可用的备份文件:" + + # 构建菜单选项字符串 + menu_options="退出 - 不恢复任何文件" + for i in "${!backup_files[@]}"; do + menu_options="$menu_options|$(basename "${backup_files[$i]}")" + done + + # 使用菜单选择函数 + select_menu_option "请使用上下箭头选择要恢复的备份文件,按Enter确认:" "$menu_options" 0 + choice=$? + + # 处理用户输入 + if [ "$choice" = "0" ]; then + log_info "跳过恢复操作" + return 0 + fi + + # 获取选择的备份文件 (减1是因为第一个选项是"退出") + local selected_backup="${backup_files[$((choice-1))]}" + + # 验证文件存在性和可读性 + if [ ! -f "$selected_backup" ] || [ ! -r "$selected_backup" ]; then + log_error "无法访问选择的备份文件" + return 1 + fi + + # 尝试恢复配置 + if cp "$selected_backup" "$STORAGE_FILE"; then + chmod 644 "$STORAGE_FILE" + chown "$CURRENT_USER" "$STORAGE_FILE" + log_info "已从备份文件恢复配置: $(basename "$selected_backup")" + return 0 + else + log_error "恢复配置失败" + return 1 + fi +} + +# 解决"应用已损坏,无法打开"问题 +fix_damaged_app() { + log_info "正在修复"应用已损坏"问题..." + + # 检查Cursor应用是否存在 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log_error "未找到Cursor应用: $CURSOR_APP_PATH" + return 1 + fi + + log_info "尝试移除隔离属性..." + if sudo find "$CURSOR_APP_PATH" -print0 \ + | xargs -0 sudo xattr -d com.apple.quarantine 2>/dev/null + log_info "成功移除隔离属性" + else + log_warn "移除隔离属性失败,尝试其他方法..." + fi + + log_info "尝试重新签名应用..." + if sudo codesign --force --deep --sign - "$CURSOR_APP_PATH" 2>/dev/null; then + log_info "应用重新签名成功" + else + log_warn "应用重新签名失败" + fi + + echo + log_info "修复完成!请尝试重新打开Cursor应用" + echo + echo -e "${YELLOW}如果仍然无法打开,您可以尝试以下方法:${NC}" + echo "1. 在系统偏好设置->安全性与隐私中,点击"仍要打开"按钮" + echo "2. 暂时关闭Gatekeeper(不建议): sudo spctl --master-disable" + echo "3. 重新下载安装Cursor应用" + echo + echo -e "${BLUE}参考链接: https://sysin.org/blog/macos-if-crashes-when-opening/${NC}" + + return 0 +} + +# 新增:通用菜单选择函数 +# 参数: +# $1 - 提示信息 +# $2 - 选项数组,格式为 "选项1|选项2|选项3" +# $3 - 默认选项索引 (从0开始) +# 返回: 选中的选项索引 (从0开始) +select_menu_option() { + local prompt="$1" + IFS='|' read -ra options <<< "$2" + local default_index=${3:-0} + local selected_index=$default_index + local key_input + local cursor_up='\033[A' + local cursor_down='\033[B' + local enter_key=$'\n' + + # 保存光标位置 + tput sc + + # 显示提示信息 + echo -e "$prompt" + + # 第一次显示菜单 + for i in "${!options[@]}"; do + if [ $i -eq $selected_index ]; then + echo -e " ${GREEN}►${NC} ${options[$i]}" + else + echo -e " ${options[$i]}" + fi + done + + # 循环处理键盘输入 + while true; do + # 读取单个按键 + read -rsn3 key_input + + # 检测按键 + case "$key_input" in + # 上箭头键 + $'\033[A') + if [ $selected_index -gt 0 ]; then + ((selected_index--)) + fi + ;; + # 下箭头键 + $'\033[B') + if [ $selected_index -lt $((${#options[@]}-1)) ]; then + ((selected_index++)) + fi + ;; + # Enter键 + "") + echo # 换行 + log_info "您选择了: ${options[$selected_index]}" + return $selected_index + ;; + esac + + # 恢复光标位置 + tput rc + + # 重新显示菜单 + for i in "${!options[@]}"; do + if [ $i -eq $selected_index ]; then + echo -e " ${GREEN}►${NC} ${options[$i]}" + else + echo -e " ${options[$i]}" + fi + done + done +} + +# 主函数 +main() { + + # 初始化日志文件 + initialize_log + log_info "脚本启动..." + + # 记录系统信息 + log_info "系统信息: $(uname -a)" + log_info "当前用户: $CURRENT_USER" + log_cmd_output "sw_vers" "macOS 版本信息" + log_cmd_output "which codesign" "codesign 路径" + log_cmd_output "ls -la \"$CURSOR_APP_PATH\"" "Cursor 应用信息" + + # 新增环境检查 + if [[ $(uname) != "Darwin" ]]; then + log_error "本脚本仅支持 macOS 系统" + exit 1 + fi + + clear + # 显示 Logo + echo -e " + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ + ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ + ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ + ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ + " + echo -e "${BLUE}================================${NC}" + echo -e "${GREEN}🚀 Cursor 防掉试用Pro删除工具 ${NC}" + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】 ${NC}" + echo -e "${YELLOW}🤝 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${BLUE}================================${NC}" + echo + echo -e "${YELLOW}💡 [重要提示]${NC} 本工具采用分阶段执行策略,既能彻底清理又能修改机器码" + echo -e "${YELLOW}💡 [重要提示]${NC} 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】" + echo + + # 📋 执行流程说明 + echo + echo -e "${GREEN}📋 [执行流程]${NC} 本脚本将按以下步骤执行:" + echo -e "${BLUE} 1️⃣ 检测并关闭Cursor进程${NC}" + echo -e "${BLUE} 2️⃣ 保存Cursor程序路径信息${NC}" + echo -e "${BLUE} 3️⃣ 删除指定的Cursor试用相关文件夹${NC}" + echo -e "${BLUE} 📁 ~/Library/Application Support/Cursor${NC}" + echo -e "${BLUE} 📁 ~/.cursor${NC}" + echo -e "${BLUE} 3.5️⃣ 预创建必要目录结构,避免权限问题${NC}" + echo -e "${BLUE} 4️⃣ 重新启动Cursor让其生成新的配置文件${NC}" + echo -e "${BLUE} 5️⃣ 等待配置文件生成完成(最多45秒)${NC}" + echo -e "${BLUE} 6️⃣ 关闭Cursor进程${NC}" + echo -e "${BLUE} 7️⃣ 修改新生成的机器码配置文件${NC}" + echo -e "${BLUE} 8️⃣ 显示操作完成统计信息${NC}" + echo + echo -e "${YELLOW}⚠️ [注意事项]${NC}" + echo -e "${YELLOW} • 脚本执行过程中请勿手动操作Cursor${NC}" + echo -e "${YELLOW} • 建议在执行前关闭所有Cursor窗口${NC}" + echo -e "${YELLOW} • 执行完成后需要重新启动Cursor${NC}" + echo -e "${YELLOW} • 原配置文件会自动备份到backups文件夹${NC}" + echo -e "${YELLOW} • 需要Python3环境来处理JSON配置文件${NC}" + echo + + # 🤔 用户确认 + echo -e "${GREEN}🤔 [确认]${NC} 请确认您已了解上述执行流程" + read -p "是否继续执行?(输入 y 或 yes 继续,其他任意键退出): " confirmation + if [[ ! "$confirmation" =~ ^(y|yes)$ ]]; then + echo -e "${YELLOW}👋 [退出]${NC} 用户取消执行,脚本退出" + exit 0 + fi + echo -e "${GREEN}✅ [确认]${NC} 用户确认继续执行" + echo + + # 🚀 执行主要功能 + check_permissions + check_and_kill_cursor + + # 🚨 重要警告提示 + echo + echo -e "${RED}🚨 [重要警告]${NC} ============================================" + log_warn "⚠️ [风控提醒] Cursor 风控机制非常严格!" + log_warn "⚠️ [必须删除] 必须完全删除指定文件夹,不能有任何残留设置" + log_warn "⚠️ [防掉试用] 只有彻底清理才能有效防止掉试用Pro状态" + echo -e "${RED}🚨 [重要警告]${NC} ============================================" + echo + + # 🎯 执行 Cursor 防掉试用Pro删除文件夹功能 + log_info "🚀 [开始] 开始执行核心功能..." + remove_cursor_trial_folders + + # 🔄 重启Cursor让其重新生成配置文件 + restart_cursor_and_wait + + # 🛠️ 修改机器码配置 + modify_machine_code_config + + # 🎉 显示操作完成信息 + echo + log_info "🎉 [完成] Cursor 防掉试用Pro删除操作已完成!" + echo + + # 📱 显示公众号信息 + echo -e "${GREEN}================================${NC}" + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${GREEN}================================${NC}" + echo + log_info "🚀 [提示] 现在可以重新启动 Cursor 尝试使用了!" + echo + + # 🚫 以下功能已暂时屏蔽 + log_warn "⚠️ [提示] 以下功能已暂时屏蔽:" + log_info "📋 [说明] - 自动更新禁用功能" + log_info "📋 [说明] - 应用修复功能" + log_info "📋 [说明] 如需恢复这些功能,请联系开发者" + echo + + # 🎉 脚本执行完成 + log_info "🎉 [完成] 所有操作已完成!" + echo + log_info "💡 [提示] 如果需要恢复机器码修改功能,请联系开发者" + log_warn "⚠️ [注意] 重启 Cursor 后生效" + echo + log_info "🚀 [下一步] 现在可以启动 Cursor 尝试使用了!" + echo + + # 记录脚本完成信息 + log_info "📝 [日志] 脚本执行完成" + echo "========== Cursor 防掉试用Pro删除工具日志结束 $(date) ==========" >> "$LOG_FILE" + + # 显示日志文件位置 + echo + log_info "📄 [日志] 详细日志已保存到: $LOG_FILE" + echo "如遇问题请将此日志文件提供给开发者以协助排查" + echo +} + +# 执行主函数 +main + diff --git a/scripts/run/cursor_mac_id_modifier.sh b/scripts/run/cursor_mac_id_modifier.sh index 45560c20..bf2bf249 100644 --- a/scripts/run/cursor_mac_id_modifier.sh +++ b/scripts/run/cursor_mac_id_modifier.sh @@ -1,8 +1,35 @@ #!/bin/bash +# ======================================== +# Cursor macOS 机器码修改脚本 (增强权限修复版) +# ======================================== +# +# 🔧 权限修复增强: +# - 集成用户提供的核心权限修复命令 +# - 特别处理logs目录权限问题 +# - 解决EACCES: permission denied错误 +# - 确保Cursor能正常启动 +# +# 🚨 如果遇到权限错误,脚本会自动执行: +# - sudo chown -R $(whoami) ~/Library/"Application Support"/Cursor +# - sudo chown -R $(whoami) ~/.cursor +# - chmod -R u+w ~/Library/"Application Support"/Cursor +# - chmod -R u+w ~/.cursor/extensions +# +# ======================================== + # 设置错误处理 set -e +# 定义日志文件路径 +LOG_FILE="/tmp/cursor_free_trial_reset.log" + +# 初始化日志文件 +initialize_log() { + echo "========== Cursor Free Trial Reset Tool Log Start $(date) ==========" > "$LOG_FILE" + chmod 644 "$LOG_FILE" +} + # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' @@ -10,23 +37,605 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color -# 日志函数 +# 日志函数 - 同时输出到终端和日志文件 log_info() { echo -e "${GREEN}[INFO]${NC} $1" + echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" + echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_error() { echo -e "${RED}[ERROR]${NC} $1" + echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" } log_debug() { echo -e "${BLUE}[DEBUG]${NC} $1" + echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE" +} + +# 记录命令输出到日志文件 +log_cmd_output() { + local cmd="$1" + local msg="$2" + echo "[CMD] $(date '+%Y-%m-%d %H:%M:%S') 执行命令: $cmd" >> "$LOG_FILE" + echo "[CMD] $msg:" >> "$LOG_FILE" + eval "$cmd" 2>&1 | tee -a "$LOG_FILE" + echo "" >> "$LOG_FILE" +} + +# 🚀 新增 Cursor 防掉试用Pro删除文件夹功能 +remove_cursor_trial_folders() { + echo + log_info "🎯 [核心功能] 正在执行 Cursor 防掉试用Pro删除文件夹..." + log_info "📋 [说明] 此功能将删除指定的Cursor相关文件夹以重置试用状态" + echo + + # 定义需要删除的文件夹路径 + local folders_to_delete=( + "$HOME/Library/Application Support/Cursor" + "$HOME/.cursor" + ) + + log_info "📂 [检测] 将检查以下文件夹:" + for folder in "${folders_to_delete[@]}"; do + echo " 📁 $folder" + done + echo + + local deleted_count=0 + local skipped_count=0 + local error_count=0 + + # 删除指定文件夹 + for folder in "${folders_to_delete[@]}"; do + log_debug "🔍 [检查] 检查文件夹: $folder" + + if [ -d "$folder" ]; then + log_warn "⚠️ [警告] 发现文件夹存在,正在删除..." + if rm -rf "$folder"; then + log_info "✅ [成功] 已删除文件夹: $folder" + ((deleted_count++)) + else + log_error "❌ [错误] 删除文件夹失败: $folder" + ((error_count++)) + fi + else + log_warn "⏭️ [跳过] 文件夹不存在: $folder" + ((skipped_count++)) + fi + echo + done + + # 🔧 重要:删除文件夹后立即执行权限修复 + log_info "� [权限修复] 删除文件夹后立即执行权限修复..." + echo + + # 调用统一的权限修复函数 + ensure_cursor_directory_permissions + + # 显示操作统计 + log_info "📊 [统计] 操作完成统计:" + echo " ✅ 成功删除: $deleted_count 个文件夹" + echo " ⏭️ 跳过处理: $skipped_count 个文件夹" + echo " ❌ 删除失败: $error_count 个文件夹" + echo + + if [ $deleted_count -gt 0 ]; then + log_info "🎉 [完成] Cursor 防掉试用Pro文件夹删除完成!" + else + log_warn "🤔 [提示] 未找到需要删除的文件夹,可能已经清理过了" + fi + echo +} + +# 🔄 重启Cursor并等待配置文件生成 +restart_cursor_and_wait() { + echo + log_info "🔄 [重启] 正在重启Cursor以重新生成配置文件..." + + if [ -z "$CURSOR_PROCESS_PATH" ]; then + log_error "❌ [错误] 未找到Cursor进程信息,无法重启" + return 1 + fi + + log_info "📍 [路径] 使用路径: $CURSOR_PROCESS_PATH" + + if [ ! -f "$CURSOR_PROCESS_PATH" ]; then + log_error "❌ [错误] Cursor可执行文件不存在: $CURSOR_PROCESS_PATH" + return 1 + fi + + # 🔧 启动前权限修复 + log_info "🔧 [启动前权限] 执行启动前权限修复..." + ensure_cursor_directory_permissions + + # 启动Cursor + log_info "🚀 [启动] 正在启动Cursor..." + "$CURSOR_PROCESS_PATH" > /dev/null 2>&1 & + CURSOR_PID=$! + + log_info "⏳ [等待] 等待15秒让Cursor完全启动并生成配置文件..." + sleep 15 + + # 检查配置文件是否生成 + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + local max_wait=30 + local waited=0 + + while [ ! -f "$config_path" ] && [ $waited -lt $max_wait ]; do + log_info "⏳ [等待] 等待配置文件生成... ($waited/$max_wait 秒)" + sleep 1 + waited=$((waited + 1)) + done + + if [ -f "$config_path" ]; then + log_info "✅ [成功] 配置文件已生成: $config_path" + + # 🛡️ 关键修复:配置文件生成后立即确保权限正确 + ensure_cursor_directory_permissions + else + log_warn "⚠️ [警告] 配置文件未在预期时间内生成,继续执行..." + + # 即使配置文件未生成,也要确保目录权限正确 + ensure_cursor_directory_permissions + fi + + # 强制关闭Cursor + log_info "🔄 [关闭] 正在关闭Cursor以进行配置修改..." + if [ ! -z "$CURSOR_PID" ]; then + kill $CURSOR_PID 2>/dev/null || true + fi + + # 确保所有Cursor进程都关闭 + pkill -f "Cursor" 2>/dev/null || true + + log_info "✅ [完成] Cursor重启流程完成" + return 0 +} + +# 🔍 检查Cursor环境 +test_cursor_environment() { + local mode=${1:-"FULL"} + + echo + log_info "🔍 [环境检查] 正在检查Cursor环境..." + + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + local cursor_app_data="$HOME/Library/Application Support/Cursor" + local cursor_app_path="/Applications/Cursor.app" + local issues=() + + # 检查Python3环境(macOS版本需要) + if ! command -v python3 >/dev/null 2>&1; then + issues+=("Python3环境不可用,macOS版本需要Python3来处理JSON配置文件") + log_warn "⚠️ [警告] 未找到Python3,请安装Python3: brew install python3" + else + log_info "✅ [检查] Python3环境可用: $(python3 --version)" + fi + + # 检查配置文件 + if [ ! -f "$config_path" ]; then + issues+=("配置文件不存在: $config_path") + else + # 验证JSON格式 + if python3 -c "import json; json.load(open('$config_path'))" 2>/dev/null; then + log_info "✅ [检查] 配置文件格式正确" + else + issues+=("配置文件格式错误或损坏") + fi + fi + + # 检查Cursor目录结构 + if [ ! -d "$cursor_app_data" ]; then + issues+=("Cursor应用数据目录不存在: $cursor_app_data") + fi + + # 检查Cursor应用安装 + if [ ! -d "$cursor_app_path" ]; then + issues+=("未找到Cursor应用安装: $cursor_app_path") + else + log_info "✅ [检查] 找到Cursor应用: $cursor_app_path" + fi + + # 检查目录权限 + if [ -d "$cursor_app_data" ] && [ ! -w "$cursor_app_data" ]; then + issues+=("Cursor应用数据目录无写入权限: $cursor_app_data") + fi + + # 返回检查结果 + if [ ${#issues[@]} -eq 0 ]; then + log_info "✅ [环境检查] 所有检查通过" + return 0 + else + log_error "❌ [环境检查] 发现 ${#issues[@]} 个问题:" + for issue in "${issues[@]}"; do + echo -e "${RED} • $issue${NC}" + done + return 1 + fi +} + +# 🚀 启动Cursor生成配置文件 +start_cursor_to_generate_config() { + log_info "🚀 [启动] 正在尝试启动Cursor生成配置文件..." + + local cursor_app_path="/Applications/Cursor.app" + local cursor_executable="$cursor_app_path/Contents/MacOS/Cursor" + + if [ ! -f "$cursor_executable" ]; then + log_error "❌ [错误] 未找到Cursor可执行文件: $cursor_executable" + return 1 + fi + + log_info "📍 [路径] 使用Cursor路径: $cursor_executable" + + # 🚀 启动前权限修复 + ensure_cursor_directory_permissions + + # 启动Cursor + "$cursor_executable" > /dev/null 2>&1 & + local cursor_pid=$! + log_info "🚀 [启动] Cursor已启动,PID: $cursor_pid" + + log_info "⏳ [等待] 请等待Cursor完全加载(约30秒)..." + log_info "💡 [提示] 您可以在Cursor完全加载后手动关闭它" + + # 等待配置文件生成 + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + local max_wait=60 + local waited=0 + + while [ ! -f "$config_path" ] && [ $waited -lt $max_wait ]; do + sleep 2 + waited=$((waited + 2)) + if [ $((waited % 10)) -eq 0 ]; then + log_info "⏳ [等待] 等待配置文件生成... ($waited/$max_wait 秒)" + fi + done + + if [ -f "$config_path" ]; then + log_info "✅ [成功] 配置文件已生成!" + log_info "💡 [提示] 现在可以关闭Cursor并重新运行脚本" + return 0 + else + log_warn "⚠️ [超时] 配置文件未在预期时间内生成" + log_info "💡 [建议] 请手动操作Cursor(如创建新文件)以触发配置生成" + return 1 + fi +} + +# 🛡️ 统一权限修复函数(优化版本) +ensure_cursor_directory_permissions() { + log_info "🛡️ [权限修复] 执行核心权限修复命令..." + + local cursor_support_dir="$HOME/Library/Application Support/Cursor" + local cursor_home_dir="$HOME/.cursor" + + # 确保目录存在 + mkdir -p "$cursor_support_dir" 2>/dev/null || true + mkdir -p "$cursor_home_dir/extensions" 2>/dev/null || true + + # 🔧 执行用户验证有效的4个核心权限修复命令 + log_info "🔧 [修复] 执行4个核心权限修复命令..." + + # 命令1: sudo chown -R $(whoami) ~/Library/"Application Support"/Cursor + if sudo chown -R "$(whoami)" "$cursor_support_dir" 2>/dev/null; then + log_info "✅ [1/4] sudo chown Application Support/Cursor 成功" + else + log_warn "⚠️ [1/4] sudo chown Application Support/Cursor 失败" + fi + + # 命令2: sudo chown -R $(whoami) ~/.cursor + if sudo chown -R "$(whoami)" "$cursor_home_dir" 2>/dev/null; then + log_info "✅ [2/4] sudo chown .cursor 成功" + else + log_warn "⚠️ [2/4] sudo chown .cursor 失败" + fi + + # 命令3: chmod -R u+w ~/Library/"Application Support"/Cursor + if chmod -R u+w "$cursor_support_dir" 2>/dev/null; then + log_info "✅ [3/4] chmod Application Support/Cursor 成功" + else + log_warn "⚠️ [3/4] chmod Application Support/Cursor 失败" + fi + + # 命令4: chmod -R u+w ~/.cursor/extensions + if chmod -R u+w "$cursor_home_dir/extensions" 2>/dev/null; then + log_info "✅ [4/4] chmod .cursor/extensions 成功" + else + log_warn "⚠️ [4/4] chmod .cursor/extensions 失败" + fi + + log_info "✅ [完成] 核心权限修复命令执行完成" + return 0 +} + +# 关键权限修复函数(简化版本) +fix_cursor_permissions_critical() { + log_info "🚨 [关键权限修复] 执行权限修复..." + ensure_cursor_directory_permissions +} + +# 🚀 Cursor启动前权限确保(简化版本) +ensure_cursor_startup_permissions() { + log_info "🚀 [启动前权限] 执行权限修复..." + ensure_cursor_directory_permissions +} + + + + + +# 🛠️ 修改机器码配置(增强版) +modify_machine_code_config() { + local mode=${1:-"FULL"} + + echo + log_info "🛠️ [配置] 正在修改机器码配置..." + + local config_path="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" + + # 增强的配置文件检查 + if [ ! -f "$config_path" ]; then + log_error "❌ [错误] 配置文件不存在: $config_path" + echo + log_info "💡 [解决方案] 请尝试以下步骤:" + echo -e "${BLUE} 1️⃣ 手动启动Cursor应用程序${NC}" + echo -e "${BLUE} 2️⃣ 等待Cursor完全加载(约30秒)${NC}" + echo -e "${BLUE} 3️⃣ 关闭Cursor应用程序${NC}" + echo -e "${BLUE} 4️⃣ 重新运行此脚本${NC}" + echo + log_warn "⚠️ [备选方案] 如果问题持续:" + echo -e "${BLUE} • 选择脚本的'重置环境+修改机器码'选项${NC}" + echo -e "${BLUE} • 该选项会自动生成配置文件${NC}" + echo + + # 提供用户选择 + read -p "是否现在尝试启动Cursor生成配置文件?(y/n): " user_choice + if [[ "$user_choice" =~ ^(y|yes)$ ]]; then + log_info "🚀 [尝试] 正在尝试启动Cursor..." + if start_cursor_to_generate_config; then + return 0 + fi + fi + + return 1 + fi + + # 验证配置文件格式并显示结构 + log_info "🔍 [验证] 检查配置文件格式..." + if ! python3 -c "import json; json.load(open('$config_path'))" 2>/dev/null; then + log_error "❌ [错误] 配置文件格式错误或损坏" + log_info "💡 [建议] 配置文件可能已损坏,建议选择'重置环境+修改机器码'选项" + return 1 + fi + log_info "✅ [验证] 配置文件格式正确" + + # 显示当前配置文件中的相关属性 + log_info "📋 [当前配置] 检查现有的遥测属性:" + python3 -c " +import json +try: + with open('$config_path', 'r', encoding='utf-8') as f: + config = json.load(f) + + properties = ['telemetry.machineId', 'telemetry.macMachineId', 'telemetry.devDeviceId', 'telemetry.sqmId'] + for prop in properties: + if prop in config: + value = config[prop] + display_value = value[:20] + '...' if len(value) > 20 else value + print(f' ✓ {prop} = {display_value}') + else: + print(f' - {prop} (不存在,将创建)') +except Exception as e: + print(f'Error reading config: {e}') +" + echo + + # 显示操作进度 + log_info "⏳ [进度] 1/5 - 生成新的设备标识符..." + + # 生成新的ID + local MAC_MACHINE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]') + local UUID=$(uuidgen | tr '[:upper:]' '[:lower:]') + local MACHINE_ID="auth0|user_$(openssl rand -hex 32)" + local SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" + + log_info "✅ [进度] 1/5 - 设备标识符生成完成" + + log_info "⏳ [进度] 2/5 - 创建备份目录..." + + # 备份原始配置(增强版) + local backup_dir="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" + if ! mkdir -p "$backup_dir"; then + log_error "❌ [错误] 无法创建备份目录: $backup_dir" + return 1 + fi + + local backup_name="storage.json.backup_$(date +%Y%m%d_%H%M%S)" + local backup_path="$backup_dir/$backup_name" + + log_info "⏳ [进度] 3/5 - 备份原始配置..." + if ! cp "$config_path" "$backup_path"; then + log_error "❌ [错误] 备份配置文件失败" + return 1 + fi + + # 验证备份是否成功 + if [ -f "$backup_path" ]; then + local backup_size=$(wc -c < "$backup_path") + local original_size=$(wc -c < "$config_path") + if [ "$backup_size" -eq "$original_size" ]; then + log_info "✅ [进度] 3/5 - 配置备份成功: $backup_name" + else + log_warn "⚠️ [警告] 备份文件大小不匹配,但继续执行" + fi + else + log_error "❌ [错误] 备份文件创建失败" + return 1 + fi + + log_info "⏳ [进度] 4/5 - 更新配置文件..." + + # 使用Python修改JSON配置(更可靠,安全方式) + local python_result=$(python3 -c " +import json +import sys + +try: + with open('$config_path', 'r', encoding='utf-8') as f: + config = json.load(f) + + # 安全更新配置,确保属性存在 + properties_to_update = { + 'telemetry.machineId': '$MACHINE_ID', + 'telemetry.macMachineId': '$MAC_MACHINE_ID', + 'telemetry.devDeviceId': '$UUID', + 'telemetry.sqmId': '$SQM_ID' + } + + for key, value in properties_to_update.items(): + if key in config: + print(f' ✓ 更新属性: {key}') + else: + print(f' + 添加属性: {key}') + config[key] = value + + with open('$config_path', 'w', encoding='utf-8') as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + print('SUCCESS') +except Exception as e: + print(f'ERROR: {e}') + sys.exit(1) +" 2>&1) + + # 🔧 关键修复:正确解析Python执行结果 + local python_exit_code=$? + local python_success=false + + # 检查Python脚本是否成功执行 + if [ $python_exit_code -eq 0 ]; then + # 检查输出中是否包含SUCCESS标记(忽略其他输出) + if echo "$python_result" | grep -q "SUCCESS"; then + python_success=true + log_info "✅ [Python] 配置修改执行成功" + else + log_warn "⚠️ [Python] 执行成功但未找到SUCCESS标记" + log_info "💡 [调试] Python完整输出:" + echo "$python_result" + fi + else + log_error "❌ [Python] 脚本执行失败,退出码: $python_exit_code" + log_info "💡 [调试] Python完整输出:" + echo "$python_result" + fi + + if [ "$python_success" = true ]; then + log_info "⏳ [进度] 5/5 - 验证修改结果..." + + # 🔒 关键修复:在验证前确保文件权限正确 + chmod 644 "$config_path" 2>/dev/null || true + + # 验证修改是否成功 + local verification_result=$(python3 -c " +import json +try: + with open('$config_path', 'r', encoding='utf-8') as f: + config = json.load(f) + + properties_to_check = { + 'telemetry.machineId': '$MACHINE_ID', + 'telemetry.macMachineId': '$MAC_MACHINE_ID', + 'telemetry.devDeviceId': '$UUID', + 'telemetry.sqmId': '$SQM_ID' + } + + verification_passed = True + for key, expected_value in properties_to_check.items(): + actual_value = config.get(key) + if actual_value == expected_value: + print(f'✓ {key}: 验证通过') + else: + print(f'✗ {key}: 验证失败 (期望: {expected_value}, 实际: {actual_value})') + verification_passed = False + + if verification_passed: + print('VERIFICATION_SUCCESS') + else: + print('VERIFICATION_FAILED') +except Exception as e: + print(f'VERIFICATION_ERROR: {e}') +" 2>&1) + + # 检查验证结果(忽略其他输出,只关注最终结果) + if echo "$verification_result" | grep -q "VERIFICATION_SUCCESS"; then + log_info "✅ [进度] 5/5 - 修改验证成功" + + # 🔐 关键修复:设置配置文件为只读保护 + if chmod 444 "$config_path" 2>/dev/null; then + log_info "🔐 [保护] 配置文件已设置为只读保护" + else + log_warn "⚠️ [警告] 无法设置配置文件只读保护" + fi + + # 🛡️ 关键修复:执行权限修复 + ensure_cursor_directory_permissions + + echo + log_info "🎉 [成功] 机器码配置修改完成!" + log_info "📋 [详情] 已更新以下标识符:" + echo " 🔹 machineId: ${MACHINE_ID:0:20}..." + echo " 🔹 macMachineId: $MAC_MACHINE_ID" + echo " 🔹 devDeviceId: $UUID" + echo " 🔹 sqmId: $SQM_ID" + echo + log_info "💾 [备份] 原配置已备份至: $backup_name" + return 0 + else + log_error "❌ [错误] 修改验证失败" + log_info "💡 [验证详情]:" + echo "$verification_result" + log_info "🔄 [恢复] 正在恢复备份并修复权限..." + + # 恢复备份并确保权限正确 + if cp "$backup_path" "$config_path"; then + chmod 644 "$config_path" 2>/dev/null || true + ensure_cursor_directory_permissions + log_info "✅ [恢复] 已恢复原始配置并修复权限" + else + log_error "❌ [错误] 恢复备份失败" + fi + return 1 + fi + else + log_error "❌ [错误] 修改配置失败" + log_info "💡 [调试信息] Python执行详情:" + echo "$python_result" + + # 尝试恢复备份并修复权限 + if [ -f "$backup_path" ]; then + log_info "🔄 [恢复] 正在恢复备份配置并修复权限..." + if cp "$backup_path" "$config_path"; then + chmod 644 "$config_path" 2>/dev/null || true + ensure_cursor_directory_permissions + log_info "✅ [恢复] 已恢复原始配置并修复权限" + else + log_error "❌ [错误] 恢复备份失败" + fi + fi + + return 1 + fi } + + # 获取当前用户 get_current_user() { if [ "$EUID" -eq 0 ]; then @@ -42,143 +651,1686 @@ if [ -z "$CURRENT_USER" ]; then exit 1 fi -# 定义配置文件路径 -STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" -BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" +# 定义配置文件路径 +STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" +BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" + +# 定义 Cursor 应用程序路径 +CURSOR_APP_PATH="/Applications/Cursor.app" + +# 新增:判断接口类型是否为Wi-Fi +is_wifi_interface() { + local interface_name="$1" + # 通过networksetup判断接口类型 + networksetup -listallhardwareports | \ + awk -v dev="$interface_name" 'BEGIN{found=0} /Hardware Port: Wi-Fi/{found=1} /Device:/{if(found && $2==dev){exit 0}else{found=0}}' && return 0 || return 1 +} + +# 🎯 增强的MAC地址生成和验证(集成randommac.sh特性) +generate_local_unicast_mac() { + # 第一字节:LAA+单播(低两位10),其余随机 + local first_byte=$(( (RANDOM & 0xFC) | 0x02 )) + local mac=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' \ + $first_byte $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))) + echo "$mac" +} + +# 🔍 MAC地址验证函数(基于randommac.sh) +validate_mac_address() { + local mac="$1" + local regex="^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$" + + if [[ $mac =~ $regex ]]; then + return 0 + else + return 1 + fi +} + + + +# 🔄 增强的WiFi断开和重连机制 +manage_wifi_connection() { + local action="$1" # disconnect 或 reconnect + local interface_name="$2" + + if ! is_wifi_interface "$interface_name"; then + log_info "📡 [跳过] 接口 '$interface_name' 不是WiFi,跳过WiFi管理" + return 0 + fi + + case "$action" in + "disconnect") + log_info "📡 [WiFi] 断开WiFi连接但保持适配器开启..." + + # 方法1: 使用airport工具断开 + if command -v /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport >/dev/null 2>&1; then + sudo /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -z 2>>"$LOG_FILE" + log_info "✅ [WiFi] 使用airport工具断开WiFi连接" + else + # 方法2: 使用networksetup断开 + local wifi_service=$(networksetup -listallhardwareports | grep -A1 "Device: $interface_name" | grep "Hardware Port:" | cut -d: -f2 | xargs) + if [ -n "$wifi_service" ]; then + networksetup -setairportpower "$interface_name" off 2>>"$LOG_FILE" + sleep 2 + networksetup -setairportpower "$interface_name" on 2>>"$LOG_FILE" + log_info "✅ [WiFi] 使用networksetup重置WiFi适配器" + else + log_warn "⚠️ [WiFi] 无法找到WiFi服务,跳过断开" + fi + fi + + sleep 3 + ;; + + "reconnect") + log_info "📡 [WiFi] 重新连接WiFi..." + + # 触发网络硬件重新检测 + sudo networksetup -detectnewhardware 2>>"$LOG_FILE" + + # 等待网络重新连接 + log_info "⏳ [WiFi] 等待WiFi重新连接..." + local wait_count=0 + local max_wait=30 + + while [ $wait_count -lt $max_wait ]; do + if ping -c 1 8.8.8.8 >/dev/null 2>&1; then + log_info "✅ [WiFi] 网络连接已恢复" + return 0 + fi + sleep 2 + wait_count=$((wait_count + 2)) + + if [ $((wait_count % 10)) -eq 0 ]; then + log_info "⏳ [WiFi] 等待网络连接... ($wait_count/$max_wait 秒)" + fi + done + + log_warn "⚠️ [WiFi] 网络连接未在预期时间内恢复,但继续执行" + ;; + + *) + log_error "❌ [错误] 无效的WiFi管理操作: $action" + return 1 + ;; + esac +} + +# 🛠️ 增强的第三方工具MAC地址修改 +try_third_party_mac_tool() { + local interface_name="$1" + local random_mac="$2" + local success=false + local tool_used="" + + log_info "🛠️ [第三方] 尝试使用第三方工具修改MAC地址" + + # 🔍 检测可用的第三方工具 + local available_tools=() + if command -v macchanger >/dev/null 2>&1; then + available_tools+=("macchanger") + fi + if command -v spoof-mac >/dev/null 2>&1; then + available_tools+=("spoof-mac") + fi + + if [ ${#available_tools[@]} -eq 0 ]; then + log_warn "⚠️ [警告] 未检测到可用的第三方MAC地址修改工具" + log_info "💡 [建议] 可以安装以下工具:" + echo " • brew install spoof-mac" + echo " • brew install macchanger" + return 1 + fi + + log_info "🔍 [检测] 发现可用工具: ${available_tools[*]}" + + # 🎯 优先使用macchanger + if [[ " ${available_tools[*]} " =~ " macchanger " ]]; then + log_info "🔧 [macchanger] 尝试使用macchanger修改接口 '$interface_name' 的MAC地址..." + + # 先关闭接口 + sudo ifconfig "$interface_name" down 2>>"$LOG_FILE" + sleep 2 + + if sudo macchanger -m "$random_mac" "$interface_name" >>"$LOG_FILE" 2>&1; then + success=true + tool_used="macchanger" + log_info "✅ [成功] macchanger修改成功" + else + log_warn "⚠️ [失败] macchanger修改失败" + fi + + # 重新启用接口 + sudo ifconfig "$interface_name" up 2>>"$LOG_FILE" + sleep 2 + fi + + # 🎯 如果macchanger失败,尝试spoof-mac + if ! $success && [[ " ${available_tools[*]} " =~ " spoof-mac " ]]; then + log_info "🔧 [spoof-mac] 尝试使用spoof-mac修改接口 '$interface_name' 的MAC地址..." + + if sudo spoof-mac set "$random_mac" "$interface_name" >>"$LOG_FILE" 2>&1; then + success=true + tool_used="spoof-mac" + log_info "✅ [成功] spoof-mac修改成功" + else + log_warn "⚠️ [失败] spoof-mac修改失败" + fi + fi + + if $success; then + log_info "🎉 [成功] 第三方工具 ($tool_used) 修改MAC地址成功" + return 0 + else + log_error "❌ [失败] 所有第三方工具都修改失败" + return 1 + fi +} + +# 🔍 增强的macOS环境检测和兼容性评估 +detect_macos_environment() { + local macos_version=$(sw_vers -productVersion) + local macos_major=$(echo "$macos_version" | cut -d. -f1) + local macos_minor=$(echo "$macos_version" | cut -d. -f2) + local hardware_type="" + + # 检测硬件类型 + if [[ $(uname -m) == "arm64" ]]; then + hardware_type="Apple Silicon" + else + hardware_type="Intel" + fi + + log_info "🔍 [环境] 系统环境检测: macOS $macos_version ($hardware_type)" + + # 检查SIP状态 + local sip_status=$(csrutil status 2>/dev/null | grep -o "enabled\|disabled" || echo "unknown") + log_info "🔒 [SIP] 系统完整性保护状态: $sip_status" + + # 设置环境变量 + export MACOS_VERSION="$macos_version" + export MACOS_MAJOR="$macos_major" + export MACOS_MINOR="$macos_minor" + export HARDWARE_TYPE="$hardware_type" + export SIP_STATUS="$sip_status" + + # 🎯 增强的兼容性检查 + local compatibility_level="FULL" + local compatibility_issues=() + + # 检查macOS版本兼容性 + if [[ $macos_major -ge 14 ]]; then + compatibility_issues+=("macOS $macos_major+ 对MAC地址修改有严格限制") + compatibility_level="LIMITED" + elif [[ $macos_major -ge 12 ]]; then + compatibility_issues+=("macOS $macos_major 可能对MAC地址修改有部分限制") + compatibility_level="PARTIAL" + fi + + # 检查硬件兼容性 + if [[ "$hardware_type" == "Apple Silicon" ]]; then + compatibility_issues+=("Apple Silicon硬件对MAC地址修改有硬件级限制") + if [[ "$compatibility_level" == "FULL" ]]; then + compatibility_level="PARTIAL" + else + compatibility_level="MINIMAL" + fi + fi + + # 检查SIP影响 + if [[ "$sip_status" == "enabled" ]]; then + compatibility_issues+=("系统完整性保护(SIP)可能阻止某些修改方法") + fi + + # 设置兼容性级别 + export MAC_COMPATIBILITY_LEVEL="$compatibility_level" + + # 显示兼容性评估结果 + case "$compatibility_level" in + "FULL") + log_info "✅ [兼容性] 完全兼容 - 支持所有MAC地址修改方法" + ;; + "PARTIAL") + log_warn "⚠️ [兼容性] 部分兼容 - 某些方法可能失败" + ;; + "LIMITED") + log_warn "⚠️ [兼容性] 有限兼容 - 大多数方法可能失败" + ;; + "MINIMAL") + log_error "❌ [兼容性] 最小兼容 - MAC地址修改可能完全失败" + ;; + esac + + if [ ${#compatibility_issues[@]} -gt 0 ]; then + log_info "📋 [兼容性问题]:" + for issue in "${compatibility_issues[@]}"; do + echo " • $issue" + done + fi + + # 返回兼容性状态 + case "$compatibility_level" in + "FULL"|"PARTIAL") return 0 ;; + *) return 1 ;; + esac +} + +# 🚀 增强的MAC地址修改函数,支持智能方法选择 +_change_mac_for_one_interface() { + local interface_name="$1" + + if [ -z "$interface_name" ]; then + log_error "❌ [错误] _change_mac_for_one_interface: 未提供接口名称" + return 1 + fi + + log_info "🚀 [开始] 开始处理接口: $interface_name" + echo + + # 🔍 环境检测和兼容性评估 + detect_macos_environment + local env_compatible=$? + local compatibility_level="$MAC_COMPATIBILITY_LEVEL" + + # 📡 获取当前MAC地址 + local current_mac=$(ifconfig "$interface_name" | awk '/ether/{print $2}') + if [ -z "$current_mac" ]; then + log_warn "⚠️ [警告] 无法获取接口 '$interface_name' 的当前MAC地址,可能已禁用或不存在" + return 1 + else + log_info "📍 [当前] 接口 '$interface_name' 当前MAC地址: $current_mac" + fi + + # 🎯 自动生成新MAC地址 + local random_mac=$(generate_local_unicast_mac) + log_info "🎲 [生成] 为接口 '$interface_name' 生成新MAC地址: $random_mac" + + # 📋 显示修改计划 + echo + log_info "📋 [计划] MAC地址修改计划:" + echo " 🔹 接口: $interface_name" + echo " 🔹 当前MAC: $current_mac" + echo " 🔹 目标MAC: $random_mac" + echo " 🔹 兼容性: $compatibility_level" + echo + + # 🔄 WiFi预处理 + manage_wifi_connection "disconnect" "$interface_name" + + # 🛠️ 执行MAC地址修改(多方法尝试) + local mac_change_success=false + local method_used="" + local methods_tried=() + + # 📊 根据兼容性级别选择方法顺序 + local method_order=() + case "$compatibility_level" in + "FULL") + method_order=("ifconfig" "third-party" "networksetup") + ;; + "PARTIAL") + method_order=("third-party" "ifconfig" "networksetup") + ;; + "LIMITED"|"MINIMAL") + method_order=("third-party" "networksetup" "ifconfig") + ;; + esac + + log_info "🛠️ [方法] 将按以下顺序尝试修改方法: ${method_order[*]}" + echo + + # 🔄 逐个尝试修改方法 + for method in "${method_order[@]}"; do + log_info "🔧 [尝试] 正在尝试 $method 方法..." + methods_tried+=("$method") + + case "$method" in + "ifconfig") + if _try_ifconfig_method "$interface_name" "$random_mac"; then + mac_change_success=true + method_used="ifconfig" + break + fi + ;; + "third-party") + if try_third_party_mac_tool "$interface_name" "$random_mac"; then + mac_change_success=true + method_used="third-party" + break + fi + ;; + "networksetup") + if _try_networksetup_method "$interface_name" "$random_mac"; then + mac_change_success=true + method_used="networksetup" + break + fi + ;; + esac + + log_warn "⚠️ [失败] $method 方法失败,尝试下一个方法..." + sleep 2 + done + + # 🔍 验证修改结果 + if [[ $mac_change_success == true ]]; then + log_info "🔍 [验证] 验证MAC地址修改结果..." + sleep 3 # 等待系统更新 + + local final_mac_check=$(ifconfig "$interface_name" | awk '/ether/{print $2}') + log_info "📍 [检查] 接口 '$interface_name' 最终MAC地址: $final_mac_check" + + if [ "$final_mac_check" == "$random_mac" ]; then + echo + log_info "🎉 [成功] MAC地址修改成功!" + echo " ✅ 使用方法: $method_used" + echo " ✅ 接口: $interface_name" + echo " ✅ 原MAC: $current_mac" + echo " ✅ 新MAC: $final_mac_check" + + # 🔄 WiFi后处理 + manage_wifi_connection "reconnect" "$interface_name" + + return 0 + else + log_warn "⚠️ [验证失败] MAC地址可能未生效或已被系统重置" + log_info "💡 [提示] 期望: $random_mac, 实际: $final_mac_check" + mac_change_success=false + fi + fi + + # ❌ 失败处理和用户选择 + if [[ $mac_change_success == false ]]; then + echo + log_error "❌ [失败] 所有MAC地址修改方法都失败了" + log_info "📋 [尝试过的方法]: ${methods_tried[*]}" + + # 🔄 WiFi恢复 + manage_wifi_connection "reconnect" "$interface_name" + + # 📊 显示故障排除信息 + _show_troubleshooting_info "$interface_name" + + # 🎯 提供用户选择 + echo + echo -e "${BLUE}💡 [说明]${NC} MAC地址修改失败,您可以选择:" + echo -e "${BLUE}💡 [备注]${NC} 如果所有接口都失败,脚本会自动尝试JS内核修改方案" + echo + + # 简化的用户选择 + echo "请选择操作:" + echo " 1. 重试本接口" + echo " 2. 跳过本接口" + echo " 3. 退出脚本" + + read -p "请输入选择 (1-3): " choice + + case "$choice" in + 1) + log_info "🔄 [重试] 用户选择重试本接口" + _change_mac_for_one_interface "$interface_name" + ;; + 2) + log_info "⏭️ [跳过] 用户选择跳过本接口" + return 1 + ;; + 3) + log_info "🚪 [退出] 用户选择退出脚本" + exit 1 + ;; + *) + log_info "⏭️ [默认] 无效选择,跳过本接口" + return 1 + ;; + esac + return 1 + fi +} + +# 🔧 增强的传统ifconfig方法(集成WiFi管理) +_try_ifconfig_method() { + local interface_name="$1" + local random_mac="$2" + + log_info "🔧 [ifconfig] 使用传统ifconfig方法修改MAC地址" + + # 🔄 WiFi特殊处理已在主函数中处理,这里只需要基本的接口操作 + log_info "📡 [接口] 临时禁用接口 '$interface_name' 以修改MAC地址..." + if ! sudo ifconfig "$interface_name" down 2>>"$LOG_FILE"; then + log_error "❌ [错误] 禁用接口 '$interface_name' 失败" + return 1 + fi + + log_info "⏳ [等待] 等待接口完全关闭..." + sleep 3 + + # 🎯 尝试修改MAC地址 + log_info "🎯 [修改] 设置新MAC地址: $random_mac" + if sudo ifconfig "$interface_name" ether "$random_mac" 2>>"$LOG_FILE"; then + log_info "✅ [成功] MAC地址设置命令执行成功" + + # 重新启用接口 + log_info "🔄 [启用] 重新启用接口..." + if sudo ifconfig "$interface_name" up 2>>"$LOG_FILE"; then + log_info "✅ [成功] 接口重新启用成功" + sleep 2 + return 0 + else + log_error "❌ [错误] 重新启用接口失败" + return 1 + fi + else + log_error "❌ [错误] ifconfig ether 命令失败" + log_info "🔄 [恢复] 尝试重新启用接口..." + sudo ifconfig "$interface_name" up 2>/dev/null || true + return 1 + fi +} + +# 🌐 增强的networksetup方法(适用于受限环境) +_try_networksetup_method() { + local interface_name="$1" + local random_mac="$2" + + log_info "🌐 [networksetup] 尝试使用系统网络偏好设置方法" + + # 🔍 获取硬件端口名称 + local hardware_port=$(networksetup -listallhardwareports | grep -A1 "Device: $interface_name" | grep "Hardware Port:" | cut -d: -f2 | xargs) + + if [ -z "$hardware_port" ]; then + log_warn "⚠️ [警告] 无法找到接口 $interface_name 对应的硬件端口" + log_info "📋 [调试] 可用硬件端口列表:" + networksetup -listallhardwareports | grep -E "(Hardware Port|Device)" | head -10 + return 1 + fi + + log_info "🔍 [发现] 找到硬件端口: '$hardware_port' (设备: $interface_name)" + + # 🎯 尝试多种networksetup方法 + local methods_tried=() + + # 方法1: 尝试重置网络服务 + log_info "🔧 [方法1] 尝试重置网络服务..." + methods_tried+=("reset-service") + if sudo networksetup -setnetworkserviceenabled "$hardware_port" off 2>>"$LOG_FILE"; then + sleep 2 + if sudo networksetup -setnetworkserviceenabled "$hardware_port" on 2>>"$LOG_FILE"; then + log_info "✅ [成功] 网络服务重置成功" + sleep 2 + + # 检测硬件变化 + sudo networksetup -detectnewhardware 2>>"$LOG_FILE" + sleep 3 + + # 验证是否有效果 + local new_mac=$(ifconfig "$interface_name" | awk '/ether/{print $2}') + if [ "$new_mac" != "$(ifconfig "$interface_name" | awk '/ether/{print $2}')" ]; then + log_info "✅ [成功] networksetup方法可能有效" + return 0 + fi + fi + fi + + # 方法2: 尝试手动配置 + log_info "🔧 [方法2] 尝试手动网络配置..." + methods_tried+=("manual-config") + + # 获取当前配置 + local current_config=$(networksetup -getinfo "$hardware_port" 2>/dev/null) + if [ -n "$current_config" ]; then + log_info "📋 [当前配置] $hardware_port 的网络配置:" + echo "$current_config" | head -5 + + # 尝试重新应用配置以触发MAC地址更新 + if echo "$current_config" | grep -q "DHCP"; then + log_info "🔄 [DHCP] 重新应用DHCP配置..." + if sudo networksetup -setdhcp "$hardware_port" 2>>"$LOG_FILE"; then + log_info "✅ [成功] DHCP配置重新应用成功" + sleep 3 + sudo networksetup -detectnewhardware 2>>"$LOG_FILE" + return 0 + fi + fi + fi + + # 方法3: 强制硬件重新检测 + log_info "🔧 [方法3] 强制硬件重新检测..." + methods_tried+=("hardware-detect") + + if sudo networksetup -detectnewhardware 2>>"$LOG_FILE"; then + log_info "✅ [成功] 硬件重新检测完成" + sleep 3 + return 0 + fi + + # 所有方法都失败 + log_error "❌ [失败] networksetup所有方法都失败" + log_info "📋 [尝试过的方法]: ${methods_tried[*]}" + log_warn "⚠️ [说明] networksetup方法在当前macOS版本中可能不支持直接MAC地址修改" + + return 1 +} + +# 📊 增强的故障排除信息显示 +_show_troubleshooting_info() { + local interface_name="$1" + + echo + echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${YELLOW}║ MAC地址修改故障排除信息 ║${NC}" + echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" + echo + + # 🔍 系统信息 + echo -e "${BLUE}🔍 系统环境信息:${NC}" + echo " 📱 macOS版本: $MACOS_VERSION" + echo " 💻 硬件类型: $HARDWARE_TYPE" + echo " 🔒 SIP状态: $SIP_STATUS" + echo " 🌐 接口名称: $interface_name" + echo " 📊 兼容性级别: ${MAC_COMPATIBILITY_LEVEL:-未知}" + + # 显示接口详细信息 + local interface_info=$(ifconfig "$interface_name" 2>/dev/null | head -3) + if [ -n "$interface_info" ]; then + echo " 📡 接口状态:" + echo "$interface_info" | sed 's/^/ /' + fi + echo + + # ⚠️ 问题分析 + echo -e "${BLUE}⚠️ 可能的问题原因:${NC}" + local issues_found=false + + if [[ "$HARDWARE_TYPE" == "Apple Silicon" ]] && [[ $MACOS_MAJOR -ge 12 ]]; then + echo " ❌ Apple Silicon Mac在macOS 12+版本中有硬件级MAC地址修改限制" + echo " ❌ 网络驱动程序可能完全禁止MAC地址修改" + issues_found=true + fi + + if [[ $MACOS_MAJOR -ge 14 ]]; then + echo " ❌ macOS Sonoma (14+) 对MAC地址修改有严格的系统级限制" + issues_found=true + elif [[ $MACOS_MAJOR -ge 12 ]]; then + echo " ⚠️ macOS Monterey+ 对MAC地址修改有部分限制" + issues_found=true + fi + + if [[ "$SIP_STATUS" == "enabled" ]]; then + echo " ⚠️ 系统完整性保护(SIP)可能阻止某些MAC地址修改方法" + issues_found=true + fi + + if ! $issues_found; then + echo " ❓ 网络接口可能不支持MAC地址修改" + echo " ❓ 权限不足或其他系统安全策略限制" + fi + echo + + # 💡 解决方案 + echo -e "${BLUE}💡 建议的解决方案:${NC}" + echo + echo -e "${GREEN} 🛠️ 方案1: 安装第三方工具${NC}" + echo " brew install spoof-mac" + echo " brew install macchanger" + echo " # 这些工具可能使用不同的底层方法" + echo + + if [[ "$HARDWARE_TYPE" == "Apple Silicon" ]] || [[ $MACOS_MAJOR -ge 14 ]]; then + echo -e "${GREEN} 🔧 方案2: 使用Cursor JS内核修改 (推荐)${NC}" + echo " # 本脚本会自动尝试JS内核修改方案" + echo " # 直接修改Cursor内核文件绕过系统MAC检测" + echo + fi + + echo -e "${GREEN} 🌐 方案3: 网络层解决方案${NC}" + echo " • 使用虚拟机运行需要MAC地址修改的应用" + echo " • 配置路由器级别的MAC地址过滤绕过" + echo " • 使用VPN或代理服务" + echo + + if [[ "$SIP_STATUS" == "enabled" ]]; then + echo -e "${YELLOW} ⚠️ 方案4: 临时禁用SIP (高风险,不推荐)${NC}" + echo " 1. 重启进入恢复模式 (Command+R)" + echo " 2. 打开终端运行: csrutil disable" + echo " 3. 重启后尝试修改MAC地址" + echo " 4. 完成后重新启用: csrutil enable" + echo " ⚠️ 警告: 禁用SIP会降低系统安全性" + echo + fi + + # 🔧 技术细节 + echo -e "${BLUE}🔧 技术细节和错误分析:${NC}" + echo " 📋 常见错误信息:" + echo " • ifconfig: ioctl (SIOCAIFADDR): Can't assign requested address" + echo " • Operation not permitted" + echo " • Device or resource busy" + echo + echo " 🔍 错误含义:" + echo " • 系统内核拒绝了MAC地址修改请求" + echo " • 硬件驱动程序不允许MAC地址更改" + echo " • 安全策略阻止了网络接口修改" + echo + + if [[ "$HARDWARE_TYPE" == "Apple Silicon" ]]; then + echo " 🍎 Apple Silicon特殊说明:" + echo " • 硬件级别的安全限制,无法通过软件绕过" + echo " • 网络芯片固件可能锁定了MAC地址" + echo " • 建议使用应用层解决方案(如JS内核修改)" + echo + fi + + echo -e "${BLUE}📞 获取更多帮助:${NC}" + echo " • 查看系统日志: sudo dmesg | grep -i network" + echo " • 检查网络接口: networksetup -listallhardwareports" + echo " • 测试权限: sudo ifconfig $interface_name" + echo +} + +# 检查权限 +check_permissions() { + if [ "$EUID" -ne 0 ]; then + log_error "请使用 sudo 运行此脚本" + echo "示例: sudo $0" + exit 1 + fi +} + +# 检查并关闭 Cursor 进程(保存进程信息) +check_and_kill_cursor() { + log_info "🔍 [检查] 检查 Cursor 进程..." + + local attempt=1 + local max_attempts=5 + + # 💾 保存Cursor进程路径 + CURSOR_PROCESS_PATH="/Applications/Cursor.app/Contents/MacOS/Cursor" + + # 函数:获取进程详细信息 + get_process_details() { + local process_name="$1" + log_debug "正在获取 $process_name 进程详细信息:" + ps aux | grep -i "/Applications/Cursor.app" | grep -v grep + } + + while [ $attempt -le $max_attempts ]; do + # 使用更精确的匹配来获取 Cursor 进程 + CURSOR_PIDS=$(ps aux | grep -i "/Applications/Cursor.app" | grep -v grep | awk '{print $2}') + + if [ -z "$CURSOR_PIDS" ]; then + log_info "💡 [提示] 未发现运行中的 Cursor 进程" + # 确认Cursor应用路径存在 + if [ -f "$CURSOR_PROCESS_PATH" ]; then + log_info "💾 [保存] 已保存Cursor路径: $CURSOR_PROCESS_PATH" + else + log_warn "⚠️ [警告] 未找到Cursor应用,请确认已安装" + fi + return 0 + fi + + log_warn "⚠️ [警告] 发现 Cursor 进程正在运行" + # 💾 保存进程信息 + log_info "💾 [保存] 已保存Cursor路径: $CURSOR_PROCESS_PATH" + get_process_details "cursor" + + log_warn "🔄 [操作] 尝试关闭 Cursor 进程..." + + if [ $attempt -eq $max_attempts ]; then + log_warn "💥 [强制] 尝试强制终止进程..." + kill -9 $CURSOR_PIDS 2>/dev/null || true + else + kill $CURSOR_PIDS 2>/dev/null || true + fi + + sleep 3 + + # 同样使用更精确的匹配来检查进程是否还在运行 + if ! ps aux | grep -i "/Applications/Cursor.app" | grep -v grep > /dev/null; then + log_info "✅ [成功] Cursor 进程已成功关闭" + return 0 + fi + + log_warn "⏳ [等待] 等待进程关闭,尝试 $attempt/$max_attempts..." + ((attempt++)) + done + + log_error "❌ [错误] 在 $max_attempts 次尝试后仍无法关闭 Cursor 进程" + get_process_details "cursor" + log_error "💥 [错误] 请手动关闭进程后重试" + exit 1 +} + +# 备份配置文件 +backup_config() { + if [ ! -f "$STORAGE_FILE" ]; then + log_warn "配置文件不存在,跳过备份" + return 0 + fi + + mkdir -p "$BACKUP_DIR" + local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)" + + if cp "$STORAGE_FILE" "$backup_file"; then + chmod 644 "$backup_file" + chown "$CURRENT_USER" "$backup_file" + log_info "配置已备份到: $backup_file" + else + log_error "备份失败" + exit 1 + fi +} + +# 🔧 修改Cursor内核JS文件实现设备识别绕过(新增核心功能) +modify_cursor_js_files() { + log_info "🔧 [内核修改] 开始修改Cursor内核JS文件实现设备识别绕过..." + echo + + # 检查Cursor应用是否存在 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log_error "❌ [错误] 未找到Cursor应用: $CURSOR_APP_PATH" + return 1 + fi + + # 生成新的设备标识符 + local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') + local machine_id="auth0|user_$(openssl rand -hex 16)" + local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + local mac_machine_id=$(openssl rand -hex 32) + + log_info "🔑 [生成] 已生成新的设备标识符" + + # 目标JS文件列表 + local js_files=( + "$CURSOR_APP_PATH/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "$CURSOR_APP_PATH/Contents/Resources/app/out/main.js" + "$CURSOR_APP_PATH/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" + ) + + local modified_count=0 + local need_modification=false + + # 检查是否需要修改 + log_info "🔍 [检查] 检查JS文件修改状态..." + for file in "${js_files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "⚠️ [警告] 文件不存在: ${file/$CURSOR_APP_PATH\//}" + continue + fi + + if ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then + log_info "📝 [需要] 文件需要修改: ${file/$CURSOR_APP_PATH\//}" + need_modification=true + break + else + log_info "✅ [已修改] 文件已修改: ${file/$CURSOR_APP_PATH\//}" + fi + done + + if [ "$need_modification" = false ]; then + log_info "✅ [跳过] 所有JS文件已经被修改过,无需重复操作" + return 0 + fi + + # 关闭Cursor进程 + log_info "🔄 [关闭] 关闭Cursor进程以进行文件修改..." + check_and_kill_cursor + + # 创建备份 + local timestamp=$(date +%Y%m%d_%H%M%S) + local backup_app="/tmp/Cursor.app.backup_js_${timestamp}" + + log_info "💾 [备份] 创建Cursor应用备份..." + if ! cp -R "$CURSOR_APP_PATH" "$backup_app"; then + log_error "❌ [错误] 创建备份失败" + return 1 + fi + + log_info "✅ [备份] 备份创建成功: $backup_app" + + # 修改JS文件 + log_info "🔧 [修改] 开始修改JS文件..." + + for file in "${js_files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "⚠️ [跳过] 文件不存在: ${file/$CURSOR_APP_PATH\//}" + continue + fi + + log_info "📝 [处理] 正在处理: ${file/$CURSOR_APP_PATH\//}" + + # 检查是否已经修改过 + if grep -q "return crypto.randomUUID()" "$file" || grep -q "// Cursor ID 修改工具注入" "$file"; then + log_info "✅ [跳过] 文件已经被修改过" + ((modified_count++)) + continue + fi + + # 方法1: 查找IOPlatformUUID相关函数 + if grep -q "IOPlatformUUID" "$file"; then + log_info "🔍 [发现] 找到IOPlatformUUID关键字" + + # 针对不同的函数模式进行修改 + if grep -q "function a\$" "$file"; then + if sed -i.tmp 's/function a\$(t){switch/function a\$(t){return crypto.randomUUID(); switch/' "$file"; then + log_info "✅ [成功] 修改a$函数成功" + ((modified_count++)) + continue + fi + fi + + # 通用注入方法 - ES模块兼容版本 + local inject_code=" +// Cursor ID 修改工具注入 - $(date) - ES模块兼容版本 +import crypto from 'crypto'; + +// 保存原始函数引用 +const originalRandomUUID_$(date +%s) = crypto.randomUUID; + +// 重写crypto.randomUUID方法 +crypto.randomUUID = function() { + return '${new_uuid}'; +}; + +// 覆盖所有可能的系统ID获取函数 - ES模块兼容版本 +globalThis.getMachineId = function() { return '${machine_id}'; }; +globalThis.getDeviceId = function() { return '${device_id}'; }; +globalThis.macMachineId = '${mac_machine_id}'; + +// 确保在不同环境下都能访问 +if (typeof window !== 'undefined') { + window.getMachineId = globalThis.getMachineId; + window.getDeviceId = globalThis.getDeviceId; + window.macMachineId = globalThis.macMachineId; +} + +// 确保模块顶层执行 +console.log('Cursor设备标识符已成功劫持 - ES模块版本 煎饼果子(86) 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬)'); +" + + # 替换变量 + inject_code=${inject_code//\$\{new_uuid\}/$new_uuid} + inject_code=${inject_code//\$\{machine_id\}/$machine_id} + inject_code=${inject_code//\$\{device_id\}/$device_id} + inject_code=${inject_code//\$\{mac_machine_id\}/$mac_machine_id} + + # 注入代码到文件开头 + echo "$inject_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + log_info "✅ [成功] 通用注入方法修改成功" + ((modified_count++)) + + # 方法2: 查找其他设备ID相关函数 + elif grep -q "function t\$()" "$file" || grep -q "async function y5" "$file"; then + log_info "🔍 [发现] 找到设备ID相关函数" + + # 修改MAC地址获取函数 + if grep -q "function t\$()" "$file"; then + sed -i.tmp 's/function t\$(){/function t\$(){return "00:00:00:00:00:00";/' "$file" + log_info "✅ [成功] 修改MAC地址获取函数" + fi + + # 修改设备ID获取函数 + if grep -q "async function y5" "$file"; then + sed -i.tmp 's/async function y5(t){/async function y5(t){return crypto.randomUUID();/' "$file" + log_info "✅ [成功] 修改设备ID获取函数" + fi + + ((modified_count++)) + else + log_warn "⚠️ [警告] 未找到已知的设备ID函数模式,跳过此文件" + fi + done + + if [ $modified_count -gt 0 ]; then + log_info "🎉 [完成] 成功修改 $modified_count 个JS文件" + log_info "💾 [备份] 原始文件备份位置: $backup_app" + return 0 + else + log_error "❌ [失败] 没有成功修改任何文件" + return 1 + fi +} + +# 增强的系统MAC地址修改函数,支持多种兼容性检测和修改方法 +change_system_mac_address() { + log_info "开始尝试修改所有活动的 Wi-Fi/Ethernet 接口的系统 MAC 地址..." + echo + + # 环境兼容性预检查 + detect_macos_environment + local env_compatible=$? + + if [[ $env_compatible -ne 0 ]]; then + echo -e "${YELLOW}⚠️ [兼容性警告]${NC} 检测到可能存在MAC地址修改限制的环境:" + echo -e "${YELLOW} • macOS版本: $MACOS_VERSION${NC}" + echo -e "${YELLOW} • 硬件类型: $HARDWARE_TYPE${NC}" + echo -e "${YELLOW} • SIP状态: $SIP_STATUS${NC}" + echo + echo -e "${BLUE}💡 [建议]${NC} 在此环境中,传统的ifconfig方法可能失败。" + echo -e "${BLUE} 脚本将自动尝试多种兼容性方法,包括第三方工具。${NC}" + echo + + # 检查第三方工具可用性 + local tools_available=false + if command -v spoof-mac >/dev/null 2>&1; then + echo -e "${GREEN}✅ 检测到 spoof-mac 工具${NC}" + tools_available=true + fi + if command -v macchanger >/dev/null 2>&1; then + echo -e "${GREEN}✅ 检测到 macchanger 工具${NC}" + tools_available=true + fi + + if [[ $tools_available == false ]]; then + echo -e "${YELLOW}⚠️ 未检测到第三方MAC修改工具${NC}" + echo -e "${BLUE}💡 建议安装: brew install spoof-mac 或 brew install macchanger${NC}" + echo + + # 🔧 Apple Silicon智能替代方案 + if [[ "$HARDWARE_TYPE" == "Apple Silicon" ]]; then + echo -e "${BLUE}🔧 [智能方案]${NC} 检测到Apple Silicon环境,MAC地址修改受硬件限制" + echo -e "${BLUE}💡 [自动切换]${NC} 将自动使用JS内核修改实现更直接的设备识别绕过" + echo + + log_info "🔄 [智能切换] 自动切换到JS内核修改方案..." + if modify_cursor_js_files; then + log_info "✅ [成功] JS内核修改完成,已实现设备识别绕过" + log_info "💡 [说明] 此方案比MAC地址修改更直接有效,完美适配Apple Silicon" + return 0 + else + log_warn "⚠️ [警告] JS内核修改失败,将继续尝试MAC地址修改" + fi + fi + + # 非Apple Silicon环境或JS修改失败时,询问是否继续MAC地址修改 + read -p "是否继续尝试MAC地址修改?(y/n): " continue_choice + if [[ ! "$continue_choice" =~ ^(y|yes)$ ]]; then + log_info "用户选择跳过MAC地址修改" + return 1 + fi + fi + fi + + echo -e "${YELLOW}[警告]${NC} 即将尝试修改您所有活动的 Wi-Fi 或以太网接口的 MAC 地址。" + echo -e "${YELLOW}[警告]${NC} 此更改是 ${RED}临时${NC} 的,将在您重启 Mac 后恢复为原始地址。" + echo -e "${YELLOW}[警告]${NC} 修改 MAC 地址可能会导致临时的网络中断或连接问题。" + echo -e "${YELLOW}[警告]${NC} 请确保您了解相关风险。此操作主要影响本地网络识别,而非互联网身份。" + echo + + local active_interfaces=() + local potential_interfaces=() + local default_route_interface="" + + # 0. 尝试获取默认路由接口,作为后备 + log_info "尝试通过路由表获取默认网络接口 (用于后备)..." + default_route_interface=$(route get default | grep 'interface:' | awk '{print $2}') + if [ -n "$default_route_interface" ]; then + log_info "检测到默认路由接口 (后备): $default_route_interface" + else + log_warn "未能通过路由表获取默认接口 (后备)。" + fi + + # 1. 获取所有 Wi-Fi 和 Ethernet 接口名称 + log_info "正在检测 Wi-Fi 和 Ethernet 接口..." + while IFS= read -r line; do + if [[ $line == "Hardware Port: Wi-Fi" || $line == "Hardware Port: Ethernet" ]]; then + read -r dev_line # 读取下一行 Device: enX + device=$(echo "$dev_line" | awk '{print $2}') + if [ -n "$device" ]; then + log_debug "检测到潜在接口: $device ($line)" + potential_interfaces+=("$device") + fi + fi + done < <(networksetup -listallhardwareports) + + if [ ${#potential_interfaces[@]} -eq 0 ]; then + log_warn "未能通过 networksetup 检测到任何 Wi-Fi 或 Ethernet 接口。" + # 检查是否有路由表接口作为后备 + if [ -n "$default_route_interface" ]; then + log_warn "将使用路由表检测到的接口 '$default_route_interface' 作为后备。" + potential_interfaces+=("$default_route_interface") + else + log_warn "路由表也未能提供后备接口。" + # 在此情况下,potential_interfaces 仍为空,后续逻辑会处理 + fi + fi + + # 2. 检查哪些接口是活动的 + log_info "正在检查接口活动状态..." + for interface_name in "${potential_interfaces[@]}"; do + log_debug "检查接口 '$interface_name' 状态..." + if ifconfig "$interface_name" 2>/dev/null | grep -q "status: active"; then + log_info "发现活动接口: $interface_name" + active_interfaces+=("$interface_name") + else + log_debug "接口 '$interface_name' 非活动或不存在。" + fi + done + + # 3. 检查是否找到活动接口 + if [ ${#active_interfaces[@]} -eq 0 ]; then + log_warn "未找到任何活动的 Wi-Fi 或 Ethernet 接口可供修改 MAC 地址。" + echo -e "${YELLOW}未找到活动的 Wi-Fi 或 Ethernet 接口。跳过 MAC 地址修改。${NC}" + return 1 # 返回错误码,表示没有接口被修改 + fi + + log_info "将尝试为以下活动接口修改 MAC 地址: ${active_interfaces[*]}" + echo + + # 4. 🚀 循环处理找到的活动接口(增强版) + local overall_success=true + local successful_interfaces=() + local failed_interfaces=() + + echo -e "${BLUE}🚀 [开始] 开始处理 ${#active_interfaces[@]} 个活动接口...${NC}" + echo + + # 处理每个接口 + for i in "${!active_interfaces[@]}"; do + local interface_name="${active_interfaces[$i]}" + local interface_num=$((i + 1)) + + echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${YELLOW}║ 处理接口 $interface_num/${#active_interfaces[@]}: $interface_name ║${NC}" + echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" + echo + + if _change_mac_for_one_interface "$interface_name"; then + log_info "✅ [成功] 接口 '$interface_name' MAC地址修改成功" + successful_interfaces+=("$interface_name") + else + log_warn "⚠️ [失败] 接口 '$interface_name' MAC地址修改失败" + failed_interfaces+=("$interface_name") + overall_success=false + fi + + echo + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo + done + + # 📊 显示处理结果统计 + echo -e "${BLUE}📊 [统计] MAC地址修改结果统计:${NC}" + echo " ✅ 成功: ${#successful_interfaces[@]} 个接口" + if [ ${#successful_interfaces[@]} -gt 0 ]; then + for interface in "${successful_interfaces[@]}"; do + echo " • $interface" + done + fi + echo " ❌ 失败: ${#failed_interfaces[@]} 个接口" + if [ ${#failed_interfaces[@]} -gt 0 ]; then + for interface in "${failed_interfaces[@]}"; do + echo " • $interface" + done + fi + echo + + log_info "📋 [完成] 所有活动接口的MAC地址修改尝试完成" + + if $overall_success; then + return 0 # 所有尝试都成功 + else + # 🔧 MAC地址修改失败时自动切换到JS内核修改 + echo + log_warn "⚠️ [警告] MAC地址修改失败或部分失败" + log_info "🔧 [智能切换] 自动切换到JS内核修改方案..." + log_info "💡 [说明] JS内核修改直接修改Cursor设备检测逻辑,绕过效果更好" + + if modify_cursor_js_files; then + log_info "✅ [成功] JS内核修改完成,已实现设备识别绕过" + log_info "💡 [结果] 虽然MAC地址修改失败,但JS内核修改提供了更直接的解决方案" + return 0 + else + log_error "❌ [失败] JS内核修改也失败了" + log_error "💥 [严重] 所有设备识别绕过方案都失败了" + return 1 + fi + fi +} + + + + +# 修改现有文件 +modify_or_add_config() { + local key="$1" + local value="$2" + local file="$3" + + if [ ! -f "$file" ]; then + log_error "文件不存在: $file" + return 1 + fi + + # 确保文件可写 + chmod 644 "$file" || { + log_error "无法修改文件权限: $file" + return 1 + } + + # 创建临时文件 + local temp_file=$(mktemp) + + # 检查key是否存在 + if grep -q "\"$key\":" "$file"; then + # key存在,执行替换 + sed "s/\"$key\":[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$file" > "$temp_file" || { + log_error "修改配置失败: $key" + rm -f "$temp_file" + return 1 + } + else + # key不存在,添加新的key-value对 + sed "s/}$/,\n \"$key\": \"$value\"\n}/" "$file" > "$temp_file" || { + log_error "添加配置失败: $key" + rm -f "$temp_file" + return 1 + } + fi + + # 检查临时文件是否为空 + if [ ! -s "$temp_file" ]; then + log_error "生成的临时文件为空" + rm -f "$temp_file" + return 1 + fi + + # 使用 cat 替换原文件内容 + cat "$temp_file" > "$file" || { + log_error "无法写入文件: $file" + rm -f "$temp_file" + return 1 + } + + rm -f "$temp_file" + + # 恢复文件权限 + chmod 444 "$file" + + return 0 +} + +# 清理 Cursor 之前的修改 +clean_cursor_app() { + log_info "尝试清理 Cursor 之前的修改..." + + # 如果存在备份,直接恢复备份 + local latest_backup="" + + # 查找最新的备份 + latest_backup=$(find /tmp -name "Cursor.app.backup_*" -type d -print 2>/dev/null | sort -r | head -1) + + if [ -n "$latest_backup" ] && [ -d "$latest_backup" ]; then + log_info "找到现有备份: $latest_backup" + log_info "正在恢复原始版本..." + + # 停止 Cursor 进程 + check_and_kill_cursor + + # 恢复备份 + sudo rm -rf "$CURSOR_APP_PATH" + sudo cp -R "$latest_backup" "$CURSOR_APP_PATH" + sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH" + sudo chmod -R 755 "$CURSOR_APP_PATH" + + log_info "已恢复原始版本" + return 0 + else + log_warn "未找到现有备份,尝试重新安装 Cursor..." + echo "您可以从 https://cursor.sh 下载并重新安装 Cursor" + echo "或者继续执行此脚本,将尝试修复现有安装" + + # 可以在这里添加重新下载和安装的逻辑 + return 1 + fi +} + +# 修改 Cursor 主程序文件(安全模式) +modify_cursor_app_files() { + log_info "正在安全修改 Cursor 主程序文件..." + log_info "详细日志将记录到: $LOG_FILE" + + # 先清理之前的修改 + clean_cursor_app + + # 验证应用是否存在 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log_error "未找到 Cursor.app,请确认安装路径: $CURSOR_APP_PATH" + return 1 + fi + + # 定义目标文件 - 将extensionHostProcess.js放在最前面优先处理 + local target_files=( + "${CURSOR_APP_PATH}/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "${CURSOR_APP_PATH}/Contents/Resources/app/out/main.js" + "${CURSOR_APP_PATH}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" + ) + + # 检查文件是否存在并且是否已修改 + local need_modification=false + local missing_files=false + + log_debug "检查目标文件..." + for file in "${target_files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "文件不存在: ${file/$CURSOR_APP_PATH\//}" + echo "[FILE_CHECK] 文件不存在: $file" >> "$LOG_FILE" + missing_files=true + continue + fi + + echo "[FILE_CHECK] 文件存在: $file ($(wc -c < "$file") 字节)" >> "$LOG_FILE" + + if ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then + log_info "文件需要修改: ${file/$CURSOR_APP_PATH\//}" + grep -n "IOPlatformUUID" "$file" | head -3 >> "$LOG_FILE" || echo "[FILE_CHECK] 未找到 IOPlatformUUID" >> "$LOG_FILE" + need_modification=true + break + else + log_info "文件已修改: ${file/$CURSOR_APP_PATH\//}" + fi + done + + # 如果所有文件都已修改或不存在,则退出 + if [ "$missing_files" = true ]; then + log_error "部分目标文件不存在,请确认 Cursor 安装是否完整" + return 1 + fi + + if [ "$need_modification" = false ]; then + log_info "所有目标文件已经被修改过,无需重复操作" + return 0 + fi + + # 创建临时工作目录 + local timestamp=$(date +%Y%m%d_%H%M%S) + local temp_dir="/tmp/cursor_reset_${timestamp}" + local temp_app="${temp_dir}/Cursor.app" + local backup_app="/tmp/Cursor.app.backup_${timestamp}" + + log_debug "创建临时目录: $temp_dir" + echo "[TEMP_DIR] 创建临时目录: $temp_dir" >> "$LOG_FILE" + + # 清理可能存在的旧临时目录 + if [ -d "$temp_dir" ]; then + log_info "清理已存在的临时目录..." + rm -rf "$temp_dir" + fi + + # 创建新的临时目录 + mkdir -p "$temp_dir" || { + log_error "无法创建临时目录: $temp_dir" + echo "[ERROR] 无法创建临时目录: $temp_dir" >> "$LOG_FILE" + return 1 + } + + # 备份原应用 + log_info "备份原应用..." + echo "[BACKUP] 开始备份: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE" + + cp -R "$CURSOR_APP_PATH" "$backup_app" || { + log_error "无法创建应用备份" + echo "[ERROR] 备份失败: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE" + rm -rf "$temp_dir" + return 1 + } + + echo "[BACKUP] 备份完成" >> "$LOG_FILE" + + # 复制应用到临时目录 + log_info "创建临时工作副本..." + echo "[COPY] 开始复制: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE" + + cp -R "$CURSOR_APP_PATH" "$temp_dir" || { + log_error "无法复制应用到临时目录" + echo "[ERROR] 复制失败: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE" + rm -rf "$temp_dir" "$backup_app" + return 1 + } + + echo "[COPY] 复制完成" >> "$LOG_FILE" + + # 确保临时目录的权限正确 + chown -R "$CURRENT_USER:staff" "$temp_dir" + chmod -R 755 "$temp_dir" + + # 移除签名(增强兼容性) + log_info "移除应用签名..." + echo "[CODESIGN] 移除签名: $temp_app" >> "$LOG_FILE" + + codesign --remove-signature "$temp_app" 2>> "$LOG_FILE" || { + log_warn "移除应用签名失败" + echo "[WARN] 移除签名失败: $temp_app" >> "$LOG_FILE" + } + + # 移除所有相关组件的签名 + local components=( + "$temp_app/Contents/Frameworks/Cursor Helper.app" + "$temp_app/Contents/Frameworks/Cursor Helper (GPU).app" + "$temp_app/Contents/Frameworks/Cursor Helper (Plugin).app" + "$temp_app/Contents/Frameworks/Cursor Helper (Renderer).app" + ) + + for component in "${components[@]}"; do + if [ -e "$component" ]; then + log_info "正在移除签名: $component" + codesign --remove-signature "$component" || { + log_warn "移除组件签名失败: $component" + } + fi + done + + # 修改目标文件 - 优先处理js文件 + local modified_count=0 + local files=( + "${temp_app}/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "${temp_app}/Contents/Resources/app/out/main.js" + "${temp_app}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" + ) + + for file in "${files[@]}"; do + if [ ! -f "$file" ]; then + log_warn "文件不存在: ${file/$temp_dir\//}" + continue + fi + + log_debug "处理文件: ${file/$temp_dir\//}" + echo "[PROCESS] 开始处理文件: $file" >> "$LOG_FILE" + echo "[PROCESS] 文件大小: $(wc -c < "$file") 字节" >> "$LOG_FILE" + + # 输出文件部分内容到日志 + echo "[FILE_CONTENT] 文件头部 100 行:" >> "$LOG_FILE" + head -100 "$file" 2>/dev/null | grep -v "^$" | head -50 >> "$LOG_FILE" + echo "[FILE_CONTENT] ..." >> "$LOG_FILE" + + # 创建文件备份 + cp "$file" "${file}.bak" || { + log_error "无法创建文件备份: ${file/$temp_dir\//}" + echo "[ERROR] 无法创建文件备份: $file" >> "$LOG_FILE" + continue + } + + # 使用 sed 替换而不是字符串操作 + if [[ "$file" == *"extensionHostProcess.js"* ]]; then + log_debug "处理 extensionHostProcess.js 文件..." + echo "[PROCESS_DETAIL] 开始处理 extensionHostProcess.js 文件" >> "$LOG_FILE" + + # 检查是否包含目标代码 + if grep -q 'i.header.set("x-cursor-checksum' "$file"; then + log_debug "找到 x-cursor-checksum 设置代码" + echo "[FOUND] 找到 x-cursor-checksum 设置代码" >> "$LOG_FILE" + + # 记录匹配的行到日志 + grep -n 'i.header.set("x-cursor-checksum' "$file" >> "$LOG_FILE" + + # 执行特定的替换 + if sed -i.tmp 's/i\.header\.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${e}`)/i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${p}`)/' "$file"; then + log_info "成功修改 x-cursor-checksum 设置代码" + echo "[SUCCESS] 成功完成 x-cursor-checksum 设置代码替换" >> "$LOG_FILE" + # 记录修改后的行 + grep -n 'i.header.set("x-cursor-checksum' "$file" >> "$LOG_FILE" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 x-cursor-checksum 设置代码失败" + cp "${file}.bak" "$file" + fi + else + log_warn "未找到 x-cursor-checksum 设置代码" + echo "[FILE_CHECK] 未找到 x-cursor-checksum 设置代码" >> "$LOG_FILE" + + # 记录文件部分内容到日志以便排查 + echo "[FILE_CONTENT] 文件中包含 'header.set' 的行:" >> "$LOG_FILE" + grep -n "header.set" "$file" | head -20 >> "$LOG_FILE" + + echo "[FILE_CONTENT] 文件中包含 'checksum' 的行:" >> "$LOG_FILE" + grep -n "checksum" "$file" | head -20 >> "$LOG_FILE" + fi + + echo "[PROCESS_DETAIL] 完成处理 extensionHostProcess.js 文件" >> "$LOG_FILE" + elif grep -q "IOPlatformUUID" "$file"; then + log_debug "找到 IOPlatformUUID 关键字" + echo "[FOUND] 找到 IOPlatformUUID 关键字" >> "$LOG_FILE" + grep -n "IOPlatformUUID" "$file" | head -5 >> "$LOG_FILE" + + # 定位 IOPlatformUUID 相关函数 + if grep -q "function a\$" "$file"; then + # 检查是否已经修改过 + if grep -q "return crypto.randomUUID()" "$file"; then + log_info "文件已经包含 randomUUID 调用,跳过修改" + ((modified_count++)) + continue + fi + + # 针对 main.js 中发现的代码结构进行修改 + if sed -i.tmp 's/function a\$(t){switch/function a\$(t){return crypto.randomUUID(); switch/' "$file"; then + log_debug "成功注入 randomUUID 调用到 a\$ 函数" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 a\$ 函数失败" + cp "${file}.bak" "$file" + fi + elif grep -q "async function v5" "$file"; then + # 检查是否已经修改过 + if grep -q "return crypto.randomUUID()" "$file"; then + log_info "文件已经包含 randomUUID 调用,跳过修改" + ((modified_count++)) + continue + fi + + # 替代方法 - 修改 v5 函数 + if sed -i.tmp 's/async function v5(t){let e=/async function v5(t){return crypto.randomUUID(); let e=/' "$file"; then + log_debug "成功注入 randomUUID 调用到 v5 函数" + ((modified_count++)) + log_info "成功修改文件: ${file/$temp_dir\//}" + else + log_error "修改 v5 函数失败" + cp "${file}.bak" "$file" + fi + else + # 检查是否已经注入了自定义代码 + if grep -q "// Cursor ID 修改工具注入" "$file"; then + log_info "文件已经包含自定义注入代码,跳过修改" + ((modified_count++)) + continue + fi + + # 新增检查:检查是否已存在 randomDeviceId_ 时间戳模式 + if grep -q "const randomDeviceId_[0-9]\\{10,\\}" "$file"; then + log_info "文件已经包含 randomDeviceId_ 模式,跳过通用注入" + echo "[FOUND] 文件已包含 randomDeviceId_ 模式,跳过通用注入: $file" >> "$LOG_FILE" + ((modified_count++)) # 计为已修改,防止后续尝试其他方法 + continue + fi + + # 使用更通用的注入方法 + log_warn "未找到具体函数,尝试使用通用修改方法" + inject_code=" +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) - ES模块兼容版本 +// 随机设备ID生成器注入 - $(date +%s) +import crypto from 'crypto'; + +const randomDeviceId_$(date +%s) = () => { + try { + return crypto.randomUUID(); + } catch (e) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = Math.random() * 16 | 0; + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } +}; +" + # 将代码注入到文件开头 + echo "$inject_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + # 替换调用点 + sed -i.tmp 's/await v5(!1)/randomDeviceId_'"$(date +%s)"'()/g' "$file" + sed -i.tmp 's/a\$(t)/randomDeviceId_'"$(date +%s)"'()/g' "$file" + + log_debug "完成通用修改" + ((modified_count++)) + log_info "使用通用方法成功修改文件: ${file/$temp_dir\//}" + fi + else + # 未找到 IOPlatformUUID,可能是文件结构变化 + log_warn "未找到 IOPlatformUUID,尝试替代方法" + + # 检查是否已经注入或修改过 + if grep -q "return crypto.randomUUID()" "$file" || grep -q "// Cursor ID 修改工具注入" "$file"; then + log_info "文件已经被修改过,跳过修改" + ((modified_count++)) + continue + fi + + # 尝试找其他关键函数如 getMachineId 或 getDeviceId + if grep -q "function t\$()" "$file" || grep -q "async function y5" "$file"; then + log_debug "找到设备ID相关函数" + + # 修改 MAC 地址获取函数 + if grep -q "function t\$()" "$file"; then + sed -i.tmp 's/function t\$(){/function t\$(){return "00:00:00:00:00:00";/' "$file" + log_debug "修改 MAC 地址获取函数成功" + fi + + # 修改设备ID获取函数 + if grep -q "async function y5" "$file"; then + sed -i.tmp 's/async function y5(t){/async function y5(t){return crypto.randomUUID();/' "$file" + log_debug "修改设备ID获取函数成功" + fi + + ((modified_count++)) + log_info "使用替代方法成功修改文件: ${file/$temp_dir\//}" + else + # 最后尝试的通用方法 - 在文件顶部插入重写函数定义 + log_warn "未找到任何已知函数,使用最通用的方法" + + inject_universal_code=" +// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) - ES模块兼容版本 +// 全局拦截设备标识符 - $(date +%s) +import crypto from 'crypto'; + +// 保存原始函数引用 +const originalRandomUUID_$(date +%s) = crypto.randomUUID; + +// 重写crypto.randomUUID方法 +crypto.randomUUID = function() { + return '${new_uuid}'; +}; -# 检查权限 -check_permissions() { - if [ "$EUID" -ne 0 ]; then - log_error "请使用 sudo 运行此脚本" - echo "示例: sudo $0" - exit 1 - fi +// 覆盖所有可能的系统ID获取函数 - 使用globalThis +globalThis.getMachineId = function() { return '${machine_id}'; }; +globalThis.getDeviceId = function() { return '${device_id}'; }; +globalThis.macMachineId = '${mac_machine_id}'; + +// 确保在不同环境下都能访问 +if (typeof window !== 'undefined') { + window.getMachineId = globalThis.getMachineId; + window.getDeviceId = globalThis.getDeviceId; + window.macMachineId = globalThis.macMachineId; } -# 检查并关闭 Cursor 进程 -check_and_kill_cursor() { - log_info "检查 Cursor 进程..." - - local attempt=1 - local max_attempts=5 - - # 函数:获取进程详细信息 - get_process_details() { - local process_name="$1" - log_debug "正在获取 $process_name 进程详细信息:" - ps aux | grep -i "$process_name" | grep -v grep - } - - while [ $attempt -le $max_attempts ]; do - CURSOR_PIDS=$(pgrep -i "cursor" || true) - - if [ -z "$CURSOR_PIDS" ]; then - log_info "未发现运行中的 Cursor 进程" - return 0 +// 确保模块顶层执行 +console.log('Cursor全局设备标识符拦截已激活 - ES模块版本'); +" + # 将代码注入到文件开头 + local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') + local machine_id="auth0|user_$(openssl rand -hex 16)" + local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + local mac_machine_id=$(openssl rand -hex 32) + + inject_universal_code=${inject_universal_code//\$\{new_uuid\}/$new_uuid} + inject_universal_code=${inject_universal_code//\$\{machine_id\}/$machine_id} + inject_universal_code=${inject_universal_code//\$\{device_id\}/$device_id} + inject_universal_code=${inject_universal_code//\$\{mac_machine_id\}/$mac_machine_id} + + echo "$inject_universal_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + log_debug "完成通用覆盖" + ((modified_count++)) + log_info "使用最通用方法成功修改文件: ${file/$temp_dir\//}" + fi fi - - log_warn "发现 Cursor 进程正在运行" - get_process_details "cursor" - - log_warn "尝试关闭 Cursor 进程..." - - if [ $attempt -eq $max_attempts ]; then - log_warn "尝试强制终止进程..." - kill -9 $CURSOR_PIDS 2>/dev/null || true + + # 添加在关键操作后记录日志 + echo "[MODIFIED] 文件修改后内容:" >> "$LOG_FILE" + grep -n "return crypto.randomUUID()" "$file" | head -3 >> "$LOG_FILE" + + # 清理临时文件 + rm -f "${file}.tmp" "${file}.bak" + echo "[PROCESS] 文件处理完成: $file" >> "$LOG_FILE" + done + + if [ "$modified_count" -eq 0 ]; then + log_error "未能成功修改任何文件" + rm -rf "$temp_dir" + return 1 + fi + + # 重新签名应用(增加重试机制) + local max_retry=3 + local retry_count=0 + local sign_success=false + + while [ $retry_count -lt $max_retry ]; do + ((retry_count++)) + log_info "尝试签名 (第 $retry_count 次)..." + + # 使用更详细的签名参数 + if codesign --sign - --force --deep --preserve-metadata=entitlements,identifier,flags "$temp_app" 2>&1 | tee /tmp/codesign.log; then + # 验证签名 + if codesign --verify -vvvv "$temp_app" 2>/dev/null; then + sign_success=true + log_info "应用签名验证通过" + break + else + log_warn "签名验证失败,错误日志:" + cat /tmp/codesign.log + fi else - kill $CURSOR_PIDS 2>/dev/null || true - fi - - sleep 1 - - if ! pgrep -i "cursor" > /dev/null; then - log_info "Cursor 进程已成功关闭" - return 0 + log_warn "签名失败,错误日志:" + cat /tmp/codesign.log fi - log_warn "等待进程关闭,尝试 $attempt/$max_attempts..." - ((attempt++)) + sleep 3 done - - log_error "在 $max_attempts 次尝试后仍无法关闭 Cursor 进程" - get_process_details "cursor" - log_error "请手动关闭进程后重试" - exit 1 -} -# 备份配置文件 -backup_config() { - if [ ! -f "$STORAGE_FILE" ]; then - log_warn "配置文件不存在,跳过备份" - return 0 + if ! $sign_success; then + log_error "经过 $max_retry 次尝试仍无法完成签名" + log_error "请手动执行以下命令完成签名:" + echo -e "${BLUE}sudo codesign --sign - --force --deep '${temp_app}'${NC}" + echo -e "${YELLOW}操作完成后,请手动将应用复制到原路径:${NC}" + echo -e "${BLUE}sudo cp -R '${temp_app}' '/Applications/'${NC}" + log_info "临时文件保留在:${temp_dir}" + return 1 fi - - mkdir -p "$BACKUP_DIR" - local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)" - - if cp "$STORAGE_FILE" "$backup_file"; then - chmod 644 "$backup_file" - chown "$CURRENT_USER" "$backup_file" - log_info "配置已备份到: $backup_file" - else - log_error "备份失败" - exit 1 + + # 替换原应用 + log_info "安装修改版应用..." + if ! sudo rm -rf "$CURSOR_APP_PATH" || ! sudo cp -R "$temp_app" "/Applications/"; then + log_error "应用替换失败,正在恢复..." + sudo rm -rf "$CURSOR_APP_PATH" + sudo cp -R "$backup_app" "$CURSOR_APP_PATH" + rm -rf "$temp_dir" "$backup_app" + return 1 fi -} -# 生成随机 ID -generate_random_id() { - # 生成32字节(64个十六进制字符)的随机数 - openssl rand -hex 32 -} + # 清理临时文件 + rm -rf "$temp_dir" "$backup_app" -# 生成随机 UUID -generate_uuid() { - uuidgen | tr '[:upper:]' '[:lower:]' -} + # 设置权限 + sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH" + sudo chmod -R 755 "$CURSOR_APP_PATH" -# 生成新的配置 -generate_new_config() { - # 检查配置文件是否存在 - if [ ! -f "$STORAGE_FILE" ]; then - log_error "未找到配置文件: $STORAGE_FILE" - log_warn "请先安装并运行一次 Cursor 后再使用此脚本" - exit 1 - fi - - # 将 auth0|user_ 转换为字节数组的十六进制 - local prefix_hex=$(echo -n "auth0|user_" | xxd -p) - local random_part=$(generate_random_id) - local machine_id="${prefix_hex}${random_part}" - - local mac_machine_id=$(generate_random_id) - local device_id=$(generate_uuid | tr '[:upper:]' '[:lower:]') - local sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}" - - # 修改现有文件 - sed -i '' -e "s/\"telemetry\.machineId\":[[:space:]]*\"[^\"]*\"/\"telemetry.machineId\": \"$machine_id\"/" "$STORAGE_FILE" - sed -i '' -e "s/\"telemetry\.macMachineId\":[[:space:]]*\"[^\"]*\"/\"telemetry.macMachineId\": \"$mac_machine_id\"/" "$STORAGE_FILE" - sed -i '' -e "s/\"telemetry\.devDeviceId\":[[:space:]]*\"[^\"]*\"/\"telemetry.devDeviceId\": \"$device_id\"/" "$STORAGE_FILE" - sed -i '' -e "s/\"telemetry\.sqmId\":[[:space:]]*\"[^\"]*\"/\"telemetry.sqmId\": \"$sqm_id\"/" "$STORAGE_FILE" - - # 设置文件权限和所有者 - chmod 444 "$STORAGE_FILE" # 改为只读权限 - chown "$CURRENT_USER" "$STORAGE_FILE" - - # 验证权限设置 - if [ -w "$STORAGE_FILE" ]; then - log_warn "无法设置只读权限,尝试使用其他方法..." - chattr +i "$STORAGE_FILE" 2>/dev/null || true - else - log_info "成功设置文件只读权限" - fi - - echo - log_info "已更新配置:" - log_debug "machineId: $machine_id" - log_debug "macMachineId: $mac_machine_id" - log_debug "devDeviceId: $device_id" - log_debug "sqmId: $sqm_id" + log_info "Cursor 主程序文件修改完成!原版备份在: ${backup_app/$HOME/\~}" + return 0 } # 显示文件树结构 @@ -190,7 +2342,7 @@ show_file_tree() { echo "├── globalStorage" echo "│ ├── storage.json (已修改)" echo "│ └── backups" - + # 列出备份文件 if [ -d "$BACKUP_DIR" ]; then local backup_files=("$BACKUP_DIR"/*) @@ -211,17 +2363,259 @@ show_file_tree() { show_follow_info() { echo echo -e "${GREEN}================================${NC}" - echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识 ${NC}" + echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" echo -e "${GREEN}================================${NC}" echo } +# 禁用自动更新 +disable_auto_update() { + local updater_path="$HOME/Library/Application Support/Caches/cursor-updater" + local app_update_yml="/Applications/Cursor.app/Contents/Resources/app-update.yml" + + echo + log_info "正在禁用 Cursor 自动更新..." + + # 备份并清空 app-update.yml + if [ -f "$app_update_yml" ]; then + log_info "备份并修改 app-update.yml..." + if ! sudo cp "$app_update_yml" "${app_update_yml}.bak" 2>/dev/null; then + log_warn "备份 app-update.yml 失败,继续执行..." + fi + + if sudo bash -c "echo '' > \"$app_update_yml\"" && \ + sudo chmod 444 "$app_update_yml"; then + log_info "成功禁用 app-update.yml" + else + log_error "修改 app-update.yml 失败,请手动执行以下命令:" + echo -e "${BLUE}sudo cp \"$app_update_yml\" \"${app_update_yml}.bak\"${NC}" + echo -e "${BLUE}sudo bash -c 'echo \"\" > \"$app_update_yml\"'${NC}" + echo -e "${BLUE}sudo chmod 444 \"$app_update_yml\"${NC}" + fi + else + log_warn "未找到 app-update.yml 文件" + fi + + # 同时也处理 cursor-updater + log_info "处理 cursor-updater..." + if sudo rm -rf "$updater_path" && \ + sudo touch "$updater_path" && \ + sudo chmod 444 "$updater_path"; then + log_info "成功禁用 cursor-updater" + else + log_error "禁用 cursor-updater 失败,请手动执行以下命令:" + echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}" + fi + + echo + log_info "验证方法:" + echo "1. 运行命令:ls -l \"$updater_path\"" + echo " 确认文件权限显示为:r--r--r--" + echo "2. 运行命令:ls -l \"$app_update_yml\"" + echo " 确认文件权限显示为:r--r--r--" + echo + log_info "完成后请重启 Cursor" +} + +# 新增恢复功能选项 +restore_feature() { + # 检查备份目录是否存在 + if [ ! -d "$BACKUP_DIR" ]; then + log_warn "备份目录不存在" + return 1 + fi + + # 使用 find 命令获取备份文件列表并存储到数组 + backup_files=() + while IFS= read -r file; do + [ -f "$file" ] && backup_files+=("$file") + done < <(find "$BACKUP_DIR" -name "*.backup_*" -type f 2>/dev/null | sort) + + # 检查是否找到备份文件 + if [ ${#backup_files[@]} -eq 0 ]; then + log_warn "未找到任何备份文件" + return 1 + fi + + echo + log_info "可用的备份文件:" + + # 构建菜单选项字符串 + menu_options="退出 - 不恢复任何文件" + for i in "${!backup_files[@]}"; do + menu_options="$menu_options|$(basename "${backup_files[$i]}")" + done + + # 使用菜单选择函数 + select_menu_option "请使用上下箭头选择要恢复的备份文件,按Enter确认:" "$menu_options" 0 + choice=$? + + # 处理用户输入 + if [ "$choice" = "0" ]; then + log_info "跳过恢复操作" + return 0 + fi + + # 获取选择的备份文件 (减1是因为第一个选项是"退出") + local selected_backup="${backup_files[$((choice-1))]}" + + # 验证文件存在性和可读性 + if [ ! -f "$selected_backup" ] || [ ! -r "$selected_backup" ]; then + log_error "无法访问选择的备份文件" + return 1 + fi + + # 尝试恢复配置 + if cp "$selected_backup" "$STORAGE_FILE"; then + chmod 644 "$STORAGE_FILE" + chown "$CURRENT_USER" "$STORAGE_FILE" + log_info "已从备份文件恢复配置: $(basename "$selected_backup")" + return 0 + else + log_error "恢复配置失败" + return 1 + fi +} + +# 解决"应用已损坏,无法打开"问题 +fix_damaged_app() { + log_info "正在修复"应用已损坏"问题..." + + # 检查Cursor应用是否存在 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log_error "未找到Cursor应用: $CURSOR_APP_PATH" + return 1 + fi + + log_info "尝试移除隔离属性..." + if sudo find "$CURSOR_APP_PATH" -print0 \ + | xargs -0 sudo xattr -d com.apple.quarantine 2>/dev/null + then + log_info "成功移除隔离属性" + else + log_warn "移除隔离属性失败,尝试其他方法..." + fi + + log_info "尝试重新签名应用..." + if sudo codesign --force --deep --sign - "$CURSOR_APP_PATH" 2>/dev/null; then + log_info "应用重新签名成功" + else + log_warn "应用重新签名失败" + fi + + echo + log_info "修复完成!请尝试重新打开Cursor应用" + echo + echo -e "${YELLOW}如果仍然无法打开,您可以尝试以下方法:${NC}" + echo "1. 在系统偏好设置->安全性与隐私中,点击"仍要打开"按钮" + echo "2. 暂时关闭Gatekeeper(不建议): sudo spctl --master-disable" + echo "3. 重新下载安装Cursor应用" + echo + echo -e "${BLUE} 参考链接: https://sysin.org/blog/macos-if-crashes-when-opening/ ${NC}" + + return 0 +} + +# 新增:通用菜单选择函数 +# 参数: +# $1 - 提示信息 +# $2 - 选项数组,格式为 "选项1|选项2|选项3" +# $3 - 默认选项索引 (从0开始) +# 返回: 选中的选项索引 (从0开始) +select_menu_option() { + local prompt="$1" + IFS='|' read -ra options <<< "$2" + local default_index=${3:-0} + local selected_index=$default_index + local key_input + local cursor_up='\033[A' + local cursor_down='\033[B' + local enter_key=$'\n' + + # 保存光标位置 + tput sc + + # 显示提示信息 + echo -e "$prompt" + + # 第一次显示菜单 + for i in "${!options[@]}"; do + if [ $i -eq $selected_index ]; then + echo -e " ${GREEN}►${NC} ${options[$i]}" + else + echo -e " ${options[$i]}" + fi + done + + # 循环处理键盘输入 + while true; do + # 读取单个按键 + read -rsn3 key_input + + # 检测按键 + case "$key_input" in + # 上箭头键 + $'\033[A') + if [ $selected_index -gt 0 ]; then + ((selected_index--)) + fi + ;; + # 下箭头键 + $'\033[B') + if [ $selected_index -lt $((${#options[@]}-1)) ]; then + ((selected_index++)) + fi + ;; + # Enter键 + "") + echo # 换行 + log_info "您选择了: ${options[$selected_index]}" + return $selected_index + ;; + esac + + # 恢复光标位置 + tput rc + + # 重新显示菜单 + for i in "${!options[@]}"; do + if [ $i -eq $selected_index ]; then + echo -e " ${GREEN}►${NC} ${options[$i]}" + else + echo -e " ${options[$i]}" + fi + done + done +} + # 主函数 main() { + + # 初始化日志文件 + initialize_log + log_info "脚本启动..." + + # 🚀 启动时权限修复(解决EACCES错误) + log_info "🚀 [启动时权限] 执行启动时权限修复..." + ensure_cursor_directory_permissions + + # 记录系统信息 + log_info "系统信息: $(uname -a)" + log_info "当前用户: $CURRENT_USER" + log_cmd_output "sw_vers" "macOS 版本信息" + log_cmd_output "which codesign" "codesign 路径" + log_cmd_output "ls -ld "$CURSOR_APP_PATH"" "Cursor 应用信息" + + # 新增环境检查 + if [[ $(uname) != "Darwin" ]]; then + log_error "本脚本仅支持 macOS 系统" + exit 1 + fi + clear - # 显示 CURSOR Logo + # 显示 Logo echo -e " - ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ @@ -229,94 +2623,283 @@ main() { ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ " echo -e "${BLUE}================================${NC}" - echo -e "${GREEN} Cursor ID 修改工具 ${NC}" + echo -e "${GREEN}🚀 Cursor 防掉试用Pro删除工具 ${NC}" + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】 ${NC}" + echo -e "${YELLOW}🤝 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" echo -e "${BLUE}================================${NC}" echo - echo -e "${YELLOW}[重要提示]${NC} 本工具仅支持 Cursor v0.44.11 及以下版本" - echo -e "${YELLOW}[重要提示]${NC} 最新的 0.45.x 版本暂不支持" + echo -e "${YELLOW}💰 [小小广告] 出售CursorPro教育号一年质保三个月,有需要找我(86),WeChat:JavaRookie666 ${NC}" + echo + echo -e "${YELLOW}💡 [重要提示]${NC} 本工具采用分阶段执行策略,既能彻底清理又能修改机器码。" + echo -e "${YELLOW}💡 [重要提示]${NC} 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】" + echo echo - - check_permissions - check_and_kill_cursor - backup_config - generate_new_config - - echo - log_info "操作完成!" - show_file_tree - show_follow_info - log_info "请重启 Cursor 以应用新的配置" - - # 询问是否要禁用自动更新 - disable_auto_update -} -# 执行主函数 -main + # 🎯 用户选择菜单 + echo + echo -e "${GREEN}🎯 [选择模式]${NC} 请选择您要执行的操作:" + echo + echo -e "${BLUE} 1️⃣ 仅修改机器码${NC}" + echo -e "${YELLOW} • 仅执行机器码修改功能${NC}" + echo -e "${YELLOW} • 跳过文件夹删除/环境重置步骤${NC}" + echo -e "${YELLOW} • 保留现有Cursor配置和数据${NC}" + echo + echo -e "${BLUE} 2️⃣ 重置环境+修改机器码${NC}" + echo -e "${RED} • 执行完全环境重置(删除Cursor文件夹)${NC}" + echo -e "${RED} • ⚠️ 配置将丢失,请注意备份${NC}" + echo -e "${YELLOW} • 按照机器代码修改${NC}" + echo -e "${YELLOW} • 这相当于当前的完整脚本行为${NC}" + echo + + # 获取用户选择 + while true; do + read -p "请输入选择 (1 或 2): " user_choice + if [ "$user_choice" = "1" ]; then + echo -e "${GREEN}✅ [选择]${NC} 您选择了:仅修改机器码" + execute_mode="MODIFY_ONLY" + break + elif [ "$user_choice" = "2" ]; then + echo -e "${GREEN}✅ [选择]${NC} 您选择了:重置环境+修改机器码" + echo -e "${RED}⚠️ [重要警告]${NC} 此操作将删除所有Cursor配置文件!" + read -p "确认执行完全重置?(输入 yes 确认,其他任意键取消): " confirm_reset + if [ "$confirm_reset" = "yes" ]; then + execute_mode="RESET_AND_MODIFY" + break + else + echo -e "${YELLOW}👋 [取消]${NC} 用户取消重置操作" + continue + fi + else + echo -e "${RED}❌ [错误]${NC} 无效选择,请输入 1 或 2" + fi + done -# 询问是否要禁用自动更新 -disable_auto_update() { echo - log_warn "是否要禁用 Cursor 自动更新功能?" - echo "0) 否 - 保持默认设置 (按回车键)" - echo "1) 是 - 禁用自动更新" - read -r choice - - if [ "$choice" = "1" ]; then + + # 📋 根据选择显示执行流程说明 + if [ "$execute_mode" = "MODIFY_ONLY" ]; then + echo -e "${GREEN}📋 [执行流程]${NC} 仅修改机器码模式将按以下步骤执行:" + echo -e "${BLUE} 1️⃣ 检测Cursor配置文件${NC}" + echo -e "${BLUE} 2️⃣ 备份现有配置文件${NC}" + echo -e "${BLUE} 3️⃣ 修改机器码配置${NC}" + echo -e "${BLUE} 4️⃣ 显示操作完成信息${NC}" echo - log_info "正在处理自动更新..." - local updater_path="$HOME/Library/Application Support/cursor-updater" - - # 定义手动设置教程 - show_manual_guide() { + echo -e "${YELLOW}⚠️ [注意事项]${NC}" + echo -e "${YELLOW} • 不会删除任何文件夹或重置环境${NC}" + echo -e "${YELLOW} • 保留所有现有配置和数据${NC}" + echo -e "${YELLOW} • 原配置文件会自动备份${NC}" + echo -e "${YELLOW} • 需要Python3环境来处理JSON配置文件${NC}" + else + echo -e "${GREEN}📋 [执行流程]${NC} 重置环境+修改机器码模式将按以下步骤执行:" + echo -e "${BLUE} 1️⃣ 检测并关闭Cursor进程${NC}" + echo -e "${BLUE} 2️⃣ 保存Cursor程序路径信息${NC}" + echo -e "${BLUE} 3️⃣ 删除指定的Cursor试用相关文件夹${NC}" + echo -e "${BLUE} 📁 ~/Library/Application Support/Cursor${NC}" + echo -e "${BLUE} 📁 ~/.cursor${NC}" + echo -e "${BLUE} 3.5️⃣ 预创建必要目录结构,避免权限问题${NC}" + echo -e "${BLUE} 4️⃣ 重新启动Cursor让其生成新的配置文件${NC}" + echo -e "${BLUE} 5️⃣ 等待配置文件生成完成(最多45秒)${NC}" + echo -e "${BLUE} 6️⃣ 关闭Cursor进程${NC}" + echo -e "${BLUE} 7️⃣ 修改新生成的机器码配置文件${NC}" + echo -e "${BLUE} 8️⃣ 智能设备识别绕过(MAC地址修改或JS内核修改)${NC}" + echo -e "${BLUE} 9️⃣ 禁用自动更新${NC}" + echo -e "${BLUE} 🔟 显示操作完成统计信息${NC}" + echo + echo -e "${YELLOW}⚠️ [注意事项]${NC}" + echo -e "${YELLOW} • 脚本执行过程中请勿手动操作Cursor${NC}" + echo -e "${YELLOW} • 建议在执行前关闭所有Cursor窗口${NC}" + echo -e "${YELLOW} • 执行完成后需要重新启动Cursor${NC}" + echo -e "${YELLOW} • 原配置文件会自动备份到backups文件夹${NC}" + echo -e "${YELLOW} • 需要Python3环境来处理JSON配置文件${NC}" + echo -e "${YELLOW} • MAC地址修改是临时的,重启后恢复${NC}" + fi + echo + + # 🤔 用户确认 + echo -e "${GREEN}🤔 [确认]${NC} 请确认您已了解上述执行流程" + read -p "是否继续执行?(输入 y 或 yes 继续,其他任意键退出): " confirmation + if [[ ! "$confirmation" =~ ^(y|yes)$ ]]; then + echo -e "${YELLOW}👋 [退出]${NC} 用户取消执行,脚本退出" + exit 0 + fi + echo -e "${GREEN}✅ [确认]${NC} 用户确认继续执行" + echo + + # 🚀 根据用户选择执行相应功能 + if [ "$execute_mode" = "MODIFY_ONLY" ]; then + log_info "🚀 [开始] 开始执行仅修改机器码功能..." + + # 先进行环境检查 + if ! test_cursor_environment "MODIFY_ONLY"; then + echo + log_error "❌ [环境检查失败] 无法继续执行" echo - log_warn "自动设置失败,请尝试手动操作:" - echo -e "${YELLOW}手动禁用更新步骤:${NC}" - echo "1. 打开终端(Terminal)" - echo "2. 复制粘贴以下命令:" - echo -e "${BLUE}rm -rf \"$updater_path\" && touch \"$updater_path\" && chmod 444 \"$updater_path\"${NC}" + log_info "💡 [建议] 请选择以下操作:" + echo -e "${BLUE} 1️⃣ 选择'重置环境+修改机器码'选项(推荐)${NC}" + echo -e "${BLUE} 2️⃣ 手动启动Cursor一次,然后重新运行脚本${NC}" + echo -e "${BLUE} 3️⃣ 检查Cursor是否正确安装${NC}" + echo -e "${BLUE} 4️⃣ 安装Python3: brew install python3${NC}" echo - echo -e "${YELLOW}如果上述命令提示权限不足,请使用 sudo:${NC}" - echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}" + read -p "按回车键退出..." + exit 1 + fi + + # 执行机器码修改 + if modify_machine_code_config "MODIFY_ONLY"; then echo - echo -e "${YELLOW}验证方法:${NC}" - echo "1. 运行命令:ls -l \"$updater_path\"" - echo "2. 确认文件权限为 r--r--r--" + log_info "🎉 [完成] 机器码修改完成!" + log_info "💡 [提示] 现在可以启动Cursor使用新的机器码配置" + else echo - log_warn "完成后请重启 Cursor" - } - - if [ -d "$updater_path" ]; then - rm -rf "$updater_path" 2>/dev/null || { - log_error "删除 cursor-updater 目录失败" - show_manual_guide - return 1 - } - log_info "成功删除 cursor-updater 目录" + log_error "❌ [失败] 机器码修改失败!" + log_info "💡 [建议] 请尝试'重置环境+修改机器码'选项" + fi + + # 🔧 智能设备识别绕过(MAC地址修改或JS内核修改) + echo + log_info "🔧 [设备识别] 开始智能设备识别绕过..." + log_info "💡 [说明] 将根据系统环境自动选择最佳方案(MAC地址修改或JS内核修改)" + + if change_system_mac_address; then + log_info "✅ [成功] 设备识别绕过完成(使用MAC地址修改)" + else + log_warn "⚠️ [警告] 设备识别绕过失败或部分失败" + log_info "💡 [提示] 但可能已通过JS内核修改实现了绕过效果" + fi + + # 🚫 禁用自动更新(仅修改模式也需要) + echo + log_info "🚫 [禁用更新] 正在禁用Cursor自动更新..." + disable_auto_update + + # 🛡️ 关键修复:仅修改模式的权限修复 + echo + log_info "🛡️ [权限修复] 执行仅修改模式的权限修复..." + log_info "💡 [说明] 确保Cursor应用能够正常启动,无权限错误" + ensure_cursor_directory_permissions + + # 🔧 关键修复:修复应用签名问题(防止"应用已损坏"错误) + echo + log_info "🔧 [应用修复] 正在修复Cursor应用签名问题..." + log_info "💡 [说明] 防止出现'应用已损坏,无法打开'的错误" + + if fix_damaged_app; then + log_info "✅ [应用修复] Cursor应用签名修复成功" + else + log_warn "⚠️ [应用修复] 应用签名修复失败,可能需要手动处理" + log_info "💡 [建议] 如果Cursor无法启动,请在系统偏好设置中允许打开" fi - - touch "$updater_path" 2>/dev/null || { - log_error "创建阻止文件失败" - show_manual_guide - return 1 - } - - chmod 444 "$updater_path" 2>/dev/null && \ - chown "$CURRENT_USER" "$updater_path" 2>/dev/null || { - log_error "设置文件权限失败" - show_manual_guide - return 1 - } - - # 验证设置是否成功 - if [ ! -f "$updater_path" ] || [ -w "$updater_path" ]; then - log_error "验证失败:文件权限设置可能未生效" - show_manual_guide - return 1 - } - - log_info "成功禁用自动更新" else - log_info "保持默认设置,不进行更改" + # 完整的重置环境+修改机器码流程 + log_info "🚀 [开始] 开始执行重置环境+修改机器码功能..." + + # 🚀 执行主要功能 + check_permissions + check_and_kill_cursor + + # 🚨 重要警告提示 + echo + echo -e "${RED}🚨 [重要警告]${NC} ============================================" + log_warn "⚠️ [风控提醒] Cursor 风控机制非常严格!" + log_warn "⚠️ [必须删除] 必须完全删除指定文件夹,不能有任何残留设置" + log_warn "⚠️ [防掉试用] 只有彻底清理才能有效防止掉试用Pro状态" + echo -e "${RED}🚨 [重要警告]${NC} ============================================" + echo + + # 🎯 执行 Cursor 防掉试用Pro删除文件夹功能 + log_info "🚀 [开始] 开始执行核心功能..." + remove_cursor_trial_folders + + # 🔄 重启Cursor让其重新生成配置文件 + restart_cursor_and_wait + + # 🛠️ 修改机器码配置 + modify_machine_code_config + + # 🔧 智能设备识别绕过(MAC地址修改或JS内核修改) + echo + log_info "🔧 [设备识别] 开始智能设备识别绕过..." + log_info "💡 [说明] 将根据系统环境自动选择最佳方案(MAC地址修改或JS内核修改)" + + if change_system_mac_address; then + log_info "✅ [成功] 设备识别绕过完成(使用MAC地址修改)" + else + log_warn "⚠️ [警告] 设备识别绕过失败或部分失败" + log_info "💡 [提示] 但可能已通过JS内核修改实现了绕过效果" + fi + + # 🔧 关键修复:修复应用签名问题(防止"应用已损坏"错误) + echo + log_info "🔧 [应用修复] 正在修复Cursor应用签名问题..." + log_info "💡 [说明] 防止出现'应用已损坏,无法打开'的错误" + + if fix_damaged_app; then + log_info "✅ [应用修复] Cursor应用签名修复成功" + else + log_warn "⚠️ [应用修复] 应用签名修复失败,可能需要手动处理" + log_info "💡 [建议] 如果Cursor无法启动,请在系统偏好设置中允许打开" + fi fi + + # 🚫 禁用自动更新 + echo + log_info "🚫 [禁用更新] 正在禁用Cursor自动更新..." + disable_auto_update + + # 🎉 显示操作完成信息 + echo + log_info "🎉 [完成] Cursor 防掉试用Pro删除操作已完成!" + echo + + # 📱 显示公众号信息 + echo -e "${GREEN}================================${NC}" + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${GREEN}================================${NC}" + echo + log_info "🚀 [提示] 现在可以重新启动 Cursor 尝试使用了!" + echo + + # 🎉 显示修改结果总结 + echo + echo -e "${GREEN}================================${NC}" + echo -e "${BLUE} 🎯 修改结果总结 ${NC}" + echo -e "${GREEN}================================${NC}" + echo -e "${GREEN}✅ JSON配置文件修改: 完成${NC}" + echo -e "${GREEN}✅ MAC地址修改: 完成${NC}" + echo -e "${GREEN}✅ 自动更新禁用: 完成${NC}" + echo -e "${GREEN}================================${NC}" + echo + + # 🛡️ 脚本完成前最终权限修复 + echo + log_info "🛡️ [最终权限修复] 执行脚本完成前的最终权限修复..." + ensure_cursor_directory_permissions + + # 🎉 脚本执行完成 + log_info "🎉 [完成] 所有操作已完成!" + echo + log_info "💡 [重要提示] 完整的Cursor破解流程已执行:" + echo -e "${BLUE} ✅ 机器码配置文件修改${NC}" + echo -e "${BLUE} ✅ 系统MAC地址修改${NC}" + echo -e "${BLUE} ✅ 自动更新功能禁用${NC}" + echo -e "${BLUE} ✅ 权限修复和验证${NC}" + echo + log_warn "⚠️ [注意] 重启 Cursor 后生效" + echo + log_info "🚀 [下一步] 现在可以启动 Cursor 尝试使用了!" + echo + + # 记录脚本完成信息 + log_info "📝 [日志] 脚本执行完成" + echo "========== Cursor 防掉试用Pro删除工具日志结束 $(date) ==========" >> "$LOG_FILE" + + # 显示日志文件位置 + echo + log_info "📄 [日志] 详细日志已保存到: $LOG_FILE" + echo "如遇问题请将此日志文件提供给开发者以协助排查" + echo } + +# 执行主函数 +main + diff --git a/scripts/run/cursor_mac_id_modifier_new.sh b/scripts/run/cursor_mac_id_modifier_new.sh new file mode 100644 index 00000000..6eaff205 --- /dev/null +++ b/scripts/run/cursor_mac_id_modifier_new.sh @@ -0,0 +1,688 @@ +#!/bin/bash + +# ======================================== +# Cursor macOS 机器码修改脚本 (重构精简版) +# ======================================== +# +# 🎯 重构目标: +# - 简化脚本复杂度,从3158行压缩到约900行 +# - 自动化权限修复,解决EACCES权限错误 +# - 减少用户交互步骤,提升执行效率 +# - 保持所有原有功能完整性 +# +# 🚀 执行流程说明: +# 1. 环境检测和权限预修复 +# 2. 用户选择执行模式(仅修改 vs 完整重置) +# 3. 自动执行所有必要步骤 +# 4. 智能设备识别绕过(MAC地址或JS内核修改) +# 5. 自动权限修复和验证 +# +# ======================================== + +set -e + +# ==================== 核心配置 ==================== +LOG_FILE="/tmp/cursor_reset_$(date +%Y%m%d_%H%M%S).log" +CURSOR_APP_PATH="/Applications/Cursor.app" +STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" +BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups" + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# ==================== 统一工具函数 ==================== + +# 初始化日志 +init_log() { + echo "========== Cursor重构脚本执行日志 $(date) ==========" > "$LOG_FILE" + chmod 644 "$LOG_FILE" +} + +# 精简日志函数 +log() { + local level="$1" + local msg="$2" + local color="" + + case "$level" in + "INFO") color="$GREEN" ;; + "WARN") color="$YELLOW" ;; + "ERROR") color="$RED" ;; + *) color="$BLUE" ;; + esac + + echo -e "${color}[$level]${NC} $msg" + echo "[$level] $(date '+%H:%M:%S') $msg" >> "$LOG_FILE" +} + +# 统一权限管理器 - 解决所有权限问题 +fix_permissions() { + log "INFO" "🔧 执行统一权限修复..." + + local cursor_support="$HOME/Library/Application Support/Cursor" + local cursor_home="$HOME/.cursor" + + # 创建必要目录结构 + local dirs=( + "$cursor_support" + "$cursor_support/User" + "$cursor_support/User/globalStorage" + "$cursor_support/logs" + "$cursor_support/CachedData" + "$cursor_home" + "$cursor_home/extensions" + ) + + for dir in "${dirs[@]}"; do + mkdir -p "$dir" 2>/dev/null || true + done + + # 执行核心权限修复命令 + if sudo chown -R "$(whoami)" "$cursor_support" 2>/dev/null && \ + sudo chown -R "$(whoami)" "$cursor_home" 2>/dev/null && \ + chmod -R u+w "$cursor_support" 2>/dev/null && \ + chmod -R u+w "$cursor_home/extensions" 2>/dev/null; then + log "INFO" "✅ 权限修复成功" + return 0 + else + log "ERROR" "❌ 权限修复失败" + return 1 + fi +} + +# 环境检测器 +detect_environment() { + log "INFO" "🔍 检测系统环境..." + + # 检测macOS版本和硬件 + MACOS_VERSION=$(sw_vers -productVersion) + HARDWARE_TYPE=$(uname -m) + + if [[ "$HARDWARE_TYPE" == "arm64" ]]; then + HARDWARE_TYPE="Apple Silicon" + else + HARDWARE_TYPE="Intel" + fi + + # 检测兼容性 + local macos_major=$(echo "$MACOS_VERSION" | cut -d. -f1) + if [[ $macos_major -ge 14 ]] || [[ "$HARDWARE_TYPE" == "Apple Silicon" ]]; then + MAC_COMPATIBLE=false + log "WARN" "⚠️ 检测到MAC地址修改受限环境: macOS $MACOS_VERSION ($HARDWARE_TYPE)" + else + MAC_COMPATIBLE=true + log "INFO" "✅ 环境兼容性检查通过" + fi + + # 检查Python3 + if ! command -v python3 >/dev/null 2>&1; then + log "ERROR" "❌ 未找到Python3,请安装: brew install python3" + return 1 + fi + + # 检查Cursor应用 + if [ ! -d "$CURSOR_APP_PATH" ]; then + log "ERROR" "❌ 未找到Cursor应用: $CURSOR_APP_PATH" + return 1 + fi + + log "INFO" "✅ 环境检测完成: macOS $MACOS_VERSION ($HARDWARE_TYPE)" + return 0 +} + +# 进程管理器 +manage_cursor_process() { + local action="$1" # kill 或 start + + case "$action" in + "kill") + log "INFO" "🔄 关闭Cursor进程..." + pkill -f "Cursor" 2>/dev/null || true + sleep 2 + + # 验证是否关闭 + if pgrep -f "Cursor" >/dev/null; then + pkill -9 -f "Cursor" 2>/dev/null || true + sleep 2 + fi + log "INFO" "✅ Cursor进程已关闭" + ;; + "start") + log "INFO" "🚀 启动Cursor..." + "$CURSOR_APP_PATH/Contents/MacOS/Cursor" > /dev/null 2>&1 & + sleep 15 + log "INFO" "✅ Cursor已启动" + ;; + esac +} + +# 错误处理器 +handle_error() { + local error_msg="$1" + local recovery_action="$2" + + log "ERROR" "❌ 错误: $error_msg" + + if [ -n "$recovery_action" ]; then + log "INFO" "🔄 尝试恢复: $recovery_action" + eval "$recovery_action" + fi + + log "INFO" "💡 如需帮助,请查看日志: $LOG_FILE" +} + +# ==================== 功能模块 ==================== + +# 机器码修改器 +modify_machine_code() { + log "INFO" "🛠️ 开始修改机器码配置..." + + # 检查配置文件 + if [ ! -f "$STORAGE_FILE" ]; then + log "ERROR" "❌ 配置文件不存在,请先启动Cursor生成配置" + return 1 + fi + + # 创建备份 + mkdir -p "$BACKUP_DIR" + local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)" + cp "$STORAGE_FILE" "$backup_file" || { + log "ERROR" "❌ 备份失败" + return 1 + } + + # 生成新ID + local machine_id="auth0|user_$(openssl rand -hex 16)" + local mac_machine_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]') + local sqm_id="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" + + # 修改配置文件 + local python_result=$(python3 -c " +import json +import sys + +try: + with open('$STORAGE_FILE', 'r', encoding='utf-8') as f: + config = json.load(f) + + config['telemetry.machineId'] = '$machine_id' + config['telemetry.macMachineId'] = '$mac_machine_id' + config['telemetry.devDeviceId'] = '$device_id' + config['telemetry.sqmId'] = '$sqm_id' + + with open('$STORAGE_FILE', 'w', encoding='utf-8') as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + print('SUCCESS') +except Exception as e: + print(f'ERROR: {e}') + sys.exit(1) +" 2>&1) + + if echo "$python_result" | grep -q "SUCCESS"; then + # 设置只读保护 + chmod 444 "$STORAGE_FILE" 2>/dev/null || true + log "INFO" "✅ 机器码修改成功" + log "INFO" "💾 备份保存至: $(basename "$backup_file")" + return 0 + else + log "ERROR" "❌ 机器码修改失败: $python_result" + cp "$backup_file" "$STORAGE_FILE" 2>/dev/null || true + return 1 + fi +} + +# 智能设备绕过器 - 根据环境自动选择最佳方案 +bypass_device_detection() { + log "INFO" "🔧 开始智能设备识别绕过..." + + # 根据环境兼容性选择方案 + if [ "$MAC_COMPATIBLE" = false ]; then + log "INFO" "💡 检测到MAC地址修改受限,使用JS内核修改方案" + return modify_js_kernel + else + log "INFO" "💡 尝试MAC地址修改方案" + if modify_mac_address; then + return 0 + else + log "WARN" "⚠️ MAC地址修改失败,切换到JS内核修改" + return modify_js_kernel + fi + fi +} + +# MAC地址修改器(简化版) +modify_mac_address() { + log "INFO" "🌐 开始MAC地址修改..." + + # 获取活动网络接口 + local interfaces=() + while IFS= read -r line; do + if [[ $line == "Hardware Port: Wi-Fi" || $line == "Hardware Port: Ethernet" ]]; then + read -r dev_line + local device=$(echo "$dev_line" | awk '{print $2}') + if [ -n "$device" ] && ifconfig "$device" 2>/dev/null | grep -q "status: active"; then + interfaces+=("$device") + fi + fi + done < <(networksetup -listallhardwareports) + + if [ ${#interfaces[@]} -eq 0 ]; then + log "WARN" "⚠️ 未找到活动网络接口" + return 1 + fi + + local success_count=0 + for interface in "${interfaces[@]}"; do + log "INFO" "🔧 处理接口: $interface" + + # 生成新MAC地址 + local new_mac=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' \ + $(( (RANDOM & 0xFC) | 0x02 )) $((RANDOM%256)) $((RANDOM%256)) \ + $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))) + + # 尝试修改MAC地址 + if sudo ifconfig "$interface" down 2>/dev/null && \ + sleep 2 && \ + sudo ifconfig "$interface" ether "$new_mac" 2>/dev/null && \ + sudo ifconfig "$interface" up 2>/dev/null; then + log "INFO" "✅ 接口 $interface MAC地址修改成功: $new_mac" + ((success_count++)) + else + log "WARN" "⚠️ 接口 $interface MAC地址修改失败" + fi + sleep 2 + done + + if [ $success_count -gt 0 ]; then + log "INFO" "✅ MAC地址修改完成 ($success_count/${#interfaces[@]} 成功)" + return 0 + else + return 1 + fi +} + +# JS内核修改器(简化版) +modify_js_kernel() { + log "INFO" "🔧 开始JS内核修改..." + + # 关闭Cursor + manage_cursor_process "kill" + + # 目标JS文件 + local js_files=( + "$CURSOR_APP_PATH/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "$CURSOR_APP_PATH/Contents/Resources/app/out/main.js" + ) + + # 检查是否需要修改 + local need_modify=false + for file in "${js_files[@]}"; do + if [ -f "$file" ] && ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then + need_modify=true + break + fi + done + + if [ "$need_modify" = false ]; then + log "INFO" "✅ JS文件已修改,跳过" + return 0 + fi + + # 创建备份 + local backup_app="/tmp/Cursor.app.backup_$(date +%Y%m%d_%H%M%S)" + cp -R "$CURSOR_APP_PATH" "$backup_app" || { + log "ERROR" "❌ 创建备份失败" + return 1 + fi + + # 生成设备ID + local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') + local machine_id="auth0|user_$(openssl rand -hex 16)" + + # 修改JS文件 + local modified_count=0 + for file in "${js_files[@]}"; do + if [ ! -f "$file" ]; then + continue + fi + + log "INFO" "📝 处理文件: $(basename "$file")" + + # 创建注入代码 + local inject_code=" +// Cursor设备标识符劫持 - $(date +%Y%m%d%H%M%S) +import crypto from 'crypto'; +const originalRandomUUID = crypto.randomUUID; +crypto.randomUUID = function() { return '$new_uuid'; }; +globalThis.getMachineId = function() { return '$machine_id'; }; +console.log('Cursor设备标识符已劫持'); +" + + # 注入代码 + echo "$inject_code" > "${file}.new" + cat "$file" >> "${file}.new" + mv "${file}.new" "$file" + + ((modified_count++)) + log "INFO" "✅ 文件修改成功: $(basename "$file")" + done + + if [ $modified_count -gt 0 ]; then + # 重新签名 + if codesign --sign - --force --deep "$CURSOR_APP_PATH" 2>/dev/null; then + log "INFO" "✅ JS内核修改完成 ($modified_count 个文件)" + return 0 + else + log "WARN" "⚠️ 签名失败,但修改已完成" + return 0 + fi + else + log "ERROR" "❌ 未修改任何文件" + return 1 + fi +} + +# 环境重置器 +reset_environment() { + log "INFO" "🗑️ 开始环境重置..." + + # 关闭Cursor + manage_cursor_process "kill" + + # 删除目标文件夹 + local folders=( + "$HOME/Library/Application Support/Cursor" + "$HOME/.cursor" + ) + + local deleted_count=0 + for folder in "${folders[@]}"; do + if [ -d "$folder" ]; then + if rm -rf "$folder"; then + log "INFO" "✅ 已删除: $folder" + ((deleted_count++)) + else + log "ERROR" "❌ 删除失败: $folder" + fi + fi + done + + # 修复权限 + fix_permissions + + log "INFO" "✅ 环境重置完成 (删除 $deleted_count 个文件夹)" + return 0 +} + +# 禁用自动更新 +disable_auto_update() { + log "INFO" "🚫 禁用自动更新..." + + local app_update_yml="$CURSOR_APP_PATH/Contents/Resources/app-update.yml" + local updater_path="$HOME/Library/Application Support/Caches/cursor-updater" + + # 禁用app-update.yml + if [ -f "$app_update_yml" ]; then + sudo cp "$app_update_yml" "${app_update_yml}.bak" 2>/dev/null || true + sudo bash -c "echo '' > \"$app_update_yml\"" 2>/dev/null || true + sudo chmod 444 "$app_update_yml" 2>/dev/null || true + fi + + # 禁用cursor-updater + sudo rm -rf "$updater_path" 2>/dev/null || true + sudo touch "$updater_path" 2>/dev/null || true + sudo chmod 444 "$updater_path" 2>/dev/null || true + + log "INFO" "✅ 自动更新已禁用" +} + +# 修复应用签名问题 +fix_app_signature() { + log "INFO" "🔧 修复应用签名..." + + # 移除隔离属性 + sudo find "$CURSOR_APP_PATH" -print0 2>/dev/null | \ + xargs -0 sudo xattr -d com.apple.quarantine 2>/dev/null || true + + # 重新签名 + sudo codesign --force --deep --sign - "$CURSOR_APP_PATH" 2>/dev/null || true + + log "INFO" "✅ 应用签名修复完成" +} + +# ==================== 主执行流程 ==================== + +# 快速模式 - 仅修改机器码 +quick_mode() { + log "INFO" "🚀 执行快速模式(仅修改机器码)..." + + # 检查环境 + if ! detect_environment; then + handle_error "环境检测失败" "exit 1" + return 1 + fi + + # 预修复权限 + fix_permissions + + # 修改机器码 + if ! modify_machine_code; then + handle_error "机器码修改失败" "exit 1" + return 1 + fi + + # 设备绕过 + bypass_device_detection || log "WARN" "⚠️ 设备绕过失败,但机器码修改已完成" + + # 禁用更新 + disable_auto_update + + # 修复签名 + fix_app_signature + + # 最终权限修复 + fix_permissions + + log "INFO" "🎉 快速模式执行完成!" + return 0 +} + +# 完整模式 - 重置环境+修改机器码 +full_mode() { + log "INFO" "🚀 执行完整模式(重置环境+修改机器码)..." + + # 检查环境 + if ! detect_environment; then + handle_error "环境检测失败" "exit 1" + return 1 + fi + + # 环境重置 + if ! reset_environment; then + handle_error "环境重置失败" "exit 1" + return 1 + fi + + # 启动Cursor生成配置 + manage_cursor_process "start" + + # 等待配置文件生成 + local config_wait=0 + while [ ! -f "$STORAGE_FILE" ] && [ $config_wait -lt 30 ]; do + sleep 2 + ((config_wait += 2)) + log "INFO" "⏳ 等待配置文件生成... ($config_wait/30秒)" + done + + # 关闭Cursor + manage_cursor_process "kill" + + # 修改机器码 + if ! modify_machine_code; then + handle_error "机器码修改失败" "exit 1" + return 1 + fi + + # 设备绕过 + bypass_device_detection || log "WARN" "⚠️ 设备绕过失败,但机器码修改已完成" + + # 禁用更新 + disable_auto_update + + # 修复签名 + fix_app_signature + + # 最终权限修复 + fix_permissions + + log "INFO" "🎉 完整模式执行完成!" + return 0 +} + +# ==================== 用户界面 ==================== + +# 显示Logo和信息 +show_header() { + clear + echo -e " + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ + ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ + ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ + ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ + " + echo -e "${BLUE}================================${NC}" + echo -e "${GREEN}🚀 Cursor 机器码修改工具 (重构版) ${NC}" + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】 ${NC}" + echo -e "${BLUE}================================${NC}" + echo + echo -e "${YELLOW}💡 [免费工具]${NC} 如果对您有帮助,请关注公众号支持开发者" + echo +} + +# 用户选择菜单 +user_menu() { + echo -e "${GREEN}🎯 [选择模式]${NC} 请选择执行模式:" + echo + echo -e "${BLUE} 1️⃣ 快速模式 - 仅修改机器码${NC}" + echo -e "${YELLOW} • 保留现有配置和数据${NC}" + echo -e "${YELLOW} • 执行时间约30秒${NC}" + echo -e "${YELLOW} • 自动权限修复${NC}" + echo + echo -e "${BLUE} 2️⃣ 完整模式 - 重置环境+修改机器码${NC}" + echo -e "${RED} • 删除所有Cursor配置(请备份)${NC}" + echo -e "${YELLOW} • 执行时间约90秒${NC}" + echo -e "${YELLOW} • 彻底重置试用状态${NC}" + echo + + while true; do + read -p "请输入选择 (1 或 2): " choice + case "$choice" in + 1) + log "INFO" "✅ 用户选择:快速模式" + return 1 + ;; + 2) + echo -e "${RED}⚠️ [警告]${NC} 完整模式将删除所有Cursor配置!" + read -p "确认执行?(输入 yes 确认): " confirm + if [ "$confirm" = "yes" ]; then + log "INFO" "✅ 用户选择:完整模式" + return 2 + else + echo -e "${YELLOW}👋 [取消]${NC} 请重新选择" + continue + fi + ;; + *) + echo -e "${RED}❌ [错误]${NC} 无效选择,请输入 1 或 2" + ;; + esac + done +} + +# 显示完成信息 +show_completion() { + echo + echo -e "${GREEN}================================${NC}" + echo -e "${BLUE} 🎯 执行完成总结 ${NC}" + echo -e "${GREEN}================================${NC}" + echo -e "${GREEN}✅ 机器码配置: 已修改${NC}" + echo -e "${GREEN}✅ 设备识别绕过: 已完成${NC}" + echo -e "${GREEN}✅ 自动更新: 已禁用${NC}" + echo -e "${GREEN}✅ 权限修复: 已完成${NC}" + echo -e "${GREEN}✅ 应用签名: 已修复${NC}" + echo -e "${GREEN}================================${NC}" + echo + echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】获取更多Cursor技巧${NC}" + echo + echo -e "${BLUE}🚀 [下一步]${NC} 现在可以启动Cursor使用了!" + echo -e "${BLUE}📄 [日志]${NC} 详细日志保存在: $LOG_FILE" + echo +} + +# ==================== 主函数 ==================== + +main() { + # 检查权限 + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}❌ [错误]${NC} 请使用 sudo 运行此脚本" + echo "示例: sudo $0" + exit 1 + fi + + # 检查macOS + if [[ $(uname) != "Darwin" ]]; then + echo -e "${RED}❌ [错误]${NC} 本脚本仅支持 macOS 系统" + exit 1 + fi + + # 初始化 + init_log + log "INFO" "🚀 Cursor重构脚本启动..." + + # 预修复权限 + fix_permissions + + # 显示界面 + show_header + + # 用户选择 + user_menu + local mode=$? + + echo + log "INFO" "🚀 开始执行,请稍候..." + echo + + # 执行对应模式 + case $mode in + 1) + if quick_mode; then + show_completion + exit 0 + else + log "ERROR" "❌ 快速模式执行失败" + exit 1 + fi + ;; + 2) + if full_mode; then + show_completion + exit 0 + else + log "ERROR" "❌ 完整模式执行失败" + exit 1 + fi + ;; + esac +} + +# 执行主函数 +main "$@" diff --git a/scripts/run/cursor_win_id_modifier.ps1 b/scripts/run/cursor_win_id_modifier.ps1 index 7fdcec6a..6b276395 100644 --- a/scripts/run/cursor_win_id_modifier.ps1 +++ b/scripts/run/cursor_win_id_modifier.ps1 @@ -1,4 +1,4 @@ -# 设置输出编码为 UTF-8 +# 设置输出编码为 UTF-8 $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 @@ -13,6 +13,1144 @@ $NC = "`e[0m" $STORAGE_FILE = "$env:APPDATA\Cursor\User\globalStorage\storage.json" $BACKUP_DIR = "$env:APPDATA\Cursor\User\globalStorage\backups" +# PowerShell原生方法生成随机字符串 +function Generate-RandomString { + param([int]$Length) + $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + $result = "" + for ($i = 0; $i -lt $Length; $i++) { + $result += $chars[(Get-Random -Maximum $chars.Length)] + } + return $result +} + +# 修改Cursor内核JS文件实现设备识别绕过(从macOS版本移植) +function Modify-CursorJSFiles { + Write-Host "" + Write-Host "$BLUE🔧 [内核修改]$NC 开始修改Cursor内核JS文件实现设备识别绕过..." + Write-Host "" + + # Windows版Cursor应用路径 + $cursorAppPath = "${env:LOCALAPPDATA}\Programs\Cursor" + if (-not (Test-Path $cursorAppPath)) { + # 尝试其他可能的安装路径 + $alternatePaths = @( + "${env:ProgramFiles}\Cursor", + "${env:ProgramFiles(x86)}\Cursor", + "${env:USERPROFILE}\AppData\Local\Programs\Cursor" + ) + + foreach ($path in $alternatePaths) { + if (Test-Path $path) { + $cursorAppPath = $path + break + } + } + + if (-not (Test-Path $cursorAppPath)) { + Write-Host "$RED❌ [错误]$NC 未找到Cursor应用安装路径" + Write-Host "$YELLOW💡 [提示]$NC 请确认Cursor已正确安装" + return $false + } + } + + Write-Host "$GREEN✅ [发现]$NC 找到Cursor安装路径: $cursorAppPath" + + # 生成新的设备标识符 + $newUuid = [System.Guid]::NewGuid().ToString().ToLower() + $machineId = "auth0|user_$(Generate-RandomString -Length 32)" + $deviceId = [System.Guid]::NewGuid().ToString().ToLower() + $macMachineId = Generate-RandomString -Length 64 + + Write-Host "$GREEN🔑 [生成]$NC 已生成新的设备标识符" + + # 目标JS文件列表(Windows路径) + $jsFiles = @( + "$cursorAppPath\resources\app\out\vs\workbench\api\node\extensionHostProcess.js", + "$cursorAppPath\resources\app\out\main.js", + "$cursorAppPath\resources\app\out\vs\code\node\cliProcessMain.js" + ) + + $modifiedCount = 0 + $needModification = $false + + # 检查是否需要修改 + Write-Host "$BLUE🔍 [检查]$NC 检查JS文件修改状态..." + foreach ($file in $jsFiles) { + if (-not (Test-Path $file)) { + Write-Host "$YELLOW⚠️ [警告]$NC 文件不存在: $(Split-Path $file -Leaf)" + continue + } + + $content = Get-Content $file -Raw -ErrorAction SilentlyContinue + if ($content -and $content -notmatch "return crypto\.randomUUID\(\)") { + Write-Host "$BLUE📝 [需要]$NC 文件需要修改: $(Split-Path $file -Leaf)" + $needModification = $true + break + } else { + Write-Host "$GREEN✅ [已修改]$NC 文件已修改: $(Split-Path $file -Leaf)" + } + } + + if (-not $needModification) { + Write-Host "$GREEN✅ [跳过]$NC 所有JS文件已经被修改过,无需重复操作" + return $true + } + + # 关闭Cursor进程 + Write-Host "$BLUE🔄 [关闭]$NC 关闭Cursor进程以进行文件修改..." + Stop-AllCursorProcesses -MaxRetries 3 -WaitSeconds 3 | Out-Null + + # 创建备份 + $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" + $backupPath = "$env:TEMP\Cursor_JS_Backup_$timestamp" + + Write-Host "$BLUE💾 [备份]$NC 创建Cursor JS文件备份..." + try { + New-Item -ItemType Directory -Path $backupPath -Force | Out-Null + foreach ($file in $jsFiles) { + if (Test-Path $file) { + $fileName = Split-Path $file -Leaf + Copy-Item $file "$backupPath\$fileName" -Force + } + } + Write-Host "$GREEN✅ [备份]$NC 备份创建成功: $backupPath" + } catch { + Write-Host "$RED❌ [错误]$NC 创建备份失败: $($_.Exception.Message)" + return $false + } + + # 修改JS文件 + Write-Host "$BLUE🔧 [修改]$NC 开始修改JS文件..." + + foreach ($file in $jsFiles) { + if (-not (Test-Path $file)) { + Write-Host "$YELLOW⚠️ [跳过]$NC 文件不存在: $(Split-Path $file -Leaf)" + continue + } + + Write-Host "$BLUE📝 [处理]$NC 正在处理: $(Split-Path $file -Leaf)" + + try { + $content = Get-Content $file -Raw -Encoding UTF8 + + # 检查是否已经修改过 + if ($content -match "return crypto\.randomUUID\(\)" -or $content -match "// Cursor ID 修改工具注入") { + Write-Host "$GREEN✅ [跳过]$NC 文件已经被修改过" + $modifiedCount++ + continue + } + + # ES模块兼容的JavaScript注入代码 + $timestampVar = [DateTimeOffset]::Now.ToUnixTimeSeconds() + $injectCode = @" +// Cursor ID 修改工具注入 - $(Get-Date) - ES模块兼容版本 +import crypto from 'crypto'; + +// 保存原始函数引用 +const originalRandomUUID_${timestampVar} = crypto.randomUUID; + +// 重写crypto.randomUUID方法 +crypto.randomUUID = function() { + return '${newUuid}'; +}; + +// 覆盖所有可能的系统ID获取函数 - ES模块兼容版本 +globalThis.getMachineId = function() { return '${machineId}'; }; +globalThis.getDeviceId = function() { return '${deviceId}'; }; +globalThis.macMachineId = '${macMachineId}'; + +// 确保在不同环境下都能访问 +if (typeof window !== 'undefined') { + window.getMachineId = globalThis.getMachineId; + window.getDeviceId = globalThis.getDeviceId; + window.macMachineId = globalThis.macMachineId; +} + +// 确保模块顶层执行 +console.log('Cursor设备标识符已成功劫持 - ES模块版本 煎饼果子(86) 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬)'); + +"@ + + # 方法1: 查找IOPlatformUUID相关函数 + if ($content -match "IOPlatformUUID") { + Write-Host "$BLUE🔍 [发现]$NC 找到IOPlatformUUID关键字" + + # 针对不同的函数模式进行修改 + if ($content -match "function a\$") { + $content = $content -replace "function a\$\(t\)\{switch", "function a`$(t){return crypto.randomUUID(); switch" + Write-Host "$GREEN✅ [成功]$NC 修改a`$函数成功" + $modifiedCount++ + continue + } + + # 通用注入方法 + $content = $injectCode + $content + Write-Host "$GREEN✅ [成功]$NC 通用注入方法修改成功" + $modifiedCount++ + } + # 方法2: 查找其他设备ID相关函数 + elseif ($content -match "function t\$\(\)" -or $content -match "async function y5") { + Write-Host "$BLUE🔍 [发现]$NC 找到设备ID相关函数" + + # 修改MAC地址获取函数 + if ($content -match "function t\$\(\)") { + $content = $content -replace "function t\$\(\)\{", "function t`$(){return `"00:00:00:00:00:00`";" + Write-Host "$GREEN✅ [成功]$NC 修改MAC地址获取函数" + } + + # 修改设备ID获取函数 + if ($content -match "async function y5") { + $content = $content -replace "async function y5\(t\)\{", "async function y5(t){return crypto.randomUUID();" + Write-Host "$GREEN✅ [成功]$NC 修改设备ID获取函数" + } + + $modifiedCount++ + } + else { + Write-Host "$YELLOW⚠️ [警告]$NC 未找到已知的设备ID函数模式,使用通用注入" + $content = $injectCode + $content + $modifiedCount++ + } + + # 写入修改后的内容 + Set-Content -Path $file -Value $content -Encoding UTF8 -NoNewline + Write-Host "$GREEN✅ [完成]$NC 文件修改完成: $(Split-Path $file -Leaf)" + + } catch { + Write-Host "$RED❌ [错误]$NC 修改文件失败: $($_.Exception.Message)" + # 尝试从备份恢复 + $fileName = Split-Path $file -Leaf + $backupFile = "$backupPath\$fileName" + if (Test-Path $backupFile) { + Copy-Item $backupFile $file -Force + Write-Host "$YELLOW🔄 [恢复]$NC 已从备份恢复文件" + } + } + } + + if ($modifiedCount -gt 0) { + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 成功修改 $modifiedCount 个JS文件" + Write-Host "$BLUE💾 [备份]$NC 原始文件备份位置: $backupPath" + Write-Host "$BLUE💡 [说明]$NC JavaScript注入功能已启用,实现设备识别绕过" + return $true + } else { + Write-Host "$RED❌ [失败]$NC 没有成功修改任何文件" + return $false + } +} + + +# 🚀 新增 Cursor 防掉试用Pro删除文件夹功能 +function Remove-CursorTrialFolders { + Write-Host "" + Write-Host "$GREEN🎯 [核心功能]$NC 正在执行 Cursor 防掉试用Pro删除文件夹..." + Write-Host "$BLUE📋 [说明]$NC 此功能将删除指定的Cursor相关文件夹以重置试用状态" + Write-Host "" + + # 定义需要删除的文件夹路径 + $foldersToDelete = @() + + # Windows Administrator 用户路径 + $adminPaths = @( + "C:\Users\Administrator\.cursor", + "C:\Users\Administrator\AppData\Roaming\Cursor" + ) + + # 当前用户路径 + $currentUserPaths = @( + "$env:USERPROFILE\.cursor", + "$env:APPDATA\Cursor" + ) + + # 合并所有路径 + $foldersToDelete += $adminPaths + $foldersToDelete += $currentUserPaths + + Write-Host "$BLUE📂 [检测]$NC 将检查以下文件夹:" + foreach ($folder in $foldersToDelete) { + Write-Host " 📁 $folder" + } + Write-Host "" + + $deletedCount = 0 + $skippedCount = 0 + $errorCount = 0 + + # 删除指定文件夹 + foreach ($folder in $foldersToDelete) { + Write-Host "$BLUE🔍 [检查]$NC 检查文件夹: $folder" + + if (Test-Path $folder) { + try { + Write-Host "$YELLOW⚠️ [警告]$NC 发现文件夹存在,正在删除..." + Remove-Item -Path $folder -Recurse -Force -ErrorAction Stop + Write-Host "$GREEN✅ [成功]$NC 已删除文件夹: $folder" + $deletedCount++ + } + catch { + Write-Host "$RED❌ [错误]$NC 删除文件夹失败: $folder" + Write-Host "$RED💥 [详情]$NC 错误信息: $($_.Exception.Message)" + $errorCount++ + } + } else { + Write-Host "$YELLOW⏭️ [跳过]$NC 文件夹不存在: $folder" + $skippedCount++ + } + Write-Host "" + } + + # 显示操作统计 + Write-Host "$GREEN📊 [统计]$NC 操作完成统计:" + Write-Host " ✅ 成功删除: $deletedCount 个文件夹" + Write-Host " ⏭️ 跳过处理: $skippedCount 个文件夹" + Write-Host " ❌ 删除失败: $errorCount 个文件夹" + Write-Host "" + + if ($deletedCount -gt 0) { + Write-Host "$GREEN🎉 [完成]$NC Cursor 防掉试用Pro文件夹删除完成!" + + # 🔧 预创建必要的目录结构,避免权限问题 + Write-Host "$BLUE🔧 [修复]$NC 预创建必要的目录结构以避免权限问题..." + + $cursorAppData = "$env:APPDATA\Cursor" + $cursorLocalAppData = "$env:LOCALAPPDATA\cursor" + $cursorUserProfile = "$env:USERPROFILE\.cursor" + + # 创建主要目录 + try { + if (-not (Test-Path $cursorAppData)) { + New-Item -ItemType Directory -Path $cursorAppData -Force | Out-Null + } + if (-not (Test-Path $cursorUserProfile)) { + New-Item -ItemType Directory -Path $cursorUserProfile -Force | Out-Null + } + Write-Host "$GREEN✅ [完成]$NC 目录结构预创建完成" + } catch { + Write-Host "$YELLOW⚠️ [警告]$NC 预创建目录时出现问题: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW🤔 [提示]$NC 未找到需要删除的文件夹,可能已经清理过了" + } + Write-Host "" +} + +# 🔄 重启Cursor并等待配置文件生成 +function Restart-CursorAndWait { + Write-Host "" + Write-Host "$GREEN🔄 [重启]$NC 正在重启Cursor以重新生成配置文件..." + + if (-not $global:CursorProcessInfo) { + Write-Host "$RED❌ [错误]$NC 未找到Cursor进程信息,无法重启" + return $false + } + + $cursorPath = $global:CursorProcessInfo.Path + + # 修复:确保路径是字符串类型 + if ($cursorPath -is [array]) { + $cursorPath = $cursorPath[0] + } + + # 验证路径不为空 + if ([string]::IsNullOrEmpty($cursorPath)) { + Write-Host "$RED❌ [错误]$NC Cursor路径为空" + return $false + } + + Write-Host "$BLUE📍 [路径]$NC 使用路径: $cursorPath" + + if (-not (Test-Path $cursorPath)) { + Write-Host "$RED❌ [错误]$NC Cursor可执行文件不存在: $cursorPath" + + # 尝试使用备用路径 + $backupPaths = @( + "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe", + "$env:PROGRAMFILES\Cursor\Cursor.exe", + "$env:PROGRAMFILES(X86)\Cursor\Cursor.exe" + ) + + $foundPath = $null + foreach ($backupPath in $backupPaths) { + if (Test-Path $backupPath) { + $foundPath = $backupPath + Write-Host "$GREEN💡 [发现]$NC 使用备用路径: $foundPath" + break + } + } + + if (-not $foundPath) { + Write-Host "$RED❌ [错误]$NC 无法找到有效的Cursor可执行文件" + return $false + } + + $cursorPath = $foundPath + } + + try { + Write-Host "$GREEN🚀 [启动]$NC 正在启动Cursor..." + $process = Start-Process -FilePath $cursorPath -PassThru -WindowStyle Hidden + + Write-Host "$YELLOW⏳ [等待]$NC 等待20秒让Cursor完全启动并生成配置文件..." + Start-Sleep -Seconds 20 + + # 检查配置文件是否生成 + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $maxWait = 45 + $waited = 0 + + while (-not (Test-Path $configPath) -and $waited -lt $maxWait) { + Write-Host "$YELLOW⏳ [等待]$NC 等待配置文件生成... ($waited/$maxWait 秒)" + Start-Sleep -Seconds 1 + $waited++ + } + + if (Test-Path $configPath) { + Write-Host "$GREEN✅ [成功]$NC 配置文件已生成: $configPath" + + # 额外等待确保文件完全写入 + Write-Host "$YELLOW⏳ [等待]$NC 等待5秒确保配置文件完全写入..." + Start-Sleep -Seconds 5 + } else { + Write-Host "$YELLOW⚠️ [警告]$NC 配置文件未在预期时间内生成" + Write-Host "$BLUE💡 [提示]$NC 可能需要手动启动Cursor一次来生成配置文件" + } + + # 强制关闭Cursor + Write-Host "$YELLOW🔄 [关闭]$NC 正在关闭Cursor以进行配置修改..." + if ($process -and -not $process.HasExited) { + $process.Kill() + $process.WaitForExit(5000) + } + + # 确保所有Cursor进程都关闭 + Get-Process -Name "Cursor" -ErrorAction SilentlyContinue | Stop-Process -Force + Get-Process -Name "cursor" -ErrorAction SilentlyContinue | Stop-Process -Force + + Write-Host "$GREEN✅ [完成]$NC Cursor重启流程完成" + return $true + + } catch { + Write-Host "$RED❌ [错误]$NC 重启Cursor失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [调试]$NC 错误详情: $($_.Exception.GetType().FullName)" + return $false + } +} + +# 🔒 强制关闭所有Cursor进程(增强版) +function Stop-AllCursorProcesses { + param( + [int]$MaxRetries = 3, + [int]$WaitSeconds = 5 + ) + + Write-Host "$BLUE🔒 [进程检查]$NC 正在检查并关闭所有Cursor相关进程..." + + # 定义所有可能的Cursor进程名称 + $cursorProcessNames = @( + "Cursor", + "cursor", + "Cursor Helper", + "Cursor Helper (GPU)", + "Cursor Helper (Plugin)", + "Cursor Helper (Renderer)", + "CursorUpdater" + ) + + for ($retry = 1; $retry -le $MaxRetries; $retry++) { + Write-Host "$BLUE🔍 [检查]$NC 第 $retry/$MaxRetries 次进程检查..." + + $foundProcesses = @() + foreach ($processName in $cursorProcessNames) { + $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue + if ($processes) { + $foundProcesses += $processes + Write-Host "$YELLOW⚠️ [发现]$NC 进程: $processName (PID: $($processes.Id -join ', '))" + } + } + + if ($foundProcesses.Count -eq 0) { + Write-Host "$GREEN✅ [成功]$NC 所有Cursor进程已关闭" + return $true + } + + Write-Host "$YELLOW🔄 [关闭]$NC 正在关闭 $($foundProcesses.Count) 个Cursor进程..." + + # 先尝试优雅关闭 + foreach ($process in $foundProcesses) { + try { + $process.CloseMainWindow() | Out-Null + Write-Host "$BLUE • 优雅关闭: $($process.ProcessName) (PID: $($process.Id))$NC" + } catch { + Write-Host "$YELLOW • 优雅关闭失败: $($process.ProcessName)$NC" + } + } + + Start-Sleep -Seconds 3 + + # 强制终止仍在运行的进程 + foreach ($processName in $cursorProcessNames) { + $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue + if ($processes) { + foreach ($process in $processes) { + try { + Stop-Process -Id $process.Id -Force + Write-Host "$RED • 强制终止: $($process.ProcessName) (PID: $($process.Id))$NC" + } catch { + Write-Host "$RED • 强制终止失败: $($process.ProcessName)$NC" + } + } + } + } + + if ($retry -lt $MaxRetries) { + Write-Host "$YELLOW⏳ [等待]$NC 等待 $WaitSeconds 秒后重新检查..." + Start-Sleep -Seconds $WaitSeconds + } + } + + Write-Host "$RED❌ [失败]$NC 经过 $MaxRetries 次尝试仍有Cursor进程在运行" + return $false +} + +# 🔐 检查文件权限和锁定状态 +function Test-FileAccessibility { + param( + [string]$FilePath + ) + + Write-Host "$BLUE🔐 [权限检查]$NC 检查文件访问权限: $(Split-Path $FilePath -Leaf)" + + if (-not (Test-Path $FilePath)) { + Write-Host "$RED❌ [错误]$NC 文件不存在" + return $false + } + + # 检查文件是否被锁定 + try { + $fileStream = [System.IO.File]::Open($FilePath, 'Open', 'ReadWrite', 'None') + $fileStream.Close() + Write-Host "$GREEN✅ [权限]$NC 文件可读写,无锁定" + return $true + } catch [System.IO.IOException] { + Write-Host "$RED❌ [锁定]$NC 文件被其他进程锁定: $($_.Exception.Message)" + return $false + } catch [System.UnauthorizedAccessException] { + Write-Host "$YELLOW⚠️ [权限]$NC 文件权限受限,尝试修改权限..." + + # 尝试修改文件权限 + try { + $file = Get-Item $FilePath + if ($file.IsReadOnly) { + $file.IsReadOnly = $false + Write-Host "$GREEN✅ [修复]$NC 已移除只读属性" + } + + # 再次测试 + $fileStream = [System.IO.File]::Open($FilePath, 'Open', 'ReadWrite', 'None') + $fileStream.Close() + Write-Host "$GREEN✅ [权限]$NC 权限修复成功" + return $true + } catch { + Write-Host "$RED❌ [权限]$NC 无法修复权限: $($_.Exception.Message)" + return $false + } + } catch { + Write-Host "$RED❌ [错误]$NC 未知错误: $($_.Exception.Message)" + return $false + } +} + +# 🧹 Cursor 初始化清理功能(从旧版本移植) +function Invoke-CursorInitialization { + Write-Host "" + Write-Host "$GREEN🧹 [初始化]$NC 正在执行 Cursor 初始化清理..." + $BASE_PATH = "$env:APPDATA\Cursor\User" + + $filesToDelete = @( + (Join-Path -Path $BASE_PATH -ChildPath "globalStorage\state.vscdb"), + (Join-Path -Path $BASE_PATH -ChildPath "globalStorage\state.vscdb.backup") + ) + + $folderToCleanContents = Join-Path -Path $BASE_PATH -ChildPath "History" + $folderToDeleteCompletely = Join-Path -Path $BASE_PATH -ChildPath "workspaceStorage" + + Write-Host "$BLUE🔍 [调试]$NC 基础路径: $BASE_PATH" + + # 删除指定文件 + foreach ($file in $filesToDelete) { + Write-Host "$BLUE🔍 [检查]$NC 检查文件: $file" + if (Test-Path $file) { + try { + Remove-Item -Path $file -Force -ErrorAction Stop + Write-Host "$GREEN✅ [成功]$NC 已删除文件: $file" + } + catch { + Write-Host "$RED❌ [错误]$NC 删除文件 $file 失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW⚠️ [跳过]$NC 文件不存在,跳过删除: $file" + } + } + + # 清空指定文件夹内容 + Write-Host "$BLUE🔍 [检查]$NC 检查待清空文件夹: $folderToCleanContents" + if (Test-Path $folderToCleanContents) { + try { + Get-ChildItem -Path $folderToCleanContents -Recurse | Remove-Item -Force -Recurse -ErrorAction Stop + Write-Host "$GREEN✅ [成功]$NC 已清空文件夹内容: $folderToCleanContents" + } + catch { + Write-Host "$RED❌ [错误]$NC 清空文件夹 $folderToCleanContents 失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW⚠️ [跳过]$NC 文件夹不存在,跳过清空: $folderToCleanContents" + } + + # 完全删除指定文件夹 + Write-Host "$BLUE🔍 [检查]$NC 检查待删除文件夹: $folderToDeleteCompletely" + if (Test-Path $folderToDeleteCompletely) { + try { + Remove-Item -Path $folderToDeleteCompletely -Recurse -Force -ErrorAction Stop + Write-Host "$GREEN✅ [成功]$NC 已删除文件夹: $folderToDeleteCompletely" + } + catch { + Write-Host "$RED❌ [错误]$NC 删除文件夹 $folderToDeleteCompletely 失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW⚠️ [跳过]$NC 文件夹不存在,跳过删除: $folderToDeleteCompletely" + } + + Write-Host "$GREEN✅ [完成]$NC Cursor 初始化清理完成" + Write-Host "" +} + +# 🔧 修改系统注册表 MachineGuid(从旧版本移植) +function Update-MachineGuid { + try { + Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表 MachineGuid..." + + # 检查注册表路径是否存在,不存在则创建 + $registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography" + if (-not (Test-Path $registryPath)) { + Write-Host "$YELLOW⚠️ [警告]$NC 注册表路径不存在: $registryPath,正在创建..." + New-Item -Path $registryPath -Force | Out-Null + Write-Host "$GREEN✅ [信息]$NC 注册表路径创建成功" + } + + # 获取当前的 MachineGuid,如果不存在则使用空字符串作为默认值 + $originalGuid = "" + try { + $currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction SilentlyContinue + if ($currentGuid) { + $originalGuid = $currentGuid.MachineGuid + Write-Host "$GREEN✅ [信息]$NC 当前注册表值:" + Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" + Write-Host " MachineGuid REG_SZ $originalGuid" + } else { + Write-Host "$YELLOW⚠️ [警告]$NC MachineGuid 值不存在,将创建新值" + } + } catch { + Write-Host "$YELLOW⚠️ [警告]$NC 读取注册表失败: $($_.Exception.Message)" + Write-Host "$YELLOW⚠️ [警告]$NC 将尝试创建新的 MachineGuid 值" + } + + # 创建备份文件(仅当原始值存在时) + $backupFile = $null + if ($originalGuid) { + $backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg" + Write-Host "$BLUE💾 [备份]$NC 正在备份注册表..." + $backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru + + if ($backupResult.ExitCode -eq 0) { + Write-Host "$GREEN✅ [备份]$NC 注册表项已备份到:$backupFile" + } else { + Write-Host "$YELLOW⚠️ [警告]$NC 备份创建失败,继续执行..." + $backupFile = $null + } + } + + # 生成新GUID + $newGuid = [System.Guid]::NewGuid().ToString() + Write-Host "$BLUE🔄 [生成]$NC 新的 MachineGuid: $newGuid" + + # 更新或创建注册表值 + Set-ItemProperty -Path $registryPath -Name MachineGuid -Value $newGuid -Force -ErrorAction Stop + + # 验证更新 + $verifyGuid = (Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop).MachineGuid + if ($verifyGuid -ne $newGuid) { + throw "注册表验证失败:更新后的值 ($verifyGuid) 与预期值 ($newGuid) 不匹配" + } + + Write-Host "$GREEN✅ [成功]$NC 注册表更新成功:" + Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" + Write-Host " MachineGuid REG_SZ $newGuid" + return $true + } + catch { + Write-Host "$RED❌ [错误]$NC 注册表操作失败:$($_.Exception.Message)" + + # 尝试恢复备份(如果存在) + if ($backupFile -and (Test-Path $backupFile)) { + Write-Host "$YELLOW🔄 [恢复]$NC 正在从备份恢复..." + $restoreResult = Start-Process "reg.exe" -ArgumentList "import", "`"$backupFile`"" -NoNewWindow -Wait -PassThru + + if ($restoreResult.ExitCode -eq 0) { + Write-Host "$GREEN✅ [恢复成功]$NC 已还原原始注册表值" + } else { + Write-Host "$RED❌ [错误]$NC 恢复失败,请手动导入备份文件:$backupFile" + } + } else { + Write-Host "$YELLOW⚠️ [警告]$NC 未找到备份文件或备份创建失败,无法自动恢复" + } + + return $false + } +} + +# 检查配置文件和环境 +function Test-CursorEnvironment { + param( + [string]$Mode = "FULL" + ) + + Write-Host "" + Write-Host "$BLUE🔍 [环境检查]$NC 正在检查Cursor环境..." + + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $cursorAppData = "$env:APPDATA\Cursor" + $issues = @() + + # 检查配置文件 + if (-not (Test-Path $configPath)) { + $issues += "配置文件不存在: $configPath" + } else { + try { + $content = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop + $config = $content | ConvertFrom-Json -ErrorAction Stop + Write-Host "$GREEN✅ [检查]$NC 配置文件格式正确" + } catch { + $issues += "配置文件格式错误: $($_.Exception.Message)" + } + } + + # 检查Cursor目录结构 + if (-not (Test-Path $cursorAppData)) { + $issues += "Cursor应用数据目录不存在: $cursorAppData" + } + + # 检查Cursor安装 + $cursorPaths = @( + "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe", + "$env:PROGRAMFILES\Cursor\Cursor.exe", + "$env:PROGRAMFILES(X86)\Cursor\Cursor.exe" + ) + + $cursorFound = $false + foreach ($path in $cursorPaths) { + if (Test-Path $path) { + Write-Host "$GREEN✅ [检查]$NC 找到Cursor安装: $path" + $cursorFound = $true + break + } + } + + if (-not $cursorFound) { + $issues += "未找到Cursor安装,请确认Cursor已正确安装" + } + + # 返回检查结果 + if ($issues.Count -eq 0) { + Write-Host "$GREEN✅ [环境检查]$NC 所有检查通过" + return @{ Success = $true; Issues = @() } + } else { + Write-Host "$RED❌ [环境检查]$NC 发现 $($issues.Count) 个问题:" + foreach ($issue in $issues) { + Write-Host "$RED • ${issue}$NC" + } + return @{ Success = $false; Issues = $issues } + } +} + +# �🛠️ 修改机器码配置(增强版) +function Modify-MachineCodeConfig { + param( + [string]$Mode = "FULL" + ) + + Write-Host "" + Write-Host "$GREEN🛠️ [配置]$NC 正在修改机器码配置..." + + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + + # 增强的配置文件检查 + if (-not (Test-Path $configPath)) { + Write-Host "$RED❌ [错误]$NC 配置文件不存在: $configPath" + Write-Host "" + Write-Host "$YELLOW💡 [解决方案]$NC 请尝试以下步骤:" + Write-Host "$BLUE 1️⃣ 手动启动Cursor应用程序$NC" + Write-Host "$BLUE 2️⃣ 等待Cursor完全加载(约30秒)$NC" + Write-Host "$BLUE 3️⃣ 关闭Cursor应用程序$NC" + Write-Host "$BLUE 4️⃣ 重新运行此脚本$NC" + Write-Host "" + Write-Host "$YELLOW⚠️ [备选方案]$NC 如果问题持续:" + Write-Host "$BLUE • 选择脚本的'重置环境+修改机器码'选项$NC" + Write-Host "$BLUE • 该选项会自动生成配置文件$NC" + Write-Host "" + + # 提供用户选择 + $userChoice = Read-Host "是否现在尝试启动Cursor生成配置文件?(y/n)" + if ($userChoice -match "^(y|yes)$") { + Write-Host "$BLUE🚀 [尝试]$NC 正在尝试启动Cursor..." + return Start-CursorToGenerateConfig + } + + return $false + } + + # 在仅修改机器码模式下也要确保进程完全关闭 + if ($Mode -eq "MODIFY_ONLY") { + Write-Host "$BLUE🔒 [安全检查]$NC 即使在仅修改模式下,也需要确保Cursor进程完全关闭" + if (-not (Stop-AllCursorProcesses -MaxRetries 3 -WaitSeconds 3)) { + Write-Host "$RED❌ [错误]$NC 无法关闭所有Cursor进程,修改可能失败" + $userChoice = Read-Host "是否强制继续?(y/n)" + if ($userChoice -notmatch "^(y|yes)$") { + return $false + } + } + } + + # 检查文件权限和锁定状态 + if (-not (Test-FileAccessibility -FilePath $configPath)) { + Write-Host "$RED❌ [错误]$NC 无法访问配置文件,可能被锁定或权限不足" + return $false + } + + # 验证配置文件格式并显示结构 + try { + Write-Host "$BLUE🔍 [验证]$NC 检查配置文件格式..." + $originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop + $config = $originalContent | ConvertFrom-Json -ErrorAction Stop + Write-Host "$GREEN✅ [验证]$NC 配置文件格式正确" + + # 显示当前配置文件中的相关属性 + Write-Host "$BLUE📋 [当前配置]$NC 检查现有的遥测属性:" + $telemetryProperties = @('telemetry.machineId', 'telemetry.macMachineId', 'telemetry.devDeviceId', 'telemetry.sqmId') + foreach ($prop in $telemetryProperties) { + if ($config.PSObject.Properties[$prop]) { + $value = $config.$prop + $displayValue = if ($value.Length -gt 20) { "$($value.Substring(0,20))..." } else { $value } + Write-Host "$GREEN ✓ ${prop}$NC = $displayValue" + } else { + Write-Host "$YELLOW - ${prop}$NC (不存在,将创建)" + } + } + Write-Host "" + } catch { + Write-Host "$RED❌ [错误]$NC 配置文件格式错误: $($_.Exception.Message)" + Write-Host "$YELLOW💡 [建议]$NC 配置文件可能已损坏,建议选择'重置环境+修改机器码'选项" + return $false + } + + # 实现原子性文件操作和重试机制 + $maxRetries = 3 + $retryCount = 0 + + while ($retryCount -lt $maxRetries) { + $retryCount++ + Write-Host "" + Write-Host "$BLUE🔄 [尝试]$NC 第 $retryCount/$maxRetries 次修改尝试..." + + try { + # 显示操作进度 + Write-Host "$BLUE⏳ [进度]$NC 1/6 - 生成新的设备标识符..." + + # 生成新的ID + $MAC_MACHINE_ID = [System.Guid]::NewGuid().ToString() + $UUID = [System.Guid]::NewGuid().ToString() + $prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_") + $prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ }) + $randomBytes = New-Object byte[] 32 + $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new() + $rng.GetBytes($randomBytes) + $randomPart = [System.BitConverter]::ToString($randomBytes) -replace '-','' + $rng.Dispose() + $MACHINE_ID = "${prefixHex}${randomPart}" + $SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}" + + Write-Host "$GREEN✅ [进度]$NC 1/6 - 设备标识符生成完成" + + Write-Host "$BLUE⏳ [进度]$NC 2/6 - 创建备份目录..." + + # 备份原始值(增强版) + $backupDir = "$env:APPDATA\Cursor\User\globalStorage\backups" + if (-not (Test-Path $backupDir)) { + New-Item -ItemType Directory -Path $backupDir -Force -ErrorAction Stop | Out-Null + } + + $backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')_retry$retryCount" + $backupPath = "$backupDir\$backupName" + + Write-Host "$BLUE⏳ [进度]$NC 3/6 - 备份原始配置..." + Copy-Item $configPath $backupPath -ErrorAction Stop + + # 验证备份是否成功 + if (Test-Path $backupPath) { + $backupSize = (Get-Item $backupPath).Length + $originalSize = (Get-Item $configPath).Length + if ($backupSize -eq $originalSize) { + Write-Host "$GREEN✅ [进度]$NC 3/6 - 配置备份成功: $backupName" + } else { + Write-Host "$YELLOW⚠️ [警告]$NC 备份文件大小不匹配,但继续执行" + } + } else { + throw "备份文件创建失败" + } + + Write-Host "$BLUE⏳ [进度]$NC 4/6 - 读取原始配置到内存..." + + # 原子性操作:读取原始内容到内存 + $originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop + $config = $originalContent | ConvertFrom-Json -ErrorAction Stop + + Write-Host "$BLUE⏳ [进度]$NC 5/6 - 在内存中更新配置..." + + # 更新配置值(安全方式,确保属性存在) + $propertiesToUpdate = @{ + 'telemetry.machineId' = $MACHINE_ID + 'telemetry.macMachineId' = $MAC_MACHINE_ID + 'telemetry.devDeviceId' = $UUID + 'telemetry.sqmId' = $SQM_ID + } + + foreach ($property in $propertiesToUpdate.GetEnumerator()) { + $key = $property.Key + $value = $property.Value + + # 使用 Add-Member 或直接赋值的安全方式 + if ($config.PSObject.Properties[$key]) { + # 属性存在,直接更新 + $config.$key = $value + Write-Host "$BLUE ✓ 更新属性: ${key}$NC" + } else { + # 属性不存在,添加新属性 + $config | Add-Member -MemberType NoteProperty -Name $key -Value $value -Force + Write-Host "$BLUE + 添加属性: ${key}$NC" + } + } + + Write-Host "$BLUE⏳ [进度]$NC 6/6 - 原子性写入新配置文件..." + + # 原子性操作:删除原文件,写入新文件 + $tempPath = "$configPath.tmp" + $updatedJson = $config | ConvertTo-Json -Depth 10 + + # 写入临时文件 + [System.IO.File]::WriteAllText($tempPath, $updatedJson, [System.Text.Encoding]::UTF8) + + # 验证临时文件 + $tempContent = Get-Content $tempPath -Raw -Encoding UTF8 + $tempConfig = $tempContent | ConvertFrom-Json + + # 验证所有属性是否正确写入 + $tempVerificationPassed = $true + foreach ($property in $propertiesToUpdate.GetEnumerator()) { + $key = $property.Key + $expectedValue = $property.Value + $actualValue = $tempConfig.$key + + if ($actualValue -ne $expectedValue) { + $tempVerificationPassed = $false + Write-Host "$RED ✗ 临时文件验证失败: ${key}$NC" + break + } + } + + if (-not $tempVerificationPassed) { + Remove-Item $tempPath -Force -ErrorAction SilentlyContinue + throw "临时文件验证失败" + } + + # 原子性替换:删除原文件,重命名临时文件 + Remove-Item $configPath -Force + Move-Item $tempPath $configPath + + # 设置文件为只读(可选) + $file = Get-Item $configPath + $file.IsReadOnly = $false # 保持可写,便于后续修改 + + # 最终验证修改结果 + Write-Host "$BLUE🔍 [最终验证]$NC 验证新配置文件..." + + $verifyContent = Get-Content $configPath -Raw -Encoding UTF8 + $verifyConfig = $verifyContent | ConvertFrom-Json + + $verificationPassed = $true + $verificationResults = @() + + # 安全验证每个属性 + foreach ($property in $propertiesToUpdate.GetEnumerator()) { + $key = $property.Key + $expectedValue = $property.Value + $actualValue = $verifyConfig.$key + + if ($actualValue -eq $expectedValue) { + $verificationResults += "✓ ${key}: 验证通过" + } else { + $verificationResults += "✗ ${key}: 验证失败 (期望: ${expectedValue}, 实际: ${actualValue})" + $verificationPassed = $false + } + } + + # 显示验证结果 + Write-Host "$BLUE📋 [验证详情]$NC" + foreach ($result in $verificationResults) { + Write-Host " $result" + } + + if ($verificationPassed) { + Write-Host "$GREEN✅ [成功]$NC 第 $retryCount 次尝试修改成功!" + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 机器码配置修改完成!" + Write-Host "$BLUE📋 [详情]$NC 已更新以下标识符:" + Write-Host " 🔹 machineId: $MACHINE_ID" + Write-Host " 🔹 macMachineId: $MAC_MACHINE_ID" + Write-Host " 🔹 devDeviceId: $UUID" + Write-Host " 🔹 sqmId: $SQM_ID" + Write-Host "" + Write-Host "$GREEN💾 [备份]$NC 原配置已备份至: $backupName" + + # 🔒 添加配置文件保护机制 + Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." + try { + $configFile = Get-Item $configPath + $configFile.IsReadOnly = $true + Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改" + Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath" + } catch { + Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'" + } + Write-Host "$BLUE 🔒 [安全]$NC 建议重启Cursor以确保配置生效" + return $true + } else { + Write-Host "$RED❌ [失败]$NC 第 $retryCount 次尝试验证失败" + if ($retryCount -lt $maxRetries) { + Write-Host "$BLUE🔄 [恢复]$NC 恢复备份,准备重试..." + Copy-Item $backupPath $configPath -Force + Start-Sleep -Seconds 2 + continue # 继续下一次重试 + } else { + Write-Host "$RED❌ [最终失败]$NC 所有重试都失败,恢复原始配置" + Copy-Item $backupPath $configPath -Force + return $false + } + } + + } catch { + Write-Host "$RED❌ [异常]$NC 第 $retryCount 次尝试出现异常: $($_.Exception.Message)" + Write-Host "$BLUE💡 [调试信息]$NC 错误类型: $($_.Exception.GetType().FullName)" + + # 清理临时文件 + if (Test-Path "$configPath.tmp") { + Remove-Item "$configPath.tmp" -Force -ErrorAction SilentlyContinue + } + + if ($retryCount -lt $maxRetries) { + Write-Host "$BLUE🔄 [恢复]$NC 恢复备份,准备重试..." + if (Test-Path $backupPath) { + Copy-Item $backupPath $configPath -Force + } + Start-Sleep -Seconds 3 + continue # 继续下一次重试 + } else { + Write-Host "$RED❌ [最终失败]$NC 所有重试都失败" + # 尝试恢复备份 + if (Test-Path $backupPath) { + Write-Host "$BLUE🔄 [恢复]$NC 正在恢复备份配置..." + try { + Copy-Item $backupPath $configPath -Force + Write-Host "$GREEN✅ [恢复]$NC 已恢复原始配置" + } catch { + Write-Host "$RED❌ [错误]$NC 恢复备份失败: $($_.Exception.Message)" + } + } + return $false + } + } + } + + # 如果到达这里,说明所有重试都失败了 + Write-Host "$RED❌ [最终失败]$NC 经过 $maxRetries 次尝试仍无法完成修改" + return $false + +} + +# 启动Cursor生成配置文件 +function Start-CursorToGenerateConfig { + Write-Host "$BLUE🚀 [启动]$NC 正在尝试启动Cursor生成配置文件..." + + # 查找Cursor可执行文件 + $cursorPaths = @( + "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe", + "$env:PROGRAMFILES\Cursor\Cursor.exe", + "$env:PROGRAMFILES(X86)\Cursor\Cursor.exe" + ) + + $cursorPath = $null + foreach ($path in $cursorPaths) { + if (Test-Path $path) { + $cursorPath = $path + break + } + } + + if (-not $cursorPath) { + Write-Host "$RED❌ [错误]$NC 未找到Cursor安装,请确认Cursor已正确安装" + return $false + } + + try { + Write-Host "$BLUE📍 [路径]$NC 使用Cursor路径: $cursorPath" + + # 启动Cursor + $process = Start-Process -FilePath $cursorPath -PassThru -WindowStyle Normal + Write-Host "$GREEN🚀 [启动]$NC Cursor已启动,PID: $($process.Id)" + + Write-Host "$YELLOW⏳ [等待]$NC 请等待Cursor完全加载(约30秒)..." + Write-Host "$BLUE💡 [提示]$NC 您可以在Cursor完全加载后手动关闭它" + + # 等待配置文件生成 + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $maxWait = 60 + $waited = 0 + + while (-not (Test-Path $configPath) -and $waited -lt $maxWait) { + Start-Sleep -Seconds 2 + $waited += 2 + if ($waited % 10 -eq 0) { + Write-Host "$YELLOW⏳ [等待]$NC 等待配置文件生成... ($waited/$maxWait 秒)" + } + } + + if (Test-Path $configPath) { + Write-Host "$GREEN✅ [成功]$NC 配置文件已生成!" + Write-Host "$BLUE💡 [提示]$NC 现在可以关闭Cursor并重新运行脚本" + return $true + } else { + Write-Host "$YELLOW⚠️ [超时]$NC 配置文件未在预期时间内生成" + Write-Host "$BLUE💡 [建议]$NC 请手动操作Cursor(如创建新文件)以触发配置生成" + return $false + } + + } catch { + Write-Host "$RED❌ [错误]$NC 启动Cursor失败: $($_.Exception.Message)" + return $false + } +} + # 检查管理员权限 function Test-Administrator { $user = [Security.Principal.WindowsIdentity]::GetCurrent() @@ -40,21 +1178,154 @@ Write-Host @" "@ Write-Host "$BLUE================================$NC" -Write-Host "$GREEN Cursor ID 修改工具 $NC" +Write-Host "$GREEN🚀 Cursor 防掉试用Pro删除工具 $NC" +Write-Host "$YELLOW📱 关注公众号【煎饼果子卷AI】 $NC" +Write-Host "$YELLOW🤝 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC" +Write-Host "$YELLOW💡 [重要提示] 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】 $NC" +Write-Host "" +Write-Host "$YELLOW💰 [小小广告] 出售CursorPro教育号一年质保三个月,有需要找我(86),WeChat:JavaRookie666 $NC" Write-Host "$BLUE================================$NC" + +# 🎯 用户选择菜单 +Write-Host "" +Write-Host "$GREEN🎯 [选择模式]$NC 请选择您要执行的操作:" +Write-Host "" +Write-Host "$BLUE 1️⃣ 仅修改机器码$NC" +Write-Host "$YELLOW • 执行机器码修改功能$NC" +Write-Host "$YELLOW • 执行注入破解JS代码到核心文件$NC" +Write-Host "$YELLOW • 跳过文件夹删除/环境重置步骤$NC" +Write-Host "$YELLOW • 保留现有Cursor配置和数据$NC" +Write-Host "" +Write-Host "$BLUE 2️⃣ 重置环境+修改机器码$NC" +Write-Host "$RED • 执行完全环境重置(删除Cursor文件夹)$NC" +Write-Host "$RED • ⚠️ 配置将丢失,请注意备份$NC" +Write-Host "$YELLOW • 按照机器代码修改$NC" +Write-Host "$YELLOW • 执行注入破解JS代码到核心文件$NC" +Write-Host "$YELLOW • 这相当于当前的完整脚本行为$NC" +Write-Host "" + +# 获取用户选择 +do { + $userChoice = Read-Host "请输入选择 (1 或 2)" + if ($userChoice -eq "1") { + Write-Host "$GREEN✅ [选择]$NC 您选择了:仅修改机器码" + $executeMode = "MODIFY_ONLY" + break + } elseif ($userChoice -eq "2") { + Write-Host "$GREEN✅ [选择]$NC 您选择了:重置环境+修改机器码" + Write-Host "$RED⚠️ [重要警告]$NC 此操作将删除所有Cursor配置文件!" + $confirmReset = Read-Host "确认执行完全重置?(输入 yes 确认,其他任意键取消)" + if ($confirmReset -eq "yes") { + $executeMode = "RESET_AND_MODIFY" + break + } else { + Write-Host "$YELLOW👋 [取消]$NC 用户取消重置操作" + continue + } + } else { + Write-Host "$RED❌ [错误]$NC 无效选择,请输入 1 或 2" + } +} while ($true) + +Write-Host "" + +# 📋 根据选择显示执行流程说明 +if ($executeMode -eq "MODIFY_ONLY") { + Write-Host "$GREEN📋 [执行流程]$NC 仅修改机器码模式将按以下步骤执行:" + Write-Host "$BLUE 1️⃣ 检测Cursor配置文件$NC" + Write-Host "$BLUE 2️⃣ 备份现有配置文件$NC" + Write-Host "$BLUE 3️⃣ 修改机器码配置$NC" + Write-Host "$BLUE 4️⃣ 显示操作完成信息$NC" + Write-Host "" + Write-Host "$YELLOW⚠️ [注意事项]$NC" + Write-Host "$YELLOW • 不会删除任何文件夹或重置环境$NC" + Write-Host "$YELLOW • 保留所有现有配置和数据$NC" + Write-Host "$YELLOW • 原配置文件会自动备份$NC" +} else { + Write-Host "$GREEN📋 [执行流程]$NC 重置环境+修改机器码模式将按以下步骤执行:" + Write-Host "$BLUE 1️⃣ 检测并关闭Cursor进程$NC" + Write-Host "$BLUE 2️⃣ 保存Cursor程序路径信息$NC" + Write-Host "$BLUE 3️⃣ 删除指定的Cursor试用相关文件夹$NC" + Write-Host "$BLUE 📁 C:\Users\Administrator\.cursor$NC" + Write-Host "$BLUE 📁 C:\Users\Administrator\AppData\Roaming\Cursor$NC" + Write-Host "$BLUE 📁 C:\Users\%USERNAME%\.cursor$NC" + Write-Host "$BLUE 📁 C:\Users\%USERNAME%\AppData\Roaming\Cursor$NC" + Write-Host "$BLUE 3.5️⃣ 预创建必要目录结构,避免权限问题$NC" + Write-Host "$BLUE 4️⃣ 重新启动Cursor让其生成新的配置文件$NC" + Write-Host "$BLUE 5️⃣ 等待配置文件生成完成(最多45秒)$NC" + Write-Host "$BLUE 6️⃣ 关闭Cursor进程$NC" + Write-Host "$BLUE 7️⃣ 修改新生成的机器码配置文件$NC" + Write-Host "$BLUE 8️⃣ 显示操作完成统计信息$NC" + Write-Host "" + Write-Host "$YELLOW⚠️ [注意事项]$NC" + Write-Host "$YELLOW • 脚本执行过程中请勿手动操作Cursor$NC" + Write-Host "$YELLOW • 建议在执行前关闭所有Cursor窗口$NC" + Write-Host "$YELLOW • 执行完成后需要重新启动Cursor$NC" + Write-Host "$YELLOW • 原配置文件会自动备份到backups文件夹$NC" +} +Write-Host "" + +# 🤔 用户确认 +Write-Host "$GREEN🤔 [确认]$NC 请确认您已了解上述执行流程" +$confirmation = Read-Host "是否继续执行?(输入 y 或 yes 继续,其他任意键退出)" +if ($confirmation -notmatch "^(y|yes)$") { + Write-Host "$YELLOW👋 [退出]$NC 用户取消执行,脚本退出" + Read-Host "按回车键退出" + exit 0 +} +Write-Host "$GREEN✅ [确认]$NC 用户确认继续执行" +Write-Host "" + +# 获取并显示 Cursor 版本 +function Get-CursorVersion { + try { + # 主要检测路径 + $packagePath = "$env:LOCALAPPDATA\\Programs\\cursor\\resources\\app\\package.json" + + if (Test-Path $packagePath) { + $packageJson = Get-Content $packagePath -Raw | ConvertFrom-Json + if ($packageJson.version) { + Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)" + return $packageJson.version + } + } + + # 备用路径检测 + $altPath = "$env:LOCALAPPDATA\\cursor\\resources\\app\\package.json" + if (Test-Path $altPath) { + $packageJson = Get-Content $altPath -Raw | ConvertFrom-Json + if ($packageJson.version) { + Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)" + return $packageJson.version + } + } + + Write-Host "$YELLOW[警告]$NC 无法检测到 Cursor 版本" + Write-Host "$YELLOW[提示]$NC 请确保 Cursor 已正确安装" + return $null + } + catch { + Write-Host "$RED[错误]$NC 获取 Cursor 版本失败: $_" + return $null + } +} + +# 获取并显示版本信息 +$cursorVersion = Get-CursorVersion Write-Host "" -Write-Host "$YELLOW[重要提示]$NC 本工具仅支持 Cursor v0.44.11 及以下版本" -Write-Host "$YELLOW[重要提示]$NC 最新的 0.45.x 版本暂不支持" + +Write-Host "$YELLOW💡 [重要提示]$NC 最新的 1.0.x 版本已支持" + Write-Host "" -# 检查并关闭 Cursor 进程 -Write-Host "$GREEN[信息]$NC 检查 Cursor 进程..." +# 🔍 检查并关闭 Cursor 进程 +Write-Host "$GREEN🔍 [检查]$NC 正在检查 Cursor 进程..." function Get-ProcessDetails { param($processName) - Write-Host "$BLUE[调试]$NC 正在获取 $processName 进程详细信息:" - Get-WmiObject Win32_Process -Filter "name='$processName'" | - Select-Object ProcessId, ExecutablePath, CommandLine | + Write-Host "$BLUE🔍 [调试]$NC 正在获取 $processName 进程详细信息:" + Get-WmiObject Win32_Process -Filter "name='$processName'" | + Select-Object ProcessId, ExecutablePath, CommandLine | Format-List } @@ -62,355 +1333,347 @@ function Get-ProcessDetails { $MAX_RETRIES = 5 $WAIT_TIME = 1 -# 处理进程关闭 -function Close-CursorProcess { +# 🔄 处理进程关闭并保存进程信息 +function Close-CursorProcessAndSaveInfo { param($processName) - - $process = Get-Process -Name $processName -ErrorAction SilentlyContinue - if ($process) { - Write-Host "$YELLOW[警告]$NC 发现 $processName 正在运行" + + $global:CursorProcessInfo = $null + + $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue + if ($processes) { + Write-Host "$YELLOW⚠️ [警告]$NC 发现 $processName 正在运行" + + # 💾 保存进程信息用于后续重启 - 修复:确保获取单个进程路径 + $firstProcess = if ($processes -is [array]) { $processes[0] } else { $processes } + $processPath = $firstProcess.Path + + # 确保路径是字符串而不是数组 + if ($processPath -is [array]) { + $processPath = $processPath[0] + } + + $global:CursorProcessInfo = @{ + ProcessName = $firstProcess.ProcessName + Path = $processPath + StartTime = $firstProcess.StartTime + } + Write-Host "$GREEN💾 [保存]$NC 已保存进程信息: $($global:CursorProcessInfo.Path)" + Get-ProcessDetails $processName - - Write-Host "$YELLOW[警告]$NC 尝试关闭 $processName..." + + Write-Host "$YELLOW🔄 [操作]$NC 尝试关闭 $processName..." Stop-Process -Name $processName -Force - + $retryCount = 0 while ($retryCount -lt $MAX_RETRIES) { $process = Get-Process -Name $processName -ErrorAction SilentlyContinue if (-not $process) { break } - + $retryCount++ if ($retryCount -ge $MAX_RETRIES) { - Write-Host "$RED[错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName" + Write-Host "$RED❌ [错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName" Get-ProcessDetails $processName - Write-Host "$RED[错误]$NC 请手动关闭进程后重试" + Write-Host "$RED💥 [错误]$NC 请手动关闭进程后重试" Read-Host "按回车键退出" exit 1 } - Write-Host "$YELLOW[警告]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..." + Write-Host "$YELLOW⏳ [等待]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..." Start-Sleep -Seconds $WAIT_TIME } - Write-Host "$GREEN[信息]$NC $processName 已成功关闭" - } -} + Write-Host "$GREEN✅ [成功]$NC $processName 已成功关闭" + } else { + Write-Host "$BLUE💡 [提示]$NC 未发现 $processName 进程运行" + # 尝试找到Cursor的安装路径 + $cursorPaths = @( + "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe", + "$env:PROGRAMFILES\Cursor\Cursor.exe", + "$env:PROGRAMFILES(X86)\Cursor\Cursor.exe" + ) -# 关闭所有 Cursor 进程 -Close-CursorProcess "Cursor" -Close-CursorProcess "cursor" + foreach ($path in $cursorPaths) { + if (Test-Path $path) { + $global:CursorProcessInfo = @{ + ProcessName = "Cursor" + Path = $path + StartTime = $null + } + Write-Host "$GREEN💾 [发现]$NC 找到Cursor安装路径: $path" + break + } + } -# 创建备份目录 -if (-not (Test-Path $BACKUP_DIR)) { - New-Item -ItemType Directory -Path $BACKUP_DIR | Out-Null + if (-not $global:CursorProcessInfo) { + Write-Host "$YELLOW⚠️ [警告]$NC 未找到Cursor安装路径,将使用默认路径" + $global:CursorProcessInfo = @{ + ProcessName = "Cursor" + Path = "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe" + StartTime = $null + } + } + } } -# 备份现有配置 -if (Test-Path $STORAGE_FILE) { - Write-Host "$GREEN[信息]$NC 正在备份配置文件..." - $backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')" - Copy-Item $STORAGE_FILE "$BACKUP_DIR\$backupName" +# �️ 确保备份目录存在 +if (-not (Test-Path $BACKUP_DIR)) { + try { + New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null + Write-Host "$GREEN✅ [备份目录]$NC 备份目录创建成功: $BACKUP_DIR" + } catch { + Write-Host "$YELLOW⚠️ [警告]$NC 备份目录创建失败: $($_.Exception.Message)" + } } -# 生成新的 ID -Write-Host "$GREEN[信息]$NC 正在生成新的 ID..." - -# 生成随机字节数组并转换为十六进制字符串的函数 -function Get-RandomHex { - param ( - [int]$length - ) - $bytes = New-Object byte[] $length - $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new() - $rng.GetBytes($bytes) - $rng.Dispose() - return -join ($bytes | ForEach-Object { '{0:x2}' -f $_ }) -} +# �🚀 根据用户选择执行相应功能 +if ($executeMode -eq "MODIFY_ONLY") { + Write-Host "$GREEN🚀 [开始]$NC 开始执行仅修改机器码功能..." -$UUID = [System.Guid]::NewGuid().ToString() -# 将 auth0|user_ 转换为字节数组的十六进制 -$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_") -$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ }) -# 生成32字节(64个十六进制字符)的随机数作为 machineId 的随机部分 -$randomPart = Get-RandomHex -length 32 -$MACHINE_ID = "$prefixHex$randomPart" -$MAC_MACHINE_ID = Get-RandomHex -length 32 -$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}" - -# 创建或更新配置文件 -Write-Host "$GREEN[信息]$NC 正在更新配置..." - -try { - # 检查配置文件是否存在 - if (-not (Test-Path $STORAGE_FILE)) { - Write-Host "$RED[错误]$NC 未找到配置文件: $STORAGE_FILE" - Write-Host "$YELLOW[提示]$NC 请先安装并运行一次 Cursor 后再使用此脚本" + # 先进行环境检查 + $envCheck = Test-CursorEnvironment -Mode "MODIFY_ONLY" + if (-not $envCheck.Success) { + Write-Host "" + Write-Host "$RED❌ [环境检查失败]$NC 无法继续执行,发现以下问题:" + foreach ($issue in $envCheck.Issues) { + Write-Host "$RED • ${issue}$NC" + } + Write-Host "" + Write-Host "$YELLOW💡 [建议]$NC 请选择以下操作:" + Write-Host "$BLUE 1️⃣ 选择'重置环境+修改机器码'选项(推荐)$NC" + Write-Host "$BLUE 2️⃣ 手动启动Cursor一次,然后重新运行脚本$NC" + Write-Host "$BLUE 3️⃣ 检查Cursor是否正确安装$NC" + Write-Host "" Read-Host "按回车键退出" exit 1 } - # 读取现有配置文件 - try { - $originalContent = Get-Content $STORAGE_FILE -Raw -Encoding UTF8 - - # 将 JSON 字符串转换为 PowerShell 对象 - $config = $originalContent | ConvertFrom-Json - - # 备份当前值 - $oldValues = @{ - 'machineId' = $config.'telemetry.machineId' - 'macMachineId' = $config.'telemetry.macMachineId' - 'devDeviceId' = $config.'telemetry.devDeviceId' - 'sqmId' = $config.'telemetry.sqmId' - } - - # 更新特定的值 - $config.'telemetry.machineId' = $MACHINE_ID - $config.'telemetry.macMachineId' = $MAC_MACHINE_ID - $config.'telemetry.devDeviceId' = $UUID - $config.'telemetry.sqmId' = $SQM_ID - - # 将更新后的对象转换回 JSON 并保存 - $updatedJson = $config | ConvertTo-Json -Depth 10 - [System.IO.File]::WriteAllText( - [System.IO.Path]::GetFullPath($STORAGE_FILE), - $updatedJson, - [System.Text.Encoding]::UTF8 - ) - Write-Host "$GREEN[信息]$NC 成功更新配置文件" - } catch { - # 如果出错,尝试恢复原始内容 - if ($originalContent) { - [System.IO.File]::WriteAllText( - [System.IO.Path]::GetFullPath($STORAGE_FILE), - $originalContent, - [System.Text.Encoding]::UTF8 - ) - } - throw "处理 JSON 失败: $_" - } + # 执行机器码修改 + $configSuccess = Modify-MachineCodeConfig -Mode "MODIFY_ONLY" - # 尝试设置文件权限 - try { - # 使用当前用户名和域名 - $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() - $userAccount = "$($env:USERDOMAIN)\$($env:USERNAME)" - - # 创建新的访问控制列表 - $acl = New-Object System.Security.AccessControl.FileSecurity - - # 添加当前用户的只读权限 - $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule( - $userAccount, # 使用域名\用户名格式 - [System.Security.AccessControl.FileSystemRights]::ReadAndExecute, # 改为只读权限 - [System.Security.AccessControl.InheritanceFlags]::None, - [System.Security.AccessControl.PropagationFlags]::None, - [System.Security.AccessControl.AccessControlType]::Allow - ) - - try { - $acl.AddAccessRule($accessRule) - Set-Acl -Path $STORAGE_FILE -AclObject $acl -ErrorAction Stop - Write-Host "$GREEN[信息]$NC 成功设置文件只读权限" - - # 设置文件为只读属性 - Set-ItemProperty -Path $STORAGE_FILE -Name IsReadOnly -Value $true - Write-Host "$GREEN[信息]$NC 成功设置文件只读属性" - } catch { - # 如果第一种方法失败,尝试使用 icacls - Write-Host "$YELLOW[警告]$NC 使用备选方法设置权限..." - $result = Start-Process "icacls.exe" -ArgumentList "`"$STORAGE_FILE`" /grant `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru - if ($result.ExitCode -eq 0) { - Write-Host "$GREEN[信息]$NC 成功使用 icacls 设置文件只读权限" - # 设置文件为只读属性 - Set-ItemProperty -Path $STORAGE_FILE -Name IsReadOnly -Value $true - Write-Host "$GREEN[信息]$NC 成功设置文件只读属性" + if ($configSuccess) { + Write-Host "" + Write-Host "$GREEN🎉 [配置文件]$NC 机器码配置文件修改完成!" + + # 添加注册表修改 + Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表..." + $registrySuccess = Update-MachineGuid + + # 🔧 新增:JavaScript注入功能(设备识别绕过增强) + Write-Host "" + Write-Host "$BLUE🔧 [设备识别绕过]$NC 正在执行JavaScript注入功能..." + Write-Host "$BLUE💡 [说明]$NC 此功能将直接修改Cursor内核JS文件,实现更深层的设备识别绕过" + $jsSuccess = Modify-CursorJSFiles + + if ($registrySuccess) { + Write-Host "$GREEN✅ [注册表]$NC 系统注册表修改成功" + + if ($jsSuccess) { + Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功" + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 所有机器码修改完成(增强版)!" + Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:" + Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC" + Write-Host "$GREEN ✓ 系统注册表 (MachineGuid)$NC" + Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC" } else { - Write-Host "$YELLOW[警告]$NC 设置文件权限失败,但文件已写入成功" + Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败,但其他功能成功" + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 所有机器码修改完成!" + Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:" + Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC" + Write-Host "$GREEN ✓ 系统注册表 (MachineGuid)$NC" + Write-Host "$YELLOW ⚠ JavaScript内核注入(部分失败)$NC" } - } - } catch { - Write-Host "$YELLOW[警告]$NC 设置文件权限失败: $_" - Write-Host "$YELLOW[警告]$NC 尝试使用 icacls 命令..." - try { - $result = Start-Process "icacls.exe" -ArgumentList "`"$STORAGE_FILE`" /grant `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru - if ($result.ExitCode -eq 0) { - Write-Host "$GREEN[信息]$NC 成功使用 icacls 设置文件只读权限" - # 设置文件为只读属性 - Set-ItemProperty -Path $STORAGE_FILE -Name IsReadOnly -Value $true - Write-Host "$GREEN[信息]$NC 成功设置文件只读属性" + + # 🔒 添加配置文件保护机制 + Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." + try { + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $configFile = Get-Item $configPath + $configFile.IsReadOnly = $true + Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改" + Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath" + } catch { + Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'" + } + } else { + Write-Host "$YELLOW⚠️ [注册表]$NC 注册表修改失败,但配置文件修改成功" + + if ($jsSuccess) { + Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功" + Write-Host "" + Write-Host "$YELLOW🎉 [部分完成]$NC 配置文件和JavaScript注入完成,注册表修改失败" + Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表" + Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:" + Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC" + Write-Host "$YELLOW ⚠ 系统注册表 (MachineGuid) - 失败$NC" + Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC" } else { - Write-Host "$YELLOW[警告]$NC 所有权限设置方法都失败,但文件已写入成功" + Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败" + Write-Host "" + Write-Host "$YELLOW🎉 [部分完成]$NC 配置文件修改完成,注册表和JavaScript注入失败" + Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表" + } + + # 🔒 即使注册表修改失败,也要保护配置文件 + Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." + try { + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $configFile = Get-Item $configPath + $configFile.IsReadOnly = $true + Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改" + Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath" + } catch { + Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'" } - } catch { - Write-Host "$YELLOW[警告]$NC icacls 命令失败: $_" } + + Write-Host "$BLUE💡 [提示]$NC 现在可以启动Cursor使用新的机器码配置" + } else { + Write-Host "" + Write-Host "$RED❌ [失败]$NC 机器码修改失败!" + Write-Host "$YELLOW💡 [建议]$NC 请尝试'重置环境+修改机器码'选项" } +} else { + # 完整的重置环境+修改机器码流程 + Write-Host "$GREEN🚀 [开始]$NC 开始执行重置环境+修改机器码功能..." -} catch { - Write-Host "$RED[错误]$NC 主要操作失败: $_" - Write-Host "$YELLOW[尝试]$NC 使用备选方法..." - - try { - # 备选方法:使用 Add-Content - $tempFile = [System.IO.Path]::GetTempFileName() - $config | ConvertTo-Json | Set-Content -Path $tempFile -Encoding UTF8 - Copy-Item -Path $tempFile -Destination $STORAGE_FILE -Force - Remove-Item -Path $tempFile - Write-Host "$GREEN[信息]$NC 使用备选方法成功写入配置" - } catch { - Write-Host "$RED[错误]$NC 所有尝试都失败了" - Write-Host "错误详情: $_" - Write-Host "目标文件: $STORAGE_FILE" - Write-Host "请确保您有足够的权限访问该文件" - Read-Host "按回车键退出" - exit 1 + # 🚀 关闭所有 Cursor 进程并保存信息 + Close-CursorProcessAndSaveInfo "Cursor" + if (-not $global:CursorProcessInfo) { + Close-CursorProcessAndSaveInfo "cursor" } -} -# 显示结果 -Write-Host "" -Write-Host "$GREEN[信息]$NC 已更新配置:" -Write-Host "$BLUE[调试]$NC machineId: $MACHINE_ID" -Write-Host "$BLUE[调试]$NC macMachineId: $MAC_MACHINE_ID" -Write-Host "$BLUE[调试]$NC devDeviceId: $UUID" -Write-Host "$BLUE[调试]$NC sqmId: $SQM_ID" + # 🚨 重要警告提示 + Write-Host "" + Write-Host "$RED🚨 [重要警告]$NC ============================================" + Write-Host "$YELLOW⚠️ [风控提醒]$NC Cursor 风控机制非常严格!" + Write-Host "$YELLOW⚠️ [必须删除]$NC 必须完全删除指定文件夹,不能有任何残留设置" + Write-Host "$YELLOW⚠️ [防掉试用]$NC 只有彻底清理才能有效防止掉试用Pro状态" + Write-Host "$RED🚨 [重要警告]$NC ============================================" + Write-Host "" -# 显示文件树结构 -Write-Host "" -Write-Host "$GREEN[信息]$NC 文件结构:" -Write-Host "$BLUE$env:APPDATA\Cursor\User$NC" -Write-Host "├── globalStorage" -Write-Host "│ ├── storage.json (已修改)" -Write-Host "│ └── backups" - -# 列出备份文件 -$backupFiles = Get-ChildItem "$BACKUP_DIR\*" -ErrorAction SilentlyContinue -if ($backupFiles) { - foreach ($file in $backupFiles) { - Write-Host "│ └── $($file.Name)" - } -} else { - Write-Host "│ └── (空)" -} + # 🎯 执行 Cursor 防掉试用Pro删除文件夹功能 + Write-Host "$GREEN🚀 [开始]$NC 开始执行核心功能..." + Remove-CursorTrialFolders -# 显示公众号信息 -Write-Host "" -Write-Host "$GREEN================================$NC" -Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识 $NC" -Write-Host "$GREEN================================$NC" -Write-Host "" -Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置" -Write-Host "" -# 询问是否要禁用自动更新 -Write-Host "" -Write-Host "$YELLOW[询问]$NC 是否要禁用 Cursor 自动更新功能?" -Write-Host "0) 否 - 保持默认设置 (按回车键)" -Write-Host "1) 是 - 禁用自动更新" -$choice = Read-Host "请输入选项 (0)" -if ($choice -eq "1") { - Write-Host "" - Write-Host "$GREEN[信息]$NC 正在处理自动更新..." - $updaterPath = "$env:LOCALAPPDATA\cursor-updater" + # 🔄 重启Cursor让其重新生成配置文件 + Restart-CursorAndWait - # 定义手动设置教程 - function Show-ManualGuide { - Write-Host "" - Write-Host "$YELLOW[警告]$NC 自动设置失败,请尝试手动操作:" - Write-Host "$YELLOW手动禁用更新步骤:$NC" - Write-Host "1. 以管理员身份打开 PowerShell" - Write-Host "2. 复制粘贴以下命令:" - Write-Host "$BLUE命令1 - 删除现有目录(如果存在):$NC" - Write-Host "Remove-Item -Path `"$updaterPath`" -Force -Recurse -ErrorAction SilentlyContinue" - Write-Host "" - Write-Host "$BLUE命令2 - 创建阻止文件:$NC" - Write-Host "New-Item -Path `"$updaterPath`" -ItemType File -Force | Out-Null" - Write-Host "" - Write-Host "$BLUE命令3 - 设置只读属性:$NC" - Write-Host "Set-ItemProperty -Path `"$updaterPath`" -Name IsReadOnly -Value `$true" - Write-Host "" - Write-Host "$BLUE命令4 - 设置权限(可选):$NC" - Write-Host "icacls `"$updaterPath`" /inheritance:r /grant:r `"`$($env:USERNAME):(R)`"" + # 🛠️ 修改机器码配置 + $configSuccess = Modify-MachineCodeConfig + + # 🧹 执行 Cursor 初始化清理 + Invoke-CursorInitialization + + if ($configSuccess) { Write-Host "" - Write-Host "$YELLOW验证方法:$NC" - Write-Host "1. 运行命令:Get-ItemProperty `"$updaterPath`"" - Write-Host "2. 确认 IsReadOnly 属性为 True" - Write-Host "3. 运行命令:icacls `"$updaterPath`"" - Write-Host "4. 确认只有读取权限" + Write-Host "$GREEN🎉 [配置文件]$NC 机器码配置文件修改完成!" + + # 添加注册表修改 + Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表..." + $registrySuccess = Update-MachineGuid + + # 🔧 新增:JavaScript注入功能(设备识别绕过增强) Write-Host "" - Write-Host "$YELLOW[提示]$NC 完成后请重启 Cursor" - } + Write-Host "$BLUE🔧 [设备识别绕过]$NC 正在执行JavaScript注入功能..." + Write-Host "$BLUE💡 [说明]$NC 此功能将直接修改Cursor内核JS文件,实现更深层的设备识别绕过" + $jsSuccess = Modify-CursorJSFiles - try { - # 删除现有目录 - if (Test-Path $updaterPath) { - try { - Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop - Write-Host "$GREEN[信息]$NC 成功删除 cursor-updater 目录" - } - catch { - Write-Host "$RED[错误]$NC 删除 cursor-updater 目录失败" - Show-ManualGuide - return + if ($registrySuccess) { + Write-Host "$GREEN✅ [注册表]$NC 系统注册表修改成功" + + if ($jsSuccess) { + Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功" + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 所有操作完成(增强版)!" + Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:" + Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC" + Write-Host "$GREEN ✓ Cursor 初始化清理$NC" + Write-Host "$GREEN ✓ 重新生成配置文件$NC" + Write-Host "$GREEN ✓ 修改机器码配置$NC" + Write-Host "$GREEN ✓ 修改系统注册表$NC" + Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC" + } else { + Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败,但其他功能成功" + Write-Host "" + Write-Host "$GREEN🎉 [完成]$NC 所有操作完成!" + Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:" + Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC" + Write-Host "$GREEN ✓ Cursor 初始化清理$NC" + Write-Host "$GREEN ✓ 重新生成配置文件$NC" + Write-Host "$GREEN ✓ 修改机器码配置$NC" + Write-Host "$GREEN ✓ 修改系统注册表$NC" + Write-Host "$YELLOW ⚠ JavaScript内核注入(部分失败)$NC" } - } - # 创建阻止文件 - try { - New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null - Write-Host "$GREEN[信息]$NC 成功创建阻止文件" - } - catch { - Write-Host "$RED[错误]$NC 创建阻止文件失败" - Show-ManualGuide - return - } + # 🔒 添加配置文件保护机制 + Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." + try { + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $configFile = Get-Item $configPath + $configFile.IsReadOnly = $true + Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改" + Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath" + } catch { + Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'" + } + } else { + Write-Host "$YELLOW⚠️ [注册表]$NC 注册表修改失败,但其他操作成功" - # 设置文件权限 - try { - # 设置只读属性 - Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop - - # 使用 icacls 设置权限 - $result = Start-Process "icacls.exe" -ArgumentList "`"$updaterPath`" /inheritance:r /grant:r `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru - if ($result.ExitCode -ne 0) { - throw "icacls 命令失败" + if ($jsSuccess) { + Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功" + Write-Host "" + Write-Host "$YELLOW🎉 [部分完成]$NC 大部分操作完成,注册表修改失败" + Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表" + Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:" + Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC" + Write-Host "$GREEN ✓ Cursor 初始化清理$NC" + Write-Host "$GREEN ✓ 重新生成配置文件$NC" + Write-Host "$GREEN ✓ 修改机器码配置$NC" + Write-Host "$YELLOW ⚠ 修改系统注册表 - 失败$NC" + Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC" + } else { + Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败" + Write-Host "" + Write-Host "$YELLOW🎉 [部分完成]$NC 大部分操作完成,注册表和JavaScript注入失败" + Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表" } - - Write-Host "$GREEN[信息]$NC 成功设置文件权限" - } - catch { - Write-Host "$RED[错误]$NC 设置文件权限失败" - Show-ManualGuide - return - } - # 验证设置 - try { - $fileInfo = Get-ItemProperty $updaterPath - if (-not $fileInfo.IsReadOnly) { - Write-Host "$RED[错误]$NC 验证失败:文件权限设置可能未生效" - Show-ManualGuide - return + # 🔒 即使注册表修改失败,也要保护配置文件 + Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." + try { + $configPath = "$env:APPDATA\Cursor\User\globalStorage\storage.json" + $configFile = Get-Item $configPath + $configFile.IsReadOnly = $true + Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改" + Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath" + } catch { + Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)" + Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'" } } - catch { - Write-Host "$RED[错误]$NC 验证设置失败" - Show-ManualGuide - return - } - - Write-Host "$GREEN[信息]$NC 成功禁用自动更新" - } - catch { - Write-Host "$RED[错误]$NC 发生未知错误: $_" - Show-ManualGuide + } else { + Write-Host "" + Write-Host "$RED❌ [失败]$NC 机器码配置修改失败!" + Write-Host "$YELLOW💡 [建议]$NC 请检查错误信息并重试" } } -else { - Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改" -} + +# 📱 显示公众号信息 +Write-Host "" +Write-Host "$GREEN================================$NC" +Write-Host "$YELLOW📱 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC" +Write-Host "$GREEN================================$NC" +Write-Host "" + +# 🎉 脚本执行完成 +Write-Host "$GREEN🎉 [脚本完成]$NC 感谢使用 Cursor 机器码修改工具!" +Write-Host "$BLUE💡 [提示]$NC 如有问题请参考公众号或重新运行脚本" Write-Host "" Read-Host "按回车键退出" -exit 0 \ No newline at end of file +exit 0 diff --git a/scripts/run/cursor_win_id_modifier_old.ps1 b/scripts/run/cursor_win_id_modifier_old.ps1 new file mode 100644 index 00000000..278aca10 --- /dev/null +++ b/scripts/run/cursor_win_id_modifier_old.ps1 @@ -0,0 +1,607 @@ +# 设置输出编码为 UTF-8 +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# 颜色定义 +$RED = "`e[31m" +$GREEN = "`e[32m" +$YELLOW = "`e[33m" +$BLUE = "`e[34m" +$NC = "`e[0m" + +# 配置文件路径 +$STORAGE_FILE = "$env:APPDATA\Cursor\User\globalStorage\storage.json" +$BACKUP_DIR = "$env:APPDATA\Cursor\User\globalStorage\backups" + +# 新增 Cursor 初始化函数 +function Cursor-初始化 { + Write-Host "$GREEN[信息]$NC 正在执行 Cursor 初始化清理..." + $BASE_PATH = "$env:APPDATA\Cursor\User" + + $filesToDelete = @( + (Join-Path -Path $BASE_PATH -ChildPath "globalStorage\\state.vscdb"), + (Join-Path -Path $BASE_PATH -ChildPath "globalStorage\\state.vscdb.backup") + ) + + $folderToCleanContents = Join-Path -Path $BASE_PATH -ChildPath "History" + $folderToDeleteCompletely = Join-Path -Path $BASE_PATH -ChildPath "workspaceStorage" + + Write-Host "$BLUE[调试]$NC 基础路径: $BASE_PATH" + + # 删除指定文件 + foreach ($file in $filesToDelete) { + Write-Host "$BLUE[调试]$NC 检查文件: $file" + if (Test-Path $file) { + try { + Remove-Item -Path $file -Force -ErrorAction Stop + Write-Host "$GREEN[成功]$NC 已删除文件: $file" + } + catch { + Write-Host "$RED[错误]$NC 删除文件 $file 失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW[警告]$NC 文件不存在,跳过删除: $file" + } + } + + # 清空指定文件夹内容 + Write-Host "$BLUE[调试]$NC 检查待清空文件夹: $folderToCleanContents" + if (Test-Path $folderToCleanContents) { + try { + # 获取子项进行删除,以避免删除 History 文件夹本身 + Get-ChildItem -Path $folderToCleanContents -Recurse | Remove-Item -Recurse -Force -ErrorAction Stop + Write-Host "$GREEN[成功]$NC 已清空文件夹内容: $folderToCleanContents" + } + catch { + Write-Host "$RED[错误]$NC 清空文件夹 $folderToCleanContents 内容失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW[警告]$NC 文件夹不存在,跳过清空: $folderToCleanContents" + } + + # 删除指定文件夹及其内容 + Write-Host "$BLUE[调试]$NC 检查待删除文件夹: $folderToDeleteCompletely" + if (Test-Path $folderToDeleteCompletely) { + try { + Remove-Item -Path $folderToDeleteCompletely -Recurse -Force -ErrorAction Stop + Write-Host "$GREEN[成功]$NC 已删除文件夹: $folderToDeleteCompletely" + } + catch { + Write-Host "$RED[错误]$NC 删除文件夹 $folderToDeleteCompletely 失败: $($_.Exception.Message)" + } + } else { + Write-Host "$YELLOW[警告]$NC 文件夹不存在,跳过删除: $folderToDeleteCompletely" + } + + Write-Host "$GREEN[信息]$NC Cursor 初始化清理完成。" + Write-Host "" # 添加空行以改善输出格式 +} + +# 检查管理员权限 +function Test-Administrator { + $user = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($user) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +if (-not (Test-Administrator)) { + Write-Host "$RED[错误]$NC 请以管理员身份运行此脚本" + Write-Host "请右键点击脚本,选择'以管理员身份运行'" + Read-Host "按回车键退出" + exit 1 +} + +# 显示 Logo +Clear-Host +Write-Host @" + + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ + ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ + ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ + ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ + +"@ +Write-Host "$BLUE================================$NC" +Write-Host "$GREEN Cursor 设备ID 修改工具 $NC" +Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】 $NC" +Write-Host "$YELLOW 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC" +Write-Host "$YELLOW [重要提示] 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】 $NC" +Write-Host "" +Write-Host "$YELLOW [小小广告] 出售CursorPro教育号一年质保三个月,有需要找我(86),WeChat:JavaRookie666 $NC" +Write-Host "$BLUE================================$NC" + +# 获取并显示 Cursor 版本 +function Get-CursorVersion { + try { + # 主要检测路径 + $packagePath = "$env:LOCALAPPDATA\\Programs\\cursor\\resources\\app\\package.json" + + if (Test-Path $packagePath) { + $packageJson = Get-Content $packagePath -Raw | ConvertFrom-Json + if ($packageJson.version) { + Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)" + return $packageJson.version + } + } + + # 备用路径检测 + $altPath = "$env:LOCALAPPDATA\\cursor\\resources\\app\\package.json" + if (Test-Path $altPath) { + $packageJson = Get-Content $altPath -Raw | ConvertFrom-Json + if ($packageJson.version) { + Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)" + return $packageJson.version + } + } + + Write-Host "$YELLOW[警告]$NC 无法检测到 Cursor 版本" + Write-Host "$YELLOW[提示]$NC 请确保 Cursor 已正确安装" + return $null + } + catch { + Write-Host "$RED[错误]$NC 获取 Cursor 版本失败: $_" + return $null + } +} + +# 获取并显示版本信息 +$cursorVersion = Get-CursorVersion +Write-Host "" + +Write-Host "$YELLOW[重要提示]$NC 最新的 1.0.x (以支持)" +Write-Host "" + +# 检查并关闭 Cursor 进程 +Write-Host "$GREEN[信息]$NC 检查 Cursor 进程..." + +function Get-ProcessDetails { + param($processName) + Write-Host "$BLUE[调试]$NC 正在获取 $processName 进程详细信息:" + Get-WmiObject Win32_Process -Filter "name='$processName'" | + Select-Object ProcessId, ExecutablePath, CommandLine | + Format-List +} + +# 定义最大重试次数和等待时间 +$MAX_RETRIES = 5 +$WAIT_TIME = 1 + +# 处理进程关闭 +function Close-CursorProcess { + param($processName) + + $process = Get-Process -Name $processName -ErrorAction SilentlyContinue + if ($process) { + Write-Host "$YELLOW[警告]$NC 发现 $processName 正在运行" + Get-ProcessDetails $processName + + Write-Host "$YELLOW[警告]$NC 尝试关闭 $processName..." + Stop-Process -Name $processName -Force + + $retryCount = 0 + while ($retryCount -lt $MAX_RETRIES) { + $process = Get-Process -Name $processName -ErrorAction SilentlyContinue + if (-not $process) { break } + + $retryCount++ + if ($retryCount -ge $MAX_RETRIES) { + Write-Host "$RED[错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName" + Get-ProcessDetails $processName + Write-Host "$RED[错误]$NC 请手动关闭进程后重试" + Read-Host "按回车键退出" + exit 1 + } + Write-Host "$YELLOW[警告]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..." + Start-Sleep -Seconds $WAIT_TIME + } + Write-Host "$GREEN[信息]$NC $processName 已成功关闭" + } +} + +# 关闭所有 Cursor 进程 +Close-CursorProcess "Cursor" +Close-CursorProcess "cursor" + +# 执行 Cursor 初始化清理 +# Cursor-初始化 + +# 创建备份目录 +if (-not (Test-Path $BACKUP_DIR)) { + New-Item -ItemType Directory -Path $BACKUP_DIR | Out-Null +} + +# 备份现有配置 +if (Test-Path $STORAGE_FILE) { + Write-Host "$GREEN[信息]$NC 正在备份配置文件..." + $backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')" + Copy-Item $STORAGE_FILE "$BACKUP_DIR\$backupName" +} + +# 生成新的 ID +Write-Host "$GREEN[信息]$NC 正在生成新的 ID..." + +# 在颜色定义后添加此函数 +function Get-RandomHex { + param ( + [int]$length + ) + + $bytes = New-Object byte[] ($length) + $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new() + $rng.GetBytes($bytes) + $hexString = [System.BitConverter]::ToString($bytes) -replace '-','' + $rng.Dispose() + return $hexString +} + +# 改进 ID 生成函数 +function New-StandardMachineId { + $template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + $result = $template -replace '[xy]', { + param($match) + $r = [Random]::new().Next(16) + $v = if ($match.Value -eq "x") { $r } else { ($r -band 0x3) -bor 0x8 } + return $v.ToString("x") + } + return $result +} + +# 在生成 ID 时使用新函数 +$MAC_MACHINE_ID = New-StandardMachineId +$UUID = [System.Guid]::NewGuid().ToString() +# 将 auth0|user_ 转换为字节数组的十六进制 +$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_") +$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ }) +# 生成32字节(64个十六进制字符)的随机数作为 machineId 的随机部分 +$randomPart = Get-RandomHex -length 32 +$MACHINE_ID = "$prefixHex$randomPart" +$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}" + +# 在Update-MachineGuid函数前添加权限检查 +if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + Write-Host "$RED[错误]$NC 请使用管理员权限运行此脚本" + Start-Process powershell "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + exit +} + +function Update-MachineGuid { + try { + # 检查注册表路径是否存在,不存在则创建 + $registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography" + if (-not (Test-Path $registryPath)) { + Write-Host "$YELLOW[警告]$NC 注册表路径不存在: $registryPath,正在创建..." + New-Item -Path $registryPath -Force | Out-Null + Write-Host "$GREEN[信息]$NC 注册表路径创建成功" + } + + # 获取当前的 MachineGuid,如果不存在则使用空字符串作为默认值 + $originalGuid = "" + try { + $currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction SilentlyContinue + if ($currentGuid) { + $originalGuid = $currentGuid.MachineGuid + Write-Host "$GREEN[信息]$NC 当前注册表值:" + Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" + Write-Host " MachineGuid REG_SZ $originalGuid" + } else { + Write-Host "$YELLOW[警告]$NC MachineGuid 值不存在,将创建新值" + } + } catch { + Write-Host "$YELLOW[警告]$NC 获取 MachineGuid 失败: $($_.Exception.Message)" + } + + # 创建备份目录(如果不存在) + if (-not (Test-Path $BACKUP_DIR)) { + New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null + } + + # 创建备份文件(仅当原始值存在时) + if ($originalGuid) { + $backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg" + $backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru + + if ($backupResult.ExitCode -eq 0) { + Write-Host "$GREEN[信息]$NC 注册表项已备份到:$backupFile" + } else { + Write-Host "$YELLOW[警告]$NC 备份创建失败,继续执行..." + } + } + + # 生成新GUID + $newGuid = [System.Guid]::NewGuid().ToString() + + # 更新或创建注册表值 + Set-ItemProperty -Path $registryPath -Name MachineGuid -Value $newGuid -Force -ErrorAction Stop + + # 验证更新 + $verifyGuid = (Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop).MachineGuid + if ($verifyGuid -ne $newGuid) { + throw "注册表验证失败:更新后的值 ($verifyGuid) 与预期值 ($newGuid) 不匹配" + } + + Write-Host "$GREEN[信息]$NC 注册表更新成功:" + Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" + Write-Host " MachineGuid REG_SZ $newGuid" + return $true + } + catch { + Write-Host "$RED[错误]$NC 注册表操作失败:$($_.Exception.Message)" + + # 尝试恢复备份(如果存在) + if (($backupFile -ne $null) -and (Test-Path $backupFile)) { + Write-Host "$YELLOW[恢复]$NC 正在从备份恢复..." + $restoreResult = Start-Process "reg.exe" -ArgumentList "import", "`"$backupFile`"" -NoNewWindow -Wait -PassThru + + if ($restoreResult.ExitCode -eq 0) { + Write-Host "$GREEN[恢复成功]$NC 已还原原始注册表值" + } else { + Write-Host "$RED[错误]$NC 恢复失败,请手动导入备份文件:$backupFile" + } + } else { + Write-Host "$YELLOW[警告]$NC 未找到备份文件或备份创建失败,无法自动恢复" + } + return $false + } +} + +# 创建或更新配置文件 +Write-Host "$GREEN[信息]$NC 正在更新配置..." + +try { + # 检查配置文件是否存在 + if (-not (Test-Path $STORAGE_FILE)) { + Write-Host "$RED[错误]$NC 未找到配置文件: $STORAGE_FILE" + Write-Host "$YELLOW[提示]$NC 请先安装并运行一次 Cursor 后再使用此脚本" + Read-Host "按回车键退出" + exit 1 + } + + # 读取现有配置文件 + try { + $originalContent = Get-Content $STORAGE_FILE -Raw -Encoding UTF8 + + # 将 JSON 字符串转换为 PowerShell 对象 + $config = $originalContent | ConvertFrom-Json + + # 备份当前值 + $oldValues = @{ + 'machineId' = $config.'telemetry.machineId' + 'macMachineId' = $config.'telemetry.macMachineId' + 'devDeviceId' = $config.'telemetry.devDeviceId' + 'sqmId' = $config.'telemetry.sqmId' + } + + # 更新特定的值 + $config.'telemetry.machineId' = $MACHINE_ID + $config.'telemetry.macMachineId' = $MAC_MACHINE_ID + $config.'telemetry.devDeviceId' = $UUID + $config.'telemetry.sqmId' = $SQM_ID + + # 将更新后的对象转换回 JSON 并保存 + $updatedJson = $config | ConvertTo-Json -Depth 10 + [System.IO.File]::WriteAllText( + [System.IO.Path]::GetFullPath($STORAGE_FILE), + $updatedJson, + [System.Text.Encoding]::UTF8 + ) + Write-Host "$GREEN[信息]$NC 成功更新配置文件" + } catch { + # 如果出错,尝试恢复原始内容 + if ($originalContent) { + [System.IO.File]::WriteAllText( + [System.IO.Path]::GetFullPath($STORAGE_FILE), + $originalContent, + [System.Text.Encoding]::UTF8 + ) + } + throw "处理 JSON 失败: $_" + } + # 直接执行更新 MachineGuid,不再询问 + Update-MachineGuid + # 显示结果 + Write-Host "" + Write-Host "$GREEN[信息]$NC 已更新配置:" + Write-Host "$BLUE[调试]$NC machineId: $MACHINE_ID" + Write-Host "$BLUE[调试]$NC macMachineId: $MAC_MACHINE_ID" + Write-Host "$BLUE[调试]$NC devDeviceId: $UUID" + Write-Host "$BLUE[调试]$NC sqmId: $SQM_ID" + + # 显示文件树结构 + Write-Host "" + Write-Host "$GREEN[信息]$NC 文件结构:" + Write-Host "$BLUE$env:APPDATA\Cursor\User$NC" + Write-Host "├── globalStorage" + Write-Host "│ ├── storage.json (已修改)" + Write-Host "│ └── backups" + + # 列出备份文件 + $backupFiles = Get-ChildItem "$BACKUP_DIR\*" -ErrorAction SilentlyContinue + if ($backupFiles) { + foreach ($file in $backupFiles) { + Write-Host "│ └── $($file.Name)" + } + } else { + Write-Host "│ └── (空)" + } + + # 显示公众号信息 + Write-Host "" + Write-Host "$GREEN================================$NC" + Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC" + Write-Host "$GREEN================================$NC" + Write-Host "" + Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置" + Write-Host "" + + # 询问是否要禁用自动更新 + Write-Host "" + Write-Host "$YELLOW[询问]$NC 是否要禁用 Cursor 自动更新功能?" + Write-Host "0) 否 - 保持默认设置 (按回车键)" + Write-Host "1) 是 - 禁用自动更新" + $choice = Read-Host "请输入选项 (0)" + + if ($choice -eq "1") { + Write-Host "" + Write-Host "$GREEN[信息]$NC 正在处理自动更新..." + $updaterPath = "$env:LOCALAPPDATA\cursor-updater" + + # 定义手动设置教程 + function Show-ManualGuide { + Write-Host "" + Write-Host "$YELLOW[警告]$NC 自动设置失败,请尝试手动操作:" + Write-Host "$YELLOW手动禁用更新步骤:$NC" + Write-Host "1. 以管理员身份打开 PowerShell" + Write-Host "2. 复制粘贴以下命令:" + Write-Host "$BLUE命令1 - 删除现有目录(如果存在):$NC" + Write-Host "Remove-Item -Path `"$updaterPath`" -Force -Recurse -ErrorAction SilentlyContinue" + Write-Host "" + Write-Host "$BLUE命令2 - 创建阻止文件:$NC" + Write-Host "New-Item -Path `"$updaterPath`" -ItemType File -Force | Out-Null" + Write-Host "" + Write-Host "$BLUE命令3 - 设置只读属性:$NC" + Write-Host "Set-ItemProperty -Path `"$updaterPath`" -Name IsReadOnly -Value `$true" + Write-Host "" + Write-Host "$BLUE命令4 - 设置权限(可选):$NC" + Write-Host "icacls `"$updaterPath`" /inheritance:r /grant:r `"`$($env:USERNAME):(R)`"" + Write-Host "" + Write-Host "$YELLOW验证方法:$NC" + Write-Host "1. 运行命令:Get-ItemProperty `"$updaterPath`"" + Write-Host "2. 确认 IsReadOnly 属性为 True" + Write-Host "3. 运行命令:icacls `"$updaterPath`"" + Write-Host "4. 确认只有读取权限" + Write-Host "" + Write-Host "$YELLOW[提示]$NC 完成后请重启 Cursor" + } + + try { + # 检查cursor-updater是否存在 + if (Test-Path $updaterPath) { + # 如果是文件,说明已经创建了阻止更新 + if ((Get-Item $updaterPath) -is [System.IO.FileInfo]) { + Write-Host "$GREEN[信息]$NC 已创建阻止更新文件,无需再次阻止" + return + } + # 如果是目录,尝试删除 + else { + try { + Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop + Write-Host "$GREEN[信息]$NC 成功删除 cursor-updater 目录" + } + catch { + Write-Host "$RED[错误]$NC 删除 cursor-updater 目录失败" + Show-ManualGuide + return + } + } + } + + # 创建阻止文件 + try { + New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null + Write-Host "$GREEN[信息]$NC 成功创建阻止文件" + } + catch { + Write-Host "$RED[错误]$NC 创建阻止文件失败" + Show-ManualGuide + return + } + + # 设置文件权限 + try { + # 设置只读属性 + Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop + + # 使用 icacls 设置权限 + $result = Start-Process "icacls.exe" -ArgumentList "`"$updaterPath`" /inheritance:r /grant:r `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru + if ($result.ExitCode -ne 0) { + throw "icacls 命令失败" + } + + Write-Host "$GREEN[信息]$NC 成功设置文件权限" + } + catch { + Write-Host "$RED[错误]$NC 设置文件权限失败" + Show-ManualGuide + return + } + + # 验证设置 + try { + $fileInfo = Get-ItemProperty $updaterPath + if (-not $fileInfo.IsReadOnly) { + Write-Host "$RED[错误]$NC 验证失败:文件权限设置可能未生效" + Show-ManualGuide + return + } + } + catch { + Write-Host "$RED[错误]$NC 验证设置失败" + Show-ManualGuide + return + } + + Write-Host "$GREEN[信息]$NC 成功禁用自动更新" + } + catch { + Write-Host "$RED[错误]$NC 发生未知错误: $_" + Show-ManualGuide + } + } + else { + Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改" + } + + # 保留有效的注册表更新 + Update-MachineGuid + +} catch { + Write-Host "$RED[错误]$NC 主要操作失败: $_" + Write-Host "$YELLOW[尝试]$NC 使用备选方法..." + + try { + # 备选方法:使用 Add-Content + $tempFile = [System.IO.Path]::GetTempFileName() + $config | ConvertTo-Json | Set-Content -Path $tempFile -Encoding UTF8 + Copy-Item -Path $tempFile -Destination $STORAGE_FILE -Force + Remove-Item -Path $tempFile + Write-Host "$GREEN[信息]$NC 使用备选方法成功写入配置" + } catch { + Write-Host "$RED[错误]$NC 所有尝试都失败了" + Write-Host "错误详情: $_" + Write-Host "目标文件: $STORAGE_FILE" + Write-Host "请确保您有足够的权限访问该文件" + Read-Host "按回车键退出" + exit 1 + } +} + +Write-Host "" +Read-Host "按回车键退出" +exit 0 + +# 在文件写入部分修改 +function Write-ConfigFile { + param($config, $filePath) + + try { + # 使用 UTF8 无 BOM 编码 + $utf8NoBom = New-Object System.Text.UTF8Encoding $false + $jsonContent = $config | ConvertTo-Json -Depth 10 + + # 统一使用 LF 换行符 + $jsonContent = $jsonContent.Replace("`r`n", "`n") + + [System.IO.File]::WriteAllText( + [System.IO.Path]::GetFullPath($filePath), + $jsonContent, + $utf8NoBom + ) + + Write-Host "$GREEN[信息]$NC 成功写入配置文件(UTF8 无 BOM)" + } + catch { + throw "写入配置文件失败: $_" + } +} \ No newline at end of file