diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc9a792 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Binary files +app.exe +cursor-id-modifier-linux +cursor-id-modifier-macos +cursor-id-modifier-* +*.exe +*.dll +*.so +*.dylib + +# Build directories +bin/ +releases/ +scripts/ + +# IDE +.vscode/ + +# Go specific +go.sum + +# Temporary files +*.tmp +*~ + +# System files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md index a66d6a4..9c8fc53 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,285 @@ -# Cursor Free Trial Reset Tool +# 🚀 Cursor Free Trial Reset Tool + +
+ +[![Release](https://img.shields.io/github/v/release/yuaotian/go-cursor-help?style=for-the-badge&logo=github&color=blue)](https://github.com/yuaotian/go-cursor-help/releases/latest) +[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/main/LICENSE) +[![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=for-the-badge&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers) [English](#english) | [中文](#chinese) -### Important Notice | 重要声明 +--- + +Cursor Logo + +
+ +## ⚠️ Important Notice | 重要声明 + + + + + +
+ This tool is only intended for the following specific scenarios: -1. During Cursor's official free trial period -2. When system mistakenly flags as duplicate trial due to technical issues -3. As a temporary solution when official support is not readily available - -Please note: -- This tool is not meant for bypassing paid features or cracking software -- If your trial period has expired, please purchase a license or seek alternatives -- It's recommended to contact official support first -- Please ensure you are within valid trial period before using this tool - -本工具仅用于解决以下特定场景: -1. 在Cursor官方承诺的免费试用期内 -2. 由于技术原因导致系统误判为重复试用 -3. 无法通过正常渠道及时获得官方支持时的临时解决方案 - -请注意: -- 本工具不是用于规避付费或破解软件 -- 如果您已超出试用期,请购买正版授权或寻找其他替代方案 -- 建议优先通过官方支持渠道解决问题 -- 使用本工具前请确认您处于有效的试用期内 +- 🕒 During Cursor's official free trial period +- 🔄 When system mistakenly flags as duplicate trial +- 🆘 As a temporary solution when official support is unavailable + +**Please note:** +- 🚫 Not for bypassing paid features +- 💳 Purchase a license if trial expired +- 📞 Contact official support first +- ✅ Ensure valid trial period before use + +
-## English +## 🌟 English -### Description +### 📝 Description A tool to resolve the following prompt issue during Cursor's free trial period: + + + + +
+
+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.
+
+
+ +### ✨ Features +- 🔄 Reset Cursor free trial limitations +- 🔍 Automatic detection and closing of running instances +- 🌐 Cross-platform support with architecture detection +- 📦 Automated installation scripts +- 🖥️ Both GUI and command-line interfaces + +### 💻 System Support +| Platform | Status | +|----------|---------| +| ![Windows](https://img.shields.io/badge/Windows-0078D6?style=flat&logo=windows&logoColor=white) | ✅ x64 | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | ✅ Intel/Apple Silicon | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | ✅ x64/ARM64 | + +### 📥 Installation Methods + +
+1️⃣ Automated Installation (Recommended) + +#### Linux/macOS +```bash +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/main/install.sh | sudo bash ``` -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. + +#### Windows PowerShell +```powershell +# Download and run with admin privileges +$url = "https://github.com/yuaotian/go-cursor-help/releases/latest/download/cursor-id-modifier.exe" +$output = "$env:TEMP\cursor-id-modifier.exe" +Invoke-WebRequest -Uri $url -OutFile $output +Start-Process -FilePath $output -Verb RunAs ``` +
-### Features -- Reset Cursor free trial limitations -- Provides both automatic and manual reset methods -- Support multiple platforms - -### System Support -- ✅ Windows (Tested) -- ✅ MacOS (Tested) -- ✅ Linux (Tested) - -### Automatic Reset -#### Prerequisites -- Requires administrator/root privileges -- Ensure Cursor is completely closed before use - -#### Usage -1. Download the appropriate executable for your system: - - Windows: `cursor_id_modifier.exe` - - MacOS: `cursor_id_modifier_mac` or `cursor_id_modifier_mac_arm64` - - Linux: `cursor_id_modifier_linux` -2. Run the program as administrator -3. Follow the prompts -4. Restart Cursor to apply changes - -### Manual Reset -1. Close Cursor completely -2. Locate the storage.json file: - - Windows: `%APPDATA%\Roaming\Cursor\User\globalStorage\storage.json` - - MacOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json` - - Linux: `~/.config/Cursor/User/globalStorage/storage.json` -3. Make the file writable (if needed): - - Windows: Right click -> Properties -> Uncheck "Read-only" - - MacOS/Linux: `chmod 666 storage.json` -4. Edit the file and replace these fields with new random values: - ```json - { - "telemetry.macMachineId": "generate-64-char-hex", - "telemetry.machineId": "generate-64-char-hex", - "telemetry.devDeviceId": "generate-uuid-format" - } - ``` - - For hex values: Use 64 characters (0-9, a-f) - - For UUID: Use format like "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -5. Make the file read-only: - - Windows: Right click -> Properties -> Check "Read-only" - - MacOS/Linux: `chmod 444 storage.json` -6. Restart Cursor +
+2️⃣ Manual Download -### ⚠️ Cautions -1. Use this tool at your own risk -2. Backup important data before use -3. For educational and research purposes only +Download from [Releases](https://github.com/yuaotian/go-cursor-help/releases/latest): + +| Platform | Architecture | File | +|----------|-------------|------| +| ![Windows](https://img.shields.io/badge/Windows-0078D6?style=flat&logo=windows&logoColor=white) | x64 | `cursor-id-modifier.exe` | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | Intel (x64) | `cursor-id-modifier-amd64` | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | Apple Silicon | `cursor-id-modifier-arm64` | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | x64 | `cursor-id-modifier` | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | ARM64 | `cursor-id-modifier-arm64` | + +
+ +### 📚 Usage Instructions + +
+Prerequisites + +- 👑 Administrator/root privileges required +- 🚫 Cursor should be completely closed +- 🌐 Internet connection for installation +
+ +
+Running the Tool + +The tool will automatically: +1. 🔑 Check for and request admin privileges +2. 🔍 Detect and close Cursor instances +3. 💾 Backup existing configuration +4. 🔄 Generate new identifiers +5. ✅ Apply changes + +Restart Cursor after completion. +
+ +### 🔧 Troubleshooting + +
+Common Issues + +#### 🚫 Permission Denied +- Windows: Right-click → "Run as Administrator" +- Linux/macOS: Use \`sudo\` when running -### Disclaimer -This tool is for educational purposes only. Users bear all risks and responsibilities associated with its use. +#### ⚠️ Cursor Still Running +- Tool will attempt auto-close +- Manual close via Task Manager if needed -### Contributing -Issues and Pull Requests are welcome to help improve this project. +#### 🔒 macOS Security +If you see "unidentified developer" warning: +1. Right-click → Open +2. Click "Open" in dialog +
+ +### ⚠️ Cautions +- 🛡️ Use at your own risk +- 💾 Backup important data +- 📚 Educational purposes only + +### 🤝 Contributing +We welcome contributions! Please ensure: +- ✅ Cross-platform compatibility +- 🚫 No breaking changes +- 🔍 Proper error handling +- 📝 Documentation updates --- -## 中文 +## 🌏 中文 -### 问题描述 +### 📝 问题描述 解决Cursor在免费订阅期间出现以下提示的问题: + + + + +
+
+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.
+
+
+ +### ✨ 功能特性 +- 🔄 重置Cursor免费试用限制 +- 🔍 自动检测和关闭运行中的实例 +- 🌐 跨平台支持,自动检测系统架构 +- 📦 自动化安装脚本 +- 🖥️ 支持图形界面和命令行 + +### 💻 系统支持 +| 平台 | 状态 | +|----------|---------| +| ![Windows](https://img.shields.io/badge/Windows-0078D6?style=flat&logo=windows&logoColor=white) | ✅ x64 | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | ✅ Intel/Apple Silicon | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | ✅ x64/ARM64 | + +### 📥 安装方法 + +
+1️⃣ 自动安装(推荐) + +#### Linux/macOS +```bash +curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/main/install.sh | sudo bash ``` -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. + +#### Windows PowerShell +```powershell +# 下载并以管理员权限运行 +$url = "https://github.com/yuaotian/go-cursor-help/releases/latest/download/cursor-id-modifier.exe" +$output = "$env:TEMP\cursor-id-modifier.exe" +Invoke-WebRequest -Uri $url -OutFile $output +Start-Process -FilePath $output -Verb RunAs ``` +
-### 功能特性 -- 重置Cursor免费试用限制 -- 提供自动和手动重置方法 -- 支持多个操作系统平台 - -### 系统支持 -- ✅ Windows (已测试) -- ✅ MacOS (已测试) -- ✅ Linux (已测试) - -### 自动重置 -#### 使用前提 -- 需要管理员/root权限执行 -- 请确保已完全退出Cursor程序 - -#### 使用方法 -1. 下载对应系统的可执行文件: - - Windows系统:`cursor_id_modifier.exe` - - MacOS系统:`cursor_id_modifier_mac` 或 `cursor_id_modifier_mac_arm64` - - Linux系统:`cursor_id_modifier_linux` -2. 以管理员身份运行程序 -3. 按照提示进行操作 -4. 重启Cursor即可 - -### 手动重置 -1. 完全关闭Cursor -2. 找到storage.json文件: - - Windows: `%APPDATA%\Roaming\Cursor\User\globalStorage\storage.json` - - MacOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json` - - Linux: `~/.config/Cursor/User/globalStorage/storage.json` -3. 修改文件为可写(如果需要): - - Windows: 右键 -> 属性 -> 取消勾选"只读" - - MacOS/Linux: `chmod 666 storage.json` -4. 编辑文件,替换以下字段为新的随机值: - ```json - { - "telemetry.macMachineId": "生成64位十六进制", - "telemetry.machineId": "生成64位十六进制", - "telemetry.devDeviceId": "生成UUID格式" - } - ``` - - 十六进制值:使用64个字符(0-9, a-f) - - UUID格式:类似 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -5. 将文件设为只读: - - Windows: 右键 -> 属性 -> 勾选"只读" - - MacOS/Linux: `chmod 444 storage.json` -6. 重启Cursor +
+2️⃣ 手动下载 -### ⚠️ 注意事项 -1. 使用本工具需要您自行承担风险 -2. 建议在重要数据做好备份后使用 -3. 本工具仅用于学习研究,请勿用于商业用途 +从[发布页面](https://github.com/yuaotian/go-cursor-help/releases/latest)下载: + +| 平台 | 架构 | 文件 | +|----------|-------------|------| +| ![Windows](https://img.shields.io/badge/Windows-0078D6?style=flat&logo=windows&logoColor=white) | x64 | `cursor-id-modifier.exe` | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | Intel (x64) | `cursor-id-modifier-amd64` | +| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat&logo=apple&logoColor=white) | Apple Silicon | `cursor-id-modifier-arm64` | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | x64 | `cursor-id-modifier` | +| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat&logo=linux&logoColor=black) | ARM64 | `cursor-id-modifier-arm64` | + +
+ +### 📚 使用说明 + +
+使用前提 + +- 👑 需要管理员/root权限 +- 🚫 确保Cursor完全关闭 +- 🌐 需要网络连接进行安装 +
+ +
+运行工具 + +工具将自动执行: +1. 🔑 检查并请求管理员权限 +2. 🔍 检测并关闭Cursor进程 +3. 💾 备份现有配置 +4. 🔄 生成新的标识符 +5. ✅ 应用更改 -### 免责声明 -本工具仅供学习交流使用,使用本工具所造成的任何问题由使用者自行承担。 +完成后重启Cursor即可。 +
-### 贡献 -欢迎提交Issue和Pull Request来帮助改进这个项目。 +### 🔧 故障排除 -## License +
+常见问题 + +#### 🚫 权限被拒绝 +- Windows:右键 → "以管理员身份运行" +- Linux/macOS:使用 \`sudo\` 运行 + +#### ⚠️ Cursor仍在运行 +- 工具会尝试自动关闭 +- 如需要请通过任务管理器手动关闭 + +#### 🔒 macOS安全性问题 +如果看到"未识别的开发者"提示: +1. 右键点击 → 打开 +2. 点击确认对话框中的"打开" +
+ +### ⚠️ 注意事项 +- 🛡️ 使用本工具需自行承担风险 +- 💾 使用前请备份重要数据 +- 📚 仅用于学习研究目的 + +### 🤝 贡献 +欢迎提交问题和改进建议!请确保: +- ✅ 保持跨平台兼容性 +- 🚫 避免破坏性更改 +- 🔍 完善的错误处理 +- 📝 更新相关文档 + +## 📄 License MIT License Copyright (c) 2024 @@ -176,10 +292,4 @@ 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. - - - - - - +copies or substantial portions of the Software. \ No newline at end of file diff --git a/app.exe b/app.exe new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/app.exe differ diff --git a/app.manifest b/app.manifest new file mode 100644 index 0000000..7af02b2 --- /dev/null +++ b/app.manifest @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/cursor-id-modifier-linux b/cursor-id-modifier-linux new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/cursor-id-modifier-linux differ diff --git a/cursor-id-modifier-macos b/cursor-id-modifier-macos new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/cursor-id-modifier-macos differ diff --git a/go.mod b/go.mod index 9d89899..2de8890 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module go-cursor-help +module github.com/yuaotian/go-cursor-help go 1.22.0 @@ -7,5 +7,5 @@ require github.com/fatih/color v1.18.0 require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.28.0 ) diff --git a/go.sum b/go.sum index 33148a4..a266003 100644 --- a/go.sum +++ b/go.sum @@ -9,3 +9,5 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..fdc3afe --- /dev/null +++ b/install.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Error handling function / 错误处理函数 +error() { + echo "Error/错误: $1" >&2 + exit 1 +} + +# Detect OS and architecture / 检测操作系统和架构 +detect_platform() { + # Get lowercase OS name and architecture / 获取小写操作系统名称和架构 + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + # Set binary name based on platform / 根据平台设置二进制文件名 + case "$OS" in + linux*) + case "$ARCH" in + x86_64) BINARY_NAME="cursor-id-modifier" ;; + aarch64) BINARY_NAME="cursor-id-modifier-arm64" ;; + *) error "Unsupported Linux architecture/不支持的Linux架构: $ARCH" ;; + esac + ;; + darwin*) + case "$ARCH" in + x86_64) BINARY_NAME="cursor-id-modifier-amd64" ;; + arm64) BINARY_NAME="cursor-id-modifier-arm64" ;; + *) error "Unsupported macOS architecture/不支持的macOS架构: $ARCH" ;; + esac + ;; + *) + error "Unsupported operating system/不支持的操作系统: $OS" + ;; + esac +} + +# Check root privileges / 检查root权限 +if [ "$(id -u)" -ne 0 ]; then + error "This script must be run with sudo or as root/此脚本必须使用sudo或root权限运行" +fi + +# Initialize installation / 初始化安装 +detect_platform +INSTALL_DIR="/usr/local/bin" +[ -d "$INSTALL_DIR" ] || mkdir -p "$INSTALL_DIR" + +# Download binary / 下载二进制文件 +echo "Downloading cursor-id-modifier for/正在下载 $OS ($ARCH)..." +TEMP_DIR=$(mktemp -d) +DOWNLOAD_URL="https://github.com/yuaotian/go-cursor-help/releases/latest/download/$BINARY_NAME" + +if ! curl -fsSL "$DOWNLOAD_URL" -o "$TEMP_DIR/$BINARY_NAME"; then + error "Failed to download binary/下载二进制文件失败" +fi + +# Set permissions / 设置权限 +if ! chmod +x "$TEMP_DIR/$BINARY_NAME"; then + error "Failed to make binary executable/无法设置可执行权限" +fi + +# Handle macOS security / 处理macOS安全设置 +if [ "$OS" = "darwin" ]; then + echo "Removing macOS quarantine attribute/移除macOS隔离属性..." + xattr -d com.apple.quarantine "$TEMP_DIR/$BINARY_NAME" 2>/dev/null || true +fi + +# Install binary / 安装二进制文件 +if ! mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/cursor-id-modifier"; then + error "Failed to install binary/安装二进制文件失败" +fi + +# Cleanup / 清理 +rm -rf "$TEMP_DIR" + +echo "✅ Installation successful! You can now run 'cursor-id-modifier' from anywhere." +echo "✅ 安装成功!现在可以在任何位置运行 'cursor-id-modifier'。" \ No newline at end of file diff --git a/main.go b/main.go index ccd3034..dfa7a7b 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,7 @@ +// 主程序包 / Main package package main +// 导入所需的包 / Import required packages import ( "bufio" "crypto/rand" @@ -13,61 +15,132 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "time" "github.com/fatih/color" + "golang.org/x/sys/windows" ) -// StorageConfig 存储配置结构体优化 +// 语言类型和常量 / Language type and constants +type Language string + +const ( + CN Language = "cn" + EN Language = "en" + + // Version constant + Version = "1.0.1" +) + +// TextResource 存储多语言文本 / TextResource stores multilingual text +type TextResource struct { + SuccessMessage string + RestartMessage string + ReadingConfig string + GeneratingIds string + PressEnterToExit string + ErrorPrefix string + PrivilegeError string + RunAsAdmin string + RunWithSudo string + SudoExample string +} + +// StorageConfig 优化的存储配置结构 / StorageConfig optimized storage configuration struct type StorageConfig struct { - TelemetryMacMachineId string `json:"telemetry.macMachineId"` - TelemetryMachineId string `json:"telemetry.machineId"` - TelemetryDevDeviceId string `json:"telemetry.devDeviceId"` + TelemetryMacMachineId string `json:"telemetry.macMachineId"` + TelemetryMachineId string `json:"telemetry.machineId"` + TelemetryDevDeviceId string `json:"telemetry.devDeviceId"` LastModified time.Time `json:"lastModified"` Version string `json:"version"` } -// NewStorageConfig 创建新的配置实例 +// AppError 定义错误类型 / AppError defines error types +type AppError struct { + Op string + Path string + Err error +} + +// ProgressSpinner 用于显示进度动画 / ProgressSpinner for showing progress animation +type ProgressSpinner struct { + frames []string + current int + message string +} + +// 全局变量 / Global variables +var ( + currentLanguage = CN // 默认为中文 / Default to Chinese + + texts = map[Language]TextResource{ + CN: { + SuccessMessage: "[√] 配置文件已成功更新!", + RestartMessage: "[!] 请手动重启 Cursor 以使更新生效", + ReadingConfig: "正在读取配置文件...", + GeneratingIds: "正在生成新的标识符...", + PressEnterToExit: "按回车键退出程序...", + ErrorPrefix: "程序发生严重错误: %v", + PrivilegeError: "\n[!] 错误:需要管理员权限", + RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」", + RunWithSudo: "请使用 sudo 命令运行此程序", + SudoExample: "示例: sudo %s", + }, + EN: { + SuccessMessage: "[√] Configuration file updated successfully!", + RestartMessage: "[!] Please restart Cursor manually for changes to take effect", + ReadingConfig: "Reading configuration file...", + GeneratingIds: "Generating new identifiers...", + PressEnterToExit: "Press Enter to exit...", + ErrorPrefix: "Program encountered a serious error: %v", + PrivilegeError: "\n[!] Error: Administrator privileges required", + RunAsAdmin: "Please right-click and select 'Run as Administrator'", + RunWithSudo: "Please run this program with sudo", + SudoExample: "Example: sudo %s", + }, + } +) + +// Error implementation for AppError +func (e *AppError) Error() string { + if e.Path != "" { + return fmt.Sprintf("%s: %v [Path: %s]", e.Op, e.Err, e.Path) + } + return fmt.Sprintf("%s: %v", e.Op, e.Err) +} + +// NewStorageConfig 创建新的配置实例 / Creates a new configuration instance func NewStorageConfig() *StorageConfig { return &StorageConfig{ - TelemetryMacMachineId: generateMacMachineId(), + TelemetryMacMachineId: generateMachineId(), TelemetryMachineId: generateMachineId(), TelemetryDevDeviceId: generateDevDeviceId(), LastModified: time.Now(), - Version: "1.0.1", + Version: Version, } } -// 生成类似原始machineId的字符串 (64位小写hex) +// 生成类似原始machineId的字符串(64位小写十六进制) / Generate a string similar to the original machineId (64-bit lowercase hex) func generateMachineId() string { - // 生成一些随机数据 data := make([]byte, 32) - rand.Read(data) - - // 使用SHA256生成hash - hash := sha256.New() - hash.Write(data) - - // 转换为小写的hex字符串 - return hex.EncodeToString(hash.Sum(nil)) -} - -// 生成类似原始macMachineId的字符串 (64位小写hex) -func generateMacMachineId() string { - return generateMachineId() // 使用相同的格式 + if _, err := rand.Read(data); err != nil { + panic(fmt.Errorf("failed to generate random data: %v", err)) + } + hash := sha256.Sum256(data) + return hex.EncodeToString(hash[:]) } -// 生成类似原始devDeviceId的字符 (标准UUID格式) +// 生成类似原始devDeviceId的字符串(标准UUID格式) / Generate a string similar to the original devDeviceId (standard UUID format) func generateDevDeviceId() string { - // 生成 UUID v4 uuid := make([]byte, 16) - rand.Read(uuid) + if _, err := rand.Read(uuid); err != nil { + panic(fmt.Errorf("failed to generate UUID: %v", err)) + } - // 设置版本 (4) 和变体位 - uuid[6] = (uuid[6] & 0x0f) | 0x40 // 版本 4 - uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 变体 + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant - // 格式化为标准 UUID 字符串 return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], @@ -76,19 +149,39 @@ func generateDevDeviceId() string { uuid[10:16]) } -// 获取配置文件路径 +// NewProgressSpinner creates a new progress spinner +func NewProgressSpinner(message string) *ProgressSpinner { + return &ProgressSpinner{ + frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, + message: message, + } +} + +// Spin advances the spinner animation +func (s *ProgressSpinner) Spin() { + frame := s.frames[s.current%len(s.frames)] + s.current++ + fmt.Printf("\r%s %s", color.CyanString(frame), s.message) +} + +// Stop ends the spinner animation +func (s *ProgressSpinner) Stop() { + fmt.Println() +} + +// File and system operations + func getConfigPath() (string, error) { var configDir string switch runtime.GOOS { + case "windows": + configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage") case "darwin": homeDir, err := os.UserHomeDir() if err != nil { return "", err } configDir = filepath.Join(homeDir, "Library", "Application Support", "Cursor", "User", "globalStorage") - case "windows": - appData := os.Getenv("APPDATA") - configDir = filepath.Join(appData, "Cursor", "User", "globalStorage") case "linux": homeDir, err := os.UserHomeDir() if err != nil { @@ -96,22 +189,87 @@ func getConfigPath() (string, error) { } configDir = filepath.Join(homeDir, ".config", "Cursor", "User", "globalStorage") default: - return "", fmt.Errorf("不支持的操作系统: %s", runtime.GOOS) + return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } return filepath.Join(configDir, "storage.json"), nil } -// 修改件权限 +func safeWriteFile(path string, data []byte, perm os.FileMode) error { + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0755); err != nil { + return &AppError{"create directory", dir, err} + } + + tmpPath := path + ".tmp" + if err := os.WriteFile(tmpPath, data, perm); err != nil { + return &AppError{"write temporary file", tmpPath, err} + } + + if err := os.Rename(tmpPath, path); err != nil { + os.Remove(tmpPath) + return &AppError{"rename file", path, err} + } + + return nil +} + func setFilePermissions(filePath string) error { + return os.Chmod(filePath, 0444) +} + +// Process management functions + +func killCursorProcesses() error { + if runtime.GOOS == "windows" { + // First try graceful shutdown + exec.Command("taskkill", "/IM", "Cursor.exe").Run() + exec.Command("taskkill", "/IM", "cursor.exe").Run() + + time.Sleep(time.Second) + + // Force kill any remaining instances + exec.Command("taskkill", "/F", "/IM", "Cursor.exe").Run() + exec.Command("taskkill", "/F", "/IM", "cursor.exe").Run() + } else { + exec.Command("pkill", "-f", "Cursor").Run() + exec.Command("pkill", "-f", "cursor").Run() + } + return nil +} + +func checkCursorRunning() bool { + var cmd *exec.Cmd if runtime.GOOS == "windows" { - // Windows 使用 ACL 权限系统,这里仅设置为只读 - return os.Chmod(filePath, 0444) + cmd = exec.Command("tasklist", "/FI", "IMAGENAME eq Cursor.exe", "/NH") } else { - // Linux 和 macOS - return os.Chmod(filePath, 0444) + cmd = exec.Command("pgrep", "-f", "Cursor") } + + output, _ := cmd.Output() + return strings.Contains(string(output), "Cursor") || strings.Contains(string(output), "cursor") } +// UI and display functions + +func clearScreen() { + var cmd *exec.Cmd + if runtime.GOOS == "windows" { + cmd = exec.Command("cmd", "/c", "cls") + } else { + cmd = exec.Command("clear") + } + cmd.Stdout = os.Stdout + cmd.Run() +} + +func showProgress(message string) { + spinner := NewProgressSpinner(message) + for i := 0; i < 15; i++ { + spinner.Spin() + time.Sleep(100 * time.Millisecond) + } + spinner.Stop() +} func printCyberpunkBanner() { cyan := color.New(color.FgCyan, color.Bold) @@ -123,7 +281,7 @@ func printCyberpunkBanner() { ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ - ██║ ██║ ██║█╔══██╗╚════██║██║ ██║██╔══██╗ + ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ` @@ -131,7 +289,6 @@ func printCyberpunkBanner() { yellow.Println("\t\t>> Cursor ID Modifier v1.0 <<") magenta.Println("\t\t [ By Pancake Fruit Rolled Shark Chili ]") - // 添加语言标识 langText := "当前语言/Language: " if currentLanguage == CN { langText += "简体中文" @@ -141,352 +298,265 @@ func printCyberpunkBanner() { green.Printf("\n\t\t %s\n\n", langText) } -type ProgressSpinner struct { - frames []string - current int - message string -} - -func NewProgressSpinner(message string) *ProgressSpinner { - return &ProgressSpinner{ - frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, - message: message, - } -} - -func (s *ProgressSpinner) Spin() { - frame := s.frames[s.current%len(s.frames)] - s.current++ - fmt.Printf("\r%s %s", color.CyanString(frame), s.message) -} - -func (s *ProgressSpinner) Stop() { - fmt.Println() -} - -// 定义错误类型 -type AppError struct { - Op string - Path string - Err error -} - -func (e *AppError) Error() string { - if e.Path != "" { - return fmt.Sprintf("%s: %v [路径: %s]", e.Op, e.Err, e.Path) - } - return fmt.Sprintf("%s: %v", e.Op, e.Err) -} - -// 文件操作包装函数 -func safeWriteFile(path string, data []byte, perm os.FileMode) error { - // 创建临时文件 - tmpPath := path + ".tmp" - if err := os.WriteFile(tmpPath, data, perm); err != nil { - return &AppError{"写入临时文件", tmpPath, err} - } - - // 重命名临时文件 - if err := os.Rename(tmpPath, path); err != nil { - os.Remove(tmpPath) // 清理临时文件 - return &AppError{"重命名文件", path, err} - } +func showSuccess() { + text := texts[currentLanguage] + successColor := color.New(color.FgGreen, color.Bold) + warningColor := color.New(color.FgYellow, color.Bold) - return nil + successColor.Printf("\n%s\n", text.SuccessMessage) + warningColor.Printf("%s\n", text.RestartMessage) } - - -// clearScreen 清除终端屏幕 -func clearScreen() { +func showPrivilegeError() { + text := texts[currentLanguage] + red := color.New(color.FgRed, color.Bold) + yellow := color.New(color.FgYellow) + + red.Println(text.PrivilegeError) if runtime.GOOS == "windows" { - cmd := exec.Command("cmd", "/c", "cls") - cmd.Stdout = os.Stdout - cmd.Run() + yellow.Println(text.RunAsAdmin) } else { - cmd := exec.Command("clear") - cmd.Stdout = os.Stdout - cmd.Run() + yellow.Println(text.RunWithSudo) + yellow.Printf(text.SudoExample, os.Args[0]) } } -// showProgress 显示进度 -func showProgress(message string) { - spinner := NewProgressSpinner(message) - for i := 0; i < 15; i++ { - spinner.Spin() - time.Sleep(100 * time.Millisecond) +func showIdComparison(oldConfig *StorageConfig, newConfig *StorageConfig) { + cyan := color.New(color.FgCyan) + yellow := color.New(color.FgYellow) + + fmt.Println("\n=== ID Modification Comparison / ID 修改对比 ===") + + if oldConfig != nil { + cyan.Println("\n[Original IDs / 原始 ID]") + yellow.Printf("Machine ID: %s\n", oldConfig.TelemetryMachineId) + yellow.Printf("Mac Machine ID: %s\n", oldConfig.TelemetryMacMachineId) + yellow.Printf("Dev Device ID: %s\n", oldConfig.TelemetryDevDeviceId) } - spinner.Stop() + + cyan.Println("\n[Newly Generated IDs / 新生成 ID]") + yellow.Printf("Machine ID: %s\n", newConfig.TelemetryMachineId) + yellow.Printf("Mac Machine ID: %s\n", newConfig.TelemetryMacMachineId) + yellow.Printf("Dev Device ID: %s\n", newConfig.TelemetryDevDeviceId) + fmt.Println() } -// saveConfig 保存配置到文件 +// Configuration operations + func saveConfig(config *StorageConfig) error { configPath, err := getConfigPath() if err != nil { return err } - // 转换为JSON content, err := json.MarshalIndent(config, "", " ") if err != nil { - return &AppError{"生成JSON", "", err} + return &AppError{"generate JSON", "", err} } - // 确保文件可写 - err = os.Chmod(configPath, 0666) - if err != nil { - return &AppError{"修改文件权限", configPath, err} + if err := os.Chmod(configPath, 0666); err != nil && !os.IsNotExist(err) { + return &AppError{"modify file permissions", configPath, err} } - // 安全写入文件 if err := safeWriteFile(configPath, content, 0666); err != nil { return err } - // 设置为只读 return setFilePermissions(configPath) } -// showSuccess 显示成功信息 -func showSuccess() { - text := texts[currentLanguage] - successColor := color.New(color.FgGreen, color.Bold) - warningColor := color.New(color.FgYellow, color.Bold) - - successColor.Printf("\n%s\n", text.SuccessMessage) - warningColor.Printf("%s\n", text.RestartMessage) -} - -// 修改 loadAndUpdateConfig 函数使用 configPath -func loadAndUpdateConfig() (*StorageConfig, error) { +func readExistingConfig() (*StorageConfig, error) { configPath, err := getConfigPath() if err != nil { return nil, err } - text := texts[currentLanguage] - showProgress(text.ReadingConfig) - - // 读取原始文件内容 - _, err = os.ReadFile(configPath) - if err != nil && !os.IsNotExist(err) { - return nil, &AppError{"读取配置文件", configPath, err} + data, err := os.ReadFile(configPath) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err } - showProgress(text.GeneratingIds) - config := NewStorageConfig() - - return config, nil -} + var config StorageConfig + if err := json.Unmarshal(data, &config); err != nil { + return nil, err + } -// 修改 waitExit 函数,正确初始化 reader -func waitExit() { - reader := bufio.NewReader(os.Stdin) - color.Cyan("\n" + texts[currentLanguage].PressEnterToExit) - reader.ReadString('\n') + return &config, nil } -func main() { - currentLanguage = detectLanguage() - defer func() { - if err := recover(); err != nil { - color.Red(texts[currentLanguage].ErrorPrefix, err) - waitExit() - } - }() - - // 添加权限检查 - isAdmin, err := checkAdminPrivileges() +func loadAndUpdateConfig() (*StorageConfig, error) { + configPath, err := getConfigPath() if err != nil { - handleError("权限检查失败", err) - waitExit() - return - } - - if !isAdmin { - showPrivilegeError() - waitExit() - return + return nil, err } - setupProgram() - - oldConfig, err := readExistingConfig() - if err != nil { - oldConfig = nil - } - - config, err := loadAndUpdateConfig() - if err != nil { - handleError("配置更新失败", err) - return - } - - showIdComparison(oldConfig, config) + text := texts[currentLanguage] + showProgress(text.ReadingConfig) - if err := saveConfig(config); err != nil { - handleError("保存配置失败", err) - return + _, err = os.ReadFile(configPath) + if err != nil && !os.IsNotExist(err) { + return nil, &AppError{"read config file", configPath, err} } - showSuccess() - waitExit() -} - -func setupProgram() { - clearScreen() - printCyberpunkBanner() + showProgress(text.GeneratingIds) + return NewStorageConfig(), nil } -func handleError(msg string, err error) { - if appErr, ok := err.(*AppError); ok { - color.Red("%s: %v", msg, appErr) - } else { - color.Red("%s: %v", msg, err) - } -} +// System privilege functions func checkAdminPrivileges() (bool, error) { switch runtime.GOOS { case "windows": - // Windows 管理员权限检查 cmd := exec.Command("net", "session") err := cmd.Run() return err == nil, nil case "darwin", "linux": - // Unix 系统检查 root 权限 currentUser, err := user.Current() if err != nil { - return false, fmt.Errorf("获取当前用户失败: %v", err) + return false, fmt.Errorf("failed to get current user: %v", err) } return currentUser.Uid == "0", nil default: - return false, fmt.Errorf("不支持的操作系统: %s", runtime.GOOS) + return false, fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } } -func showPrivilegeError() { - text := texts[currentLanguage] - red := color.New(color.FgRed, color.Bold) - yellow := color.New(color.FgYellow) +func selfElevate() error { + verb := "runas" + exe, err := os.Executable() + if err != nil { + return err + } - red.Println(text.PrivilegeError) - if runtime.GOOS == "windows" { - yellow.Println(text.RunAsAdmin) - } else { - yellow.Println(text.RunWithSudo) - yellow.Printf(text.SudoExample, os.Args[0]) + cwd, err := os.Getwd() + if err != nil { + return err } -} -// 在文件开头添加新的类型和变量定义 -type Language string + verbPtr, _ := syscall.UTF16PtrFromString(verb) + exePtr, _ := syscall.UTF16PtrFromString(exe) + cwdPtr, _ := syscall.UTF16PtrFromString(cwd) + argPtr, _ := syscall.UTF16PtrFromString("") -const ( - CN Language = "cn" - EN Language = "en" -) + var showCmd int32 = 1 //SW_NORMAL -// TextResource 存储多语言文本 -type TextResource struct { - SuccessMessage string - RestartMessage string - ReadingConfig string - GeneratingIds string - PressEnterToExit string - ErrorPrefix string - PrivilegeError string - RunAsAdmin string - RunWithSudo string - SudoExample string + err = windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) + if err != nil { + return err + } + os.Exit(0) + return nil } -var ( - currentLanguage = CN // 默认使用中文 - - texts = map[Language]TextResource{ - CN: { - SuccessMessage: "[√] 配置文件已成功更新!", - RestartMessage: "[!] 请手动重启 Cursor 以使更改生效", - ReadingConfig: "正在读取配置文件...", - GeneratingIds: "正在生成新的标识符...", - PressEnterToExit: "按回车键退出程序...", - ErrorPrefix: "程序发生严重错误: %v", - PrivilegeError: "\n[!] 错误:需要管理员权限", - RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」", - RunWithSudo: "请使用 sudo 命令运行此程序", - SudoExample: "示例: sudo %s", - }, - EN: { - SuccessMessage: "[√] Configuration file updated successfully!", - RestartMessage: "[!] Please restart Cursor manually for changes to take effect", - ReadingConfig: "Reading configuration file...", - GeneratingIds: "Generating new identifiers...", - PressEnterToExit: "Press Enter to exit...", - ErrorPrefix: "Program encountered a serious error: %v", - PrivilegeError: "\n[!] Error: Administrator privileges required", - RunAsAdmin: "Please right-click and select 'Run as Administrator'", - RunWithSudo: "Please run this program with sudo", - SudoExample: "Example: sudo %s", - }, - } -) +// Utility functions -// 添加语言检测函数 func detectLanguage() Language { - // 获取系统语言环境 - lang := os.Getenv("LANG") - if lang == "" { - lang = os.Getenv("LANGUAGE") - } - - // 如果包含 zh 则使用中文,否则使用英文 - if strings.Contains(strings.ToLower(lang), "zh") { - return CN - } - return EN + lang := os.Getenv("LANG") + if lang == "" { + lang = os.Getenv("LANGUAGE") + } + + if strings.Contains(strings.ToLower(lang), "zh") { + return CN + } + return EN } -// 添加新函数用于显示ID对比 -func showIdComparison(oldConfig *StorageConfig, newConfig *StorageConfig) { - cyan := color.New(color.FgCyan) - yellow := color.New(color.FgYellow) - - fmt.Println("\n=== ID 修改对比 ===") - - if oldConfig != nil { - cyan.Println("\n[原始 ID]") - yellow.Printf("Machine ID: %s\n", oldConfig.TelemetryMachineId) - yellow.Printf("Mac Machine ID: %s\n", oldConfig.TelemetryMacMachineId) - yellow.Printf("Dev Device ID: %s\n", oldConfig.TelemetryDevDeviceId) +func waitExit() { + fmt.Println("\nPress Enter to exit... / 按回车键退出程序...") + os.Stdout.Sync() + bufio.NewReader(os.Stdin).ReadString('\n') +} + +func handleError(msg string, err error) { + if appErr, ok := err.(*AppError); ok { + color.Red("%s: %v", msg, appErr) + } else { + color.Red("%s: %v", msg, err) } - - cyan.Println("\n[新生成 ID]") - yellow.Printf("Machine ID: %s\n", newConfig.TelemetryMachineId) - yellow.Printf("Mac Machine ID: %s\n", newConfig.TelemetryMacMachineId) - yellow.Printf("Dev Device ID: %s\n", newConfig.TelemetryDevDeviceId) - fmt.Println() } -// 新增函数用于读取现有配置 -func readExistingConfig() (*StorageConfig, error) { - configPath, err := getConfigPath() +// Main program entry + +func main() { + defer func() { + if r := recover(); r != nil { + color.Red(texts[currentLanguage].ErrorPrefix, r) + fmt.Println("\nAn error occurred! / 发生错误!") + waitExit() + } + }() + + os.Stdout.Sync() + currentLanguage = detectLanguage() + + isAdmin, err := checkAdminPrivileges() if err != nil { - return nil, err + handleError("permission check failed", err) + waitExit() + return } - data, err := os.ReadFile(configPath) - if err != nil { - return nil, err + if !isAdmin && runtime.GOOS == "windows" { + fmt.Println("Requesting administrator privileges... / 请求管理员权限...") + if err := selfElevate(); err != nil { + handleError("failed to elevate privileges", err) + showPrivilegeError() + waitExit() + return + } + return + } else if !isAdmin { + showPrivilegeError() + waitExit() + return } - var config StorageConfig - if err := json.Unmarshal(data, &config); err != nil { - return nil, err + if checkCursorRunning() { + fmt.Println("\nDetected running Cursor instance(s). Closing... / 检测到正在运行的 Cursor 实例,正在关闭...") + if err := killCursorProcesses(); err != nil { + fmt.Println("Warning: Could not close all Cursor instances. Please close them manually. / 警告:无法关闭所有 Cursor 实例,请手动关闭。") + waitExit() + return + } + + time.Sleep(2 * time.Second) + if checkCursorRunning() { + fmt.Println("\nWarning: Cursor is still running. Please close it manually. / 警告:Cursor 仍在运行,请手动关闭。") + waitExit() + return + } } - return &config, nil + clearScreen() + printCyberpunkBanner() + + oldConfig, err := readExistingConfig() + if err != nil { + oldConfig = nil + } + + config, err := loadAndUpdateConfig() + if err != nil { + handleError("configuration update failed", err) + waitExit() + return + } + + showIdComparison(oldConfig, config) + + if err := saveConfig(config); err != nil { + handleError("failed to save configuration", err) + waitExit() + return + } + + showSuccess() + fmt.Println("\nOperation completed! / 操作完成!") + waitExit() } diff --git a/releases/linux/cursor-id-modifier b/releases/linux/cursor-id-modifier new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/releases/linux/cursor-id-modifier differ diff --git a/releases/macos/cursor-id-modifier-amd64 b/releases/macos/cursor-id-modifier-amd64 new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/releases/macos/cursor-id-modifier-amd64 differ diff --git a/releases/macos/cursor-id-modifier-arm64 b/releases/macos/cursor-id-modifier-arm64 new file mode 100644 index 0000000..d1b67a7 Binary files /dev/null and b/releases/macos/cursor-id-modifier-arm64 differ diff --git a/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier new file mode 100644 index 0000000..81163c1 Binary files /dev/null and b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier differ diff --git a/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-amd64 b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-amd64 new file mode 100644 index 0000000..81163c1 Binary files /dev/null and b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-amd64 differ diff --git a/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-arm64 b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-arm64 new file mode 100644 index 0000000..81163c1 Binary files /dev/null and b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier-arm64 differ diff --git a/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier.exe b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier.exe new file mode 100644 index 0000000..81163c1 Binary files /dev/null and b/releases/v1.0.1/releases/v1.0.1/cursor-id-modifier.exe differ