@@ -6,7 +6,7 @@ import { makeCoderSdk, needToken } from "./api"
66import { extractAgents } from "./api-helper"
77import { CertificateError } from "./error"
88import { Storage } from "./storage"
9- import { AuthorityPrefix , toSafeHost } from "./util"
9+ import { toRemoteAuthority , toSafeHost } from "./util"
1010import { OpenableTreeItem } from "./workspacesProvider"
1111
1212export class Commands {
@@ -499,6 +499,26 @@ export class Commands {
499499await openWorkspace ( baseUrl , workspaceOwner , workspaceName , workspaceAgent , folderPath , openRecent )
500500}
501501
502+ /**
503+ * Open a devcontainer from a workspace belonging to the currently logged-in deployment.
504+ *
505+ * Throw if not logged into a deployment.
506+ */
507+ public async openDevContainer ( ...args :string [ ] ) :Promise < void > {
508+ const baseUrl = this . restClient . getAxiosInstance ( ) . defaults . baseURL
509+ if ( ! baseUrl ) {
510+ throw new Error ( "You are not logged in" )
511+ }
512+
513+ const workspaceOwner = args [ 0 ] as string
514+ const workspaceName = args [ 1 ] as string
515+ const workspaceAgent = undefined // args[2] is reserved, but we do not support multiple agents yet.
516+ const devContainerName = args [ 3 ] as string
517+ const devContainerFolder = args [ 4 ] as string
518+
519+ await openDevContainer ( baseUrl , workspaceOwner , workspaceName , workspaceAgent , devContainerName , devContainerFolder )
520+ }
521+
502522/**
503523 * Update the current workspace. If there is no active workspace connection,
504524 * this is a no-op.
@@ -536,10 +556,7 @@ async function openWorkspace(
536556) {
537557// A workspace can have multiple agents, but that's handled
538558// when opening a workspace unless explicitly specified.
539- let remoteAuthority = `ssh-remote+${ AuthorityPrefix } .${ toSafeHost ( baseUrl ) } --${ workspaceOwner } --${ workspaceName } `
540- if ( workspaceAgent ) {
541- remoteAuthority += `.${ workspaceAgent } `
542- }
559+ const remoteAuthority = toRemoteAuthority ( baseUrl , workspaceOwner , workspaceName , workspaceAgent )
543560
544561let newWindow = true
545562// Open in the existing window if no workspaces are open.
@@ -598,3 +615,32 @@ async function openWorkspace(
598615reuseWindow :! newWindow ,
599616} )
600617}
618+
619+ async function openDevContainer (
620+ baseUrl :string ,
621+ workspaceOwner :string ,
622+ workspaceName :string ,
623+ workspaceAgent :string | undefined ,
624+ devContainerName :string ,
625+ devContainerFolder :string ,
626+ ) {
627+ const remoteAuthority = toRemoteAuthority ( baseUrl , workspaceOwner , workspaceName , workspaceAgent )
628+
629+ const devContainer = Buffer . from ( JSON . stringify ( { containerName :devContainerName } ) , "utf-8" ) . toString ( "hex" )
630+ const devContainerAuthority = `attached-container+${ devContainer } @${ remoteAuthority } `
631+
632+ let newWindow = true
633+ if ( ! vscode . workspace . workspaceFolders ?. length ) {
634+ newWindow = false
635+ }
636+
637+ await vscode . commands . executeCommand (
638+ "vscode.openFolder" ,
639+ vscode . Uri . from ( {
640+ scheme :"vscode-remote" ,
641+ authority :devContainerAuthority ,
642+ path :devContainerFolder ,
643+ } ) ,
644+ newWindow ,
645+ )
646+ }