Skip to content

Commit 3e27f67

Browse files
committed
Show agent chooser when there are multiple
1 parent 6654938 commit 3e27f67

File tree

2 files changed

+63
-77
lines changed

2 files changed

+63
-77
lines changed

src/commands.ts

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,52 @@ export class Commands {
2727
private readonly storage: Storage,
2828
) {}
2929

30+
/**
31+
* Find the requested agent if specified, otherwise return the agent if there
32+
* is only one or ask the user to pick if there are multiple. Return
33+
* undefined if the user cancels.
34+
*/
35+
public async maybeAskAgent(workspace: Workspace, filter?: string): Promise<WorkspaceAgent | undefined> {
36+
const agents = extractAgents(workspace)
37+
const filteredAgents = filter ? agents.filter((agent) => agent.name === filter) : agents
38+
if (filteredAgents.length === 0) {
39+
throw new Error("Workspace has no matching agents")
40+
} else if (filteredAgents.length === 1) {
41+
return filteredAgents[0]
42+
} else {
43+
const quickPick = vscode.window.createQuickPick()
44+
quickPick.title = "Select an agent"
45+
quickPick.busy = true
46+
const agentItems: vscode.QuickPickItem[] = filteredAgents.map((agent) => {
47+
let icon = "$(debug-start)"
48+
if (agent.status !== "connected") {
49+
icon = "$(debug-stop)"
50+
}
51+
return {
52+
alwaysShow: true,
53+
label: `${icon} ${agent.name}`,
54+
detail: `${agent.name} • Status: ${agent.status}`,
55+
}
56+
})
57+
quickPick.items = agentItems
58+
quickPick.busy = false
59+
quickPick.show()
60+
61+
const selected = await new Promise<WorkspaceAgent | undefined>((resolve) => {
62+
quickPick.onDidHide(() => resolve(undefined))
63+
quickPick.onDidChangeSelection((selected) => {
64+
if (selected.length < 1) {
65+
return resolve(undefined)
66+
}
67+
const agent = filteredAgents[quickPick.items.indexOf(selected[0])]
68+
resolve(agent)
69+
})
70+
})
71+
quickPick.dispose()
72+
return selected
73+
}
74+
}
75+
3076
/**
3177
* Ask the user for the URL, letting them choose from a list of recent URLs or
3278
* CODER_URL or enter a new one. Undefined means the user aborted.
@@ -376,58 +422,19 @@ export class Commands {
376422
})
377423
})
378424
if (!workspace) {
425+
// User declined to pick a workspace.
379426
return
380427
}
381428
workspaceOwner = workspace.owner_name
382429
workspaceName = workspace.name
383430

384-
const agents = extractAgents(workspace)
385-
386-
if (agents.length === 1) {
387-
folderPath = agents[0].expanded_directory
388-
workspaceAgent = agents[0].name
389-
} else if (agents.length > 0) {
390-
const agentQuickPick = vscode.window.createQuickPick()
391-
agentQuickPick.title = `Select an agent`
392-
393-
agentQuickPick.busy = true
394-
const lastAgents = agents
395-
const agentItems: vscode.QuickPickItem[] = agents.map((agent) => {
396-
let icon = "$(debug-start)"
397-
if (agent.status !== "connected") {
398-
icon = "$(debug-stop)"
399-
}
400-
return {
401-
alwaysShow: true,
402-
label: `${icon} ${agent.name}`,
403-
detail: `${agent.name} • Status: ${agent.status}`,
404-
}
405-
})
406-
agentQuickPick.items = agentItems
407-
agentQuickPick.busy = false
408-
agentQuickPick.show()
409-
410-
const agent = await new Promise<WorkspaceAgent | undefined>((resolve) => {
411-
agentQuickPick.onDidHide(() => {
412-
resolve(undefined)
413-
})
414-
agentQuickPick.onDidChangeSelection((selected) => {
415-
if (selected.length < 1) {
416-
return resolve(undefined)
417-
}
418-
const agent = lastAgents[agentQuickPick.items.indexOf(selected[0])]
419-
resolve(agent)
420-
})
421-
})
422-
423-
if (agent) {
424-
folderPath = agent.expanded_directory
425-
workspaceAgent = agent.name
426-
} else {
427-
folderPath = ""
428-
workspaceAgent = ""
429-
}
431+
const agent = await this.maybeAskAgent(workspace)
432+
if (!agent) {
433+
// User declined to pick an agent.
434+
return
430435
}
436+
folderPath = agent.expanded_directory
437+
workspaceAgent = agent.name
431438
} else {
432439
workspaceOwner = args[0] as string
433440
workspaceName = args[1] as string

src/remote.ts

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isAxiosError } from "axios"
22
import { Api } from "coder/site/src/api/api"
3-
import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
3+
import { Workspace } from "coder/site/src/api/typesGenerated"
44
import EventSource from "eventsource"
55
import find from "find-process"
66
import * as fs from "fs/promises"
@@ -11,6 +11,7 @@ import prettyBytes from "pretty-bytes"
1111
import * as semver from "semver"
1212
import * as vscode from "vscode"
1313
import { makeCoderSdk, startWorkspace, waitForBuild } from "./api"
14+
import { extractAgents } from "./api-helper"
1415
import { Commands } from "./commands"
1516
import { getHeaderCommand } from "./headers"
1617
import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
@@ -283,28 +284,13 @@ export class Remote {
283284

284285
// Pick an agent.
285286
this.storage.writeToCoderOutputChannel(`Finding agent for ${workspaceName}...`)
286-
const agents = workspace.latest_build.resources.reduce((acc, resource) => {
287-
return acc.concat(resource.agents || [])
288-
}, [] as WorkspaceAgent[])
289-
290-
// With no agent specified, pick the first one. Otherwise choose the
291-
// matching agent.
292-
let agent: WorkspaceAgent
293-
if (!parts.agent) {
294-
if (agents.length === 1) {
295-
agent = agents[0]
296-
} else {
297-
// TODO: Show the agent selector here instead.
298-
throw new Error("Invalid Coder SSH authority. An agent must be specified when there are multiple.")
299-
}
300-
} else {
301-
const matchingAgents = agents.filter((agent) => agent.name === parts.agent)
302-
if (matchingAgents.length !== 1) {
303-
// TODO: Show the agent selector here instead.
304-
throw new Error("Invalid Coder SSH authority. Agent not found.")
305-
}
306-
agent = matchingAgents[0]
287+
const gotAgent = await this.commands.maybeAskAgent(workspace, parts.agent)
288+
if (!gotAgent) {
289+
// User declined to pick an agent.
290+
await this.closeRemote()
291+
return
307292
}
293+
let agent = gotAgent // Reassign so it cannot be undefined in callbacks.
308294
this.storage.writeToCoderOutputChannel(`Found agent ${agent.name} with status ${agent.status}`)
309295

310296
// Do some janky setting manipulation.
@@ -462,17 +448,11 @@ export class Remote {
462448
async () => {
463449
await new Promise<void>((resolve) => {
464450
const updateEvent = workspaceUpdate.event((workspace) => {
465-
const agents = workspace.latest_build.resources.reduce((acc, resource) => {
466-
return acc.concat(resource.agents || [])
467-
}, [] as WorkspaceAgent[])
468451
if (!agent) {
469452
return
470453
}
454+
const agents = extractAgents(workspace)
471455
const found = agents.find((newAgent) => {
472-
if (!agent) {
473-
// This shouldn't be possible... just makes the types happy!
474-
return false
475-
}
476456
return newAgent.id === agent.id
477457
})
478458
if (!found) {
@@ -534,10 +514,9 @@ export class Remote {
534514
})
535515

536516
// Register the label formatter again because SSH overrides it!
537-
const agentName = agents.length > 1 ? agent.name : undefined
538517
disposables.push(
539518
vscode.extensions.onDidChange(() => {
540-
disposables.push(this.registerLabelFormatter(remoteAuthority, workspace.owner_name, workspace.name, agentName))
519+
disposables.push(this.registerLabelFormatter(remoteAuthority, workspace.owner_name, workspace.name, agent.name))
541520
}),
542521
)
543522

0 commit comments

Comments
 (0)