diff --git a/packages/cloud/src/auth/WebAuthService.ts b/packages/cloud/src/auth/WebAuthService.ts index 82d3122426..2f23576205 100644 --- a/packages/cloud/src/auth/WebAuthService.ts +++ b/packages/cloud/src/auth/WebAuthService.ts @@ -242,6 +242,24 @@ export class WebAuthService extends EventEmitter implements A * This method initiates the authentication flow by generating a state parameter * and opening the browser to the authorization URL. */ + /** + * Detects if running in Firebase Studio IDE (formerly IDX Google) environment + */ + private isFirebaseStudioIDE(): boolean { + const appName = vscode.env.appName?.toLowerCase() || "" + const remoteName = vscode.env.remoteName?.toLowerCase() || "" + + return ( + appName.includes("idx") || + appName.includes("firebase") || + appName.includes("studio") || + remoteName.includes("idx") || + remoteName.includes("firebase") || + process.env.IDX_WORKSPACE_ID !== undefined || + process.env.FIREBASE_PROJECT_ID !== undefined + ) + } + public async login(): Promise { try { // Generate a cryptographically random state parameter. @@ -250,11 +268,34 @@ export class WebAuthService extends EventEmitter implements A const packageJSON = this.context.extension?.packageJSON const publisher = packageJSON?.publisher ?? "RooVeterinaryInc" const name = packageJSON?.name ?? "roo-cline" + + const isCloudIDE = this.isFirebaseStudioIDE() + this.log(`[auth] Initiating login - Firebase Studio IDE detected: ${isCloudIDE}`) + this.log(`[auth] App name: ${vscode.env.appName}`) + this.log(`[auth] Remote name: ${vscode.env.remoteName}`) + this.log(`[auth] URI scheme: ${vscode.env.uriScheme}`) + const params = new URLSearchParams({ state, auth_redirect: `${vscode.env.uriScheme}://${publisher}.${name}`, }) + + // Add cloud IDE indicator for server-side handling + if (isCloudIDE) { + params.append("cloud_ide", "firebase_studio") + } + const url = `${getRooCodeApiUrl()}/extension/sign-in?${params.toString()}` + this.log(`[auth] Opening authentication URL: ${url}`) + + if (isCloudIDE) { + // Show additional guidance for Firebase Studio IDE users + vscode.window.showInformationMessage( + "Opening authentication in Firebase Studio IDE. After signing in, the callback should be automatically handled.", + { modal: false }, + ) + } + await vscode.env.openExternal(vscode.Uri.parse(url)) } catch (error) { this.log(`[auth] Error initiating Roo Code Cloud auth: ${error}`) @@ -277,8 +318,24 @@ export class WebAuthService extends EventEmitter implements A state: string | null, organizationId?: string | null, ): Promise { + const isCloudIDE = this.isFirebaseStudioIDE() + + this.log(`[auth] Handling callback - Firebase Studio IDE: ${isCloudIDE}`) + this.log(`[auth] Code present: ${!!code}, State present: ${!!state}`) + if (!code || !state) { - vscode.window.showInformationMessage("Invalid Roo Code Cloud sign in url") + const message = "Invalid Roo Code Cloud sign in url" + this.log(`[auth] ${message}`) + + if (isCloudIDE) { + // Provide more specific guidance for Firebase Studio IDE + vscode.window.showErrorMessage( + "Authentication callback failed in Firebase Studio IDE. Please try signing in again. " + + "If the issue persists, check that popup blockers are disabled and the extension has proper permissions.", + ) + } else { + vscode.window.showInformationMessage(message) + } return } @@ -288,9 +345,11 @@ export class WebAuthService extends EventEmitter implements A if (state !== storedState) { this.log("[auth] State mismatch in callback") + this.log(`[auth] Expected state: ${storedState}, Received state: ${state}`) throw new Error("Invalid state parameter. Authentication request may have been tampered with.") } + this.log("[auth] State validation successful, proceeding with sign-in") const credentials = await this.clerkSignIn(code) // Set organizationId (null for personal accounts) @@ -298,10 +357,28 @@ export class WebAuthService extends EventEmitter implements A await this.storeCredentials(credentials) - vscode.window.showInformationMessage("Successfully authenticated with Roo Code Cloud") + const successMessage = "Successfully authenticated with Roo Code Cloud" + if (isCloudIDE) { + vscode.window.showInformationMessage( + `${successMessage} in Firebase Studio IDE. You can now use Roo Code Cloud features.`, + ) + } else { + vscode.window.showInformationMessage(successMessage) + } + this.log("[auth] Successfully authenticated with Roo Code Cloud") } catch (error) { this.log(`[auth] Error handling Roo Code Cloud callback: ${error}`) + + if (isCloudIDE) { + // Provide more detailed error information for Firebase Studio IDE + const errorMessage = error instanceof Error ? error.message : String(error) + vscode.window.showErrorMessage( + `Authentication failed in Firebase Studio IDE: ${errorMessage}. ` + + "Please try again or contact support if the issue persists.", + ) + } + const previousState = this.state this.state = "logged-out" this.emit("logged-out", { previousState }) diff --git a/packages/cloud/src/utils.ts b/packages/cloud/src/utils.ts index cf87aa5e28..00e17ce92a 100644 --- a/packages/cloud/src/utils.ts +++ b/packages/cloud/src/utils.ts @@ -1,10 +1,46 @@ import * as vscode from "vscode" +/** + * Detects if running in Firebase Studio IDE (formerly IDX Google) environment + */ +function isFirebaseStudioIDE(): boolean { + const appName = vscode.env.appName?.toLowerCase() || "" + const remoteName = vscode.env.remoteName?.toLowerCase() || "" + + return ( + appName.includes("idx") || + appName.includes("firebase") || + appName.includes("studio") || + remoteName.includes("idx") || + remoteName.includes("firebase") || + process.env.IDX_WORKSPACE_ID !== undefined || + process.env.FIREBASE_PROJECT_ID !== undefined + ) +} + /** * Get the User-Agent string for API requests * @param context Optional extension context for more accurate version detection - * @returns User-Agent string in format "Roo-Code {version}" + * @returns User-Agent string in format "Roo-Code {version} ({environment})" */ export function getUserAgent(context?: vscode.ExtensionContext): string { - return `Roo-Code ${context?.extension?.packageJSON?.version || "unknown"}` + const version = context?.extension?.packageJSON?.version || "unknown" + const baseUserAgent = `Roo-Code ${version}` + + // Add environment information for better debugging + const environment = [] + + if (isFirebaseStudioIDE()) { + environment.push("Firebase-Studio-IDE") + } + + if (vscode.env.remoteName) { + environment.push(`Remote-${vscode.env.remoteName}`) + } + + if (environment.length > 0) { + return `${baseUserAgent} (${environment.join("; ")})` + } + + return baseUserAgent } diff --git a/src/activate/handleUri.ts b/src/activate/handleUri.ts index 7f0b4c64cc..2ef3e4bf7d 100644 --- a/src/activate/handleUri.ts +++ b/src/activate/handleUri.ts @@ -4,12 +4,43 @@ import { CloudService } from "@roo-code/cloud" import { ClineProvider } from "../core/webview/ClineProvider" +/** + * Detects if running in Firebase Studio IDE (formerly IDX Google) environment + */ +function isFirebaseStudioIDE(): boolean { + // Check for Firebase Studio IDE specific environment indicators + const appName = vscode.env.appName?.toLowerCase() || "" + const remoteName = vscode.env.remoteName?.toLowerCase() || "" + + // Firebase Studio IDE typically has these characteristics + return ( + appName.includes("idx") || + appName.includes("firebase") || + appName.includes("studio") || + remoteName.includes("idx") || + remoteName.includes("firebase") || + process.env.IDX_WORKSPACE_ID !== undefined || + process.env.FIREBASE_PROJECT_ID !== undefined + ) +} + export const handleUri = async (uri: vscode.Uri) => { const path = uri.path const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B")) const visibleProvider = ClineProvider.getVisibleInstance() + // Enhanced logging for debugging authentication issues in cloud IDEs + const isCloudIDE = isFirebaseStudioIDE() + console.log(`[handleUri] Processing URI: ${uri.toString()}`) + console.log(`[handleUri] Path: ${path}`) + console.log(`[handleUri] Query params: ${query.toString()}`) + console.log(`[handleUri] Firebase Studio IDE detected: ${isCloudIDE}`) + console.log(`[handleUri] App name: ${vscode.env.appName}`) + console.log(`[handleUri] Remote name: ${vscode.env.remoteName}`) + console.log(`[handleUri] URI scheme: ${vscode.env.uriScheme}`) + if (!visibleProvider) { + console.warn(`[handleUri] No visible provider found for URI: ${uri.toString()}`) return } @@ -17,6 +48,7 @@ export const handleUri = async (uri: vscode.Uri) => { case "/glama": { const code = query.get("code") if (code) { + console.log(`[handleUri] Processing Glama callback with code: ${code.substring(0, 10)}...`) await visibleProvider.handleGlamaCallback(code) } break @@ -24,6 +56,7 @@ export const handleUri = async (uri: vscode.Uri) => { case "/openrouter": { const code = query.get("code") if (code) { + console.log(`[handleUri] Processing OpenRouter callback with code: ${code.substring(0, 10)}...`) await visibleProvider.handleOpenRouterCallback(code) } break @@ -31,6 +64,7 @@ export const handleUri = async (uri: vscode.Uri) => { case "/requesty": { const code = query.get("code") if (code) { + console.log(`[handleUri] Processing Requesty callback with code: ${code.substring(0, 10)}...`) await visibleProvider.handleRequestyCallback(code) } break @@ -40,14 +74,46 @@ export const handleUri = async (uri: vscode.Uri) => { const state = query.get("state") const organizationId = query.get("organizationId") - await CloudService.instance.handleAuthCallback( - code, - state, - organizationId === "null" ? null : organizationId, - ) + console.log(`[handleUri] Processing Clerk auth callback`) + console.log(`[handleUri] Code present: ${!!code}`) + console.log(`[handleUri] State present: ${!!state}`) + console.log(`[handleUri] Organization ID: ${organizationId}`) + + if (isCloudIDE) { + console.log(`[handleUri] Firebase Studio IDE environment detected - using enhanced callback handling`) + + // Show user feedback for cloud IDE environments + if (code && state) { + vscode.window.showInformationMessage( + "Authentication callback received in Firebase Studio IDE. Processing login...", + ) + } + } + + try { + await CloudService.instance.handleAuthCallback( + code, + state, + organizationId === "null" ? null : organizationId, + ) + + if (isCloudIDE) { + console.log(`[handleUri] Successfully processed auth callback in Firebase Studio IDE`) + } + } catch (error) { + console.error(`[handleUri] Error processing auth callback:`, error) + + if (isCloudIDE) { + vscode.window.showErrorMessage( + `Authentication failed in Firebase Studio IDE: ${error instanceof Error ? error.message : String(error)}`, + ) + } + throw error + } break } default: + console.log(`[handleUri] Unhandled URI path: ${path}`) break } } diff --git a/src/extension.ts b/src/extension.ts index bd43bcbf8a..c8854bef3b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -51,13 +51,51 @@ import { initializeI18n } from "./i18n" let outputChannel: vscode.OutputChannel let extensionContext: vscode.ExtensionContext +/** + * Detects if running in Firebase Studio IDE (formerly IDX Google) environment + */ +function isFirebaseStudioIDE(): boolean { + const appName = vscode.env.appName?.toLowerCase() || "" + const remoteName = vscode.env.remoteName?.toLowerCase() || "" + + return ( + appName.includes("idx") || + appName.includes("firebase") || + appName.includes("studio") || + remoteName.includes("idx") || + remoteName.includes("firebase") || + process.env.IDX_WORKSPACE_ID !== undefined || + process.env.FIREBASE_PROJECT_ID !== undefined + ) +} + // This method is called when your extension is activated. // Your extension is activated the very first time the command is executed. export async function activate(context: vscode.ExtensionContext) { extensionContext = context outputChannel = vscode.window.createOutputChannel(Package.outputChannel) context.subscriptions.push(outputChannel) + + // Enhanced logging for Firebase Studio IDE environments + const isCloudIDE = isFirebaseStudioIDE() outputChannel.appendLine(`${Package.name} extension activated - ${JSON.stringify(Package)}`) + outputChannel.appendLine(`Environment detection:`) + outputChannel.appendLine(` - Firebase Studio IDE: ${isCloudIDE}`) + outputChannel.appendLine(` - App name: ${vscode.env.appName}`) + outputChannel.appendLine(` - Remote name: ${vscode.env.remoteName}`) + outputChannel.appendLine(` - URI scheme: ${vscode.env.uriScheme}`) + outputChannel.appendLine(` - Machine ID: ${vscode.env.machineId}`) + outputChannel.appendLine(` - Session ID: ${vscode.env.sessionId}`) + + if (isCloudIDE) { + outputChannel.appendLine(`Firebase Studio IDE environment detected - enabling enhanced authentication handling`) + + // Show user notification about Firebase Studio IDE support + vscode.window.showInformationMessage( + "Roo Code detected Firebase Studio IDE environment. Enhanced authentication support is enabled.", + { modal: false }, + ) + } // Migrate old settings to new await migrateSettings(context, outputChannel)