package cli import ( "bufio" "fmt" "io" "net/url" "os" "strings" "golang.org/x/term" ) type Candidate struct { Value string Source string } func ResolveProfileName(flagProfile, envProfile, currentProfile string) string { for _, candidate := range []string{flagProfile, envProfile, currentProfile} { if value := strings.TrimSpace(candidate); value != "" { return value } } return "default" } func FirstNonEmpty(candidates ...Candidate) (string, string) { for _, candidate := range candidates { if value := strings.TrimSpace(candidate.Value); value != "" { return value, candidate.Source } } return "", "" } func ValidateBaseURL(raw string) error { parsed, err := url.Parse(raw) if err != nil { return fmt.Errorf("parse URL: %w", err) } if parsed.Scheme == "" || parsed.Host == "" { return fmt.Errorf("must include scheme and host") } return nil } func PromptLine(reader *bufio.Reader, w io.Writer, label, defaultValue string) (string, error) { if defaultValue != "" { fmt.Fprintf(w, "%s [%s]: ", label, defaultValue) } else { fmt.Fprintf(w, "%s: ", label) } line, err := reader.ReadString('\n') if err != nil && err != io.EOF { return "", err } line = strings.TrimSpace(line) if line == "" { return defaultValue, nil } return line, nil } func PromptSecret(stdin *os.File, w io.Writer, label string, hasStoredSecret bool, storedSecret string) (string, error) { if hasStoredSecret { fmt.Fprintf(w, "%s [stored, leave blank to keep]: ", label) } else { fmt.Fprintf(w, "%s: ", label) } if term.IsTerminal(int(stdin.Fd())) { secret, err := term.ReadPassword(int(stdin.Fd())) fmt.Fprintln(w) if err != nil { return "", err } value := strings.TrimSpace(string(secret)) if value == "" && hasStoredSecret { return storedSecret, nil } return value, nil } reader := bufio.NewReader(stdin) line, err := reader.ReadString('\n') if err != nil && err != io.EOF { return "", err } value := strings.TrimSpace(line) if value == "" && hasStoredSecret { return storedSecret, nil } return value, nil }