89 lines
2.4 KiB
Go
89 lines
2.4 KiB
Go
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
|
|
}
|