
    KiO                       U 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Zddl	Z	ddl
mZ ddlmZmZmZ ddlZddlmZ ddlmZ  ej        e          ZdZd	Zdad
ed<   dad@dZdAdZdAdZdBdZ dAdZ!i Z"ded<    ej#                    Z$dCdZ%dDdEdZ&dFdZ'efdGd$Z(defdHd&Z)defdId(Z*defdGd)Z+dJdKd*Z,	 	 dLdMd-Z-dJdNd/Z.dJdOd1Z/dJdPd3Z0dJdQd4Z1dJdRd6Z2dJdQd7Z3dJdQd8Z4	 	 dSdTd;Z5dSdUd=Z6dVd?Z7dS )WuA  Camofox browser backend — local anti-detection browser via REST API.

Camofox-browser is a self-hosted Node.js server wrapping Camoufox (Firefox
fork with C++ fingerprint spoofing).  It exposes a REST API that maps 1:1
to our browser tool interface: accessibility snapshots with element refs,
click/type/scroll by ref, screenshots, etc.

When ``CAMOFOX_URL`` is set (e.g. ``http://localhost:9377``), the browser
tools route through this module instead of the ``agent-browser`` CLI.

Setup::

    # Option 1: npm
    git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
    npm install && npm start   # downloads Camoufox (~300MB) on first run

    # Option 2: Docker
    docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser

Then set ``CAMOFOX_URL=http://localhost:9377`` in ``~/.hermes/.env``.
    )annotationsN)Path)AnyDictOptionalload_config)get_camofox_identity   i8 Optional[str]_vnc_urlFreturnstrc                 R    t          j        dd                              d          S )z:Return the configured Camofox server URL, or empty string.CAMOFOX_URL /)osgetenvrstrip     2/home/ubuntu/hermes-agent/tools/browser_camofox.pyget_camofox_urlr   4   s"    9]B''..s333r   boolc                 8    t          t                                S )z(True when Camofox backend is configured.)r   r   r   r   r   is_camofox_moder   9   s    !!"""r   c                    t                      } | sdS 	 t          j        |  dd          }|j        dk    rt          s	 |                                }|                    d          }t          |t                    r2d|cxk    rdk    r%n n"d	d
lm	}  ||           }|j
        pd}d| d| an# t          t          f$ r Y nw xY wda|j        dk    S # t          $ r Y dS w xY w)z'Verify the Camofox server is reachable.Fz/health   timeout   vncPort   i  r   )urlparse	localhostzhttp://:T)r   requestsgetstatus_code_vnc_url_checkedjson
isinstanceinturllib.parser%   hostnamer   
ValueErrorKeyError	Exception)urlrespdatavnc_portr%   parsedhosts          r   check_camofox_availabler:   >   sA    

C u|sOOOQ777s""+;"	yy{{88I..h,, ;h1G1G1G1G%1G1G1G1G1G555555%Xc]]F!?9kD:::::H)   #3&&   uus6   +C  A0B1 0C 1CC CC 
C#"C#c                 :    t           st                       t          S )z>Return the VNC URL if the Camofox server exposes one, or None.)r+   r:   r   r   r   r   get_vnc_urlr<   W   s     "!!!Or   c                     	 t                                          di                               di           } n# t          $ r Y dS w xY wt          |                     d                    S )al  Return whether Hermes-managed persistence is enabled for Camofox.

    When enabled, sessions use a stable profile-scoped userId so the
    Camofox server can map it to a persistent browser profile directory.
    When disabled (default), each session gets a random userId (ephemeral).

    Controlled by ``browser.camofox.managed_persistence`` in config.yaml.
    browsercamofoxFmanaged_persistence)r	   r)   r3   r   )camofox_cfgs    r   _managed_persistence_enabledrB   ^   so    !mm''	266::9bII   uu 566777s   69 
AAzDict[str, Dict[str, Any]]	_sessionstask_idDict[str, Any]c                p   | pd} t           5  | t          v rt          |          cddd           S t                      r#t          |           }|d         d|d         dd}n3dt	          j                    j        dd          dd	| dd
          dd}|t          | <   |cddd           S # 1 swxY w Y   dS )zGet or create a camofox session for the given task.

    When managed persistence is enabled, uses a deterministic userId
    derived from the Hermes profile so the Camofox server can map it
    to the same persistent browser profile across restarts.
    defaultNuser_idsession_keyT)rH   tab_idrI   managedhermes_
   task_   F)_sessions_lockrC   rB   r
   uuiduuid4hex)rD   identitysessions      r   _get_sessionrV   v   sG    "G	  iW%        ()) 	+G44H#I.'6	 GG =TZ\\%5crc%:<<5wss|55 	 G %	''                 s   B+A/B++B/2B/about:blankr4   c                2   t          |           }|d         r|S t                      }t          j        | d|d         |d         |dt                    }|                                 |                                }|                    d          |d<   |S )z<Ensure a tab exists for the session, creating one if needed.rJ   z/tabsrH   rI   )userId
sessionKeyr4   r,   r!   tabId)rV   r   r(   post_DEFAULT_TIMEOUTraise_for_statusr,   r)   )rD   r4   rU   baser5   r6   s         r   _ensure_tabra      s    7##Gx D=i(!-0
 

 !  D 	99;;D))GHNr   Optional[Dict[str, Any]]c                    | pd} t           5  t                              | d          cddd           S # 1 swxY w Y   dS )zRemove and return session info.rG   N)rP   rC   pop)rD   s    r   _drop_sessionre      s    "G	 , ,}}Wd++, , , , , , , , , , , , , , , , , ,s   488pathbodydictr!   r.   c                    t                       |  }t          j        |||          }|                                 |                                S )z0POST JSON to camofox and return parsed response.r[   )r   r(   r]   r_   r,   rf   rg   r!   r4   r5   s        r   _postrk      sN    
&
&
&C=4999D99;;r   paramsc                    t                       |  }t          j        |||          }|                                 |                                S )z,GET from camofox and return parsed response.rl   r!   )r   r(   r)   r_   r,   rf   rl   r!   r4   r5   s        r   _getrp      sN    
&
&
&C<FG<<<D99;;r   requests.Responsec                    t                       |  }t          j        |||          }|                                 |S )z;GET from camofox and return raw response (for binary data).rn   )r   r(   r)   r_   ro   s        r   _get_rawrs      sE    
&
&
&C<FG<<<DKr   c                    t                       |  }t          j        |||          }|                                 |                                S )z-DELETE to camofox and return parsed response.r[   )r   r(   deleter_   r,   rj   s        r   _deleterv      sN    
&
&
&C?3T7;;;D99;;r   c                   	 t          |          }|d         st          ||           }d| d}n%t          d|d          d|d         | dd	          }d|                    d
|           |                    dd          d}t	                      }|r
||d<   d|d<   t          j        |          S # t          j        $ r$}t          j        dd| d          cY d}~S d}~wt          j	        $ r* t          j        ddt                       dd          cY S t          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)zNavigate to a URL via Camofox.rJ   T)okr4   /tabs/z	/navigaterH   )rY   r4   <   r    r4   titler   )successr4   r{   vnc_urlz]Browser is visible via VNC. Share this link with the user so they can watch the browser live.vnc_hintFzNavigation failed: r|   errorNzCannot connect to Camofox at z. Is the server running? Start with: npm start (in camofox-browser dir) or: docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser)rV   ra   rk   r)   r<   r,   dumpsr(   	HTTPErrorConnectionErrorr   r3   r   )r4   rD   rU   r6   resultvnces          r   camofox_navigater      s   $?w''x  
	!'3//Gs++DD 5*555"9-c::  D 88E3''XXgr**
 

 mm 	 #F9T : z&!!! R R Rze6OA6O6OPPQQQQQQQQ#   z__5F5F _ _ _
 
   	 	 	  ? ? ?zec!ff==>>>>>>>>?s6   B-B0 0E?CE9E	E"#EEEfull	user_taskc                   	 t          |          }|d         st          j        ddd          S t          d|d          dd|d         i	          }|                    d
d          }|                    dd          }ddlm}m}m}	 t          |          |k    r|r |||          }n |	|          }t          j        d||d          S # t          $ r.}
t          j        dt          |
          d          cY d}
~
S d}
~
ww xY w)z-Get accessibility tree snapshot from Camofox.rJ   F0No browser session. Call browser_navigate first.r   ry   	/snapshotrY   rH   rl   snapshotr   	refsCountr   )SNAPSHOT_SUMMARIZE_THRESHOLD_extract_relevant_content_truncate_snapshotT)r|   r   element_countN)rV   r,   r   rp   r)   tools.browser_toolr   r   r   lenr3   r   )r   rD   r   rU   r6   r   
refs_countr   r   r   r   s              r   camofox_snapshotr     s~    ?w''x  	o:%:lmmnnn1WX&111gi01
 
 

 88J++XXk1--
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 x==777 844XyII--h77z '
 
   	
  ? ? ?zec!ff==>>>>>>>>?s#   -C BC 
D#D;DDrefc                   	 t          |          }|d         st          j        ddd          S |                     d          }t	          d|d          d|d         |d	          }t          j        d
||                    dd          d          S # t          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)z$Click an element by ref via Camofox.rJ   Fr   r   @ry   z/clickrH   )rY   r   Tr4   r   )r|   clickedr4   N)rV   r,   r   lstriprk   r)   r3   r   )r   rD   rU   	clean_refr6   r   s         r   camofox_clickr   '  s   ?w''x  	o:%:lmmnnn JJsOO	.WX&...y))<<
 
 z 88E2&&
 
   	
  ? ? ?zec!ff==>>>>>>>>?s#   -B A#B 
C#CCCtextc                x   	 t          |          }|d         st          j        ddd          S |                     d          }t	          d|d          d|d         ||d	           t          j        d
||d          S # t
          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)z-Type text into an element by ref via Camofox.rJ   Fr   r   r   ry   z/typerH   )rY   r   r   T)r|   typedelementN)rV   r,   r   r   rk   r3   r   )r   r   rD   rU   r   r   s         r   camofox_typer   >  s    ?w''x  	o:%:lmmnnnJJsOO	-WX&---y))TJJ	
 	
 	
 z 
 
   	
  ? ? ?zec!ff==>>>>>>>>?s#   -B AB 
B9#B4.B94B9	directionc                J   	 t          |          }|d         st          j        ddd          S t          d|d          d|d         | d           t          j        d	| d
          S # t          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)zScroll the page via Camofox.rJ   Fr   r   ry   z/scrollrH   )rY   r   T)r|   scrolledNrV   r,   r   rk   r3   r   )r   rD   rU   r   s       r   camofox_scrollr   T  s    ?w''x  	o:%:lmmnnn/WX&///y)	BB	
 	
 	
 zd	BBCCC ? ? ?zec!ff==>>>>>>>>?"   -A* 9A* *
B"4#BB"B"c                p   	 t          |           }|d         st          j        ddd          S t          d|d          dd|d         i          }t          j        d	|                    d
d          d          S # t
          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)zNavigate back via Camofox.rJ   Fr   r   ry   z/backrY   rH   Tr4   r   )r|   r4   N)rV   r,   r   rk   r)   r3   r   )rD   rU   r6   r   s       r   camofox_backr   d  s    ?w''x  	o:%:lmmnnn-WX&---wy)*
 
 zd488E23F3FGGHHH ? ? ?zec!ff==>>>>>>>>?s#   -A= AA= =
B5#B0*B50B5keyc                J   	 t          |          }|d         st          j        ddd          S t          d|d          d|d         | d           t          j        d	| d
          S # t          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)z!Press a keyboard key via Camofox.rJ   Fr   r   ry   z/pressrH   )rY   r   T)r|   pressedNr   )r   rD   rU   r   s       r   camofox_pressr   t  s    ?w''x  	o:%:lmmnnn.WX&...y)#66	
 	
 	
 zds;;<<< ? ? ?zec!ff==>>>>>>>>?r   c                *   	 t          |           }|st          j        ddd          S t          d|d                     t          j        ddd          S # t          $ r/}t          j        ddt          |          d          cY d}~S d}~ww xY w)z&Close the browser session via Camofox.T)r|   closed
/sessions/rH   )r|   r   warningN)re   r,   r   rv   r3   r   )rD   rU   r   s      r   camofox_closer     s    
P(( 	A:$$??@@@-+--	
 	
 	
 zdd;;<<< P P Pzdds1vvNNOOOOOOOOPs"   'A .A 
B#$BBBc                   	 t          |           }|d         st          j        ddd          S ddl}t	          d|d          dd	|d
         i          }|                    dd          }g }|                    d          }t          |          D ]\  }}|                                }	|		                    d          s|		                    d          r|
                    d|	          }
|
r|
                    d          nd}d}|dz   t          |          k     rH|
                    d||dz                                                      }|r|                    d          }|s|r|                    ||d           t          j        d|t          |          d          S # t          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w)zGet images on the current page via Camofox.

    Extracts image information from the accessibility tree snapshot,
    since Camofox does not expose a dedicated /images endpoint.
    rJ   Fr   r   r   Nry   r   rY   rH   r   r   r   
z- img zimg zimg\s+"([^"]*)"r$   z/url:\s*(\S+))srcaltT)r|   imagescount)rV   r,   r   rerp   r)   split	enumeratestrip
startswithsearchgroupr   appendr3   r   )rD   rU   r   r6   r   r   linesilinestripped	alt_matchr   r   	url_matchr   s                  r   camofox_get_imagesr     s'   &?w''x  	o:%:lmmnnn			1WX&111gi01
 
 
 88J++
 t$$ '' 	< 	<GAtzz||H""8,, 
<0C0CF0K0K 
<II&8(CC	,5=iooa(((2q53u::%% "		*:E!a%L<N<N<P<P Q QI  1'ooa00 <# <MM#c":":;;;z[[
 
   	
  ? ? ?zec!ff==>>>>>>>>?s#   -F' E6F' '
G1#GGGquestionannotatec                z   	 t          |          }|d         st          j        ddd          S t          d|d          dd|d         i	          }d
dlm}  |            dz  }|                    dd           t          |dt          j	                    j
        dd          dz            }t          |d          5 }|                    |j                   ddd           n# 1 swxY w Y   t          j        |j                                      d          }	d}
|rV	 t#          d|d          dd|d         i	          }d|                    dd          dd          }
n# t&          $ r Y nw xY wd
dlm}  ||
          }
d
dlm} d|  |
 }	 d
dlm}  |            }t5          |                    di                               di                               d d!                    }n# t&          $ r d!}Y nw xY w |d"d#|d$d%d&d'|	 id(gd)gd|*          }|j        r+|j        d
         j        j        pd                                nd}d
dlm}  ||          }t          j        d||d+          S # t&          $ r.}t          j        dt          |          d          cY d}~S d}~ww xY w),z<Take a screenshot and analyze it with vision AI via Camofox.rJ   Fr   r   ry   z/screenshotrY   rH   r   r   )get_hermes_homebrowser_screenshotsT)parentsexist_okbrowser_screenshot_N   z.pngwbzutf-8r   r   z5

Accessibility tree (element refs for interaction):
r   i  )redact_sensitive_text)call_llmz,Analyze this browser screenshot and answer: r   	auxiliaryvisionr!   x   userr   )typer   	image_urlr4   zdata:image/png;base64,)r   r   )rolecontent)messagestaskr!   )r|   analysisscreenshot_path)rV   r,   r   rs   hermes_constantsr   mkdirr   rQ   rR   rS   openwriter   base64	b64encodedecoderp   r)   r3   agent.redactr   agent.auxiliary_clientr   hermes_cli.configr	   r.   choicesmessager   )r   r   rD   rU   r5   r   screenshots_dirr   fimg_b64annotation_context	snap_datar   r   vision_promptr	   _cfg_vision_timeoutresponser   r   s                        r   camofox_visionr     s=   T?w''x  	o:%:lmmnnn 3WX&333gi01
 
 
 	544444)/++.CCdT:::o0`djllFVWYXYWYFZ0`0`0``aa/4(( 	"AGGDL!!!	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" "4<0077@@   	 9WX.999$gi&89  	 &G`i`m`mnxz|`}`}  D  @D  D  aE  &G  &G""    	766666223EFF 	433333$8 $!$ $ 	
	"555555;==D!$((;";";"?"?""M"M"Q"QR[]`"a"abbOO 	" 	" 	"!OOO	" 8#];; +!#EG#E#E&    #
 
 
  KSJZbH$Q'/7=2DDFFF`b 	766666((22z .
 
   	
  ? ? ?zec!ff==>>>>>>>>?s   -J BJ C(J (C,,J /C,03J $AE) (J )
E63J 5E66!J AG4 3J 4H J HA>J 
J:#J5/J:5J:clearc           	     8    t          j        dg g dddd          S )u   Get console output — limited support in Camofox.

    Camofox does not expose browser console logs via its REST API.
    Returns an empty result with a note.
    Tr   z|Console log capture is not available with the Camofox backend. Use browser_snapshot or browser_vision to inspect page state.)r|   console_messages	js_errorstotal_messagestotal_errorsnote)r,   r   )r   rD   s     r   camofox_consoler     s7     :P    r   Nonec                 d   t           5  t          t                                                    } ddd           n# 1 swxY w Y   | D ].\  }}	 t	          d|d                     # t
          $ r Y +w xY wt           5  t                                           ddd           dS # 1 swxY w Y   dS )z"Close all active camofox sessions.Nr   rH   )rP   listrC   itemsrv   r3   r   )sessionsrD   rU   s      r   cleanup_all_camofox_sessionsr  1  sF   	 + +	))**+ + + + + + + + + + + + + + +$  	5!3556666 	 	 	D		                   s1   ';??A''
A43A4>B%%B),B))r   r   )r   r   )r   r   )rD   r   r   rE   )rW   )rD   r   r4   r   r   rE   )rD   r   r   rb   )rf   r   rg   rh   r!   r.   r   rh   )rf   r   rl   rh   r!   r.   r   rh   )rf   r   rl   rh   r!   r.   r   rq   )N)r4   r   rD   r   r   r   )FNN)r   r   rD   r   r   r   r   r   )r   r   rD   r   r   r   )r   r   r   r   rD   r   r   r   )r   r   rD   r   r   r   )rD   r   r   r   )r   r   rD   r   r   r   )FN)r   r   r   r   rD   r   r   r   )r   r   rD   r   r   r   )r   r   )8__doc__
__future__r   r   r,   loggingr   	threadingtimerQ   pathlibr   typingr   r   r   r(   r   r	   tools.browser_camofox_stater
   	getLogger__name__loggerr^   _SNAPSHOT_MAX_CHARSr   __annotations__r+   r   r   r:   r<   rB   rC   LockrP   rV   ra   re   rk   rp   rs   rv   r   r   r   r   r   r   r   r   r   r   r   r  r   r   r   <module>r     s    , # " " " " "    				             & & & & & & & & & &  ) ) ) ) ) ) < < < < < <		8	$	$        4 4 4 4
# # # #
   2   8 8 8 8( (*	 ) ) ) )!!   <    *, , , , 1A      $(8H      (,<L      %)9I     &? &? &? &? &?R CG04#? #? #? #? #?L? ? ? ? ?.? ? ? ? ?,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? P P P P P,? ,? ,? ,? ,?^ 49,0W? W? W? W? W?t    *
 
 
 
 
 
r   