package config import ( "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" ) type Config struct { Port int `yaml:"port"` CursorCLIPath string `yaml:"cursor_cli_path"` DefaultModel string `yaml:"default_model"` Timeout int `yaml:"timeout"` MaxConcurrent int `yaml:"max_concurrent"` UseACP bool `yaml:"use_acp"` ChatOnlyWorkspace bool `yaml:"chat_only_workspace"` LogLevel string `yaml:"log_level"` AvailableModels []string `yaml:"available_models,omitempty"` } // Defaults returns a Config populated with default values. // // ChatOnlyWorkspace defaults to true. This is the cursor-api-proxy posture: // every Cursor CLI / ACP child is spawned in an empty temp directory with // HOME / CURSOR_CONFIG_DIR overridden so it cannot see the host user's real // project files or global ~/.cursor rules. Set to false only if you really // want the Cursor agent to have access to the cwd where cursor-adapter // started. func Defaults() Config { return Config{ Port: 8976, CursorCLIPath: "agent", DefaultModel: "auto", Timeout: 300, MaxConcurrent: 5, UseACP: false, ChatOnlyWorkspace: true, LogLevel: "INFO", } } // Load reads a YAML config file from path. If path is empty it defaults to // ~/.cursor-adapter/config.yaml. When the file does not exist, a config with // default values is returned without an error. func Load(path string) (*Config, error) { if path == "" { home, err := os.UserHomeDir() if err != nil { return nil, fmt.Errorf("resolving home directory: %w", err) } path = filepath.Join(home, ".cursor-adapter", "config.yaml") } data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { c := Defaults() return &c, nil } return nil, fmt.Errorf("reading config file %s: %w", path, err) } cfg := Defaults() if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parsing config file %s: %w", path, err) } if err := cfg.validate(); err != nil { return nil, fmt.Errorf("validating config: %w", err) } return &cfg, nil } func (c *Config) validate() error { if c.Port <= 0 { return fmt.Errorf("port must be > 0, got %d", c.Port) } if c.CursorCLIPath == "" { return fmt.Errorf("cursor_cli_path must not be empty") } if c.Timeout <= 0 { return fmt.Errorf("timeout must be > 0, got %d", c.Timeout) } return nil }