diff --git a/.goreleaser.yml b/.goreleaser.yml
deleted file mode 100644
index 13c0b21..0000000
--- a/.goreleaser.yml
+++ /dev/null
@@ -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
diff --git a/cmd/cursor-id-modifier/main.go b/cmd/cursor-id-modifier/main.go
deleted file mode 100644
index 4ff9e0e..0000000
--- a/cmd/cursor-id-modifier/main.go
+++ /dev/null
@@ -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)
- }
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 6bcebe3..0000000
--- a/go.mod
+++ /dev/null
@@ -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
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index d930a66..0000000
--- a/go.sum
+++ /dev/null
@@ -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=
diff --git a/internal/config/config.go b/internal/config/config.go
deleted file mode 100644
index 75b64df..0000000
--- a/internal/config/config.go
+++ /dev/null
@@ -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
-}
diff --git a/internal/lang/lang.go b/internal/lang/lang.go
deleted file mode 100644
index 973fc24..0000000
--- a/internal/lang/lang.go
+++ /dev/null
@@ -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:",
- },
-}
diff --git a/internal/process/manager.go b/internal/process/manager.go
deleted file mode 100644
index f48ac20..0000000
--- a/internal/process/manager.go
+++ /dev/null
@@ -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
- }
-}
diff --git a/internal/ui/display.go b/internal/ui/display.go
deleted file mode 100644
index 32ed566..0000000
--- a/internal/ui/display.go
+++ /dev/null
@@ -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)
- }
- }
-}
diff --git a/internal/ui/logo.go b/internal/ui/logo.go
deleted file mode 100644
index 564525a..0000000
--- a/internal/ui/logo.go
+++ /dev/null
@@ -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)
-}
diff --git a/internal/ui/spinner.go b/internal/ui/spinner.go
deleted file mode 100644
index 145f730..0000000
--- a/internal/ui/spinner.go
+++ /dev/null
@@ -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
- }
- }
-}
diff --git a/pkg/idgen/generator.go b/pkg/idgen/generator.go
deleted file mode 100644
index 3061f74..0000000
--- a/pkg/idgen/generator.go
+++ /dev/null
@@ -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
-}
diff --git a/process_cursor_links.py b/process_cursor_links.py
deleted file mode 100644
index 2952064..0000000
--- a/process_cursor_links.py
+++ /dev/null
@@ -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
-
-📦 Windows x64 安装包
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # Windows x64
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['windows']['x64']}) |\n"
-
- md += """
-
-
-## ARM64
-
-📱 Windows ARM64 安装包
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # Windows ARM64
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['windows']['arm64']}) |\n"
-
- md += """
-
-
-# 🍎 macOS
-
-## Universal
-
-🎯 macOS Universal 安装包
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # macOS Universal
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['mac']['universal']}) |\n"
-
- md += """
-
-
-## ARM64
-
-💪 macOS ARM64 安装包
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # macOS ARM64
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['mac']['arm64']}) |\n"
-
- md += """
-
-
-## Intel
-
-💻 macOS Intel 安装包
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # macOS Intel
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['mac']['x64']}) |\n"
-
- md += """
-
-
-# 🐧 Linux
-
-## x64
-
-🎮 Linux x64 AppImage
-
-| 版本 | 下载链接 |
-|------|----------|
-"""
-
- # Linux x64
- for version in versions:
- links = version.get_download_links()
- md += f"| {version.version} | [下载]({links['linux']['x64']}) |\n"
-
- md += """
-
-
-
-"""
- return md
-
-def main():
- # 示例数据
- data = """
-0.45.11,250207y6nbaw5qc
-0.45.10,250205buadkzpea
-0.45.9,250202tgstl42dt
-0.45.8,250201b44xw1x2k
-0.45.7,250130nr6eorv84
-0.45.6,25013021lv9say3
-0.45.5,250128loaeyulq8
-0.45.4,250126vgr3vztvj
-0.45.3,250124b0rcj0qql
-0.45.2,250123mhituoa6o
-0.45.1,2501213ljml5byg
-0.45.0,250120dh9ezx9pg
-0.44.11,250103fqxdt5u9z
-0.44.10,250102ys80vtnud
-0.44.9,2412268nc6pfzgo
-0.44.8,241222ooktny8mh
-0.44.7,2412219nhracv01
-0.44.6,2412214pmryneua
-0.44.5,241220s3ux0e1tv
-0.44.4,241219117fcvexy
-0.44.3,241218sybfbogmq
-0.44.2,241218ntls52u8v
-0.44.0,2412187f9v0nffu
-0.43.6,241206z7j6me2e2
-0.43.5,241127pdg4cnbu2
-0.43.4,241126w13goyvrs
-0.43.3,2411246yqzx1jmm
-0.43.1,241124gsiwb66nc
-0.42.5,24111460bf2loz1
-0.42.4,2410291z3bdg1dy
-0.42.3,241016kxu9umuir
-0.42.2,2410127mj66lvaq
-0.42.1,241011i66p9fuvm
-0.42.0,241009fij7nohn5
-0.41.3,240925fkhcqg263
-0.41.2,240921llnho65ov
-0.41.1,2409189xe3envg5
-0.40.4,2409052yfcjagw2
-0.40.3,240829epqamqp7h
-0.40.2,240828c021k3aib
-0.40.1,2408245thnycuzj
-0.40.0,24082202sreugb2
-0.39.6,240819ih4ta2fye
-0.39.5,240814y9rhzmu7h
-0.39.4,240810elmeg3seq
-0.39.3,2408092hoyaxt9m
-0.39.2,240808phaxh4b5r
-0.39.1,240807g919tr4ly
-0.39.0,240802cdixtv9a6
-0.38.1,240725f0ti25os7
-0.38.0,240723790oxe4a2
-0.37.1,240714yrr3gmv3k
-0.36.2,2407077n6pzboby
-0.36.1,240706uekt2eaft
-0.36.0,240703xqkjv5aqa
-0.35.1,240621pc2f7rl8a
-0.35.0,240608cv11mfsjl
-0.34.6,240606kgzq24cfb
-0.34.6,240605r495newcf
-0.34.5,240602rq6xovt3a
-0.34.4,2406014h0rgjghe
-0.34.3,240529baisuyd2e
-0.34.2,240528whh1qyo9h
-0.34.1,24052838ygfselt
-0.34.0,240527xus72jmkj
-0.33.4,240511kb8wt1tms
-0.33.3,2405103lx8342ta
-0.33.2,240510dwmw395qe
-0.33.1,2405039a9h2fqc9
-0.33.0,240503hyjsnhazo
-0.32.8,240428d499o6zja
-0.32.7,240427w5guozr0l
-0.32.2,240417ab4wag7sx
-0.32.1,2404152czor73fk
-0.32.0,240412ugli06ue0
-0.31.3,240402rq154jw46
-0.31.1,240402pkwfm2ps6
-0.31.0,2404018j7z0xv2g
-0.30.5,240327tmd2ozdc7
-0.30.4,240325dezy8ziab
-0.30.3,2403229gtuhto9g
-0.30.2,240322gzqjm3p0d
-0.30.1,2403212w1ejubt8
-0.30.0,240320tpx86e7hk
-0.29.1,2403027twmz0d1t
-0.29.0,240301kpqvacw2h
-0.28.1,240226tstim4evd
-0.28.0,240224g2d7jazcq
-0.27.4,240219qdbagglqz
-0.27.3,240218dxhc6y8os
-0.27.2,240216kkzl9nhxi
-0.27.1,240215l4ooehnyl
-0.27.0,240215at6ewkd59
-0.26.2,240212o6r9qxtcg
-0.26.1,2402107t904hing
-0.26.0,240210k8is5xr6v
-0.25.3,240207aacboj1k8
-0.25.2,240206p3708uc9z
-0.25.1,2402033t030rprh
-0.25.0,240203kh86t91q8
-0.24.4,240129iecm3e33w
-0.24.3,2401289dx79qsc0
-0.24.1,240127cad17436d
-0.24.0,240126wp9irhmza
-0.23.9,240124dsmraeml3
-0.23.8,240123fnn1hj1fg
-0.23.7,240123xsfe7ywcv
-0.23.6,240121m1740elox
-0.23.5,2401215utj6tx6q
-0.23.4,240121f4qy6ba2y
-0.23.3,2401201und3ytom
-0.23.2,240120an2k2hf1i
-0.23.1,240119fgzxwudn9
-0.22.2,24011721vsch1l1
-0.22.1,2401083eyk8kmzc
-0.22.0,240107qk62kvva3
-0.21.1,231230h0vi6srww
-0.21.0,231229ezidnxiu3
-0.20.2,231219aksf83aad
-0.20.1,231218ywfaxax09
-0.20.0,231216nsyfew5j1
-0.19.1,2312156z2ric57n
-0.19.0,231214per9qal2p
-0.18.8,2312098ffjr3ign
-0.18.7,23120880aolip2i
-0.18.6,231207ueqazwde8
-0.18.5,231206jzy2n2sbi
-0.18.4,2312033zjv5fqai
-0.18.3,231203k2vnkxq2m
-0.18.1,23120176kaer07t
-0.17.0,231127p7iyxn8rg
-0.16.0,231116rek2xuq6a
-0.15.5,231115a5mv63u9f
-0.15.4,23111469e1i3xyi
-0.15.3,231113b0yv3uqem
-0.15.2,231113ah0kuf3pf
-0.15.1,231111yanyyovap
-0.15.0,231110mdkomczmw
-0.14.1,231109xitrgihlk
-0.14.0,231102m6tuamwbx
-0.13.4,231029rso7pso8l
-0.13.3,231025uihnjkh9v
-0.13.2,231024w4iv7xlm6
-0.13.1,231022f3j0ubckv
-0.13.0,231022ptw6i4j42
-0.12.3,231008c5ursm0oj"""
-
- versions = parse_versions(data)
-
- # 生成 Markdown 文件
- markdown_content = generate_markdown(versions)
- with open('Cursor历史.md', 'w', encoding='utf-8') as f:
- f.write(markdown_content)
-
- # 创建结果数据结构
- result = {
- "versions": []
- }
-
- # 处理每个版本
- for version in versions:
- version_info = {
- "version": version.version,
- "build_id": version.build_id,
- "downloads": version.get_download_links()
- }
- result["versions"].append(version_info)
-
- # 保存为JSON文件
- with open('cursor_downloads.json', 'w', encoding='utf-8') as f:
- json.dump(result, f, indent=2, ensure_ascii=False)
-
- # 同时生成CSV格式的下载链接
- with open('cursor_downloads.csv', 'w', newline='', encoding='utf-8') as f:
- writer = csv.writer(f)
- writer.writerow(['Version', 'Platform', 'Architecture', 'Download URL'])
-
- for version in versions:
- links = version.get_download_links()
- for platform, archs in links.items():
- for arch, url in archs.items():
- writer.writerow([version.version, platform, arch, url])
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/scripts/build_all.bat b/scripts/build_all.bat
deleted file mode 100644
index e07e387..0000000
--- a/scripts/build_all.bat
+++ /dev/null
@@ -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%
\ No newline at end of file
diff --git a/scripts/build_all.sh b/scripts/build_all.sh
deleted file mode 100755
index 7db5e7b..0000000
--- a/scripts/build_all.sh
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/scripts/cursor_id_modifier.pot b/scripts/cursor_id_modifier.pot
deleted file mode 100644
index 54f4417..0000000
--- a/scripts/cursor_id_modifier.pot
+++ /dev/null
@@ -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 ""
diff --git a/scripts/cursor_id_modifier.py b/scripts/cursor_id_modifier.py
deleted file mode 100644
index aad0bc8..0000000
--- a/scripts/cursor_id_modifier.py
+++ /dev/null
@@ -1,1298 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-AppImage instructions:
-mkdir -p ~/Downloads/Cursor
-cd ~/Downloads/Cursor
-cd Cursor && ./Cursor-1.0.5-x86_64.AppImage --appimage-extract
-mkdir -p ~/.local
-rsync -rt ~/Downloads/Cursor/squashfs-root/usr/ ~/.local
-# ^ copy the subfolders not usr itself, so the resulting executable should be ~/.local/bin/cursor
-"""
-import subprocess
-import os
-
-SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
-# repo_dir = os.path.dirname(SCRIPTS_DIR)
-repo_dir = SCRIPTS_DIR
-locales_dir = os.path.join(repo_dir, 'locales')
-t_domain = "cursor_id_modifier"
-
-def compile_messages():
- global _
- languages = ['en_US', 'zh_CN']
- for lang in languages:
- lang_dir = os.path.join(locales_dir, lang, 'LC_MESSAGES')
- if os.path.isdir(lang_dir):
- # Change the directory to the LC_MESSAGES folder
- os.chdir(lang_dir)
- # Run msgfmt command
- out_name = 'cursor_id_modifier.mo'
- subprocess.run(['msgfmt', '-o', out_name, 'cursor_id_modifier.po'], check=True)
- print(os.path.abspath(out_name))
- else:
- print(f"Directory not found: {lang_dir}")
-
-import sys
-import subprocess
-import datetime
-import tempfile
-import shutil
-import uuid
-import hashlib
-import re
-import getpass
-import time
-import select
-import tty
-import termios
-import signal
-import json
-from pathlib import Path
-import glob
-import pwd
-
-import gettext
-import random
-
-# 尝试导入netifaces,如果不可用则使用系统命令
-# Try to import netifaces, use system commands if not available
-try:
- import netifaces
- NETIFACES_AVAILABLE = True
-except ImportError:
- NETIFACES_AVAILABLE = False
- log_warn = lambda x: print(f"[WARN] {x}") # 临时日志函数
- log_warn("netifaces library not found, will use system commands for network operations")
-
-# 设置语言环境
-# Set language environment
-
-# lang = 'zh' if '--en' not in sys.argv else 'en'
-lang = 'en'
-
-assert os.path.isdir(locales_dir)
-# gettext.bindtextdomain(t_domain, localedir=locales_dir)
-if lang == 'zh':
- translation = gettext.translation(
- t_domain,
- localedir=locales_dir,
- languages=['en_US', 'zh_CN'],
- )
-else:
- translation = gettext.NullTranslations()
-translation.install()
-_ = translation.gettext
-
-# 设置错误处理
-# Set error handling
-def set_error_handling():
- global _
- # 在 Python 中,我们使用 try/except 来处理错误,而不是 bash 的 set -e
- # In Python, we use try/except to handle errors instead of bash's set -e
- pass
-
-set_error_handling()
-
-# 定义日志文件路径
-# Define log file path
-LOG_FILE = "/tmp/cursor_linux_id_modifier.log"
-
-# 初始化日志文件
-# Initialize log file
-def initialize_log():
- with open(LOG_FILE, 'w') as f:
- f.write(_("========== Cursor ID modification tool log start {} ==========").format(datetime.datetime.now()) + "\n")
- os.chmod(LOG_FILE, 0o644)
-
-# 颜色定义
-# Color definitions
-RED = '\033[0;31m'
-GREEN = '\033[0;32m'
-YELLOW = '\033[1;33m'
-BLUE = '\033[0;34m'
-NC = '\033[0m' # No Color
-
-# 日志函数 - 同时输出到终端和日志文件
-# Log functions - output to terminal and log file simultaneously
-def log_info(message):
- global _
- print(f"{GREEN}[INFO]{NC} {_(message)}")
- with open(LOG_FILE, 'a') as f:
- f.write(_("[INFO] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n")
-
-def log_warn(message):
- global _
- print(f"{YELLOW}[WARN]{NC} {_(message)}")
- with open(LOG_FILE, 'a') as f:
- f.write(_("[WARN] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n")
-
-def log_error(message):
- global _
- print(f"{RED}[ERROR]{NC} {_(message)}")
- with open(LOG_FILE, 'a') as f:
- f.write(_("[ERROR] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n")
-
-def log_debug(message):
- global _
- print(f"{BLUE}[DEBUG]{NC} {_(message)}")
- with open(LOG_FILE, 'a') as f:
- f.write(_("[DEBUG] {} {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _(message)) + "\n")
-
-# 记录命令输出到日志文件
-# Log command output to log file
-def log_cmd_output(cmd, msg):
- global _
- with open(LOG_FILE, 'a') as f:
- f.write(_("[CMD] {} Executing command: {}").format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), cmd) + "\n")
- f.write(_("[CMD] {}:").format(_(msg)) + "\n")
- process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
- with open(LOG_FILE, 'a') as f:
- for line in process.stdout:
- print(line, end='')
- f.write(line)
- process.wait()
- with open(LOG_FILE, 'a') as f:
- f.write("\n")
-
-# 获取当前用户
-# Get current user
-def get_current_user():
- global _
- if os.geteuid() == 0:
- return os.environ.get('SUDO_USER', '')
- return getpass.getuser()
-
-CURRENT_USER = get_current_user()
-if not CURRENT_USER:
- log_error(_("Unable to get username"))
- sys.exit(1)
-
-# 定义Linux下的Cursor路径
-# Define Cursor paths on Linux
-CURSOR_CONFIG_DIR = os.path.expanduser("~/.config/Cursor")
-STORAGE_FILE = os.path.join(CURSOR_CONFIG_DIR, "User/globalStorage/storage.json")
-BACKUP_DIR = os.path.join(CURSOR_CONFIG_DIR, "User/globalStorage/backups")
-
-# 可能的Cursor二进制路径
-# Possible Cursor binary paths
-CURSOR_BIN_PATHS = [
- "/usr/bin/cursor",
- "/usr/local/bin/cursor",
- os.path.expanduser("~/.local/bin/cursor"),
- "/opt/cursor/cursor",
- "/snap/bin/cursor",
-]
-
-# 找到Cursor安装路径
-# Find Cursor installation path
-def find_cursor_path():
- global _
- log_info(_("Finding Cursor installation path..."))
-
- for path in CURSOR_BIN_PATHS:
- if os.path.isfile(path):
- log_info(_("Found Cursor installation path: {}").format(path))
- os.environ['CURSOR_PATH'] = path
- return True
-
- # 尝试通过which命令定位
- # Try locating via which command
- try:
- result = subprocess.run(['which', 'cursor'], capture_output=True, text=True)
- if result.returncode == 0:
- os.environ['CURSOR_PATH'] = result.stdout.strip()
- log_info(_("Found Cursor via which: {}").format(os.environ['CURSOR_PATH']))
- return True
- except subprocess.CalledProcessError:
- pass
-
- # 尝试查找可能的安装路径
- # Try finding possible installation paths
- search_dirs = ['/usr', '/opt', os.path.expanduser('~/.local')]
- for dir in search_dirs:
- try:
- for root, _1, files in os.walk(dir):
- if 'cursor' in files:
- path = os.path.join(root, 'cursor')
- if os.access(path, os.X_OK):
- os.environ['CURSOR_PATH'] = path
- log_info(_("Found Cursor via search: {}").format(os.environ['CURSOR_PATH']))
- return True
- except PermissionError:
- continue
- log_warn(_("Cursor executable not found, will try using config directory"))
- return False
-
-# 查找并定位Cursor资源文件目录
-# Find and locate Cursor resource directory
-def find_cursor_resources():
- global _
- log_info(_("Finding Cursor resource directory..."))
-
- # 可能的资源目录路径
- # Possible resource directory paths
- resource_paths = [
- "/usr/lib/cursor",
- "/usr/share/cursor",
- "/opt/cursor",
- os.path.expanduser("~/.local/share/cursor"),
- ]
-
- for path in resource_paths:
- if os.path.isdir(path):
- log_info(_("Found Cursor resource directory: {}").format(path))
- os.environ['CURSOR_RESOURCES'] = path
- return True
-
- # 如果有CURSOR_PATH,尝试从它推断
- # If CURSOR_PATH exists, try to infer from it
- if os.environ.get('CURSOR_PATH'):
- base_dir = os.path.dirname(os.environ['CURSOR_PATH'])
- resource_dir = os.path.join(base_dir, 'resources')
- if os.path.isdir(resource_dir):
- os.environ['CURSOR_RESOURCES'] = resource_dir
- log_info(_("Found resource directory via binary path: {}").format(os.environ['CURSOR_RESOURCES']))
- return True
-
- log_warn(_("Cursor resource directory not found"))
- return False
-
-# 检查权限
-# Check permissions
-def check_permissions():
- global _
- if os.geteuid() != 0:
- log_error(_("Please run this script with sudo"))
- print(_("Example: sudo {}").format(sys.argv[0]))
- sys.exit(1)
-
-# 检查并关闭 Cursor 进程
-# Check and kill Cursor processes
-def check_and_kill_cursor():
- global _
- log_info(_("Checking Cursor processes..."))
-
- attempt = 1
- max_attempts = 5
-
- # 函数:获取进程详细信息
- # Function: Get process details
- def get_process_details(process_name):
- log_debug(_("Getting process details for {}:").format(process_name))
- try:
- result = subprocess.run(
- 'ps aux | grep -i "cursor" | grep -v grep | grep -v "cursor_id_modifier.py"',
- shell=True, capture_output=True, text=True
- )
- print(result.stdout)
- except subprocess.CalledProcessError:
- pass
-
- while attempt <= max_attempts:
- # 使用更精确的匹配来获取 Cursor 进程,排除当前脚本和grep进程
- # Use more precise matching to get Cursor processes, excluding current script and grep
- try:
- result = subprocess.run(
- 'ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_id_modifier.py" | awk \'{print $2}\'',
- shell=True, capture_output=True, text=True
- )
- CURSOR_PIDS = result.stdout.strip().split('\n')
- CURSOR_PIDS = [pid for pid in CURSOR_PIDS if pid]
- except subprocess.CalledProcessError:
- CURSOR_PIDS = []
-
- if not CURSOR_PIDS:
- log_info(_("No running Cursor processes found"))
- return True
-
- log_warn(_("Found running Cursor processes"))
- get_process_details("cursor")
-
- log_warn(_("Attempting to terminate Cursor processes..."))
-
- for pid in CURSOR_PIDS:
- try:
- if attempt == max_attempts:
- log_warn(_("Attempting to forcefully terminate processes..."))
- os.kill(int(pid), signal.SIGKILL)
- else:
- os.kill(int(pid), signal.SIGTERM)
- except (OSError, ValueError):
- continue
-
- time.sleep(1)
-
- # 再次检查进程是否还在运行
- # Check again if processes are still running
- try:
- result = subprocess.run(
- 'ps aux | grep -i "cursor" | grep -v "grep" | grep -v "cursor_id_modifier.py"',
- shell=True, capture_output=True, text=True
- )
- if not result.stdout.strip():
- log_info(_("Cursor processes successfully terminated"))
- return True
- except subprocess.CalledProcessError:
- log_info(_("Cursor processes successfully terminated"))
- return True
-
- log_warn(_("Waiting for processes to terminate, attempt {}/{}...").format(attempt, max_attempts))
- attempt += 1
-
- log_error(_("Unable to terminate Cursor processes after {} attempts").format(max_attempts))
- get_process_details("cursor")
- log_error(_("Please manually terminate the processes and try again"))
- sys.exit(1)
-
-# 备份配置文件
-# Backup configuration file
-def backup_config():
- global _
- if not os.path.isfile(STORAGE_FILE):
- log_warn(_("Configuration file does not exist, skipping backup"))
- return True
-
- os.makedirs(BACKUP_DIR, exist_ok=True)
- backup_file = os.path.join(BACKUP_DIR, "storage.json.backup_{}".format(datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))
-
- try:
- shutil.copy(STORAGE_FILE, backup_file)
- os.chmod(backup_file, 0o644)
- os.chown(backup_file, pwd.getpwnam(CURRENT_USER).pw_uid, -1)
- log_info(_("Configuration backed up to: {}").format(backup_file))
- except (OSError, shutil.Error):
- log_error(_("Backup failed"))
- sys.exit(1)
-
-# 生成随机 ID
-# Generate random ID
-def generate_random_id():
- global _
- # 生成32字节(64个十六进制字符)的随机数
- # Generate 32 bytes (64 hexadecimal characters) of random data
- return hashlib.sha256(os.urandom(32)).hexdigest()
-
-# 生成随机 UUID
-# Generate random UUID
-def generate_uuid():
- global _
- # 在Linux上使用uuid模块生成UUID
- # Use uuid module to generate UUID on Linux
- try:
- return str(uuid.uuid1()).lower()
- except Exception:
- # 备选方案:生成类似UUID的字符串
- # Fallback: Generate UUID-like string
- rand_bytes = os.urandom(16)
- rand_hex = rand_bytes.hex()
- return f"{rand_hex[:8]}-{rand_hex[8:12]}-{rand_hex[12:16]}-{rand_hex[16:20]}-{rand_hex[20:]}"
-
-# 修改现有文件
-# Modify or add to configuration file
-def modify_or_add_config(key, value, file):
- global _
- if not os.path.isfile(file):
- log_error(_("File does not exist: {}").format(file))
- return False
-
- # 确保文件可写
- # Ensure file is writable
- try:
- os.chmod(file, 0o644)
- except OSError:
- log_error(_("Unable to modify file permissions: {}").format(file))
- return False
-
- # 创建临时文件
- # Create temporary file
- with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file:
- temp_path = temp_file.name
-
- # 读取原始文件
- # Read original file
- with open(file, 'r') as f:
- content = f.read()
-
- # 检查key是否存在
- # Check if key exists
- if f'"{key}":' in content:
- # key存在,执行替换
- # Key exists, perform replacement
- pattern = f'"{key}":\\s*"[^"]*"'
- replacement = f'"{key}": "{value}"'
- new_content = re.sub(pattern, replacement, content)
- else:
- # key不存在,添加新的key-value对
- # Key does not exist, add new key-value pair
- new_content = content.rstrip('}\n') + f',\n "{key}": "{value}"\n}}'
-
- # 写入临时文件
- # Write to temporary file
- with open(temp_path, 'w') as f:
- f.write(new_content)
-
- # 检查临时文件是否为空
- # Check if temporary file is empty
- if os.path.getsize(temp_path) == 0:
- log_error(_("Generated temporary file is empty"))
- os.unlink(temp_path)
- return False
-
- # 替换原文件
- # Replace original file
- try:
- shutil.move(temp_path, file)
- except OSError:
- log_error(_("Unable to write to file: {}").format(file))
- os.unlink(temp_path)
- return False
-
- # 恢复文件权限
- # Restore file permissions
- os.chmod(file, 0o444)
- return True
-
-# 生成新的配置
-# Generate new configuration
-def generate_new_config():
- global _
- print()
- log_warn(_("Machine code reset options"))
-
- # 使用菜单选择函数询问用户是否重置机器码
- # Use menu selection function to ask user whether to reset machine code
- reset_choice = select_menu_option(
- _("Do you need to reset the machine code? (Usually, modifying JS files is sufficient):"),
- [_("Don't reset - only modify JS files"), _("Reset - modify both config file and machine code")],
- 0
- )
-
- # 记录日志以便调试
- # Log for debugging
- with open(LOG_FILE, 'a') as f:
- f.write(_("[INPUT_DEBUG] Machine code reset option selected: {}").format(reset_choice) + "\n")
-
- # 处理用户选择
- # Handle user selection
- if reset_choice == 1:
- log_info(_("You chose to reset the machine code"))
-
- # 确保配置文件目录存在
- # Ensure configuration file directory exists
- if os.path.isfile(STORAGE_FILE):
- log_info(_("Found existing configuration file: {}").format(STORAGE_FILE))
-
- # 备份现有配置
- # Backup existing configuration
- backup_config()
-
- # 生成并设置新的设备ID
- # Generate and set new device ID
- new_device_id = generate_uuid()
- new_machine_id = "auth0|user_{}".format(hashlib.sha256(os.urandom(16)).hexdigest()[:32])
-
- log_info(_("Setting new device and machine IDs..."))
- log_debug(_("New device ID: {}").format(new_device_id))
- log_debug(_("New machine ID: {}").format(new_machine_id))
-
- # 修改配置文件
- # Modify configuration file
- if (modify_or_add_config("deviceId", new_device_id, STORAGE_FILE) and
- modify_or_add_config("machineId", new_machine_id, STORAGE_FILE)):
- log_info(_("Configuration file modified successfully"))
- else:
- log_error(_("Configuration file modification failed"))
- else:
- log_warn(_("Configuration file not found, this is normal, skipping ID modification"))
- else:
- log_info(_("You chose not to reset the machine code, will only modify JS files"))
-
- # 确保配置文件目录存在
- # Ensure configuration file directory exists
- if os.path.isfile(STORAGE_FILE):
- log_info(_("Found existing configuration file: {}").format(STORAGE_FILE))
-
- # 备份现有配置
- # Backup existing configuration
- backup_config()
- else:
- log_warn(_("Configuration file not found, this is normal, skipping ID modification"))
-
- print()
- log_info(_("Configuration processing completed"))
-
-# 查找Cursor的JS文件
-# Find Cursor's JS files
-def find_cursor_js_files():
- global _
- log_info(_("Finding Cursor's JS files..."))
-
- js_files = []
- found = False
-
- # 如果找到了资源目录,在资源目录中搜索
- # If resource directory is found, search in it
- if os.environ.get('CURSOR_RESOURCES'):
- log_debug(_("Searching for JS files in resource directory: {}").format(os.environ['CURSOR_RESOURCES']))
-
- # 在资源目录中递归搜索特定JS文件
- # Recursively search for specific JS files in resource directory
- js_patterns = [
- "*/extensionHostProcess.js",
- "*/main.js",
- "*/cliProcessMain.js",
- "*/app/out/vs/workbench/api/node/extensionHostProcess.js",
- "*/app/out/main.js",
- "*/app/out/vs/code/node/cliProcessMain.js",
- ]
-
- for pattern in js_patterns:
- try:
- files = glob.glob(os.path.join(os.environ['CURSOR_RESOURCES'], pattern), recursive=True)
- for file in files:
- log_info(_("Found JS file: {}").format(file))
- js_files.append(file)
- found = True
- except Exception:
- continue
-
- # 如果还没找到,尝试在/usr和$HOME目录下搜索
- # If not found, try searching in /usr and $HOME directories
- if not found:
- log_warn(_("No JS files found in resource directory, trying other directories..."))
-
- search_dirs = [
- "/usr/lib/cursor",
- "/usr/share/cursor",
- "/opt/cursor",
- os.path.expanduser("~/.config/Cursor"),
- os.path.expanduser("~/.local/share/cursor"),
- ]
-
- for dir in search_dirs:
- if os.path.isdir(dir):
- log_debug(_("Searching directory: {}").format(dir))
- try:
- for root, _1, files in os.walk(dir):
- for file in files:
- if file.endswith('.js'):
- file_path = os.path.join(root, file)
- with open(file_path, 'r', errors='ignore') as f:
- content = f.read()
- if "IOPlatformUUID" in content or "x-cursor-checksum" in content:
- log_info(_("Found JS file: {}").format(file_path))
- js_files.append(file_path)
- found = True
- except Exception:
- continue
-
- if not found:
- log_error(_("No modifiable JS files found"))
- return False, []
-
- # 保存找到的文件列表到环境变量
- # Save found files to environment variable
- os.environ['CURSOR_JS_FILES'] = json.dumps(js_files)
- log_info(_("Found {} JS files to modify").format(len(js_files)))
- return True, js_files
-
-# 修改Cursor的JS文件
-# Modify Cursor's JS files
-def modify_cursor_js_files():
- global _
- log_info(_("Starting to modify Cursor's JS files..."))
-
- # 先查找需要修改的JS文件
- # First find JS files to modify
- success, js_files = find_cursor_js_files()
- if not success:
- log_error(_("Unable to find modifiable JS files"))
- return False
-
- modified_count = 0
-
- for file in js_files:
- log_info(_("Processing file: {}").format(file))
-
- # 创建文件备份
- # Create file backup
- backup_file = f"{file}.backup_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}"
- try:
- shutil.copy(file, backup_file)
- except OSError:
- log_error(_("Unable to create backup for file: {}").format(file))
- continue
-
- # 确保文件可写
- # Ensure file is writable
- try:
- os.chmod(file, 0o644)
- except OSError:
- log_error(_("Unable to modify file permissions: {}").format(file))
- continue
-
- # 读取文件内容
- # Read file content
- with open(file, 'r', errors='ignore') as f:
- content = f.read()
-
- # 检查文件内容并进行相应修改
- # Check file content and make appropriate modifications
- if 'i.header.set("x-cursor-checksum' in content:
- log_debug(_("Found x-cursor-checksum setting code"))
- new_content = content.replace(
- 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}/${e}`)',
- 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}/${p}`)'
- )
- if new_content != content:
- with open(file, 'w') as f:
- f.write(new_content)
- log_info(_("Successfully modified x-cursor-checksum setting code"))
- modified_count += 1
- else:
- log_error(_("Failed to modify x-cursor-checksum setting code"))
- shutil.copy(backup_file, file)
- elif "IOPlatformUUID" in content:
- log_debug(_("Found IOPlatformUUID keyword"))
- if "function a$" in content and "return crypto.randomUUID()" not in content:
- new_content = content.replace(
- "function a$(t){switch",
- "function a$(t){return crypto.randomUUID(); switch"
- )
- if new_content != content:
- with open(file, 'w') as f:
- f.write(new_content)
- log_debug(_("Successfully injected randomUUID call into a$ function"))
- modified_count += 1
- else:
- log_error(_("Failed to modify a$ function"))
- shutil.copy(backup_file, file)
- elif "async function v5" in content and "return crypto.randomUUID()" not in content:
- new_content = content.replace(
- "async function v5(t){let e=",
- "async function v5(t){return crypto.randomUUID(); let e="
- )
- if new_content != content:
- with open(file, 'w') as f:
- f.write(new_content)
- log_debug(_("Successfully injected randomUUID call into v5 function"))
- modified_count += 1
- else:
- log_error(_("Failed to modify v5 function"))
- shutil.copy(backup_file, file)
- else:
- # 通用注入方法
- # Universal injection method
- if "// Cursor ID 修改工具注入" not in content:
- timestamp = datetime.datetime.now().strftime('%s')
- datetime_s = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
- inject_code = f"""
-// Cursor ID 修改工具注入 - {datetime_s}
-// 随机设备ID生成器注入 - {timestamp}
-const randomDeviceId_{timestamp} = () => {{
- try {{
- return require('crypto').randomUUID();
- }} catch (e) {{
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {{
- const r = Math.random() * 16 | 0;
- return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
- }});
- }}
-}};
-"""
- # NOTE: double {{ or }} is literal, so code matches:
- old_bash_inject_code = """
-// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S)
-// 随机设备ID生成器注入 - $(date +%s)
-const randomDeviceId_$(date +%s) = () => {
- try {
- return require('crypto').randomUUID();
- } catch (e) {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
- const r = Math.random() * 16 | 0;
- return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
- });
- }
-};
-"""
- new_content = inject_code + content
- new_content = new_content.replace(f"await v5(!1)", f"randomDeviceId_{timestamp}()")
- new_content = new_content.replace(f"a$(t)", f"randomDeviceId_{timestamp}()")
- with open(file, 'w') as f:
- f.write(new_content)
- log_debug(_("Completed universal modification"))
- modified_count += 1
- else:
- log_info(_("File already contains custom injection code, skipping modification"))
- else:
- # 未找到关键字,尝试通用方法
- # No keywords found, try universal method
- if "return crypto.randomUUID()" not in content and "// Cursor ID 修改工具注入" not in content:
- if "function t$()" in content or "async function y5" in content:
- new_content = content
- if "function t$()" in new_content:
- new_content = new_content.replace(
- "function t$(){",
- 'function t$(){return "00:00:00:00:00:00";'
- )
- if "async function y5" in new_content:
- new_content = new_content.replace(
- "async function y5(t){",
- "async function y5(t){return crypto.randomUUID();"
- )
- if new_content != content:
- with open(file, 'w') as f:
- f.write(new_content)
- modified_count += 1
- else:
- # 最通用的注入方法
- # Most universal injection method
- new_uuid = generate_uuid()
- machine_id = f"auth0|user_{hashlib.sha256(os.urandom(16)).hexdigest()[:32]}"
- device_id = generate_uuid()
- mac_machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
- timestamp = datetime.datetime.now().strftime('%s')
- datetime_s = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
- inject_universal_code = f"""
-// Cursor ID 修改工具注入 - {datetime_s}
-// 全局拦截设备标识符 - {timestamp}
-const originalRequire_{timestamp} = require;
-require = function(module) {{
- const result = originalRequire_{timestamp}(module);
- if (module === 'crypto' && result.randomUUID) {{
- const originalRandomUUID_{timestamp} = result.randomUUID;
- result.randomUUID = function() {{
- return '{new_uuid}';
- }};
- }}
- return result;
-}};
-
-// 覆盖所有可能的系统ID获取函数
-global.getMachineId = function() {{ return '{machine_id}'; }};
-global.getDeviceId = function() {{ return '{device_id}'; }};
-global.macMachineId = '{mac_machine_id}';
-"""
- # NOTE: Double {{ or }} is literal, so matches:
- old_bash_inject_code = """
-// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S)
-// 全局拦截设备标识符 - $(date +%s)
-const originalRequire_$(date +%s) = require;
-require = function(module) {
- const result = originalRequire_$(date +%s)(module);
- if (module === 'crypto' && result.randomUUID) {
- const originalRandomUUID_$(date +%s) = result.randomUUID;
- result.randomUUID = function() {
- return '$new_uuid';
- };
- }
- return result;
-};
-
-// 覆盖所有可能的系统ID获取函数
-global.getMachineId = function() { return '$machine_id'; };
-global.getDeviceId = function() { return '$device_id'; };
-global.macMachineId = '$mac_machine_id';
-"""
- new_content = inject_universal_code + content
- with open(file, 'w') as f:
- f.write(new_content)
- log_debug(_("Completed most universal injection"))
- modified_count += 1
- else:
- log_info(_("File has already been modified, skipping modification"))
-
- # 恢复文件权限
- # Restore file permissions
- os.chmod(file, 0o444)
-
- if modified_count == 0:
- log_error(_("Failed to modify any JS files"))
- return False
-
- log_info(_("Successfully modified {} JS files").format(modified_count))
- return True
-
-# 获取网络接口列表
-# Get network interface list
-def get_network_interfaces():
- global _
- if NETIFACES_AVAILABLE:
- try:
- # 使用netifaces库
- # Use netifaces library
- interfaces = netifaces.interfaces()
- # 过滤掉回环接口
- # Filter out loopback interfaces
- return [iface for iface in interfaces if not iface.startswith('lo')]
- except Exception:
- pass
-
- # 如果netifaces不可用或失败,使用系统命令
- # If netifaces is not available or failed, use system commands
- try:
- result = subprocess.run(['ip', 'link', 'show'], capture_output=True, text=True)
- interfaces = []
- for line in result.stdout.split('\n'):
- if ': ' in line and 'state' in line.lower():
- iface_name = line.split(': ')[1].split('@')[0]
- if not iface_name.startswith('lo'):
- interfaces.append(iface_name)
- return interfaces
- except subprocess.CalledProcessError:
- # 最后的备选方案
- # Last fallback option
- try:
- result = subprocess.run(['ls', '/sys/class/net/'], capture_output=True, text=True)
- interfaces = result.stdout.strip().split('\n')
- return [iface for iface in interfaces if not iface.startswith('lo')]
- except subprocess.CalledProcessError:
- log_error(_("Unable to get network interface list"))
- return []
-
-# 生成随机MAC地址
-# Generate random MAC address
-def generate_random_mac():
- global _
- # 生成随机MAC地址,确保第一个字节的最低位为0(单播地址)
- # Generate random MAC address, ensure the lowest bit of first byte is 0 (unicast address)
- mac = [0x00, 0x16, 0x3e,
- random.randint(0x00, 0x7f),
- random.randint(0x00, 0xff),
- random.randint(0x00, 0xff)]
- return ':'.join(map(lambda x: "%02x" % x, mac))
-
-# 获取当前MAC地址
-# Get current MAC address
-def get_current_mac(interface):
- global _
- if NETIFACES_AVAILABLE:
- try:
- # 使用netifaces库
- # Use netifaces library
- addrs = netifaces.ifaddresses(interface)
- if netifaces.AF_LINK in addrs:
- return addrs[netifaces.AF_LINK][0]['addr']
- except Exception:
- pass
-
- # 使用系统命令获取MAC地址
- # Use system command to get MAC address
- try:
- result = subprocess.run(['cat', f'/sys/class/net/{interface}/address'],
- capture_output=True, text=True)
- if result.returncode == 0:
- return result.stdout.strip()
- except subprocess.CalledProcessError:
- pass
-
- # 备选方案:使用ip命令
- # Fallback: use ip command
- try:
- result = subprocess.run(['ip', 'link', 'show', interface],
- capture_output=True, text=True)
- for line in result.stdout.split('\n'):
- if 'link/ether' in line:
- return line.split()[1]
- except subprocess.CalledProcessError:
- pass
-
- return None
-
-# 修改MAC地址
-# Modify MAC address
-def modify_mac_address():
- global _
- log_info(_("Starting MAC address modification..."))
-
- # 获取网络接口列表
- # Get network interface list
- interfaces = get_network_interfaces()
- if not interfaces:
- log_error(_("No network interfaces found"))
- return False
-
- log_info(_("Found network interfaces: {}").format(', '.join(interfaces)))
-
- # 选择要修改的接口(通常选择第一个非回环接口)
- # Select interface to modify (usually the first non-loopback interface)
- target_interface = None
- for iface in interfaces:
- # 优先选择以太网接口
- # Prefer ethernet interfaces
- if any(prefix in iface.lower() for prefix in ['eth', 'enp', 'ens', 'enx']):
- target_interface = iface
- break
-
- # 如果没有找到以太网接口,选择第一个可用接口
- # If no ethernet interface found, select first available interface
- if not target_interface and interfaces:
- target_interface = interfaces[0]
-
- if not target_interface:
- log_error(_("No suitable network interface found"))
- return False
-
- log_info(_("Selected network interface: {}").format(target_interface))
-
- # 获取当前MAC地址
- # Get current MAC address
- current_mac = get_current_mac(target_interface)
- if current_mac:
- log_info(_("Current MAC address: {}").format(current_mac))
-
- # 备份当前MAC地址
- # Backup current MAC address
- backup_file = os.path.join(BACKUP_DIR, f"original_mac_{target_interface}.txt")
- try:
- os.makedirs(BACKUP_DIR, exist_ok=True)
- with open(backup_file, 'w') as f:
- f.write(f"{target_interface}:{current_mac}\n")
- log_info(_("Original MAC address backed up to: {}").format(backup_file))
- except OSError:
- log_warn(_("Failed to backup original MAC address"))
- else:
- log_warn(_("Unable to get current MAC address"))
-
- # 生成新的MAC地址
- # Generate new MAC address
- new_mac = generate_random_mac()
- log_info(_("Generated new MAC address: {}").format(new_mac))
-
- # 修改MAC地址
- # Modify MAC address
- success = False
-
- # 方法1:使用ip命令
- # Method 1: Use ip command
- try:
- log_debug(_("Attempting to modify MAC address using ip command..."))
-
- # 先关闭接口
- # First bring down the interface
- subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'down'],
- check=True, capture_output=True)
-
- # 修改MAC地址
- # Modify MAC address
- subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'address', new_mac],
- check=True, capture_output=True)
-
- # 重新启用接口
- # Bring up the interface
- subprocess.run(['ip', 'link', 'set', 'dev', target_interface, 'up'],
- check=True, capture_output=True)
-
- success = True
- log_info(_("Successfully modified MAC address using ip command"))
-
- except subprocess.CalledProcessError as e:
- log_warn(_("Failed to modify MAC address using ip command: {}").format(str(e)))
-
- # 方法2:使用ifconfig命令(备选方案)
- # Method 2: Use ifconfig command (fallback)
- if not success:
- try:
- log_debug(_("Attempting to modify MAC address using ifconfig command..."))
-
- # 先关闭接口
- # First bring down the interface
- subprocess.run(['ifconfig', target_interface, 'down'],
- check=True, capture_output=True)
-
- # 修改MAC地址
- # Modify MAC address
- subprocess.run(['ifconfig', target_interface, 'hw', 'ether', new_mac],
- check=True, capture_output=True)
-
- # 重新启用接口
- # Bring up the interface
- subprocess.run(['ifconfig', target_interface, 'up'],
- check=True, capture_output=True)
-
- success = True
- log_info(_("Successfully modified MAC address using ifconfig command"))
-
- except subprocess.CalledProcessError as e:
- log_warn(_("Failed to modify MAC address using ifconfig command: {}").format(str(e)))
-
- # 验证MAC地址修改
- # Verify MAC address modification
- if success:
- time.sleep(2) # 等待网络接口稳定
- # Wait for network interface to stabilize
-
- new_current_mac = get_current_mac(target_interface)
- if new_current_mac and new_current_mac.lower() == new_mac.lower():
- log_info(_("MAC address modification verified successfully"))
- log_info(_("New MAC address: {}").format(new_current_mac))
-
- # 保存新MAC地址信息
- # Save new MAC address information
- new_mac_file = os.path.join(BACKUP_DIR, f"new_mac_{target_interface}.txt")
- try:
- with open(new_mac_file, 'w') as f:
- f.write(f"{target_interface}:{new_current_mac}\n")
- f.write(f"Modified at: {datetime.datetime.now()}\n")
- log_info(_("New MAC address information saved to: {}").format(new_mac_file))
- except OSError:
- log_warn(_("Failed to save new MAC address information"))
-
- return True
- else:
- log_error(_("MAC address modification verification failed"))
- log_error(_("Expected: {}, Actual: {}").format(new_mac, new_current_mac))
- return False
- else:
- log_error(_("All MAC address modification methods failed"))
- log_error(_("Please check if you have sufficient permissions or try running with sudo"))
- return False
-
-# 禁用自动更新
-# Disable auto-update
-def disable_auto_update():
- global _
- log_info(_("Disabling Cursor auto-update..."))
-
- # 查找可能的更新配置文件
- # Find possible update configuration files
- update_configs = [
- os.path.join(CURSOR_CONFIG_DIR, "update-config.json"),
- os.path.expanduser("~/.local/share/cursor/update-config.json"),
- "/opt/cursor/resources/app-update.yml",
- ]
-
- disabled = False
-
- for config in update_configs:
- if os.path.isfile(config):
- log_info(_("Found update configuration file: {}").format(config))
- try:
- shutil.copy(config, f"{config}.bak")
- with open(config, 'w') as f:
- json.dump({"autoCheck": False, "autoDownload": False}, f)
- os.chmod(config, 0o444)
- log_info(_("Disabled update configuration file: {}").format(config))
- disabled = True
- except (OSError, shutil.Error):
- continue
-
- # 尝试查找updater可执行文件并禁用
- # Try to find and disable updater executable
- updater_paths = [
- os.path.join(CURSOR_CONFIG_DIR, "updater"),
- "/opt/cursor/updater",
- "/usr/lib/cursor/updater",
- ]
-
- for updater in updater_paths:
- if os.path.exists(updater):
- log_info(_("Found updater: {}").format(updater))
- try:
- if os.path.isfile(updater):
- shutil.move(updater, f"{updater}.bak")
- else:
- Path(f"{updater}.disabled").touch()
- log_info(_("Disabled updater: {}").format(updater))
- disabled = True
- except (OSError, shutil.Error):
- continue
-
- if not disabled:
- log_warn(_("No update configuration files or updaters found"))
- else:
- log_info(_("Successfully disabled auto-update"))
-
-# 新增:通用菜单选择函数
-# New: Universal menu selection function
-def select_menu_option(prompt, options, default_index=0):
- global _
- # 保存终端设置
- # Save terminal settings
- old_settings = termios.tcgetattr(sys.stdin)
- selected_index = default_index
-
- try:
- # 设置终端为非缓冲模式
- # Set terminal to non-buffered mode
- tty.setcbreak(sys.stdin.fileno())
-
- # 显示提示信息
- # Display prompt
- print(_(prompt))
-
- # 第一次显示菜单
- # Display menu initially
- for i, option in enumerate(options):
- if i == selected_index:
- print(f" {GREEN}►{NC} {_(option)}")
- else:
- print(f" {_(option)}")
-
- while True:
- # 读取键盘输入
- # Read keyboard input
- rlist, _1, _2 = select.select([sys.stdin], [], [], 0.1)
- if rlist:
- key = sys.stdin.read(1)
- # 上箭头键
- # Up arrow key
- if key == '\033':
- next_char = sys.stdin.read(2)
- if next_char == '[A' and selected_index > 0:
- selected_index -= 1
- # 下箭头键
- # Down arrow key
- elif next_char == '[B' and selected_index < len(options) - 1:
- selected_index += 1
- # Enter键
- # Enter key
- elif key == '\n':
- print()
- log_info(_("You selected: {}").format(options[selected_index]))
- return selected_index
-
- # 清除当前菜单
- # Clear current menu
- sys.stdout.write('\033[{}A\033[J'.format(len(options) + 1))
- sys.stdout.flush()
-
- # 重新显示提示和菜单
- # Redisplay prompt and menu
- print(_(prompt))
- for i, option in enumerate(options):
- if i == selected_index:
- print(f" {GREEN}►{NC} {_(option)}")
- else:
- print(f" {_(option)}")
- sys.stdout.flush()
-
- finally:
- # 恢复终端设置
- # Restore terminal settings
- termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
-
-# 主函数
-# Main function
-def main():
- global _
- # 检查系统环境
- # Check system environment
- if sys.platform != "linux":
- log_error(_("This script only supports Linux systems"))
- sys.exit(1)
-
- # 初始化日志文件
- # Initialize log file
- initialize_log()
- log_info(_("Script started..."))
-
- # 记录系统信息
- # Log system information
- log_info(_("System information: {}").format(subprocess.getoutput("uname -a")))
- log_info(_("Current user: {}").format(CURRENT_USER))
- log_cmd_output(
- "lsb_release -a 2>/dev/null || cat /etc/*release 2>/dev/null || cat /etc/issue",
- _("System version information")
- )
-
- # 清除终端
- # Clear terminal
- os.system('clear')
-
- # 显示 Logo
- # Display Logo
- print("""
- ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
- ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
- ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
- ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
- ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
- ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
- """)
- print(f"{BLUE}================================{NC}")
- print(f"{GREEN} {_('Cursor Linux startup tool')} {NC}")
- print(f"{BLUE}================================{NC}")
- print()
- print(f"{YELLOW}[{_('Important notice')}] {NC} {_('This tool prioritizes modifying JS files, which is safer and more reliable')}")
- print()
-
- # 执行主要功能
- # Execute main functions
- check_permissions()
- find_cursor_path()
- find_cursor_resources()
- check_and_kill_cursor()
- backup_config()
- generate_new_config()
-
- # 修改JS文件
- # Modify JS files
- log_info(_("Modifying Cursor JS files..."))
- js_success = modify_cursor_js_files()
- if js_success:
- log_info(_("JS files modified successfully!"))
- else:
- log_warn(_("JS file modification failed, but configuration file modification may have succeeded"))
- log_warn(_("If Cursor still indicates the device is disabled after restarting, please rerun this script"))
-
- # 修改MAC地址
- # Modify MAC address
- log_info(_("Modifying system MAC address..."))
- mac_success = modify_mac_address()
- if mac_success:
- log_info(_("MAC address modified successfully!"))
- else:
- log_warn(_("MAC address modification failed"))
- log_warn(_("This may affect the effectiveness of device identification bypass"))
-
- # 禁用自动更新
- # Disable auto-update
- disable_auto_update()
-
- # 显示修改结果总结
- # Display modification result summary
- print()
- print(f"{GREEN}================================{NC}")
- print(f"{BLUE} {_('Modification Results Summary')} {NC}")
- print(f"{GREEN}================================{NC}")
-
- if js_success:
- print(f"{GREEN}✓{NC} {_('JS files modification: SUCCESS')}")
- else:
- print(f"{RED}✗{NC} {_('JS files modification: FAILED')}")
-
- if mac_success:
- print(f"{GREEN}✓{NC} {_('MAC address modification: SUCCESS')}")
- else:
- print(f"{RED}✗{NC} {_('MAC address modification: FAILED')}")
-
- print(f"{GREEN}================================{NC}")
- print()
-
- log_info(_("Please restart Cursor to apply the new configuration"))
-
- # 显示最后的提示信息
- # Display final prompt
- print()
- print(f"{GREEN}================================{NC}")
- print(f"{YELLOW} {_('Follow the WeChat public account [Pancake AI] to discuss more Cursor tips and AI knowledge (script is free, join the group via the public account for more tips and experts)')} {NC}")
- print("WeChat account: [煎饼果子卷AI]")
- print(f"{GREEN}================================{NC}")
- print()
-
- # 记录脚本完成信息
- # Log script completion information
- log_info(_("Script execution completed"))
- with open(LOG_FILE, 'a') as f:
- f.write(_("========== Cursor ID modification tool log end {} ==========").format(datetime.datetime.now()) + "\n")
-
- # 显示日志文件位置
- # Display log file location
- print()
- log_info(_("Detailed log saved to: {}").format(LOG_FILE))
- print(_("If you encounter issues, please provide this log file to the developer for troubleshooting"))
- print()
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/scripts/git-actions.sh b/scripts/git-actions.sh
deleted file mode 100755
index 632e312..0000000
--- a/scripts/git-actions.sh
+++ /dev/null
@@ -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
-
diff --git a/scripts/install.ps1 b/scripts/install.ps1
deleted file mode 100644
index b30350b..0000000
--- a/scripts/install.ps1
+++ /dev/null
@@ -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')
- }
-}
\ No newline at end of file
diff --git a/scripts/install.sh b/scripts/install.sh
deleted file mode 100755
index adc7881..0000000
--- a/scripts/install.sh
+++ /dev/null
@@ -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