@@ -77,16 +77,20 @@ func PrettyPrint(i any) string {
7777
7878var ErrPromptTerminated = errdefs .Cancelled (errors .New ("prompt terminated" ))
7979
80- type promptOptions struct {
81- disableEcho bool
82- }
83-
84- type PromptOptions func (* promptOptions )
85-
86- func WithHideUserInput (t bool ) PromptOptions {
87- return func (p * promptOptions ) {
88- p .disableEcho = t
80+ // DisableInputEcho disables input echo on the provided io.ReadCloser.
81+ // This is useful when the user provides sensitive information like passwords.
82+ // The function returns a restore function that should be called to restore the
83+ // terminal state.
84+ func DisableInputEcho (in io.ReadCloser ) (restore func () error , err error ) {
85+ ins := streams .NewIn (in )
86+ oldState , err := term .SaveState (ins .FD ())
87+ if err != nil {
88+ return nil , err
89+ }
90+ restore = func () error {
91+ return term .RestoreTerminal (ins .FD (), oldState )
8992 }
93+ return restore , term .DisableEcho (ins .FD (), oldState )
9094}
9195
9296// PromptForInput requests input from the user.
@@ -96,29 +100,10 @@ func WithHideUserInput(t bool) PromptOptions {
96100// When the prompt returns an error, the caller should propagate the error up
97101// the stack and close the io.Reader used for the prompt which will prevent the
98102// background goroutine from blocking indefinitely.
99- func PromptForInput (ctx context.Context , ins * streams.In , outs * streams.Out , message string , opts ... PromptOptions ) (string , error ) {
100- options := promptOptions {}
101- for _ , o := range opts {
102- o (& options )
103- }
103+ func PromptForInput (ctx context.Context , in io.ReadCloser , out io.Writer , message string ) (string , error ) {
104+ _ , _ = fmt .Fprint (out , message )
104105
105- _ , _ = fmt .Fprint (outs , message )
106-
107- if options .disableEcho {
108- oldState , err := term .SaveState (ins .FD ())
109- if err != nil {
110- return "" , err
111- }
112- _ = term .DisableEcho (ins .FD (), oldState )
113- defer func () {
114- _ = term .RestoreTerminal (ins .FD (), oldState )
115- }()
116- }
117-
118- // On Windows, force the use of the regular OS stdin stream.
119- if runtime .GOOS == "windows" {
120- ins = streams .NewIn (os .Stdin )
121- }
106+ ins := streams .NewIn (in )
122107
123108 result := make (chan string )
124109
@@ -131,7 +116,7 @@ func PromptForInput(ctx context.Context, ins *streams.In, outs *streams.Out, mes
131116
132117 select {
133118 case <- ctx .Done ():
134- _ , _ = fmt .Fprintln (outs , "" )
119+ _ , _ = fmt .Fprintln (out , "" )
135120 return "" , ErrPromptTerminated
136121 case r := <- result :
137122 return r , nil
0 commit comments