package cursor import ( "bytes" "encoding/json" "errors" "fmt" "os" "path/filepath" "sync" "cursor/internal/appdata" "cursor/internal/securefile" ) var proxySettingsMu sync.Mutex type proxySettingsBackup struct { Original map[string]json.RawMessage `json:"original"` Applied map[string]json.RawMessage `json:"applied"` } func settingsBackupPath() string { return filepath.Join(appdata.DataRootPath(), "proxy-settings-backup.json") } func rawEqual(a, b json.RawMessage) bool { var x, y bytes.Buffer if json.Compact(&x, a) != nil || json.Compact(&y, b) != nil { return len(a) == 0 && len(b) == 0 } return bytes.Equal(x.Bytes(), y.Bytes()) } func loadSettings(path string) (map[string]json.RawMessage, error) { data, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { return map[string]json.RawMessage{}, nil } if err != nil { return nil, err } data, err = normalizeJSONC(data) if err != nil { return nil, err } if len(bytes.TrimSpace(data)) == 0 { return map[string]json.RawMessage{}, nil } var settings map[string]json.RawMessage if err = json.Unmarshal(data, &settings); err != nil { return nil, err } if settings == nil { return nil, fmt.Errorf("Cursor 配置必须是 JSON 对象") } return settings, nil } func readSettingsBackup() (proxySettingsBackup, error) { backup := proxySettingsBackup{Original: map[string]json.RawMessage{}, Applied: map[string]json.RawMessage{}} data, err := os.ReadFile(settingsBackupPath()) if errors.Is(err, os.ErrNotExist) { return backup, nil } if err != nil { return backup, err } err = json.Unmarshal(data, &backup) if err == nil && (backup.Original == nil || backup.Applied == nil) { err = fmt.Errorf("代理配置备份损坏") } return backup, err } func WriteUserProxySettings(proxyURL string) error { if proxyURL == "" { return fmt.Errorf("代理地址为空") } proxySettingsMu.Lock() defer proxySettingsMu.Unlock() path, err := resolveCursorSettingsPath() if err != nil { return err } settings, err := loadSettings(path) if err != nil { return fmt.Errorf("解析 Cursor 配置失败,原文件已保留: %w", err) } backup, err := readSettingsBackup() if err != nil { return err } values := map[string]any{"http.proxy": proxyURL, "http.proxyKerberosServicePrincipal": proxyURL, "http.proxySupport": "on", "cursor.general.disableHttp2": true, "http.experimental.systemCertificatesV2": true} for key, value := range values { if previous, exists := settings[key]; exists { if !rawEqual(previous, backup.Applied[key]) { backup.Original[key] = append(json.RawMessage(nil), previous...) } } else { delete(backup.Original, key) } encoded, _ := json.Marshal(value) backup.Applied[key] = encoded settings[key] = encoded } backupData, err := json.Marshal(backup) if err != nil { return err } if err = securefile.Write(settingsBackupPath(), backupData); err != nil { return err } // Preserve the product's intentional permanent auto-update setting. settings["update.mode"] = json.RawMessage(`"none"`) data, err := json.MarshalIndent(settings, "", " ") if err != nil { return err } return securefile.Write(path, append(data, '\n')) } func ClearUserProxySettings() error { proxySettingsMu.Lock() defer proxySettingsMu.Unlock() path, err := resolveCursorSettingsPath() if err != nil { return err } backup, err := readSettingsBackup() if err != nil { return err } if len(backup.Applied) == 0 { return nil } // No ownership proof: preserve user values. settings, err := loadSettings(path) if err != nil { return fmt.Errorf("解析 Cursor 配置失败,原文件已保留: %w", err) } for key, applied := range backup.Applied { if rawEqual(settings[key], applied) { if previous, exists := backup.Original[key]; exists { settings[key] = previous } else { delete(settings, key) } } } data, err := json.MarshalIndent(settings, "", " ") if err != nil { return err } if err = securefile.Write(path, append(data, '\n')); err != nil { return err } if err = os.Remove(settingsBackupPath()); err != nil && !os.IsNotExist(err) { return err } return nil }