+
    jHàjß  ã                  óØ   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ]P                  ! ]4      t]! R4      R,          t^tRtR R ltRR	 R
 lltR]/R R lltR R ltR R ltR# )u�  Point-in-time copies of ``config.yaml``: one directory, one naming scheme, bounded count.

Every writer that wants a "before" copy of the user's config (setup wizard, corrupt-file
snapshot, model migrations) goes through :func:`backup_config`. Copies live in
``<HERMES_HOME>/backups/config/`` â€” ``backups/`` is already excluded from full backups, so they
never nest â€” as ``config.yaml.<reason>.<YYYYMMDD-HHMMSS>``. A copy identical to the newest one
for the same reason is skipped, and only the newest ``keep`` per reason survive, so repeated
``hermes setup`` runs or a gateway restarting against broken YAML cannot litter the home dir.
)ÚannotationsN)ÚPath)ÚOptionalÚbackupsÚconfigc               ó    € V ^8„  d   QhRRRR/# )é   Úconfig_pathr   Úreturn© )Úformats   "Ú>/home/ubuntu/.hermes/hermes-agent/hermes_cli/config_backups.pyÚ__annotate__r      s   € ÷ /ñ /˜Tð / dñ /ó    c                ó0   € V P                   \        ,          # ©N)ÚparentÚBACKUPS_SUBDIR)r	   s   &r   Úbackups_dirr      s   € Ø×Ñ¤Õ.Ð.r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r	   r   ÚreasonzOptional[str]r
   z
list[Path]r   )r   s   "r   r   r   "   s!   € ÷ 6ñ 6 Tð 6°=ð 6ÈJñ 6r   c                óê   a€ \        V 4      pVP                  4       '       g   . # V'       d   V P                   RV R2MV P                   R2o\        V3R lVP	                  4        4       R RR7      # )zDExisting backups, newest first; filtered to one *reason* when given.Ú.c              3  óœ   <"  € T FA  qP                  4       '       g   K  VP                  P                  S4      '       g   K=  Vx € KC  	  R # 5ir   )Úis_fileÚnameÚ
startswith)Ú.0ÚpÚprefixs   & €r   Ú	<genexpr>Ú&list_config_backups.<locals>.<genexpr>(   s-   øé € ÐZ™n˜·	±	·”1ÀÇÁ×@QÑ@QÐRX×@Y—1’1›nùs   ƒAŸAÁ
Ac                ó   € V P                   # r   )r   )r   s   &r   Ú<lambda>Ú%list_config_backups.<locals>.<lambda>)   s   €  §¢r   T)ÚkeyÚreverse)r   Úis_dirr   ÚsortedÚiterdir)r	   r   Úrootr   s   && @r   Úlist_config_backupsr+   "   si   ø€ ä�{Ó#€DØ�;‰;�=Š=Øˆ	ß06�× Ñ Ð!  6 (¨!Ñ,¸{×?OÑ?OÐ>PÐPQÐ<R€FÜÔZ˜dŸl™lœnÓZÙ&°ô6ð 6r   Úkeepc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r	   r   r   Ústrr,   Úintr
   zOptional[Path]r   )r   s   "r   r   r   ,   s)   € ÷ ñ ˜tð ¨Sð ¸3ð ÐR`ñ r   c               óÌ  €  V P                  4       '       d    V P                  4       P                  ^ 8X  d   R# \        V 4      pVP	                  RRR7       \        W4       \        W4      pV'       d-   V P                  4       V^ ,          P                  4       8X  d   R# W0P                   RV R\        P                  ! R4       2,          pVP                  4       '       g   VP                  4       '       d   R# \        P                  ! W4       V.VOVR  F  pVP                  RR7       K  	  V#   \          d#   p\"        P%                  RYT4        Rp?R# Rp?ii ; i)a
  Copy *config_path* to the backups dir; return the new path, or None when skipped/failed.

Skips when the file is missing/empty, or when the newest backup for *reason* already holds
identical bytes. Never raises: a failed backup must not block the write it precedes.
NT)ÚparentsÚexist_okr   z%Y%m%d-%H%M%S)Ú
missing_okzCould not back up %s (%s): %s)r   ÚstatÚst_sizer   ÚmkdirÚ_sweep_legacy_siblingsr+   Ú
read_bytesr   ÚtimeÚstrftimeÚ
is_symlinkÚexistsÚshutilÚcopy2ÚunlinkÚOSErrorÚloggerÚwarning)r	   r   r,   r*   ÚexistingÚdestÚstaleÚexcs   &&$     r   Úbackup_configrG   ,   s   € ðØ×"Ñ"×$Ò$¨×(8Ñ(8Ó(:×(BÑ(BÀaÔ(GÙÜ˜;Ó'ˆØ�
‰
˜4¨$ˆ
Ô/Ü˜{Ô1Ü& {Ó;ˆ÷ ˜×.Ñ.Ó0°H¸QµK×4JÑ4JÓ4LÔLÙØ×)Ñ)Ð*¨!¨F¨8°1´T·]²]À?Ó5SÐ4TÐUÕUˆØ�?‰?×Ò §¡§¢ÙÜ�Š�[Ô'ØÐ&˜XÐ& t uÓ-ˆEØ�L‰L DˆLÖ)ñ .àˆøÜô Ü�‰Ð6¸ÈSÔQÝûðús/   ‚D6 ˜D6 ¹A&D6 Â!AD6 Ã=8D6 Ä6E#ÅEÅE#c               ó    € V ^8„  d   QhRRRR/# )r   r	   r   r
   zOptional[dict]r   )r   s   "r   r   r   I   s   € ÷ 4ñ 4¨ð 4°.ñ 4r   c                óv  € \        V R4      R,          pV'       g   R#  ^ RIHp V^ ,          P                  RR7      ;_uu_ 4       pV! V4      pRRR4       \        X\        4      '       d   T# R#   + '       g   i     L*; i  \         d*   p\
        P                  RT^ ,          T4        Rp?R# Rp?ii ; i)aV  Parse the newest ``good`` backup (the file as it was at the last successful load).

Returns the raw mapping, or None when there is no usable copy. Older ``good`` copies are not
tried: a backup that fails to parse means the on-disk copy was damaged after the fact, and
guessing further back would serve a config the user never saw as current.
Úgood:Né   NN)Úfast_safe_loadzutf-8)Úencodingz+Last-known-good backup %s is unreadable: %s)	r+   ÚutilsrL   ÚopenÚ	ExceptionrA   rB   Ú
isinstanceÚdict)r	   ÚnewestrL   ÚfÚdatarF   s   &     r   Úload_newest_good_backuprV   I   s“   € ô ! ¨fÓ5°bÕ9€FßÙðÝ(Ø�A�Y�^‰^ Wˆ^×-Ô-°Ù! !Ó$ˆD÷ .ô
 ˜d¤D×)Ò)ˆ4Ð3¨tÐ3÷ .×-ûäô Ü�‰ÐDÀfÈQÅiÐQTÔUÝûðús5   Ÿ'B Á	A1ÁB Á1B	Á<B ÂB ÂB8ÂB3Â3B8c               ó$   € V ^8„  d   QhRRRRRR/# )r   r	   r   r*   r
   ÚNoner   )r   s   "r   r   r   ]   s&   € ÷ Nñ N¨ð N°Dð N¸Tñ Nr   c                ób  € \          Fu  pV P                  P                  V4       FS  pVP                  4       '       d   VP	                  4       '       d   K1   VP                  WP                  ,          4       KU  	  Kw  	  R#   \         d"   p\        P                  R Y44        Rp?K„  Rp?ii ; i)z#Could not move legacy backup %s: %sN)
Ú_LEGACY_SIBLING_GLOBSr   Úglobr   r;   Úreplacer   r@   rA   Údebug)r	   r*   ÚpatternÚoldrF   s   &&   r   r7   r7   ]   s   € ß(ˆØ×%Ñ%×*Ñ*¨7Ö3ˆCØ—;‘;—=’= C§N¡N×$4Ò$4ÙðNØ—‘˜D§8¡8�OÖ,ó	 4ó )øô ô NÜ—‘ÐBÀC×MÒMûðNús   Á!BÂB.ÂB)Â)B.)zconfig.yaml.bak.[0-9]*zconfig.yaml.corrupt.*.bakzconfig.yaml.bak-pre-migrate-*r   )Ú__doc__Ú
__future__r   Úloggingr=   r9   Úpathlibr   Útypingr   Ú	getLoggerÚ__name__rA   r   ÚDEFAULT_KEEPrZ   r   r+   rG   rV   r7   r   r   r   Ú<module>rh      sk   ðñõ #ã Û Û Ý Ý à	×	Ò	˜8Ó	$€á�i“ 8Õ+€Ø€ð
 qÐ õ/÷6ðÀ÷ õ:4÷(Nr   