4
4
"bufio"
5
5
"context"
6
6
"encoding/json"
7
- "fmt"
8
7
"io"
9
8
"os"
10
9
"strings"
@@ -15,6 +14,7 @@ import (
15
14
"golang.org/x/xerrors"
16
15
17
16
"github.com/coder/coder/v2/cli/cliui"
17
+ "github.com/coder/coder/v2/coderd/util/slice"
18
18
"github.com/coder/coder/v2/codersdk"
19
19
"github.com/coder/coder/v2/codersdk/workspacesdk"
20
20
"github.com/coder/coder/v2/pty"
@@ -96,6 +96,7 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
96
96
}else {
97
97
reconnectID = uuid .New ()
98
98
}
99
+
99
100
ws ,agt ,err := getWorkspaceAndAgent (ctx ,inv ,client ,true ,args .NamedWorkspace )
100
101
if err != nil {
101
102
return err
@@ -118,14 +119,6 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
118
119
}
119
120
}
120
121
121
- if err := cliui .Agent (ctx ,inv .Stderr ,agt .ID , cliui.AgentOptions {
122
- FetchInterval :0 ,
123
- Fetch :client .WorkspaceAgent ,
124
- Wait :false ,
125
- });err != nil {
126
- return err
127
- }
128
-
129
122
// Get the width and height of the terminal.
130
123
var termWidth ,termHeight uint16
131
124
stdoutFile ,validOut := inv .Stdout .(* os.File )
@@ -149,6 +142,15 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
149
142
}()
150
143
}
151
144
145
+ // If a user does not specify a command, we'll assume they intend to open an
146
+ // interactive shell.
147
+ var backend string
148
+ if isOneShotCommand (args .Command ) {
149
+ // If the user specified a command, we'll prefer to use the buffered method.
150
+ // The screen backend is not well suited for one-shot commands.
151
+ backend = "buffered"
152
+ }
153
+
152
154
conn ,err := workspacesdk .New (client ).AgentReconnectingPTY (ctx , workspacesdk.WorkspaceAgentReconnectingPTYOpts {
153
155
AgentID :agt .ID ,
154
156
Reconnect :reconnectID ,
@@ -157,14 +159,13 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
157
159
ContainerUser :args .ContainerUser ,
158
160
Width :termWidth ,
159
161
Height :termHeight ,
162
+ BackendType :backend ,
160
163
})
161
164
if err != nil {
162
165
return xerrors .Errorf ("open reconnecting PTY: %w" ,err )
163
166
}
164
167
defer conn .Close ()
165
168
166
- cliui .Infof (inv .Stderr ,"Connected to %s (agent id: %s)" ,args .NamedWorkspace ,agt .ID )
167
- cliui .Infof (inv .Stderr ,"Reconnect ID: %s" ,reconnectID )
168
169
closeUsage := client .UpdateWorkspaceUsageWithBodyContext (ctx ,ws .ID , codersdk.PostWorkspaceUsageRequest {
169
170
AgentID :agt .ID ,
170
171
AppName :codersdk .UsageAppNameReconnectingPty ,
@@ -210,7 +211,21 @@ func handleRPTY(inv *serpent.Invocation, client *codersdk.Client, args handleRPT
210
211
_ ,_ = io .Copy (inv .Stdout ,conn )
211
212
cancel ()
212
213
_ = conn .Close ()
213
- _ ,_ = fmt .Fprintf (inv .Stderr ,"Connection closed\n " )
214
214
215
215
return nil
216
216
}
217
+
218
+ var knownShells = []string {"ash" ,"bash" ,"csh" ,"dash" ,"fish" ,"ksh" ,"powershell" ,"pwsh" ,"zsh" }
219
+
220
+ func isOneShotCommand (cmd []string )bool {
221
+ // If the command is empty, we'll assume the user wants to open a shell.
222
+ if len (cmd )== 0 {
223
+ return false
224
+ }
225
+ // If the command is a single word, and that word is a known shell, we'll
226
+ // assume the user wants to open a shell.
227
+ if len (cmd )== 1 && slice .Contains (knownShells ,cmd [0 ]) {
228
+ return false
229
+ }
230
+ return true
231
+ }