Browse Source

feat: Enhance configuration management and process handling in main.go

- Added TelemetrySqmId to StorageConfig for improved telemetry tracking.
- Modified NewStorageConfig to accept an existing configuration, allowing for better state management.
- Updated getConfigPath, saveConfig, and readExistingConfig functions to include username as a parameter for user-specific configuration handling.
- Implemented ensureCursorClosed function to ensure the Cursor application is closed before proceeding.
- Enhanced process management functions to handle both uppercase and lowercase process names for better compatibility across systems.
- Updated README.md with new installation instructions and manual configuration steps for the added TelemetrySqmId.

These changes collectively improve the application's configuration handling and user experience during setup and operation.
pull/20/head
dacrab 6 months ago
parent
commit
5fb0288420
  1. 58
      README.md
  2. 207
      main.go

58
README.md

@ -6,15 +6,13 @@
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/main/LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square&logo=bookstack)](https://github.com/yuaotian/go-cursor-help/blob/main/LICENSE)
[![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=flat-square&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers) [![Stars](https://img.shields.io/github/stars/yuaotian/go-cursor-help?style=flat-square&logo=github)](https://github.com/yuaotian/go-cursor-help/stargazers)
[English](#english) | [中文](#chinese)
[English](#-english) | [中文](#-chinese)
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/> <img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
</div> </div>
<a name="english"></a>
## 🌟 English
# 🌟 English
### 📝 Description ### 📝 Description
@ -35,6 +33,8 @@ this is a mistake.
### 📥 Installation ### 📥 Installation
#### Automatic Installation
**Linux/macOS** **Linux/macOS**
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_* curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_*
@ -45,6 +45,27 @@ curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.0.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.0.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue
``` ```
#### Manual Method
1. Close Cursor completely
2. Navigate to the configuration file location:
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
3. Create a backup of `storage.json`
4. Edit `storage.json` and update these fields with new random UUIDs:
```json
{
"telemetry.machineId": "generate-new-uuid",
"telemetry.macMachineId": "generate-new-uuid",
"telemetry.devDeviceId": "generate-new-uuid",
"telemetry.sqmId": "generate-new-uuid",
"lastModified": "2024-01-01T00:00:00.000Z",
"version": "1.0.1"
}
```
5. Save the file and restart Cursor
### 🔧 Technical Details ### 🔧 Technical Details
The program modifies Cursor's `storage.json` config file: The program modifies Cursor's `storage.json` config file:
@ -56,12 +77,11 @@ Generates new unique identifiers for:
- `telemetry.machineId` - `telemetry.machineId`
- `telemetry.macMachineId` - `telemetry.macMachineId`
- `telemetry.devDeviceId` - `telemetry.devDeviceId`
- `telemetry.sqmId`
--- ---
<a name="chinese"></a>
## 🌏 中文
# 🌏 Chinese
### 📝 问题描述 ### 📝 问题描述
@ -82,6 +102,8 @@ this is a mistake.
### 📥 安装方法 ### 📥 安装方法
#### 自动安装
**Linux/macOS** **Linux/macOS**
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_* curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_*
@ -92,6 +114,27 @@ curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.0.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.0.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue
``` ```
#### 手动方法
1. 完全关闭 Cursor
2. 找到配置文件位置:
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
3. 备份 `storage.json`
4. 编辑 `storage.json` 并更新以下字段(使用新的随机UUID):
```json
{
"telemetry.machineId": "生成新的uuid",
"telemetry.macMachineId": "生成新的uuid",
"telemetry.devDeviceId": "生成新的uuid",
"telemetry.sqmId": "生成新的uuid",
"lastModified": "2024-01-01T00:00:00.000Z",
"version": "1.0.1"
}
```
5. 保存文件并重启 Cursor
### 🔧 技术细节 ### 🔧 技术细节
程序修改Cursor的`storage.json`配置文件: 程序修改Cursor的`storage.json`配置文件:
@ -103,6 +146,7 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage
- `telemetry.machineId` - `telemetry.machineId`
- `telemetry.macMachineId` - `telemetry.macMachineId`
- `telemetry.devDeviceId` - `telemetry.devDeviceId`
- `telemetry.sqmId`
## 📄 License ## 📄 License

207
main.go

@ -63,10 +63,10 @@ type (
TelemetryMacMachineId string `json:"telemetry.macMachineId"` TelemetryMacMachineId string `json:"telemetry.macMachineId"`
TelemetryMachineId string `json:"telemetry.machineId"` TelemetryMachineId string `json:"telemetry.machineId"`
TelemetryDevDeviceId string `json:"telemetry.devDeviceId"` TelemetryDevDeviceId string `json:"telemetry.devDeviceId"`
TelemetrySqmId string `json:"telemetry.sqmId"` // Added TelemetrySqmId
LastModified time.Time `json:"lastModified"` LastModified time.Time `json:"lastModified"`
Version string `json:"version"` Version string `json:"version"`
} }
// AppError defines error types / 定义错误类型 // AppError defines error types / 定义错误类型
AppError struct { AppError struct {
Type string Type string
@ -152,35 +152,26 @@ func (e *AppError) Error() string {
} }
// Configuration Functions / 配置函数 // Configuration Functions / 配置函数
func NewStorageConfig() *StorageConfig {
return &StorageConfig{
func NewStorageConfig(oldConfig *StorageConfig) *StorageConfig { // Modified to take old config
newConfig := &StorageConfig{
TelemetryMacMachineId: generateMachineId(), TelemetryMacMachineId: generateMachineId(),
TelemetryMachineId: generateMachineId(), TelemetryMachineId: generateMachineId(),
TelemetryDevDeviceId: generateDevDeviceId(), TelemetryDevDeviceId: generateDevDeviceId(),
LastModified: time.Now(), LastModified: time.Now(),
Version: Version, Version: Version,
} }
}
func initConfig() *Config {
return &Config{
Storage: StorageConfig{
Version: Version,
},
UI: UIConfig{
Language: detectLanguage(),
Theme: "default",
Spinner: SpinnerConfig{
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
Delay: 100 * time.Millisecond,
},
},
System: SystemConfig{
RetryAttempts: 3,
RetryDelay: time.Second,
Timeout: 30 * time.Second,
},
if oldConfig != nil {
newConfig.TelemetrySqmId = oldConfig.TelemetrySqmId
} else {
newConfig.TelemetrySqmId = generateMachineId()
}
if newConfig.TelemetrySqmId == "" {
newConfig.TelemetrySqmId = generateMachineId()
} }
return newConfig
} }
// ID Generation Functions / ID生成函数 // ID Generation Functions / ID生成函数
@ -205,22 +196,16 @@ func generateDevDeviceId() string {
} }
// File Operations / 文件操作 // File Operations / 文件操作
func getConfigPath() (string, error) {
func getConfigPath(username string) (string, error) { // Modified to take username
var configDir string var configDir string
switch runtime.GOOS { switch runtime.GOOS {
case "windows": case "windows":
configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage") configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage")
case "darwin": case "darwin":
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
homeDir := filepath.Join("/home/", username)
configDir = filepath.Join(homeDir, "Library", "Application Support", "Cursor", "User", "globalStorage") configDir = filepath.Join(homeDir, "Library", "Application Support", "Cursor", "User", "globalStorage")
case "linux": case "linux":
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
homeDir := filepath.Join("/home/", username)
configDir = filepath.Join(homeDir, ".config", "Cursor", "User", "globalStorage") configDir = filepath.Join(homeDir, ".config", "Cursor", "User", "globalStorage")
default: default:
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS) return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
@ -228,8 +213,8 @@ func getConfigPath() (string, error) {
return filepath.Join(configDir, "storage.json"), nil return filepath.Join(configDir, "storage.json"), nil
} }
func saveConfig(config *StorageConfig) error {
configPath, err := getConfigPath()
func saveConfig(config *StorageConfig, username string) error { // Modified to take username
configPath, err := getConfigPath(username)
if err != nil { if err != nil {
return err return err
} }
@ -307,8 +292,8 @@ func saveConfig(config *StorageConfig) error {
return nil return nil
} }
func readExistingConfig() (*StorageConfig, error) {
configPath, err := getConfigPath()
func readExistingConfig(username string) (*StorageConfig, error) { // Modified to take username
configPath, err := getConfigPath(username)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,38 +353,71 @@ func (pm *ProcessManager) killWindowsProcess(ctx context.Context) error {
} }
func (pm *ProcessManager) killUnixProcess(ctx context.Context) error { func (pm *ProcessManager) killUnixProcess(ctx context.Context) error {
// Search for the process by it's executable name (AppRun) in ps output
cmd := exec.CommandContext(ctx, "ps", "aux")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to execute ps command: %w", err)
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "AppRun") {
parts := strings.Fields(line)
if len(parts) > 1 {
pid := parts[1]
if err := pm.forceKillProcess(ctx, pid); err != nil {
return err
}
}
}
// handle lowercase
if strings.Contains(line, "apprun") {
parts := strings.Fields(line)
if len(parts) > 1 {
pid := parts[1]
if err := pm.forceKillProcess(ctx, pid); err != nil {
return err
}
}
}
}
return nil
}
// helper function to kill process by pid
func (pm *ProcessManager) forceKillProcess(ctx context.Context, pid string) error {
// First try graceful termination // First try graceful termination
if err := exec.CommandContext(ctx, "pkill", "-TERM", "-f", "Cursor").Run(); err == nil {
if err := exec.CommandContext(ctx, "kill", pid).Run(); err == nil {
// Wait for processes to terminate gracefully // Wait for processes to terminate gracefully
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
} }
// Force kill if still running // Force kill if still running
if err := exec.CommandContext(ctx, "pkill", "-KILL", "-f", "Cursor").Run(); err == nil {
time.Sleep(1 * time.Second)
}
// Also try lowercase variant
exec.CommandContext(ctx, "pkill", "-KILL", "-f", "cursor").Run()
// Verify no processes are left
if output, err := exec.CommandContext(ctx, "pgrep", "-f", "Cursor").Output(); err == nil && len(output) > 0 {
return errors.New("cursor processes still running after kill attempts")
if err := exec.CommandContext(ctx, "kill", "-9", pid).Run(); err != nil {
return fmt.Errorf("failed to force kill process %s: %w", pid, err)
} }
return nil return nil
} }
func checkCursorRunning() bool { func checkCursorRunning() bool {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("tasklist", "/FI", "IMAGENAME eq Cursor.exe", "/NH")
} else {
cmd = exec.Command("pgrep", "-f", "Cursor")
cmd := exec.Command("ps", "aux")
output, err := cmd.Output()
if err != nil {
return false
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "AppRun") || strings.Contains(line, "apprun") {
return true
}
} }
output, _ := cmd.Output()
return strings.Contains(string(output), "Cursor") || strings.Contains(string(output), "cursor")
return false
} }
// UI Components / UI组件 // UI Components / UI组件
@ -458,7 +476,15 @@ func showSuccess() {
// Add spacing before config location // Add spacing before config location
fmt.Println() fmt.Println()
if configPath, err := getConfigPath(); err == nil {
username := os.Getenv("SUDO_USER")
if username == "" {
user, err := user.Current()
if err != nil {
panic(err)
}
username = user.Username
}
if configPath, err := getConfigPath(username); err == nil {
pathColor.Printf("%s\n%s\n", text.ConfigLocation, configPath) pathColor.Printf("%s\n%s\n", text.ConfigLocation, configPath)
} }
} }
@ -495,7 +521,7 @@ func checkAdminPrivileges() (bool, error) {
return false, err return false, err
} }
return strings.Contains(string(output), "S-1-16-12288") || return strings.Contains(string(output), "S-1-16-12288") ||
strings.Contains(string(output), "S-1-5-32-544"), nil
strings.Contains(string(output), "S-1-5-32-544"), nil
case "darwin", "linux": case "darwin", "linux":
currentUser, err := user.Current() currentUser, err := user.Current()
@ -620,7 +646,29 @@ func waitExit() {
bufio.NewReader(os.Stdin).ReadString('\n') bufio.NewReader(os.Stdin).ReadString('\n')
} }
// Main Function / 主函数
// Add this new function near the other process management functions
func ensureCursorClosed() error {
maxAttempts := 3
for attempt := 1; attempt <= maxAttempts; attempt++ {
if !checkCursorRunning() {
return nil
}
if currentLanguage == EN {
fmt.Printf("\nPlease close Cursor before continuing. Attempt %d/%d\n", attempt, maxAttempts)
fmt.Println("Waiting 5 seconds...")
} else {
fmt.Printf("\n请在继续之前关闭 Cursor。尝试 %d/%d\n", attempt, maxAttempts)
fmt.Println("等待 5 秒...")
}
time.Sleep(5 * time.Second)
}
return errors.New("cursor is still running")
}
func main() { func main() {
// Initialize error recovery // Initialize error recovery
defer func() { defer func() {
@ -631,9 +679,25 @@ func main() {
} }
}() }()
var username string
if username = os.Getenv("SUDO_USER"); username == "" {
user, err := user.Current()
if err != nil {
panic(err)
}
username = user.Username
}
log.Println("Current user: ", username)
// Initialize configuration // Initialize configuration
config := initConfig()
ui := NewUI(&config.UI)
ui := NewUI(&UIConfig{
Language: detectLanguage(),
Theme: "default",
Spinner: SpinnerConfig{
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
Delay: 100 * time.Millisecond,
},
})
// Check privileges // Check privileges
os.Stdout.Sync() os.Stdout.Sync()
@ -665,6 +729,17 @@ func main() {
return return
} }
// Add this block after the privilege check
if err := ensureCursorClosed(); err != nil {
if currentLanguage == EN {
fmt.Println("\nError: Please close Cursor manually before running this program.")
} else {
fmt.Println("\n错误:请在运行此程序之前手动关闭 Cursor。")
}
waitExit()
return
}
// Process management // Process management
pm := &ProcessManager{ pm := &ProcessManager{
config: &SystemConfig{ config: &SystemConfig{
@ -707,12 +782,12 @@ func main() {
printCyberpunkBanner() printCyberpunkBanner()
// Read and update configuration // Read and update configuration
oldConfig, err := readExistingConfig()
oldConfig, err := readExistingConfig(username) // add username parameter
if err != nil { if err != nil {
oldConfig = nil oldConfig = nil
} }
storageConfig, err := loadAndUpdateConfig(ui)
storageConfig, err := loadAndUpdateConfig(ui, username) // add username parameter
if err != nil { if err != nil {
handleError(err) handleError(err)
waitExit() waitExit()
@ -722,7 +797,7 @@ func main() {
// Show changes and save // Show changes and save
showIdComparison(oldConfig, storageConfig) showIdComparison(oldConfig, storageConfig)
if err := saveConfig(storageConfig); err != nil {
if err := saveConfig(storageConfig, username); err != nil { // add username parameter
handleError(err) handleError(err)
waitExit() waitExit()
return return
@ -822,12 +897,13 @@ func showIdComparison(oldConfig *StorageConfig, newConfig *StorageConfig) {
yellow.Printf("Machine ID: %s\n", newConfig.TelemetryMachineId) yellow.Printf("Machine ID: %s\n", newConfig.TelemetryMachineId)
yellow.Printf("Mac Machine ID: %s\n", newConfig.TelemetryMacMachineId) yellow.Printf("Mac Machine ID: %s\n", newConfig.TelemetryMacMachineId)
yellow.Printf("Dev Device ID: %s\n", newConfig.TelemetryDevDeviceId) yellow.Printf("Dev Device ID: %s\n", newConfig.TelemetryDevDeviceId)
yellow.Printf("SQM ID: %s\n", newConfig.TelemetrySqmId)
fmt.Println() fmt.Println()
} }
// Configuration functions / 配置函数 // Configuration functions / 配置函数
func loadAndUpdateConfig(ui *UI) (*StorageConfig, error) {
configPath, err := getConfigPath()
func loadAndUpdateConfig(ui *UI, username string) (*StorageConfig, error) { // add username parameter
configPath, err := getConfigPath(username) // add username parameter
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -835,7 +911,7 @@ func loadAndUpdateConfig(ui *UI) (*StorageConfig, error) {
text := texts[currentLanguage] text := texts[currentLanguage]
ui.showProgress(text.ReadingConfig) ui.showProgress(text.ReadingConfig)
_, err = os.ReadFile(configPath)
oldConfig, err := readExistingConfig(username) // add username parameter
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, &AppError{ return nil, &AppError{
Type: ErrSystem, Type: ErrSystem,
@ -846,6 +922,5 @@ func loadAndUpdateConfig(ui *UI) (*StorageConfig, error) {
} }
ui.showProgress(text.GeneratingIds) ui.showProgress(text.GeneratingIds)
return NewStorageConfig(), nil
return NewStorageConfig(oldConfig), nil
} }
Loading…
Cancel
Save