
    5i;                        d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ  ej                  e      ZdZdZ G d d	      Z	 	 	 	 	 	 dd
Zy)ui  
Flow Token Service — uses headless Playwright to automatically extract
project_id and fresh reCAPTCHA tokens from labs.google/fx.

This service:
1. Loads labs.google/fx with the user's cookies
2. Intercepts network requests to capture the project_id
3. Extracts reCAPTCHA tokens on-demand via grecaptcha.enterprise.execute()
4. Keeps the browser alive so tokens can be refreshed without reloading

Usage:
    service = FlowTokenService(session_token, csrf_token)
    await service.start()
    project_id = service.project_id
    recaptcha_token = await service.get_fresh_recaptcha_token()
    await service.stop()
    )annotationsN)Path)Optional(6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMVz%https://labs.google/fx/tools/image-fxc                  H    e Zd ZdZ	 	 	 	 d	dZd
ddZddZddZddZd Z	y)FlowTokenServicez<Headless browser service that extracts Flow API credentials.c                t    || _         || _        d | _        d | _        d | _        d | _        d | _        d| _        y )NF)session_token
csrf_token
project_id_recaptcha_token_browser_context_page_running)selfr
   r   s      E/root/.openclaw/workspace/visionaryfx/providers/flow_token_service.py__init__zFlowTokenService.__init__&   s>    
 +$)-/3
    c                *   K   ddl m} dddd}	  |       j                          d{    _         j                  j                  j                  dddg       d{    _         j                  j                  d	d
dd       d{    _         j                  j                  d j                  ddddddd j                  ddddddg       d{     j                  j                          d{    _        d fd} j                  j                  d|       t        j!                  dt"                j                  j%                  t"        d|       d{    t        j!                  d j                  j&                         t)        j*                  d       d{    s<	  j                  j-                  d       d{   rt        j!                  d        j2                  s	  j5                          d{    _        s.t        j!                  d       	  j9                          d{    _        |d!<    j2                  |d"<   d _        t        j!                  d#xs d$t?         j2                               |S 7 v7 C7 7 7 7 E7 7 # t.        $ r }t        j1                  d|       Y d}~d}~ww xY w7 # t.        $ r }t        j7                  d|       Y d}~d}~ww xY w7 # t.        $ r }t        j7                  d |       Y d}~d}~ww xY w# t.        $ rH}tA        |      |d%<   t        jC                  d&|        jE                          d{  7   Y d}~|S d}~ww xY ww)'z
        Launch headless browser, navigate to labs.google/fx, and extract credentials.

        Returns dict with:
            - project_id: str or None
            - recaptcha_token: str or None
            - error: str or None
        r   )async_playwrightN)r   recaptcha_tokenerrorTz--no-sandboxz---disable-blink-features=AutomationControlled)headlessargszeMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36i   i   )widthheight)
user_agentviewportz __Secure-next-auth.session-tokenzlabs.google/Lax)namevaluedomainpathsecurehttpOnlysameSitez__Host-next-auth.csrf-tokenc                  K   | j                   }t        j                  d|      }|r'|j                  d      	t        j                  d	       d|v r	 | j                  }|rt        j                  |      }|j                  d      xs |j                  di       }|j                  d      xs |j                  d      }|r|	t        j                  d	|       |j                  d
      xs |j                  di       }|j                  d      }|r'|
_
        t        j                  dt        |             y y y y # t        $ r Y y w xY ww)Nz2aisandbox-pa\.googleapis\.com/v1/projects/([^/]+)/   z$Captured project_id from request: %szaisandbox-pa.googleapis.comclientContextclient_context	projectIdr   z!Captured project_id from body: %srecaptchaContextrecaptcha_contexttokenz.Captured reCAPTCHA token from request (len=%d))urlresearchgrouploggerinfo	post_datajsonloadsgetr   len	Exception)requestr1   matchbodydatactxpidrecapr0   captured_project_idr   s            r   
on_requestz*FlowTokenService.start.<locals>.on_requestp   s;    kk		I */++a.'KK FH[\ 1C7&00#'::d#3D"&((?";"]txxHXZ\?]C"%''+"6"O#'',:OC"69 3 &,OQT U %(GG,>$?$c377K^`bCcE$)IIg$6E$8= 5 &,\^abg^h i  %   8$ % s+   AEC$D< 8E<	EEEEr=   zNavigating to %s...networkidle)
wait_untiltimeoutzPage loaded: %s   aL  
                        () => {
                            // Deep search: recursively look for projectId in __NEXT_DATA__
                            const nextData = document.getElementById('__NEXT_DATA__');
                            if (nextData) {
                                try {
                                    const text = nextData.textContent;
                                    // Regex search for projectId pattern
                                    const match = text.match(/"projectId"\s*:\s*"([a-z0-9]+)"/);
                                    if (match) return match[1];
                                } catch(e) {}
                            }
                            // Search all script tags for projectId
                            const scripts = document.querySelectorAll('script');
                            for (const s of scripts) {
                                const t = s.textContent || '';
                                const match = t.match(/projectId['"\s:=]+['"]([a-z0-9]{10,})['"]/);
                                if (match) return match[1];
                            }
                            // Check window/global
                            if (window.__PROJECT_ID__) return window.__PROJECT_ID__;
                            return null;
                        }
                    zFound project_id in page JS: %sz(Could not extract project_id from JS: %sz%Could not extract reCAPTCHA token: %szHproject_id not found passively. Triggering a generation to capture it...z(Could not trigger project_id capture: %sr   r   z4Auto-setup complete: project_id=%s, has_recaptcha=%sz	NOT FOUNDr   zAuto-setup failed: %s)#playwright.async_apir   start_pwchromiumlaunchr   new_contextr   add_cookiesr
   r   new_pager   onr5   r6   LABS_URLgotor1   asynciosleepevaluater<   debugr   _extract_recaptcha_tokenwarning_trigger_and_capture_project_idr   r   boolstrr   stop)r   
timeout_msr   resultrE   erD   s   `     @r   rK   zFlowTokenService.start5   sb     	: $MW	-/5577DH"&(("3"3":":"C #; # DM #'--";";I $(37 #< # DM --++>!//+" $ % :!__+" $ %-   *  $}}5577DJ #' D JJMM)Z0 KK-x8**//(}j/YYYKK)4::>>: --""" 'P04

0C0C E 1 +'0 +$EGZ[
 ((O262O2O2Q,QD)
 'fgR040T0T0V*V' 2DO#6F< (,(=(=F$% DMKKF#2{T**+ q 8* 8V Z #
+4 ! PLL!KQOOP -R  ONN#JANNO +W  RNN#MqQQR  	!!fF7OLL0!4))+	sn  NL? J 5L?  J#!,L? J&AL? J)!L? =J,>A,L? *J/+AL? 1J22L? 9J7 J5J7 4L? K% K#	K% L? 6L 	L
L AL? N L? #L? &L? )L? ,L? /L? 2L? 5J7 7	K  KL? K  L? #K% %	L.L	L? 	LL? L 	L<L72L? 7L<<L? ?	N7N?N NNNNc                @  K   | j                   sy	 | j                   j                  dd       d{    | j                   j                  d
t         dt         d       d{   }|rt        j                  dt        |             |S 7 Y# t        $ r t        j	                  d       t        j                  d       d{  7   	 | j                   j                  dd       d{  7   n%# t        $ r t        j	                  d	       Y Y yw xY wY w xY w7 w)z>Execute reCAPTCHA Enterprise on the page to get a fresh token.NzW() => typeof grecaptcha !== 'undefined' && typeof grecaptcha.enterprise !== 'undefined'i'  rH   z>grecaptcha.enterprise not found on page, trying alternative...   z'() => typeof grecaptcha !== 'undefined'i  zNo grecaptcha found at allz
            async () => {
                try {
                    if (typeof grecaptcha !== 'undefined' && grecaptcha.enterprise) {
                        const token = await grecaptcha.enterprise.execute('z', {action: 'generate'});
                        return token;
                    }
                    if (typeof grecaptcha !== 'undefined' && grecaptcha.execute) {
                        const token = await grecaptcha.execute('z', {action: 'generate'});
                        return token;
                    }
                    return null;
                } catch(e) {
                    return null;
                }
            }
        z(Extracted fresh reCAPTCHA token (len=%d))r   wait_for_functionr<   r5   rZ   rU   rV   rW   RECAPTCHA_SITE_KEYr6   r;   )r   r0   s     r   rY   z)FlowTokenService._extract_recaptcha_token   s-    zz	**..i /   " jj)) /L M_K_ `A BT@T U	+  " KKBCJOI  	NN[\--"""jj22=  3     ;<		s   D B BB -D#D$'DB 6DCD
 C1*C-+C10D1DDDDDDDDc                :  K   | j                   sy	 g d}d}|D ]  }	 | j                   j                  |      j                  }|j                  d       d{   rO|j	                  d       d{    |j                  dd       d{    d}t        j                  d	|        n |st        j                  d
       yt        j                  d       d{    g d}d}|D ]v  }	 | j                   j                  |      j                  }|j                  d       d{   r4|j	                  d       d{    d}t        j                  d|        nx |sBt        j                  d       | j                   j                  j                  d       d{    t        j                  d       t        d      D ]Y  }t        j                  d       d{    | j                  s-t        j                  d| j                         | j                  c S  t        j                  d       y7 7 7 # t        $ r Y 5w xY w7 u7 -7 # t        $ r Y }w xY w7 7 # t        $ r }	t        j                  d|	       Y d}	~	yd}	~	ww xY ww)zSType a simple prompt and click generate to capture project_id from the API request.N)z textarea[aria-label*="prompt" i]z!textarea[placeholder*="prompt" i]zdiv[contenteditable="true"]textareazinput[type="text"]Fi  rc   za red apple on a tablei  TzFilled prompt input via: %sz Could not find/fill prompt inputr*   )z button[aria-label*="generate" i]zbutton[aria-label*="create" i]zbutton:has-text("Generate")zbutton:has-text("Create")zbutton[type="submit"]zbutton[aria-label*="send" i]zClicked generate button via: %sz+No generate button found, pressing Enter...Enterz*Waiting for API request with project_id...   zCaptured project_id: %sz3project_id not captured after triggering generationzError triggering generation: %s)r   locatorfirst
is_visibleclickfillr5   r6   r<   rZ   rU   rV   keyboardpressranger   )
r   prompt_selectorsfilledselelbutton_selectorsclickedbtn_ra   s
             r   r[   z0FlowTokenService._trigger_and_capture_project_id
  sc    zzG	  F' 
	++C066B]]4]888 hhth444 gg&>gMMM!%$A3G 9
 AB--"""  G' 	**,,S177C ^^D^999!iii555"&$EsK	 :	 IJjj))//888 KKDE2Y +mmA&&&??KK 94??K??*	+ NNPQi 94M !  #" :5 !  9
 '  	NN<a@	s  JI/ :IH9I1H<2IH?I)I/ JI/ II/ .:I(I)IIIAI/ "I+#?I/ "I-#I/ 4-I/ !J"I/ 8J9I<I?I	II/ II/ II	I($I/ 'I((I/ -I/ /	J8JJJJc                "  K   | j                   r| j                  s| j                  S 	 | j                          d{   }|r|| _        | j                  S 7 # t        $ r,}t
        j                  d|       | j                  cY d}~S d}~ww xY ww)z@Get a fresh reCAPTCHA token (call this before each API request).Nz%Failed to refresh reCAPTCHA token: %s)r   r   r   rY   r<   r5   rZ   )r   r0   ra   s      r   get_fresh_recaptcha_tokenz*FlowTokenService.get_fresh_recaptcha_tokenX  s}     zz(((	)7799E(-%((( :  	)NNBAF(((	)sD   %BA AA BA 	B !BBBBBc                n  K   d| _         	 | j                  r"| j                  j                          d{    	 | j                  r"| j                  j                          d{    d| _        d| _        d| _        t        j                  d       y7 ^# t        $ r Y gw xY w7 @# t        $ r Y Iw xY ww)zClose the browser and clean up.FNzToken service stopped)
r   r   closer<   rL   r^   r   r   r5   r6   )r   s    r   r^   zFlowTokenService.stopf  s     	}}mm))+++	xxhhmmo%% 
+, , 		 & 		sg   B5)B BB )B& #B$$B& (+B5B 	B!B5 B!!B5$B& &	B2/B51B22B5N)r
   r]   r   r]   )i0u  )r_   intreturndict)r   zOptional[str])
__name__
__module____qualname____doc__r   rK   rY   r[   r|   r^    r   r   r   r   #   s:    F fP+ZL\)-r   r   c                   K   t        | |      }|j                  d       d{   }|j                          d{    |S 7 7 w)z
    One-shot auto-setup: extract project_id and recaptcha_token, then close browser.

    Returns:
        {
            "project_id": str | None,
            "recaptcha_token": str | None,
            "error": str | None,
        }
    iȯ  )r_   N)r   rK   r^   )r
   r   servicer`   s       r   auto_setup_flow_credentialsr   y  sD      }j9G==E=22F
,,.M 3s   "AAAAAA)r
   r]   r   r]   r   r   )r   
__future__r   rU   r8   loggingosr2   pathlibr   typingr   	getLoggerr   r5   rf   rS   r   r   r   r   r   <module>r      sm   $ #    	 	  			8	$? 2S- S-l
 
r   