Ë
    ãß²jÃ3  ã            
       ó´  — U d 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Zddl	Z	ddl
mZ ddlmZ ddlmZmZmZ  ej$                  e«      ZdZde› d�Zd	Zd
ZdZi Zeedf   ed<    ej:                  «       Ze G d„ d«      «       Zde fd„Z!dee   fd„Z"de#fd„Z$defd„Z%defd„Z&dZ' ejP                  d¬«      dede)fd„«       Z*dedefd„Z+de,fd„Z-d3dede.de)fd„Z/d edefd!„Z0d4d"ejb                  d#eddfd$„Z2d%e#dee#   fd&„Z3d%e#ddfd'„Z4d(eddfd)„Z5d*d+œded,e)deee   ee   f   fd-„Z6dedee   fd.„Z7deddfd/„Z8d5d0„Z9d%e#de)fd1„Z:de#fd2„Z;y)6a‰  Lightpanda local engine for Browser Use mode.

With ``browser.engine: lightpanda``, Browser Use mode spawns one ``lightpanda serve`` per
browser session and points ``browser_exec`` at its CDP endpoint (``BU_CDP_URL``); the
built-in ``browser_*`` tools keep going through ``agent-browser --engine lightpanda``.
``tools.browser_tool`` owns the session cache, inactivity reaper and atexit sweep.
é    N)Ú	dataclass)ÚPath)ÚDictÚOptionalÚTuplez=https://lightpanda.io/docs/run-locally/installation/one-linerzInstall Lightpanda from z& and make sure `lightpanda` is on PATHg      $@gš™™™™™¹?iÐ  ÚLightpandaServerÚ_serversc                   ó€   — e Zd ZU eed<   eed<   ej                  ed<   eed<   dZe	e   ed<   e
defd„«       Zdefd	„Zy)
r   Úsession_nameÚportÚprocÚlog_pathNÚ
start_timeÚreturnc                 ó    — d| j                   › �S )Núhttp://127.0.0.1:)r   ©Úselfs    ú=/home/ubuntu/.hermes/hermes-agent/tools/browser_lightpanda.pyÚcdp_urlzLightpandaServer.cdp_url+   s   € à" 4§9¡9 +Ð.Ð.ó    c                 ó:   — | j                   j                  «       d u S ©N)r   Úpollr   s    r   Úis_alivezLightpandaServer.is_alive/   s   € Ø�y‰y�~‰~Ó 4Ð'Ð'r   )Ú__name__Ú
__module__Ú__qualname__ÚstrÚ__annotations__ÚintÚ
subprocessÚPopenr   r   Úpropertyr   Úboolr   © r   r   r   r   #   sQ   … àÓØ
ƒIØ
×
Ñ
ÓØƒMØ $€J�˜‘Ó$àð/˜ò /ó ð/ð(˜$ô (r   r   c                  ó  — t        j                  «       } | dz  dz  | dz  dz  dz  g}	 ddlm} |j	                  t         |«       «      dz  dz  «       |S # t
        $ r!}t        j                  d|«       Y d }~|S d }~ww xY w)Nz.lightpandaÚ
lightpandaz.localÚbinr   ©Úget_hermes_homez1hermes home unavailable for lightpanda lookup: %s)r   ÚhomeÚhermes_constantsr+   ÚappendÚ	ExceptionÚloggerÚdebug)r,   Ú
candidatesr+   Úes       r   Ú_home_candidatesr4   3   s�   € Ü�9‰9‹;€DØ˜Ñ&¨Ñ5°t¸h±ÈÑ7NÐQ]Ñ7]Ð^€JðMÝ4Ø×Ñœ$™Ó0Ó1°EÑ9¸LÑHÔIð Ðøô ò MÜ�‰ÐHÈ!×LÐLØÐûðMús   ©+A Á	B ÁA;Á;B c                  ór  — t         j                  dk(  rt        j                  d«       yt         j                  j                  dd«      } 	 ddlm}  || «      } t        j                  d	| ¬
«      xs t        d„ t        «       D «       d«      S # t        $ r }t        j                  d|«       Y d}~ŒXd}~ww xY w)zÇReturn the lightpanda executable, or None. Order: PATH (with agent-browser's Homebrew/managed-node
    fallbacks), then installer/agent-browser locations, then ``$HERMES_HOME/bin``. No Windows build.ÚntzLightpanda has no Windows buildNÚPATHÚ r   )Ú_merge_browser_pathz"browser PATH merge unavailable: %sr(   ©Úpathc              3   óž   K  — | ]E  }|j                  «       sŒt        j                  |t        j                  «      sŒ9t	        |«      –— ŒG y ­wr   )Úis_fileÚosÚaccessÚX_OKr   )Ú.0Úcs     r   ú	<genexpr>z)find_lightpanda_binary.<locals>.<genexpr>K   s-   è ø€ ÒU�A¨q¯y©y­{¼r¿y¹yÈÌBÏGÉGÕ?TŒˆQ�ÑUùs   ‚A˜$A½A)r>   Únamer0   r1   ÚenvironÚgetÚtools.browser_tool_installr9   r/   ÚshutilÚwhichÚnextr4   )Úpath_envr9   r3   s      r   Úfind_lightpanda_binaryrL   >   sœ   € ô 
‡w�w�$‚Ü�‰Ð6Ô7ØÜ�z‰z�~‰~˜f bÓ)€Hð>ÝBÙ& xÓ0ˆô �<‰<˜¨8Ô4ò ]¼ÙUÔ)Ó+ÔUÐW[ó9]ð ]øô ò >Ü�‰Ð9¸1×=Ñ=ûð>ús   ÁB Â	B6ÂB1Â1B6c                  óJ  — t        j                   t         j                  t         j                  «      5 } | j                  t         j                  t         j
                  d«       | j                  d«       t        | j                  «       d   «      cd d d «       S # 1 sw Y   y xY w)Né   )ú	127.0.0.1r   )	ÚsocketÚAF_INETÚSOCK_STREAMÚ
setsockoptÚ
SOL_SOCKETÚSO_REUSEADDRÚbindr!   Úgetsockname)Úsocks    r   Ú_pick_free_loopback_portrY   N   sp   € Ü	�‰”v—~‘~¤v×'9Ñ'9Ó	:ð *¸dØ�‰œ×)Ñ)¬6×+>Ñ+>ÀÔBØ�	‰	Ð"Ô#Ü�4×#Ñ#Ó% aÑ(Ó)÷*÷ *ò *ús   ³ABÂB"c                  ój   — ddl m}  t         | «       «      dz  dz  dz  }|j                  dd¬«       |S )Nr   r*   Úcachezbrowser-user(   T©ÚparentsÚexist_ok)r-   r+   r   Úmkdir)r+   r;   s     r   Ú
_state_dirr`   U   s6   € Ý0Ü‘Ó!Ó" WÑ,¨}Ñ<¸|ÑK€DØ‡J�J�t d€JÔ+Ø€Kr   c                  óF   — t        «       dz  } | j                  dd¬«       | S )a\  Filesystem HTTP cache shared by every Lightpanda this Hermes spawns.

    Shared rather than per-session so a cached asset survives session churn.
    Lightpanda holds it in sqlite (WAL) with a best-effort write path, and
    ``--http-cache-entry-limit`` (upstream default 1000, not passed here)
    bounds it without Hermes managing eviction.
    z
http-cacheTr\   )r`   r_   r:   s    r   Ú_http_cache_dirrb   \   s%   € ô ‹<˜,Ñ&€DØ‡J�J�t d€JÔ+Ø€Kr   z--http-cache-dirrN   )ÚmaxsizeÚbinaryc                 óþ   — 	 t        j                  | dgdddt         j                  ¬«      }t        |j                  xs d|j
                  xs dz   v S # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)	a_  True if ``lightpanda serve`` accepts ``--http-cache-dir``.

    The flag landed upstream in 0.3.x; older binaries fatally reject it
    ("unknown argument"), which would break every launch. Probing ``help``
    output keeps working across future flag additions without parsing
    versions, and the lru_cache keeps it once per binary per process.
    ÚhelpTg      @)Úcapture_outputÚtextÚtimeoutÚstdinr8   z4lightpanda http-cache probe failed (%s); assuming noNF)	r"   ÚrunÚDEVNULLÚ_HTTP_CACHE_FLAGÚstdoutÚstderrr/   r0   r1   )rd   r   r3   s      r   Ú_binary_supports_http_cacherp   l   ss   € ð	Ü�~‰~Ø�VÐØ d°CÜ×$Ñ$ô
ˆô
   T§[¡[Ò%6°B¸4¿;¹;Ò;LÈ"Ñ$MÐNÐNøÜò Ü�‰ÐKÈQÔOÜûðús   ‚AA Á	A<ÁA7Á7A<r   c                 ó"   — t        «       | › d�z  S )Nú.json)r`   ©r   s    r   Ú_record_pathrt   �   s   € Ü‹<˜\˜N¨%Ð0Ñ0Ð0r   c                  ó²   — 	 ddl m}   | «       S # t        $ r>}t        j	                  d|«       t
        j                  j                  «       cY d }~S d }~ww xY w)Nr   )Ú_build_browser_envz/credential-scrubbed browser env unavailable: %s)Útools.browser_toolrv   r/   r0   r1   r>   rE   Úcopy)rv   r3   s     r   Ú_browser_envry   …   sC   € ð!Ý9Ù!Ó#Ð#øÜò !Ü�‰ÐFÈÔJÜ�z‰z�‰Ó Õ ûð!ús   ‚ �	A˜3AÁAÁAÚurlri   c                 ó~   — 	 ddl m}  || |¬«      S # t        $ r!}t        j	                  d| |«       Y d }~yd }~ww xY w)Nr   )Úis_browser_debug_ready©ri   z%CDP readiness probe failed for %s: %sF)Úhermes_cli.browser_connectr|   r/   r0   r1   )rz   ri   r|   r3   s       r   Ú
_cdp_readyr   Ž   s;   € ðÝEÙ% c°7Ô;Ð;øÜò Ü�‰Ð<¸cÀ1ÔEÜûðús   ‚ ’	<›7·<r;   c                 óö   — 	 t        | «      j                  «       t         d j                  dd¬«      }t        d„ t        |j                  «       j                  «       «      D «       d«      S # t        $ r Y yw xY w)zCLast non-empty line of the child's stderr log ('' when unreadable).Núutf-8Úreplace)Úerrorsr8   c              3   óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­wr   )Ústrip)rA   Úlns     r   rC   z!_read_log_tail.<locals>.<genexpr>�   s   è ø€ ÒP˜ÀRÇXÁXÅZ”ÑPùs   ‚˜)	r   Ú
read_bytesÚ_STDERR_TAIL_LIMITÚdecodeÚOSErrorrJ   Úreversedr…   Ú
splitlines)r;   rh   s     r   Ú_read_log_tailr�   —   st   € ðÜ�D‹z×$Ñ$Ó&Ô(:Ð':Ð';Ð<×CÑCÀGÐT]ÐCÓ^ˆô ÑPœh t§z¡z£|×'>Ñ'>Ó'@ÓAÔPÐRTÓUÐUøô ò Ùðús   ‚2A, Á,	A8Á7A8r   Úwhatc                 ó  — 	 | j                  «       €$| j                  «        	 | j                  d¬«       yy# t        $ r | j	                  «        Y yw xY w# t        $ r!}t
        j                  d||«       Y d}~yd}~ww xY w)zRpoll -> terminate -> wait(5) -> kill; shared with the real-profile Chrome cleanup.Né   r}   z%s terminate failed: %s)r   Ú	terminateÚwaitr/   Úkillr0   r1   )r   rŽ   r3   s      r   Ú
_terminater”       sr   € ð9Ø�9‰9‹;ÐØ�N‰NÔðØ—	‘	 !�	Õ$ð øô ò Ø—	‘	–ðûäò 9Ü�‰Ð.°°a×8Ñ8ûð9ús2   ‚ A £7 ·AÁA ÁAÁA Á	B ÁA;Á;B Úpidc                 óP   — 	 ddl m} |j                  | «      S # t        $ r Y y w xY w)Nr   ©ÚProcessRegistry)Útools.process_registryr˜   Ú_safe_host_start_timer/   )r•   r˜   s     r   Ú_safe_start_timer›   ­   s-   € ðÝ:Ø×4Ñ4°SÓ9Ð9øÜò Ùðús   ‚ ™	%¤%c                 ó6   — ddl m} |j                  | |¬«       y)zFTree-kill ``pid`` via ProcessRegistry, verifying its start time first.r   r—   )Úexpected_startN)r™   r˜   Ú_terminate_host_pid)r•   r�   r˜   s      r   Ú
_tree_killrŸ   µ   s   € å6Ø×'Ñ'¨¸NÐ'ÕKr   Úserverc                 ó˜  — | j                   j                  | j                  t        j                  «       | j
                  t        j                  «       dœ}	 t        «       | j                  › d�z  j                  t        j                  |«      d¬«       y # t        $ r+}t        j                  d| j                  |«       Y d }~y d }~ww xY w)N)r•   r   Ú	owner_pidr   Ú
started_atrr   r�   ©Úencodingz,could not write lightpanda record for %s: %s)r   r•   r   r>   Úgetpidr   Útimer`   r   Ú
write_textÚjsonÚdumpsrŠ   r0   r1   )r    Úrecordr3   s      r   Ú_write_recordr¬   »   s™   € Ø—[‘[—_‘_¨f¯k©kÌÏ	É	ËØ"×-Ñ-¼T¿Y¹Y»[ñJ€Fð]Ü	‹˜6×.Ñ.Ð/¨uÐ5Ñ	5×AÑAÄ$Ç*Á*ÈVÓBTÐ_fÐAÕgøÜò ]Ü�‰ÐCÀV×EXÑEXÐZ[×\Ñ\ûð]ús   Á>B Â	C	Â!CÃC	F)Úblock_private_networksr­   c          	      óà  — t        «       }|s t        j                  dk(  ryddt        › d�fS t	        «       }|dddd	t        |«      g}t        |«      r|t        t        t        «       «      gz  }|r|j                  d
«       t        t        «       | › d�z  «      }	 t        |d«      5 }t        j                  |t        j                  t        j                  |t        «       d¬«      }ddd«       d|› �}	t%        j&                  «       t(        z   }
	 j+                  «       }|�"t-        |«      }dd|› d|	› d|rd|› �› �fS d› �fS t/        |	«      rnkt%        j&                  «       |
k\  r:t1        |«       t-        |«      }|rd|› d�nd}dd|	› dt3        t(        «      › d|› �fS t%        j4                  t6        «       Œ«t9        | |||t;        |j<                  «      «      }t?        |«       t@        5  |tB        | <   ddd«       tD        jG                  d|j<                  || «       |dfS # 1 sw Y   �ŒAxY w# t         t        j"                  f$ r}dd|› d|› �fcY d}~S d}~ww xY w# 1 sw Y   ŒlxY w)zÙStart ``lightpanda serve`` on a free loopback port; ``(server, None)`` once ``/json/version`` answers,
    else ``(None, error)``. stderr goes to ``<state_dir>/<session>.log`` so a chatty child never blocks on a pipe.r6   )Nzzbrowser.engine is 'lightpanda' but Lightpanda has no Windows build. Set browser.engine to auto (or run Hermes under WSL2).Nzjbrowser.engine is 'lightpanda' but no lightpanda binary was found on PATH, ~/.lightpanda or ~/.local/bin. z , or set browser.engine to auto.Úservez--hostrO   z--portz--block-private-networksz.logÚwbT)rj   rn   ro   ÚenvÚstart_new_sessionz#Failed to launch lightpanda serve (z): r   z"lightpanda serve exited with code z before z/json/version answeredz: r8   z (last stderr line: ú)z lightpanda serve did not expose z/json/version within Úsz9Started lightpanda serve (pid %s, port %s) for session %s)$rL   r>   rD   ÚLIGHTPANDA_INSTALL_HINTrY   r   rp   rm   rb   r.   r`   Úopenr"   r#   rl   ry   rŠ   ÚSubprocessErrorr§   Ú	monotonicÚ_READY_TIMEOUT_Sr   r�   r   r”   r!   ÚsleepÚ_POLL_INTERVAL_Sr   r›   r•   r¬   Ú_servers_lockr	   r0   Úinfo)r   r­   rd   r   Úargvr   Úlog_filer   r3   rz   ÚdeadlineÚrcÚtailÚdetailr    s                  r   Úlaunch_lightpandarÄ   Ä   s»  € ô $Ó%€FÙÜ�7‰7�dŠ?ð[àð *Ü*AÐ)BÐBbðdð eð 	eô $Ó%€DØ�G˜X {°H¼cÀ$»iÐH€DÜ" 6Ô*ØÔ!¤3¤Ó'8Ó#9Ð:Ñ:ˆÙØ�‰Ð.Ô/Ü”:“< \ N°$Ð"7Ñ7Ó8€HðJÜ�(˜DÓ!ð 	a XÜ×#Ñ# D´
×0BÑ0BÌ:×K]ÑK]Ø+3¼»Ð[_ôaˆD÷	að ˜d˜VÐ
$€CÜ�~‰~ÓÔ"2Ñ2€HØ
Ø�Y‰Y‹[ˆØˆ>Ü! (Ó+ˆDØÐ=¸b¸TÀÈ#ÈÐNdÑtxÐgiÐjnÐioÐepð  eBð  Cð  Cð  Cð  Að  eBð  Cð  Cð  CÜ�cŒ?ØÜ�>‰>Ó˜xÒ'Ü�tÔÜ! (Ó+ˆDÙ7;Ð+¨D¨6°Ñ3ÀˆFØÐ;¸C¸5Ð@UÔVYÔZjÓVkÐUlÐlmÐntÐmuÐvÐvÐvÜ�
‰
Ô#Ô$ð ô ˜l¨D°$¸ÔBRÐSW×S[ÑS[ÓB\Ó]€FÜ�&ÔÜ	ñ (Ø!'Œ�Ñ÷(ä
‡K�KÐKÈTÏXÉXÐW[Ð]iÔjØ�4ˆ<Ð÷7	añ 	aûô ”Z×/Ñ/Ð0ò JØÐ:¸6¸(À#ÀaÀSÐIÐIÕIûðJú÷*(ð (úsC   ÂH4 Â(A H'Ã(H4 Ç/
I$È'H1È,H4 È4I!É	IÉI!ÉI!É$I-c                 ód   — t         5  t        j                  | «      cd d d «       S # 1 sw Y   y xY wr   )r¼   r	   rF   rs   s    r   Ú
get_serverrÆ   õ   s&   € Ü	ñ *Ü�|‰|˜LÓ)÷*÷ *ò *ús   ‡&¦/c                 óÈ  — t         5  t        j                  | d«      }ddd«       �X|j                  «       rH	 t	        |j
                  j                  |j                  «       	 |j
                  j                  d¬«       	 t        «       | › d�z  j                  d¬«       |�t        j                  d	| «       yy# 1 sw Y   ŒžxY w# t        $ r@}t        j                  d|j                  |«       t        |j
                  «       Y d}~Œ¨d}~ww xY w# t        $ r t        |j
                  «       Y Œ³w xY w# t         $ r!}t        j                  d| |«       Y d}~Œ»d}~ww xY w)
zaStop the server for ``session_name`` (tree-kill, plain terminate on failure) and drop its record.Nz&lightpanda tree-kill failed for %s: %sr�   r}   rr   T©Ú
missing_okz-could not remove lightpanda record for %s: %sz'Stopped lightpanda serve for session %s)r¼   r	   Úpopr   rŸ   r   r•   r   r/   r0   r1   r   r”   r’   r`   ÚunlinkrŠ   )r   r    r3   s      r   Ústop_lightpandarÌ   ú   s-  € ä	ñ 2Ü—‘˜l¨DÓ1ˆ÷2àÐ˜fŸo™oÔ/ð	$Ü�v—{‘{—‘¨×(9Ñ(9Ô:ð	$Ø�K‰K×Ñ QÐÔ'ðWÜ	‹˜<˜.¨Ð.Ñ	.×6Ñ6À$Ð6ÔGð ÐÜ�‰Ð>ÀÕMð ÷!2ð 2ûô
 ò 	$Ü�L‰LÐAÀ6×CVÑCVÐXYÔZÜ�v—{‘{×#Ñ#ûð	$ûô
 ò 	$Ü�v—{‘{Ö#ð	$ûô ò WÜ�‰ÐDÀlÐTU×VÑVûðWúsL   ‡B;¹*C Á$D Â D7 Â;CÃ	DÃ6DÄDÄD4Ä3D4Ä7	E!Å EÅE!c                  óØ   — t         5  t        t        «      } ddd«        D ]  }	 t        |«       Œ y# 1 sw Y   ŒxY w# t        $ r!}t
        j                  d||«       Y d}~ŒCd}~ww xY w)zEStop every server this process started. Idempotent; safe from atexit.Nz!lightpanda stop failed for %s: %s)r¼   Úlistr	   rÌ   r/   r0   r1   )ÚnamesrD   r3   s      r   Ústop_all_lightpandarÐ     si   € ä	ñ Ü”X“ˆ÷àò Gˆð	GÜ˜DÕ!ñG÷ð ûô
 ò 	GÜ�L‰LÐ<¸dÀA×FÑFûð	Gús   ‡3¥?³<¿	A)ÁA$Á$A)c                 óü   — 	 ddl }|j                  | «      }d|j                  «       j                  «       vry|j	                  «       }d|vst        |«      |vry|rddlm}  || «      |k(  S 	 y# t        $ r Y yw xY w)zJTrue only when ``pid`` is verifiably the ``lightpanda serve`` we recorded.r   Nr(   Fr¯   )Úget_process_start_timeT)	ÚpsutilÚProcessrD   ÚlowerÚcmdliner   Úgateway.statusrÒ   r/   )r•   r   r   rÓ   r   rÖ   rÒ   s          r   Ú_is_lightpanda_processrØ     s‡   € ðÛØ�~‰~˜cÓ"ˆØ˜tŸy™y›{×0Ñ0Ó2Ñ2ØØ—,‘,“.ˆØ˜'Ñ!¤S¨£Y°gÑ%=ØÙÝ=Ù)¨#Ó.°*Ñ<Ð<ð ð
 øô ò Ùðús   ‚5A/ ¸!A/ ÁA/ Á/	A;Á:A;c                  ód  — 	 t        «       } 	 ddlm} d}t        | j                  d«      «      D �]`  }|j                  }	 t        j                  |j                  d¬«      «      }t        |t        «      s!t        dt        |«      j                   › �«      ‚	 |j'                  d«      }|t)        j*                  «       k(  r#t,        5  |t.        v r
	 ddd«       Œ©	 ddd«       n|r |t1        |«      «      rŒÇ|j'                  d«      }|rut3        t1        |«      |j'                  d«      |j'                  d«      «      rA	 t5        t1        |«      |j'                  d«      «       |dz  }t        j7                  d||«       |j%                  d	¬
«       �Œc |S # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w# t        $ r Y yw xY w# t"        t        f$ r |j%                  d	¬
«       Y �ŒÅw xY w# 1 sw Y   �ŒxY w# t        $ r!}t        j                  d||«       Y d}~Œ¬d}~ww xY w)zÚKill ``lightpanda serve`` processes whose owning Hermes is gone; return the count. A live owner is
    never touched; a PID is only signalled after psutil confirms it is still ``lightpanda serve`` on the recorded port.z$lightpanda state dir unavailable: %sNr   )Ú_pid_existsz*.jsonr�   r¤   zexpected a JSON object, got TrÈ   r¢   r•   r   r   rN   z4Reaped orphaned lightpanda serve pid %s (session %s)z,orphan lightpanda kill failed for pid %s: %s)r`   r/   r0   r1   r×   rÚ   ÚsortedÚglobÚstemr©   ÚloadsÚ	read_textÚ
isinstanceÚdictÚ
ValueErrorÚtyper   rŠ   rË   rF   r>   r¦   r¼   r	   r!   rØ   rŸ   r½   )	Ú	state_dirr3   rÚ   ÚreapedÚrecord_pathr   r«   r¢   r•   s	            r   Úreap_orphaned_lightpandarç   -  s	  € ðÜ“Lˆ	ðÝ.ð €FÜ˜iŸn™n¨XÓ6Ó7ó ,ˆØ"×'Ñ'ˆð	Ü—Z‘Z × 5Ñ 5¸wÐ 5Ó GÓHˆFÜ˜f¤dÔ+Ü Ð#?ÄÀVÃ×@UÑ@UÐ?VÐ!WÓXÐXð ,ð
 —J‘J˜{Ó+ˆ	ØœŸ	™	›Ò#Üñ Ø¤8Ñ+Ø÷ð Ø+÷ð ñ ™;¤s¨9£~Ô6ØØ�j‰j˜ÓˆÙÔ)¬#¨c«(°F·J±J¸vÓ4FÈÏ
É
ÐS_ÓH`ÔaðUÜœ3˜s›8 V§Z¡Z°Ó%=Ô>Ø˜!‘�Ü—‘ÐRÐTWÐYeÔfð 	×Ñ dÐÖ+ð1,ð2 €MøôE ò Ü�‰Ð;¸QÔ?Üûðûô
 ò Ùðûô œÐ$ò 	Ø×Ñ¨$ÐÔ/Úð	ú÷
ñ ûô ò UÜ—‘ÐKÈSÐRS×TÑTûðUús`   ‚
F �G Á AGÃ
G8Ä>A HÆ	F>ÆF9Æ9F>Ç	GÇGÇ!G5Ç4G5Ç8H	È	H/ÈH*È*H/)gš™™™™™É?)r(   )r   N)<Ú__doc__Ú	functoolsr©   Úloggingr>   rH   rP   r"   Ú	threadingr§   Údataclassesr   Úpathlibr   Útypingr   r   r   Ú	getLoggerr   r0   ÚLIGHTPANDA_INSTALL_URLrµ   r¹   r»   rˆ   r	   r   r    ÚLockr¼   r   rÎ   r4   rL   r!   rY   r`   rb   rm   Ú	lru_cacher%   rp   rt   rá   ry   Úfloatr   r�   r#   r”   r›   rŸ   r¬   rÄ   rÆ   rÌ   rÐ   rØ   rç   r&   r   r   ú<module>rô      sv  ðòó Û Û Û 	Û Û Û Û Û Ý !Ý ß (Ñ (à	ˆ×	Ñ	˜8Ó	$€àXÐ Ø4Ð5KÐ4LÐLvÐvÐ àÐ ØÐ ØÐ à*,€ˆ$ˆsÐ&Ð&Ñ
'Ó ,Ø�	—‘Ó €ð ÷(ð (ó ð(ð˜$ó ð] ¨¡ó ]ð * #ó *ð�Dó ð
˜ó 
ð &Ð ð €×Ñ˜QÔð¨ð °ò ó  ðð(1˜sð 1 tó 1ð!�dó !ñ�Cð  %ð °$ó ðV˜ð V ó Vñ
9�Z×%Ñ%ð 
9¨Sð 
9ÀDó 
9ð˜#ð  (¨3¡-ó ðL�Cð L¨Dó Lð]Ð*ð ]¨tó ]ð LQò . Cð .ÀDð .ÐUZÐ[cÐdtÑ[uÐwð  ADñ  xEð  \Eñ  VFó .ðb*˜Sð * XÐ.>Ñ%?ó *ð
N #ð N¨$ó Nó,Gð ð ¸$ó ð$' #ô 'r   