@@ -10,6 +10,8 @@ export interface TerminalContext {
10
10
workspaceAgentError ?: Error | unknown
11
11
websocket ?: WebSocket
12
12
websocketError ?: Error | unknown
13
+ websocketURL ?: string
14
+ websocketURLError ?: Error | unknown
13
15
14
16
// Assigned by connecting!
15
17
// The workspace agent is entirely optional. If the agent is omitted the
@@ -19,6 +21,8 @@ export interface TerminalContext {
19
21
workspaceName ?: string
20
22
reconnection ?: string
21
23
command ?: string
24
+ // If baseURL is not.....
25
+ baseURL ?: string
22
26
}
23
27
24
28
export type TerminalEvent =
@@ -34,7 +38,7 @@ export type TerminalEvent =
34
38
| { type : "DISCONNECT" }
35
39
36
40
export const terminalMachine =
37
- /** @xstate -layout N4IgpgJg5mDOIC5QBcwCcC2BLAdgQwBsBlZPVAOhmWVygHk0o8csAvMrAex1gGIJuYcrgBunANZCqDJi3Y1u8JCAAOnWFgU5EoAB6IA7AE4AzOSMBGAEwBWCyYAsANgs2bVgDQgAnohNGbcgsnEIcHCwAOBwAGCKMHAF8Er1RMXEISMikwaloZZjYORV50NE40chUCMgAzcoxKHPy5Ip4dVXVNLm1lfQQIiLMIpxMbQYjYo2jXL18EawtyAwGwk1HTMdGklPRsfGJSCioaHCgAdXLxWBU8AGMwfkFhHDFJRuQLtCub+-a1DS07T69icVnILisDhspiccQ2sz8y3INmiqNGsMcBmiNm2IFSewyh2yuVOn2+dwepXKlWqyDqmHeZOuFL+nUBvUQILBEKhMLhowRCAMTkCIzWDkcEyMBhsBlx+PSByy7xO50uzPuAEEYDhkI8cEJRBJiUyfmBtWBdayAd0gZyjCFyFZbKjnC4jKYLIK7GYYqirCYBk5olYDIlknjdorMkccqrTRSLbqSmgyhUqrV6oz1Wak8hrV1uHb5g6nE6XdE3RYPSYvT5EWCA2s7E4sbZhfKo-sY0JbtwDbdVfrDS9jeQ+zgB-nlP9Cz09IhIcLyCZIUYotEDCZNxEDN7peY1ms4gYrNNBp20t2ieP+2BB7QU2maZmGROpwX2QuEEuy6uHOuMRbjue71ggdiLPY4phiYMoWNYl4EkqFDvveqAQLwZwAEoAJIACoAKKfraHI-gYkTkCGAFYmG0TbnWcw2A4YKmGskJno44RWIh0Y3qhg6QLwWEEZqAAixFFqRobViusKngYMpWEGgpQmWUFsSEcSOHRPHXsq-Hobwok4UQADCdAAHIWQRpl4RJ84gH0SmtuQDgTNWMJTDKJgqUEq4RNCljTOs3ERgqekUBAWCwAZgnmVZNl2TObIkd+zplrEYanvY0SwrCESCs6BiuaidGrgGTgekYSQRjgnAQHA7ThYSyrHHkjAFPI3RKKAs5fo5iAxIEXHMSMErWNiTiFa4K6ldi1ayu4FjhjsV4tbGJJql8GpgPZxYWNMDiubBdh2Ju7pGIK-jFW6rYiq4bZOLp63EvGOaJjq069Slknfq4jrOii51udiMpXbC5ATKi1ghNEljNs9yG9neD6nHtUlnhErmgqeIyosKazejNZ7+qMi3BiYiM9rek5oZA6NpeR0ROmGpgnhT0qCmNSxHhKW4BiGERUzeUUxSj6EMwN4HM5ukLLXBViRKGNhXVj64etM0JOG5YTC1kktORlp7hA4CtK2DYEALQQ8M5FKQd27OJTNVAA */
41
+ /** @xstate -layout N4IgpgJg5mDOIC5QBcwCcC2BLAdgQwBsBlZPVAOljGQFcAHAYggHscxLSLVNdCSz2VWnQDaABgC6iUHWawsyLK2kgAHogAcAVgCM5MQGYdAJgMaALOYDsV8zq0AaEAE9ExgGwHyATmPf3VmI6Yub+5gYAvhFO3Nj4xJyC1PTkMMgA6sxoANawdHgAxuxpijhQmTl5hWBMrOy4AG7M2cXUFbn5ReJSSCCy8orKveoI5qbkOhq23hozflruxuZOrghGXuZaRsFiWlbeWuYa7lEx6HF8iZTJdKltWR3Vd8il5Q9VRQzoaFnkdARkABmWQwz3aHzA3RU-QUShwKhGYy8k2msw080WyxcbmMYnIoW8hO8ZkMYisOlOIFivASAmer3BnTAAEEYDhkLU2ORGs1Whl3kzWWB2VDejDBvDhogdAYrFoJuYxJjdmJvCENCs3Fo8e4glpjFptWSDhpKdT4vwKCVcG9KoK2Rzvr9-kCQWCBdUhSLJNC5LChqARotNQgpniNAYtAdjFYDL4pidolTzjTLXyGWAAEZEZgFFrIACqACUADKc+o4JotZ45vPUYsl0UyP0ShGIWVWchGA27Akzbwh4LjDQmAk6AmBbxmlMWq7WsrpLO1-MNr5oH5oP4A5DAzA13Mr0tNvotuFthBWYyDo56ULGewWHTk0zGac8Wd0gqsNgFV7l7mVry5BfjgP7IMe4pnlKobmO45D3mG7ihFMBgBIOhgaPoizar4xIRv4b4XLSFAgWBNprhuW6unupFgL+EGngGaiIMG2IIPYtj4psMYaGIxh+Do9iEamVy0b+kAMOkRYAJIACoAKIMQMUGBogdiYZs3Z+N444GqYg42F4kY6LqmxWLMUaREm5qXJ+350agEAMEW8nMgAIkp-qSqpow6N45BIRYkYRgsBxyoOASdnG2gyu43jcUhwkfiR9niU5bnSUQADCADyAByeXyVlsmea20F+Zho6EksgU6WIGpsZMuj6OZfimLB0bmEltkUBAWCwGJjkMLlBVFSVPpiox3nMexV6Na+lI4MwEBwCoNnEWAvrKUxIwALRjCGu1yuQRpiCEBpyrshLdRt1zCFtXnnu4IabCdZ1nWMezalGFLWTOPVJMI7p2tUD1lT5hyDgYXjvR9KrxSZXV-e+AN3SkaSMk8862o8RRgypM1DiGL4+FY7iGvqniXvYSNnCjt1COj9wg0UlA0AURSwPAk3bdNQbQ-o3YLCYHGwcTRwBeE-GYjToRWDdab0jamNFF6yD4ztiD+PKgTdtYljuAEBjE3G+J+HFI7ah45IK3O1AZtmB71qWGt84gezofe+j2Lq7h+XxSFWXTRGK4NNqu09fFYUssyLPxCyOI1hjyh4CzW1b3jy8jIeialjkR9BhzweTiwBPqOm2Asg5TOYXbqmY6xISEtt0n1A155ABc+aheiGCYfhGAnthWIO33kOSxwRn3vgBFEURAA */
38
42
createMachine (
39
43
{
40
44
id : "terminalState" ,
@@ -50,6 +54,9 @@ export const terminalMachine =
50
54
getWorkspaceAgent : {
51
55
data : TypesGen . WorkspaceAgent
52
56
}
57
+ getWebsocketURL : {
58
+ data : string
59
+ }
53
60
connect : {
54
61
data : WebSocket
55
62
}
@@ -98,7 +105,7 @@ export const terminalMachine =
98
105
onDone : [
99
106
{
100
107
actions : [ "assignWorkspaceAgent" , "clearWorkspaceAgentError" ] ,
101
- target : "connecting " ,
108
+ target : "gettingWebSocketURL " ,
102
109
} ,
103
110
] ,
104
111
onError : [
@@ -109,6 +116,24 @@ export const terminalMachine =
109
116
] ,
110
117
} ,
111
118
} ,
119
+ gettingWebSocketURL : {
120
+ invoke : {
121
+ src : "getWebsocketURL" ,
122
+ id : "getWebsocketURL" ,
123
+ onDone : [
124
+ {
125
+ actions : [ "assignWebsocketURL" , "clearWebsocketURLError" ] ,
126
+ target : "connecting" ,
127
+ } ,
128
+ ] ,
129
+ onError : [
130
+ {
131
+ actions : "assignWebsocketURLError" ,
132
+ target : "disconnected" ,
133
+ } ,
134
+ ] ,
135
+ } ,
136
+ } ,
112
137
connecting : {
113
138
invoke : {
114
139
src : "connect" ,
@@ -185,17 +210,60 @@ export const terminalMachine =
185
210
}
186
211
return agent
187
212
} ,
213
+ getWebsocketURL : async ( context ) => {
214
+ if ( ! context . workspaceAgent ) {
215
+ throw new Error ( "workspace agent is not set" )
216
+ }
217
+ if ( ! context . reconnection ) {
218
+ throw new Error ( "reconnection ID is not set" )
219
+ }
220
+
221
+ let baseURL = context . baseURL || ""
222
+ if ( ! baseURL ) {
223
+ baseURL = `${ location . protocol } //${ location . host } `
224
+ }
225
+
226
+ const query = new URLSearchParams ( {
227
+ reconnect : context . reconnection ,
228
+ } )
229
+ if ( context . command ) {
230
+ query . set ( "command" , context . command )
231
+ }
232
+
233
+ const url = new URL ( baseURL )
234
+ url . protocol = url . protocol === "https:" ? "wss:" : "ws:"
235
+ if ( ! url . pathname . endsWith ( "/" ) ) {
236
+ url . pathname + "/"
237
+ }
238
+ url . pathname += `api/v2/workspaceagents/${ context . workspaceAgent . id } /pty`
239
+ url . search = "?" + query . toString ( )
240
+
241
+ // If the URL is just the primary API, we don't need a signed token to
242
+ // connect.
243
+ if ( ! context . baseURL ) {
244
+ return url . toString ( )
245
+ }
246
+
247
+ // Do ticket issuance and set the query parameter.
248
+ const tokenRes = await API . issueReconnectingPTYSignedToken ( {
249
+ url : url . toString ( ) ,
250
+ agentID : context . workspaceAgent . id ,
251
+ } )
252
+ query . set ( "coder_signed_app_token_23db1dde" , tokenRes . signed_token )
253
+ url . search = "?" + query . toString ( )
254
+
255
+ return url . toString ( )
256
+ } ,
188
257
connect : ( context ) => ( send ) => {
189
258
return new Promise < WebSocket > ( ( resolve , reject ) => {
190
259
if ( ! context . workspaceAgent ) {
191
260
return reject ( "workspace agent is not set" )
192
261
}
193
- const proto = location . protocol === "https:" ? "wss:" : "ws:"
194
- const commandQuery = context . command
195
- ? `&command=${ encodeURIComponent ( context . command ) } `
196
- : ""
197
- const url = `${ proto } //${ location . host } /api/v2/workspaceagents/${ context . workspaceAgent . id } /pty?reconnect=${ context . reconnection } ${ commandQuery } `
198
- const socket = new WebSocket ( url )
262
+ if ( ! context . websocketURL ) {
263
+ return reject ( "websocket URL is not set" )
264
+ }
265
+
266
+ const socket = new WebSocket ( context . websocketURL )
199
267
socket . binaryType = "arraybuffer"
200
268
socket . addEventListener ( "open" , ( ) => {
201
269
resolve ( socket )
@@ -254,6 +322,16 @@ export const terminalMachine =
254
322
...context ,
255
323
webSocketError : undefined ,
256
324
} ) ) ,
325
+ assignWebsocketURL : assign ( {
326
+ websocketURL : ( context , event ) => event . data ?? context . websocketURL ,
327
+ } ) ,
328
+ assignWebsocketURLError : assign ( {
329
+ websocketURLError : ( _ , event ) => event . data ,
330
+ } ) ,
331
+ clearWebsocketURLError : assign ( ( context : TerminalContext ) => ( {
332
+ ...context ,
333
+ websocketURLError : undefined ,
334
+ } ) ) ,
257
335
sendMessage : ( context , event ) => {
258
336
if ( ! context . websocket ) {
259
337
throw new Error ( "websocket doesn't exist" )
0 commit comments