8 Commits

Author SHA1 Message Date
煎饼果子卷鲨鱼辣椒 79817c9b44 docs(readme): 更新logo图片路径 1 month ago
煎饼果子卷鲨鱼辣椒 db49f51bd9 docs(readme): 调整运行成功截图的位置 1 month ago
煎饼果子卷鲨鱼辣椒 660bbe3ecc docs(readme): 更新 README 文件中的运行成功截图和 Windows 安装说明 1 month ago
煎饼果子卷鲨鱼辣椒 aaa76fc387 refactor(scripts): 移除旧版构建和修改脚本 1 month ago
煎饼果子卷鲨鱼辣椒 f5f1e64eec docs(readme): 更新支持的Cursor版本信息 1 month ago
煎饼果子卷鲨鱼辣椒 139ba0b638 chore(scripts): 删除 Cursor 免费试用重置脚本 1 month ago
煎饼果子卷鲨鱼辣椒 8bd3afc320 feat(cursor): 增强设备标识符管理与配置更新 1 month ago
煎饼果子卷鲨鱼辣椒 5f01e1a5dd refactor(scripts): 优化跨平台ID修改脚本的ESM兼容性 1 month ago
  1. 92
      .goreleaser.yml
  2. 246
      README.md
  3. 142
      README_CN.md
  4. 328
      cmd/cursor-id-modifier/main.go
  5. 15
      go.mod
  6. 26
      go.sum
  7. BIN
      img/cursor.png
  8. 153
      internal/config/config.go
  9. 187
      internal/lang/lang.go
  10. 216
      internal/process/manager.go
  11. 94
      internal/ui/display.go
  12. 20
      internal/ui/logo.go
  13. 122
      internal/ui/spinner.go
  14. 116
      pkg/idgen/generator.go
  15. 375
      process_cursor_links.py
  16. 74
      scripts/build_all.bat
  17. 143
      scripts/build_all.sh
  18. 318
      scripts/cursor_id_modifier.pot
  19. 1298
      scripts/cursor_id_modifier.py
  20. 9
      scripts/git-actions.sh
  21. 193
      scripts/install.ps1
  22. 127
      scripts/install.sh
  23. 90
      scripts/run/cursor_linux_id_modifier.sh
  24. 1401
      scripts/run/cursor_mac_free_trial_reset.sh
  25. 290
      scripts/run/cursor_mac_id_modifier.sh
  26. 688
      scripts/run/cursor_mac_id_modifier_new.sh
  27. 86
      scripts/run/cursor_win_id_modifier.ps1
  28. 607
      scripts/run/cursor_win_id_modifier_old.ps1

92
.goreleaser.yml

@ -1,92 +0,0 @@
version: 2
before:
hooks:
- go mod tidy
- go mod vendor
- go mod verify
builds:
- id: cursor-id-modifier
main: ./cmd/cursor-id-modifier/main.go
binary: cursor-id-modifier
env:
- CGO_ENABLED=0
- GO111MODULE=on
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
- "386"
ignore:
- goos: darwin
goarch: "386"
ldflags:
- -s -w
- -X 'main.version={{.Version}}'
- -X 'main.commit={{.ShortCommit}}'
- -X 'main.date={{.CommitDate}}'
- -X 'main.builtBy=goreleaser'
flags:
- -trimpath
mod_timestamp: '{{ .CommitTimestamp }}'
archives:
- id: binary
format: binary
name_template: >-
{{- .ProjectName }}_
{{- .Version }}_
{{- .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
builds:
- cursor-id-modifier
allow_different_binary_count: true
files:
- none*
checksum:
name_template: 'checksums.txt'
algorithm: sha256
release:
draft: true
prerelease: auto
mode: replace
header: |
## Release {{.Tag}} ({{.Date}})
See [CHANGELOG.md](CHANGELOG.md) for details.
footer: |
**Full Changelog**: https://github.com/owner/repo/compare/{{ .PreviousTag }}...{{ .Tag }}
extra_files:
- glob: 'LICENSE*'
- glob: 'README*'
- glob: 'CHANGELOG*'
changelog:
sort: asc
use: github
filters:
exclude:
- '^docs:'
- '^test:'
- '^ci:'
- Merge pull request
- Merge branch
groups:
- title: Features
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: 'Bug fixes'
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: Others
order: 999
project_name: cursor-id-modifier

246
README.md

@ -8,7 +8,7 @@
[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md) [🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md)
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
<img src="/img/cursor.png" alt="Cursor Logo" width="120"/>
</div> </div>
@ -47,230 +47,13 @@
> ⚠️ **IMPORTANT NOTICE** > ⚠️ **IMPORTANT NOTICE**
> >
> This tool currently supports: > This tool currently supports:
> - ✅ Windows: Latest 1.0.x versions (Supported)
> - ✅ Mac/Linux: Latest 1.0.x versions (Supported, feedback welcome)
> - ✅ Windows: Latest 2.x.x versions (Supported)
> - ✅ Mac/Linux: Latest 2.x.x versions (Supported, feedback welcome)
> >
> Please check your Cursor version before using this tool. > Please check your Cursor version before using this tool.
<details open>
<summary><b>📦 Version History & Downloads</b></summary>
<div class="version-card" style="background: linear-gradient(135deg, #6e8efb, #a777e3); border-radius: 8px; padding: 15px; margin: 10px 0; color: white;">
### 🌟 Latest Versions
[View Full Version History]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file))
</div>
</details>
⚠️ **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
>
<details >
<summary><b>🔒 Disable Auto-Update Feature</b></summary>
> To prevent Cursor from automatically updating to unsupported new versions, you can choose to disable the auto-update feature.
#### Method 1: Using Built-in Script (Recommended)
When running the reset tool, the script will ask if you want to disable auto-updates:
```text
[Question] Do you want to disable Cursor auto-update feature?
0) No - Keep default settings (Press Enter)
1) Yes - Disable auto-update
```
Select `1` to automatically complete the disable operation.
#### Method 2: Manual Disable
**Windows:**
1. Close all Cursor processes
2. Delete directory: `%LOCALAPPDATA%\cursor-updater`
3. Create a file with the same name (without extension) in the same location
**macOS:**
```bash
# NOTE: As tested, this method only works for version 0.45.11 and below.
# Close Cursor
pkill -f "Cursor"
# 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:**
```bash
# Close Cursor
pkill -f "Cursor"
# Remove update directory and create blocking file
rm -rf ~/.config/cursor-updater
touch ~/.config/cursor-updater
```
> ⚠️ **Note:** After disabling auto-updates, you'll need to manually download and install new versions. It's recommended to update only after confirming the new version is compatible.
</details>
--- ---
### 📝 Description
> When you encounter any of these messages:
#### Issue 1: Trial Account Limit <p align="right"><a href="#issue1"><img src="https://img.shields.io/badge/Move%20to%20Solution-Blue?style=plastic" alt="Back To Top"></a></p>
```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.
```
#### Issue 2: API Key Limitation <p align="right"><a href="#issue2"><img src="https://img.shields.io/badge/Move%20to%20Solution-green?style=plastic" alt="Back To Top"></a></p>
```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
```
#### Issue 3: Trial Request Limit
> This indicates you've reached the usage limit during the VIP free trial period:
```text
You've reached your trial request limit.
```
#### Issue 4: Claude 3.7 High Load <p align="right"><a href="#issue4"><img src="https://img.shields.io/badge/Move%20to%20Solution-purple?style=plastic" alt="Back To Top"></a></p>
```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.
```
<br>
<p id="issue2"></p>
#### Solution : Uninstall Cursor Completely And Reinstall (API key Issue)
1. Download [Geek.exe Uninstaller[Free]](https://geekuninstaller.com/download)
2. Uninstall Cursor app completely
3. Re-Install Cursor app
4. Continue to Solution 1
<br>
<p id="issue1"></p>
> Temporary Solution:
#### Solution 1: Quick Reset (Recommended)
1. Close Cursor application
2. Run the machine code reset script (see installation instructions below)
3. Reopen Cursor to continue using
#### Solution 2: Account Switch
1. File -> Cursor Settings -> Sign Out
2. Close Cursor
3. Run the machine code reset script
4. Login with a new account
#### Solution 3: Network Optimization
If the above solutions don't work, try:
- Switch to low-latency nodes (Recommended regions: Japan, Singapore, US, Hong Kong)
- 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
<table>
<tr>
<td>
**Windows** ✅
- x64 (64-bit)
- x86 (32-bit)
</td>
<td>
**macOS** ✅
- Intel (x64)
- Apple Silicon (M1/M2)
</td>
<td>
**Linux** ✅
- x64 (64-bit)
- x86 (32-bit)
- ARM64
</td>
</tr>
</table>
### 🚀 One-Click Solution ### 🚀 One-Click Solution
<details open> <details open>
@ -279,8 +62,7 @@ If you see the "High Load" message for Claude 3.7 Sonnet, this indicates Cursor
**macOS** **macOS**
```bash ```bash
# Method two
curl -fsSL https://wget.la/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
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** **Linux**
@ -297,19 +79,11 @@ curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex 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
<div align="center">
<img src="img/run_success.png" alt="Run Success" width="600"/>
</div>
</details> </details>
<details open> <details open>
<summary><b>China Users (Recommended)</b></summary> <summary><b>China Users (Recommended)</b></summary>
@ -331,14 +105,12 @@ curl -fsSL https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-
irm https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex irm https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
``` ```
**Windows (Enhanced Version)**
</details>
```powershell
irm https://wget.la/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
</details>
<div align="center">
<img src="img/run_success.png" alt="Run Success" width="600"/>
</div>
<details open> <details open>
<summary><b>Windows Terminal Run and Configuration</b></summary> <summary><b>Windows Terminal Run and Configuration</b></summary>

142
README_CN.md

@ -8,7 +8,7 @@
[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md) [🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md)
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
<img src="/img/cursor.png" alt="Cursor Logo" width="120"/>
</div> </div>
@ -47,140 +47,13 @@
> ⚠️ **重要提示** > ⚠️ **重要提示**
> >
> 本工具当前支持版本: > 本工具当前支持版本:
> - ✅ Windows: 最新的 1.0.x 版本(已支持)
> - ✅ Mac/Linux: 最新的 1.0.x 版本(已支持,欢迎测试并反馈问题)
> - ✅ Windows: 最新的 2.x.x 版本(已支持)
> - ✅ Mac/Linux: 最新的 2.x.x 版本(已支持,欢迎测试并反馈问题)
> 使用前请确认您的 Cursor 版本。 > 使用前请确认您的 Cursor 版本。
<details open>
<summary><b>📦 版本历史与下载</b></summary>
<div class="version-card" style="background: linear-gradient(135deg, #6e8efb, #a777e3); border-radius: 8px; padding: 15px; margin: 10px 0; color: white;">
[查看完整版本历史]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file))
</div>
</details>
⚠️ **Cursor通用解决方案**
> 1. 关闭Cursor、退出账号、官网Setting删除账号(刷新节点IP:日本、新加坡、 美国、香港,低延迟为主不一定需要但是有条件就换,Windows用户建议刷新DNS缓存:`ipconfig /flushdns`)
> 前往Cursor官网删除当前账号
> 步骤:用户头像->Setting-左下角Advanced▼->Delete Account
>
> 2. 刷新机器码脚本,看下面脚本地址,国内可用
>
> 3. 重新注册账号、登录、打开Cursor,即可恢复正常使用。
>
> 4. 备用方案:如果步骤 [**3**] 后仍不可用,或者遇到注册账号失败、无法删除账号等问题,这通常意味着您的浏览器被目标网站识别或限制(风控)。此时,请尝试更换浏览器,例如:Edge、Google Chrome、Firefox。(或者,可以尝试使用能够修改或随机化浏览器指纹信息的浏览器)。
关注大佬公众号:煎饼果子卷AI
---
> ⚠️ **MAC地址修改警告**
>
> Mac用户请注意: 本脚本包含MAC地址修改功能,将会:
> - 修改您的网络接口MAC地址
> - 在修改前备份原始MAC地址
> - 此修改可能会暂时影响网络连接
> - 执行过程中可以选择跳过此步骤
--- ---
### 📝 问题描述
> 当您遇到以下任何消息时:
#### 问题 1: 试用账号限制 <p align="right"><a href="#solution1"><img src="https://img.shields.io/badge/跳转到解决方案-Blue?style=plastic" alt="跳转到顶部"></a></p>
```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密钥限制 <p align="right"><a href="#solution2"><img src="https://img.shields.io/badge/跳转到解决方案-green?style=plastic" alt="跳转到顶部"></a></p>
```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 高负载 (High Load) <p align="right"><a href="#solution4"><img src="https://img.shields.io/badge/跳转到解决方案-purple?style=plastic" alt="跳转到顶部"></a></p>
```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.
```
<br>
<p id="solution2"></p>
#### 解决方案:完全卸载Cursor并重新安装(API密钥问题)
1. 下载 [Geek.exe 卸载工具[免费]](https://geekuninstaller.com/download)
2. 完全卸载Cursor应用
3. 重新安装Cursor应用
4. 继续执行解决方案1
<br>
<p id="solution1"></p>
> 临时解决方案:
#### 解决方案 1: 快速重置(推荐)
1. 关闭Cursor应用
2. 运行机器码重置脚本(见下方安装说明)
3. 重新打开Cursor继续使用
#### 解决方案 2: 切换账号
1. 文件 -> Cursor设置 -> 退出登录
2. 关闭Cursor
3. 运行机器码重置脚本
4. 使用新账号登录
#### 解决方案 3: 网络优化
如果上述解决方案不起作用,请尝试:
- 切换到低延迟节点(推荐区域:日本、新加坡、美国、香港)
- 确保网络稳定性
- 清除浏览器缓存并重试
<p id="solution4"></p>
#### 解决方案 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调整资源分配策略,这些访问模式可能会发生变化。
### 🚀 系统支持 ### 🚀 系统支持
@ -237,17 +110,14 @@ curl -fsSL https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-
irm https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex irm https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
``` ```
**Windows (增强版)**
```powershell
irm https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
```
> 增强版Cursor机器码修改工具,支持双模式操作和试用重置功能
</details>
<div align="center"> <div align="center">
<img src="img/run_success.png" alt="运行成功" width="600"/> <img src="img/run_success.png" alt="运行成功" width="600"/>
</div> </div>
</details>
<details open> <details open>
<summary><b>Windows 管理员终端运行和手动安装</b></summary> <summary><b>Windows 管理员终端运行和手动安装</b></summary>

328
cmd/cursor-id-modifier/main.go

@ -1,328 +0,0 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
"os/exec"
"os/user"
"runtime"
"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"
)
// Global variables
var (
version = "dev"
setReadOnly = flag.Bool("r", false, "set storage.json to read-only mode")
showVersion = flag.Bool("v", false, "show version information")
log = logrus.New()
)
func main() {
// 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()
username := getCurrentUser()
log.Debug("Running as user:", username)
// Initialize components
display := ui.NewDisplay(nil)
configManager := initConfigManager(username)
generator := idgen.NewGenerator()
processManager := process.NewManager(nil, log)
// Check and handle privileges
if err := handlePrivileges(display); err != nil {
return
}
// Setup display
setupDisplay(display)
text := lang.GetText()
// Handle Cursor processes
if err := handleCursorProcesses(display, processManager); err != nil {
return
}
// Handle configuration
oldConfig := readExistingConfig(display, configManager, text)
newConfig := generateNewConfig(display, generator, oldConfig, text)
if err := saveConfiguration(display, configManager, newConfig); err != nil {
return
}
// Show completion messages
showCompletionMessages(display)
if os.Getenv("AUTOMATED_MODE") != "1" {
waitExit()
}
}
func handleFlags() {
flag.Parse()
if *showVersion {
fmt.Printf("Cursor ID Modifier v%s\n", version)
os.Exit(0)
}
}
func setupLogger() {
log.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
DisableLevelTruncation: true,
PadLevelText: true,
})
log.SetLevel(logrus.InfoLevel)
}
func getCurrentUser() string {
if username := os.Getenv("SUDO_USER"); username != "" {
return username
}
user, err := user.Current()
if err != nil {
log.Fatal(err)
}
return user.Username
}
func initConfigManager(username string) *config.Manager {
configManager, err := config.NewManager(username)
if err != nil {
log.Fatal(err)
}
return configManager
}
func handlePrivileges(display *ui.Display) error {
isAdmin, err := checkAdminPrivileges()
if err != nil {
log.Error(err)
waitExit()
return err
}
if !isAdmin {
if runtime.GOOS == "windows" {
return handleWindowsPrivileges(display)
}
display.ShowPrivilegeError(
lang.GetText().PrivilegeError,
lang.GetText().RunWithSudo,
lang.GetText().SudoExample,
)
waitExit()
return fmt.Errorf("insufficient privileges")
}
return nil
}
func handleWindowsPrivileges(display *ui.Display) error {
message := "\nRequesting administrator privileges..."
if lang.GetCurrentLanguage() == lang.CN {
message = "\n请求管理员权限..."
}
fmt.Println(message)
if err := selfElevate(); err != nil {
log.Error(err)
display.ShowPrivilegeError(
lang.GetText().PrivilegeError,
lang.GetText().RunAsAdmin,
lang.GetText().RunWithSudo,
lang.GetText().SudoExample,
)
waitExit()
return err
}
return nil
}
func setupDisplay(display *ui.Display) {
if err := display.ClearScreen(); err != nil {
log.Warn("Failed to clear screen:", err)
}
display.ShowLogo()
fmt.Println()
}
func handleCursorProcesses(display *ui.Display, processManager *process.Manager) error {
if os.Getenv("AUTOMATED_MODE") == "1" {
log.Debug("Running in automated mode, skipping Cursor process closing")
return nil
}
display.ShowProgress("Closing Cursor...")
log.Debug("Attempting to close Cursor processes")
if err := processManager.KillCursorProcesses(); err != nil {
log.Error("Failed to close Cursor:", err)
display.StopProgress()
display.ShowError("Failed to close Cursor. Please close it manually and try again.")
waitExit()
return err
}
if processManager.IsCursorRunning() {
log.Error("Cursor processes still detected after closing")
display.StopProgress()
display.ShowError("Failed to close Cursor completely. Please close it manually and try again.")
waitExit()
return fmt.Errorf("cursor still running")
}
log.Debug("Successfully closed all Cursor processes")
display.StopProgress()
fmt.Println()
return nil
}
func readExistingConfig(display *ui.Display, configManager *config.Manager, text lang.TextResource) *config.StorageConfig {
fmt.Println()
display.ShowProgress(text.ReadingConfig)
oldConfig, err := configManager.ReadConfig()
if err != nil {
log.Warn("Failed to read existing config:", err)
oldConfig = nil
}
display.StopProgress()
fmt.Println()
return oldConfig
}
func generateNewConfig(display *ui.Display, generator *idgen.Generator, oldConfig *config.StorageConfig, text lang.TextResource) *config.StorageConfig {
display.ShowProgress(text.GeneratingIds)
newConfig := &config.StorageConfig{}
if machineID, err := generator.GenerateMachineID(); err != nil {
log.Fatal("Failed to generate machine ID:", err)
} else {
newConfig.TelemetryMachineId = machineID
}
if macMachineID, err := generator.GenerateMacMachineID(); err != nil {
log.Fatal("Failed to generate MAC machine ID:", err)
} else {
newConfig.TelemetryMacMachineId = macMachineID
}
if deviceID, err := generator.GenerateDeviceID(); err != nil {
log.Fatal("Failed to generate device ID:", err)
} else {
newConfig.TelemetryDevDeviceId = deviceID
}
if oldConfig != nil && oldConfig.TelemetrySqmId != "" {
newConfig.TelemetrySqmId = oldConfig.TelemetrySqmId
} else if sqmID, err := generator.GenerateSQMID(); err != nil {
log.Fatal("Failed to generate SQM ID:", err)
} else {
newConfig.TelemetrySqmId = sqmID
}
display.StopProgress()
fmt.Println()
return newConfig
}
func saveConfiguration(display *ui.Display, configManager *config.Manager, newConfig *config.StorageConfig) error {
display.ShowProgress("Saving configuration...")
if err := configManager.SaveConfig(newConfig, *setReadOnly); err != nil {
log.Error(err)
waitExit()
return err
}
display.StopProgress()
fmt.Println()
return nil
}
func showCompletionMessages(display *ui.Display) {
display.ShowSuccess(lang.GetText().SuccessMessage, lang.GetText().RestartMessage)
fmt.Println()
message := "Operation completed!"
if lang.GetCurrentLanguage() == lang.CN {
message = "操作完成!"
}
display.ShowInfo(message)
}
func waitExit() {
fmt.Print(lang.GetText().PressEnterToExit)
os.Stdout.Sync()
bufio.NewReader(os.Stdin).ReadString('\n')
}
func checkAdminPrivileges() (bool, error) {
switch runtime.GOOS {
case "windows":
cmd := exec.Command("net", "session")
return cmd.Run() == nil, nil
case "darwin", "linux":
currentUser, err := user.Current()
if err != nil {
return false, fmt.Errorf("failed to get current user: %w", err)
}
return currentUser.Uid == "0", nil
default:
return false, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}
func selfElevate() error {
os.Setenv("AUTOMATED_MODE", "1")
switch runtime.GOOS {
case "windows":
verb := "runas"
exe, _ := os.Executable()
cwd, _ := os.Getwd()
args := strings.Join(os.Args[1:], " ")
cmd := exec.Command("cmd", "/C", "start", verb, exe, args)
cmd.Dir = cwd
return cmd.Run()
case "darwin", "linux":
exe, err := os.Executable()
if err != nil {
return err
}
cmd := exec.Command("sudo", append([]string{exe}, os.Args[1:]...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}

15
go.mod

@ -1,15 +0,0 @@
module github.com/yuaotian/go-cursor-help
go 1.21
require (
github.com/fatih/color v1.15.0
github.com/sirupsen/logrus v1.9.3
)
require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/stretchr/testify v1.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
)

26
go.sum

@ -1,26 +0,0 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
img/cursor.png

After

Width: 1024  |  Height: 1024  |  Size: 31 KiB

153
internal/config/config.go

@ -1,153 +0,0 @@
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
"time"
)
// StorageConfig represents the storage configuration
type StorageConfig struct {
TelemetryMacMachineId string `json:"telemetry.macMachineId"`
TelemetryMachineId string `json:"telemetry.machineId"`
TelemetryDevDeviceId string `json:"telemetry.devDeviceId"`
TelemetrySqmId string `json:"telemetry.sqmId"`
LastModified string `json:"lastModified"`
Version string `json:"version"`
}
// Manager handles configuration operations
type Manager struct {
configPath string
mu sync.RWMutex
}
// NewManager creates a new configuration manager
func NewManager(username string) (*Manager, error) {
configPath, err := getConfigPath(username)
if err != nil {
return nil, fmt.Errorf("failed to get config path: %w", err)
}
return &Manager{configPath: configPath}, nil
}
// ReadConfig reads the existing configuration
func (m *Manager) ReadConfig() (*StorageConfig, error) {
m.mu.RLock()
defer m.mu.RUnlock()
data, err := os.ReadFile(m.configPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("failed to read config file: %w", err)
}
var config StorageConfig
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
return &config, nil
}
// SaveConfig saves the configuration
func (m *Manager) SaveConfig(config *StorageConfig, readOnly bool) error {
m.mu.Lock()
defer m.mu.Unlock()
// Ensure parent directories exist
if err := os.MkdirAll(filepath.Dir(m.configPath), 0755); err != nil {
return fmt.Errorf("failed to create config directory: %w", err)
}
// Prepare updated configuration
updatedConfig := m.prepareUpdatedConfig(config)
// Write configuration
if err := m.writeConfigFile(updatedConfig, readOnly); err != nil {
return err
}
return nil
}
// prepareUpdatedConfig merges existing config with updates
func (m *Manager) prepareUpdatedConfig(config *StorageConfig) map[string]interface{} {
// Read existing config
originalFile := make(map[string]interface{})
if data, err := os.ReadFile(m.configPath); err == nil {
json.Unmarshal(data, &originalFile)
}
// Update fields
originalFile["telemetry.sqmId"] = config.TelemetrySqmId
originalFile["telemetry.macMachineId"] = config.TelemetryMacMachineId
originalFile["telemetry.machineId"] = config.TelemetryMachineId
originalFile["telemetry.devDeviceId"] = config.TelemetryDevDeviceId
originalFile["lastModified"] = time.Now().UTC().Format(time.RFC3339)
// originalFile["version"] = "1.0.1"
return originalFile
}
// writeConfigFile handles the atomic write of the config file
func (m *Manager) writeConfigFile(config map[string]interface{}, readOnly bool) error {
// Marshal with indentation
content, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
// Write to temporary file
tmpPath := m.configPath + ".tmp"
if err := os.WriteFile(tmpPath, content, 0666); err != nil {
return fmt.Errorf("failed to write temporary file: %w", err)
}
// Set final permissions
fileMode := os.FileMode(0666)
if readOnly {
fileMode = 0444
}
if err := os.Chmod(tmpPath, fileMode); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("failed to set temporary file permissions: %w", err)
}
// Atomic rename
if err := os.Rename(tmpPath, m.configPath); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("failed to rename file: %w", err)
}
// Sync directory
if dir, err := os.Open(filepath.Dir(m.configPath)); err == nil {
defer dir.Close()
dir.Sync()
}
return nil
}
// getConfigPath returns the path to the configuration file
func getConfigPath(username string) (string, error) {
var configDir string
switch runtime.GOOS {
case "windows":
configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage")
case "darwin":
configDir = filepath.Join("/Users", username, "Library", "Application Support", "Cursor", "User", "globalStorage")
case "linux":
configDir = filepath.Join("/home", username, ".config", "Cursor", "User", "globalStorage")
default:
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
return filepath.Join(configDir, "storage.json"), nil
}

187
internal/lang/lang.go

@ -1,187 +0,0 @@
package lang
import (
"os"
"os/exec"
"strings"
"sync"
)
// Language represents a supported language code
type Language string
const (
// CN represents Chinese language
CN Language = "cn"
// EN represents English language
EN Language = "en"
)
// TextResource contains all translatable text resources
type TextResource struct {
// Success messages
SuccessMessage string
RestartMessage string
// Progress messages
ReadingConfig string
GeneratingIds string
CheckingProcesses string
ClosingProcesses string
ProcessesClosed string
PleaseWait string
// Error messages
ErrorPrefix string
PrivilegeError string
// Instructions
RunAsAdmin string
RunWithSudo string
SudoExample string
PressEnterToExit string
SetReadOnlyMessage string
// Info messages
ConfigLocation string
}
var (
currentLanguage Language
currentLanguageOnce sync.Once
languageMutex sync.RWMutex
)
// GetCurrentLanguage returns the current language, detecting it if not already set
func GetCurrentLanguage() Language {
currentLanguageOnce.Do(func() {
currentLanguage = detectLanguage()
})
languageMutex.RLock()
defer languageMutex.RUnlock()
return currentLanguage
}
// SetLanguage sets the current language
func SetLanguage(lang Language) {
languageMutex.Lock()
defer languageMutex.Unlock()
currentLanguage = lang
}
// GetText returns the TextResource for the current language
func GetText() TextResource {
return texts[GetCurrentLanguage()]
}
// detectLanguage detects the system language
func detectLanguage() Language {
// Check environment variables first
if isChineseEnvVar() {
return CN
}
// Then check OS-specific locale
if isWindows() {
if isWindowsChineseLocale() {
return CN
}
} else if isUnixChineseLocale() {
return CN
}
return EN
}
func isChineseEnvVar() bool {
for _, envVar := range []string{"LANG", "LANGUAGE", "LC_ALL"} {
if lang := os.Getenv(envVar); lang != "" && strings.Contains(strings.ToLower(lang), "zh") {
return true
}
}
return false
}
func isWindows() bool {
return os.Getenv("OS") == "Windows_NT"
}
func isWindowsChineseLocale() bool {
// Check Windows UI culture
cmd := exec.Command("powershell", "-Command",
"[System.Globalization.CultureInfo]::CurrentUICulture.Name")
output, err := cmd.Output()
if err == nil && strings.HasPrefix(strings.ToLower(strings.TrimSpace(string(output))), "zh") {
return true
}
// Check Windows locale
cmd = exec.Command("wmic", "os", "get", "locale")
output, err = cmd.Output()
return err == nil && strings.Contains(string(output), "2052")
}
func isUnixChineseLocale() bool {
cmd := exec.Command("locale")
output, err := cmd.Output()
return err == nil && strings.Contains(strings.ToLower(string(output)), "zh_cn")
}
// texts contains all translations
var texts = map[Language]TextResource{
CN: {
// Success messages
SuccessMessage: "[√] 配置文件已成功更新!",
RestartMessage: "[!] 请手动重启 Cursor 以使更新生效",
// Progress messages
ReadingConfig: "正在读取配置文件...",
GeneratingIds: "正在生成新的标识符...",
CheckingProcesses: "正在检查运行中的 Cursor 实例...",
ClosingProcesses: "正在关闭 Cursor 实例...",
ProcessesClosed: "所有 Cursor 实例已关闭",
PleaseWait: "请稍候...",
// Error messages
ErrorPrefix: "程序发生严重错误: %v",
PrivilegeError: "\n[!] 错误:需要管理员权限",
// Instructions
RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」",
RunWithSudo: "请使用 sudo 命令运行此程序",
SudoExample: "示例: sudo %s",
PressEnterToExit: "\n按回车键退出程序...",
SetReadOnlyMessage: "设置 storage.json 为只读模式, 这将导致 workspace 记录信息丢失等问题",
// Info messages
ConfigLocation: "配置文件位置:",
},
EN: {
// Success messages
SuccessMessage: "[√] Configuration file updated successfully!",
RestartMessage: "[!] Please restart Cursor manually for changes to take effect",
// Progress messages
ReadingConfig: "Reading configuration file...",
GeneratingIds: "Generating new identifiers...",
CheckingProcesses: "Checking for running Cursor instances...",
ClosingProcesses: "Closing Cursor instances...",
ProcessesClosed: "All Cursor instances have been closed",
PleaseWait: "Please wait...",
// Error messages
ErrorPrefix: "Program encountered a serious error: %v",
PrivilegeError: "\n[!] Error: Administrator privileges required",
// Instructions
RunAsAdmin: "Please right-click and select 'Run as Administrator'",
RunWithSudo: "Please run this program with sudo",
SudoExample: "Example: sudo %s",
PressEnterToExit: "\nPress Enter to exit...",
SetReadOnlyMessage: "Set storage.json to read-only mode, which will cause issues such as lost workspace records",
// Info messages
ConfigLocation: "Config file location:",
},
}

216
internal/process/manager.go

@ -1,216 +0,0 @@
package process
import (
"fmt"
"os/exec"
"runtime"
"strings"
"time"
"github.com/sirupsen/logrus"
)
// Config holds process manager configuration
type Config struct {
MaxAttempts int // Maximum number of attempts to kill processes
RetryDelay time.Duration // Delay between retry attempts
ProcessPatterns []string // Process names to look for
}
// DefaultConfig returns the default configuration
func DefaultConfig() *Config {
return &Config{
MaxAttempts: 3,
RetryDelay: 2 * time.Second,
ProcessPatterns: []string{
"Cursor.exe", // Windows executable
"Cursor ", // Linux/macOS executable with space
"cursor ", // Linux/macOS executable lowercase with space
"cursor", // Linux/macOS executable lowercase
"Cursor", // Linux/macOS executable
"*cursor*", // Any process containing cursor
"*Cursor*", // Any process containing Cursor
},
}
}
// Manager handles process-related operations
type Manager struct {
config *Config
log *logrus.Logger
}
// NewManager creates a new process manager with optional config and logger
func NewManager(config *Config, log *logrus.Logger) *Manager {
if config == nil {
config = DefaultConfig()
}
if log == nil {
log = logrus.New()
}
return &Manager{
config: config,
log: log,
}
}
// IsCursorRunning checks if any Cursor process is currently running
func (m *Manager) IsCursorRunning() bool {
processes, err := m.getCursorProcesses()
if err != nil {
m.log.Warn("Failed to get Cursor processes:", err)
return false
}
return len(processes) > 0
}
// KillCursorProcesses attempts to kill all running Cursor processes
func (m *Manager) KillCursorProcesses() error {
for attempt := 1; attempt <= m.config.MaxAttempts; attempt++ {
processes, err := m.getCursorProcesses()
if err != nil {
return fmt.Errorf("failed to get processes: %w", err)
}
if len(processes) == 0 {
return nil
}
// Try graceful shutdown first on Windows
if runtime.GOOS == "windows" {
for _, pid := range processes {
exec.Command("taskkill", "/PID", pid).Run()
time.Sleep(500 * time.Millisecond)
}
}
// Force kill remaining processes
remainingProcesses, _ := m.getCursorProcesses()
for _, pid := range remainingProcesses {
m.killProcess(pid)
}
time.Sleep(m.config.RetryDelay)
if processes, _ := m.getCursorProcesses(); len(processes) == 0 {
return nil
}
}
return nil
}
// getCursorProcesses returns PIDs of running Cursor processes
func (m *Manager) getCursorProcesses() ([]string, error) {
cmd := m.getProcessListCommand()
if cmd == nil {
return nil, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to execute command: %w", err)
}
return m.parseProcessList(string(output)), nil
}
// getProcessListCommand returns the appropriate command to list processes based on OS
func (m *Manager) getProcessListCommand() *exec.Cmd {
switch runtime.GOOS {
case "windows":
return exec.Command("tasklist", "/FO", "CSV", "/NH")
case "darwin":
return exec.Command("ps", "-ax")
case "linux":
return exec.Command("ps", "-A")
default:
return nil
}
}
// parseProcessList extracts Cursor process PIDs from process list output
func (m *Manager) parseProcessList(output string) []string {
var processes []string
for _, line := range strings.Split(output, "\n") {
lowerLine := strings.ToLower(line)
if m.isOwnProcess(lowerLine) {
continue
}
if pid := m.findCursorProcess(line, lowerLine); pid != "" {
processes = append(processes, pid)
}
}
return processes
}
// isOwnProcess checks if the process belongs to this application
func (m *Manager) isOwnProcess(line string) bool {
return strings.Contains(line, "cursor-id-modifier") ||
strings.Contains(line, "cursor-helper")
}
// findCursorProcess checks if a process line matches Cursor patterns and returns its PID
func (m *Manager) findCursorProcess(line, lowerLine string) string {
for _, pattern := range m.config.ProcessPatterns {
if m.matchPattern(lowerLine, strings.ToLower(pattern)) {
return m.extractPID(line)
}
}
return ""
}
// matchPattern checks if a line matches a pattern, supporting wildcards
func (m *Manager) matchPattern(line, pattern string) bool {
switch {
case strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*"):
search := pattern[1 : len(pattern)-1]
return strings.Contains(line, search)
case strings.HasPrefix(pattern, "*"):
return strings.HasSuffix(line, pattern[1:])
case strings.HasSuffix(pattern, "*"):
return strings.HasPrefix(line, pattern[:len(pattern)-1])
default:
return line == pattern
}
}
// extractPID extracts process ID from a process list line based on OS format
func (m *Manager) extractPID(line string) string {
switch runtime.GOOS {
case "windows":
parts := strings.Split(line, ",")
if len(parts) >= 2 {
return strings.Trim(parts[1], "\"")
}
case "darwin", "linux":
parts := strings.Fields(line)
if len(parts) >= 1 {
return parts[0]
}
}
return ""
}
// killProcess forcefully terminates a process by PID
func (m *Manager) killProcess(pid string) error {
cmd := m.getKillCommand(pid)
if cmd == nil {
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
return cmd.Run()
}
// getKillCommand returns the appropriate command to kill a process based on OS
func (m *Manager) getKillCommand(pid string) *exec.Cmd {
switch runtime.GOOS {
case "windows":
return exec.Command("taskkill", "/F", "/PID", pid)
case "darwin", "linux":
return exec.Command("kill", "-9", pid)
default:
return nil
}
}

94
internal/ui/display.go

@ -1,94 +0,0 @@
package ui
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"github.com/fatih/color"
)
// Display handles UI operations for terminal output
type Display struct {
spinner *Spinner
}
// NewDisplay creates a new display instance with an optional spinner
func NewDisplay(spinner *Spinner) *Display {
if spinner == nil {
spinner = NewSpinner(nil)
}
return &Display{spinner: spinner}
}
// Terminal Operations
// ClearScreen clears the terminal screen based on OS
func (d *Display) ClearScreen() error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
return cmd.Run()
}
// Progress Indicator
// ShowProgress displays a progress message with a spinner
func (d *Display) ShowProgress(message string) {
d.spinner.SetMessage(message)
d.spinner.Start()
}
// StopProgress stops the progress spinner
func (d *Display) StopProgress() {
d.spinner.Stop()
}
// Message Display
// ShowSuccess displays success messages in green
func (d *Display) ShowSuccess(messages ...string) {
green := color.New(color.FgGreen)
for _, msg := range messages {
green.Println(msg)
}
}
// ShowInfo displays an info message in cyan
func (d *Display) ShowInfo(message string) {
cyan := color.New(color.FgCyan)
cyan.Println(message)
}
// ShowError displays an error message in red
func (d *Display) ShowError(message string) {
red := color.New(color.FgRed)
red.Println(message)
}
// ShowPrivilegeError displays privilege error messages with instructions
func (d *Display) ShowPrivilegeError(messages ...string) {
red := color.New(color.FgRed, color.Bold)
yellow := color.New(color.FgYellow)
// Main error message
red.Println(messages[0])
fmt.Println()
// Additional instructions
for _, msg := range messages[1:] {
if strings.Contains(msg, "%s") {
exe, _ := os.Executable()
yellow.Printf(msg+"\n", exe)
} else {
yellow.Println(msg)
}
}
}

20
internal/ui/logo.go

@ -1,20 +0,0 @@
package ui
import (
"github.com/fatih/color"
)
const cyberpunkLogo = `
`
// ShowLogo displays the application logo
func (d *Display) ShowLogo() {
cyan := color.New(color.FgCyan, color.Bold)
cyan.Println(cyberpunkLogo)
}

122
internal/ui/spinner.go

@ -1,122 +0,0 @@
package ui
import (
"fmt"
"sync"
"time"
"github.com/fatih/color"
)
// SpinnerConfig defines spinner configuration
type SpinnerConfig struct {
Frames []string // Animation frames for the spinner
Delay time.Duration // Delay between frame updates
}
// DefaultSpinnerConfig returns the default spinner configuration
func DefaultSpinnerConfig() *SpinnerConfig {
return &SpinnerConfig{
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
Delay: 100 * time.Millisecond,
}
}
// Spinner represents a progress spinner
type Spinner struct {
config *SpinnerConfig
message string
current int
active bool
stopCh chan struct{}
mu sync.RWMutex
}
// NewSpinner creates a new spinner with the given configuration
func NewSpinner(config *SpinnerConfig) *Spinner {
if config == nil {
config = DefaultSpinnerConfig()
}
return &Spinner{
config: config,
stopCh: make(chan struct{}),
}
}
// State management
// SetMessage sets the spinner message
func (s *Spinner) SetMessage(message string) {
s.mu.Lock()
defer s.mu.Unlock()
s.message = message
}
// IsActive returns whether the spinner is currently active
func (s *Spinner) IsActive() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.active
}
// Control methods
// Start begins the spinner animation
func (s *Spinner) Start() {
s.mu.Lock()
if s.active {
s.mu.Unlock()
return
}
s.active = true
s.mu.Unlock()
go s.run()
}
// Stop halts the spinner animation
func (s *Spinner) Stop() {
s.mu.Lock()
defer s.mu.Unlock()
if !s.active {
return
}
s.active = false
close(s.stopCh)
s.stopCh = make(chan struct{})
fmt.Print("\r") // Clear the spinner line
}
// Internal methods
func (s *Spinner) run() {
ticker := time.NewTicker(s.config.Delay)
defer ticker.Stop()
cyan := color.New(color.FgCyan, color.Bold)
message := s.message
// Print initial state
fmt.Printf("\r %s %s", cyan.Sprint(s.config.Frames[0]), message)
for {
select {
case <-s.stopCh:
return
case <-ticker.C:
s.mu.RLock()
if !s.active {
s.mu.RUnlock()
return
}
frame := s.config.Frames[s.current%len(s.config.Frames)]
s.current++
s.mu.RUnlock()
fmt.Printf("\r %s", cyan.Sprint(frame))
fmt.Printf("\033[%dG%s", 4, message) // Move cursor and print message
}
}
}

116
pkg/idgen/generator.go

@ -1,116 +0,0 @@
package idgen
import (
"crypto/rand"
"encoding/hex"
"fmt"
"sync"
)
// Generator handles secure ID generation for machines and devices
type Generator struct {
bufferPool sync.Pool
}
// NewGenerator creates a new ID generator
func NewGenerator() *Generator {
return &Generator{
bufferPool: sync.Pool{
New: func() interface{} {
return make([]byte, 64)
},
},
}
}
// Constants for ID generation
const (
machineIDPrefix = "auth0|user_"
uuidFormat = "%s-%s-%s-%s-%s"
)
// generateRandomHex generates a random hex string of specified length
func (g *Generator) generateRandomHex(length int) (string, error) {
buffer := g.bufferPool.Get().([]byte)
defer g.bufferPool.Put(buffer)
if _, err := rand.Read(buffer[:length]); err != nil {
return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
return hex.EncodeToString(buffer[:length]), nil
}
// GenerateMachineID generates a new machine ID with auth0|user_ prefix
func (g *Generator) GenerateMachineID() (string, error) {
randomPart, err := g.generateRandomHex(32) // 生成64字符的十六进制
if err != nil {
return "", err
}
return fmt.Sprintf("%x%s", []byte(machineIDPrefix), randomPart), nil
}
// GenerateMacMachineID generates a new 64-byte MAC machine ID
func (g *Generator) GenerateMacMachineID() (string, error) {
return g.generateRandomHex(32) // 生成64字符的十六进制
}
// GenerateDeviceID generates a new device ID in UUID format
func (g *Generator) GenerateDeviceID() (string, error) {
id, err := g.generateRandomHex(16)
if err != nil {
return "", err
}
return fmt.Sprintf(uuidFormat,
id[0:8], id[8:12], id[12:16], id[16:20], id[20:32]), nil
}
// GenerateSQMID generates a new SQM ID in UUID format (with braces)
func (g *Generator) GenerateSQMID() (string, error) {
id, err := g.GenerateDeviceID()
if err != nil {
return "", err
}
return fmt.Sprintf("{%s}", id), nil
}
// ValidateID validates the format of various ID types
func (g *Generator) ValidateID(id string, idType string) bool {
switch idType {
case "machineID", "macMachineID":
return len(id) == 64 && isHexString(id)
case "deviceID":
return isValidUUID(id)
case "sqmID":
if len(id) < 2 || id[0] != '{' || id[len(id)-1] != '}' {
return false
}
return isValidUUID(id[1 : len(id)-1])
default:
return false
}
}
// Helper functions
func isHexString(s string) bool {
_, err := hex.DecodeString(s)
return err == nil
}
func isValidUUID(uuid string) bool {
if len(uuid) != 36 {
return false
}
for i, r := range uuid {
if i == 8 || i == 13 || i == 18 || i == 23 {
if r != '-' {
return false
}
continue
}
if !((r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')) {
return false
}
}
return true
}

375
process_cursor_links.py

@ -1,375 +0,0 @@
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
<details>
<summary style="font-size:1.2em">📦 Windows x64 </summary>
| | |
|------|----------|
"""
# Windows x64
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['windows']['x64']}) |\n"
md += """
</details>
## ARM64
<details>
<summary style="font-size:1.2em">📱 Windows ARM64 </summary>
| | |
|------|----------|
"""
# Windows ARM64
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['windows']['arm64']}) |\n"
md += """
</details>
# 🍎 macOS
## Universal
<details>
<summary style="font-size:1.2em">🎯 macOS Universal </summary>
| | |
|------|----------|
"""
# macOS Universal
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['mac']['universal']}) |\n"
md += """
</details>
## ARM64
<details>
<summary style="font-size:1.2em">💪 macOS ARM64 </summary>
| | |
|------|----------|
"""
# macOS ARM64
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['mac']['arm64']}) |\n"
md += """
</details>
## Intel
<details>
<summary style="font-size:1.2em">💻 macOS Intel </summary>
| | |
|------|----------|
"""
# macOS Intel
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['mac']['x64']}) |\n"
md += """
</details>
# 🐧 Linux
## x64
<details>
<summary style="font-size:1.2em">🎮 Linux x64 AppImage</summary>
| | |
|------|----------|
"""
# Linux x64
for version in versions:
links = version.get_download_links()
md += f"| {version.version} | [下载]({links['linux']['x64']}) |\n"
md += """
</details>
<style>
details {
margin: 1em 0;
padding: 0.5em 1em;
background: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
summary {
cursor: pointer;
font-weight: bold;
margin: -0.5em -1em;
padding: 0.5em 1em;
}
summary:hover {
background: #f1f3f5;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 1em;
}
th, td {
padding: 0.5em;
text-align: left;
border-bottom: 1px solid #dee2e6;
}
tr:hover {
background: #f1f3f5;
}
a {
color: #0366d6;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
"""
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()

74
scripts/build_all.bat

@ -1,74 +0,0 @@
@echo off
setlocal EnableDelayedExpansion
:: Build optimization flags
set "OPTIMIZATION_FLAGS=-trimpath -ldflags=\"-s -w\""
set "BUILD_JOBS=4"
:: Messages / 消息
set "EN_MESSAGES[0]=Starting build process for version"
set "EN_MESSAGES[1]=Using optimization flags:"
set "EN_MESSAGES[2]=Cleaning old builds..."
set "EN_MESSAGES[3]=Cleanup completed"
set "EN_MESSAGES[4]=Starting builds for all platforms..."
set "EN_MESSAGES[5]=Building for"
set "EN_MESSAGES[6]=Build successful:"
set "EN_MESSAGES[7]=All builds completed!"
:: Colors
set "GREEN=[32m"
set "RED=[31m"
set "RESET=[0m"
:: Cleanup function
:cleanup
if exist "..\bin" (
rd /s /q "..\bin"
echo %GREEN%!EN_MESSAGES[3]!%RESET%
)
mkdir "..\bin" 2>nul
:: Build function with optimizations
:build
set "os=%~1"
set "arch=%~2"
set "ext="
if "%os%"=="windows" set "ext=.exe"
echo %GREEN%!EN_MESSAGES[5]! %os%/%arch%%RESET%
set "CGO_ENABLED=0"
set "GOOS=%os%"
set "GOARCH=%arch%"
start /b cmd /c "go build -trimpath -ldflags=\"-s -w\" -o ..\bin\%os%\%arch%\cursor-id-modifier%ext% -a -installsuffix cgo -mod=readonly ..\cmd\cursor-id-modifier"
exit /b 0
:: Main execution
echo %GREEN%!EN_MESSAGES[0]!%RESET%
echo %GREEN%!EN_MESSAGES[1]! %OPTIMIZATION_FLAGS%%RESET%
call :cleanup
echo %GREEN%!EN_MESSAGES[4]!%RESET%
:: Start builds in parallel
set "pending=0"
for %%o in (windows linux darwin) do (
for %%a in (amd64 386) do (
call :build %%o %%a
set /a "pending+=1"
if !pending! geq %BUILD_JOBS% (
timeout /t 1 /nobreak >nul
set "pending=0"
)
)
)
:: Wait for all builds to complete
:wait_builds
timeout /t 2 /nobreak >nul
tasklist /fi "IMAGENAME eq go.exe" 2>nul | find "go.exe" >nul
if not errorlevel 1 goto wait_builds
echo %GREEN%!EN_MESSAGES[7]!%RESET%

143
scripts/build_all.sh

@ -1,143 +0,0 @@
#!/bin/bash
# 设置颜色代码 / Set color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color / 无颜色
# Build optimization flags
OPTIMIZATION_FLAGS="-trimpath -ldflags=\"-s -w\""
PARALLEL_JOBS=$(nproc || echo "4") # Get number of CPU cores or default to 4
# Messages / 消息
EN_MESSAGES=(
"Starting build process for version"
"Cleaning old builds..."
"Creating bin directory..."
"Failed to create bin directory"
"Building for"
"Successfully built:"
"Failed to build for"
"Build Summary:"
"Successful builds:"
"Failed builds:"
"Generated files:"
)
CN_MESSAGES=(
"开始构建版本"
"正在清理旧的构建文件..."
"正在创建bin目录..."
"创建bin目录失败"
"正在构建"
"构建成功:"
"构建失败:"
"构建摘要:"
"成功构建数:"
"失败构建数:"
"生成的文件:"
"构建过程被中断"
"错误:"
)
# 版本信息 / Version info
VERSION="1.0.0"
# Detect system language / 检测系统语言
detect_language() {
if [[ $(locale | grep "LANG=zh_CN") ]]; then
echo "cn"
else
echo "en"
fi
}
# Get message based on language / 根据语言获取消息
get_message() {
local index=$1
local lang=$(detect_language)
if [[ "$lang" == "cn" ]]; then
echo "${CN_MESSAGES[$index]}"
else
echo "${EN_MESSAGES[$index]}"
fi
}
# 错误处理函数 / Error handling function
handle_error() {
echo -e "${RED}$(get_message 12) $1${NC}"
exit 1
}
# 清理函数 / Cleanup function
cleanup() {
if [ -d "../bin" ]; then
rm -rf ../bin
echo -e "${GREEN}$(get_message 1)${NC}"
fi
}
# Build function with optimizations
build() {
local os=$1
local arch=$2
local ext=""
[ "$os" = "windows" ] && ext=".exe"
echo -e "${GREEN}$(get_message 4) $os/$arch${NC}"
GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build \
-trimpath \
-ldflags="-s -w" \
-o "../bin/$os/$arch/cursor-id-modifier$ext" \
-a -installsuffix cgo \
-mod=readonly \
../cmd/cursor-id-modifier &
}
# Parallel build execution
build_all() {
local builds=0
local max_parallel=$PARALLEL_JOBS
# Define build targets
declare -A targets=(
["linux/amd64"]=1
["linux/386"]=1
["linux/arm64"]=1
["windows/amd64"]=1
["windows/386"]=1
["darwin/amd64"]=1
["darwin/arm64"]=1
)
for target in "${!targets[@]}"; do
IFS='/' read -r os arch <<< "$target"
build "$os" "$arch"
((builds++))
if ((builds >= max_parallel)); then
wait
builds=0
fi
done
# Wait for remaining builds
wait
}
# Main execution
main() {
cleanup
mkdir -p ../bin || { echo -e "${RED}$(get_message 3)${NC}"; exit 1; }
build_all
echo -e "${GREEN}Build completed successfully${NC}"
}
# 捕获错误信号 / Catch error signals
trap 'echo -e "\n${RED}$(get_message 11)${NC}"; exit 1' INT TERM
# 执行主函数 / Execute main function
main

318
scripts/cursor_id_modifier.pot

@ -1,318 +0,0 @@
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 ""

1298
scripts/cursor_id_modifier.py
File diff suppressed because it is too large
View File

9
scripts/git-actions.sh

@ -1,9 +0,0 @@
#!/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

193
scripts/install.ps1

@ -1,193 +0,0 @@
# Check for admin rights and handle elevation
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (-NOT $isAdmin) {
# Detect PowerShell version and path
$pwshPath = if (Get-Command "pwsh" -ErrorAction SilentlyContinue) {
(Get-Command "pwsh").Source # PowerShell 7+
} elseif (Test-Path "$env:ProgramFiles\PowerShell\7\pwsh.exe") {
"$env:ProgramFiles\PowerShell\7\pwsh.exe"
} else {
"powershell.exe" # Windows PowerShell
}
try {
Write-Host "`nRequesting administrator privileges..." -ForegroundColor Cyan
$scriptPath = $MyInvocation.MyCommand.Path
$argList = "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
Start-Process -FilePath $pwshPath -Verb RunAs -ArgumentList $argList -Wait
exit
}
catch {
Write-Host "`nError: Administrator privileges required" -ForegroundColor Red
Write-Host "Please run this script from an Administrator PowerShell window" -ForegroundColor Yellow
Write-Host "`nTo do this:" -ForegroundColor Cyan
Write-Host "1. Press Win + X" -ForegroundColor White
Write-Host "2. Click 'Windows Terminal (Admin)' or 'PowerShell (Admin)'" -ForegroundColor White
Write-Host "3. Run the installation command again" -ForegroundColor White
Write-Host "`nPress enter to exit..."
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
exit 1
}
}
# Set TLS to 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Create temporary directory
$TmpDir = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $TmpDir | Out-Null
# Cleanup function
function Cleanup {
if (Test-Path $TmpDir) {
Remove-Item -Recurse -Force $TmpDir
}
}
# Error handler
trap {
Write-Host "Error: $_" -ForegroundColor Red
Cleanup
Write-Host "Press enter to exit..."
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
exit 1
}
# Detect system architecture
function Get-SystemArch {
if ([Environment]::Is64BitOperatingSystem) {
return "x86_64"
} else {
return "i386"
}
}
# Download with progress
function Get-FileWithProgress {
param (
[string]$Url,
[string]$OutputFile
)
try {
$webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("User-Agent", "PowerShell Script")
$webClient.DownloadFile($Url, $OutputFile)
return $true
}
catch {
Write-Host "Failed to download: $_" -ForegroundColor Red
return $false
}
}
# Main installation function
function Install-CursorModifier {
Write-Host "Starting installation..." -ForegroundColor Cyan
# Detect architecture
$arch = Get-SystemArch
Write-Host "Detected architecture: $arch" -ForegroundColor Green
# Set installation directory
$InstallDir = "$env:ProgramFiles\CursorModifier"
if (!(Test-Path $InstallDir)) {
New-Item -ItemType Directory -Path $InstallDir | Out-Null
}
# Get latest release
try {
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yuaotian/go-cursor-help/releases/latest"
Write-Host "Found latest release: $($latestRelease.tag_name)" -ForegroundColor Cyan
# Look for Windows binary with our architecture
$version = $latestRelease.tag_name.TrimStart('v')
Write-Host "Version: $version" -ForegroundColor Cyan
$possibleNames = @(
"cursor-id-modifier_${version}_windows_x86_64.exe",
"cursor-id-modifier_${version}_windows_$($arch).exe"
)
$asset = $null
foreach ($name in $possibleNames) {
Write-Host "Checking for asset: $name" -ForegroundColor Cyan
$asset = $latestRelease.assets | Where-Object { $_.name -eq $name }
if ($asset) {
Write-Host "Found matching asset: $($asset.name)" -ForegroundColor Green
break
}
}
if (!$asset) {
Write-Host "`nAvailable assets:" -ForegroundColor Yellow
$latestRelease.assets | ForEach-Object { Write-Host "- $($_.name)" }
throw "Could not find appropriate Windows binary for $arch architecture"
}
$downloadUrl = $asset.browser_download_url
}
catch {
Write-Host "Failed to get latest release: $_" -ForegroundColor Red
exit 1
}
# Download binary
Write-Host "`nDownloading latest release..." -ForegroundColor Cyan
$binaryPath = Join-Path $TmpDir "cursor-id-modifier.exe"
if (!(Get-FileWithProgress -Url $downloadUrl -OutputFile $binaryPath)) {
exit 1
}
# Install binary
Write-Host "Installing..." -ForegroundColor Cyan
try {
Copy-Item -Path $binaryPath -Destination "$InstallDir\cursor-id-modifier.exe" -Force
# Add to PATH if not already present
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$InstallDir*") {
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$InstallDir", "Machine")
}
}
catch {
Write-Host "Failed to install: $_" -ForegroundColor Red
exit 1
}
Write-Host "Installation completed successfully!" -ForegroundColor Green
Write-Host "Running cursor-id-modifier..." -ForegroundColor Cyan
# Run the program
try {
& "$InstallDir\cursor-id-modifier.exe"
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to run cursor-id-modifier" -ForegroundColor Red
exit 1
}
}
catch {
Write-Host "Failed to run cursor-id-modifier: $_" -ForegroundColor Red
exit 1
}
}
# Run installation
try {
Install-CursorModifier
}
catch {
Write-Host "Installation failed: $_" -ForegroundColor Red
Cleanup
Write-Host "Press enter to exit..."
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
exit 1
}
finally {
Cleanup
if ($LASTEXITCODE -ne 0) {
Write-Host "Press enter to exit..." -ForegroundColor Green
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
}

127
scripts/install.sh

@ -1,127 +0,0 @@
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;36m'
YELLOW='\033[0;33m'
NC='\033[0m'
# Temporary directory for downloads
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
# Check for required commands
check_requirements() {
if ! command -v curl >/dev/null 2>&1; then
echo -e "${RED}Error: curl is required${NC}"
exit 1
fi
}
# Detect system information
detect_system() {
local os arch suffix
case "$(uname -s)" in
Linux*) os="linux";;
Darwin*) os="darwin";;
*) echo -e "${RED}Unsupported OS${NC}"; exit 1;;
esac
case "$(uname -m)" in
x86_64)
arch="x86_64"
;;
aarch64|arm64)
arch="arm64"
;;
i386|i686)
arch="i386"
;;
*) echo -e "${RED}Unsupported architecture${NC}"; exit 1;;
esac
echo "$os $arch"
}
# Download with progress
download() {
local url="$1"
local output="$2"
curl -#L "$url" -o "$output"
}
# Check and create installation directory
setup_install_dir() {
local install_dir="$1"
if [ ! -d "$install_dir" ]; then
mkdir -p "$install_dir" || {
echo -e "${RED}Failed to create installation directory${NC}"
exit 1
}
fi
}
# Main installation function
main() {
check_requirements
echo -e "${BLUE}Starting installation...${NC}"
# Detect system
read -r OS ARCH SUFFIX <<< "$(detect_system)"
echo -e "${GREEN}Detected: $OS $ARCH${NC}"
# Set installation directory
INSTALL_DIR="/usr/local/bin"
# Setup installation directory
setup_install_dir "$INSTALL_DIR"
# Get latest release info
echo -e "${BLUE}Fetching latest release information...${NC}"
LATEST_URL="https://api.github.com/repos/yuaotian/go-cursor-help/releases/latest"
# Get latest version and remove 'v' prefix
VERSION=$(curl -s "$LATEST_URL" | grep "tag_name" | cut -d'"' -f4 | sed 's/^v//')
# Construct binary name
BINARY_NAME="cursor-id-modifier_${VERSION}_${OS}_${ARCH}"
echo -e "${BLUE}Looking for asset: $BINARY_NAME${NC}"
# Get download URL directly
DOWNLOAD_URL=$(curl -s "$LATEST_URL" | grep -o "\"browser_download_url\": \"[^\"]*${BINARY_NAME}[^\"]*\"" | cut -d'"' -f4)
if [ -z "$DOWNLOAD_URL" ]; then
echo -e "${RED}Error: Could not find appropriate binary for $OS $ARCH${NC}"
echo -e "${YELLOW}Available assets:${NC}"
curl -s "$LATEST_URL" | grep "browser_download_url" | cut -d'"' -f4
exit 1
fi
echo -e "${GREEN}Found matching asset: $BINARY_NAME${NC}"
echo -e "${BLUE}Downloading from: $DOWNLOAD_URL${NC}"
download "$DOWNLOAD_URL" "$TMP_DIR/cursor-id-modifier"
# Install binary
echo -e "${BLUE}Installing...${NC}"
chmod +x "$TMP_DIR/cursor-id-modifier"
sudo mv "$TMP_DIR/cursor-id-modifier" "$INSTALL_DIR/"
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${BLUE}Running cursor-id-modifier...${NC}"
# Run the program with sudo, preserving environment variables
export AUTOMATED_MODE=1
if ! sudo -E cursor-id-modifier; then
echo -e "${RED}Failed to run cursor-id-modifier${NC}"
exit 1
fi
}
main

90
scripts/run/cursor_linux_id_modifier.sh

@ -539,17 +539,84 @@ generate_new_config() {
# 生成并设置新的设备ID # 生成并设置新的设备ID
local new_device_id=$(generate_uuid) local new_device_id=$(generate_uuid)
local new_machine_id=$(generate_uuid) # 使用 UUID 作为 Machine ID 更常见 local new_machine_id=$(generate_uuid) # 使用 UUID 作为 Machine ID 更常见
# 🔧 新增: serviceMachineId (用于 storage.serviceMachineId)
local new_service_machine_id=$(generate_uuid)
# 🔧 新增: firstSessionDate (重置首次会话日期)
local new_first_session_date=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
# 🔧 新增: macMachineId 和 sqmId
local new_mac_machine_id=$(openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | xxd -p | tr -d '\n')
local new_sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}"
log_info "正在设置新的设备和机器ID..." log_info "正在设置新的设备和机器ID..."
log_debug "新设备ID: $new_device_id" log_debug "新设备ID: $new_device_id"
log_debug "新机器ID: $new_machine_id" log_debug "新机器ID: $new_machine_id"
log_debug "新serviceMachineId: $new_service_machine_id"
log_debug "新firstSessionDate: $new_first_session_date"
# 修改配置文件 # 修改配置文件
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 修改成功"
# 🔧 修复: 添加 storage.serviceMachineId, telemetry.firstSessionDate, telemetry.macMachineId, telemetry.sqmId
local config_success=true
modify_or_add_config "deviceId" "$new_device_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "machineId" "$new_machine_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "telemetry.machineId" "$new_machine_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "telemetry.macMachineId" "$new_mac_machine_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "telemetry.devDeviceId" "$new_device_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "telemetry.sqmId" "$new_sqm_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "storage.serviceMachineId" "$new_service_machine_id" "$STORAGE_FILE" || config_success=false
modify_or_add_config "telemetry.firstSessionDate" "$new_first_session_date" "$STORAGE_FILE" || config_success=false
if [ "$config_success" = true ]; then
log_info "配置文件中的所有标识符修改成功"
log_info "📋 [详情] 已更新以下标识符:"
echo " 🔹 deviceId: ${new_device_id:0:16}..."
echo " 🔹 machineId: ${new_machine_id:0:16}..."
echo " 🔹 macMachineId: ${new_mac_machine_id:0:16}..."
echo " 🔹 sqmId: $new_sqm_id"
echo " 🔹 serviceMachineId: $new_service_machine_id"
echo " 🔹 firstSessionDate: $new_first_session_date"
# 🔧 新增: 修改 machineid 文件
log_info "🔧 [machineid] 正在修改 machineid 文件..."
local machineid_file_path="$HOME/.config/Cursor/machineid"
if [ -f "$machineid_file_path" ]; then
# 备份原始 machineid 文件
local machineid_backup="$BACKUP_DIR/machineid.backup_$(date +%Y%m%d_%H%M%S)"
cp "$machineid_file_path" "$machineid_backup" 2>/dev/null && \
log_info "💾 [备份] machineid 文件已备份: $machineid_backup"
fi
# 写入新的 serviceMachineId 到 machineid 文件
if echo -n "$new_service_machine_id" > "$machineid_file_path" 2>/dev/null; then
log_info "✅ [machineid] machineid 文件修改成功: $new_service_machine_id"
# 设置 machineid 文件为只读
chmod 444 "$machineid_file_path" 2>/dev/null && \
log_info "🔒 [保护] machineid 文件已设置为只读"
else else
log_error "配置文件中的 deviceId 或 machineId 修改失败"
log_warn "⚠️ [machineid] machineid 文件修改失败"
log_info "💡 [提示] 可手动修改文件: $machineid_file_path"
fi
# 🔧 新增: 修改 .updaterId 文件(更新器设备标识符)
log_info "🔧 [updaterId] 正在修改 .updaterId 文件..."
local updater_id_file_path="$HOME/.config/Cursor/.updaterId"
if [ -f "$updater_id_file_path" ]; then
# 备份原始 .updaterId 文件
local updater_id_backup="$BACKUP_DIR/.updaterId.backup_$(date +%Y%m%d_%H%M%S)"
cp "$updater_id_file_path" "$updater_id_backup" 2>/dev/null && \
log_info "💾 [备份] .updaterId 文件已备份: $updater_id_backup"
fi
# 生成新的 updaterId(UUID格式)
local new_updater_id=$(generate_uuid)
if echo -n "$new_updater_id" > "$updater_id_file_path" 2>/dev/null; then
log_info "✅ [updaterId] .updaterId 文件修改成功: $new_updater_id"
# 设置 .updaterId 文件为只读
chmod 444 "$updater_id_file_path" 2>/dev/null && \
log_info "🔒 [保护] .updaterId 文件已设置为只读"
else
log_warn "⚠️ [updaterId] .updaterId 文件修改失败"
log_info "💡 [提示] 可手动修改文件: $updater_id_file_path"
fi
else
log_error "配置文件中的部分标识符修改失败"
# 注意:即使失败,备份仍在,但配置文件可能已部分修改 # 注意:即使失败,备份仍在,但配置文件可能已部分修改
return 1 # 返回错误状态 return 1 # 返回错误状态
fi fi
@ -675,6 +742,8 @@ modify_cursor_js_files() {
local mac_machine_id=$(openssl rand -hex 32) local mac_machine_id=$(openssl rand -hex 32)
local sqm_id=$(generate_uuid) local sqm_id=$(generate_uuid)
local session_id=$(generate_uuid) local session_id=$(generate_uuid)
# 🔧 新增: 生成 firstSessionDate 用于替换 someValue.firstSessionDate
local first_session_date=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
log_info "🔑 [生成] 已生成新的设备标识符" log_info "🔑 [生成] 已生成新的设备标识符"
log_info " machineId: ${machine_id:0:16}..." log_info " machineId: ${machine_id:0:16}..."
@ -782,10 +851,17 @@ modify_cursor_js_files() {
replaced=true replaced=true
fi fi
# 🔧 新增: 替换 someValue.firstSessionDate(首次会话日期)
if grep -q 'someValue\.firstSessionDate' "$file"; then
sed -i "s/someValue\.firstSessionDate/${first_session_date}/g" "$file"
log_info " ✓ [方案A] 替换 someValue.firstSessionDate"
replaced=true
fi
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ========== # ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
# 使用IIFE包装,兼容webpack打包的bundle文件,无需import语法
# 劫持crypto.randomUUID从源头拦截所有UUID生成
local inject_code=";(function(){/*__cursor_patched__*/var _cr=require('crypto'),_orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}try{var _os=require('os'),_origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 使用IIFE包装,兼容webpack打包的bundle文件
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
local inject_code=";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 注入代码到文件开头 # 注入代码到文件开头
local temp_file=$(mktemp) local temp_file=$(mktemp)

1401
scripts/run/cursor_mac_free_trial_reset.sh
File diff suppressed because it is too large
View File

290
scripts/run/cursor_mac_id_modifier.sh

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# ======================================== # ========================================
# Cursor macOS 机器码修改脚本 (增强权限修复版)
# Cursor macOS 机器码修改脚本
# ======================================== # ========================================
# #
# 🔧 权限修复增强: # 🔧 权限修复增强:
@ -445,6 +445,10 @@ except Exception as e:
local UUID=$(uuidgen | tr '[:upper:]' '[:lower:]') local UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
local MACHINE_ID="auth0|user_$(openssl rand -hex 32)" local MACHINE_ID="auth0|user_$(openssl rand -hex 32)"
local SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" local SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}"
# 🔧 新增: serviceMachineId (用于 storage.serviceMachineId)
local SERVICE_MACHINE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
# 🔧 新增: firstSessionDate (重置首次会话日期)
local FIRST_SESSION_DATE=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
log_info "✅ [进度] 1/5 - 设备标识符生成完成" log_info "✅ [进度] 1/5 - 设备标识符生成完成"
@ -492,11 +496,14 @@ try:
config = json.load(f) config = json.load(f)
# 安全更新配置,确保属性存在 # 安全更新配置,确保属性存在
# 🔧 修复: 添加 storage.serviceMachineId 和 telemetry.firstSessionDate
properties_to_update = { properties_to_update = {
'telemetry.machineId': '$MACHINE_ID', 'telemetry.machineId': '$MACHINE_ID',
'telemetry.macMachineId': '$MAC_MACHINE_ID', 'telemetry.macMachineId': '$MAC_MACHINE_ID',
'telemetry.devDeviceId': '$UUID', 'telemetry.devDeviceId': '$UUID',
'telemetry.sqmId': '$SQM_ID'
'telemetry.sqmId': '$SQM_ID',
'storage.serviceMachineId': '$SERVICE_MACHINE_ID',
'telemetry.firstSessionDate': '$FIRST_SESSION_DATE'
} }
for key, value in properties_to_update.items(): for key, value in properties_to_update.items():
@ -549,11 +556,14 @@ try:
with open('$config_path', 'r', encoding='utf-8') as f: with open('$config_path', 'r', encoding='utf-8') as f:
config = json.load(f) config = json.load(f)
# 🔧 修复: 添加 storage.serviceMachineId 和 telemetry.firstSessionDate 验证
properties_to_check = { properties_to_check = {
'telemetry.machineId': '$MACHINE_ID', 'telemetry.machineId': '$MACHINE_ID',
'telemetry.macMachineId': '$MAC_MACHINE_ID', 'telemetry.macMachineId': '$MAC_MACHINE_ID',
'telemetry.devDeviceId': '$UUID', 'telemetry.devDeviceId': '$UUID',
'telemetry.sqmId': '$SQM_ID'
'telemetry.sqmId': '$SQM_ID',
'storage.serviceMachineId': '$SERVICE_MACHINE_ID',
'telemetry.firstSessionDate': '$FIRST_SESSION_DATE'
} }
verification_passed = True verification_passed = True
@ -594,8 +604,52 @@ except Exception as e:
echo " 🔹 macMachineId: $MAC_MACHINE_ID" echo " 🔹 macMachineId: $MAC_MACHINE_ID"
echo " 🔹 devDeviceId: $UUID" echo " 🔹 devDeviceId: $UUID"
echo " 🔹 sqmId: $SQM_ID" echo " 🔹 sqmId: $SQM_ID"
echo " 🔹 serviceMachineId: $SERVICE_MACHINE_ID"
echo " 🔹 firstSessionDate: $FIRST_SESSION_DATE"
echo echo
log_info "💾 [备份] 原配置已备份至: $backup_name" log_info "💾 [备份] 原配置已备份至: $backup_name"
# 🔧 新增: 修改 machineid 文件
log_info "🔧 [machineid] 正在修改 machineid 文件..."
local machineid_file_path="$HOME/Library/Application Support/Cursor/machineid"
if [ -f "$machineid_file_path" ]; then
# 备份原始 machineid 文件
local machineid_backup="$backup_dir/machineid.backup_$(date +%Y%m%d_%H%M%S)"
cp "$machineid_file_path" "$machineid_backup" 2>/dev/null && \
log_info "💾 [备份] machineid 文件已备份: $machineid_backup"
fi
# 写入新的 serviceMachineId 到 machineid 文件
if echo -n "$SERVICE_MACHINE_ID" > "$machineid_file_path" 2>/dev/null; then
log_info "✅ [machineid] machineid 文件修改成功: $SERVICE_MACHINE_ID"
# 设置 machineid 文件为只读
chmod 444 "$machineid_file_path" 2>/dev/null && \
log_info "🔒 [保护] machineid 文件已设置为只读"
else
log_warn "⚠️ [machineid] machineid 文件修改失败"
log_info "💡 [提示] 可手动修改文件: $machineid_file_path"
fi
# 🔧 新增: 修改 .updaterId 文件(更新器设备标识符)
log_info "🔧 [updaterId] 正在修改 .updaterId 文件..."
local updater_id_file_path="$HOME/Library/Application Support/Cursor/.updaterId"
if [ -f "$updater_id_file_path" ]; then
# 备份原始 .updaterId 文件
local updater_id_backup="$backup_dir/.updaterId.backup_$(date +%Y%m%d_%H%M%S)"
cp "$updater_id_file_path" "$updater_id_backup" 2>/dev/null && \
log_info "💾 [备份] .updaterId 文件已备份: $updater_id_backup"
fi
# 生成新的 updaterId(UUID格式)
local new_updater_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
if echo -n "$new_updater_id" > "$updater_id_file_path" 2>/dev/null; then
log_info "✅ [updaterId] .updaterId 文件修改成功: $new_updater_id"
# 设置 .updaterId 文件为只读
chmod 444 "$updater_id_file_path" 2>/dev/null && \
log_info "🔒 [保护] .updaterId 文件已设置为只读"
else
log_warn "⚠️ [updaterId] .updaterId 文件修改失败"
log_info "💡 [提示] 可手动修改文件: $updater_id_file_path"
fi
return 0 return 0
else else
log_error "❌ [错误] 修改验证失败" log_error "❌ [错误] 修改验证失败"
@ -1442,6 +1496,8 @@ modify_cursor_js_files() {
local sqm_id=$(uuidgen | tr '[:upper:]' '[:lower:]') local sqm_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
# 生成一个固定的session_id用于替换someValue.sessionId # 生成一个固定的session_id用于替换someValue.sessionId
local session_id=$(uuidgen | tr '[:upper:]' '[:lower:]') local session_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
# 🔧 新增: 生成 firstSessionDate 用于替换 someValue.firstSessionDate
local first_session_date=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
log_info "🔑 [生成] 已生成新的设备标识符" log_info "🔑 [生成] 已生成新的设备标识符"
log_info " machineId: ${machine_id:0:16}..." log_info " machineId: ${machine_id:0:16}..."
@ -1554,10 +1610,17 @@ modify_cursor_js_files() {
replaced=true replaced=true
fi fi
# 🔧 新增: 替换 someValue.firstSessionDate(首次会话日期)
if grep -q 'someValue\.firstSessionDate' "$file"; then
sed -i.tmp "s/someValue\.firstSessionDate/${first_session_date}/g" "$file"
log_info " ✓ [方案A] 替换 someValue.firstSessionDate"
replaced=true
fi
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ========== # ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
# 使用IIFE包装,兼容webpack打包的bundle文件,无需import语法
# 劫持crypto.randomUUID从源头拦截所有UUID生成
local inject_code=";(function(){/*__cursor_patched__*/var _cr=require('crypto'),_orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}try{var _os=require('os'),_origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 使用IIFE包装,兼容webpack打包的bundle文件
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
local inject_code=";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 注入代码到文件开头 # 注入代码到文件开头
echo "$inject_code" > "${file}.new" echo "$inject_code" > "${file}.new"
@ -1590,203 +1653,6 @@ modify_cursor_js_files() {
fi 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
}
@ -2752,17 +2618,7 @@ main() {
log_info "💡 [建议] 请尝试'重置环境+修改机器码'选项" log_info "💡 [建议] 请尝试'重置环境+修改机器码'选项"
fi 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 echo
@ -2818,12 +2674,6 @@ main() {
log_info "🔧 [设备识别] 开始智能设备识别绕过..." log_info "🔧 [设备识别] 开始智能设备识别绕过..."
log_info "💡 [说明] 将根据系统环境自动选择最佳方案(MAC地址修改或JS内核修改)" log_info "💡 [说明] 将根据系统环境自动选择最佳方案(MAC地址修改或JS内核修改)"
if change_system_mac_address; then
log_info "✅ [成功] 设备识别绕过完成(使用MAC地址修改)"
else
log_warn "⚠️ [警告] 设备识别绕过失败或部分失败"
log_info "💡 [提示] 但可能已通过JS内核修改实现了绕过效果"
fi
# 🔧 关键修复:修复应用签名问题(防止"应用已损坏"错误) # 🔧 关键修复:修复应用签名问题(防止"应用已损坏"错误)
echo echo
@ -2862,7 +2712,6 @@ main() {
echo -e "${BLUE} 🎯 修改结果总结 ${NC}" echo -e "${BLUE} 🎯 修改结果总结 ${NC}"
echo -e "${GREEN}================================${NC}" echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}✅ JSON配置文件修改: 完成${NC}" echo -e "${GREEN}✅ JSON配置文件修改: 完成${NC}"
echo -e "${GREEN}✅ MAC地址修改: 完成${NC}"
echo -e "${GREEN}✅ 自动更新禁用: 完成${NC}" echo -e "${GREEN}✅ 自动更新禁用: 完成${NC}"
echo -e "${GREEN}================================${NC}" echo -e "${GREEN}================================${NC}"
echo echo
@ -2877,7 +2726,6 @@ main() {
echo echo
log_info "💡 [重要提示] 完整的Cursor破解流程已执行:" log_info "💡 [重要提示] 完整的Cursor破解流程已执行:"
echo -e "${BLUE} ✅ 机器码配置文件修改${NC}" echo -e "${BLUE} ✅ 机器码配置文件修改${NC}"
echo -e "${BLUE} ✅ 系统MAC地址修改${NC}"
echo -e "${BLUE} ✅ 自动更新功能禁用${NC}" echo -e "${BLUE} ✅ 自动更新功能禁用${NC}"
echo -e "${BLUE} ✅ 权限修复和验证${NC}" echo -e "${BLUE} ✅ 权限修复和验证${NC}"
echo echo

688
scripts/run/cursor_mac_id_modifier_new.sh

@ -1,688 +0,0 @@
#!/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 "$@"

86
scripts/run/cursor_win_id_modifier.ps1

@ -196,10 +196,18 @@ function Modify-CursorJSFiles {
$replaced = $true $replaced = $true
} }
# 🔧 新增: 替换 someValue.firstSessionDate
$firstSessionDateValue = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
if ($content -match 'someValue\.firstSessionDate') {
$content = $content -replace 'someValue\.firstSessionDate', $firstSessionDateValue
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.firstSessionDate"
$replaced = $true
}
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ========== # ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
# 使用IIFE包装,兼容webpack打包的bundle文件,无需import语法
# 劫持crypto.randomUUID从源头拦截所有UUID生成
$injectCode = ";(function(){/*__cursor_patched__*/var _cr=require('crypto'),_orig=_cr.randomUUID;_cr.randomUUID=function(){return'$newUuid';};if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='$machineId';globalThis.__cursor_mac_machine_id='$macMachineId';globalThis.__cursor_dev_device_id='$deviceId';globalThis.__cursor_sqm_id='$sqmId';}try{var _os=require('os'),_origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 使用IIFE包装,兼容webpack打包的bundle文件
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
$injectCode = ";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'$newUuid';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='$machineId';globalThis.__cursor_mac_machine_id='$macMachineId';globalThis.__cursor_dev_device_id='$deviceId';globalThis.__cursor_sqm_id='$sqmId';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
# 注入代码到文件开头 # 注入代码到文件开头
$content = $injectCode + "`n" + $content $content = $injectCode + "`n" + $content
@ -880,10 +888,14 @@ function Modify-MachineCodeConfig {
$rng.Dispose() $rng.Dispose()
$MACHINE_ID = "${prefixHex}${randomPart}" $MACHINE_ID = "${prefixHex}${randomPart}"
$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}" $SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
# 🔧 新增: serviceMachineId (用于 storage.serviceMachineId)
$SERVICE_MACHINE_ID = [System.Guid]::NewGuid().ToString()
# 🔧 新增: firstSessionDate (重置首次会话日期)
$FIRST_SESSION_DATE = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
Write-Host "$GREEN✅ [进度]$NC 1/6 - 设备标识符生成完成"
Write-Host "$GREEN✅ [进度]$NC 1/7 - 设备标识符生成完成"
Write-Host "$BLUE⏳ [进度]$NC 2/6 - 创建备份目录..."
Write-Host "$BLUE⏳ [进度]$NC 2/7 - 创建备份目录..."
# 备份原始值(增强版) # 备份原始值(增强版)
$backupDir = "$env:APPDATA\Cursor\User\globalStorage\backups" $backupDir = "$env:APPDATA\Cursor\User\globalStorage\backups"
@ -894,7 +906,7 @@ function Modify-MachineCodeConfig {
$backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')_retry$retryCount" $backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')_retry$retryCount"
$backupPath = "$backupDir\$backupName" $backupPath = "$backupDir\$backupName"
Write-Host "$BLUE⏳ [进度]$NC 3/6 - 备份原始配置..."
Write-Host "$BLUE⏳ [进度]$NC 3/7 - 备份原始配置..."
Copy-Item $configPath $backupPath -ErrorAction Stop Copy-Item $configPath $backupPath -ErrorAction Stop
# 验证备份是否成功 # 验证备份是否成功
@ -902,7 +914,7 @@ function Modify-MachineCodeConfig {
$backupSize = (Get-Item $backupPath).Length $backupSize = (Get-Item $backupPath).Length
$originalSize = (Get-Item $configPath).Length $originalSize = (Get-Item $configPath).Length
if ($backupSize -eq $originalSize) { if ($backupSize -eq $originalSize) {
Write-Host "$GREEN✅ [进度]$NC 3/6 - 配置备份成功: $backupName"
Write-Host "$GREEN✅ [进度]$NC 3/7 - 配置备份成功: $backupName"
} else { } else {
Write-Host "$YELLOW⚠️ [警告]$NC 备份文件大小不匹配,但继续执行" Write-Host "$YELLOW⚠️ [警告]$NC 备份文件大小不匹配,但继续执行"
} }
@ -910,20 +922,23 @@ function Modify-MachineCodeConfig {
throw "备份文件创建失败" throw "备份文件创建失败"
} }
Write-Host "$BLUE⏳ [进度]$NC 4/6 - 读取原始配置到内存..."
Write-Host "$BLUE⏳ [进度]$NC 4/7 - 读取原始配置到内存..."
# 原子性操作:读取原始内容到内存 # 原子性操作:读取原始内容到内存
$originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop $originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop
$config = $originalContent | ConvertFrom-Json -ErrorAction Stop $config = $originalContent | ConvertFrom-Json -ErrorAction Stop
Write-Host "$BLUE⏳ [进度]$NC 5/6 - 在内存中更新配置..."
Write-Host "$BLUE⏳ [进度]$NC 5/7 - 在内存中更新配置..."
# 更新配置值(安全方式,确保属性存在) # 更新配置值(安全方式,确保属性存在)
# 🔧 修复: 添加 storage.serviceMachineId 和 telemetry.firstSessionDate
$propertiesToUpdate = @{ $propertiesToUpdate = @{
'telemetry.machineId' = $MACHINE_ID 'telemetry.machineId' = $MACHINE_ID
'telemetry.macMachineId' = $MAC_MACHINE_ID 'telemetry.macMachineId' = $MAC_MACHINE_ID
'telemetry.devDeviceId' = $UUID 'telemetry.devDeviceId' = $UUID
'telemetry.sqmId' = $SQM_ID 'telemetry.sqmId' = $SQM_ID
'storage.serviceMachineId' = $SERVICE_MACHINE_ID
'telemetry.firstSessionDate' = $FIRST_SESSION_DATE
} }
foreach ($property in $propertiesToUpdate.GetEnumerator()) { foreach ($property in $propertiesToUpdate.GetEnumerator()) {
@ -942,7 +957,7 @@ function Modify-MachineCodeConfig {
} }
} }
Write-Host "$BLUE⏳ [进度]$NC 6/6 - 原子性写入新配置文件..."
Write-Host "$BLUE⏳ [进度]$NC 6/7 - 原子性写入新配置文件..."
# 原子性操作:删除原文件,写入新文件 # 原子性操作:删除原文件,写入新文件
$tempPath = "$configPath.tmp" $tempPath = "$configPath.tmp"
@ -983,7 +998,7 @@ function Modify-MachineCodeConfig {
$file.IsReadOnly = $false # 保持可写,便于后续修改 $file.IsReadOnly = $false # 保持可写,便于后续修改
# 最终验证修改结果 # 最终验证修改结果
Write-Host "$BLUE🔍 [最终验证]$NC 验证新配置文件..."
Write-Host "$BLUE⏳ [进度]$NC 7/7 - 验证新配置文件..."
$verifyContent = Get-Content $configPath -Raw -Encoding UTF8 $verifyContent = Get-Content $configPath -Raw -Encoding UTF8
$verifyConfig = $verifyContent | ConvertFrom-Json $verifyConfig = $verifyContent | ConvertFrom-Json
@ -1020,9 +1035,58 @@ function Modify-MachineCodeConfig {
Write-Host " 🔹 macMachineId: $MAC_MACHINE_ID" Write-Host " 🔹 macMachineId: $MAC_MACHINE_ID"
Write-Host " 🔹 devDeviceId: $UUID" Write-Host " 🔹 devDeviceId: $UUID"
Write-Host " 🔹 sqmId: $SQM_ID" Write-Host " 🔹 sqmId: $SQM_ID"
Write-Host " 🔹 serviceMachineId: $SERVICE_MACHINE_ID"
Write-Host " 🔹 firstSessionDate: $FIRST_SESSION_DATE"
Write-Host "" Write-Host ""
Write-Host "$GREEN💾 [备份]$NC 原配置已备份至: $backupName" Write-Host "$GREEN💾 [备份]$NC 原配置已备份至: $backupName"
# 🔧 新增: 修改 machineid 文件
Write-Host "$BLUE🔧 [machineid]$NC 正在修改 machineid 文件..."
$machineIdFilePath = "$env:APPDATA\Cursor\machineid"
try {
if (Test-Path $machineIdFilePath) {
# 备份原始 machineid 文件
$machineIdBackup = "$backupDir\machineid.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
Copy-Item $machineIdFilePath $machineIdBackup -Force
Write-Host "$GREEN💾 [备份]$NC machineid 文件已备份: $machineIdBackup"
}
# 写入新的 serviceMachineId 到 machineid 文件
[System.IO.File]::WriteAllText($machineIdFilePath, $SERVICE_MACHINE_ID, [System.Text.Encoding]::UTF8)
Write-Host "$GREEN✅ [machineid]$NC machineid 文件修改成功: $SERVICE_MACHINE_ID"
# 设置 machineid 文件为只读
$machineIdFile = Get-Item $machineIdFilePath
$machineIdFile.IsReadOnly = $true
Write-Host "$GREEN🔒 [保护]$NC machineid 文件已设置为只读"
} catch {
Write-Host "$YELLOW⚠️ [machineid]$NC machineid 文件修改失败: $($_.Exception.Message)"
Write-Host "$BLUE💡 [提示]$NC 可手动修改文件: $machineIdFilePath"
}
# 🔧 新增: 修改 .updaterId 文件(更新器设备标识符)
Write-Host "$BLUE🔧 [updaterId]$NC 正在修改 .updaterId 文件..."
$updaterIdFilePath = "$env:APPDATA\Cursor\.updaterId"
try {
if (Test-Path $updaterIdFilePath) {
# 备份原始 .updaterId 文件
$updaterIdBackup = "$backupDir\.updaterId.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
Copy-Item $updaterIdFilePath $updaterIdBackup -Force
Write-Host "$GREEN💾 [备份]$NC .updaterId 文件已备份: $updaterIdBackup"
}
# 生成新的 updaterId(UUID格式)
$newUpdaterId = [System.Guid]::NewGuid().ToString()
[System.IO.File]::WriteAllText($updaterIdFilePath, $newUpdaterId, [System.Text.Encoding]::UTF8)
Write-Host "$GREEN✅ [updaterId]$NC .updaterId 文件修改成功: $newUpdaterId"
# 设置 .updaterId 文件为只读
$updaterIdFile = Get-Item $updaterIdFilePath
$updaterIdFile.IsReadOnly = $true
Write-Host "$GREEN🔒 [保护]$NC .updaterId 文件已设置为只读"
} catch {
Write-Host "$YELLOW⚠️ [updaterId]$NC .updaterId 文件修改失败: $($_.Exception.Message)"
Write-Host "$BLUE💡 [提示]$NC 可手动修改文件: $updaterIdFilePath"
}
# 🔒 添加配置文件保护机制 # 🔒 添加配置文件保护机制
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..." Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
try { try {

607
scripts/run/cursor_win_id_modifier_old.ps1

@ -1,607 +0,0 @@
# 设置输出编码为 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⚡ [小小广告] Cursor官网正规成品号:Pro¥65 | Pro+¥265 | Ultra¥888 独享账号/7天质保,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 "写入配置文件失败: $_"
}
}
Loading…
Cancel
Save