opencode-cursor-agent/cmd/resethwid.go

262 lines
6.4 KiB
Go
Raw Normal View History

2026-03-30 14:09:15 +00:00
package cmd
import (
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"time"
"github.com/google/uuid"
)
func sha256hex() string {
b := make([]byte, 32)
_, _ = rand.Read(b)
h := sha256.Sum256(b)
return hex.EncodeToString(h[:])
}
func sha512hex() string {
b := make([]byte, 64)
_, _ = rand.Read(b)
h := sha512.Sum512(b)
return hex.EncodeToString(h[:])
}
func newUUID() string {
return uuid.New().String()
}
func log(icon, msg string) {
fmt.Printf(" %s %s\n", icon, msg)
}
func getCursorGlobalStorage() string {
switch runtime.GOOS {
case "darwin":
home, _ := os.UserHomeDir()
return filepath.Join(home, "Library", "Application Support", "Cursor", "User", "globalStorage")
case "windows":
appdata := os.Getenv("APPDATA")
return filepath.Join(appdata, "Cursor", "User", "globalStorage")
default:
xdg := os.Getenv("XDG_CONFIG_HOME")
if xdg == "" {
home, _ := os.UserHomeDir()
xdg = filepath.Join(home, ".config")
}
return filepath.Join(xdg, "Cursor", "User", "globalStorage")
}
}
func getCursorRoot() string {
gs := getCursorGlobalStorage()
return filepath.Dir(filepath.Dir(gs))
}
func generateNewIDs() map[string]string {
return map[string]string{
"telemetry.machineId": sha256hex(),
"telemetry.macMachineId": sha512hex(),
"telemetry.devDeviceId": newUUID(),
"telemetry.sqmId": "{" + fmt.Sprintf("%s", newUUID()+"") + "}",
"storage.serviceMachineId": newUUID(),
}
}
func killCursor() {
log("", "Stopping Cursor processes...")
switch runtime.GOOS {
case "windows":
exec.Command("taskkill", "/F", "/IM", "Cursor.exe").Run()
default:
exec.Command("pkill", "-x", "Cursor").Run()
exec.Command("pkill", "-f", "Cursor.app").Run()
}
log("", "Cursor stopped (or was not running)")
}
func updateStorageJSON(storagePath string, ids map[string]string) {
if _, err := os.Stat(storagePath); os.IsNotExist(err) {
log("", fmt.Sprintf("storage.json not found: %s", storagePath))
return
}
if runtime.GOOS == "darwin" {
exec.Command("chflags", "nouchg", storagePath).Run()
exec.Command("chmod", "644", storagePath).Run()
}
data, err := os.ReadFile(storagePath)
if err != nil {
log("", fmt.Sprintf("storage.json read error: %v", err))
return
}
var obj map[string]interface{}
if err := json.Unmarshal(data, &obj); err != nil {
log("", fmt.Sprintf("storage.json parse error: %v", err))
return
}
for k, v := range ids {
obj[k] = v
}
out, err := json.MarshalIndent(obj, "", " ")
if err != nil {
log("", fmt.Sprintf("storage.json marshal error: %v", err))
return
}
if err := os.WriteFile(storagePath, out, 0644); err != nil {
log("", fmt.Sprintf("storage.json write error: %v", err))
return
}
log("", "storage.json updated")
}
func updateStateVscdb(dbPath string, ids map[string]string) {
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
log("", fmt.Sprintf("state.vscdb not found: %s", dbPath))
return
}
if runtime.GOOS == "darwin" {
exec.Command("chflags", "nouchg", dbPath).Run()
exec.Command("chmod", "644", dbPath).Run()
}
if err := updateVscdbPureGo(dbPath, ids); err != nil {
log("", fmt.Sprintf("state.vscdb error: %v", err))
} else {
log("", "state.vscdb updated")
}
}
func updateMachineIDFile(machineID, cursorRoot string) {
var candidates []string
if runtime.GOOS == "linux" {
candidates = []string{
filepath.Join(cursorRoot, "machineid"),
filepath.Join(cursorRoot, "machineId"),
}
} else {
candidates = []string{filepath.Join(cursorRoot, "machineId")}
}
filePath := candidates[0]
for _, c := range candidates {
if _, err := os.Stat(c); err == nil {
filePath = c
break
}
}
if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
log("", fmt.Sprintf("machineId dir error: %v", err))
return
}
if runtime.GOOS == "darwin" {
if _, err := os.Stat(filePath); err == nil {
exec.Command("chflags", "nouchg", filePath).Run()
exec.Command("chmod", "644", filePath).Run()
}
}
if err := os.WriteFile(filePath, []byte(machineID+"\n"), 0644); err != nil {
log("", fmt.Sprintf("machineId write error: %v", err))
return
}
log("", fmt.Sprintf("machineId file updated (%s)", filepath.Base(filePath)))
}
var dirsToWipe = []string{
"Session Storage", "Local Storage", "IndexedDB", "Cache", "Code Cache",
"GPUCache", "Service Worker", "Network", "Cookies", "Cookies-journal",
}
func deepClean(cursorRoot string) {
log("", "Deep-cleaning session data...")
wiped := 0
for _, name := range dirsToWipe {
target := filepath.Join(cursorRoot, name)
if _, err := os.Stat(target); os.IsNotExist(err) {
continue
}
info, err := os.Stat(target)
if err != nil {
continue
}
if info.IsDir() {
if err := os.RemoveAll(target); err == nil {
wiped++
}
} else {
if err := os.Remove(target); err == nil {
wiped++
}
}
}
log("", fmt.Sprintf("Wiped %d cache/session items", wiped))
}
func HandleResetHwid(doDeepClean, dryRun bool) error {
fmt.Print("\nCursor HWID Reset\n\n")
fmt.Println(" Resets all machine / telemetry IDs so Cursor sees a fresh install.")
fmt.Print(" Cursor must be closed — it will be killed automatically.\n\n")
globalStorage := getCursorGlobalStorage()
cursorRoot := getCursorRoot()
if _, err := os.Stat(globalStorage); os.IsNotExist(err) {
fmt.Printf("Cursor config not found at:\n %s\n", globalStorage)
fmt.Println(" Make sure Cursor is installed and has been run at least once.")
os.Exit(1)
}
if dryRun {
fmt.Println(" [DRY RUN] Would reset IDs in:")
fmt.Printf(" %s\n", filepath.Join(globalStorage, "storage.json"))
fmt.Printf(" %s\n", filepath.Join(globalStorage, "state.vscdb"))
fmt.Printf(" %s\n", filepath.Join(cursorRoot, "machineId"))
return nil
}
killCursor()
time.Sleep(800 * time.Millisecond)
newIDs := generateNewIDs()
log("", "Generated new IDs:")
for k, v := range newIDs {
fmt.Printf(" %s: %s\n", k, v)
}
fmt.Println()
log("", "Updating storage.json...")
updateStorageJSON(filepath.Join(globalStorage, "storage.json"), newIDs)
log("", "Updating state.vscdb...")
updateStateVscdb(filepath.Join(globalStorage, "state.vscdb"), newIDs)
log("", "Updating machineId file...")
updateMachineIDFile(newIDs["telemetry.machineId"], cursorRoot)
if doDeepClean {
fmt.Println()
deepClean(cursorRoot)
}
fmt.Print("\nHWID reset complete. You can now restart Cursor.\n\n")
return nil
}