+
    iHàjw  ã                  óÆ   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	H
t
Ht ^ RIHt Rt^t0 Rmt^tR R ltR	 R
 ltR R ltR R ltRRRRRR/R R lltR R ltR# )a|  ``$HERMES_HOME/cache/<kind>/`` materialisation helpers for the image/video
generation provider ABCs.

Several backends return *ephemeral* delivery URLs that expire before a downstream
consumer (Telegram ``send_photo``, browser fetch) can resolve them, so providers
materialise the bytes locally at tool-completion time. Filenames are
``<prefix>_<YYYYMMDD_HHMMSS>_<uuid8>.<ext>``.
)ÚannotationsN)ÚPath)ÚDictÚOptionalÚTuple)ÚurljoinÚ	generatedc               ó    € V ^8„  d   QhRRRR/# )é   ÚkindÚstrÚreturnr   © )Úformats   "Ú9/home/ubuntu/.hermes/hermes-agent/agent/provider_media.pyÚ__annotate__r      s   € ÷ ñ �Cð ˜Dñ ó    c                ób   € ^ RI Hp V! 4       R,          V ,          pVP                  RRR7       V# )zBReturn ``$HERMES_HOME/cache/<kind>/``, creating parents as needed.)Úget_hermes_homeÚcacheT)ÚparentsÚexist_ok)Úhermes_constantsr   Úmkdir)r   r   Úpaths   &  r   Ú	cache_dirr      s,   € å0ÙÓ˜wÕ&¨Õ-€DØ‡J�J�t d€JÔ+Ø€Kr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r
   r   r   ÚprefixÚ	extensionr   r   r   )r   s   "r   r   r   '   s.   € ÷ Bñ B�Sð B #ð B°#ð B¸$ñ Br   c                óâ   € \         P                   P                  4       P                  R 4      p\        P                  ! 4       P
                  R,          p\        V 4      V RV RV RV 2,          # )z%Y%m%d_%H%M%S:Né   NÚ_Ú.)ÚdatetimeÚnowÚstrftimeÚuuidÚuuid4Úhexr   )r   r   r   ÚtsÚshorts   &&&  r   Ú
cache_pathr+   '   s]   € Ü	×	Ñ	×	Ñ	Ó	 ×	)Ñ	)¨/Ó	:€BÜ�JŠJ‹L×Ñ˜RÕ €EÜ�T‹? ˜x q¨¨¨A¨e¨W°A°i°[ÐAÕAÐAr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r
   r   r   ÚrawÚbytesr   r   r   r   r   )r   s   "r   r   r   -   s/   € ÷ ñ �Sð ˜uð °ð Àð Èñ r   c               ó@   € \        WV4      pVP                  V4       V# )z:Write raw bytes to the cache and return the absolute path.)r+   Úwrite_bytes)r   r-   r   r   r   s   &&$$ r   Ú
save_bytesr1   -   s!   € ä�d IÓ.€DØ×Ñ�SÔØ€Kr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )r
   r   r   Úb64_datar   r   r   r   r   )r   s   "r   r   r   4   s6   € ÷ \ñ \�3ð \ #ð \°#ð \À#ð \È$ñ \r   c               óF   € \        V \        P                  ! V4      W#R7      # )z?Decode base64 data into the cache and return the absolute path.)r   r   )r1   Úbase64Ú	b64decode)r   r3   r   r   s   &&$$r   Úsave_b64r7   4   s   € ä�dœF×,Ò,¨XÓ6¸vÔ[Ð[r   ÚheadersÚrequire_known_content_typeFÚtrusted_originc               óT   € V ^8„  d   QhRRRRRRRRRRR	RR
RRRRRRRRRRRRRRRRR/# )r
   r   r   Úurlr   ÚtimeoutÚfloatÚ	max_bytesÚintÚ
chunk_sizeÚcontent_typeszdict[str, str]Úurl_extensionsztuple[str, ...]Údefault_extensionÚlabelÚempty_errorr8   zOptional[dict[str, str]]r9   Úboolr:   r   r   r   )r   s   "r   r   r   9   s´   € ÷ T^ñ T^Ø
ðT^ØðT^Ø$'ðT^Ø27ðT^ØDGðT^ØUXðT^à!ðT^à3BðT^àWZðT^ð ðT^ð !ðT^ð ,DðT^ð !%ð	T^ð ?Cð	T^ð
 
ñT^r   c               óü  a€ ^ RI p^ RIHpHpHp YTppp\        \        ^,           4       EFð  pV'       d3   V! V4      '       d   \        V	 RV 24      hVP                  VRR7      pM(V! V4      '       g   \        V	 RV 24      hV! VRR7      pT;_uu_ 4        VP                  RVVR7      ;_uu_ 4       pVP                  \        9   dW   VP                  P                  R	4      pV'       g   \        V	 R
V 24      h\        VV4      RRppp RRR4       RRR4       Kù  VP                  '       g!   VP!                  4        VP#                  4        VP                  P                  R4      ;'       g    RP%                  R^4      ^ ,          P'                  4       P)                  4       pVP                  V4      pV'       d   Vf   \        T	 RT;'       g    R 24      hVf>   VP%                  R^4      ^ ,          P)                  4       o\+        V3R lV 4       V4      p\-        WV4      p^ pVP/                  R4      ;_uu_ 4       pVP1                  VR7       Fm  pV'       g   K  V\3        V4      ,          pVV8”  d7   VP5                  4        \7        V4       \        V	 RV RVR,           R24      hVP9                  V4       Ko  	  RRR4       V^ 8X  d'   \7        V4       \        V
P;                  VR7      4      hVuuRRR4       uuRRR4       u # 	  \        V	 R\         RV 24      h  + '       g   i     Ln; i  + '       g   i     M; i RRR4       EK>    + '       g   i     EKP  ; i)u9  Stream-download *url* into the cache with a size cap.

The extension comes from the response ``Content-Type`` (an explicit table â€”
never inherit a type pointing at HTML/JSON from a degenerate response), then
the URL suffix (some CDNs return ``application/octet-stream``), then
*default_extension*. Raises on any network / HTTP / oversize / empty error so
callers can fall back to the bare URL; a partial file is never left behind.

The URL is provider-supplied, so every hop is validated with
``tools.url_safety`` before a socket opens (and again at TCP connect by the
guarded transport, closing DNS-rebinding TOCTOU). Caller-supplied *headers*
(e.g. provider auth) go to the first hop only â€” a redirect target never
receives them.

*trusted_origin* is for callers that built *url* from the operator's own
provider ``base_url`` (not from a provider response): the first hop skips the
private-address class check so a LAN/loopback relay works without
``security.allow_private_urls``, but the cloud-metadata floor still applies and
every redirect target is re-validated in full.
N)Úcreate_ssrf_safe_clientÚis_always_blocked_urlÚis_safe_urlz( URL targets an always-blocked address: F)r=   Úfollow_redirectsz# URL failed the SSRF safety check: ÚGET)r8   Úlocationz) download redirected without a Location: zContent-TypeÚ Ú;z+ download returned unexpected Content-Type z	(missing)Ú?c              3  ór   <"  € T F,  pSP                  R V 24      '       g   K  VR8X  d   RMTx € K.  	  R# 5i)r"   ÚjpegÚjpgN)Úendswith)Ú.0ÚextÚurl_paths   & €r   Ú	<genexpr>Úsave_url.<locals>.<genexpr>v   s7   øé € ÐsÁN¸SÐV^×VgÑVgÐjkÐloÐkpÐhq×VrÔ8 #¨¤-™°SÔ8ÃNùs   ƒ7¤7Úwb)rA   z at z	 exceeds zMB cap; refusing to cache.)r<   z download exceeded z redirects: i   )ÚhttpxÚtools.url_safetyrI   rJ   rK   ÚrangeÚ_MAX_SAVE_URL_REDIRECTSÚ
ValueErrorÚClientÚstreamÚstatus_codeÚ_REDIRECT_STATUS_CODESr8   Úgetr   Ú
is_successÚreadÚraise_for_statusÚsplitÚstripÚlowerÚnextr+   ÚopenÚ
iter_bytesÚlenÚcloseÚ_unlink_quietÚwriter   ) r   r<   r   r=   r?   rA   rB   rC   rD   rE   rF   r8   r9   r:   r\   rI   rJ   rK   Úcurrent_urlÚhop_headersÚtrusted_hopr!   ÚclientÚresponserN   Úcontent_typer   r   Úbytes_writtenÚfhÚchunkrX   s    &&$$$$$$$$$$$$                 @r   Úsave_urlr|   9   s$  ø€ ó4 ç\Ñ\à,/¸.˜k�€KÜÔ*¨QÕ.×/ˆßÙ$ [×1Ò1Ü  E 7Ð*RÐS^ÐR_Ð!`ÓaÐaØ—\‘\¨'ÀE�\ÓJ‰Fá˜{×+Ò+Ü  E 7Ð*MÈkÈ]Ð![Ó\Ð\Ù,°WÈuÔUˆFßŠVØ—‘˜u k¸;�×GÔGÈ8Ø×'Ñ'Ô+AÔAØ'×/Ñ/×3Ñ3°JÓ?�Hß#Ü(¨E¨7Ð2[Ð\gÐ[hÐ)iÓjÐjÜ<CÀKÐQYÓ<ZÐ\`Ðbg¨k �KØ÷ H÷ ‰Vð  ×*×*Ð*Ø—M‘M”OØ×-Ñ-Ô/à (× 0Ñ 0× 4Ñ 4°^Ó D× JÐ JÈ×QÑQÐRUÐWXÓYÐZ[Õ\×bÑbÓd×jÑjÓl�Ø)×-Ñ-¨lÓ;�	ß-°)Ò2CÜ$Ø ˜'Ð!LÈ\×MhÐMhÐ]hÐLiÐjóð ð Ò$Ø*×0Ñ0°°aÓ8¸Õ;×AÑAÓC�HÜ $ÜsÁNÓsØ)ó!�Iô " $°	Ó:�Ø !�Ø—Y‘Y˜t—_”_¨Ø!)×!4Ñ!4À
Ð!4Ö!K˜ß$Ù$Ø%¬¨U«Õ3˜Ø(¨9Ô4ØŸH™HœJÜ)¨$Ô/Ü",Ø#( '¨¨c¨U°)¸IÈ+Õ<VÐ;WÐWqÐ ró#ð ð Ÿ™ žñ "L÷ %ð ! AÔ%Ü! $Ô'Ü$ [×%7Ñ%7¸CÐ%7Ó%@ÓAÐAà÷U HÑG÷ ŒVñ 0ôj ˜�wÐ1Ô2IÐ1JÈ,ÐWZÐV[Ð\Ó
]Ð]÷% %—_ú÷3 H×GúÐG÷ �V�VŒVúsj   ÂM)Â88M
Ã1!M
ÄM)Ä$M
Ä6AM
Å8AM
ÇM
ÇA1M
ÉBL7Ë6M
Ì
M)Ì7MÍM
Í
MÍM)Í)M;c               ó    € V ^8„  d   QhRRRR/# )r
   r   r   r   ÚNoner   )r   s   "r   r   r   �   s   € ÷ ñ ˜ð  ñ r   c                óL   €  V P                  4        R #   \         d     R # i ; i)N)ÚunlinkÚOSError)r   s   &r   rq   rq   �   s"   € ðØ�‰ŽøÜô Úðús   ‚ ”#¢#>   é-  é.  é/  é3  é4  )Ú__doc__Ú
__future__r   r5   r#   r&   Úpathlibr   Útypingr   r   r   Úurllib.parser   ÚGENERATED_SUBDIRÚMEDIA_CACHE_MAX_AGE_HOURSrd   r_   r   r+   r1   r7   r|   rq   r   r   r   Ú<module>rŽ      s‹   ðñõ #ã Û Û Ý ß (Ñ (Ý  ð
 Ð ð Ð â2Ð ØÐ õõBõõ\ð
T^ð GKðT^ð (-ð	T^ð FK÷	T^÷nr   