@@ -141,7 +141,8 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
141
141
if (
142
142
this . credentials === null ||
143
143
this . credentials . clientToken !== credentials . clientToken ||
144
- this . credentials . sessionId !== credentials . sessionId
144
+ this . credentials . sessionId !== credentials . sessionId ||
145
+ this . credentials . organizationId !== credentials . organizationId
145
146
) {
146
147
this . transitionToAttemptingSession ( credentials )
147
148
}
@@ -174,6 +175,7 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
174
175
175
176
this . changeState ( "attempting-session" )
176
177
178
+ this . timer . stop ( )
177
179
this . timer . start ( )
178
180
}
179
181
@@ -248,8 +250,10 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
248
250
*
249
251
* This method initiates the authentication flow by generating a state parameter
250
252
* and opening the browser to the authorization URL.
253
+ *
254
+ * @param landingPageSlug Optional slug of a specific landing page (e.g., "supernova", "special-offer", etc.)
251
255
*/
252
- public async login ( ) : Promise < void > {
256
+ public async login ( landingPageSlug ?: string ) : Promise < void > {
253
257
try {
254
258
const vscode = await importVscode ( )
255
259
@@ -267,11 +271,17 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
267
271
state,
268
272
auth_redirect : `${ vscode . env . uriScheme } ://${ publisher } .${ name } ` ,
269
273
} )
270
- const url = `${ getRooCodeApiUrl ( ) } /extension/sign-in?${ params . toString ( ) } `
274
+
275
+ // Use landing page URL if slug is provided, otherwise use default sign-in URL
276
+ const url = landingPageSlug
277
+ ? `${ getRooCodeApiUrl ( ) } /l/${ landingPageSlug } ?${ params . toString ( ) } `
278
+ : `${ getRooCodeApiUrl ( ) } /extension/sign-in?${ params . toString ( ) } `
279
+
271
280
await vscode . env . openExternal ( vscode . Uri . parse ( url ) )
272
281
} catch ( error ) {
273
- this . log ( `[auth] Error initiating Roo Code Cloud auth: ${ error } ` )
274
- throw new Error ( `Failed to initiate Roo Code Cloud authentication: ${ error } ` )
282
+ const context = landingPageSlug ? ` (landing page: ${ landingPageSlug } )` : ""
283
+ this . log ( `[auth] Error initiating Roo Code Cloud auth${ context } : ${ error } ` )
284
+ throw new Error ( `Failed to initiate Roo Code Cloud authentication${ context } : ${ error } ` )
275
285
}
276
286
}
277
287
@@ -461,6 +471,42 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
461
471
return this . credentials ?. organizationId || null
462
472
}
463
473
474
+ /**
475
+ * Switch to a different organization context
476
+ * @param organizationId The organization ID to switch to, or null for personal account
477
+ */
478
+ public async switchOrganization ( organizationId : string | null ) : Promise < void > {
479
+ if ( ! this . credentials ) {
480
+ throw new Error ( "Cannot switch organization: not authenticated" )
481
+ }
482
+
483
+ // Update the stored credentials with the new organization ID
484
+ const updatedCredentials : AuthCredentials = {
485
+ ...this . credentials ,
486
+ organizationId : organizationId ,
487
+ }
488
+
489
+ // Store the updated credentials, handleCredentialsChange will handle the update
490
+ await this . storeCredentials ( updatedCredentials )
491
+ }
492
+
493
+ /**
494
+ * Get all organization memberships for the current user
495
+ * @returns Array of organization memberships
496
+ */
497
+ public async getOrganizationMemberships ( ) : Promise < CloudOrganizationMembership [ ] > {
498
+ if ( ! this . credentials ) {
499
+ return [ ]
500
+ }
501
+
502
+ try {
503
+ return await this . clerkGetOrganizationMemberships ( )
504
+ } catch ( error ) {
505
+ this . log ( `[auth] Failed to get organization memberships: ${ error } ` )
506
+ return [ ]
507
+ }
508
+ }
509
+
464
510
private async clerkSignIn ( ticket : string ) : Promise < AuthCredentials > {
465
511
const formData = new URLSearchParams ( )
466
512
formData . append ( "strategy" , "ticket" )
@@ -645,9 +691,14 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
645
691
}
646
692
647
693
private async clerkGetOrganizationMemberships ( ) : Promise < CloudOrganizationMembership [ ] > {
694
+ if ( ! this . credentials ) {
695
+ this . log ( "[auth] Cannot get organization memberships: missing credentials" )
696
+ return [ ]
697
+ }
698
+
648
699
const response = await fetch ( `${ getClerkBaseUrl ( ) } /v1/me/organization_memberships` , {
649
700
headers : {
650
- Authorization : `Bearer ${ this . credentials ! . clientToken } ` ,
701
+ Authorization : `Bearer ${ this . credentials . clientToken } ` ,
651
702
"User-Agent" : this . userAgent ( ) ,
652
703
} ,
653
704
signal : AbortSignal . timeout ( 10000 ) ,
0 commit comments