@@ -9,6 +9,7 @@ import { createWorkspaceIdentifier, extractAgents } from "./api/api-helper";
99import { type CliManager } from "./core/cliManager" ;
1010import { type ServiceContainer } from "./core/container" ;
1111import { type ContextManager } from "./core/contextManager" ;
12+ import { type Deployment } from "./core/deployment" ;
1213import { type MementoManager } from "./core/mementoManager" ;
1314import { type PathResolver } from "./core/pathResolver" ;
1415import { type SecretsManager } from "./core/secretsManager" ;
@@ -61,6 +62,17 @@ export class Commands {
6162 this . loginCoordinator = serviceContainer . getLoginCoordinator ( ) ;
6263 }
6364
65+ /**
66+ * Get the current deployment, throwing if not logged in.
67+ */
68+ private async requireDeployment ( ) : Promise < Deployment > {
69+ const deployment = await this . secretsManager . getCurrentDeployment ( ) ;
70+ if ( ! deployment ) {
71+ throw new Error ( "You are not logged in" ) ;
72+ }
73+ return deployment ;
74+ }
75+
6476 /**
6577 * Log into the provided deployment. If the deployment URL is not specified,
6678 * ask for it first with a menu showing recent URLs along with the default URL
@@ -76,7 +88,12 @@ export class Commands {
7688 }
7789 this . logger . info ( "Logging in" ) ;
7890
79- const url = await maybeAskUrl ( this . mementoManager , args ?. url ) ;
91+ const currentDeployment = await this . secretsManager . getCurrentDeployment ( ) ;
92+ const url = await maybeAskUrl (
93+ this . mementoManager ,
94+ args ?. url ,
95+ currentDeployment ?. url ,
96+ ) ;
8097 if ( ! url ) {
8198 return ;
8299 }
@@ -88,7 +105,8 @@ export class Commands {
88105 this . logger . info ( "Using deployment label" , label ) ;
89106
90107 const result = await this . loginCoordinator . promptForLogin ( {
91- deployment : { url, label } ,
108+ label,
109+ url,
92110 autoLogin : args ?. autoLogin ,
93111 oauthSessionManager : this . oauthSessionManager ,
94112 } ) ;
@@ -97,16 +115,13 @@ export class Commands {
97115 return ;
98116 }
99117
100- // Authorize the global client
118+ // Set client immediately so subsequent operations in this function have the correct host/token.
119+ // The cross-window listener will also update the client, but that's async.
101120 this . restClient . setHost ( url ) ;
102121 this . restClient . setSessionToken ( result . token ) ;
103122
104- // Store for later sessions
105- await this . mementoManager . setUrl ( url ) ;
106- await this . secretsManager . setSessionAuth ( label , {
107- url,
108- token : result . token ,
109- } ) ;
123+ // Set as current deployment (triggers cross-window sync).
124+ await this . secretsManager . setCurrentDeployment ( { url, label } ) ;
110125
111126 // Update contexts
112127 this . contextManager . set ( "coder.authenticated" , true ) ;
@@ -129,7 +144,6 @@ export class Commands {
129144 }
130145 } ) ;
131146
132- await this . secretsManager . triggerLoginStateChange ( label , "login" ) ;
133147 vscode . commands . executeCommand ( "coder.refreshWorkspaces" ) ;
134148 }
135149
@@ -162,13 +176,8 @@ export class Commands {
162176 * Log out from the currently logged-in deployment.
163177 */
164178 public async logout ( ) : Promise < void > {
165- const url = this . mementoManager . getUrl ( ) ;
166- if ( ! url ) {
167- // Sanity check; command should not be available if no url.
168- throw new Error ( "You are not logged in" ) ;
169- }
170-
171- await this . forceLogout ( toSafeHost ( url ) ) ;
179+ const deployment = await this . requireDeployment ( ) ;
180+ await this . forceLogout ( deployment . label ) ;
172181 }
173182
174183 public async forceLogout ( label : string ) : Promise < void > {
@@ -177,8 +186,7 @@ export class Commands {
177186 }
178187 this . logger . info ( `Logging out of deployment: ${ label } ` ) ;
179188
180- // Only clear REST client and UI context if logging out of current deployment
181- // Fire and forget
189+ // Fire and forget OAuth logout
182190 this . oauthSessionManager . logout ( ) . catch ( ( error ) => {
183191 this . logger . warn ( "OAuth logout failed, continuing with cleanup:" , error ) ;
184192 } ) ;
@@ -188,8 +196,10 @@ export class Commands {
188196 this . restClient . setHost ( "" ) ;
189197 this . restClient . setSessionToken ( "" ) ;
190198
191- // Clear from memory.
192- await this . mementoManager . setUrl ( undefined ) ;
199+ // Clear current deployment (triggers cross-window sync)
200+ await this . secretsManager . setCurrentDeployment ( undefined ) ;
201+
202+ // Clear all auth data for this deployment
193203 await this . secretsManager . clearAllAuthData ( label ) ;
194204
195205 this . contextManager . set ( "coder.authenticated" , false ) ;
@@ -203,8 +213,6 @@ export class Commands {
203213
204214 // This will result in clearing the workspace list.
205215 vscode . commands . executeCommand ( "coder.refreshWorkspaces" ) ;
206-
207- await this . secretsManager . triggerLoginStateChange ( label , "logout" ) ;
208216 }
209217
210218 /**
@@ -213,7 +221,8 @@ export class Commands {
213221 * Must only be called if currently logged in.
214222 */
215223 public async createWorkspace ( ) : Promise < void > {
216- const uri = this . mementoManager . getUrl ( ) + "/templates" ;
224+ const deployment = await this . requireDeployment ( ) ;
225+ const uri = deployment . url + "/templates" ;
217226 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
218227 }
219228
@@ -227,8 +236,9 @@ export class Commands {
227236 */
228237 public async navigateToWorkspace ( item : OpenableTreeItem ) {
229238 if ( item ) {
239+ const deployment = await this . requireDeployment ( ) ;
230240 const workspaceId = createWorkspaceIdentifier ( item . workspace ) ;
231- const uri = this . mementoManager . getUrl ( ) + `/@${ workspaceId } ` ;
241+ const uri = deployment . url + `/@${ workspaceId } ` ;
232242 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
233243 } else if ( this . workspace && this . workspaceRestClient ) {
234244 const baseUrl =
@@ -250,8 +260,9 @@ export class Commands {
250260 */
251261 public async navigateToWorkspaceSettings ( item : OpenableTreeItem ) {
252262 if ( item ) {
263+ const deployment = await this . requireDeployment ( ) ;
253264 const workspaceId = createWorkspaceIdentifier ( item . workspace ) ;
254- const uri = this . mementoManager . getUrl ( ) + `/@${ workspaceId } /settings` ;
265+ const uri = deployment . url + `/@${ workspaceId } /settings` ;
255266 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
256267 } else if ( this . workspace && this . workspaceRestClient ) {
257268 const baseUrl =
@@ -328,18 +339,14 @@ export class Commands {
328339 const terminal = vscode . window . createTerminal ( app . name ) ;
329340
330341 // If workspace_name is provided, run coder ssh before the command
331-
332- const url = this . mementoManager . getUrl ( ) ;
333- if ( ! url ) {
334- throw new Error ( "No coder url found for sidebar" ) ;
335- }
342+ const deployment = await this . requireDeployment ( ) ;
336343 const binary = await this . cliManager . fetchBinary (
337344 this . restClient ,
338- toSafeHost ( url ) ,
345+ deployment . label ,
339346 ) ;
340347
341348 const configDir = this . pathResolver . getGlobalConfigDir (
342- toSafeHost ( url ) ,
349+ deployment . label ,
343350 ) ;
344351 const globalFlags = getGlobalFlags (
345352 vscode . workspace . getConfiguration ( ) ,
0 commit comments