Ë
    âß²jf;  ã                  ó  — d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
 ddlmZmZmZmZmZ ddlmZmZmZ  ej.                  e«      ZdZdZd	Z ej:                  d
ej<                  «      Zdd„Z dd„Z!dd„Z" G d„ de«      Z#ddiZ$d„ Z%y)uå  xAI Web Search â€” search-only provider backed by Grok's server-side ``web_search`` tool on the
Responses API (https://docs.x.ai/developers/tools/web-search); Grok is asked for structured JSON
so rows match every other Hermes web provider. Config: ``web.backend: "xai"``; optional ``web.xai``:
``model`` (default grok-build-0.1), ``allowed_domains`` / ``excluded_domains`` (max 5, mutually
exclusive), ``timeout`` (default 90s). Auth: Grok OAuth via ``hermes auth``, else XAI_API_KEY.
é    )ÚannotationsN)ÚAnyÚDictÚListÚOptional)ÚBaseWebSearchProviderÚsearch_failÚ	search_okÚsetup_schemaÚ	title_hit)Úhas_xai_credentialsÚhermes_xai_user_agentÚresolve_xai_http_credentialszgrok-build-0.1éZ   é   z\{[\s\S]*\}c                 óö   — 	 ddl m}   | «       }dD ]%  }t        |t        «      r|j	                  |«      nd}Œ' t        |t        «      r|S i S # t
        $ r"}t        j                  d|«       i cY d}~S d}~ww xY w)z7Read ``web.xai`` from config.yaml (returns {} on miss).r   )Úload_config)ÚwebÚxaiNz!Could not load web.xai config: %s)Úhermes_cli.configr   Ú
isinstanceÚdictÚgetÚ	ExceptionÚloggerÚdebug)r   ÚcfgÚkeyÚexcs       ú=/home/ubuntu/.hermes/hermes-agent/plugins/web/xai/provider.pyÚ_load_xai_web_configr!      sr   € ðÝ1Ù‹mˆØ!ò 	BˆCÜ",¨S´$Ô"7�#—'‘'˜#”,¸T‰Cð	Bä  ¤dÔ+ˆsÐ3°Ð3øÜò Ü�‰Ð8¸#Ô>Ø�	ûðús$   ‚AA ÁA Á	A8ÁA3Á-A8Á3A8c                ó¾   — t        | t        «      rG| D �cg c]4  }t        |t        «      sŒ|j                  «       sŒ%|j                  «       ‘Œ6 c}d t         S g S c c}w ©N)r   ÚlistÚstrÚstripÚ_MAX_DOMAIN_FILTERS)ÚvalueÚitems     r    Ú_coerce_domain_listr*   )   sY   € ÜoyÐzô  BFô  pG UÖU˜T¬j¸¼sÕ.CÈÏ
É
ÍˆD�J‰J�LÒUÐVjÔWjÐkð  Oð  MOð  OùÒUs   •A«A¼Ac                óB   — 	  | |«      S # t         t        f$ r |cY S w xY wr#   )Ú	TypeErrorÚ
ValueError)Úcastr(   Údefaults      r    Ú_coercer0   -   s*   € ðÙ�E‹{ÐøÜ”zÐ"ò ØŠðús   ‚
 Š�c                  ó°   — e Zd ZdZdZdZdd„Zddd„Zedd„«       Z	edd„«       Z
edd„«       Zedd	„«       Zedd
„«       Zedd„«       Zedd„«       Zdd„Zy)ÚXAIWebSearchProvideru  Sends a structured prompt with ``tools=[{"type": "web_search"}]`` and parses the JSON Grok
    returns; falls back to message annotations, then ``citations``. Trust model: Grok *generates*
    the URLs/titles/descriptions and is steerable by the query text â€” validate before fetching.r   úxAI Web Search (Grok)c                ó   — t        «       S )zØCheap probe (env var OR auth-store tokens). Deliberately NOT
        ``resolve_xai_http_credentials``: must never refresh tokens or take the
        auth-store lock, since this runs on every ``hermes tools`` repaint.)r   ©Úselfs    r    Úis_availablez!XAIWebSearchProvider.is_available<   s   € ô #Ó$Ð$ó    c                óð  — 	 ddl m}  |«       rt        d«      S 	 t	        «       }t        |j                  d«      xs d«      j                  «       }t        |j                  d«      xs d«      j                  «       j                  d«      }|st        d	«      S t        d
t        t        t        |d«      d«      «      }t        «       }t        |j                  d«      t
        «      r|d   j                  «       ndxs t        }| j!                  |«      }	|	€t        d«      S |d| j#                  ||«      dœg|	gdgdœ}
	 dd l}t(        j+                  d||||«       | j-                  ||
|t        t.        |j                  dt0        «      t0        «      |j                  d«      dk(  ¬«      \  }}|r|S t        |t2        «      r|j                  d«      nd }t        |t2        «      rL|j                  d«      xs |j                  d«      xs d}t(        j5                  d|«       t        d|› �«      S t7        | j9                  ||¬«      «      S # t        $ r Y �Œ?w xY w# t&        $ r t        d«      cY S w xY w) Nr   )Úis_interruptedÚInterruptedÚapi_keyÚ Úbase_urlzhttps://api.x.ai/v1ú/z_No xAI credentials found. Run `hermes auth` to sign in with xAI Grok OAuth, or set XAI_API_KEY.é   r   éd   ÚmodelzZweb.xai.allowed_domains and web.xai.excluded_domains cannot both be set (xAI restriction).Úuser)ÚroleÚcontentÚno_inline_citations)rB   ÚinputÚtoolsÚincludez4httpx is not installed (required for xAI web search)z0xAI web search via %s: '%s' (limit=%d, model=%s)ÚtimeoutÚproviderz	xai-oauth)Úis_oauth_pathÚerrorÚmessageÚcodezunknown errorz*xAI web search returned error envelope: %szxAI returned an error: ©Úlimit)Útools.interruptr:   Ú_failr   r   r%   r   r&   ÚrstripÚmaxÚminr0   Úintr!   r   ÚDEFAULT_MODELÚ_web_search_toolÚ_build_promptÚhttpxÚImportErrorr   ÚinfoÚ_post_responsesÚfloatÚDEFAULT_TIMEOUTr   Úwarningr
   Ú_extract_results)r6   ÚqueryrQ   r:   Úcredsr<   r>   r   rB   Úweb_search_toolÚpayloadr[   ÚdatarM   Ú	api_errorÚerr_msgs                   r    ÚsearchzXAIWebSearchProvider.searchB   sg  € ð	Ý6ÙÔÜ˜]Ó+Ð+ð  ô -Ó.ˆÜ�e—i‘i 	Ó*Ò0¨bÓ1×7Ñ7Ó9ˆÜ�u—y‘y Ó,ÒEÐ0EÓF×LÑLÓN×UÑUÐVYÓZˆÙÜÐzÓ{Ð{ä�A”sœ7¤3¨¨qÓ1°3Ó7Ó8ˆÜ"Ó$ˆÜ)3°C·G±G¸GÓ4DÄcÔ)J��W‘×#Ñ#Ô%ÐPRÒdÔWdˆØ×/Ñ/°Ó4ˆØÐ"äÐuÓvÐvà,1ÀfÐY]×YkÑYkÐlqÐsxÓYyÑ=zÐ<{ð  HWð  GXð  f{ð  e|ñ  #}ˆð	QÛô 	�‰ÐFÈÐRWÐY^Ð`eÔfØ×*Ñ*Ø�g˜w¬´°s·w±w¸yÌ/Ó7ZÔ\kÓ(lØ Ÿ9™9 ZÓ0°KÑ?ð +ó 
‰ˆˆeñ ØˆLô *4°D¼$Ô)?�D—H‘H˜WÔ%ÀTˆ	Ü�i¤Ô&Ø—m‘m IÓ.ÒZ°)·-±-ÀÓ2GÒZÈ?ˆGÜ�N‰NÐGÈÔQÜÐ2°7°)Ð<Ó=Ð=ä˜×.Ñ.¨t¸5Ð.ÓAÓBÐBøôI ò 	Úð	ûô& ò 	QÜÐOÓPÒPð	Qús#   ‚I Å I É	IÉIÉI5É4I5c           	     óè   — dD �ci c]  }|t        | j                  |«      «      “Œ }}|j                  «       D ��ci c]  \  }}|sŒ	||“Œ }}}t        |«      dk(  ry|rd|dœS ddiS c c}w c c}}w )z‰``web_search`` tool spec with optional domain filters; None when both
        allowed and excluded are set (xAI rejects the combination).)Úallowed_domainsÚexcluded_domainsé   NÚ
web_search)ÚtypeÚfiltersrp   )r*   r   ÚitemsÚlen)r   Úkrq   Úvs       r    rY   z%XAIWebSearchProvider._web_search_toolm   s   € ð @gÖg¸!�1Ô)¨#¯'©'°!«*Ó5Ñ5ÐgˆÐgØ$+§M¡M£O×9™D˜A˜q²q�1�a‘4Ð9ˆÑ9Üˆw‹<˜1ÒØÙ=D˜°Ñ9Ð`È6ÐS_ÐJ`Ð`ùò	 hùÛ9s   …"A)¼
A.ÁA.c          	     óÂ  ‡— ddl }d‰› �dt        «       dœ}dˆfd„}d}t        d«      D ],  }		 |j                  | › d�|||¬	«      }|j	                  «         n |€dt        d«      fS 	 |j!                  «       dfS # |j
                  $ rÜ}
|
j                  �|
j                  j                  nd}|d
k(  r5|	dk(  r0|r.t        j                  d«        |«       x}r|d|› �cŠ|d<   Y d}
~
Œ¾	 |
j                  �|
j                  j                  dd nd}n# t        $ r d}Y nw xY wt        j                  d||«       dt        d|› d|› �j                  «       «      fcY d}
~
c S d}
~
w|j                  $ r2}
t        j                  d|
«       dt        d|
› �«      fcY d}
~
c S d}
~
ww xY w# t        $ r-}
t        j                  d|
«       dt        d«      fcY d}
~
S d}
~
ww xY w)uf  POST ``/responses`` â†’ ``(parsed_json, None)`` or ``(None, failure_envelope)``.

        Two attempts: on a first-call 401 with OAuth creds, force-refresh once and retry
        (opaque tokens the resolver can't pre-check; mid-window revocation/rotation).
        XAI_API_KEY creds can't be refreshed, so they skip the retry rather than burn quota.
        r   NzBearer zapplication/json)ÚAuthorizationzContent-Typez
User-Agentc                 óÞ   •— 	 t        t        d‰¬«      j                  d«      xs d«      j                  «       } | ‰k7  r| S dS # t        $ r }t
        j                  d|«       Y d}~yd}~ww xY w)zeNew bearer after a 401, or "" when refresh fails / returns the same token (retry would be pointless).T)Úforce_refreshÚapi_key_hintr<   r=   z1xAI web search OAuth refresh after 401 failed: %sN)r%   r   r   r&   r   r   ra   )r   Úrefresh_excr<   s     €r    Ú_refreshed_keyz<XAIWebSearchProvider._post_responses.<locals>._refreshed_key�   sj   ø€ ðÜÔ6ÀTÐX_Ô`×dÑdÐenÓoÒuÐsuÓv×|Ñ|Ó~�Ø! Wšn�sÐ4°"Ð4øÜò Ü—‘ÐRÐT_Ô`Üûðús   ƒ=A ÁA Á	A,ÁA'Á'A,rn   z
/responses)ÚheadersÚjsonrJ   i‘  zQxAI web search got 401 on first attempt; forcing OAuth refresh and retrying once.rw   i,  r=   zxAI web search HTTP %d: %szxAI web search returned HTTP z: z xAI web search request error: %szCould not reach xAI: z#xAI web search produced no responsezxAI web search bad JSON: %sz/Could not parse xAI Responses API reply as JSON)Úreturnr%   )r[   r   ÚrangeÚpostÚraise_for_statusÚHTTPStatusErrorÚresponseÚstatus_coder   r]   Útextr   ra   rS   rT   ÚRequestErrorr~   )r>   rf   r<   rJ   rL   r[   r}   r|   ÚrespÚattemptr   ÚstatusÚnew_keyÚbodys     `           r    r^   z$XAIWebSearchProvider._post_responsesw   s  ø€ ó 	Ø&-¨g¨YÐ$7ÐI[ô  lAó  lCñ  Dˆõ	ð ˆÜ˜Q“xò 	BˆGðBØ—z‘z X J¨jÐ"9À7ÐQXÐbi�zÓj�Ø×%Ñ%Ô'Ùð		Bð* ˆ<ØœÐDÓEÐEÐEð	RØ—9‘9“; Ð$Ð$øð' ×(Ñ(ò ^Ø58·\±\Ð5M˜Ÿ™×1Ò1ÐST�Ø˜S’= W°¢\±mÜ—K‘KÐ sÔtÙ"0Ó"2Ð2�wÐ2Ø<CÀwÈwÈiÐEXÐ9˜ ¨Ñ!9Ü ðØ69·l±lÐ6N˜3Ÿ<™<×,Ñ,¨T¨cÑ2ÐTV‘DøÜ ò Ø’Dðúä—‘Ð;¸VÀTÔJØœUÐ%BÀ6À(È"ÈTÈFÐ#S×#ZÑ#ZÓ#\Ó]Ð]×]ûØ×%Ñ%ò BÜ—‘ÐAÀ3ÔGØœUÐ%:¸3¸%Ð#@ÓAÐA×AûðBûô ò 	RÜ�N‰NÐ8¸#Ô>ØœÐPÓQÐQÕQûð	Rúsr   ®(A;Á)F( Á;F%Â
AE!Ã)'DÄE!ÄDÄE!ÄDÄ:E!ÅF%Å!F%Å3%F ÆF%Æ F%Æ(	GÆ1"GÇGÇGc                ó   — d|› d| › �S )z�Ask for a JSON *object* (cheap to match with ``_JSON_BLOCK_RE``) and forbid
        prose/fences/inline citations to keep the payload parseable.u<  Use the web_search tool to find current information for the query below, then respond with ONLY a single JSON object â€” no prose, no markdown fences, no inline citation links â€” matching this exact schema:

{"results": [{"title": "string", "url": "string", "description": "1-2 sentence summary"}]}

Return at most zy results, ordered by relevance, with absolute https:// URLs. If no usable results exist, return {"results": []}.

Query: © )rc   rQ   s     r    rZ   z"XAIWebSearchProvider._build_prompt¨   s"   € ð
ð $˜Wð %à�Wðð	
r8   c               óÎ  ‡ ‡— ‰ j                  |«      \  }}t        d„ ˆ ˆfd„|D «       D «       d«      }|s'|r'‰ j                  |dj                  |«      ‰¬«      x}r|S |j	                  d«      xs g }t        |t        «      r[t        |d‰ «      D ��cg c]A  \  }}t        |t        «      sŒ|j                  «       sŒ(t        dt        |«      d|dz   «      ‘ŒC c}}S g S c c}}w )	a1  Rows in order of preference: (1) the JSON object in ``output_text`` blocks,
        (2) ``url_citation`` annotations paired with surrounding text, (3) the raw
        ``citations`` list. (2) only short-circuits when it yields rows, so future
        annotation types don't mask real data in ``citations``.c              3  ó&   K  — | ]	  }|sŒ|–— Œ y ­wr#   rŽ   )Ú.0Úps     r    ú	<genexpr>z8XAIWebSearchProvider._extract_results.<locals>.<genexpr>¼   s   è ø€ Òj˜QÒhi”qÑjùs   ‚Šc              3  óD   •K  — | ]  }‰j                  |‰¬ «      –— Œ y­w)rP   N)Ú_try_parse_json_results)r‘   ÚbÚclsrQ   s     €€r    r“   z8XAIWebSearchProvider._extract_results.<locals>.<genexpr>¼   s"   øè ø€ Ò"dÐST 3×#>Ñ#>¸qÈÐ#>×#NÑ"dùs   ƒ NÚ
rP   Ú	citationsr=   r@   )Ú_collect_output_textÚnextÚ_results_from_annotationsÚjoinr   r   r$   Ú	enumerater%   r&   Ú_row)	r—   Úresponse_datarQ   Útext_blocksr   Úparsedr™   ÚiÚus	   ` `      r    rb   z%XAIWebSearchProvider._extract_resultsµ   s   ù€ ð $'×#;Ñ#;¸MÓ#JÑ ˆ�[ÜÑjÔ"dÐXcÔ"dÔjÐlpÓqˆÙ‘k°×1NÑ1NÈ{Ð\`×\eÑ\eÐfqÓ\rÐzÐ1Nó  2Að  (A vð  (AØˆMØ!×%Ñ% kÓ2Ò8°bˆ	ô ~Hð  IRô  TXô  ~Y¼	À)ÈFÈUÐBSÓ8T×y±°°1ÔXbÐcdÔfiÕXjÐop×ovÑovÕox”�Rœ˜Q›  Q¨¡UÕ+Óyð  	að  _að  	aùÓys   ÂC!Â.C!Â?C!c           	     óš  — | j                  d«      }t        |t        «      r|ng D ��cg c]u  }t        |t        «      rc|j                  d«      dk(  rOt        |j                  d«      t        «      r0|d   D ](  }t        |t        «      r|j                  d«      dk(  r|‘Œ* Œw }}}|D �cg c];  }t        |j                  d«      t        «      sŒ#|d   j                  «       sŒ7|d   ‘Œ= }}|D ��cg c]?  }t        |j                  d«      t        «      sŒ#|d   D ]  }t        |t        «      sŒ|‘Œ ŒA }}}||fS c c}}w c c}w c c}}w )zC(text_blocks, annotations) from ``response.output`` message chunks.Úoutputrp   rN   rE   Úoutput_textr†   r   )r   r   r$   r   r%   r&   )	r    r¦   r)   ÚchunkÚchunksÚcr¡   Úar   s	            r    rš   z)XAIWebSearchProvider._collect_output_textÂ   sV  € ð ×"Ñ" 8Ó,ˆô $.¨f´dÔ#;™À÷
àÜ˜$¤Ô%¨$¯(©(°6Ó*:¸iÒ*GÌJÐW[×W_ÑW_Ð`iÓWjÔlpÔLqØ˜i™ò	
ð Ü˜%¤Ô&¨5¯9©9°VÓ+<ÀÒ+Mò	 ð
Øð
ˆñ 
ð +1Öi Q´J¸q¿u¹uÀV»}ÌcÕ4RÐWXÐY_ÑW`×WfÑWfÕWh�q˜“yÐiˆÐiØ"(÷  E˜Q¬J°q·u±u¸]Ó7KÌTÕ,RÐ\]Ð^kÑ\lò  EÐWXÔpzÐ{|ô  Cõ  qD’qð  E�qð  Eˆñ  EØ˜KÐ'Ð'ùó
ùò jùó Es*   ©A:D<Â*$EÃEÃ#EÃ1$EÄEÄ/Ec               óú  — t         j                  | «      }| g|r&|j                  d«      | k7  r|j                  d«      gng z   D �]  }	 t        j                  |«      }t        |t        «      r|j                  d«      nd}t        |t        «      sŒNg }|d| D ]·  }t        |t        «      r)t        |j                  dd«      «      j                  «       nd}|sŒA|j                  t        t        |j                  dd«      «      j                  «       |t        |j                  dd«      «      j                  «       t        |«      dz   «      «       Œ¹ |s�Œ|c S  y# t        j
                  t        f$ r Y �Œ5w xY w)	z­Parse a JSON object with a ``results`` array out of ``text``; None when absent.
        Whole string first, then the regex-matched block (reasoning models prefix narration).r   ÚresultsNÚurlr=   ÚtitleÚdescriptionr@   )Ú_JSON_BLOCK_RErj   Úgroupr~   ÚloadsÚJSONDecodeErrorr-   r   r   r   r$   r%   r&   ÚappendrŸ   rs   )	r†   rQ   ÚmatchÚ	candidater¢   r­   Ú
normalizedÚrowr®   s	            r    r•   z,XAIWebSearchProvider._try_parse_json_resultsÑ   sq  € ô ×%Ñ% dÓ+ˆØ˜±uÀÇÁÈQÃÐSWÒAW E§K¡K°£NÑ#3Ð]_Ñ`ó 	"ˆIðÜŸ™ IÓ.�ô 0:¸&Ä$Ô/G�f—j‘j Ô+ÈTˆGÜ˜g¤tÔ,ØØ/1ˆJØ˜v �ò R�Ü9CÀCÌÔ9N”c˜#Ÿ'™' %¨Ó,Ó-×3Ñ3Ô5ÐTV�Úà×%Ñ%¤d¬3¨s¯w©w°wÀÓ/CÓ+D×+JÑ+JÓ+LÈcÔSVÐWZ×W^ÑW^Ð_lÐnpÓWqÓSr×SxÑSxÓSzÔ|ð  AKó  }Lð  OPñ  }Pó  'Qõ  Rð	Ró
 Ø!Ò!ð	"ð  øô ×(Ñ(¬*Ð5ò Úðús   Á	EÅE:Å9E:c               ó–  — t        «       }g }| D �]6  }|j                  d«      dk(  r)t        |j                  dd«      «      j                  «       nd}|r||v rŒJ|j	                  |«       d}|j                  d«      |j                  d«      }	}t        |t        «      rnt        |	t        «      r^d|cxk  r|	cxk  rt        |«      k  rDn nA|t        d|dz
  «      | j                  «       }t        |«      dkD  r|d	d
 j                  «       }|j                  t        d||t        |«      dz   «      «       t        |«      |k\  s�Œ6 |S  |S )zšFallback rows from ``url_citation`` annotations: URL plus ~200 chars of
        preceding text as the description (the annotation title is just a number).rp   Úurl_citationr®   r=   Ústart_indexÚ	end_indexr   éÈ   i8ÿÿÿNr@   )Úsetr   r%   r&   Úaddr   rW   rs   rU   rµ   rŸ   )
r   Újoined_textrQ   Úseenr­   Úannr®   r°   ÚstartÚends
             r    rœ   z.XAIWebSearchProvider._results_from_annotationsè   s/  € ô ›ˆØ(*ˆØó 	ˆCØ58·W±W¸V³_ÈÒ5V”#�c—g‘g˜e RÓ(Ó)×/Ñ/Ô1Ð\^ˆCÙ˜# ™+ØØ�H‰H�SŒMØˆKØŸ™ Ó/°·±¸Ó1E�3ˆEÜ˜%¤Ô%¬*°S¼#Ô*>À1ÈÔCgÐPSÔCgÔWZÐ[fÓWgÕCgØ)¬#¨a°¸±Ó*=¸eÐD×JÑJÓL�Ü�{Ó# cÒ)Ø"-¨d¨eÐ"4×":Ñ":Ó"<�KØ�N‰Nœ4  C¨´c¸'³lÀQÑ6FÓGÔHÜ�7‹|˜uÔ$ØØˆð	ð ˆr8   c                ó    — t        dddd¬«      S )Nr3   ÚpaiduU   Agentic web search via Grok's web_search tool â€” uses xAI Grok OAuth or XAI_API_KEY.Úxai_grok)Ú
post_setup)r   r5   s    r    Úget_setup_schemaz%XAIWebSearchProvider.get_setup_schemaþ   s   € ô Ø# VØcÐpzô
ð 	
r8   N)r   Úbool)r   )rc   r%   rQ   rW   r   úDict[str, Any])r   rÌ   r   zOptional[Dict[str, Any]])r>   r%   rf   rÌ   r<   r%   rJ   r_   rL   rË   r   z$tuple[Any, Optional[Dict[str, Any]]])rc   r%   rQ   rW   r   r%   )r    rÌ   rQ   rW   r   úList[Dict[str, Any]])r    rÌ   r   z&tuple[List[str], List[Dict[str, Any]]])r†   r%   rQ   rW   r   zOptional[List[Dict[str, Any]]])r   rÍ   rÁ   r%   rQ   rW   r   rÍ   ©r   rÌ   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚNAMEÚDISPLAY_NAMEr7   rj   ÚstaticmethodrY   r^   rZ   Úclassmethodrb   rš   r•   rœ   rÊ   rŽ   r8   r    r2   r2   4   s¸   „ ñeð €DØ*€Ló%ô)CðV òaó ðað ò.Ró ð.Rð` ò

ó ð

ð ò
aó ð
að ò(ó ð(ð òó ðð, òó ðô*
r8   r2   ÚWebSearchProvider)zagent.web_search_providerr×   c                óÌ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t        |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncer@   )	Ú_PLUGIN_COMPAT_LAZYr   ÚAttributeErrorrÏ   Ú	importlibÚhermes_cli.plugin_compatrÙ   ÚgetattrÚimport_module)ÚnameÚtargetrÜ   rÙ   s       r    Ú__getattr__râ     se   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&Ü�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐAr8   rÎ   )r(   r   r   z	List[str])r(   r   r/   r   r   r   )&rÒ   Ú
__future__r   r~   ÚloggingÚreÚtypingr   r   r   r   Úplugins.web._commonr   r	   rS   r
   r   r   rŸ   Útools.xai_httpr   r   r   Ú	getLoggerrÏ   r   rX   r`   r'   ÚcompileÚ	MULTILINEr±   r!   r*   r0   r2   rÚ   râ   rŽ   r8   r    ú<module>rì      sš   ðñõ #ã Û Û 	ß ,Ó ,ç wÕ wß cÑ cà	ˆ×	Ñ	˜8Ó	$€à €Ø€ØÐ ð �—‘˜N¨B¯L©LÓ9€ó
óOóôP
Ð0ô P
ðt ÐKðÐ ó
Br8   