@@ -608,33 +608,87 @@ export namespace AuthBrowser {
608608 if ( code ) break
609609 }
610610
611- // Try to click "Authorize" button if we're on consent screen
611+ // Try to dismiss cookie banner first, then click "Authorize" button (language-agnostic)
612612 if ( ! clickedAuthorize && currentUrl . includes ( "claude.ai" ) ) {
613613 try {
614- // Try multiple selectors for the authorize button
614+ // Try to dismiss cookie banner if present
615+ const dismissedCookie = await page . evaluate ( ( ) => {
616+ // Look for cookie-related containers
617+ const cookieSelectors = [
618+ '[data-testid*="cookie"]' ,
619+ '[class*="cookie"]' ,
620+ '[id*="cookie"]' ,
621+ '[class*="consent"]' ,
622+ '[id*="consent"]' ,
623+ ]
624+
625+ for ( const selector of cookieSelectors ) {
626+ const container = document . querySelector ( selector )
627+ if ( container ) {
628+ const buttons = Array . from ( container . querySelectorAll ( "button" ) )
629+ if ( buttons . length > 0 ) {
630+ // Click the last button (usually "Accept All")
631+ buttons [ buttons . length - 1 ] . click ( )
632+ return true
633+ }
634+ }
635+ }
636+ return false
637+ } )
638+ if ( dismissedCookie ) {
639+ log . info ( "dismissed cookie banner" )
640+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) )
641+ }
642+
643+ // Try to find and click the authorize button (language-agnostic)
644+ // Strategy: Find the primary button (with solid background) that's not a deny button
615645 const clicked = await page . evaluate ( ( ) => {
616- // Find button with text "Authorize"
617- const buttons = Array . from ( document . querySelectorAll ( "button" ) )
618- for ( const btn of buttons ) {
619- if ( btn . textContent ?. trim ( ) === "Authorize" || btn . textContent ?. includes ( "Authorize" ) ) {
646+ const mainContent = document . querySelector ( "main" ) || document . body
647+ const buttons = Array . from ( mainContent . querySelectorAll ( "button" ) )
648+
649+ // Filter visible, enabled buttons
650+ const visibleButtons = buttons . filter ( ( btn ) => {
651+ const style = getComputedStyle ( btn )
652+ return (
653+ style . display !== "none" &&
654+ style . visibility !== "hidden" &&
655+ ! btn . disabled &&
656+ btn . offsetParent !== null
657+ )
658+ } )
659+
660+ // Deny patterns to skip (common words across languages)
661+ const denyPatterns = [ "deny" , "cancel" , "reject" , "decline" , "no" , "nein" , "non" , "nie" ]
662+
663+ // Find primary button (with background color = filled/primary style)
664+ for ( const btn of visibleButtons ) {
665+ const bg = getComputedStyle ( btn ) . backgroundColor
666+ const text = btn . textContent ?. toLowerCase ( ) || ""
667+
668+ if ( denyPatterns . some ( ( p ) => text . includes ( p ) ) ) continue
669+
670+ const hasBackground = bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent"
671+ if ( hasBackground ) {
620672 btn . click ( )
621673 return true
622674 }
623675 }
624- // Also try input submit buttons
625- const inputs = Array . from ( document . querySelectorAll ( 'input[type="submit"]' ) )
626- for ( const input of inputs ) {
627- if ( ( input as HTMLInputElement ) . value ?. includes ( "Authorize" ) ) {
628- ; ( input as HTMLInputElement ) . click ( )
676+
677+ // Fallback: first non-deny button
678+ for ( const btn of visibleButtons ) {
679+ const text = btn . textContent ?. toLowerCase ( ) || ""
680+ if ( ! denyPatterns . some ( ( p ) => text . includes ( p ) ) ) {
681+ btn . click ( )
629682 return true
630683 }
631684 }
685+
632686 return false
633687 } )
634688 if ( clicked ) {
635689 log . info ( "clicked authorize button" )
636690 clickedAuthorize = true
637- await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) // Wait for redirect
691+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) )
638692 }
639693 } catch {
640694 // Button not found or click failed, continue polling
@@ -646,6 +700,15 @@ export namespace AuthBrowser {
646700 }
647701
648702 if ( ! code ) {
703+ // Save debug screenshot for troubleshooting
704+ try {
705+ const screenshotPath = path . join ( profilePath , "debug-screenshot.png" )
706+ await page . screenshot ( { path : screenshotPath , fullPage : true } )
707+ log . info ( "saved debug screenshot" , { screenshotPath } )
708+ } catch {
709+ // Screenshot failed
710+ }
711+
649712 throw new Error ( "Session expired or refresh timed out. Please run setup again." )
650713 }
651714
0 commit comments