@@ -25,6 +25,7 @@ import { needToken } from "../api/utils";
2525import {
2626 startWorkspaceIfStoppedOrFailed ,
2727 waitForBuild ,
28+ writeAgentLogs ,
2829} from "../api/workspace" ;
2930import { type Commands } from "../commands" ;
3031import { type CliManager } from "../core/cliManager" ;
@@ -51,7 +52,6 @@ import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport";
5152export interface RemoteDetails extends vscode . Disposable {
5253 url : string ;
5354 token : string ;
54- startedWorkspace : boolean ;
5555}
5656
5757export class Remote {
@@ -131,29 +131,18 @@ export class Remote {
131131 const workspaceName = createWorkspaceIdentifier ( workspace ) ;
132132
133133 // A terminal will be used to stream the build, if one is necessary.
134- let writeEmitter : undefined | vscode . EventEmitter < string > ;
135- let terminal : undefined | vscode . Terminal ;
134+ let writeEmitter : vscode . EventEmitter < string > | undefined ;
135+ let terminal : vscode . Terminal | undefined ;
136136 let attempts = 0 ;
137137
138- function initWriteEmitterAndTerminal ( ) : vscode . EventEmitter < string > {
139- writeEmitter ??= new vscode . EventEmitter < string > ( ) ;
140- if ( ! terminal ) {
141- terminal = vscode . window . createTerminal ( {
142- name : "Build Log" ,
143- location : vscode . TerminalLocation . Panel ,
144- // Spin makes this gear icon spin!
145- iconPath : new vscode . ThemeIcon ( "gear~spin" ) ,
146- pty : {
147- onDidWrite : writeEmitter . event ,
148- close : ( ) => undefined ,
149- open : ( ) => undefined ,
150- // eslint-disable-next-line @typescript-eslint/no-explicit-any
151- } as Partial < vscode . Pseudoterminal > as any ,
152- } ) ;
153- terminal . show ( true ) ;
138+ const initBuildLogTerminal = ( ) => {
139+ if ( ! writeEmitter ) {
140+ const init = this . initWriteEmitterAndTerminal ( "Build Log" ) ;
141+ writeEmitter = init . writeEmitter ;
142+ terminal = init . terminal ;
154143 }
155144 return writeEmitter ;
156- }
145+ } ;
157146
158147 try {
159148 // Show a notification while we wait.
@@ -171,7 +160,7 @@ export class Remote {
171160 case "pending" :
172161 case "starting" :
173162 case "stopping" :
174- writeEmitter = initWriteEmitterAndTerminal ( ) ;
163+ writeEmitter = initBuildLogTerminal ( ) ;
175164 this . logger . info ( `Waiting for ${ workspaceName } ...` ) ;
176165 workspace = await waitForBuild ( client , writeEmitter , workspace ) ;
177166 break ;
@@ -182,7 +171,7 @@ export class Remote {
182171 ) {
183172 return undefined ;
184173 }
185- writeEmitter = initWriteEmitterAndTerminal ( ) ;
174+ writeEmitter = initBuildLogTerminal ( ) ;
186175 this . logger . info ( `Starting ${ workspaceName } ...` ) ;
187176 workspace = await startWorkspaceIfStoppedOrFailed (
188177 client ,
@@ -203,7 +192,7 @@ export class Remote {
203192 ) {
204193 return undefined ;
205194 }
206- writeEmitter = initWriteEmitterAndTerminal ( ) ;
195+ writeEmitter = initBuildLogTerminal ( ) ;
207196 this . logger . info ( `Starting ${ workspaceName } ...` ) ;
208197 workspace = await startWorkspaceIfStoppedOrFailed (
209198 client ,
@@ -246,6 +235,27 @@ export class Remote {
246235 }
247236 }
248237
238+ private initWriteEmitterAndTerminal ( name : string ) : {
239+ writeEmitter : vscode . EventEmitter < string > ;
240+ terminal : vscode . Terminal ;
241+ } {
242+ const writeEmitter = new vscode . EventEmitter < string > ( ) ;
243+ const terminal = vscode . window . createTerminal ( {
244+ name,
245+ location : vscode . TerminalLocation . Panel ,
246+ // Spin makes this gear icon spin!
247+ iconPath : new vscode . ThemeIcon ( "gear~spin" ) ,
248+ pty : {
249+ onDidWrite : writeEmitter . event ,
250+ close : ( ) => undefined ,
251+ open : ( ) => undefined ,
252+ } ,
253+ } ) ;
254+ terminal . show ( true ) ;
255+
256+ return { writeEmitter, terminal } ;
257+ }
258+
249259 /**
250260 * Ensure the workspace specified by the remote authority is ready to receive
251261 * SSH connections. Return undefined if the authority is not for a Coder
@@ -416,7 +426,6 @@ export class Remote {
416426 }
417427 }
418428
419- let startedWorkspace = false ;
420429 const disposables : vscode . Disposable [ ] = [ ] ;
421430 try {
422431 // Register before connection so the label still displays!
@@ -444,7 +453,6 @@ export class Remote {
444453 await this . closeRemote ( ) ;
445454 return ;
446455 }
447- startedWorkspace = true ;
448456 workspace = updatedWorkspace ;
449457 }
450458 this . commands . workspace = workspace ;
@@ -593,7 +601,6 @@ export class Remote {
593601 }
594602
595603 // Make sure the agent is connected.
596- // TODO: Should account for the lifecycle state as well?
597604 if ( agent . status !== "connected" ) {
598605 const result = await this . vscodeProposed . window . showErrorMessage (
599606 `${ workspaceName } /${ agent . name } ${ agent . status } ` ,
@@ -611,6 +618,41 @@ export class Remote {
611618 return ;
612619 }
613620
621+ if ( agent . lifecycle_state === "starting" ) {
622+ const isBlocking = agent . scripts . some (
623+ ( script ) => script . start_blocks_login ,
624+ ) ;
625+ if ( isBlocking ) {
626+ const { writeEmitter, terminal } =
627+ this . initWriteEmitterAndTerminal ( "Agent Log" ) ;
628+ const socket = await writeAgentLogs (
629+ workspaceClient ,
630+ writeEmitter ,
631+ agent . id ,
632+ ) ;
633+ await new Promise < void > ( ( resolve ) => {
634+ const updateEvent = monitor . onChange . event ( ( workspace ) => {
635+ const agents = extractAgents ( workspace . latest_build . resources ) ;
636+ const found = agents . find ( ( newAgent ) => {
637+ return newAgent . id === agent . id ;
638+ } ) ;
639+ if ( ! found ) {
640+ return ;
641+ }
642+ agent = found ;
643+ if ( agent . lifecycle_state === "starting" ) {
644+ return ;
645+ }
646+ updateEvent . dispose ( ) ;
647+ resolve ( ) ;
648+ } ) ;
649+ } ) ;
650+ writeEmitter . dispose ( ) ;
651+ terminal . dispose ( ) ;
652+ socket . close ( ) ;
653+ }
654+ }
655+
614656 const logDir = this . getLogDir ( featureSet ) ;
615657
616658 // This ensures the Remote SSH extension resolves the host to execute the
@@ -684,7 +726,6 @@ export class Remote {
684726 return {
685727 url : baseUrlRaw ,
686728 token,
687- startedWorkspace,
688729 dispose : ( ) => {
689730 disposables . forEach ( ( d ) => d . dispose ( ) ) ;
690731 } ,
0 commit comments