package env import ( "encoding/json" "os" "path/filepath" "runtime" "strconv" "strings" ) type EnvSource map[string]string type LoadedEnv struct { AgentBin string AgentNode string AgentScript string CommandShell string Host string Port int RequiredKey string DefaultModel string Force bool ApproveMcps bool StrictModel bool Workspace string TimeoutMs int TLSCertPath string TLSKeyPath string SessionsLogPath string ChatOnlyWorkspace bool Verbose bool MaxMode bool ConfigDirs []string MultiPort bool WinCmdlineMax int } type AgentCommand struct { Command string Args []string Env map[string]string WindowsVerbatimArguments bool AgentScriptPath string ConfigDir string } func getEnvVal(e EnvSource, names []string) string { for _, name := range names { if v, ok := e[name]; ok && strings.TrimSpace(v) != "" { return strings.TrimSpace(v) } } return "" } func envBool(e EnvSource, names []string, def bool) bool { raw := getEnvVal(e, names) if raw == "" { return def } switch strings.ToLower(raw) { case "1", "true", "yes", "on": return true case "0", "false", "no", "off": return false } return def } func envInt(e EnvSource, names []string, def int) int { raw := getEnvVal(e, names) if raw == "" { return def } v, err := strconv.Atoi(raw) if err != nil { return def } return v } func normalizeModelId(raw string) string { raw = strings.TrimSpace(raw) if raw == "" { return "auto" } parts := strings.Split(raw, "/") last := parts[len(parts)-1] if last == "" { return "auto" } return last } func resolveAbs(raw, cwd string) string { if raw == "" { return "" } if filepath.IsAbs(raw) { return raw } return filepath.Join(cwd, raw) } func isAuthenticatedAccountDir(dir string) bool { data, err := os.ReadFile(filepath.Join(dir, "cli-config.json")) if err != nil { return false } var cfg struct { AuthInfo *struct { Email string `json:"email"` } `json:"authInfo"` } if err := json.Unmarshal(data, &cfg); err != nil { return false } return cfg.AuthInfo != nil && cfg.AuthInfo.Email != "" } func discoverAccountDirs(homeDir string) []string { if homeDir == "" { return nil } accountsDir := filepath.Join(homeDir, ".cursor-api-proxy", "accounts") entries, err := os.ReadDir(accountsDir) if err != nil { return nil } var dirs []string for _, e := range entries { if !e.IsDir() { continue } dir := filepath.Join(accountsDir, e.Name()) if isAuthenticatedAccountDir(dir) { dirs = append(dirs, dir) } } return dirs } func OsEnvToMap() EnvSource { m := make(EnvSource) for _, kv := range os.Environ() { parts := strings.SplitN(kv, "=", 2) if len(parts) == 2 { m[parts[0]] = parts[1] } } return m } func LoadEnvConfig(e EnvSource, cwd string) LoadedEnv { if e == nil { e = OsEnvToMap() } if cwd == "" { var err error cwd, err = os.Getwd() if err != nil { cwd = "." } } host := getEnvVal(e, []string{"CURSOR_BRIDGE_HOST"}) if host == "" { host = "127.0.0.1" } port := envInt(e, []string{"CURSOR_BRIDGE_PORT"}, 8765) if port <= 0 { port = 8765 } home := getEnvVal(e, []string{"HOME", "USERPROFILE"}) sessionsLogPath := func() string { if p := resolveAbs(getEnvVal(e, []string{"CURSOR_BRIDGE_SESSIONS_LOG"}), cwd); p != "" { return p } if home != "" { return filepath.Join(home, ".cursor-api-proxy", "sessions.log") } return filepath.Join(cwd, "sessions.log") }() var configDirs []string if raw := getEnvVal(e, []string{"CURSOR_CONFIG_DIRS", "CURSOR_ACCOUNT_DIRS"}); raw != "" { for _, d := range strings.Split(raw, ",") { d = strings.TrimSpace(d) if d != "" { if p := resolveAbs(d, cwd); p != "" { configDirs = append(configDirs, p) } } } } if len(configDirs) == 0 { configDirs = discoverAccountDirs(home) } winMax := envInt(e, []string{"CURSOR_BRIDGE_WIN_CMDLINE_MAX"}, 30000) if winMax < 4096 { winMax = 4096 } if winMax > 32700 { winMax = 32700 } agentBin := getEnvVal(e, []string{"CURSOR_AGENT_BIN", "CURSOR_CLI_BIN", "CURSOR_CLI_PATH"}) if agentBin == "" { agentBin = "agent" } commandShell := getEnvVal(e, []string{"COMSPEC"}) if commandShell == "" { commandShell = "cmd.exe" } workspace := resolveAbs(getEnvVal(e, []string{"CURSOR_BRIDGE_WORKSPACE"}), cwd) if workspace == "" { workspace = cwd } return LoadedEnv{ AgentBin: agentBin, AgentNode: getEnvVal(e, []string{"CURSOR_AGENT_NODE"}), AgentScript: getEnvVal(e, []string{"CURSOR_AGENT_SCRIPT"}), CommandShell: commandShell, Host: host, Port: port, RequiredKey: getEnvVal(e, []string{"CURSOR_BRIDGE_API_KEY"}), DefaultModel: normalizeModelId(getEnvVal(e, []string{"CURSOR_BRIDGE_DEFAULT_MODEL"})), Force: envBool(e, []string{"CURSOR_BRIDGE_FORCE"}, false), ApproveMcps: envBool(e, []string{"CURSOR_BRIDGE_APPROVE_MCPS"}, false), StrictModel: envBool(e, []string{"CURSOR_BRIDGE_STRICT_MODEL"}, true), Workspace: workspace, TimeoutMs: envInt(e, []string{"CURSOR_BRIDGE_TIMEOUT_MS"}, 300000), TLSCertPath: resolveAbs(getEnvVal(e, []string{"CURSOR_BRIDGE_TLS_CERT"}), cwd), TLSKeyPath: resolveAbs(getEnvVal(e, []string{"CURSOR_BRIDGE_TLS_KEY"}), cwd), SessionsLogPath: sessionsLogPath, ChatOnlyWorkspace: envBool(e, []string{"CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE"}, true), Verbose: envBool(e, []string{"CURSOR_BRIDGE_VERBOSE"}, false), MaxMode: envBool(e, []string{"CURSOR_BRIDGE_MAX_MODE"}, false), ConfigDirs: configDirs, MultiPort: envBool(e, []string{"CURSOR_BRIDGE_MULTI_PORT"}, false), WinCmdlineMax: winMax, } } func ResolveAgentCommand(cmd string, args []string, e EnvSource, cwd string) AgentCommand { if e == nil { e = OsEnvToMap() } loaded := LoadEnvConfig(e, cwd) cloneEnv := func() map[string]string { m := make(map[string]string, len(e)) for k, v := range e { m[k] = v } return m } if runtime.GOOS == "windows" { if loaded.AgentNode != "" && loaded.AgentScript != "" { agentScriptPath := loaded.AgentScript if !filepath.IsAbs(agentScriptPath) { agentScriptPath = filepath.Join(cwd, agentScriptPath) } agentDir := filepath.Dir(agentScriptPath) configDir := filepath.Join(agentDir, "..", "data", "config") env2 := cloneEnv() env2["CURSOR_INVOKED_AS"] = "agent.cmd" ac := AgentCommand{ Command: loaded.AgentNode, Args: append([]string{loaded.AgentScript}, args...), Env: env2, AgentScriptPath: agentScriptPath, } if _, err := os.Stat(filepath.Join(configDir, "cli-config.json")); err == nil { ac.ConfigDir = configDir } return ac } if strings.HasSuffix(strings.ToLower(cmd), ".cmd") { cmdResolved := cmd if !filepath.IsAbs(cmd) { cmdResolved = filepath.Join(cwd, cmd) } dir := filepath.Dir(cmdResolved) nodeBin := filepath.Join(dir, "node.exe") script := filepath.Join(dir, "index.js") if _, err1 := os.Stat(nodeBin); err1 == nil { if _, err2 := os.Stat(script); err2 == nil { configDir := filepath.Join(dir, "..", "data", "config") env2 := cloneEnv() env2["CURSOR_INVOKED_AS"] = "agent.cmd" ac := AgentCommand{ Command: nodeBin, Args: append([]string{script}, args...), Env: env2, AgentScriptPath: script, } if _, err := os.Stat(filepath.Join(configDir, "cli-config.json")); err == nil { ac.ConfigDir = configDir } return ac } } quotedArgs := make([]string, len(args)) for i, a := range args { if strings.Contains(a, " ") { quotedArgs[i] = `"` + a + `"` } else { quotedArgs[i] = a } } cmdLine := `""` + cmd + `" ` + strings.Join(quotedArgs, " ") + `"` return AgentCommand{ Command: loaded.CommandShell, Args: []string{"/d", "/s", "/c", cmdLine}, Env: cloneEnv(), WindowsVerbatimArguments: true, } } } return AgentCommand{Command: cmd, Args: args, Env: cloneEnv()} }