2 changed files with 310 additions and 2 deletions
@ -0,0 +1,308 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"flag" |
|||
"fmt" |
|||
"os" |
|||
"os/exec" |
|||
"os/user" |
|||
"runtime" |
|||
"runtime/debug" |
|||
"strings" |
|||
"time" |
|||
|
|||
"github.com/dacrab/go-cursor-help/internal/config" |
|||
"github.com/dacrab/go-cursor-help/internal/lang" |
|||
"github.com/dacrab/go-cursor-help/internal/process" |
|||
"github.com/dacrab/go-cursor-help/internal/ui" |
|||
"github.com/dacrab/go-cursor-help/pkg/idgen" |
|||
|
|||
"github.com/sirupsen/logrus" |
|||
) |
|||
|
|||
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() { |
|||
// Initialize error recovery
|
|||
defer func() { |
|||
if r := recover(); r != nil { |
|||
log.Errorf("Panic recovered: %v\n", r) |
|||
debug.PrintStack() |
|||
waitExit() |
|||
} |
|||
}() |
|||
|
|||
// Parse flags
|
|||
flag.Parse() |
|||
|
|||
// Show version if requested
|
|||
if *showVersion { |
|||
fmt.Printf("Cursor ID Modifier v%s\n", version) |
|||
return |
|||
} |
|||
|
|||
// Initialize logger
|
|||
log.SetFormatter(&logrus.TextFormatter{ |
|||
FullTimestamp: true, |
|||
}) |
|||
|
|||
// Get current user
|
|||
username := os.Getenv("SUDO_USER") |
|||
if username == "" { |
|||
user, err := user.Current() |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
username = user.Username |
|||
} |
|||
|
|||
// Initialize components
|
|||
display := ui.NewDisplay(nil) |
|||
procManager := process.NewManager(process.DefaultConfig(), log) |
|||
configManager, err := config.NewManager(username) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
generator := idgen.NewGenerator() |
|||
|
|||
// Check privileges
|
|||
isAdmin, err := checkAdminPrivileges() |
|||
if err != nil { |
|||
log.Error(err) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
if !isAdmin { |
|||
if runtime.GOOS == "windows" { |
|||
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 |
|||
} |
|||
return |
|||
} |
|||
display.ShowPrivilegeError( |
|||
lang.GetText().PrivilegeError, |
|||
lang.GetText().RunAsAdmin, |
|||
lang.GetText().RunWithSudo, |
|||
lang.GetText().SudoExample, |
|||
) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
// Ensure Cursor is closed
|
|||
if err := ensureCursorClosed(display, procManager); err != nil { |
|||
message := "\nError: Please close Cursor manually before running this program." |
|||
if lang.GetCurrentLanguage() == lang.CN { |
|||
message = "\n错误:请在运行此程序之前手动关闭 Cursor。" |
|||
} |
|||
display.ShowError(message) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
// Kill any remaining Cursor processes
|
|||
if procManager.IsCursorRunning() { |
|||
text := lang.GetText() |
|||
display.ShowProcessStatus(text.ClosingProcesses) |
|||
|
|||
if err := procManager.KillCursorProcesses(); err != nil { |
|||
fmt.Println() |
|||
message := "Warning: Could not close all Cursor instances. Please close them manually." |
|||
if lang.GetCurrentLanguage() == lang.CN { |
|||
message = "警告:无法关闭所有 Cursor 实例,请手动关闭。" |
|||
} |
|||
display.ShowWarning(message) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
if procManager.IsCursorRunning() { |
|||
fmt.Println() |
|||
message := "\nWarning: Cursor is still running. Please close it manually." |
|||
if lang.GetCurrentLanguage() == lang.CN { |
|||
message = "\n警告:Cursor 仍在运行,请手动关闭。" |
|||
} |
|||
display.ShowWarning(message) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
display.ShowProcessStatus(text.ProcessesClosed) |
|||
fmt.Println() |
|||
} |
|||
|
|||
// Clear screen
|
|||
if err := display.ClearScreen(); err != nil { |
|||
log.Warn("Failed to clear screen:", err) |
|||
} |
|||
|
|||
// Read existing config
|
|||
text := lang.GetText() |
|||
display.ShowProgress(text.ReadingConfig) |
|||
|
|||
oldConfig, err := configManager.ReadConfig() |
|||
if err != nil { |
|||
log.Warn("Failed to read existing config:", err) |
|||
oldConfig = nil |
|||
} |
|||
|
|||
// Generate new IDs
|
|||
display.ShowProgress(text.GeneratingIds) |
|||
|
|||
machineID, err := generator.GenerateMachineID() |
|||
if err != nil { |
|||
log.Fatal("Failed to generate machine ID:", err) |
|||
} |
|||
|
|||
macMachineID, err := generator.GenerateMacMachineID() |
|||
if err != nil { |
|||
log.Fatal("Failed to generate MAC machine ID:", err) |
|||
} |
|||
|
|||
deviceID, err := generator.GenerateDeviceID() |
|||
if err != nil { |
|||
log.Fatal("Failed to generate device ID:", err) |
|||
} |
|||
|
|||
// Create new config
|
|||
newConfig := &config.StorageConfig{ |
|||
TelemetryMachineId: machineID, |
|||
TelemetryMacMachineId: macMachineID, |
|||
TelemetryDevDeviceId: deviceID, |
|||
} |
|||
|
|||
if oldConfig != nil && oldConfig.TelemetrySqmId != "" { |
|||
newConfig.TelemetrySqmId = oldConfig.TelemetrySqmId |
|||
} else { |
|||
sqmID, err := generator.GenerateMacMachineID() |
|||
if err != nil { |
|||
log.Fatal("Failed to generate SQM ID:", err) |
|||
} |
|||
newConfig.TelemetrySqmId = sqmID |
|||
} |
|||
|
|||
// Save config
|
|||
if err := configManager.SaveConfig(newConfig, *setReadOnly); err != nil { |
|||
log.Error(err) |
|||
waitExit() |
|||
return |
|||
} |
|||
|
|||
// Show success
|
|||
display.ShowSuccess(text.SuccessMessage, text.RestartMessage) |
|||
message := "\nOperation completed!" |
|||
if lang.GetCurrentLanguage() == lang.CN { |
|||
message = "\n操作完成!" |
|||
} |
|||
display.ShowInfo(message) |
|||
|
|||
if os.Getenv("AUTOMATED_MODE") != "1" { |
|||
waitExit() |
|||
} |
|||
} |
|||
|
|||
func waitExit() { |
|||
if os.Getenv("AUTOMATED_MODE") == "1" { |
|||
return |
|||
} |
|||
|
|||
fmt.Println(lang.GetText().PressEnterToExit) |
|||
os.Stdout.Sync() |
|||
bufio.NewReader(os.Stdin).ReadString('\n') |
|||
} |
|||
|
|||
func ensureCursorClosed(display *ui.Display, procManager *process.Manager) error { |
|||
maxAttempts := 3 |
|||
text := lang.GetText() |
|||
|
|||
display.ShowProcessStatus(text.CheckingProcesses) |
|||
|
|||
for attempt := 1; attempt <= maxAttempts; attempt++ { |
|||
if !procManager.IsCursorRunning() { |
|||
display.ShowProcessStatus(text.ProcessesClosed) |
|||
fmt.Println() |
|||
return nil |
|||
} |
|||
|
|||
message := fmt.Sprintf("Please close Cursor before continuing. Attempt %d/%d\n%s", |
|||
attempt, maxAttempts, text.PleaseWait) |
|||
if lang.GetCurrentLanguage() == lang.CN { |
|||
message = fmt.Sprintf("请在继续之前关闭 Cursor。尝试 %d/%d\n%s", |
|||
attempt, maxAttempts, text.PleaseWait) |
|||
} |
|||
display.ShowProcessStatus(message) |
|||
|
|||
time.Sleep(5 * time.Second) |
|||
} |
|||
|
|||
return fmt.Errorf("cursor is still running") |
|||
} |
|||
|
|||
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) |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue