@@ -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 }
@@ -98,16 +115,13 @@ export class Commands {
98115 return ;
99116 }
100117
101- // 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.
102120 this . restClient . setHost ( url ) ;
103121 this . restClient . setSessionToken ( result . token ) ;
104122
105- // Store for later sessions
106- await this . mementoManager . setUrl ( url ) ;
107- await this . secretsManager . setSessionAuth ( label , {
108- url,
109- token : result . token ,
110- } ) ;
123+ // Set as current deployment (triggers cross-window sync).
124+ await this . secretsManager . setCurrentDeployment ( { url, label } ) ;
111125
112126 // Update contexts
113127 this . contextManager . set ( "coder.authenticated" , true ) ;
@@ -130,7 +144,6 @@ export class Commands {
130144 }
131145 } ) ;
132146
133- await this . secretsManager . triggerLoginStateChange ( label , "login" ) ;
134147 vscode . commands . executeCommand ( "coder.refreshWorkspaces" ) ;
135148 }
136149
@@ -163,13 +176,8 @@ export class Commands {
163176 * Log out from the currently logged-in deployment.
164177 */
165178 public async logout ( ) : Promise < void > {
166- const url = this . mementoManager . getUrl ( ) ;
167- if ( ! url ) {
168- // Sanity check; command should not be available if no url.
169- throw new Error ( "You are not logged in" ) ;
170- }
171-
172- await this . forceLogout ( toSafeHost ( url ) ) ;
179+ const deployment = await this . requireDeployment ( ) ;
180+ await this . forceLogout ( deployment . label ) ;
173181 }
174182
175183 public async forceLogout ( label : string ) : Promise < void > {
@@ -178,8 +186,7 @@ export class Commands {
178186 }
179187 this . logger . info ( `Logging out of deployment: ${ label } ` ) ;
180188
181- // Only clear REST client and UI context if logging out of current deployment
182- // Fire and forget
189+ // Fire and forget OAuth logout
183190 this . oauthSessionManager . logout ( ) . catch ( ( error ) => {
184191 this . logger . warn ( "OAuth logout failed, continuing with cleanup:" , error ) ;
185192 } ) ;
@@ -189,8 +196,10 @@ export class Commands {
189196 this . restClient . setHost ( "" ) ;
190197 this . restClient . setSessionToken ( "" ) ;
191198
192- // Clear from memory.
193- 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
194203 await this . secretsManager . clearAllAuthData ( label ) ;
195204
196205 this . contextManager . set ( "coder.authenticated" , false ) ;
@@ -204,8 +213,6 @@ export class Commands {
204213
205214 // This will result in clearing the workspace list.
206215 vscode . commands . executeCommand ( "coder.refreshWorkspaces" ) ;
207-
208- await this . secretsManager . triggerLoginStateChange ( label , "logout" ) ;
209216 }
210217
211218 /**
@@ -214,7 +221,8 @@ export class Commands {
214221 * Must only be called if currently logged in.
215222 */
216223 public async createWorkspace ( ) : Promise < void > {
217- const uri = this . mementoManager . getUrl ( ) + "/templates" ;
224+ const deployment = await this . requireDeployment ( ) ;
225+ const uri = deployment . url + "/templates" ;
218226 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
219227 }
220228
@@ -228,8 +236,9 @@ export class Commands {
228236 */
229237 public async navigateToWorkspace ( item : OpenableTreeItem ) {
230238 if ( item ) {
239+ const deployment = await this . requireDeployment ( ) ;
231240 const workspaceId = createWorkspaceIdentifier ( item . workspace ) ;
232- const uri = this . mementoManager . getUrl ( ) + `/@${ workspaceId } ` ;
241+ const uri = deployment . url + `/@${ workspaceId } ` ;
233242 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
234243 } else if ( this . workspace && this . workspaceRestClient ) {
235244 const baseUrl =
@@ -251,8 +260,9 @@ export class Commands {
251260 */
252261 public async navigateToWorkspaceSettings ( item : OpenableTreeItem ) {
253262 if ( item ) {
263+ const deployment = await this . requireDeployment ( ) ;
254264 const workspaceId = createWorkspaceIdentifier ( item . workspace ) ;
255- const uri = this . mementoManager . getUrl ( ) + `/@${ workspaceId } /settings` ;
265+ const uri = deployment . url + `/@${ workspaceId } /settings` ;
256266 await vscode . commands . executeCommand ( "vscode.open" , uri ) ;
257267 } else if ( this . workspace && this . workspaceRestClient ) {
258268 const baseUrl =
@@ -329,18 +339,14 @@ export class Commands {
329339 const terminal = vscode . window . createTerminal ( app . name ) ;
330340
331341 // If workspace_name is provided, run coder ssh before the command
332-
333- const url = this . mementoManager . getUrl ( ) ;
334- if ( ! url ) {
335- throw new Error ( "No coder url found for sidebar" ) ;
336- }
342+ const deployment = await this . requireDeployment ( ) ;
337343 const binary = await this . cliManager . fetchBinary (
338344 this . restClient ,
339- toSafeHost ( url ) ,
345+ deployment . label ,
340346 ) ;
341347
342348 const configDir = this . pathResolver . getGlobalConfigDir (
343- toSafeHost ( url ) ,
349+ deployment . label ,
344350 ) ;
345351 const globalFlags = getGlobalFlags (
346352 vscode . workspace . getConfiguration ( ) ,
0 commit comments