package main import ( "encoding/json" "fmt" "os/exec" tea "github.com/charmbracelet/bubbletea" ) // Constants for SDM command exit codes const ( exitCodeAuthRequired = 9 ) // connection represents a SDM connection from the status command. type connection struct { Name string `json:"name"` Type string `json:"type"` Address string `json:"address"` Connected bool `json:"connected"` WebURL *string `json:"web_url"` } // statusMsg is sent when the status fetch operation completes. type statusMsg struct { connections []connection err error } // authRequiredMsg is sent when authentication is required. type authRequiredMsg struct{} // loginMsg is sent when the login operation completes. type loginMsg struct { err error } // connectMsg is sent when a connect operation completes. type connectMsg struct { name string address string err error } // disconnectMsg is sent when a disconnect operation completes. type disconnectMsg struct { name string err error } // checkSDMBinary verifies that the sdm binary is available in PATH. func checkSDMBinary() error { _, err := exec.LookPath("sdm") if err != nil { return fmt.Errorf("sdm binary not found in PATH. Please install sdm and try again") } return nil } // fetchStatus executes the SDM status command and returns the result as a message. // If authentication is required (exit code 9), it returns authRequiredMsg. func fetchStatus() tea.Msg { cmd := exec.Command("sdm", "status", "-j") output, err := cmd.CombinedOutput() if err != nil { if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == exitCodeAuthRequired { return authRequiredMsg{} } return statusMsg{err: fmt.Errorf("%v: %s", err, string(output))} } var connections []connection if err := json.Unmarshal(output, &connections); err != nil { return statusMsg{err: err} } return statusMsg{connections: connections} } // loginSDM executes the SDM login command with the provided email. func loginSDM(email string) tea.Msg { cmd := exec.Command("sdm", "login", "--email", email) output, err := cmd.CombinedOutput() if err != nil { return loginMsg{err: fmt.Errorf("%v: %s", err, string(output))} } return loginMsg{} } // connectSDM executes the SDM connect command and waits for it to complete. func connectSDM(name, address string) tea.Msg { cmd := exec.Command("sdm", "connect", name) output, err := cmd.CombinedOutput() if err != nil { return connectMsg{name: name, address: address, err: fmt.Errorf("%v: %s", err, string(output))} } return connectMsg{name: name, address: address} } // disconnectSDM executes the SDM disconnect command for a specific connection. func disconnectSDM(name string) tea.Msg { cmd := exec.Command("sdm", "disconnect", name) output, err := cmd.CombinedOutput() if err != nil { return disconnectMsg{name: name, err: fmt.Errorf("%v: %s", err, string(output))} } return disconnectMsg{name: name} } // getAddress returns the appropriate address for a connection, preferring WebURL over Address. func getAddress(conn connection) string { if conn.WebURL != nil { return *conn.WebURL } return conn.Address } // sendNotification sends a desktop notification using notify-send. func sendNotification(title, message string) { cmd := exec.Command("notify-send", title, message) cmd.Run() // Ignore errors - notification is non-critical }