Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f95a72f

Browse files
committed
Merge branch 'main' into Liorba/main
2 parents d46db51 + 9b936cd commit f95a72f

10 files changed

+503
-168
lines changed

package.json

+25-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"displayName": "Coder Remote",
55
"description": "Open any workspace with a single click.",
66
"repository": "https://github.com/coder/vscode-coder",
7-
"version": "0.1.17",
7+
"version": "0.1.18",
88
"engines": {
99
"vscode": "^1.73.0"
1010
},
@@ -111,6 +111,12 @@
111111
},
112112
{
113113
"command": "coder.open",
114+
"title": "Open Workspace",
115+
"icon": "$(play)",
116+
"category": "Coder"
117+
},
118+
{
119+
"command": "coder.openFromSidebar",
114120
"title": "Coder: Open Workspace",
115121
"icon": "$(play)"
116122
},
@@ -167,20 +173,31 @@
167173
],
168174
"view/item/context": [
169175
{
170-
"command": "coder.open",
171-
"when": "coder.authenticated && view == myWorkspaces || coder.authenticated && view == allWorkspaces",
176+
"command": "coder.openFromSidebar",
177+
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderAgent",
172178
"group": "inline"
173179
},
174180
{
175181
"command": "coder.navigateToWorkspace",
176-
"when": "coder.authenticated && view == myWorkspaces || coder.authenticated && view == allWorkspaces",
182+
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
177183
"group": "inline"
178184
},
179185
{
180186
"command": "coder.navigateToWorkspaceSettings",
181-
"when": "coder.authenticated && view == myWorkspaces || coder.authenticated && view == allWorkspaces",
187+
"when": "coder.authenticated && viewItem == coderWorkspaceSingleAgent || coder.authenticated && viewItem == coderWorkspaceMultipleAgents",
182188
"group": "inline"
183189
}
190+
],
191+
"statusBar/remoteIndicator": [
192+
{
193+
"command": "coder.open",
194+
"group": "remote_11_ssh_coder@1"
195+
},
196+
{
197+
"command": "coder.createWorkspace",
198+
"group": "remote_11_ssh_coder@2",
199+
"when": "coder.authenticated"
200+
}
184201
]
185202
}
186203
},
@@ -241,6 +258,7 @@
241258
"tar-fs": "^2.1.1",
242259
"which": "^2.0.2",
243260
"ws": "^8.11.0",
244-
"yaml": "^1.10.0"
261+
"yaml": "^1.10.0",
262+
"zod": "^3.21.4"
245263
}
246-
}
264+
}

src/api-helper.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
2+
import { z } from "zod"
23

3-
export function extractAgentsAndFolderPath(
4-
workspace: Workspace,
5-
): [agents: WorkspaceAgent[], folderPath: string | undefined] {
6-
// TODO: multiple agent support
4+
export function extractAgents(workspace: Workspace): WorkspaceAgent[] {
75
const agents = workspace.latest_build.resources.reduce((acc, resource) => {
86
return acc.concat(resource.agents || [])
97
}, [] as WorkspaceAgent[])
108

11-
let folderPath = undefined
12-
if (agents.length === 1) {
13-
folderPath = agents[0].expanded_directory
14-
}
15-
return [agents, folderPath]
9+
return agents
1610
}
11+
12+
export const AgentMetadataEventSchema = z.object({
13+
result: z.object({
14+
collected_at: z.string(),
15+
age: z.number(),
16+
value: z.string(),
17+
error: z.string(),
18+
}),
19+
description: z.object({
20+
display_name: z.string(),
21+
key: z.string(),
22+
script: z.string(),
23+
interval: z.number(),
24+
timeout: z.number(),
25+
}),
26+
})
27+
28+
export const AgentMetadataEventSchemaArray = z.array(AgentMetadataEventSchema)
29+
30+
export type AgentMetadataEvent = z.infer<typeof AgentMetadataEventSchema>

src/commands.ts

+135-72
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import axios from "axios"
22
import { getAuthenticatedUser, getWorkspaces, updateWorkspaceVersion } from "coder/site/src/api/api"
3-
import { Workspace } from "coder/site/src/api/typesGenerated"
3+
import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
44
import * as vscode from "vscode"
5-
import { extractAgentsAndFolderPath } from "./api-helper"
5+
import { extractAgents } from "./api-helper"
66
import { Remote } from "./remote"
77
import { Storage } from "./storage"
8-
import { WorkspaceTreeItem } from "./workspacesProvider"
8+
import { OpenableTreeItem } from "./workspacesProvider"
99

1010
export class Commands {
1111
public constructor(private readonly vscodeProposed: typeof vscode, private readonly storage: Storage) {}
@@ -118,7 +118,7 @@ export class Commands {
118118
await vscode.commands.executeCommand("vscode.open", uri)
119119
}
120120

121-
public async navigateToWorkspace(workspace: WorkspaceTreeItem) {
121+
public async navigateToWorkspace(workspace: OpenableTreeItem) {
122122
if (workspace) {
123123
const uri = this.storage.getURL() + `/@${workspace.workspaceOwner}/${workspace.workspaceName}`
124124
await vscode.commands.executeCommand("vscode.open", uri)
@@ -130,7 +130,7 @@ export class Commands {
130130
}
131131
}
132132

133-
public async navigateToWorkspaceSettings(workspace: WorkspaceTreeItem) {
133+
public async navigateToWorkspaceSettings(workspace: OpenableTreeItem) {
134134
if (workspace) {
135135
const uri = this.storage.getURL() + `/@${workspace.workspaceOwner}/${workspace.workspaceName}/settings`
136136
await vscode.commands.executeCommand("vscode.open", uri)
@@ -143,9 +143,21 @@ export class Commands {
143143
}
144144
}
145145

146+
public async openFromSidebar(treeItem: OpenableTreeItem) {
147+
if (treeItem) {
148+
await openWorkspace(
149+
treeItem.workspaceOwner,
150+
treeItem.workspaceName,
151+
treeItem.workspaceAgent,
152+
treeItem.workspaceFolderPath,
153+
)
154+
}
155+
}
156+
146157
public async open(...args: unknown[]): Promise<void> {
147158
let workspaceOwner: string
148159
let workspaceName: string
160+
let workspaceAgent: string | undefined
149161
let folderPath: string | undefined
150162

151163
if (args.length === 0) {
@@ -200,83 +212,61 @@ export class Commands {
200212
workspaceOwner = workspace.owner_name
201213
workspaceName = workspace.name
202214

203-
const [, folderPathExtracted] = extractAgentsAndFolderPath(workspace)
204-
folderPath = folderPathExtracted
205-
} else if (args.length === 2) {
206-
// opening a workspace from the sidebar
207-
const workspaceTreeItem = args[0] as WorkspaceTreeItem
208-
workspaceOwner = workspaceTreeItem.workspaceOwner
209-
workspaceName = workspaceTreeItem.workspaceName
210-
folderPath = workspaceTreeItem.workspaceFolderPath
211-
} else {
212-
workspaceOwner = args[0] as string
213-
workspaceName = args[1] as string
214-
// workspaceAgent is reserved for args[2], but multiple agents aren't supported yet.
215-
folderPath = args[3] as string | undefined
216-
}
217-
218-
// A workspace can have multiple agents, but that's handled
219-
// when opening a workspace unless explicitly specified.
220-
const remoteAuthority = `ssh-remote+${Remote.Prefix}${workspaceOwner}--${workspaceName}`
215+
const agents = extractAgents(workspace)
221216

222-
let newWindow = true
223-
// Open in the existing window if no workspaces are open.
224-
if (!vscode.workspace.workspaceFolders?.length) {
225-
newWindow = false
226-
}
217+
if (agents.length === 1) {
218+
folderPath = agents[0].expanded_directory
219+
workspaceAgent = agents[0].name
220+
} else if (agents.length > 0) {
221+
const agentQuickPick = vscode.window.createQuickPick()
222+
agentQuickPick.title = `Select an agent`
227223

228-
// If a folder isn't specified, we can try to open a recently opened folder.
229-
if (!folderPath) {
230-
const output: {
231-
workspaces: { folderUri: vscode.Uri; remoteAuthority: string }[]
232-
} = await vscode.commands.executeCommand("_workbench.getRecentlyOpened")
233-
const opened = output.workspaces.filter(
234-
// Filter out `/` since that's added below.
235-
(opened) => opened.folderUri?.authority === remoteAuthority,
236-
)
237-
if (opened.length > 0) {
238-
let selected: (typeof opened)[0]
224+
agentQuickPick.busy = true
225+
const lastAgents = agents
226+
const agentItems: vscode.QuickPickItem[] = agents.map((agent) => {
227+
let icon = "$(debug-start)"
228+
if (agent.status !== "connected") {
229+
icon = "$(debug-stop)"
230+
}
231+
return {
232+
alwaysShow: true,
233+
label: `${icon} ${agent.name}`,
234+
detail: `${agent.name} • Status: ${agent.status}`,
235+
}
236+
})
237+
agentQuickPick.items = agentItems
238+
agentQuickPick.busy = false
239+
agentQuickPick.show()
239240

240-
if (opened.length > 1) {
241-
const items: vscode.QuickPickItem[] = opened.map((folder): vscode.QuickPickItem => {
242-
return {
243-
label: folder.folderUri.path,
244-
}
241+
const agent = await new Promise<WorkspaceAgent | undefined>((resolve) => {
242+
agentQuickPick.onDidHide(() => {
243+
resolve(undefined)
245244
})
246-
const item = await vscode.window.showQuickPick(items, {
247-
title: "Select a recently opened folder",
245+
agentQuickPick.onDidChangeSelection((selected) => {
246+
if (selected.length < 1) {
247+
return resolve(undefined)
248+
}
249+
const agent = lastAgents[agentQuickPick.items.indexOf(selected[0])]
250+
resolve(agent)
248251
})
249-
if (!item) {
250-
return
251-
}
252-
selected = opened[items.indexOf(item)]
252+
})
253+
254+
if (agent) {
255+
folderPath = agent.expanded_directory
256+
workspaceAgent = agent.name
253257
} else {
254-
selected = opened[0]
258+
folderPath = ""
259+
workspaceAgent = ""
255260
}
256-
257-
folderPath = selected.folderUri.path
258261
}
262+
} else {
263+
workspaceOwner = args[0] as string
264+
workspaceName = args[1] as string
265+
// workspaceAgent is reserved for args[2], but multiple agents aren't supported yet.
266+
folderPath = args[3] as string | undefined
259267
}
260268

261-
if (folderPath) {
262-
await vscode.commands.executeCommand(
263-
"vscode.openFolder",
264-
vscode.Uri.from({
265-
scheme: "vscode-remote",
266-
authority: remoteAuthority,
267-
path: folderPath,
268-
}),
269-
// Open this in a new window!
270-
newWindow,
271-
)
272-
return
273-
}
274-
275-
// This opens the workspace without an active folder opened.
276-
await vscode.commands.executeCommand("vscode.newWindow", {
277-
remoteAuthority: remoteAuthority,
278-
reuseWindow: !newWindow,
279-
})
269+
await openWorkspace(workspaceOwner, workspaceName, workspaceAgent, folderPath)
280270
}
281271

282272
public async updateWorkspace(): Promise<void> {
@@ -297,3 +287,76 @@ export class Commands {
297287
}
298288
}
299289
}
290+
291+
async function openWorkspace(
292+
workspaceOwner: string,
293+
workspaceName: string,
294+
workspaceAgent: string | undefined,
295+
folderPath: string | undefined,
296+
) {
297+
// A workspace can have multiple agents, but that's handled
298+
// when opening a workspace unless explicitly specified.
299+
let remoteAuthority = `ssh-remote+${Remote.Prefix}${workspaceOwner}--${workspaceName}`
300+
if (workspaceAgent) {
301+
remoteAuthority += `--${workspaceAgent}`
302+
}
303+
304+
let newWindow = true
305+
// Open in the existing window if no workspaces are open.
306+
if (!vscode.workspace.workspaceFolders?.length) {
307+
newWindow = false
308+
}
309+
310+
// If a folder isn't specified, we can try to open a recently opened folder.
311+
if (!folderPath) {
312+
const output: {
313+
workspaces: { folderUri: vscode.Uri; remoteAuthority: string }[]
314+
} = await vscode.commands.executeCommand("_workbench.getRecentlyOpened")
315+
const opened = output.workspaces.filter(
316+
// Filter out `/` since that's added below.
317+
(opened) => opened.folderUri?.authority === remoteAuthority,
318+
)
319+
if (opened.length > 0) {
320+
let selected: (typeof opened)[0]
321+
322+
if (opened.length > 1) {
323+
const items: vscode.QuickPickItem[] = opened.map((folder): vscode.QuickPickItem => {
324+
return {
325+
label: folder.folderUri.path,
326+
}
327+
})
328+
const item = await vscode.window.showQuickPick(items, {
329+
title: "Select a recently opened folder",
330+
})
331+
if (!item) {
332+
return
333+
}
334+
selected = opened[items.indexOf(item)]
335+
} else {
336+
selected = opened[0]
337+
}
338+
339+
folderPath = selected.folderUri.path
340+
}
341+
}
342+
343+
if (folderPath) {
344+
await vscode.commands.executeCommand(
345+
"vscode.openFolder",
346+
vscode.Uri.from({
347+
scheme: "vscode-remote",
348+
authority: remoteAuthority,
349+
path: folderPath,
350+
}),
351+
// Open this in a new window!
352+
newWindow,
353+
)
354+
return
355+
}
356+
357+
// This opens the workspace without an active folder opened.
358+
await vscode.commands.executeCommand("vscode.newWindow", {
359+
remoteAuthority: remoteAuthority,
360+
reuseWindow: !newWindow,
361+
})
362+
}

0 commit comments

Comments
 (0)