Skip to content

Commit 7092229

Browse files
committed
Active Windows mode on Windows
1 parent 3e1a0a4 commit 7092229

File tree

8 files changed

+47
-54
lines changed

8 files changed

+47
-54
lines changed

agent/agent.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ func (a *agent) handleReconnectingPTY(ctx context.Context, rawID string, conn ne
484484
}
485485
a.reconnectingPTYs.Store(id, rpty)
486486
go func() {
487+
// CommandContext isn't respected for Windows PTYs right now,
488+
// so we need to manually track the lifecycle.
487489
// When the context has been completed either:
488490
// 1. The timeout completed.
489491
// 2. The parent context was canceled.

site/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"react-dom": "17.0.2",
4242
"react-router-dom": "6.3.0",
4343
"swr": "1.2.2",
44+
"uuid": "^8.3.2",
4445
"xstate": "4.31.0",
4546
"xterm": "^4.18.0",
4647
"xterm-addon-fit": "^0.5.0",
@@ -65,6 +66,7 @@
6566
"@types/react": "17.0.44",
6667
"@types/react-dom": "17.0.16",
6768
"@types/superagent": "4.1.15",
69+
"@types/uuid": "^8.3.4",
6870
"@typescript-eslint/eslint-plugin": "5.21.0",
6971
"@typescript-eslint/parser": "5.21.0",
7072
"@xstate/cli": "0.1.7",

site/src/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export interface WorkspaceResource {
8787
export interface WorkspaceAgent {
8888
id: string
8989
name: string
90+
operating_system: string
9091
}
9192

9293
export interface APIKeyResponse {

site/src/pages/TerminalPage/TerminalPage.test.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { waitFor } from "@testing-library/react"
2-
import crypto from "crypto"
32
import "jest-canvas-mock"
43
import WS from "jest-websocket-mock"
54
import { rest } from "msw"
@@ -25,12 +24,6 @@ Object.defineProperty(window, "matchMedia", {
2524
})),
2625
})
2726

28-
Object.defineProperty(window, "crypto", {
29-
value: {
30-
randomUUID: () => crypto.randomUUID(),
31-
},
32-
})
33-
3427
Object.defineProperty(window, "TextEncoder", {
3528
value: TextEncoder,
3629
})

site/src/pages/TerminalPage/TerminalPage.tsx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { makeStyles } from "@material-ui/core/styles"
22
import { useMachine } from "@xstate/react"
33
import React from "react"
44
import { useLocation, useNavigate, useParams } from "react-router-dom"
5+
import { v4 as uuidv4 } from "uuid"
56
import * as XTerm from "xterm"
67
import { FitAddon } from "xterm-addon-fit"
78
import { WebLinksAddon } from "xterm-addon-web-links"
@@ -39,11 +40,7 @@ export const TerminalPage: React.FC<{
3940
// a round-trip, and must be a UUIDv4.
4041
const [reconnectionToken] = React.useState<string>(() => {
4142
const search = new URLSearchParams(location.search)
42-
let reconnect = search.get("reconnect")
43-
if (reconnect === null) {
44-
reconnect = crypto.randomUUID()
45-
}
46-
return reconnect
43+
return search.get("reconnect") ?? uuidv4()
4744
})
4845
const [terminalState, sendEvent] = useMachine(terminalMachine, {
4946
actions: {
@@ -59,6 +56,8 @@ export const TerminalPage: React.FC<{
5956
},
6057
})
6158
const isConnected = terminalState.matches("connected")
59+
const { organizationsError, workspaceError, workspaceAgentError, workspaceAgent, websocketError } =
60+
terminalState.context
6261

6362
// Create the terminal!
6463
React.useEffect(() => {
@@ -150,17 +149,17 @@ export const TerminalPage: React.FC<{
150149
terminal.options = {
151150
disableStdin: true,
152151
}
153-
if (terminalState.context.organizationsError instanceof Error) {
154-
terminal.writeln(Language.organizationsErrorMessagePrefix + terminalState.context.organizationsError.message)
152+
if (organizationsError instanceof Error) {
153+
terminal.writeln(Language.organizationsErrorMessagePrefix + organizationsError.message)
155154
}
156-
if (terminalState.context.workspaceError instanceof Error) {
157-
terminal.writeln(Language.workspaceErrorMessagePrefix + terminalState.context.workspaceError.message)
155+
if (workspaceError instanceof Error) {
156+
terminal.writeln(Language.workspaceErrorMessagePrefix + workspaceError.message)
158157
}
159-
if (terminalState.context.workspaceAgentError instanceof Error) {
160-
terminal.writeln(Language.workspaceAgentErrorMessagePrefix + terminalState.context.workspaceAgentError.message)
158+
if (workspaceAgentError instanceof Error) {
159+
terminal.writeln(Language.workspaceAgentErrorMessagePrefix + workspaceAgentError.message)
161160
}
162-
if (terminalState.context.websocketError instanceof Error) {
163-
terminal.writeln(Language.websocketErrorMessagePrefix + terminalState.context.websocketError.message)
161+
if (websocketError instanceof Error) {
162+
terminal.writeln(Language.websocketErrorMessagePrefix + websocketError.message)
164163
}
165164
return
166165
}
@@ -174,6 +173,7 @@ export const TerminalPage: React.FC<{
174173
terminal.focus()
175174
terminal.options = {
176175
disableStdin: false,
176+
windowsMode: workspaceAgent?.operating_system === "windows",
177177
}
178178

179179
// Update the terminal size post-fit.
@@ -185,10 +185,11 @@ export const TerminalPage: React.FC<{
185185
},
186186
})
187187
}, [
188-
terminalState.context.workspaceError,
189-
terminalState.context.organizationsError,
190-
terminalState.context.workspaceAgentError,
191-
terminalState.context.websocketError,
188+
workspaceError,
189+
organizationsError,
190+
workspaceAgentError,
191+
websocketError,
192+
workspaceAgent,
192193
terminal,
193194
fitAddon,
194195
isConnected,

site/src/testHelpers/entities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const MockWorkspace: Workspace = {
102102
export const MockWorkspaceAgent: WorkspaceAgent = {
103103
id: "test-workspace-agent",
104104
name: "a-workspace-agent",
105+
operating_system: "linux",
105106
}
106107

107108
export const MockWorkspaceResource: WorkspaceResource = {

site/src/xServices/terminal/terminalXService.ts

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,13 @@ export const terminalMachine =
150150
{
151151
services: {
152152
getOrganizations: API.getOrganizations,
153-
getWorkspace: async (context: TerminalContext) => {
153+
getWorkspace: async (context) => {
154154
if (!context.organizations || !context.workspaceName) {
155155
throw new Error("organizations or workspace not set")
156156
}
157157
return API.getWorkspace(context.organizations[0].id, context.username, context.workspaceName)
158158
},
159-
getWorkspaceAgent: async (context: TerminalContext) => {
159+
getWorkspaceAgent: async (context) => {
160160
if (!context.workspace || !context.workspaceName) {
161161
throw new Error("workspace or workspace name is not set")
162162
}
@@ -167,38 +167,29 @@ export const terminalMachine =
167167
const agentName = workspaceNameParts[1]
168168

169169
const resources = await API.getWorkspaceResources(context.workspace.latest_build.id)
170-
for (let i = 0; i < resources.length; i++) {
171-
const resource = resources[i]
172-
if (!resource.agents) {
173-
continue
174-
}
175-
if (resource.agents.length <= 0) {
176-
continue
177-
}
178-
if (!agentName) {
179-
return resource.agents[0]
180-
}
181-
for (let a = 0; a < resource.agents.length; a++) {
182-
const agent = resource.agents[a]
183-
if (agent.name !== agentName) {
184-
continue
170+
171+
const agent = resources
172+
.map((resource) => {
173+
if (!resource.agents || resource.agents.length < 1) {
174+
return
185175
}
186-
return agent
187-
}
176+
if (!agentName) {
177+
return resource.agents[0]
178+
}
179+
return resource.agents.find((agent) => agent.name === agentName)
180+
})
181+
.filter((a) => a)[0]
182+
if (!agent) {
183+
throw new Error("no agent found with id")
188184
}
189-
throw new Error("no agent found with id")
185+
return agent
190186
},
191-
connect: (context: TerminalContext) => (send) => {
187+
connect: (context) => (send) => {
192188
return new Promise<WebSocket>((resolve, reject) => {
193189
if (!context.workspaceAgent) {
194190
return reject("workspace agent is not set")
195191
}
196-
let proto = location.protocol
197-
if (proto === "https:") {
198-
proto = "wss:"
199-
} else {
200-
proto = "ws:"
201-
}
192+
const proto = location.protocol === "https:" ? "wss:" : "ws:"
202193
const socket = new WebSocket(
203194
`${proto}//${location.host}/api/v2/workspaceagents/${context.workspaceAgent.id}/pty?reconnect=${context.reconnection}`,
204195
)
@@ -275,9 +266,6 @@ export const terminalMachine =
275266
}
276267
context.websocket.send(new TextEncoder().encode(JSON.stringify(event.request)))
277268
},
278-
readMessage: () => {
279-
// Override this with the terminal writer!
280-
},
281269
disconnect: (context: TerminalContext) => {
282270
// Code 1000 is a successful exit!
283271
context.websocket?.close(1000)

site/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,6 +3194,11 @@
31943194
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
31953195
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
31963196

3197+
"@types/uuid@^8.3.4":
3198+
version "8.3.4"
3199+
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
3200+
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
3201+
31973202
"@types/webpack-env@^1.16.0":
31983203
version "1.16.3"
31993204
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.3.tgz#b776327a73e561b71e7881d0cd6d34a1424db86a"

0 commit comments

Comments
 (0)