+
    ‰WÝj­Œ  ã                   óš  a € R„ t^0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIHt ^ RIHt ^ RIHtHt ^ RIHt ^ RIt]P.                  ! ]4      t]! 0 R…m4      tR†R R lltR‡R
 R lltR R ltRtR R lt R R lt!R R lt"R R lt#R R lt$R R lt%R R lt&]PN                  R8H  t(]! 0 Rˆm4      t)^t*Rt+Rt,]PZ                  ]P\                  3t/R  R! lt0R" R# lt1R$ R% lt2R& R' lt3R( R) lt4R* R+ lt5R, R- lt6R.R/R/ R0 llt7R1R2R3RR4R5R6RR7R/R8 R9 llt8R‰R: R; llt9R1R2R<R=R>RR?RR3RR7R/R@ RA llt:R<R=R3RR7R/RB RC llt;RD RE lt<RF^R3RRGRR7R/RH RI llt=RJ RK lt>RLR	RMR/RN RO llt?RPRRQRRRRR?R/RS RT llt@RU RV ltARRR/RW RX lltBRY RZ ltC]! 0 RŠm4      tDR[ R\ ltER]R/R^ R_ lltFR‹R` Ra lltGRb Rc ltH/ tI] ^ k ]
P”                  ! 4       tKRd Re ltLRf Rg ltMRŒRh Ri lltNR�Rj Rk lltOR†Rl Rm lltPRŽtQR�tRRn Ro ltSRp Rq ltTRr Rs ltURt Ru ltVRv Rw ltWRx Ry ltXRz R{ ltYR| R} ltZR~ R lt[R€ R� lt\R‚ Rƒ lt]R# )�z*Shared utility functions for hermes-agent.N)Úsuppress©ÚPath)ÚAnyÚUnion)ÚurlparseFc                ó<   € V ^8„  d   QhR\         R\        R\        /# )é   ÚvalueÚdefaultÚreturn©r   Úbool)Úformats   "Ú9C:\Users\johnn\AppData\Local\hermes\hermes-agent\utils.pyÚ__annotate__r      s!   € ÷ ñ œ3ð ¬ð ¼$ñ ó    c                óš   € V f   V# \        V \        4      '       d&   V P                  4       P                  4       \        9   # \        V 4      # )zDCoerce bool-ish values using the project's shared truthy string set.)Ú
isinstanceÚstrÚstripÚlowerÚTRUTHY_STRINGSr   )r
   r   s   &&r   Úis_truthy_valuer      s=   € à‚}ØˆÜ�%œ×ÒØ�{‰{‹}×"Ñ"Ó$¬Ñ6Ð6Ü�‹;Ðr   Ú c                ó<   € V ^8„  d   QhR\         R\         R\        /# )r	   Únamer   r   ©r   r   )r   s   "r   r   r   #   s&   € ÷ Dñ Dœ#ð D¬ð D´Tñ Dr   c                óD   € \        \        P                  ! W4      RR7      # )zBReturn True when an environment variable is set to a truthy value.F©r   ©r   ÚosÚgetenv)r   r   s   &&r   Úenv_var_enabledr#   #   s   € äœ2Ÿ9š9 TÓ3¸UÔCÐCr   c                óz   € V ^8„  d   QhR\         P                  R\        \        \        \        \        3,          /# )r	   Ústr   )r!   Ústat_resultÚtupleÚint)r   s   "r   r   r   (   s0   € ÷ 	Cñ 	C”r—~‘~ð 	C¬%´´S¼#¼sÐ0BÕ*Cñ 	Cr   c                ó^   € V P                   V P                  V P                  V P                  3# )u  Change-detection key for a stat result: ``(st_mtime_ns, st_size, st_ino, st_ctime_ns)``.

mtime + size alone miss a replacement that preserves both (``cp -p``, ``rsync -t``, a tar
restore, a script pinning the timestamp with ``os.utime``). The inode changes on an atomic
replace and ctime cannot be backdated from user space, so the pair catches those writers.
On Windows ``st_ino`` may be 0 and ``st_ctime_ns`` is the creation time â€” both stable across
an in-place rewrite, so the key degrades to mtime + size there rather than misfiring.
)Úst_mtime_nsÚst_sizeÚst_inoÚst_ctime_ns)r%   s   &r   Úfile_signaturer.   (   s#   € ð �N‰N˜BŸJ™J¨¯	©	°2·>±>ÐBÐBr   i ”5wc                ó0   € V ^8„  d   QhR\         R\        /# )r	   Úmtime_nsr   )r(   r   )r   s   "r   r   r   :   s   € ÷ <ñ <œCð <¤Dñ <r   c                óH   € \         P                  ! 4       V ,
          \        8  # )zSTrue while ``mtime_ns`` is too recent to rule out a same-tick write after the stat.)ÚtimeÚtime_nsÚRACY_MTIME_WINDOW_NS)r0   s   &r   Úmtime_is_racyr5   :   s   € ä�<Š<‹>˜HÕ$Ô';Ñ;Ð;r   c                ó>   € V ^8„  d   QhR\         R\        R,          /# ©r	   Úpathr   N©r   r(   )r   s   "r   r   r   ?   s   € ÷ ñ œdð ¤s¨T¥zñ r   c                ó´   €  V P                  4       '       d/   \        P                  ! V P                  4       P                  4      # R#   \         d     R# i ; i)z6Permission bits of *path* if it exists, else ``None``.N)ÚexistsÚstatÚS_IMODEÚst_modeÚOSError)r8   s   &r   Ú_preserve_file_moder@   ?   sC   € ðØ48·K±K·M²MŒt�|Š|˜DŸI™I›K×/Ñ/Ó0ÐKÀtÐKøÜô Úðús   ‚A ˜-A ÁA ÁAÁAc                ó`   € V ^8„  d   QhR\         R\        \        \        3,          R,          /# r7   ©r   r'   r(   )r   s   "r   r   r   G   s'   € ÷ 2ñ 2œtð 2¬¬c´3¨h­¸$Õ(>ñ 2r   c                óº   €  \         P                  R8X  d   V P                  4       MRpT'       d   TP                  TP
                  3# R#   \         d     R# i ; i)z8Owning ``(uid, gid)`` of *path* on POSIX, else ``None``.ÚposixN)r!   r   r<   r?   Úst_uidÚst_gid)r8   r%   s   & r   Ú_preserve_file_ownerrG   G   sO   € ðÜŸG™G wÔ.ˆT�Y‰YŒ[°Dˆ÷ &(ˆB�I‰I�r—y‘yÐ!Ð1¨TÐ1øô ô Úðús   ‚'A ÁAÁAc                ó~   € V ^8„  d   QhR\         R\        \        \        3,          R,          R\        R,          RR/# )r	   r8   ÚownerNÚmoder   rB   )r   s   "r   r   r   P   s;   € ÷ !ñ !¤ð !¬e´C¼°H­oÀÕ.Dð !ÌCÐRVÍJð !Ð[_ñ !r   c                óŠ  € Ve\   \        \        R4      '       dF   \        \        4      ;_uu_ 4        \        P                  ! W^ ,          V^,          4       RRR4       Ve9   \        \        4      ;_uu_ 4        \        P
                  ! W4       RRR4       R# R#   + '       g   i     LN; i  + '       g   i     R# ; i)a�  Best-effort re-apply of uid/gid and permission bits after an atomic replace.

Docker/NAS installs often run some commands as root on a volume owned by the runtime user;
``os.replace`` swaps in the temp file's owner, so privileged callers chown it back. ``mkstemp``
creates 0o600 files; without re-applying *mode* the target would inherit that and break
volume mounts relying on broader permissions.
NÚchown)Úhasattrr!   r   r?   rL   Úchmod)r8   rI   rJ   s   &&&r   Ú_restore_file_metadatarO   P   s{   € ð ÒœW¤R¨×1Ò1Ü”g×ÕÜ�HŠH�T �8 U¨1¥XÔ.÷ àÒÜ”g×ÕÜ�HŠH�TÔ ÷ Ññ ÷ ×ú÷ ×Ðús   ²&BÁ;B1ÂB.	Â1C	c                ó2   € V ^8„  d   QhR\         R,          /# ©r	   r   N)r(   )r   s   "r   r   r   `   s   € ÷ ñ œs T�zñ r   c                 óÂ   € \         P                  R8w  d   R#  \         P                  ! ^?4      p \         P                  ! V 4       RT ( ,          #   \         d     R# i ; i)uö  The mode ``open(path, "w")`` gives a file it has to create (``0o666 & ~umask``); ``None``
when the umask cannot be read or on non-POSIX hosts (Windows mode bits are synthesized).

``mkstemp`` always creates at 0600, so publishing a *new* non-secret file through a temp
file would tighten it to owner-only â€” the Docker/NAS volume-mount hazard
:func:`_restore_file_metadata` documents. The transient mask is 0o077: a thread that opens
a file in the read window gets a tighter file, never a looser one.
rD   Ni¶  )r!   r   Úumaskr?   )Úcurrents    r   Údefault_new_file_moderU   `   sT   € ô 
‡w�w�'ÔÙðÜ—(’(˜5“/ˆÜ
�Š�Ôð �G�8ÕÐøô ô Úðús   ™,A ÁAÁAc                ód   € V ^8„  d   QhR\         R\        \        \        3,          R,          RR/# )r	   r8   rI   Nr   rB   )r   s   "r   r   r   s   s.   € ÷ .ñ .œdð .¬5´´c°­?¸TÕ+Að .Àdñ .r   c                 ó   € \        WR 4       R # ©N©rO   )r8   rI   s   &&r   Ú_restore_file_ownerrZ   s   s   € Ü˜4¨Ö-r   c                óB   € V ^8„  d   QhR\         R\        R,          RR/# )r	   r8   rJ   Nr   r9   )r   s   "r   r   r   w   s%   € ÷ -ñ -œTð -¬¨t­ð -¸ñ -r   c                 ó    € \        V R V4       R # rX   rY   )r8   rJ   s   &&r   Ú_restore_file_moder]   w   s   € Ü˜4  tÖ,r   Úntg{®Gáz”?gš™™™™™¹?c                ó0   € V ^8„  d   QhR\         R\        /# )r	   Úexcr   )r?   r   )r   s   "r   r   r   Ž   s   € ÷ _ñ _¬Wð _¼ñ _r   c                óF   € \         ;'       d    \        V RR4      \        9   # )z<Candidate-only: winerror 5 also covers a genuine ACL denial.ÚwinerrorN)Ú_IS_WINDOWSÚgetattrÚ!_WINDOWS_CONTENDED_REPLACE_ERRORS)r`   s   &r   Ú#_is_contended_windows_replace_errorrf   Ž   s   € ä×^Ð^œ7 3¨
°DÓ9Ô=^Ñ^Ð^r   c                ó4   € V ^8„  d   QhR\         R\         RR/# ©r	   Útmp_strÚ	real_pathr   N©r   )r   s   "r   r   r   “   s!   € ÷ ñ œsð ¬sð °tñ r   c           	     óÈ  € \        V R4      ;_uu_ 4       pVP                  4       pRRR4       \        P                   ! V\        P                  \	        \        R^ 4      ,          4      p ^ pV\        X4      8  d#   V\        P                  ! WCVR 4      ,          pK2  \        P                  ! V\        V4      4       \        \        4      ;_uu_ 4        \        P                  ! V4       RRR4       \        P                  ! V4       \        P                  ! V 4       R#   + '       g   i     EL; i  + '       g   i     LR; i  \        P                  ! T4       i ; i)uC  Overwrite *real_path* through the existing file â€” last resort for a still-held target.

Not atomic (a smaller window than a copy, not none), so it runs only after the rename has
genuinely failed. Writing through the target also preserves its ACL, which ``os.replace``
does not (the temp file's inherited ACL wins there).
ÚrbNÚO_BINARY)ÚopenÚreadr!   ÚO_WRONLYrd   ÚlenÚwriteÚ	ftruncater   r?   ÚfsyncÚcloseÚunlink)ri   rj   ÚsrcÚdataÚfdÚwrittens   &&    r   Ú_rewrite_in_placer|   “   sÝ   € ô 
ˆg�t×	Ô	 Ø�x‰x‹zˆ÷ 
ä	�Š�œBŸK™K¬'´"°jÀ!Ó*DÕDÓ	E€BðØˆØœ˜D›	Ô!Ø”r—x’x ¨¨ NÓ3Õ3ŠGÜ
�Š�Rœ˜T›Ô#Ü”g×ÕÜ�HŠH�RŒL÷ ô 	�Š�ŒÜ‡I‚IˆgÖ÷ 
×	Ð	ú÷ ×ûô 	�Š��ús0   •D"Á*A+E	 ÃD6Ã,E	 Ä"D3	Ä6E	ÅE	 Å	E!c                ó4   € V ^8„  d   QhR\         R\         RR/# rh   rk   )r   s   "r   r   r   ©   s!   € ÷ ñ œCð ¬Cð °Dñ r   c           	     óþ  € \         P                  ! W4       \        \        4      ;_uu_ 4        \         P                  ! W4       RRR4       \        \        4      ;_uu_ 4        \        VR4      ;_uu_ 4       p\        P                  ! VP                  4       4       RRR4       RRR4       \        P                  ! V 4       R#   + '       g   i     L‰; i  + '       g   i     LC; i  + '       g   i     LN; i)zCCopy/fsync/unlink fallback for cross-device and bind-mount renames.Nrm   )
ÚshutilÚcopyfiler   r?   Úcopystatro   r!   ru   Úfilenorw   )ri   rj   Úfs   && r   Ú_copy_fallbackr„   ©   s€   € ä
‡O‚O�GÔ'Ü	”'×	Õ	Ü�Š˜Ô+÷ 
ä	”'×	Õ	œD ¨D×1Ô1°QÜ
�Š�—‘“Ô÷ 2×	ä‡I‚IˆgÖ÷	 
×	úç1×1ú×	×	ús/   ®CÁ$C,Á9%C	ÂC,ÃC	ÃC)Ã$C,Ã,C<	c                óh   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         /# )r	   Útmp_pathÚtargetr   ©r   r   )r   s   "r   r   r   ³   s)   € ÷ 'ñ 'œS¤4�Zð '´´tµð 'Äñ 'r   c                óŒ  € \        V4      p\        V4      p\        V 4      p \        P                  ! WC4       V#   \         Ed}   p\        T4      pTP                  \        9  d
   T'       g   h T'       d´   ^ RIH	p \        ^\        ^,           4       F’  p\        P                  ! T! T\        \        R7      4        \        P                  ! YC4       Tu u Rp?#   \         d=   p	T	pT	P                  \        9   d	   Rp Rp	?	 M"\        T	4      '       g   h  Rp	?	KŒ  Rp	?	ii ; i	  \         P#                  RYC\%        TRR4      ;'       g?    \        P&                  P)                  TP                  ;'       g    ^ TP                  4      T'       d   RMR4       T'       d   \*        M\,        ! YC4        Rp?T# Rp?ii ; i)	uÅ  Atomically move *tmp_path* onto *target*, preserving symlinks.

Resolves a symlink first so ``os.replace`` writes the real file in place and the symlink
survives. Otherwise identical to ``os.replace`` unless the rename fails with EXDEV/EBUSY
(cross-device, bind-mount, busy file: copy/fsync/unlink immediately â€” these never clear on
retry) or a Windows rename contended by another open handle (winerror 5/32/33: bounded retry,
then in-place rewrite).
)Újittered_backoff)Ú
base_delayÚ	max_delayNFz;atomic_replace: %s -> %s failed with %s; falling back to %srb   zin-place rewriteÚcopy)r   Ú_publish_pathr!   Úreplacer?   rf   ÚerrnoÚ_CROSS_DEVICE_ERRNOSÚagent.retry_utilsrŠ   ÚrangeÚ_REPLACE_RETRY_ATTEMPTSr2   ÚsleepÚ_REPLACE_RETRY_BASE_DELAY_SÚ_REPLACE_RETRY_MAX_DELAY_SÚloggerÚdebugrd   Ú	errorcodeÚgetr|   r„   )
r†   r‡   Ú
target_strrj   ri   r`   Ú	contendedrŠ   ÚattemptÚ	retry_excs
   &&        r   Úatomic_replacer    ³   sj  € ô �V“€JÜ˜jÓ)€IÜ�(‹m€GðQÜ
�
Š
�7Ô&ØÐøÜõ QÜ7¸Ó<ˆ	Ø�9‰9Ô0Ô0¿Øßå:Ü  Ô$;¸aÕ$?Ö@�Ü—
’
Ñ+¨GÔ@[ô  hBô  Cô  Dð	Ü—J’J˜wÔ2Ø$×$øÜô Ø#�CØ —‘Ô*>Ô>Ø$)˜	ÝÜ>¸y×IÒIØõ Jûðúñ Aô 	�‰ÐRÐT[Ü˜S *¨dÓ3×eÐe´u·±×7JÑ7JÈ3Ï9É9Ï>È>ÐXYÐ[^×[dÑ[dÓ7eß+4Ñ'¸&ô	B÷ (Õ	¬^Ð	;¸W×PÐPØÐûð1Qúsv   £; »GÁ'F>Á/F>Á8AF>Ã CÃF>ÃGÃD&Ã*D!ÄF>Ä	D!ÄF>Ä!D&Ä&)F>Å,F>Å=F>ÆF>Æ&F>Æ>Gc                ó0   € V ^8„  d   QhR\         R\         /# )r	   rœ   r   rk   )r   s   "r   r   r   Ý   s   € ÷ Vñ Vœcð V¤cñ Vr   c                óŽ   € \         P                  P                  V 4      '       d    \         P                  P                  V 4      # T # )z\The path :func:`atomic_replace` renames onto: a symlink's real file, else the target itself.)r!   r8   ÚislinkÚrealpath)rœ   s   &r   rŽ   rŽ   Ý   s.   € ä+-¯7©7¯>©>¸*×+EÒ+EŒ2�7‰7×Ñ˜JÓ'ÐUÈ:ÐUr   c                ót   € V ^8„  d   QhR\         \        ,          R\        R\        \        \         3,          /# )r	   r‡   Úkwr   )r   r   r   r'   r(   )r   s   "r   r   r   â   s-   € ÷ 4ñ 4œ3¤�:ð 4¬Sð 4´U¼3Ä¸8µ_ñ 4r   c                óJ  € \        V 4      p\        \        V4      P                  4      p\        P                  P                  \        V4      4      ;'       g    Tp \        P                  ! RRV/VB #   \         d#    YC8X  d   h \        P                  ! RRT/TB u # i ; i)a  ``tempfile.mkstemp`` in the directory :func:`atomic_replace` will rename into.

A temp staged next to a symlink whose target lives on another filesystem turns the publish
rename into EXDEV, and atomic_replace's copy fallback then rewrites the file in place (torn on
a crash). Staging beside the resolved target keeps the rename atomic. If that directory is not
writable to us (the file itself may still be), stage beside the link instead: the save keeps
working through the non-atomic copy fallback, exactly as before.
Údir© )
r   r   Úparentr!   r8   ÚdirnamerŽ   ÚtempfileÚmkstempÚPermissionError)r‡   r¦   rœ   Úlink_dirÚ	stage_dirs   &,   r   Úmkstemp_besider±   â   s�   € ô �V“€JÜ”4˜
Ó#×*Ñ*Ó+€HÜ—‘—‘¤¨jÓ 9Ó:×FÐF¸h€Ið4Ü×ÒÑ4 IÐ4°Ñ4Ð4øÜô 4ØÔ ØÜ×ÒÑ3 HÐ3°Ñ3Ò3ð4ús   ÁA5 Á5*B"Â!B"c                ó>   € V ^8„  d   QhR\         \        ,          RR/# r7   rˆ   )r   s   "r   r   r   ö   s   € ÷ ñ œ#¤�*ð ¨ñ r   c           	     óÆ  € \         P                  R8X  d   R#  \         P                  ! V \         P                  \	        \         R^ 4      ,          4      p \        \
        4      ;_uu_ 4        \         P                  ! T4       RRR4       \         P                  ! T4       R#   \
         d     R# i ; i  + '       g   i     L:; i  \         P                  ! T4       i ; i)u5  Best-effort fsync of a directory entry so a just-renamed file survives power loss.

No-op on Windows (directories can't be opened with ``os.open``; the file fsync still applies)
and on any OSError â€” durability of the directory entry is never worth failing a write that
has already been replaced into place.
r^   NÚO_DIRECTORY)	r!   r   ro   ÚO_RDONLYrd   r?   r   ru   rv   )r8   rz   s   & r   Úfsync_directoryr¶   ö   s‘   € ô 
‡w�w�$„ÙðÜ�WŠW�Tœ2Ÿ;™;¬´°]ÀAÓ)FÕFÓGˆðÜ”g×ÕÜ�HŠH�RŒL÷ ô 	�Š�Žøô ô Úðú÷ ×ûô 	�Š��ús5   ™;B# ÁC Á,B5ÂC Â#B2Â1B2Â5C	Ã C ÃC Úignore_errorsc                óJ   € V ^8„  d   QhR\         \        ,          R\        RR/# )r	   r8   r·   r   N)r   r   r   )r   s   "r   r   r   
  s%   € ÷  ñ  œ#¤�*ð  ¼ð  Èñ  r   c               óÆ   € R p  \         P                  ! WR7       R#   \         d    \         P                  ! YR7        R# i ; i  \         d    T'       g   h  R# i ; i)a,  ``shutil.rmtree`` that can also delete read-only trees.

``shutil.rmtree`` stops at the first entry it cannot unlink.  Git marks
loose object files read-only on Windows (``WinError 5``), and package
installs (Nix store, deb/rpm) are copied ``r--r--r--`` into ``0555``
directories on POSIX, where unlinking needs a writable *parent*.  Clear the
write bit on the failing path and on its parent, then retry the exact
operation that failed.  Only ``PermissionError`` is retried: every other
failure keeps ``shutil.rmtree``'s semantics (and ``ignore_errors``).
c           	      ó  € \        V\        4      '       d
   V^,          MTp\        V\        4      '       g   Vh\        P                  P                  V4      V3 FŒ  pV'       g   K  \        \        4      ;_uu_ 4        \        P                  ! V\        P                  ! V4      P                  \        P                  ,          \        P                  ,          4       RRR4       KŽ  	  V ! V4       R#   + '       g   i     K«  ; i)é   N)r   r'   r®   r!   r8   r«   r   r?   rN   r<   r>   ÚS_IWUSRÚS_IXUSR)ÚfuncÚfpathÚexc_infor`   Ú	candidates   &&&  r   Ú	_on_errorÚ"rmtree_readonly.<locals>._on_error  s¦   € ä'¨´%×8Ò8ˆh�qŽk¸hˆÜ˜#œ×/Ò/ØˆIÜŸ'™'Ÿ/™/¨%Ó0°%Ó8ˆIß‰yÜœg×&Õ&Ü—H’H˜Y¬¯ª°	Ó(:×(BÑ(BÄTÇ\Á\Õ(QÔTX×T`ÑT`Õ(`Ôa÷ 'Ñ&ñ 9ñ 	ˆUŽ÷ '×&Ð&ús   Á?A C5Ã5D)Úonexc)ÚonerrorN)r   ÚrmtreeÚ	TypeErrorr?   )r8   r·   rÂ   s   &$ r   Úrmtree_readonlyrÈ   
  sV   € ò	ð	ð	3Ü�MŠM˜$×0øÜô 	3Ü�MŠM˜$×2Ð2ð	3ûäô ÷ Øò ðús-   † Ÿ"AÁA ÁAÁA ÁA ÁA ÁA Úencodingúutf-8rJ   Úpreserve_ownerTÚbinaryÚ	fsync_dirc                ó~   € V ^8„  d   QhR\         R\        R\        R\        R,          R\        R\        R\        R	R/# )
r	   r8   ÚprefixrÉ   rJ   NrË   rÌ   rÍ   r   )r   r   r(   r   )r   s   "r   r   r   -  sO   € ÷ (ñ (œð (´ð (¼sð (ÔTWÐZ^ÕT^ð (Ü"&ð(Ü7;ð(ÜPTð(Øaeñ(r   c               óŽ  € ^ RI Hp V! V P                  4       Vf!   V P                  4       '       g   \	        4       pV'       d   \        V 4      MRp	\        WRR7      w  r« \        P                  ! Y¦'       d   RMRV'       d   RMTR7      ;_uu_ 4       pVe<   \        \        R4      '       d&   \        P                  ! VP                  4       V4       V! V4       VP                  4        \        P                  ! VP                  4       4       RRR4       \        \        W°4      4      p\!        WÙV4       V'       d   \#        VP                  4       R# R#   + '       g   i     LQ; i  \$         dL    \'        \(        4      ;_uu_ 4        \        P*                  ! T4       RRR4       h   + '       g   i     h ; ii ; i)	uü  Temp file + fsync + :func:`atomic_replace`, then re-apply owner/mode.

*write(f)* emits the payload into the open handle (text, or bytes when *binary*). The temp file
is created by ``mkstemp`` â€” ``O_CREAT|O_EXCL`` at 0600 regardless of umask â€” so a secret is
never readable at process umask, not even between create and chmod. *mode* is fchmod'd onto
the temp fd BEFORE the replace so the target never transits through mkstemp's 0600 (fchmod is
Unix-only; the post-replace chmod is the sole path on Windows). With no *mode* a NEW target
gets what ``open(path, "w")`` would have given it (process umask) â€” the callers this replaced
wrote at umask, and silently tightening every fresh cache/state file to 0600 breaks shared
volume mounts; an existing target with no *mode* keeps mkstemp's bits, as before. *fsync_dir*
also fsyncs the resolved target's parent so the rename itself is durable. The temp file is
removed on any failure â€” ``BaseException`` on purpose, so KeyboardInterrupt / SystemExit still
clean up.
©Úmkdir_under_hermes_homeNz.tmp)rÏ   ÚsuffixÚwbÚw©rÉ   Úfchmod)Úhermes_constantsrÒ   rª   r;   rU   rG   r±   r!   ÚfdopenrM   r×   r‚   Úflushru   r   r    rO   r¶   ÚBaseExceptionr   r?   rw   )r8   rs   rÏ   rÉ   rJ   rË   rÌ   rÍ   rÒ   Úoriginal_ownerrz   r†   rƒ   Úreplaceds   &&$$$$$$      r   Ú_atomic_writerÞ   -  s-  € õ& 9á˜DŸK™KÔ(Ø‚|˜DŸK™KŸMšMÜ$Ó&ˆß3AÔ)¨$Ô/Àt€NÜ! $¸fÔE�L€BðÜ�YŠY�r¦6™4¨sÇV¹TÐQY×ZÕZÐ^_ØÒ¤G¬B°×$9Ò$9Ü—	’	˜!Ÿ(™(›* dÔ+Ù�!ŒHØ�G‰GŒIÜ�HŠH�Q—X‘X“ZÔ ÷ [ô œ xÓ6Ó7ˆÜ˜x¸Ô>ßÜ˜HŸO™OÖ,ñ ÷ [×Zûô ô Ü”g×ÕÜ�IŠI�hÔ÷ à÷ ÖàúðúsD   Á""E. ÂE. ÂA<EÄAE. ÅE+	Å&E. Å."GÆF0Æ'	GÆ0G Æ;	Gc          	      ód   € V ^8„  d   QhR\         R\        R,          R\        R\        R,          /# )r	   r8   Úcreate_modeNÚpreserver   )r   r(   r   )r   s   "r   r   r   X  s9   € ÷ Fñ Fœ$ð F¬S°4­Zð FÄ4ð FÔSVÐY]ÕS]ñ Fr   c                óh   € V'       d   \        V 4      MRpVf   V P                  4       '       d   V# T# )zXExisting permission bits of *path* (when *preserve*), else *create_mode* for a new file.N)r@   r;   )r8   rà   rá   rJ   s   &&& r   Ú_mode_for_writerã   X  s+   € ç(0Ô˜tÔ$°d€DØÒ# t§{¡{§}¢}ˆ4ÐE¸+ÐEr   Ú
tmp_prefixz.tmp_Úpreserve_moderà   c                ó®   € V ^8„  d   QhR\         \        ,          R\         R\         R\         R\        R\        R,          R\        R,          R	\        R
R/	# )r	   r8   ÚcontentrÉ   rä   rå   rà   NrJ   rÍ   r   )r   r   r   r(   )r   s   "r   r   r   ^  sj   € ÷ Eñ EœC¤$�Jð E´ð EÄ3ð EÔ^að EÜ%)ðEÜ@CÀdÅ
ðEÜY\Ð_cÕYcðEä!%ðEà26ñEr   c               óh   a€ \        V 4      p \        T V3R lY2Ve   TM\        WVR7      WGR7       R# )a³  Write *content* to *path* via temp file + fsync + atomic rename.

The target is never left partially written on crash/interrupt. Shared by every destructive
file rewrite (memory store, skill manager, agent importer, ...). *mode* forces the final
permission bits (secret files: ``0o600``) regardless of what exists; *create_mode* applies only
when the target is new and *preserve_mode* carries an existing file's bits and owner across.
c                 ó&   <€ V P                  S4      # rX   ©rs   ©rƒ   rç   s   &€r   Ú<lambda>Ú#atomic_write_text.<locals>.<lambda>i  ó   ø€  !§'¡'¨'Ô"2r   N)rá   )rÏ   rÉ   rJ   rË   rÍ   )r   rÞ   rã   )r8   rç   rÉ   rä   rå   rà   rJ   rÍ   s   &f$$$$$$r   Úatomic_write_textrï   ^  s3   ø€ ô �‹:€DÜ�$Ô2¸:Ø#Ò/‘t´_ÀTÐanÔ5oØ!.÷Er   c                ó|   € V ^8„  d   QhR\         \        ,          R\        R\         R\        R,          R\        RR/# )r	   r8   rç   rä   rJ   NrÍ   r   )r   r   Úbytesr(   r   )r   s   "r   r   r   n  sI   € ÷ eñ eœS¤4�Zð e´%ð eÌð eÜ  4�ZðeÜ;?ðeØLPñer   c          
     óh   a€ \        V 4      p \        T V3R lTRRVe   TM
\        V 4      VR7       R# )zKBytes variant of :func:`atomic_write_text` (encrypted blobs, key material).c                 ó&   <€ V P                  S4      # rX   rê   rë   s   &€r   rì   Ú$atomic_write_bytes.<locals>.<lambda>r  rî   r   TFN)rÏ   rÌ   rË   rJ   rÍ   )r   rÞ   r@   )r8   rç   rä   rJ   rÍ   s   &f$$$r   Úatomic_write_bytesrõ   n  s6   ø€ ô �‹:€DÜ�$Ô2¸:ÈdÐchØ#Ò/‘tÔ5HÈÓ5NÐZc÷er   c          
      óZ   € V ^8„  d   QhR\         R\        R,          R\        R\        RR/# )r	   ry   ÚindentNÚensure_asciiÚdump_kwargsr   )r   r(   r   Údict)r   s   "r   r   r   v  s<   € ÷ Sñ S”Sð S¬¨d­
ð SÄ$ð SÔUYð SÐ^bñ Sr   c          
     óØ   € \         P                  ! V 3RVRV/VB p VP                  V4       R#   \         d/    TP                  \         P                  ! T 3RTRR/TB 4        R# i ; i)uV  ``json.dump`` that survives surrogate-escaped strings.

``os.fsdecode`` of a non-UTF-8 filename/argv yields lone surrogates (``'\udcff'``); a utf-8
text handle rejects them with ``UnicodeEncodeError`` â€” a ValueError, which callers guarding
``except OSError`` never see. ``ensure_ascii=True`` escapes them as ``\udcff`` and
``json.loads`` restores the identical str, so the retry round-trips; ``surrogateescape``
would emit a raw 0xFF byte that the reader's utf-8 decode rejects. Serializing to a str first
keeps the failure before any byte reaches the file, so no partial payload is left behind.
r÷   rø   TN)ÚjsonÚdumpsrs   ÚUnicodeEncodeError)ry   rƒ   r÷   rø   rù   Útexts   &&$$$ r   Ú
_dump_jsonr   v  sg   € ô �:Š:�dÑT 6ÐT¸ÐTÈÑT€DðSØ	�‰�ŽøÜô SØ	�‰”—
’
˜4ÑQ¨ÐQ¸TÐQÀ[ÑQ×RðSús   �0 °5A)Á(A)r÷   rø   c                ó”   € V ^8„  d   QhR\         \        ,          R\        R\        R\        R,          R\        R\        R\        R	R/# )
r	   r8   ry   r÷   rJ   Nrø   rÍ   rù   r   )r   r   r   r(   r   )r   s   "r   r   r   ‡  sV   € ÷ 'ñ 'Ü
”�*ð'Üð'Ü,/ð'Ü;>À½:ð'äð'ä+/ð'äHKð'ð 
ñ'r   c               óŒ   aaaa€ \        V 4      p \        T VVVV3R lRV P                   R2Ve   TM
\        V 4      VR7       R# )a‡  Write JSON to *path* atomically (temp file + fsync + replace).

Surrogate-escaped strings (non-UTF-8 argv/paths) are always persisted: the write falls back
to ``ensure_ascii=True`` escapes for that payload only, so normal content keeps its raw UTF-8
bytes. ``mode=0o600`` is the private-credential form: the temp file is 0600 from creation
(mkstemp), so the payload is never umask-readable.
c                 ó$   <€ \        SV SSSR 7      # ))r÷   rø   rù   )r   )rƒ   ry   rù   rø   r÷   s   &€€€€r   rì   Ú#atomic_json_write.<locals>.<lambda>“  s   ø€ ¤*¨T°1¸VÐR^ÐlwÕ"xr   Ú.Ú_N)rÏ   rJ   rÍ   )r   rÞ   Ústemr@   )r8   ry   r÷   rJ   rø   rÍ   rù   s   &fd$d$lr   Úatomic_json_writer  ‡  s>   û€ ô �‹:€DÜ�$×xØ˜TŸY™Y˜K qÐ)¸Ò8H±ÔNaÐbfÓNgØ%÷'r   c                óF   € V ^8„  d   QhR\         \        ,          R\        /# ©r	   r8   r   ©r   r   rú   )r   s   "r   r   r   ˜  s   € ÷ 	2ñ 	2œS¤4�Zð 	2¬Dñ 	2r   c                óÈ   €  \         P                  ! \        V 4      P                  RR7      4      p\        T\        4      '       d   T# / #   \        \
        3 d    / u # i ; i)u%  The JSON object at *path*, or ``{}`` when the file is missing, unreadable, malformed or
not an object. The read half of every ``read â†’ merge â†’ atomic_json_write`` config store
(memory-provider ``save_config``), so a corrupt sidecar degrades to defaults instead of
taking the provider down.z	utf-8-sigrÖ   )rü   Úloadsr   Ú	read_textr?   Ú
ValueErrorr   rú   )r8   ry   s   & r   Úread_json_or_emptyr  ˜  s[   € ð
Ü�zŠzœ$˜t›*×.Ñ.¸Ð.ÓDÓEˆô ˜d¤D×)Ò)ˆ4Ð1¨rÐ1øô ”ZÐ ô ØŠ	ðús   ‚/A ÁA!Á A!ÚlabelÚlogc                ó€   € V ^8„  d   QhR\         \        ,          R\         R\        P                  R,          R\        /# )r	   r8   r  r  Nr   )r   r   ÚloggingÚLoggerr   )r   s   "r   r   r   ¤  s:   € ÷ ñ ´3¼µ:ð Ìð ÔX_×XfÑXfÐimÕXmð Ôy}ñ r   c               ó¤  € \        V 4      p VP                  4       P                  p\        P
                  R8w  g3   T\        P                  \        P                  ,          ,          '       g   R# T;'       g    \        P                  RT'       d   T R2MRTP
                  \        P                  ! T4      T4       R#   \         d     R# i ; i)a©  Warn when a credential file is group/world-readable; True when a warning was emitted.

Hand-made secret files (or ones older Hermes wrote without an explicit mode) commonly end up
0o644 under the default umask; call this before loading any token/credential file. No-op on
non-POSIX (Windows ACLs don't map onto group/other bits; st_mode there is synthesized), when
the file is missing, or when permissions are already tight.
FrD   zO%s%s is group/world-readable (mode 0%o) and contains secrets. Run: chmod 600 %sÚ r   T)r   r<   r>   r?   r!   r   ÚS_IRGRPÚS_IROTHr˜   Úwarningr=   )r8   r  r  ÚpÚ	file_modes   &$$  r   Ú(warn_if_credential_file_broadly_readabler  ¤  s›   € ô 	ˆT‹
€AðØ—F‘F“H×$Ñ$ˆ	ô 
‡w�w�'Ô )¬t¯|©|¼d¿l¹lÕ/J×"KÔ"KÙØ‡]€]ŒF×ÑÐmß+0˜u˜g Q™K°b¸!¿&¹&Ä$Ç,Â,ÈyÓBYÐ[\ô^áøô ô Úðús   �C  Ã CÃCÚdefault_flow_styleÚ	sort_keysÚextra_contentc                ó–   € V ^8„  d   QhR\         \        ,          R\        R\        R\        R\         R,          R\        R,          RR/# )	r	   r8   ry   r  r  r   Nrà   r   )r   r   r   r   r(   )r   s   "r   r   r   ¸  sV   € ÷ 
bñ 
bœC¤$�Jð 
b¬cð 
bÌ$ð 
bÔcgð 
bÜ%(¨4¥Zð
bÜEHÈ4ÅZð
bØ[_ñ
br   c          	     ó†   aaaa€ \        V 4      p R VVVV3R llp\        WRV P                   R2\        W4      R7       R# )z>Write YAML to *path* atomically (temp file + fsync + replace).c                ó   € V ^8„  d   QhRR/# rQ   r©   )r   s   "r   r   Ú'atomic_yaml_write.<locals>.__annotate__½  ó   € ÷ #ñ #�Tñ #r   c                 ór   <€ \         P                  ! SV SSR 7       S'       d   V P                  S4       R# R# ))r  r  N)ÚyamlÚ	safe_dumprs   )rƒ   ry   r  r   r  s   &€€€€r   Ú_writeÚ!atomic_yaml_write.<locals>._write½  s+   ø€ Ü�Š�t˜QÐ3EÐQZÕ[ßØ�G‰G�MÖ"ñ r   r  r  ©rÏ   rJ   N)r   rÞ   r  rã   )r8   ry   r  r  r   rà   r)  s   &fddd$ r   Úatomic_yaml_writer,  ¸  s8   û€ ô �‹:€D÷#ó #ô
 �$¨¨4¯9©9¨+°QÐ'7¼oÈdÓ>`×ar   c                ó$   € V ^8„  d   QhR\         /# )r	   r8   r   )r   s   "r   r   r   Å  s   € ÷ Yñ Yœ$ñ Yr   c                óú   € ^ RI Hp \        P                  ! 4       pV P	                  4       '       d"   VP                  V P                  RR7      4      MRpT\        W14      '       d   V3# T! T;'       g    / 4      3# )z¢``(yaml_rt, CommentedMap)``: a ruamel round-trip loader keeping quotes/Unicode with 2-space
indents, plus *path* loaded through it (empty map when missing/blank).©ÚCommentedMaprÊ   rÖ   N)Úruamel.yaml.commentsr0  r'  Úroundtrip_yamlr;   Úloadr  r   )r8   r0  Úyaml_rtry   s   &   r   Ú_roundtrip_loadr5  Å  sd   € õ 2ä×!Ò!Ó#€GØ=A¿[¹[¿]º]ˆ7�<‰<˜Ÿ™°˜Ó8Ô9ÐPT€DØœJ t×:Ò:�DÐXÐXÁÈTÏZÈZÐUWÓ@XÐXÐXr   c                óB   € V ^8„  d   QhR\         R\        R,          RR/# )r	   r8   r   Nr   )r   r   )r   s   "r   r   r   Ï  s+   € ÷ Yñ Yœ$ð YÄ3ÈÅ:ð YÐY]ñ Yr   c                ól   aaa€ R  VVV3R llp\        WRV P                   R2\        V 4      R7       R# )c                ó   € V ^8„  d   QhRR/# rQ   r©   )r   s   "r   r   Ú%_roundtrip_dump.<locals>.__annotate__Ð  r%  r   c                 ób   <€ SP                  SV 4       S'       d   V P                  S4       R # R # rX   )Údumprs   )rƒ   Úconfigr   r4  s   &€€€r   r)  Ú_roundtrip_dump.<locals>._writeÐ  s%   ø€ Ø�‰�V˜QÔßØ�G‰G�MÖ"ñ r   r  r  r+  N)rÞ   r  r@   )r8   r4  r<  r   r)  s   &ffd r   Ú_roundtrip_dumpr>  Ï  s/   ú€ ÷#ò #ô
 �$¨¨4¯9©9¨+°QÐ'7Ô>QÐRVÓ>W×Xr   c                óV   € V ^8„  d   QhR\         \        ,          R\         R\        RR/# )r	   r8   Úkey_pathr
   r   N©r   r   r   )r   s   "r   r   r   Ø  s-   € ÷ (+ñ (+¤s¬T¥zð (+¼Sð (+Ìð (+ÐQUñ (+r   c                óì  € ^ RI Hp ^ RIHpHp \        V 4      p ^ RIHp V! V P                  4       \        V 4      w  rxTp	V! V4      p
^ p W«R pV! \        V	4      V4      ;'       g    V^ ,          ^3w  rÞW¾,           \        V
4      8X  d   Vf   V	P                  VR4       MW)V&   M?V	P                  V4      p\        Wó4      '       g   Vf   R# V! 4       pWùV&   Tp	W¾,          pK�  \        WV4       R# )aµ  Update one dotted YAML key while preserving comments, ordering, quoting and Unicode.

Narrower than :func:`atomic_yaml_write` on purpose: for user-edited config files where a
single setting mutation must not disturb the rest. Still writes via temp file + atomic replace.
``value=None`` removes the key (a ``key: null`` leftover reads as absent everywhere but
litters the file and diverges from whole-document writers that drop the key).
r/  )Ú_greedy_literal_matchÚ_split_key_pathrÑ   N)r1  r0  Úhermes_cli.configrC  rD  r   rØ   rÒ   rª   r5  rú   rr   Úpopr›   r   r>  )r8   r@  r
   r0  rC  rD  rÒ   r4  r<  rT   ÚkeysÚiÚ	remainingÚsegÚconsumedÚ
next_values   &&&             r   Úatomic_roundtrip_yaml_updaterM  Ø  sæ   € õ 2÷
 Iä�‹:€DÝ8á˜DŸK™KÔ(Ü% dÓ+�O€GØ€GÙ˜8Ó$€DØ	€AØ
Ø˜�Hˆ	Ù-¬d°7«m¸YÓG×\Ð\ÈIÐVWÍLÐZ[ÐK\‰ˆØ�<œ3˜t›9Ô$ØŠ}Ø—‘˜C Õ&à$˜‘ØØ—[‘[ Ó%ˆ
Ü˜*×3Ò3ØŠ}ÙÙ%›ˆJØ%�C‰LØˆØ	�ŠÜ�D 6Ö*r   c                ó0   € V ^8„  d   QhR\         R\         /# )r	   r
   r   ©r   )r   s   "r   r   r   
  s   € ÷ ñ ”Sð œSñ r   c                óp  € ^ RI HpHp ^ RIHp \        V \        4      '       d0   V! 4       pV P                  4        F  w  rV\        V4      WE&   K  	  V# \        V \        \        34      '       d   V! R V  4       4      # \        V \        4      '       d"   V P                  4       \        9   d	   V! V 4      # V # )u±   Plain Python value â†’ ruamel node: YAML 1.1-ambiguous strings force-quoted at every depth
(a bare ``off`` inside a list reads back as ``False`` just like one at the top level).©r0  ÚCommentedSeq)ÚDoubleQuotedScalarStringc              3   ó8   "  € T F  p\        V4      x € K  	  R # 5irX   )Ú	_rt_value)Ú.0Úvs   & r   Ú	<genexpr>Ú_rt_value.<locals>.<genexpr>  s   é € Ð8±%¨QœI aŸL˜L³%ùs   ‚)r1  r0  rR  Úruamel.yaml.scalarstringrS  r   rú   ÚitemsrU  Úlistr'   r   r   Ú_YAML11_AMBIGUOUS_WORDS)r
   r0  rR  rS  ÚnodeÚkrW  s   &      r   rU  rU  
  sŽ   € ÷ @ÝAä�%œ×ÒÙ‹~ˆØ—K‘K–M‰DˆAÜ “lˆD‹Gñ "àˆÜ�%œ$¤˜×'Ò'ÙÑ8±%Ó8Ó8Ð8Ü�%œ×Ò %§+¡+£-Ô3JÔ"JÙ'¨Ó.Ð.Ø€Lr   Úextra_content_on_createc                ód   € V ^8„  d   QhR\         \        ,          R\        R\         R,          RR/# )r	   r8   Ú	new_stater`  Nr   r  )r   s   "r   r   r     s8   € ÷ :jñ :j¤S¬4¥Zð :j¼Dð :jÜ8;¸d½
ð:jØNRñ:jr   c               ó¶  aa	a
aaa€ ^ RI HoHo	 ^ RIHp \        V 4      p ^ RIHp V! V P                  4       V! V 4       V P                  4       '       * ;'       g&    V P                  RR7      P                  4       '       * p\        V 4      w  rgR R loV	3R lV3R	 lloR
 VV	V
VV3R lloV3R lV3R llo
S
! Wq4       \        YYu'       d   TMRR7       R# )uA  Persist a full config-state dict while preserving comments and ordering.

THE on-disk primitive for ``config.yaml`` (production callers reach it through
``hermes_cli.config.atomic_config_write`` or ``atomic_config_replace``): the document is
loaded through ruamel round-trip mode and *new_state* is merged onto it, so comments, key order, quotes, blank
lines and readable Unicode survive. Only nodes whose value actually changed are reassigned;
an untouched scalar or list keeps its inline comments and formatting. Keys absent from
*new_state* are deleted ("explicit absence": ``cfg.pop(k)`` + save removes ``k`` from disk).
``extra_content_on_create`` (commented example blocks) is appended only when the file is
being created â€” re-appending it on every rewrite is how the stock boilerplate replaced
users' own comments (#92554).
rQ  )Ú$require_readable_config_before_writerÑ   rÊ   rÖ   c                ó<   € V ^8„  d   QhR\         R\         R\        /# )r	   rT   r
   r   r   )r   s   "r   r   Ú0atomic_roundtrip_yaml_save.<locals>.__annotate__5  s&   € ÷ Yñ YœCð Y¬ð Y´ñ Yr   c                 óZ   € W8H  ;'       d!    \        V \        4      \        V\        4      J # rX   )r   r   )rT   r
   s   &&r   Ú
_unchangedÚ.atomic_roundtrip_yaml_save.<locals>._unchanged5  s'   € àÑ×XÐX¤J¨w¼Ó$=ÄÈEÔSWÓAXÐ$XÐXr   c                ó.   <€ V ^8„  d   QhRSR\         RR/# ©r	   Údstrx   r   N)r\  )r   rR  s   "€r   r   rf  9  s"   ø€ ÷ ñ ˜ð ¬4ð °Dñ r   c                 ó¶   <€ \        V4       F:  w  r#V\        V 4      8  d   S! WV4       K   V P                  \        V4      4       K<  	  V \        V4      R 1 R # rX   )Ú	enumeraterr   ÚappendrU  )rl  rx   rH  r
   Ú_merge_items   &&  €r   Ú
_merge_seqÚ.atomic_roundtrip_yaml_save.<locals>._merge_seq9  sH   ø€ ä! #ž‰HˆAØ”3�s“8Œ|Ù˜C EÖ*à—
‘
œ9 UÓ+Ö,ñ	 'ð
 ”�C“‘	ŠNr   c                ó   € V ^8„  d   QhRR/# rQ   r©   )r   s   "r   r   rf  B  s   € ÷ (ñ (¨ñ (r   c                 ó  <€ W,          p\        V\        4      '       d   \        VS4      '       d   S! W24       R # \        V\        4      '       d   \        VS4      '       d   S! W24       R # S! W24      '       g   \        V4      W&   R # R # rX   )r   rú   r\  rU  )	rl  Úkeyr
   rT   r0  rR  Ú_mergerq  rh  s	   &&& €€€€€r   rp  Ú/atomic_roundtrip_yaml_save.<locals>._merge_itemB  sh   ø€ Ø•(ˆÜ�eœT×"Ò"¤z°'¸<×'HÒ'HÙ�7Ö"Ü˜œt×$Ò$¬°G¸\×)JÒ)JÙ�wÖ&Ù˜G×+Ò+Ü  Ó'ˆC‹Hñ ,r   c                ó.   <€ V ^8„  d   QhRSR\         RR/# rk  )rú   )r   r0  s   "€r   r   rf  L  s"   ø€ ÷ ñ �Lð ¤tð °ñ r   c                 óÆ   <€ VP                  4        F#  w  r#W 9   d   S! WV4       K  \        V4      W&   K%  	  V  Uu. uF  qDV9  g   K  VNK  	  up F  pW K  	  R # u upi rX   )r[  rU  )rl  rx   ru  r
   r_  rp  s   &&   €r   rv  Ú*atomic_roundtrip_yaml_save.<locals>._mergeL  sY   ø€ ØŸ)™)ž+‰JˆCØŒzÙ˜C eÖ,ä$ UÓ+�“ñ	 &ñ
  #Ó3™s˜!¨s¡l—A�A™sÔ3ˆCØ’ó 4ùÒ3s   ½AÁ
AN)r   )r1  r0  rR  rE  rd  r   rØ   rÒ   rª   r;   r  r   r5  r>  )r8   rb  r`  rd  rÒ   Úcreatingr4  Úexistingr0  rR  rv  rp  rq  rh  s   &&$     @@@@@@r   Úatomic_roundtrip_yaml_saver}    s§   ý€ ÷ @ÝFä�‹:€DÝ8á˜DŸK™KÔ(Ù(¨Ô.Ø—;‘;“=Ô ×PÐP¨¯©À¨Ó(H×(NÑ(NÓ(PÔ$P€HÜ'¨Ó-Ñ€GõY÷ó ÷(ô (÷ó ñ ˆ8ÔÜ�D 8ÖV^Ñ;RÐdh×ir   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r	   rÿ   r   r   )r   r   )r   s   "r   r   r   Y  s!   € ÷ ñ œ#ð ¬ð ´sñ r   c                ó€   €  \         P                  ! V 4      #   \         P                  \        \        3 d    Tu # i ; i)z3Parse JSON, returning *default* on any parse error.)rü   r  ÚJSONDecodeErrorrÇ   r  )rÿ   r   s   &&r   Úsafe_json_loadsr�  Y  s7   € ðÜ�zŠz˜$ÓÐøÜ× Ñ ¤)¬ZÐ8ô ØŠðús   ‚ ˜"=¼=c                ó0   € V ^8„  d   QhR\         R\         /# )r	   Ústreamr   rO  )r   s   "r   r   r   a  s   € ÷ "ñ "œ3ð "¤3ñ "r   c                ó.   € \         P                  ! V 4      # )zLUse the shared safe reader (which selects ruamel's C parser when available).)r'  Ú	safe_load)rƒ  s   &r   Úfast_safe_loadr†  a  s   € ä�>Š>˜&Ó!Ð!r   c                óF   € V ^8„  d   QhR\         \        ,          R\        /# r
  rA  )r   s   "r   r   r   j  s   € ÷ ñ ¤#¬¥*ð ´ñ r   c                ó  € \        V 4      p \        V P                  4       4      p\        V 4      p\        ;_uu_ 4        \
        P                  V4      pVe"   V^ ,          V8X  d   V^,          uuRRR4       # RRR4       \        V RR7      ;_uu_ 4       p\        V4      pRRR4       \        ;_uu_ 4        VX3\
        V&   RRR4       V#   + '       g   i     L]; i  + '       g   i     LG; i  + '       g   i     X# ; i)u  ``fast_safe_load`` of a file, re-parsed only when its :func:`file_signature` changes.

Returns the cached object itself â€” callers must never mutate it. Parse errors propagate and
are not cached; a missing file raises ``FileNotFoundError`` like ``open`` does.NrÊ   rÖ   )	r   r.   r<   r   Ú_YAML_FILE_CACHE_LOCKÚ_YAML_FILE_CACHEr›   ro   r†  )r8   Úsigru  Úcachedrƒ   ry   s   &     r   Úload_yaml_file_readonlyr�  j  s¼   € ô
 �‹:€DÜ
˜Ÿ™›Ó
%€CÜ
ˆd‹)€Cß	Ö	Ü!×%Ñ% cÓ*ˆØÒ &¨¥)¨sÔ"2Ø˜!•9÷ 
×	Ò	ô 
ˆd˜W×	%Õ	%¨Ü˜aÓ ˆ÷ 
&ç	Ö	Ø!$ d Ô˜Ñ÷ 
à€K÷ 
×	ú÷ 
&×	%úç	Ö	à€Kús#   ¾0CÂC!Â8C4ÃC	Ã!C1	Ã4D	c                ó$   € V ^8„  d   QhR\         /# )r	   ru  rk   )r   s   "r   r   r   }  s   € ÷ ñ ”Sñ r   c                 ó¤   € \         P                  ! V R 4      P                  4       p V'       d	   V! V4      # T#   \        \        3 d    Tu # i ; i©r   )r!   r"   r   r  rÇ   )ru  r   ÚcastÚraws   &&& r   Ú_env_numberr“  }  sJ   € Ü
�)Š)�C˜Ó
×
"Ñ
"Ó
$€Cðß‰t�C‹yÐ, WÐ,øÜœ	Ð"ô ØŠðús   §9 ¯9 ·9 ¹AÁAc                ó<   € V ^8„  d   QhR\         R\        R\        /# ©r	   ru  r   r   )r   r(   )r   s   "r   r   r   …  s!   € ÷ *ñ *”ð *œsð *¬3ñ *r   c                ó"   € \        W\        4      # )z:Read an environment variable as an integer, with fallback.)r“  r(   ©ru  r   s   &&r   Úenv_intr˜  …  s   € ä�s¤SÓ)Ð)r   c                ó<   € V ^8„  d   QhR\         R\        R\        /# r•  )r   Úfloat)r   s   "r   r   r   Š  s!   € ÷ ,ñ ,”3ð ,¤ð ,´ñ ,r   c                ó"   € \        W\        4      # )z7Read an environment variable as a float, with fallback.)r“  rš  r—  s   &&r   Ú	env_floatrœ  Š  s   € ä�s¤UÓ+Ð+r   c                ó<   € V ^8„  d   QhR\         R\        R\        /# r•  r   )r   s   "r   r   r   �  s&   € ÷ @ñ @”#ð @¤ð @´ñ @r   c                óF   € \        \        P                  ! V R4      VR7      # )z*Read an environment variable as a boolean.r   r   r    r—  s   &&r   Úenv_boolrŸ  �  s   € äœ2Ÿ9š9 S¨"Ó-°wÔ?Ð?r   c                óL   € V ^8„  d   QhR\         R,          R\         R,          /# )r	   Ú	proxy_urlNr   rk   )r   s   "r   r   r   ˜  s"   € ÷ ñ ¤3¨¥:ð ´#¸µ*ñ r   c                óÌ   € \        T ;'       g    R4      P                  4       pVP                  4       P                  R4      '       d   RV\	        R4      R  2# T;'       g    R# )zaNormalize proxy URLs for httpx/aiohttp: WSL/Clash export ``socks://``, httpx needs ``socks5://``.r   zsocks://z	socks5://N)r   r   r   Ú
startswithrr   )r¡  rÁ   s   & r   Únormalize_proxy_urlr¤  ˜  s[   € ä�I—O�O Ó$×*Ñ*Ó,€IØ‡�Ó×#Ñ# J×/Ò/Ø˜9¤S¨£_Ð%5Ð6Ð7Ð8Ð8Ø×Ð˜Ðr   c                ó>   € V ^8„  d   QhR\         R\         R,          /# )r	   Úentryr   Nrk   )r   s   "r   r   r      s   € ÷ 3ñ 3œcð 3¤c¨D¥jñ 3r   c                ó~  € ^ RI p\        T ;'       g    R4      P                  4       p\        P                  ! RV4      ;'       g    \        P                  ! RV4      pV'       g   R#  VP                  VP                  ^4      4      pTP                  ^8X  d   TP                  ^4      # R#   \         d     R# i ; i)u$  The bare IPv6 literal a NO_PROXY entry should become, or None when the entry is not an
IPv6 form httpx cannot compile (``[::1]``, ``[::1]:8080``, ``[2001:db8::1]/64``, ``::1/128``).

httpx 0.28.1's ``get_environment_proxies`` routes bracketed entries through its wildcard
branch (``all://*[::1]`` â†’ ``InvalidURL: Invalid port`` at ``Client.__init__``) and cannot
compile IPv6 CIDR bypasses at all, so both degrade to the bare literal â€” which httpx compiles
into a working ``all://[<v6>]`` bypass and urllib, requests and ``agent.proxy_bypass`` all
understand. A ``[<v6>]:port`` entry loses its port: httpx has no port-scoped IPv6 bypass form,
and the portless literal still bypasses every port (the operator's intent superset). Bracketed
or CIDR IPv4 is left alone (httpx compiles those forms fine).
Nr   z\[([0-9A-Fa-f:.]+)\](?:[/:].*)?z([0-9A-Fa-f:.]+)/\d+)	Ú	ipaddressr   r   ÚreÚ	fullmatchÚ
ip_addressÚgroupr  Úversion)r¦  r¨  rÁ   ÚmÚips   &    r   Ú_bare_ipv6_literalr°     s£   € ó ä�E—K�K˜RÓ ×&Ñ&Ó(€IÜ	�ŠÐ8¸)Ó	D÷ 
=ð 
=Ü�LŠLÐ0°)Ó<ð çÙðØ×!Ñ! !§'¡'¨!£*Ó-ˆð Ÿ™ qœˆ1�7‰7�1‹:Ð2¨dÐ2øô ô Úðús   Á) B- Â-B<Â;B<c                ó>   € V ^8„  d   QhR\         R,          R\         /# )r	   Úno_proxy_valueNr   rk   )r   s   "r   r   r   º  s   € ÷ ñ ¬c°D­jð ¼Sñ r   c                ó�  € \        T ;'       g    R4      p\        P                  ! RVP                  4       4       Uu. uF  q"'       g   K  VNK  	  ppV'       d   RV9   d   V# . pV F>  p\	        V4      pVf   VP                  V4       K%  Wd9  g   K-  VP                  V4       K@  	  WC8X  d   V# RP                  V4      # u upi )a  Rewrite NO_PROXY entries httpx 0.28.1 cannot compile into the equivalent bare-IPv6 forms.

Every other entry passes through verbatim, and the original string is returned byte-stable
when nothing needed rewriting (an env without bracketed/CIDR IPv6 entries is untouched).
r   z[\s,]+Ú*Ú,)r   r©  Úsplitr   r°  ro  Újoin)r²  r’  ÚpartÚentriesÚ	rewrittenr¦  Úbares   &      r   Úsanitize_no_proxy_entriesr¼  º  s±   € ô ˆn×"Ð" Ó
#€CÜ "§¢¨°C·I±I³KÔ @ÓIÑ @˜ÃD�tˆtÑ @€GÐIß�c˜W”nØˆ
Ø€IÛˆÜ! %Ó(ˆØŠ<Ø×Ñ˜UÖ#ØÖ"Ø×Ñ˜TÖ"ñ ð ÔØˆ
Ø�8‰8�IÓÐùò Js   ½	CÁCc                ó   € V ^8„  d   QhRR/# rQ   r©   )r   s   "r   r   r   Ð  s   € ÷ ,ñ , $ñ ,r   c                 óT  € \          FJ  p \        P                  ! V R4      p\        V4      pV'       g   K/  W!8w  g   K7  V\        P                  V &   KL  	  \
         FI  p \        P                  ! V 4      pV'       g   K#  \        V4      pW18w  g   K6  V\        P                  V &   KK  	  R# )zARewrite supported proxy env vars to canonical URL forms in-place.r   N)Ú_PROXY_ENV_KEYSr!   r"   r¤  ÚenvironÚ_NO_PROXY_ENV_KEYSr¼  )ru  r
   Ú
normalizedÚ	sanitizeds       r   Únormalize_proxy_env_varsrÄ  Ð  sx   € çˆÜ—	’	˜#˜rÓ"ˆÜ(¨Ó/ˆ
ß‰:˜*Ö-Ø(ŒB�J‰J�s‹Oñ	 ÷
 "ˆÜ—	’	˜#“ˆß‰5Ü1°%Ó8ˆIØÖ!Ø"+”—
‘
˜3“ó "r   c                ó$   € V ^8„  d   QhR\         /# )r	   Úbase_urlrk   )r   s   "r   r   r   ß  s   € ÷ Jñ Jœcñ Jr   c                ó€   € T ;'       g    RP                  4       pV'       d   \        RV9   d   V4      # RV 24      # R# )zF``urlparse`` that tolerates a bare ``host[:port][/path]`` (no scheme).r   z://z//N)r   r   )rÆ  r’  s   & r   Ú_parse_base_urlrÈ  ß  s<   € à�>ˆ>�r×
 Ñ
 Ó
"€Cß<?Œ8˜5 Cœ<�CÓ8ÐI¨r°#°¨ZÓ8ÐIÀTÐIr   c                ó$   € V ^8„  d   QhR\         /# )r	   r   rk   )r   s   "r   r   r   å  s   € ÷ Iñ IœCñ Ir   c                 ó|   € V '       d4   V P                   ;'       g    R P                  4       P                  R4      # R # )r   r  )Úhostnamer   Úrstrip)Úparseds   &r   Ú_hostname_ofrÎ  å  s/   € ß:@ˆF�O‰O×!Ð!˜r×(Ñ(Ó*×1Ñ1°#Ó6ÐHÀbÐHr   c                ó0   € V ^8„  d   QhR\         R\         /# ©r	   rÆ  r   rk   )r   s   "r   r   r   é  s   € ÷ 3ñ 3¤ð 3¬ñ 3r   c                ó*   € \        \        V 4      4      # )a0  Lowercased hostname for a base URL, or ``""`` if absent.

Compare exact hostnames against provider hosts instead of substring-matching the raw URL:
``https://api.openai.com.example/v1`` or ``https://proxy.test/api.openai.com/v1`` would
otherwise pass as native endpoints and mis-route api_mode and auth.
)rÎ  rÈ  )rÆ  s   &r   Úbase_url_hostnamerÒ  é  s   € ô œ¨Ó1Ó2Ð2r   c                ó0   € V ^8„  d   QhR\         R\         /# rÐ  rk   )r   s   "r   r   r   ó  s   € ÷ ?ñ ?œCð ?¤Cñ ?r   c                ó~   € \        V 4      pV'       d   VP                  MRP                  4       P                  R4      # )z¥Lowercased URL path without the trailing slash (``""`` for a bare host); same scheme
tolerance as :func:`base_url_hostname` so host and path checks agree on one URL.r   Ú/)rÈ  r8   r   rÌ  )rÆ  rÍ  s   & r   Úbase_url_pathrÖ  ó  s0   € ô ˜XÓ&€Fß!ˆF�KŠK r×0Ñ0Ó2×9Ñ9¸#Ó>Ð>r   c                ó0   € V ^8„  d   QhR\         R\        /# )r	   Úmodelr   r   )r   s   "r   r   r   ú  s   € ÷ Jñ J¬cð J´dñ Jr   c                ó¢   € T ;'       g    RP                  4       P                  4       P                  R^4      R,          pVP                  R4      # )zYTrue for OpenAI families that reject ``max_tokens`` (HTTP 400 ``unsupported_parameter``).r   rÕ  éÿÿÿÿ)zgpt-4ozgpt-4.1zgpt-5Úo1Úo3Úo4)r   r   Úrsplitr£  )rØ  r®  s   & r   Ú"model_forces_max_completion_tokensrß  ú  sA   € à	�ˆ�"×ÑÓ×#Ñ#Ó%×,Ñ,¨S°!Ó4°RÕ8€AØ�<‰<ÐHÓIÐIr   c                ó\   € V ^8„  d   QhR\         R\        \         \         \        3,          /# rÐ  )r   r'   r(   )r   s   "r   r   r      s)   € ÷ cñ cœcð c¤e¬C´´c¨MÕ&:ñ cr   c                ó  € \        V 4      p\        V4      pV'       g   R# VP                  ;'       g    RP                  4       p VP                  pY2Tf   RRR^P/P                  T^ 4      3# T3#   \
         d    Ru # i ; i)uŒ  ``(scheme, hostname, effective_port)`` for a base URL; ``("", "", 0)`` on no host/bad port.

Origin, not just host: ``https://h`` vs ``http://h`` and two ports on one host are different
trust boundaries, so handing a bearer secret to a new URL must compare all three â€” hostname
alone would authorise an HTTPSâ†’HTTP downgrade. Port defaults to 443/80 so ``https://h``
equals ``https://h:443``.
r   Úhttpsi»  Úhttp)r   r   é    )rÈ  rÎ  Úschemer   Úportr  r›   )rÆ  rÍ  rË  rå  ræ  s   &    r   Úbase_url_originrç     s�   € ô ˜XÓ&€FÜ˜FÓ#€HßØÐØ�m‰m×!Ð!˜r×(Ñ(Ó*€FðØ�{‰{ˆð È4Ê<˜w¨¨V°RÐ8×<Ñ<¸VÀQÓGÐbÐbÐ]aÐbÐbøô ô ØÒðús   ÁA1 Á1BÂ Bc                ó<   € V ^8„  d   QhR\         R\         R\        /# )r	   rÆ  Údomainr   r   )r   s   "r   r   r     s&   € ÷ añ a¤Cð a´ð a¼ñ ar   c                ó  € \        V 4      pT;'       g    RP                  4       P                  4       P                  R4      p\	        T;'       d    T4      ;'       d%    W!8H  ;'       g    VP                  RV,           4      # )zÙTrue when the base URL's hostname is ``domain`` or a subdomain.

Safer than ``domain in base_url`` (``evil.com/moonshot.ai`` / ``moonshot.ai.evil`` must not
match). Accepts bare hosts, full URLs, and URLs with paths.
r   r  )rÒ  r   r   rÌ  r   Úendswith)rÆ  ré  rË  s   && r   Úbase_url_host_matchesrì    sn   € ô ! Ó*€HØ�lˆl˜×!Ñ!Ó#×)Ñ)Ó+×2Ñ2°3Ó7€FÜ�×#Ð#˜VÓ$×`Ð`¨(Ñ*<×*_Ð*_À×@QÑ@QÐRUÐX^ÕR^Ó@_Ð`r   c                ó@   € V ^8„  d   Qh/ ^ \         9   d
   \        ;R&   # )r	   rŠ  )Ú__conditional_annotations__rú   )r   s   "r   r   r      s   € × 0Ñ 0÷J Ò ”$Ñ òK 1r   >   Ú1ÚonÚyesÚtrue)Fr�  >   é   é    é!   )T>
   ÚnÚyÚ~Únorð  Úoffrñ  Únullrò  ÚfalserX   )rä  )g        )ÚHTTPS_PROXYÚ
HTTP_PROXYÚ	ALL_PROXYÚhttps_proxyÚ
http_proxyÚ	all_proxy)ÚNO_PROXYÚno_proxy)_rî  Ú__doc__r�   rü   r  r!   r©  r   r<   r¬   Ú	threadingr2   Ú
contextlibr   Úpathlibr   Útypingr   r   Úurllib.parser   Úhermes_yamlr'  Ú	getLoggerÚ__name__r˜   Ú	frozensetr   r   r#   r.   r4   r5   r@   rG   rO   rU   rZ   r]   r   rc   re   r”   r–   r—   ÚEXDEVÚEBUSYr‘   rf   r|   r„   r    rŽ   r±   r¶   rÈ   rÞ   rã   rï   rõ   r   r  r  r  r,  r5  r>  rM  r]  rU  r}  r�  r†  rŠ  ÚLockr‰  r�  r“  r˜  rœ  rŸ  r¿  rÁ  r¤  r°  r¼  rÄ  rÈ  rÎ  rÒ  rÖ  rß  rç  rì  r   )rî  s   @r   Ú<module>r     sÅ  øðÞ 0ã Û Û Û 	Û 	Û Û Û Û Û Ý Ý ß Ý !ã à	×	Ò	˜8Ó	$€ñ Ò5Ó6€÷÷Dõ
	Cð %Ð õ<õ
õ2õ!õ õ&.õ-ð �g‰g˜‰o€ñ %.ªkÓ$:Ð !ð Ð Ø"Ð Ø Ð ØŸ™ U§[¡[Ð1Ð õ_õ
õ,õ'õTVõ
4õ(ð( ¸u÷  ðF(ÀWð (Ðaeð (Ø)-ð(Ø>Cð(ØW\÷(÷VFðEÈð EÐdkð EØ,1ðEØMQðEØfjðEà(-÷Eð eÈgð eØ*.ðeØBG÷eõSð"'Ø23ð'ØHLð'àð'à27÷'õ"	2ðÈrð Ðpt÷ ð(
bÐQVð 
bÐjoð 
bØ26ð
bØRV÷
bõYðYÐPT÷ Yõ(+ñ^ $Ò$fÓgÐ õð$:jØEI÷:j÷zõ"ð
 Ð Ó Ø!ŸšÓ(Ð õõ&÷*÷
,÷
@ð
 g€Ø-Ð õõ3õ4õ,,õJõIõ3õ?õJõc÷(ar   