§
    øžyj ã                   ó¦  — d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZmZ ddlmZ ddlmZmZmZmZ ddlmZmZmZ ddlmZ  ej        e¦  «        Zh d	£Z d
Z!h d£Z"h d£Z#h d£Z$dZ% G d„ de&¦  «        Z' G d„ de&¦  «        Z(ed\dede)fd„¦   «         Z*edefd„¦   «         Z+dee         fd„Z,dedee         fd„Z-dede.fd„Z/deded ede.fd!„Z0d"ed#ede.fd$„Z1d%d&d'œd(ed)e2d*e.de.fd+„Z3d,Z4d-Z5d.d.e5d/œd(ed0e.d1e.d2e2de6f
d3„Z7d"ed#ede.fd4„Z8d]d5„Z9d6eddfd7„Z:d8e
j;        de<e.e=f         fd9„Z>d8e
j;        de=fd:„Z?d]d;„Z@d<ZAd=ZBd>ZCd^dee         defd?„ZD	 	 	 	 d_d@ee=         dee         dAee2         dBee2         dee=         f
dC„ZE	 	 	 	 d_d@ee=         dee         dAee2         dBee2         dee=         f
dD„ZF	 	 d`dEe2dee         deee=ef                  fdF„ZG	 d^dGe=dee         de.fdH„ZHdIZId(edee2         fdJ„ZJ	 d^dGe=dee         deee=ef                  fdK„ZKeCfdLedAe2de2fdM„ZLeCdfdAe2dee         de2fdN„ZMd]dO„ZNd ed6edee         fdP„ZOd ed6edee         fdQ„ZPdRZQdSZRdTZSd^dee         defdU„ZTdVedAe2de2fdW„ZUdeSfdee         dAe2dee         fdX„ZVdYZWdTZXdVedAe2de2fdZ„ZYdeXfdee         dAe2dee         fd[„ZZdS )aa  
Backup and import commands for hermes CLI.

`hermes backup` creates a zip archive of the entire ~/.hermes/ directory
(excluding the hermes-agent repo and transient files).

`hermes import` restores from a backup zip, overlaying onto the current
HERMES_HOME root.
é    N)Úcontextmanager)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚDictÚListÚOptional)Úget_default_hermes_rootÚget_hermes_homeÚdisplay_hermes_home)Úformat_bytes>   ú.gitú.noxú.toxú.venvú.cacheú.mypy_cacheú.ruff_cacheú.pytest_cacheúsite-packagesÚvenvÚbackupsÚ__pycache__ÚcheckpointsÚnode_modulesúhermes-agent)z.pycz.pyoz.db-walz.db-shmz.db-journal>   úcron.pidúgateway.pidú.backup.lock>   úgateway.lockr   r   úprocesses.jsonúgateway_state.json>   ú.envústate.dbú	auth.jsonz
_external/c                   ó   — e Zd ZdZdS )ÚBackupInProgressErrorz@Raised when another process already owns the Hermes backup slot.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    ú7/home/ragecks/.hermes/hermes-agent/hermes_cli/backup.pyr(   r(   Ž   s   € € € € € ØJÐJÐJÐJr.   r(   c                   ó   — e Zd ZdS )Ú_SQLiteSnapshotErrorN)r)   r*   r+   r-   r.   r/   r1   r1   ’   s   € € € € € Ø€Dr.   r1   ç      Ð?Úhermes_homeÚtimeout_secondsc              #   óÀ  K  — | dz  }|j                              dd¬¦  «         |                     d¦  «        }d}t          j        ¦   «         t          d|¦  «        z   }	 t          j        dk    rådd	l}| 	                    ¦   «         j
        dk    r)|                     d
¦  «         |                     ¦   «          	 	 |                     d¦  «         |                     |                     ¦   «         |j        d¦  «         d}nR# t"          t$          f$ r= t          j        ¦   «         |k    rt'          d¦  «        ‚t          j        d¦  «         Y nw xY wŒ™n�dd	l}	 	 |                     |                     ¦   «         |j        |j        z  ¦  «         d}nR# t2          t"          f$ r= t          j        ¦   «         |k    rt'          d¦  «        ‚t          j        d¦  «         Y nw xY wŒ‹d	V — |r¢	 t          j        dk    rHdd	l}|                     d¦  «         |                     |                     ¦   «         |j        d¦  «         n1dd	l}|                     |                     ¦   «         |j        ¦  «         n# t"          t$          f$ r Y nw xY w|                     ¦   «          d	S # |r¢	 t          j        dk    rHdd	l}|                     d¦  «         |                     |                     ¦   «         |j        d¦  «         n1dd	l}|                     |                     ¦   «         |j        ¦  «         n# t"          t$          f$ r Y nw xY w|                     ¦   «          w xY w)zCAcquire one cross-process backup slot for full and quick snapshots.r    T©ÚparentsÚexist_okza+bFg        Úntr   Nó    é   z(another Hermes backup is already runninggš™™™™™©?)ÚparentÚmkdirÚopenÚtimeÚ	monotonicÚmaxÚosÚnameÚmsvcrtÚstatÚst_sizeÚwriteÚflushÚseekÚlockingÚfilenoÚLK_NBLCKÚOSErrorÚPermissionErrorr(   ÚsleepÚfcntlÚflockÚLOCK_EXÚLOCK_NBÚBlockingIOErrorÚLK_UNLCKÚLOCK_UNÚclose)r3   r4   Ú	lock_pathÚhandleÚacquiredÚdeadlinerD   rP   s           r/   Ú_backup_operation_lockr\   –   sc  è è € ð ˜nÑ,€IØÔ×Ò 4°$ÐÑ7Ô7Ð7Ø�^Š^˜EÑ"Ô"€FØ€HÝŒ~ÑÔ¥# c¨?Ñ";Ô";Ñ;€Hð-ÝŒ7�dŠ?ˆ?ØˆMˆMˆMà�~Š~ÑÔÔ'¨1Ò,Ð,Ø—’˜TÑ"Ô"Ð"Ø—’‘”�ð	%ð%Ø—K’K ‘N”N�NØ—N’N 6§=¢=¡?¤?°F´OÀQÑGÔGÐGØ#�HØøÝ¥Ð1ð %ð %ð %Ý”~Ñ'Ô'¨8Ò3Ð3Ý3Ð4^Ñ_Ô_Ð_Ý”J˜tÑ$Ô$Ð$Ð$Ð$ð%øøøð	%ð
 ð ˆLˆLˆLð%ð%Ø—K’K §¢¡¤°´ÀÄÑ1NÑOÔOÐOØ#�HØøÝ'­Ð1ð %ð %ð %Ý”~Ñ'Ô'¨8Ò3Ð3Ý3Ð4^Ñ_Ô_Ð_Ý”J˜tÑ$Ô$Ð$Ð$Ð$ð%øøøð%ð 	ˆˆˆàð 	ðÝ”7˜d’?�?Ø!�M�M�Mà—K’K ‘N”N�NØ—N’N 6§=¢=¡?¤?°F´OÀQÑGÔGÐGÐGà �L�L�Là—K’K §¢¡¤°´Ñ?Ô?Ð?øøÝ�_Ð-ð ð ð Ø�ðøøøà�Š‰Œˆˆˆøð ð 	ðÝ”7˜d’?�?Ø!�M�M�Mà—K’K ‘N”N�NØ—N’N 6§=¢=¡?¤?°F´OÀQÑGÔGÐGÐGà �L�L�Là—K’K §¢¡¤°´Ñ?Ô?Ð?øøÝ�_Ð-ð ð ð Ø�ðøøøà�Š‰Œˆˆøøøs�   Á AJ# Â<AD ÄJ# ÄAEÅJ# ÅEÅ
J# Å7F ÆJ# ÆAG!ÇJ# Ç G!Ç!J# Ç,B	I6 É6J
Ê	J
Ê#MÊ'B	L1Ì0MÌ1MÍMÍMÍMÚ
final_pathc           	   #   ó@  K  — |                       d| j        › dt          j        ¦   «         › dt	          j        ¦   «         › d�¦  «        }|                     d¬¦  «         	 |V — t          j        || ¦  «         dS # t          $ r |                     d¬¦  «         ‚ w xY w)zDYield a hidden sibling path and publish it only after a clean close.ú.Ú-ú.partialT©Ú
missing_okN)	Ú	with_namerC   rB   ÚgetpidÚ	threadingÚ	get_identÚunlinkÚreplaceÚBaseException)r]   Úpartial_paths     r/   Ú_atomic_output_pathrl   Î   sÃ   è è € ð ×'Ò'ØKˆJŒOÐKÐK�bœi™kœkÐKÐK­IÔ,?Ñ,AÔ,AÐKÐKÐKñô €Lð ×Ò 4ÐÑ(Ô(Ð(ðØÐÐÐÝ
Œ
�< Ñ,Ô,Ð,Ð,Ð,øÝð ð ð Ø×Ò tÐÑ,Ô,Ð,Øðøøøs   Á A; Á;"BÚreturnc                  óÔ  — 	 ddl m} m} n# t          $ r g cY S w xY w	  | ¦   «         }n# t          $ r d}Y nw xY w|sg S 	  ||¦  «        }n# t          $ r d}Y nw xY w|€g S 	 |                     ¦   «         pg }n5# t          $ r(}t
                               d||¦  «         g cY d}~S d}~ww xY wg }t          ¦   «         }|D ]¦}	 t          |¦  «         	                    ¦   «         }	n# t          $ r Y Œ1w xY w|	 
                    ¦   «         sŒJ	 |	                     ¦   «         }
n# t          t          f$ r Y Œsw xY w|
|v rŒ||                     |
¦  «         |                     |	¦  «         Œ§|S )u•  Return existing absolute paths the active memory provider stores
    outside HERMES_HOME, resolved from config only (no network, no init).

    Reads ``memory.provider`` from config, loads just that provider, and asks
    it for ``backup_paths()``. Returns an empty list when no external provider
    is active or the provider can't be loaded â€” backup must never fail because
    of a flaky plugin.
    r   )Ú_get_active_memory_providerÚload_memory_providerNz0backup_paths() failed for memory provider %r: %s)Úplugins.memoryro   rp   Ú	ExceptionÚbackup_pathsÚloggerÚwarningÚsetr   Ú
expanduserÚexistsÚresolverM   Ú
ValueErrorÚaddÚappend)ro   rp   ÚactiveÚproviderÚdeclaredÚexcÚoutÚseenÚrawÚpÚresolveds              r/   Ú'_collect_memory_provider_external_pathsr†   Ý   s  € ðØTÐTÐTÐTÐTÐTÐTÐTÐTøÝð ð ð Øˆ	ˆ	ˆ	ðøøøðØ,Ð,Ñ.Ô.ˆˆøÝð ð ð Øˆˆˆðøøøàð Øˆ	ðØ'Ð'¨Ñ/Ô/ˆˆøÝð ð ð ØˆˆˆðøøøàÐØˆ	ðØ×(Ò(Ñ*Ô*Ð0¨bˆˆøÝð ð ð Ý�ŠÐIÈ6ÐSVÑWÔWÐWØˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøð €CÝ‘”€DØð ð ˆð	Ý�S‘	”	×$Ò$Ñ&Ô&ˆAˆAøÝð 	ð 	ð 	ØˆHð	øøøà�xŠx‰zŒzð 	Øð	Ø—y’y‘{”{ˆHˆHøÝ�Ð$ð 	ð 	ð 	ØˆHð	øøøà�tÐÐØØ�Š�ÑÔÐØ�
Š
�1‰ŒˆˆØ€Jsl   ‚ ‹™ž
) ©8·8Á A ÁAÁAÁ#A: Á:
B,ÂB'Â!B,Â'B,Ã!C&Ã&
C3Ã2C3ÄD!Ä!D5Ä4D5Úbasec                 óò  — g }|                       ¦   «         r+|                      ¦   «         s|                     | ¦  «         |S |                      ¦   «         s|S t	          j        | d¬¦  «        D ]ˆ\  }}}t          |¦  «        }d„ |D ¦   «         |dd…<   |D ]_}||z  }|                     ¦   «         rŒ|j        t          v s|j         	                    t          ¦  «        rŒJ|                     |¦  «         Œ`Œ‰|S )z„Yield regular files under *base* (a file or a directory), skipping
    symlinks, caches, and pyc files. *base* itself may be a file.F©Úfollowlinksc                 ó$   — g | ]}|t           v¯|‘ŒS r-   ©Ú_EXCLUDED_DIRS©Ú.0Úds     r/   ú
<listcomp>z(_iter_external_files.<locals>.<listcomp>  s"   € ÐFÐFÐF˜Q¨aµ~Ð.EÐ.E�qÐ.EÐ.EÐ.Er.   N)Úis_fileÚ
is_symlinkr|   Úis_dirrB   Úwalkr   rC   Ú_EXCLUDED_NAMESÚendswithÚ_EXCLUDED_SUFFIXES)r‡   ÚfilesÚdirpathÚdirnamesÚ	filenamesÚdpÚfnameÚfpaths           r/   Ú_iter_external_filesr      s  € ð €EØ‡|‚|�~„~ð ˜dŸošoÑ/Ô/ð Ø�Š�TÑÔÐØˆØ�;Š;‰=Œ=ð ØˆÝ(*¬°À%Ð(HÑ(HÔ(Hð 	 ð 	 Ñ$ˆ�˜9Ý�'‰]Œ]ˆØFÐF (ÐFÑFÔFˆ���‰Øð 	 ð 	 ˆEØ˜‘JˆEØ×ÒÑ!Ô!ð ØØŒz�_Ð,Ð,°´
×0CÒ0CÕDVÑ0WÔ0WÐ,ØØ�LŠL˜ÑÔÐÐð	 ð €Lr.   Úrel_pathc                 ó¸   — | j         }|D ]!}|t          vrŒ|dk    r||d         k    rŒ dS | j        }|t          v rdS |                     t
          ¦  «        rdS dS )zFReturn True if *rel_path* (relative to hermes root) should be skipped.r   r   TF)Úpartsr�   rC   r–   r—   r˜   )r¡   r£   ÚpartrC   s       r/   Ú_should_excluder¥   )  s�   € àŒN€Eàð ð ˆØ•~Ð%Ð%Øð �>Ò!Ð! d¨e°A¬hÒ&6Ð&6ØØˆtˆtàŒ=€Dà�ÐÐØˆtà‡}‚}Õ'Ñ(Ô(ð Øˆtàˆ5r.   Úabs_pathÚout_pathc                 óÖ   — t          |¦  «        rdS |                      ¦   «         rdS 	 |                      ¦   «         |                     ¦   «         k    S # t          t          f$ r Y dS w xY w)zHReturn True when a candidate file should not be written to a backup zip.TF)r¥   r“   ry   rM   rz   )r¦   r¡   r§   s      r/   Ú_should_skip_backup_filer©   B  s�   € å�xÑ Ô ð Øˆtð ×ÒÑÔð ØˆtðØ×ÒÑ!Ô! X×%5Ò%5Ñ%7Ô%7Ò7Ð7øÝ•ZÐ ð ð ð Øˆuˆuðøøøs   ©)A ÁA(Á'A(ÚsrcÚdstc                 óŽ  — d}d}	 t          j        d| › d�d¬¦  «        }t          j        t          |¦  «        ¦  «        }|                     |¦  «         	 ||fD ]*}|�&	 |                     ¦   «          Œ# t
          $ r Y Œ&w xY wŒ+dS # t
          $ r~}t                               d| |¦  «         	 |                     d¬¦  «         n# t          $ r Y nw xY wY d}~||fD ]*}|�&	 |                     ¦   «          Œ# t
          $ r Y Œ&w xY wŒ+dS d}~ww xY w# ||fD ]*}|�&	 |                     ¦   «          Œ# t
          $ r Y Œ&w xY wŒ+w xY w)	u  Copy a SQLite database safely using the backup() API.

    Handles WAL mode â€” produces a consistent snapshot even while
    the DB is being written to. Fail closed if a consistent snapshot cannot
    be created: copying only the live main file can omit committed WAL data.
    Núfile:ú?mode=roT)Úuriz"SQLite safe copy failed for %s: %srb   F)
Úsqlite3ÚconnectÚstrÚbackuprW   rr   rt   ru   rh   rM   )rª   r«   ÚconnÚbackup_connÚ
connectionr€   s         r/   Ú_safe_copy_dbr·   V  s  € ð €DØ€KðÝŒÐ4 sÐ4Ð4Ð4¸$Ð?Ñ?Ô?ˆÝ”o¥c¨#¡h¤hÑ/Ô/ˆØ�Š�KÑ Ô Ð Øð '¨Ð-ð 	ð 	ˆJØÐ%ðØ×$Ò$Ñ&Ô&Ð&Ð&øÝ ð ð ð Ø�Dðøøøð &ð	ð 	øõ ð ð ð Ý�ŠÐ;¸SÀ#ÑFÔFÐFð	Ø�JŠJ $ˆJÑ'Ô'Ð'Ð'øÝð 	ð 	ð 	ØˆDð	øøøàˆuˆuˆuà&¨Ð-ð 	ð 	ˆJØÐ%ðØ×$Ò$Ñ&Ô&Ð&Ð&øÝ ð ð ð Ø�Dðøøøð &ð	ð 	øøøøðøøøøð '¨Ð-ð 	ð 	ˆJØÐ%ðØ×$Ò$Ñ&Ô&Ð&Ð&øÝ ð ð ð Ø�Dðøøøð &ð	øøøs–   †AB Á A5Á5
BÂBÂ
DÂDÂ/CÃDÃ
CÃDÃCÃDÃD Ã#C8Ã8
DÄDÄDÄD Ä	EÄD2Ä1EÄ2
D?	Ä<EÄ>D?	Ä?Eéd   F)Úprobe_bytesÚforceÚpathr¹   rº   c                ó  — 	 |                       ¦   «         j        }n# t          $ r Y dS w xY w|dk    rdS ddlm}  || t          d|¦  «        |¬¦  «        }|sdS |                     d¦  «        rdS t          d„ |D ¦   «         ¦  «        S )a/  True when *path* looks like the #68474 zeroed-state.db signature.

    Signature: size > 0, first *probe_bytes* are all NUL (no ``SQLite format 3``
    header). Used at SessionDB open and for snapshot diagnostics so a silent
    all-zero file becomes a guided recovery instead of a generic failure.
    Fr   ©Úread_header_bytes_preopené   )Úlengthrº   s   SQLite format 3c              3   ó"   K  — | ]
}|d k    V — ŒdS )r   Nr-   )r�   Úbytes     r/   ú	<genexpr>z(is_zeroed_sqlite_file.<locals>.<genexpr>Œ  s&   è è € Ð*Ð*˜Tˆt�qŠyÐ*Ð*Ð*Ð*Ð*Ð*r.   )rE   rF   rM   Úhermes_cli.sqlite_safe_readr¾   rA   Ú
startswithÚall)r»   r¹   rº   Úsizer¾   Úheads         r/   Úis_zeroed_sqlite_filerÉ   t  sÉ   € ðØ�yŠy‰{Œ{Ô"ˆˆøÝð ð ð Øˆuˆuðøøøàˆq‚y€yØˆuØEÐEÐEÐEÐEÐEà$Ð$Ø•S˜˜[Ñ)Ô)°ðñ ô €Dð ð ØˆuØ‡‚Ð)Ñ*Ô*ð ØˆuÝÐ*Ð* TÐ*Ñ*Ô*Ñ*Ô*Ð*s   ‚ œ
*©*s   SQLite format 3 l        T)Úcheck_headerÚ
run_pragmaÚ	max_bytesrÊ   rË   rÌ   c                ó~  — ddddœ}	 |                       ¦   «         }n6# t          $ r d| › �|d<   |cY S t          $ r}d|› �|d<   |cY d}~S d}~ww xY w|j        |d<   |j        d	k     rd
|j        › d�|d<   |S |dk    o
|j        |k    }|rhddlm}  || t          t          ¦  «        ¬¦  «        }	|	€d|d<   d|d<   |S |	t          k    r*d|d<   d|	dd…                              ¦   «         ›d�|d<   |S |�rzd}d}
	 t          j
        d| › d�dd¬¦  «        }
|
                     d¦  «                             ¦   «          |
                     d¦  «                             ¦   «          d|d<   d|j        d›d|d›d�|d<   nœ# t          j        $ rA}d|d<   d|› �|d<   |cY d}~|
�&	 |
                     ¦   «          S # t          $ r Y S w xY wS d}~wt          $ rA}d|d<   d |› �|d<   |cY d}~|
�&	 |
                     ¦   «          S # t          $ r Y S w xY wS d}~ww xY w	 |
�&	 |
                     ¦   «          n># t          $ r Y n2w xY wn-# |
�&	 |
                     ¦   «          w # t          $ r Y w w xY ww xY w|�r¸d}	 t          j
        d| › d�dd¬¦  «        }|                     d!¦  «        }|                     ¦   «         }t          |¦  «        d"k    rF|d         d         d#k    r4d|d<   d$|d<   ||�&	 |                     ¦   «          S # t          $ r Y S w xY wS d%„ |D ¦   «         }d&d'                     |dd(…         ¦  «        › �|d<   ||�&	 |                     ¦   «          S # t          $ r Y S w xY wS # t          j        $ r<}d)|› �|d<   |cY d}~|�&	 |                     ¦   «          S # t          $ r Y S w xY wS d}~wt          $ r<}d*|› �|d<   |cY d}~|�&	 |                     ¦   «          S # t          $ r Y S w xY wS d}~ww xY w# |�&	 |                     ¦   «          w # t          $ r Y w w xY ww xY wd|d<   |d         sd+|d<   |S ),uW  Verify that a SQLite database at *path* is intact.

    Checks, in order:
      1. File exists and has an expected minimum size.
      2. SQLite header magic bytes are present.
      3. For files at or under ``max_bytes``, a read-only
         ``PRAGMA integrity_check``. For larger files, a cheap structural
         probe (schema read) instead â€” see ``max_bytes``.

    Args:
        path: Path to the database file.
        check_header: When true (default), verify the SQLite header magic.
        run_pragma: When true (default), run ``PRAGMA integrity_check`` via
            a read-only connection and verify the result is ``"ok"``.
        max_bytes: Size ceiling for the full ``PRAGMA integrity_check``.
            Files larger than this fall back to the header check plus a
            cheap structural probe, because ``integrity_check`` pages
            through the ENTIRE file â€” minutes of silent pegged CPU on a
            multi-GB database. Defaults to
            :data:`DEFAULT_INTEGRITY_CHECK_MAX_BYTES` (2 GiB); pass ``0``
            to force the full check regardless of size.

    Returns:
        A dict with keys:
          - ``valid`` (bool): true when all requested checks passed.
          - ``message`` (str): human-readable outcome or error detail.
          - ``size`` (int | None): file size in bytes, or None if stat failed.
    FÚ N)ÚvalidÚmessagerÇ   znot found: rÐ   zcannot stat: rÇ   r¸   ztoo small (z% bytes) to be a valid SQLite databaser   r½   )rÀ   rÏ   zcannot read headerz!missing SQLite header magic (got r¿   ú)r­   r®   Tg      ð?)r¯   ÚtimeoutzPRAGMA schema_versionz"SELECT count(*) FROM sqlite_masterzsize Ú,z bytes exceeds max_bytes z?; skipped PRAGMA integrity_check (header + schema probe passed)zschema probe failed: zschema probe error: zPRAGMA integrity_checkr;   Úokzintegrity check passedc                 ó8   — g | ]}t          |d          ¦  «        ‘ŒS )r   )r²   )r�   Úrs     r/   r‘   z+verify_sqlite_integrity.<locals>.<listcomp>  s"   € Ð.Ð.Ð. A•c˜!˜Aœ$‘i”iÐ.Ð.Ð.r.   zintegrity check failed: z; é   zcannot open database: zintegrity check error: zheader check passed)rE   ÚFileNotFoundErrorrM   rF   rÄ   r¾   ÚlenÚ_SQLITE_HEADERÚhexr°   r±   ÚexecuteÚfetchoneÚDatabaseErrorrW   rr   ÚfetchallÚjoin)r»   rÊ   rË   rÌ   ÚresultÚstr€   Ú	oversizedr¾   rÈ   Úprober´   ÚcursorÚrowsÚerrorss                  r/   Úverify_sqlite_integrityrè      s�  € ðF #¨r¸4Ð@Ð@€FðØ�YŠY‰[Œ[ˆˆøÝð ð ð Ø0¨$Ð0Ð0ˆˆyÑØˆˆˆÝð ð ð Ø1¨CÐ1Ð1ˆˆyÑØˆˆˆˆˆˆøøøøðøøøð ”Z€Fˆ6�Nà	„z�CÒÐØ[¨"¬*Ð[Ð[Ð[ˆˆyÑØˆà˜A’Ð8 "¤*¨yÒ"8€Iàð ð
 	JÐIÐIÐIÐIÐIà(Ð(¨µc½.Ñ6IÔ6IÐJÑJÔJˆØˆ<Ø#ˆF�7‰OØ 4ˆF�9ÑØˆMØ•>Ò!Ð!Ø#ˆF�7‰OàH°D¸¸"¸´I·M²M±O´OÐHÐHÐHð �9Ñð ˆMàñ  ð ˆ
Øˆð	Ý”OÐ$:¨DÐ$:Ð$:Ð$:ÀÈcÐRÑRÔRˆEØ�MŠMÐ1Ñ2Ô2×;Ò;Ñ=Ô=Ð=Ø�MŠMÐ>Ñ?Ô?×HÒHÑJÔJÐJØ"ˆF�7‰OðP˜œ
ÐNð Pð P¸yÐNð Pð Pð Pð �9ÑÐøõ Ô$ð 	ð 	ð 	Ø#ˆF�7‰OØ =¸Ð =Ð =ˆF�9ÑØˆMˆMˆMˆMˆMð Ð ðØ—K’K‘M”M�M�MøÝ ð ð ð Ø�Dðøøøð !øøøøõ ð 	ð 	ð 	Ø#ˆF�7‰OØ <°sÐ <Ð <ˆF�9ÑØˆMˆMˆMˆMˆMàÐ ðØ—K’K‘M”M�M�MøÝ ð ð ð Ø�Dðøøøð !øøøøð	øøøð ð Ð ðØ—K’K‘M”M�M�MøÝ ð ð ð Ø�Dðøøøð !øˆuÐ ðØ—K’K‘M”M�M�MøÝ ð ð ð Ø�Dðøøøð !øøøð ñ Øˆð	Ý”?Ð#9¨4Ð#9Ð#9Ð#9¸tÈSÐQÑQÔQˆDØ—\’\Ð":Ñ;Ô;ˆFØ—?’?Ñ$Ô$ˆDÝ�4‰yŒy˜AŠ~ˆ~ $ q¤'¨!¤*°Ò"4Ð"4Ø"&��w‘Ø$<��yÑ!Øð ÐðØ—J’J‘L”L�L�LøÝ ð ð ð Ø�Dðøøøð  ð /Ð.¨Ð.Ñ.Ô.ˆFØ R¸4¿9º9ÀVÈBÈQÈBÄZÑ;PÔ;PÐ RÐ RˆF�9ÑØð ÐðØ—J’J‘L”L�L�LøÝ ð ð ð Ø�Dðøøøð  øõ Ô$ð 	ð 	ð 	Ø >¸Ð >Ð >ˆF�9ÑØˆMˆMˆMˆMˆMð
 ÐðØ—J’J‘L”L�L�LøÝ ð ð ð Ø�Dðøøøð  øøøøõ	 ð 	ð 	ð 	Ø ?¸#Ð ?Ð ?ˆF�9ÑØˆMˆMˆMˆMˆMàÐðØ—J’J‘L”L�L�LøÝ ð ð ð Ø�Dðøøøð  øøøøð		øøøøð ÐðØ—J’J‘L”L�L�LøÝ ð ð ð Ø�Dðøøøð  øøøð €Fˆ7�OØ�)Ôð 2Ø1ˆˆyÑØ€Msƒ  ˆ �A³	A¼	AÁAÁAÃ;BE= Å<I Å=HÆGÆHÆI Æ"F7Æ7
GÇGÇHÇHÇ#HÇ$I Ç+H È 
HÈHÈHÈI ÈH2 È2
H?È>H?ÉI-ÉIÉI-É
I)É&I-É(I)É)I-É6A4M, Ë-LÌ
LÌLÌ0M, ÍMÍ
M(Í'M(Í,O;Í;	N2ÎO;ÎO> ÎN!Î!
N.Î-N.Î2O;Î?	O6ÏO;Ï	O> ÏO%Ï%
O2Ï1O2Ï6O;Ï;O> Ï>P(ÐPÐP(Ð
P$Ð!P(Ð#P$Ð$P(c                 ó(  — t          | |¦  «        sdS t          |d¬¦  «        }|                     d¦  «        sY	 |                     d¬¦  «         n# t          $ r Y nw xY wt
                               d| |                     d¦  «        ¦  «         dS dS )u…  Like :func:`_safe_copy_db` but verifies the destination after copy.

    Returns True only when the copy succeeded AND the destination is valid
    SQLite (header + integrity check). Verification honours the default
    size ceiling â€” a multi-GB destination gets the header + schema probe
    rather than a full ``PRAGMA integrity_check`` that would page through
    the whole file.
    FT)rË   rÏ   rb   z.Backup of %s failed integrity verification: %srÐ   )r·   rè   Úgetrh   rM   rt   ru   )rª   r«   Ú	integritys      r/   Úcopy_db_and_verifyrì   +  s«   € õ ˜˜cÑ"Ô"ð ØˆuÝ'¨¸Ð=Ñ=Ô=€IØ�=Š=˜Ñ!Ô!ð ð	Ø�JŠJ $ˆJÑ'Ô'Ð'Ð'øÝð 	ð 	ð 	ØˆDð	øøøå�ŠÐGÈÈiÏmÊmÐ\eÑNfÔNfÑgÔgÐgØˆuØˆ4s   ºA Á
AÁAc                 óp  — t          ¦   «         }|                     ¦   «         s&t          d|› �¦  «         t          j        d¦  «         	 t          |¦  «        5  t          | |¦  «         ddd¦  «         dS # 1 swxY w Y   dS # t          $ r'}t          d|› �¦  «         t          d¦  «        |‚d}~ww xY w)z1Create a zip backup of the Hermes home directory.z*Error: Hermes home directory not found at r;   NúError: é   )	r   r”   ÚprintÚsysÚexitr\   Ú_run_backup_lockedr(   Ú
SystemExit)ÚargsÚhermes_rootr€   s      r/   Ú
run_backupr÷   E  s  € å)Ñ+Ô+€Kà×ÒÑÔð ÝÐH¸;ÐHÐHÑIÔIÐIÝŒ�‰Œˆð%Ý# KÑ0Ô0ð 	2ð 	2Ý˜t [Ñ1Ô1Ð1ð	2ð 	2ð 	2ñ 	2ô 	2ð 	2ð 	2ð 	2ð 	2ð 	2ð 	2ð 	2øøøð 	2ð 	2ð 	2ð 	2ð 	2ð 	2øå ð %ð %ð %Ýˆo˜ˆoˆoÑÔÐÝ˜‰mŒm Ð$øøøøð%øøøs<   Á
B ÁA7Á*B Á7A;Á;B Á>A;Á?B Â
B5Â"B0Â0B5rö   c           
      óÂ  ‡)— | j         r|t          | j         ¦  «                             ¦   «                              ¦   «         }|                     ¦   «         r/t          j        ¦   «                              d¦  «        }|d|› d�z  }n@t          j        ¦   «                              d¦  «        }t          j        ¦   «         d|› d�z  }|j	         
                    ¦   «         dk    r|                     |j	        dz   ¦  «        }|j                             dd¬¦  «         t          j        ¦   «         }t                                d¦  «         t%          dt'          ¦   «         › d�¦  «         g }t)          ¦   «         }t+          j        |d	¬
¦  «        D ]é\  }}}	t          |¦  «        }
|
                     |¦  «        }|t          d¦  «        k    Š)|dd…         }ˆ)fd„|D ¦   «         |dd…<   t)          |¦  «        t)          |¦  «        z
  D ]'}|                     t3          ||z  ¦  «        ¦  «         Œ(|	D ]E}|
|z  }|                     |¦  «        }t5          |||¦  «        rŒ.|                     ||f¦  «         ŒFŒêt          j        ¦   «                              ¦   «         }g }g }t9          ¦   «         D ]ë}	 |                     ¦   «         }|                     |¦  «         n9# t:          t<          f$ r% |                     t3          |¦  «        ¦  «         Y Œbw xY wt?          |¦  «        D ]u}	 |                     ¦   «                              |¦  «        }n# t:          t<          f$ r Y Œ>w xY wt@          | !                    ¦   «         z   }|                     ||f¦  «         ŒvŒì|sE|sCt                                dt          j        ¦   «         |z
  dz  ¦  «         t%          d¦  «         dS tE          |¦  «        tE          |¦  «        z   }t                                dt          j        ¦   «         |z
  dz  |¦  «         t                                d|¦  «         t%          d|› d�¦  «         d}g }t          j        ¦   «         }tG          |¦  «        5 }tI          j%        |dtH          j&        d¬¦  «        5 }tO          |d¦  «        D �]¾\  }\  }} 	 |j	        dk    rítQ          j)        dd	t3          |j        ¦  «        ¬¦  «        5 }!t          |!j*        ¦  «        }"ddd¦  «         n# 1 swxY w Y   tW          ||"¦  «        rW| ,                    |"t3          | ¦  «        ¬¦  «         ||" -                    ¦   «         j.        z  }|" /                    d¬¦  «         nq|" /                    d¬¦  «         |                     d| › d�¦  «         �Œ| ,                    |t3          | ¦  «        ¬¦  «         || -                    ¦   «         j.        z  }n@# t`          t<          t:          f$ r&}#|                     d| › d |#› �¦  «         Y d}#~#�Œ{d}#~#ww xY w|d!z  dk    r2t%          d|› d"|› d�¦  «         t                                d#||¦  «         �ŒÀ|D ]x\  }}	 | ,                    ||¬¦  «         || -                    ¦   «         j.        z  }Œ:# t`          t<          t:          f$ r%}#|                     d|› d |#› �¦  «         Y d}#~#Œqd}#~#ww xY w	 ddd¦  «         n# 1 swxY w Y   ddd¦  «         n# 1 swxY w Y   t          j        ¦   «         |z
  }$| -                    ¦   «         j.        }%t                                d$|$dz  |tE          |¦  «        |%¦  «         t%          ¦   «          |rt%          d%|› �¦  «         nt%          d&|› �¦  «         t%          d'|› �¦  «         t%          d(tc          |¦  «        › �¦  «         t%          d)tc          |%¦  «        › �¦  «         t%          d*|$d+›d,�¦  «         |r/t%          d-tE          |¦  «        › d.t'          ¦   «         › d�¦  «         |rLt%          d/tE          |¦  «        › d0�¦  «         te          |¦  «        dd1…         D ]}&t%          d2|&› �¦  «         Œ|r4t%          d3¦  «         te          |¦  «        D ]}'t%          d2|'› d"�¦  «         Œ|rrt%          d4tE          |¦  «        › d5�¦  «         |dd1…         D ]}(t%          |(¦  «         ŒtE          |¦  «        d1k    r#t%          d6tE          |¦  «        d1z
  › d7�¦  «         |st%          d8|j*        › �¦  «         dS dS )9z@Write a full backup while the cross-process backup slot is held.ú%Y-%m-%d-%H%M%Szhermes-backup-ú.zipTr6   z backup phase=scan status=startedz	Scanning z ...Fr‰   r_   Nc                 ó6   •— g | ]}|t           vs|d k    ¯‰°|‘ŒS )r   rŒ   )r�   r�   Úis_roots     €r/   r‘   z&_run_backup_locked.<locals>.<listcomp>z  s=   ø€ ð 
ð 
ð 
ØØ�Ð&Ð&¨1°Ò+>Ð+>ÀwÐ+>ð Ø+>Ð+>Ð+>r.   z/backup phase=scan status=empty duration_ms=%.1féè  zNo files to back up.z;backup phase=scan status=complete duration_ms=%.1f files=%dz,backup phase=archive status=started files=%dzBacking up ú
 files ...r   Úwé   ©Úcompresslevelr;   ú.db©ÚsuffixÚdeleteÚdir©Úarcnamerb   ú  z: SQLite safe copy failedú: éô  ú/z:backup phase=archive status=progress completed=%d total=%dzQbackup phase=archive status=complete duration_ms=%.1f files=%d errors=%d bytes=%dzBackup incomplete: zBackup complete: z  Files:       z  Original:    z  Compressed:  z  Time:        ú.1fÚsz
  Included z( memory-provider file(s) stored outside z
  Skipped zD memory-provider path(s) outside your home directory (not portable):é
   ú    z
  Excluded directories:ú
  Warnings (ú files skipped):ú
  ... and ú morez
Restore with: hermes import )3Úoutputr   rw   ry   r”   r   ÚnowÚstrftimeÚhomer  ÚlowerÚwith_suffixr<   r=   r?   r@   rt   Úinforð   r   rv   rB   r•   Úrelative_tor{   r²   r©   r|   r†   rz   rM   r    Ú_EXTERNAL_PREFIXÚas_posixrÙ   rl   ÚzipfileÚZipFileÚZIP_DEFLATEDÚ	enumerateÚtempfileÚNamedTemporaryFilerC   r·   rG   rE   rF   rh   rN   Ú_format_sizeÚsorted)*rõ   rö   r§   ÚstampÚscan_startedÚfiles_to_addÚskipped_dirsrš   r›   rœ   r�   Úrel_dirÚorig_dirnamesÚremovedrž   rŸ   ÚrelÚhome_dirÚexternal_to_addÚskipped_externalr‡   Úbase_resolvedÚrel_to_homer	  Ú
file_countÚtotal_bytesrç   Út0Úarchive_pathÚzfÚir¦   r¡   ÚtmpÚtmp_dbr€   ÚelapsedÚzip_sizer„   r�   Úerü   s*                                            @r/   ró   ró   U  s  ø€ ð „{ð >Ý˜œÑ$Ô$×/Ò/Ñ1Ô1×9Ò9Ñ;Ô;ˆà�?Š?ÑÔð 	?Ý”L‘N”N×+Ò+Ð,=Ñ>Ô>ˆEØÐ">°5Ð">Ð">Ð">Ñ>ˆHøå”‘”×'Ò'Ð(9Ñ:Ô:ˆÝ”9‘;”;Ð!=°%Ð!=Ð!=Ð!=Ñ=ˆð „×ÒÑÔ &Ò(Ð(Ø×'Ò'¨¬¸&Ñ(@ÑAÔAˆð „O×Ò $°ÐÑ6Ô6Ð6õ ”>Ñ#Ô#€LÝ
‡K‚KÐ2Ñ3Ô3Ð3Ý	Ð
1Õ)Ñ+Ô+Ð
1Ð
1Ð
1Ñ2Ô2Ð2Ø,.€LÝ‘5”5€Lå(*¬°ÈÐ(OÑ(OÔ(Oð .ð .Ñ$ˆ�˜9Ý�'‰]Œ]ˆØ—.’. Ñ-Ô-ˆð
 �T #™YœYÒ&ˆØ    œˆð
ð 
ð 
ð 
Øð
ñ 
ô 
ˆ���‰õ ˜=Ñ)Ô)­C°©M¬MÑ9ð 	5ð 	5ˆGØ×Ò�S ¨7Ñ!2Ñ3Ô3Ñ4Ô4Ð4Ð4àð 	.ð 	.ˆEØ˜‘JˆEØ×#Ò# KÑ0Ô0ˆCå'¨¨s°HÑ=Ô=ð Øà×Ò ¨ Ñ-Ô-Ð-Ð-ð	.õ Œy‰{Œ{×"Ò"Ñ$Ô$€HØ.0€OØ"$ÐÝ7Ñ9Ô9ð 5ð 5ˆð	Ø ŸLšL™NœNˆMØ×%Ò% hÑ/Ô/Ð/Ð/øÝ�GÐ$ð 	ð 	ð 	Ø×#Ò#¥C¨¡I¤IÑ.Ô.Ð.ØˆHð	øøøõ *¨$Ñ/Ô/ð 	5ð 	5ˆEðØ#Ÿmšm™oœo×9Ò9¸(ÑCÔC��øÝ¥Ð(ð ð ð Ø�ðøøøå&¨×)=Ò)=Ñ)?Ô)?Ñ?ˆGØ×"Ò" E¨7Ð#3Ñ4Ô4Ð4Ð4ð	5ð ð  ð Ý�ŠØ=ÝŒ^ÑÔ Ñ,°Ñ4ñ	
ô 	
ð 	
õ 	Ð$Ñ%Ô%Ð%Øˆõ �\Ñ"Ô"¥S¨Ñ%9Ô%9Ñ9€JÝ
‡K‚KØEÝ	ŒÑ	Ô	˜LÑ	(¨DÑ0Øñô ð õ
 ‡K‚KÐ>À
ÑKÔKÐKÝ	Ð
.˜
Ð
.Ð
.Ð
.Ñ/Ô/Ð/à€KØ€FÝ	ŒÑ	Ô	€Bå	˜XÑ	&Ô	&ð 0¨,½¼Ø�c�7Ô/¸qð9ñ 9ô 9ð 0à	Ý'0°¸qÑ'AÔ'Að "	ñ "	Ñ#ˆAÑ#�˜(ðà”? eÒ+Ð+õ
 "Ô4Ø$¨U½¸H¼OÑ8LÔ8Lðñ ô ð 0àÝ!% c¤h¡¤˜ð0ð 0ð 0ñ 0ô 0ð 0ð 0ð 0ð 0ð 0ð 0øøøð 0ð 0ð 0ð 0õ % X¨vÑ6Ô6ð !ØŸš µ°X±´˜Ñ?Ô?Ð?Ø# v§{¢{¡}¤}Ô'<Ñ<˜ØŸš°˜Ñ6Ô6Ð6Ð6àŸš°˜Ñ6Ô6Ð6ØŸšÐ&N¨8Ð&NÐ&NÐ&NÑOÔOÐOÙ à—H’H˜X­s°8©}¬}�HÑ=Ô=Ð=Ø 8§=¢=¡?¤?Ô#:Ñ:�KøøÝ#¥W­jÐ9ð ð ð Ø—’Ð4 8Ð4Ð4¨sÐ4Ð4Ñ5Ô5Ð5Ø���‘øøøøðøøøð
 �3‰w˜!Š|ˆ|ÝÐ5˜1Ð5Ð5˜zÐ5Ð5Ð5Ñ6Ô6Ð6Ý—’ØPØØñô ð ùð "1ð 	ð 	ÑˆH�gðØ—’˜¨7�Ñ3Ô3Ð3Ø˜xŸ}š}™œÔ6Ñ6��øÝ#¥W­jÐ9ð ð ð Ø—’Ð3 7Ð3Ð3¨cÐ3Ð3Ñ4Ô4Ð4Ø����øøøøðøøøð		ðU0ð 0ð 0ñ 0ô 0ð 0ð 0ð 0ð 0ð 0ð 0øøøð 0ð 0ð 0ð 0ð 0ð 0ð 0ñ 0ô 0ð 0ð 0ð 0ð 0ð 0ð 0øøøð 0ð 0ð 0ð 0õd ŒnÑÔ Ñ#€GØ�}Š}‰ŒÔ&€HÝ
‡K‚KØ[Ø�$‰ØÝˆF‰ŒØñô ð õ 
�G„G€GØð .ÝÐ. HÐ.Ð.Ñ/Ô/Ð/Ð/åÐ, (Ð,Ð,Ñ-Ô-Ð-Ý	Ð
(˜JÐ
(Ð
(Ñ)Ô)Ð)Ý	Ð
7�L¨Ñ5Ô5Ð
7Ð
7Ñ8Ô8Ð8Ý	Ð
4�L¨Ñ2Ô2Ð
4Ð
4Ñ5Ô5Ð5Ý	Ð
*˜GÐ
*Ð
*Ð
*Ð
*Ñ+Ô+Ð+àð 
Ýð7�C Ñ0Ô0ð 7ð 7Ý1Ñ3Ô3ð7ð 7ð 7ñ	
ô 	
ð 	
ð
 ð Ýð;�3Ð/Ñ0Ô0ð ;ð ;ð ;ñ	
ô 	
ð 	
õ Ð(Ñ)Ô)¨#¨2¨#Ô.ð 	ð 	ˆAÝ�*˜�*�*ÑÔÐÐàð ÝÐ)Ñ*Ô*Ð*Ý˜Ñ%Ô%ð 	ð 	ˆAÝ�+˜�+�+�+ÑÔÐÐàð 8ÝÐ<�s 6™{œ{Ð<Ð<Ð<Ñ=Ô=Ð=Ø˜˜˜”ð 	ð 	ˆAÝ�!‰HŒHˆHˆHÝˆv‰;Œ;˜ÒÐÝÐ6�s 6™{œ{¨RÑ/Ð6Ð6Ð6Ñ7Ô7Ð7àð @ÝÐ>¨x¬}Ð>Ð>Ñ?Ô?Ð?Ð?Ð?ð@ð @sá   Ê1)KË3LÌLÌ&'MÍM"Í!M"Ò#\Ò*\Ó4W>Ó9T	ÔW>ÔTÔW>Ô!TÔ"BW>Ö;\Ö=A W>×=\×>X;	ØX6	Ø0\Ø6X;	Ø;A\Ú3Z7Ú6\Ú7[3	Û[.	Û)\Û.[3	Û3\Û7\Ü\	Ü\Ü
\	Ü\Ü\Ü!\r9  c                 óÆ   — |                       ¦   «         }|sdS h d£}t          ¦   «         }|D ]/}t          |¦  «        j        }||v r|                     |¦  «         Œ0|sdS dS )zLCheck that a zip looks like a Hermes backup.

    Returns (ok, reason).
    )Fzzip archive is empty>   r$   r%   úconfig.yaml)FzZzip does not appear to be a Hermes backup (no config.yaml, .env, or state databases found))TrÎ   )Únamelistrv   r   rC   r{   )r9  ÚnamesÚmarkersÚfoundÚnÚbasenames         r/   Ú_validate_backup_ziprH  !  sˆ   € ð
 �KŠK‰MŒM€EØð -Ø,Ð,ð 2Ð1Ð1€GÝ‰EŒE€EØð  ð  ˆå˜‘7”7”<ˆØ�wÐÐØ�IŠI�hÑÔÐøàð 
ð
ð 
ð
 ˆ8r.   c                 óÚ   — d„ |                       ¦   «         D ¦   «         }|sdS d„ |D ¦   «         }d„ |D ¦   «         }t          |¦  «        dk    r|                     ¦   «         }|dv r|dz   S dS )zÉDetect if the zip has a common directory prefix wrapping all entries.

    Some tools zip as `.hermes/config.yaml` instead of `config.yaml`.
    Returns the prefix to strip (empty string if none).
    c                 ó<   — g | ]}|                      d ¦  «        °|‘ŒS ©r  ©r—   ©r�   rF  s     r/   r‘   z"_detect_prefix.<locals>.<listcomp>B  s'   € Ð=Ð=Ð=�1¨Q¯ZªZ¸©_¬_Ð=ˆQÐ=Ð=Ð=r.   rÎ   c                 ó6   — g | ]}t          |¦  «        j        ‘ŒS r-   )r   r£   rM  s     r/   r‘   z"_detect_prefix.<locals>.<listcomp>G  s    € Ð/Ð/Ð/ A•$�q‘'”'”-Ð/Ð/Ð/r.   c                 óD   — h | ]}t          |¦  «        d k    ¯|d         ’ŒS )r;   r   )rÙ   ©r�   r„   s     r/   ú	<setcomp>z!_detect_prefix.<locals>.<setcomp>J  s'   € Ð:Ð:Ð:˜A­s°1©v¬v¸ªz¨z�1�Q”4¨z¨z¨zr.   r;   >   ú.hermesÚhermesr  )rB  rÙ   Úpop)r9  rC  Ú
parts_listÚfirst_partsÚprefixs        r/   Ú_detect_prefixrX  <  s“   € ð >Ð=˜Ÿš™œÐ=Ñ=Ô=€EØð Øˆrð 0Ð/¨Ð/Ñ/Ô/€Jð ;Ð: Ð:Ñ:Ô:€KÝ
ˆ;ÑÔ˜1ÒÐØ—’Ñ"Ô"ˆàÐ*Ð*Ð*Ø˜C‘<Ðàˆ2r.   c           	      ó€  — t          | j        ¦  «                             ¦   «                              ¦   «         }|                     ¦   «         s&t          d|› �¦  «         t          j        d¦  «         t          j        |¦  «        s&t          d|› �¦  «         t          j        d¦  «         t          ¦   «         }t          j
        |d¦  «        5 }t          |¦  «        \  }}|s&t          d|› �¦  «         t          j        d¦  «         t          |¦  «        }d„ |                     ¦   «         D ¦   «         }t          |¦  «        }t          d|› d�¦  «         t          d	t          ¦   «         › �¦  «         |rt          d
|›d�¦  «         |dz                       ¦   «         }	|dz                       ¦   «         }
|	s|
rÑ| j        sÊt          ¦   «          t          d¦  «         t          d¦  «         t          ¦   «          	 t%          d¦  «                             ¦   «                              ¦   «         }n:# t*          t,          f$ r& t          d¦  «         t          j        d¦  «         Y nw xY w|dvrt          d¦  «         	 ddd¦  «         dS t          d|› d�¦  «         |                     dd¬¦  «         g }d}d}g }t          j        ¦   «                              ¦   «         }t3          j        ¦   «         }|D �]»}|                     t8          ¦  «        �r²|t          t8          ¦  «        d…         }|sŒ=||z  }	 |                     ¦   «                              |¦  «         n)# t<          $ r |                     d|› d�¦  «         Y Œ�w xY w	 |j                              dd¬¦  «         | !                    |¦  «        5 }tC          |d¦  «        5 }| "                    | #                    ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   ddd¦  «         n# 1 swxY w Y   |j$        dv s|j%        tL          v r'	 tO          j(        |d¦  «         n# tR          $ r Y nw xY w|dz  }|dz  }n9# tT          tR          f$ r%}|                     d|› d|› �¦  «         Y d}~nd}~ww xY w|d z  dk    rt          d|› d!|› d�¦  «         �ŒÐ|r-|                     |¦  «        r|t          |¦  «        d…         }n|}|s�Œt          |¦  «        j%        tV          v r|                     |¦  «         �Œ7||z  }	 |                     ¦   «                              |                     ¦   «         ¦  «         n*# t<          $ r |                     d|› d�¦  «         Y �Œ�w xY w	 |j                              dd¬¦  «         | !                    |¦  «        5 }tC          |d¦  «        5 }| "                    | #                    ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   ddd¦  «         n# 1 swxY w Y   |j%        tL          v rtO          j(        |d¦  «         |dz  }n9# tT          tR          f$ r%}|                     d|› d|› �¦  «         Y d}~nd}~ww xY w|d z  dk    rt          d|› d!|› d�¦  «         �Œ½t3          j        ¦   «         |z
  }t          ¦   «          t          d"|› d#|d$›d%�¦  «         t          d&t          ¦   «         › �¦  «         |r"t          d'|› d(t          ¦   «         › d)�¦  «         |rrt          d*t          |¦  «        › d+�¦  «         |dd,…         D ]}t          |¦  «         Œt          |¦  «        d,k    r#t          d-t          |¦  «        d,z
  › d.�¦  «         |r‚t          d/t          |¦  «        › d0�¦  «         tY          |¦  «        dd,…         D ]}t          d1|› �¦  «         Œt          |¦  «        d,k    r#t          d2t          |¦  «        d,z
  › d.�¦  «         |d3z  }g }| -                    ¦   «         �rÕ	 dd4l.m/}m0}m1}m2}  tY          | 3                    ¦   «         ¦  «        D ]«}!|! -                    ¦   «         sŒ|!j%        }"|!dz                       ¦   «         s|!dz                       ¦   «         sŒM ||"¦  «        }#|#r-t          d5|"› d6|#› �¦  «         |                     |"d7f¦  «         Œ‡ ||"¦  «        }$|                     |"|$duf¦  «         Œ¬|r©d8„ |D ¦   «         }%d9„ |D ¦   «         }&|%r%t          d:d; 4                    |%¦  «        › �¦  «         |&r%t          d<d; 4                    |&¦  «        › �¦  «          |¦   «         s9t          d= | ¦   «         › d>�¦  «         t          d?¦  «         t          d@¦  «         nO# tj          $ rB tm          | 3                    ¦   «         ¦  «        rt          dA¦  «         t          dB¦  «         Y nw xY wt          ¦   «          |dCz   -                    ¦   «         st          dD¦  «         t          dE¦  «         |r3dF„ |D ¦   «         }'t          dG¦  «         |'D ]}(t          dH|(› dI�¦  «         Œt          dJ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )Kz(Restore a Hermes backup from a zip file.zError: File not found: r;   zError: Not a valid zip file: rÖ   rî   c                 ó<   — g | ]}|                      d ¦  «        °|‘ŒS rK  rL  rM  s     r/   r‘   zrun_import.<locals>.<listcomp>j  s'   € ÐCÐCÐC˜°1·:²:¸c±?´?ÐC�1ÐCÐCÐCr.   zBackup contains z fileszTarget: zDetected archive prefix: z (will be stripped)rA  r$   z;Warning: Target directory already has Hermes configuration.z=Importing will overwrite existing files with backup contents.zContinue? [y/N] z	
Aborted.>   ÚyÚyeszAborted.Nz
Importing rþ   Tr6   r   r
  z: path traversal blockedÚwb>   ú.confú.jsonr$   i€  r  r  r  zImport complete: z files restored in r  r  z
  Target: z
  Restored z? memory-provider file(s) to their original location(s) outside r_   r  r  r  r  r  z
  Preserved z? runtime state file(s) (kept this machine's, not the backup's):r  z    ... and Úprofiles)Úcreate_wrapper_scriptÚcheck_alias_collisionÚ_is_wrapper_dir_in_pathÚ_get_wrapper_dirz  Skipped alias 'z': Fc                 ó   — g | ]	\  }}|¯|‘Œ
S r-   r-   ©r�   rF  rÔ   s      r/   r‘   zrun_import.<locals>.<listcomp>  s!   € ÐFÐFÐF¡U Q¨À2ÐF˜qÐFÐFÐFr.   c                 ó   — g | ]	\  }}|°|‘Œ
S r-   r-   rf  s      r/   r‘   zrun_import.<locals>.<listcomp>  s!   € ÐJÐJÐJ¡U Q¨ÀrÐJ˜qÐJÐJÐJr.   z
  Profile aliases restored: ú, z  Profile aliases skipped:  z	
  Note: z is not in your PATH.z3  Add to your shell config (~/.bashrc or ~/.zshrc):z(    export PATH="$HOME/.local/bin:$PATH"z6
  Profiles detected but aliases could not be created.z5  Run: hermes profile list  (after installing hermes)r   z?Note: The hermes-agent codebase was not included in the backup.z0  If this is a fresh install, run: hermes updatec                 ó   — g | ]\  }}|‘ŒS r-   r-   )r�   rF  Ú_s      r/   r‘   zrun_import.<locals>.<listcomp>(  s   € Ð;Ð;Ð;¡  A˜1Ð;Ð;Ð;r.   z,
To re-enable gateway services for profiles:z  hermes -p z gateway installz2Done. Your Hermes configuration has been restored.)7r   r   rw   ry   r’   rð   rñ   rò   Ú
is_zipfiler   r!  rH  rX  rB  rÙ   r   rx   rº   ÚinputÚstripr  ÚEOFErrorÚKeyboardInterruptr=   r  r?   r@   rÅ   r  r  rz   r|   r<   r>   rG   Úreadr  rC   Ú_SECRET_FILE_NAMESrB   ÚchmodrM   rN   Ú_IMPORT_SKIP_NAMESr'  r”   Úhermes_cli.profilesra  rb  rc  rd  Úiterdirrà   ÚImportErrorÚany))rõ   Úzip_pathrö   r9  rÔ   ÚreasonrW  Úmembersr5  Ú
has_configÚhas_envÚanswerrç   ÚrestoredÚrestored_externalÚskipped_runtimer0  r7  ÚmemberÚext_relÚtargetrª   r«   r€   r/  r=  r?  Úprofiles_dirÚrestored_profilesra  rb  rc  rd  ÚentryÚprofile_nameÚ	collisionÚwrapperÚcreatedÚskippedÚgw_profilesÚpnames)                                            r/   Ú
run_importrŽ  T  s#  € å�D”LÑ!Ô!×,Ò,Ñ.Ô.×6Ò6Ñ8Ô8€Hà×ÒÑÔð ÝÐ2¨Ð2Ð2Ñ3Ô3Ð3ÝŒ�‰ŒˆåÔ˜hÑ'Ô'ð ÝÐ8¨hÐ8Ð8Ñ9Ô9Ð9ÝŒ�‰Œˆå)Ñ+Ô+€Kå	Œ˜ 3Ñ	'Ô	'ð KD¨2å)¨"Ñ-Ô-‰
ˆˆFØð 	ÝÐ$˜FÐ$Ð$Ñ%Ô%Ð%ÝŒH�Q‰KŒKˆKå Ñ#Ô#ˆØCÐC˜bŸkšk™mœmÐCÑCÔCˆÝ˜‘\”\ˆ
åÐ3 Ð3Ð3Ð3Ñ4Ô4Ð4ÝÐ0Õ,Ñ.Ô.Ð0Ð0Ñ1Ô1Ð1àð 	MÝÐK¨fÐKÐKÐKÑLÔLÐLð " MÑ1×9Ò9Ñ;Ô;ˆ
Ø Ñ'×/Ò/Ñ1Ô1ˆàð 	˜'ð 	¨4¬:ð 	Ý‰GŒGˆGÝÐOÑPÔPÐPÝÐQÑRÔRÐRÝ‰GŒGˆGðÝÐ1Ñ2Ô2×8Ò8Ñ:Ô:×@Ò@ÑBÔB��øÝÕ/Ð0ð ð ð Ý�lÑ#Ô#Ð#Ý”˜‘”���ðøøøð ˜\Ð)Ð)Ý�jÑ!Ô!Ð!ØðCKDð KDð KDñ KDô KDð KDð KDð KDõH 	Ð3˜ZÐ3Ð3Ð3Ñ4Ô4Ð4Ø×Ò $°ÐÑ6Ô6Ð6àˆØˆØÐØ%'ˆÝ”9‘;”;×&Ò&Ñ(Ô(ˆÝŒ^ÑÔˆàð H	>ñ H	>ˆFð × Ò Õ!1Ñ2Ô2ñ Ø ¥Õ%5Ñ!6Ô!6Ð!7Ð!7Ô8�Øð ØØ! GÑ+�ðØ—N’NÑ$Ô$×0Ò0°Ñ:Ô:Ð:Ð:øÝ!ð ð ð Ø—M’MÐ"G vÐ"GÐ"GÐ"GÑHÔHÐHØ�Hðøøøð8Ø”M×'Ò'°¸tÐ'ÑDÔDÐDØŸš ™œð .¨Cµ°f¸dÑ1CÔ1Cð .ÀsØŸ	š	 #§(¢(¡*¤*Ñ-Ô-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð ”}Ð(BÐBÐBÀfÄkÕUgÐFgÐFgð!ÝœH V¨UÑ3Ô3Ð3Ð3øÝ&ð !ð !ð !Ø ˜Dð!øøøà ‘M�HØ%¨Ñ*Ð%Ð%øÝ'­Ð1ð 8ð 8ð 8Ø—M’MÐ"6 vÐ"6Ð"6°Ð"6Ð"6Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7øøøøð8øøøà˜c‘> QÒ&Ð&ÝÐ@˜xÐ@Ð@¨*Ð@Ð@Ð@ÑAÔAÐAÙð ð ˜&×+Ò+¨FÑ3Ô3ð Ø�S ™[œ[˜\˜\Ô*��à�àð Ùõ �C‰yŒyŒ~Õ!3Ð3Ð3Ø×&Ò& sÑ+Ô+Ð+Ùà  3Ñ&ˆFðØ—’Ñ Ô ×,Ò,¨[×-@Ò-@Ñ-BÔ-BÑCÔCÐCÐCøÝð ð ð Ø—’Ð@ 3Ð@Ð@Ð@ÑAÔAÐAØ‘ðøøøð1Ø”×#Ò#¨D¸4Ð#Ñ@Ô@Ð@Ø—W’W˜V‘_”_ð *¨­T°&¸$Ñ-?Ô-?ð *À3Ø—I’I˜cŸhšh™jœjÑ)Ô)Ð)ð*ð *ð *ñ *ô *ð *ð *ð *ð *ð *ð *øøøð *ð *ð *ð *ð *ð *ð *ñ *ô *ð *ð *ð *ð *ð *ð *øøøð *ð *ð *ð *à”;Õ"4Ð4Ð4Ý”H˜V UÑ+Ô+Ð+Ø˜A‘��øÝ#¥WÐ-ð 1ð 1ð 1Ø—’Ð/ 3Ð/Ð/¨#Ð/Ð/Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøð ˜#‰~ Ò"Ð"ÝÐ<˜8Ð<Ð< jÐ<Ð<Ð<Ñ=Ô=Ð=ùå”.Ñ"Ô" RÑ'ˆõ 	‰ŒˆÝÐM (ÐMÐM¸wÐMÐMÐMÐMÑNÔNÐNÝÐ2Õ.Ñ0Ô0Ð2Ð2Ñ3Ô3Ð3àð 	ÝðOÐ 1ð Oð OÝ6IÑ6KÔ6KðOð Oð Oñô ð ð
 ð 	<ÝÐ@¥3 v¡;¤;Ð@Ð@Ð@ÑAÔAÐAØ˜C˜R˜C”[ð ð �Ý�a‘”��Ý�6‰{Œ{˜RÒÐÝÐ:¥3 v¡;¤;°Ñ#3Ð:Ð:Ð:Ñ;Ô;Ð;àð 	GÝðD¥ _Ñ!5Ô!5ð Dð Dð Dñô ð õ ˜oÑ.Ô.¨s°¨sÔ3ð $ð $�Ý�l˜S�l�lÑ#Ô#Ð#Ð#Ý�?Ñ#Ô# bÒ(Ð(ÝÐE¥S¨Ñ%9Ô%9¸BÑ%>ÐEÐEÐEÑFÔFÐFð # ZÑ/ˆØÐØ×ÒÑ Ô ñ $	Sð#Sðð ð ð ð ð ð ð ð ð ð ð õ $ L×$8Ò$8Ñ$:Ô$:Ñ;Ô;ð Vð V�EØ Ÿ<š<™>œ>ð !Ø Ø#(¤:�Là! MÑ1×9Ò9Ñ;Ô;ð !ÀUÈVÁ^×D[ÒD[ÑD]ÔD]ð !Ø Ø 5Ð 5°lÑ CÔ C�IØ ð VÝÐN°,ÐNÐNÀ9ÐNÐNÑOÔOÐOØ)×0Ò0°,ÀÐ1FÑGÔGÐGÐGà"7Ð"7¸Ñ"EÔ"E˜Ø)×0Ò0°,ÀÈtÐ@SÐ1TÑUÔUÐUÐUà$ð 
JØFÐFÐ.?ÐFÑFÔF�GØJÐJÐ.?ÐJÑJÔJ�GØð UÝÐS¸t¿yºyÈÑ?QÔ?QÐSÐSÑTÔTÐTØð SÝÐQ¸T¿YºYÀwÑ=OÔ=OÐQÐQÑRÔRÐRØ2Ð2Ñ4Ô4ð JÝÐTÐ+;Ð+;Ñ+=Ô+=ÐTÐTÐTÑUÔUÐUÝÐSÑTÔTÐTÝÐHÑIÔIÐIøøÝð Sð Sð Så�|×+Ò+Ñ-Ô-Ñ.Ô.ð SÝÐSÑTÔTÐTÝÐQÑRÔRÐRøøð	Søøøõ 	‰ŒˆØ˜nÑ,×4Ò4Ñ6Ô6ð 	FÝÐSÑTÔTÐTÝÐDÑEÔEÐEàð 	>Ø;Ð;Ð):Ð;Ñ;Ô;ˆKÝÐAÑBÔBÐBØ$ð >ð >�ÝÐ< UÐ<Ð<Ð<Ñ=Ô=Ð=Ð=åÐBÑCÔCÐCðWKDð KDð KDñ KDô KDð KDð KDð KDð KDð KDð KDð KDøøøð KDð KDð KDð KDð KDð KDs·  ÃD0j3È3H5È4j3È54I,É)j3É+I,É,j3ÊB.j3Ì?'M'Í&j3Í'#NÎ
j3ÎNÎj3Î1Q2ÏPÏ(P	Ï;PÐPÐPÐPÐPÐQ2ÐP"Ð"Q2Ð%P"Ð&Q2ÑQÑQ2Ñ
Q$Ñ!Q2Ñ#Q$Ñ$Q2Ñ1j3Ñ2R(ÒR#Òj3Ò#R(Ò(Bj3Ô99U3Õ2j3Õ3#VÖj3ÖVÖj3Ö1Y×X+× (X	ØX+ØXØX+ØXØX+ØYØ+X/Ø/YØ2X/Ø3+YÙj3ÙZÙ0ZÚj3ÚZÚF5j3áFgçj3çA	hèj3èhèB
j3ê3j7ê:j7)r%   rA  r$   r&   úcron/jobs.jsonzcron/executions.dbr#   zchannel_directory.jsonzchannel_aliases.jsonr"   z#gateway/discord_message_recovery.dbzprojects.dbzresponse_store.dbzmemory_store.dbzverification_evidence.dbz	kanban.dbzkanban/boardsÚpairingzplatforms/pairingzfeishu_comment_pairing.jsonzstate-snapshotsé   c                 ó6   — | pt          ¦   «         }|t          z  S ©N)r   Ú_QUICK_SNAPSHOTS_DIR©r3   r  s     r/   Ú_quick_snapshot_rootr–  `  s   € ØÐ+�/Ñ+Ô+€DØÕ&Ñ&Ð&r.   ÚlabelÚkeepÚmax_file_sizec                 óš   — |pt          ¦   «         }t          |¦  «        5  t          | |||¬¦  «        cddd¦  «         S # 1 swxY w Y   dS )zFCreate one atomic quick snapshot while holding the shared backup slot.)r—  r3   r˜  r™  N)r   r\   Ú_create_quick_snapshot_locked)r—  r3   r˜  r™  r  s        r/   Úcreate_quick_snapshotrœ  e  s¤   € ð Ð+�/Ñ+Ô+€DÝ	 Ñ	%Ô	%ð 
ð 
Ý,ØØØØ'ð	
ñ 
ô 
ð
ð 
ð 
ð 
ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
øøøð 
ð 
ð 
ð 
ð 
ð 
s    A Á AÁAc           	      ó  ‡— |pt          ¦   «         }t          |¦  «        }dt          dt          dt          fˆfd„}t          j        t          j        ¦  «         	                    d¦  «        }| r|› d| › �n|}|}	d}
||	z   
                    ¦   «         r#|› d|
› �}	|
dz  }
||	z   
                    ¦   «         °#||	z  }|d	|	› d	t          j        ¦   «         › d
�z  }t          j        |d¬¦  «         |                     dd¬¦  «         t                                d|	¦  «         i }g }g }t$          D �],}||z  }| 
                    ¦   «         sŒ|                     ¦   «         �r¦|                     d¦  «        D �]�}|                     ¦   «         sŒ|                     |¦  «                             ¦   «         }dd|› d�v sdd|› d�v rŒP |||¦  «        r!|j        dk    r|                     |¦  «         Œ}||z  }|j                             dd¬¦  «         	 |j        dk    rwt7          ||¦  «        sf|                     |¦  «         t9          d|› d�¦  «         t;          |¦  «        r-t9          d|› d|                     ¦   «         j        › d�¦  «         �Œ nt          j         ||¦  «         |                     ¦   «         j        ||<   �ŒT# tB          tD          f$ r'}t            #                    d||¦  «         Y d}~�Œ‡d}~ww xY w�ŒØ|                     ¦   «         s�Œî |||¦  «        r"|j        dk    r|                     |¦  «         �Œ||z  }|j                             dd¬¦  «         	 |j        dk    rwt7          ||¦  «        sf|                     |¦  «         t9          d|› d�¦  «         t;          |¦  «        r-t9          d|› d|                     ¦   «         j        › d�¦  «         �Œ¿nt          j         ||¦  «         |                     ¦   «         j        ||<   �Œó# tB          tD          f$ r'}t            #                    d||¦  «         Y d}~�Œ&d}~ww xY w|rft9          dd $                    |¦  «        z   ¦  «         t9          d|› d �¦  «         t            %                    d!d $                    |¦  «        ¦  «         |s@t          j        |d¬¦  «         |r&t9          d"d $                    |¦  «        › d#�¦  «         dS |	|| tM          |¦  «        tO          | (                    ¦   «         ¦  «        |||d$œ}tS          |d%z  d&d'¬(¦  «        5 }tU          j+        ||d¬)¦  «         ddd¦  «         n# 1 swxY w Y   t          j,        ||¦  «         |p|}|st[          ||€t\          n|¬*¦  «         n‹|rSt9          d+d $                    |¦  «        z   ¦  «         t            #                    d,d $                    |¦  «        ¦  «         t            #                    d-tM          |¦  «        tM          |¦  «        ¦  «         t                                d.|	tM          |¦  «        tO          | (                    ¦   «         ¦  «        ¦  «         |	S )/u2  Create a quick state snapshot of critical files.

    Copies STATE_FILES to a timestamped directory under state-snapshots/.
    Auto-prunes old snapshots beyond the keep limit.

    Args:
        max_file_size: When set, individual files larger than this many bytes
            are skipped (with a printed warning) instead of copied. Used by
            the pre-update safety snapshot so a multi-GB ``state.db`` can
            never stall ``hermes update`` or silently eat disk â€” the small
            pairing/cron/config files the snapshot exists to protect are
            always captured. ``None`` (default) copies everything, which
            preserves manual ``/snapshot`` and ``hermes backup --quick``
            behavior.

    Returns:
        Snapshot ID (timestamp-based), or None if no files found.
    r»   Úrel_namerm   c           
      ó  •— ‰€dS 	 |                       ¦   «         j        }n# t          $ r Y dS w xY w|‰k    rdS t          d|› dt	          |¦  «        › dt	          ‰¦  «        › d�¦  «         t
                               d||‰¦  «         dS )	z<True (and warn) when ``path`` exceeds the max_file_size cap.NFu     âš  Snapshot: skipping z (z	 exceeds z limit)z9Quick snapshot skipped %s: %d bytes exceeds %d byte limitT)rE   rF   rM   rð   r&  rt   ru   )r»   rž  rÇ   r™  s      €r/   Ú
_too_largez1_create_quick_snapshot_locked.<locals>._too_large‘  sÜ   ø€ àÐ Ø�5ð	Ø—9’9‘;”;Ô&ˆDˆDøÝð 	ð 	ð 	Ø�5�5ð	øøøà�=Ò Ð Ø�5ÝðR¨ð Rð RÝ˜TÑ"Ô"ðRð RÝ-9¸-Ñ-HÔ-HðRð Rð Rñ	
ô 	
ð 	
õ 	�ŠØGØØØñ		
ô 	
ð 	
ð ˆts   ‡! ¡
/®/z%Y%m%d-%H%M%Sr`   rï   r;   r_   ra   T)Úignore_errorsFr6   z.quick snapshot phase=copy status=started id=%sÚ*z/workspaces/r  z/attachments/r  u,     âš  Snapshot: SQLite safe copy FAILED for u$    â€” file may be locked or corruptedu     âš  Snapshot: z! looks ZEROED (no SQLite header; z bytes of NULs?)zCould not snapshot %s: %sNz bytes)u/     âš  CRITICAL: could not snapshot DB file(s): rh  u0     âš  If sessions disappear after update, check z and run: hermes snapshot listz/Quick snapshot failed to capture DB file(s): %su7     âš  Snapshot aborted: no files captured (failed DBs: rÑ   )ÚidÚ	timestampr—  r5  Ú
total_sizer™   Ú
failed_dbsÚoversized_skippedúmanifest.jsonrÿ   úutf-8©Úencoding)Úindent©r˜  u<     âš  Skipping snapshot prune: DB file(s) skipped for size: z/Quick snapshot skipped oversized DB file(s): %su…   Skipping snapshot prune because %d DB(s) failed to capture and/or %d were oversized â€” preserving older snapshots as recovery sourcezAquick snapshot phase=copy status=complete id=%s files=%d bytes=%d)/r   r–  r   r²   Úboolr   r  r   Úutcr  rx   rB   re   ÚshutilÚrmtreer=   rt   r  Ú_QUICK_STATE_FILESr”   Úrglobr’   r  r  r  r|   r<   r·   rð   rÉ   rE   rF   Úcopy2rM   rN   ru   rà   ÚerrorrÙ   ÚsumÚvaluesr>   ÚjsonÚdumpri   Ú_prune_quick_snapshotsÚ_QUICK_DEFAULT_KEEP)r—  r3   r˜  r™  r  Úrootr   ÚtsÚbase_snap_idÚsnap_idr  Úsnap_dirÚstaging_dirÚmanifestr¦  r§  r/  rª   ÚsubÚsub_relr«   r€   ÚmetaÚfÚ
incompletes      `                     r/   r›  r›  v  s;  ø€ ð0 Ð+�/Ñ+Ô+€DÝ Ñ%Ô%€Dð�ð ­ð µð ð ð ð ð ð õ, 
Œ•h”lÑ	#Ô	#×	,Ò	,¨_Ñ	=Ô	=€BØ&+Ð3�b�?�?˜5�?�?�?°€LØ€GØ€FØ�'‰>×
!Ò
!Ñ
#Ô
#ð Ø!Ð,Ð, FÐ,Ð,ˆØ�!‰ˆð �'‰>×
!Ò
!Ñ
#Ô
#ð ð �g‰~€HØÐ<˜WÐ<Ð<¥r¤y¡{¤{Ð<Ð<Ð<Ñ<€KÝ
„M�+¨TÐ2Ñ2Ô2Ð2Ø×Ò˜d¨UÐÑ3Ô3Ð3Ý
‡K‚KÐ@À'ÑJÔJÐJà!€HØ€Jð
 $&Ðå!ð MBñ MBˆØ�S‰jˆØ�zŠz‰|Œ|ð 	Øà�:Š:‰<Œ<ñ )	ð —y’y ‘~”~ð $Nñ $N�Ø—{’{‘}”}ð ØØŸ/š/¨$Ñ/Ô/×8Ò8Ñ:Ô:�ð " ^¨ ^ ^ ^Ð3Ð3°È.ÈgÈ.È.È.Ð7XÐ7XØØ�:˜c 7Ñ+Ô+ð Ø”z UÒ*Ð*Ø)×0Ò0°Ñ9Ô9Ð9ØØ! GÑ+�Ø”
× Ò ¨¸Ð Ñ=Ô=Ð=ðNð ”z UÒ*Ð*Ý,¨S°#Ñ6Ô6ð %Ø&×-Ò-¨gÑ6Ô6Ð6Ý!ð!GÈwð !Gð !Gð !Gñô ð õ  5°SÑ9Ô9ð "Ý %ð%_°wð %_ð %_Ø:=¿(º(¹*¼*Ô:Lð%_ð %_ð %_ñ!"ô !"ð !"ñ %ð%õ œ S¨#Ñ.Ô.Ð.Ø(+¯ª©
¬
Ô(:�H˜WÑ%Ñ%øÝ¥Ð1ð Nð Nð NÝ—N’NÐ#>ÀÈÑMÔMÐMÐMÐMÐMÐMÑMøøøøðNøøøáà�{Š{‰}Œ}ð 	Ùàˆ:�c˜3ÑÔð 	ØŒz˜UÒ"Ð"Ø!×(Ò(¨Ñ-Ô-Ð-Ùà˜CÑˆØŒ
×Ò °ÐÑ5Ô5Ð5ð	BØŒz˜UÒ"Ð"Ý$ S¨#Ñ.Ô.ð Ø×%Ò% cÑ*Ô*Ð*Ýð?Àsð ?ð ?ð ?ñô ð õ -¨SÑ1Ô1ð ÝðN¨sð Nð NØ25·(²(±*´*Ô2DðNð Nð Nñô ð ñ ðõ ”˜S #Ñ&Ô&Ð&ØŸHšH™JœJÔ.ˆH�S‰M‰MøÝ�Ð)ð 	Bð 	Bð 	BÝ�NŠNÐ6¸¸SÑAÔAÐAÐAÐAÐAÐAÑAøøøøð	Bøøøð ð 
õ 	Ø=Ø�iŠi˜
Ñ#Ô#ñ$ñ	
ô 	
ð 	
õ 	ð4Øð4ð 4ð 4ñ	
ô 	
ð 	
õ 	�ŠØ=Ø�IŠI�jÑ!Ô!ñ	
ô 	
ð 	
ð
 ð ÝŒ�k°Ð6Ñ6Ô6Ð6Øð 	åð9Ø $§	¢	¨*Ñ 5Ô 5ð9ð 9ð 9ñô ð ð ˆtð ØØÝ˜(‘m”mÝ˜(Ÿ/š/Ñ+Ô+Ñ,Ô,ØØ Ø.ð	ð 	€Dõ 
ˆk˜OÑ+¨S¸7Ð	CÑ	CÔ	Cð %ÀqÝŒ	�$˜ !Ð$Ñ$Ô$Ð$ð%ð %ð %ñ %ô %ð %ð %ð %ð %ð %ð %øøøð %ð %ð %ð %õ „Jˆ{˜HÑ%Ô%Ð%ð Ð0Ð0€JØð 
Ý˜tÀÀÕ*=Ð*=ÐSWÐXÑXÔXÐXÐXàð 	ÝØNØ—)’)Ð-Ñ.Ô.ñ/ñô ð õ �NŠNØAØ—	’	Ð+Ñ,Ô,ñô ð õ 	�Šðõ �
‰OŒO�SÐ!2Ñ3Ô3ñ		
ô 	
ð 	
õ ‡K‚KØKØÝˆH‰ŒÝˆH�OŠOÑÔÑÔñ	ô ð ð €NsP   ÈA?KÊ2KËL ËK;Ë;L Í+A?P Ï,2P Ð QÐ1QÑQÕU5Õ5U9Õ<U9Úlimitc                 óª  — t          |¦  «        }|                     ¦   «         sg S g }t          |                     ¦   «         d¬¦  «        D �]}|                     ¦   «         r4|j                             d¦  «        s|j                             d¦  «        rŒL|dz  }|                     ¦   «         rŒ	 t          |d¬¦  «        5 }| 	                    t          j        |¦  «        ¦  «         ddd¦  «         n# 1 swxY w Y   n:# t          j        t          f$ r! | 	                    |j        d	d	d
œ¦  «         Y nw xY wt          |¦  «        | k    r n�Œ|S )z7List existing quick state snapshots, most recent first.T)Úreverser_   ra   r¨  r©  rª  Nr   )r£  r5  r¥  )r–  rx   r'  ru  r”   rC   rÅ   r—   r>   r|   r¸  ÚloadÚJSONDecodeErrorrM   rÙ   )rÈ  r3   r¼  Úresultsr�   Úmanifest_pathrÆ  s          r/   Úlist_quick_snapshotsrÏ  Y  s   € õ
   Ñ,Ô,€DØ�;Š;‰=Œ=ð Øˆ	à€GÝ�D—L’L‘N”N¨DÐ1Ñ1Ô1ð ñ ˆØ�xŠx‰zŒzð 	˜QœV×.Ò.¨sÑ3Ô3ð 	°q´v·²ÀzÑ7RÔ7Rð 	ØØ˜OÑ+ˆØ×ÒÑ!Ô!ð 	QðQÝ˜-°'Ð:Ñ:Ô:ð 1¸aØ—N’N¥4¤9¨Q¡<¤<Ñ0Ô0Ð0ð1ð 1ð 1ñ 1ô 1ð 1ð 1ð 1ð 1ð 1ð 1øøøð 1ð 1ð 1ð 1øøåÔ(­'Ð2ð Qð Qð QØ—’ a¤f¸AÈQÐOÐOÑPÔPÐPÐPÐPðQøøøåˆw‰<Œ<˜5Ò Ð ØˆEñ !ð €Ns6   Â1DÃ(C6Ã*DÃ6C:	Ã:DÃ=C:	Ã>DÄ4D9Ä8D9Úsnapshot_idc                 ót  — |pt          ¦   «         }t          |¦  «        }| rd| v sd| v s| dv rt                               d| ¦  «         dS || z  }	 |                     ¦   «                              |                     ¦   «         ¦  «         n,# t          $ r t                               d| ¦  «         Y dS w xY w|                     ¦   «         sdS |dz  }|                     ¦   «         sdS t          |d¬	¦  «        5 }t          j        |¦  «        }d
d
d
¦  «         n# 1 swxY w Y   d}|                     di ¦  «        D �]Ù}	||	z  }
	 |
                     ¦   «                              |                     ¦   «         ¦  «         n+# t          $ r t                               d|	¦  «         Y Œjw xY w||	z  }	 |                     ¦   «                              |                     ¦   «         ¦  «         n+# t          $ r t                               d|	¦  «         Y ŒÕw xY w|
                     ¦   «         sŒî|j                             dd¬¦  «         	 |j        dk    rn|j        d|j        › d�z  }t#          j        |
|¦  «         |                     d¬¦  «         t#          j        t+          |¦  «        t+          |¦  «        ¦  «         nt#          j        |
|¦  «         |dz  }�Œ # t,          t.          f$ r'}t                               d|	|¦  «         Y d
}~�ŒÓd
}~ww xY wt                               d|| ¦  «         |dk    S )z�Restore state from a quick snapshot.

    Overwrites current state files with the snapshot's copies.
    Returns True if at least one file was restored.
    r  ú\)r_   z..zInvalid snapshot_id: %sFz*Snapshot path traversal blocked for id: %sr¨  r©  rª  Nr   r™   z#Manifest path traversal blocked: %sTr6   r  r_   z.snap_restorerb   r;   zFailed to restore %s: %sz"Restored %d files from snapshot %s)r   r–  rt   rµ  ry   r  rz   r”   rx   r>   r¸  rË  rê   r<   r=   r  rC   r°  r´  rh   Úmover²   rM   rN   r  )rÐ  r3   r  r¼  rÀ  rÎ  rÆ  rÅ  r~  r/  rª   r«   r;  r€   s                 r/   Úrestore_quick_snapshotrÔ  s  s®  € ð Ð+�/Ñ+Ô+€DÝ Ñ%Ô%€Dð ð ˜# Ð,Ð,°¸Ð0CÐ0CÀ{ÐVaÐGaÐGaÝ�ŠÐ.°Ñ<Ô<Ð<Øˆuà�kÑ!€HðØ×ÒÑÔ×&Ò& t§|¢|¡~¤~Ñ6Ô6Ð6Ð6øÝð ð ð Ý�ŠÐAÀ;ÑOÔOÐOØˆuˆuðøøøð �?Š?ÑÔð Øˆuà˜Ñ.€MØ×ÒÑ!Ô!ð Øˆuå	ˆm gÐ	.Ñ	.Ô	.ð °!ÝŒy˜‰|Œ|ˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð €HØ�xŠx˜ Ñ$Ô$ð  ?ñ  ?ˆà˜‰nˆð	Ø�KŠK‰MŒM×%Ò% h×&6Ò&6Ñ&8Ô&8Ñ9Ô9Ð9Ð9øÝð 	ð 	ð 	Ý�LŠLÐ>ÀÑDÔDÐDØˆHð	øøøð �S‰jˆð	Ø�KŠK‰MŒM×%Ò% d§l¢l¡n¤nÑ5Ô5Ð5Ð5øÝð 	ð 	ð 	Ý�LŠLÐ>ÀÑDÔDÐDØˆHð	øøøð �zŠz‰|Œ|ð 	ØàŒ
×Ò °ÐÑ5Ô5Ð5ð	?ØŒz˜UÒ"Ð"à”jÐ#> s¤xÐ#>Ð#>Ð#>Ñ>�Ý”˜S #Ñ&Ô&Ð&Ø—
’
 d�
Ñ+Ô+Ð+Ý”�C ™HœH¥c¨#¡h¤hÑ/Ô/Ð/Ð/å”˜S #Ñ&Ô&Ð&Ø˜‰MˆH‰HøÝ�Ð)ð 	?ð 	?ð 	?Ý�LŠLÐ3°S¸#Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ñ>øøøøð	?øøøõ ‡K‚KÐ4°hÀÑLÔLÐLØ�aŠ<Ðsa   Á9B Â%B4Â3B4Ã9DÄDÄ!DÅ9F Æ %F(Æ'F(Æ19G+Ç+%HÈHÉBKËLË.LÌLr�  c                 óÖ  — |                       ¦   «         sdS 	 t          | dd¬¦  «        5 }t          j        |¦  «        }ddd¦  «         n# 1 swxY w Y   n# t          t          j        f$ r Y dS w xY wt          |t          ¦  «        r<|                     dg ¦  «        }t          |t          ¦  «        rt          |¦  «        ndS t          |t          ¦  «        rt          |¦  «        S dS )uÿ  Return the number of cron jobs stored in ``path``.

    The canonical on-disk shape is ``{"jobs": [...]}`` (see ``cron/jobs.py``).
    A legacy bare-list shape (``[...]``) is also honoured.

    Returns:
        The job count for any *valid, readable* JSON document, or ``None`` if
        the file is missing or cannot be parsed. ``None`` means "unknown" â€”
        callers must not treat it as "zero jobs", because acting on an
        unreadable file could mask a real corruption the user needs to see.
    NrÖ   z	utf-8-sigrª  Újobs)r’   r>   r¸  rË  rM   rÌ  Ú
isinstanceÚdictrê   ÚlistrÙ   )r»   rÆ  ÚdatarÖ  s       r/   Ú_count_cron_jobsrÛ  Ä  s&  € ð �<Š<‰>Œ>ð Øˆtðõ
 �$˜ kÐ2Ñ2Ô2ð 	 °aÝ”9˜Q‘<”<ˆDð	 ð 	 ð 	 ñ 	 ô 	 ð 	 ð 	 ð 	 ð 	 ð 	 ð 	 øøøð 	 ð 	 ð 	 ð 	 øøå•TÔ)Ð*ð ð ð Øˆtˆtðøøøå�$�ÑÔð =Ø�xŠx˜ Ñ#Ô#ˆÝ& t­TÑ2Ô2Ð<�s�4‰yŒyˆy¸Ð<Ý�$�ÑÔð Ý�4‰yŒyÐØˆ4s3   ˜A ªA¿A ÁAÁA ÁAÁA ÁA1Á0A1c                 óð  — | sdS |pt          ¦   «         }|t          z  }t          |¦  «        }|€dS t          |¦  «        | z  t          z  }t          |¦  «        }|sdS ||k    rdS 	 |j                             dd¬¦  «         t          j        ||¦  «         n:# t          t          f$ r&}t                               d|¦  «         Y d}~dS d}~ww xY wt                               d|| ||¦  «         d|| dœS )uI  Safety net for silent cron-job loss across ``hermes update``.

    Config-version migrations have been observed to leave ``cron/jobs.json``
    valid-but-empty after an update, silently dropping every scheduled job
    (issue #34600). The desktop scheduler can also overwrite the file with its
    own small set of internally-tracked crons, causing partial loss (issue
    #52144).

    This compares the *current* job count against the pre-update snapshot. If
    the live file now has **fewer** jobs than the snapshot, the snapshot copy
    of ``cron/jobs.json`` is restored in place.

    The check is deliberately conservative â€” it only ever restores when there
    is unambiguous evidence of loss (snapshot had more jobs than live file),
    so a user who genuinely deleted jobs during/after the update is never
    second-guessed, and an unreadable live file (count ``None``) is left
    untouched so real corruption still surfaces.

    Args:
        snapshot_id: The pre-update quick-snapshot id (from
            :func:`create_quick_snapshot`).
        hermes_home: Override for the Hermes home directory (tests).

    Returns:
        ``None`` when no action was taken (the common, healthy path). On a
        successful restore, a dict ``{"restored": True, "job_count": N,
        "snapshot_id": ...}`` so the caller can warn the user.
    NTr6   z@Cron jobs were emptied during update but auto-restore failed: %su‚   Restored %d cron job(s) from pre-update snapshot %s (live file had %d job(s), snapshot had %d â€” jobs were lost during migration))r~  Ú	job_countrÐ  )r   Ú_CRON_JOBS_RELrÛ  r–  r<   r=   r°  r´  rM   rN   rt   rµ  ru   )rÐ  r3   r  Ú	live_pathÚ
live_countÚ	snap_pathÚ
snap_countr€   s           r/   Úrestore_cron_jobs_if_emptiedrã  ã  sJ  € ð@ ð ØˆtàÐ+�/Ñ+Ô+€DØ•~Ñ%€Iå! )Ñ,Ô,€Jð ÐØˆtå$ TÑ*Ô*¨[Ñ8½>ÑI€IÝ! )Ñ,Ô,€JØð Øˆtð �ZÒÐØˆtðØÔ×Ò t°dÐÑ;Ô;Ð;ÝŒ�Y 	Ñ*Ô*Ð*Ð*øÝ•_Ð%ð ð ð Ý�ŠØNÐPSñ	
ô 	
ð 	
ð ˆtˆtˆtˆtˆtøøøøð	øøøõ ‡N‚Nð	YàØØØñô ð ð ¨:ÀkÐRÐRÐRs   Á(1B ÂCÂ+CÃCr¼  c                 óR  — |                       ¦   «         sdS t          d„ |                      ¦   «         D ¦   «         d„ d¬¦  «        }d}||d…         D ]T}	 t          j        |¦  «         |dz  }Œ# t
          $ r+}t                               d|j        |¦  «         Y d}~ŒMd}~ww xY w|S )	zKRemove oldest quick snapshots beyond the keep limit. Returns count deleted.r   c              3   óª   K  — | ]N}|                      ¦   «         ¯|j                             d ¦  «        °0|j                             d¦  «        °J|V — ŒOdS )r_   ra   N)r”   rC   rÅ   r—   rŽ   s     r/   rÃ   z)_prune_quick_snapshots.<locals>.<genexpr>5  sy   è è € ð 	
ð 	
àØ�xŠx‰zŒzð	
ð #$¤&×"3Ò"3°CÑ"8Ô"8ð	
ð BCÄÇÂÐQ[ÑA\ÔA\ð	
Øð	
ð 	
ð 	
ð 	
ð 	
ð 	
r.   c                 ó   — | j         S r“  ©rC   )r�   s    r/   ú<lambda>z(_prune_quick_snapshots.<locals>.<lambda>:  ó   € �a”f€ r.   T©ÚkeyrÊ  Nr;   zFailed to prune snapshot %s: %s)	rx   r'  ru  r°  r±  rM   rt   ru   rC   )r¼  r˜  ÚdirsÚdeletedr�   r€   s         r/   rº  rº  /  sí   € à�;Š;‰=Œ=ð Øˆqåð	
ð 	
à—\’\‘^”^ð	
ñ 	
ô 	
ð
 ÐØðñ ô €Dð €GØ�$�%�%Œ[ð Kð Kˆð	KÝŒM˜!ÑÔÐØ�q‰LˆGˆGøÝð 	Kð 	Kð 	KÝ�NŠNÐ<¸a¼fÀcÑJÔJÐJÐJÐJÐJÐJÐJøøøøð	Køøøð €Ns   ÁA/Á/
B$Á9!BÂB$c                 ó>   — t          t          |¦  «        | ¬¦  «        S )z6Manually prune quick snapshots. Returns count deleted.r­  )rº  r–  )r˜  r3   s     r/   Úprune_quick_snapshotsrï  I  s    € õ
 "Õ"6°{Ñ"CÔ"CÈ$ÐOÑOÔOÐOr.   c                 ó0  — t          | dd¦  «        }t          |¬¦  «        }|rct          d|› �¦  «         t          ¦   «         }t          dt	          |¦  «        › dt          ¦   «         › d�¦  «         t          d|› �¦  «         dS t          d	¦  «         dS )
z*CLI entry point for hermes backup --quick.r—  N)r—  zState snapshot created: r
  z snapshot(s) stored in z/state-snapshots/z"  Restore with: /snapshot restore z!No state files found to snapshot.)Úgetattrrœ  rð   rÏ  rÙ   r   )rõ   r—  r¿  Úsnapss       r/   Úrun_quick_backupró  Q  s®   € å�D˜' 4Ñ(Ô(€EÝ#¨%Ð0Ñ0Ô0€GØð 3ÝÐ2¨Ð2Ð2Ñ3Ô3Ð3Ý$Ñ&Ô&ˆÝÐ^•3�u‘:”:Ð^Ð^Õ6IÑ6KÔ6KÐ^Ð^Ð^Ñ_Ô_Ð_ÝÐ<°7Ð<Ð<Ñ=Ô=Ð=Ð=Ð=åÐ1Ñ2Ô2Ð2Ð2Ð2r.   c                 óÜ   — 	 t          |¦  «        5  t          | |¦  «        cddd¦  «         S # 1 swxY w Y   dS # t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)z5Single-flight wrapper for automatic full zip backups.NzFull-zip backup skipped: %s)r\   Ú_write_full_zip_backup_lockedr(   rt   ru   )r§   rö   r€   s      r/   Ú_write_full_zip_backuprö  b  sÛ   € ðÝ# KÑ0Ô0ð 	Hð 	HÝ0°¸;ÑGÔGð	Hð 	Hð 	Hð 	Hñ 	Hô 	Hð 	Hð 	Hð 	Hð 	Hð 	Hð 	Høøøð 	Hð 	Hð 	Hð 	Hð 	Hð 	Høå ð ð ð Ý�ŠÐ4°cÑ:Ô:Ð:Øˆtˆtˆtˆtˆtøøøøðøøøs-   ‚; ‘.¡; ®2²; µ2¶; »
A+ÁA&Á&A+c           
      óú  — t          j        ¦   «         }t                               d¦  «         g }	 t	          j        |d¬¦  «        D ]€\  }}}t          |¦  «        }d„ |D ¦   «         |dd…<   |D ]W}||z  }		 |	                     |¦  «        }
n# t          $ r Y Œ*w xY wt          |	|
| ¦  «        rŒ@| 
                    |	|
f¦  «         ŒXŒ�n3# t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w|sdS t                               dt          j        ¦   «         |z
  dz  t          |¦  «        ¦  «         t          j        ¦   «         }	 t          | ¦  «        5 }t          j        |d	t          j        d
¬¦  «        5 }t%          |d¦  «        D �] \  }\  }}	 |j        dk    rót)          j        ddt-          | j        ¦  «        ¬¦  «        5 }t          |j        ¦  «        }ddd¦  «         n# 1 swxY w Y   	 t3          ||¦  «        s7t                               d|¦  «         t5          t-          |¦  «        ¦  «        ‚|                     |t-          |¦  «        ¬¦  «         |                     d¬¦  «         n?# |                     d¬¦  «         w xY w|                     |t-          |¦  «        ¬¦  «         nA# t:          t          t          f$ r'}t                               d||¦  «         Y d}~�Œfd}~ww xY w|dz  dk    r)t                               d|t          |¦  «        ¦  «         �Œ¢	 ddd¦  «         n# 1 swxY w Y   ddd¦  «         n# 1 swxY w Y   n:# t          t4          f$ r&}t                               d|¦  «         Y d}~dS d}~ww xY wt                               dt          j        ¦   «         |z
  dz  t          |¦  «        |                      ¦   «         j         ¦  «         | S )u'  Write a full zip snapshot of ``hermes_root`` to ``out_path``.

    Uses the same exclusion rules and SQLite safe-copy as :func:`run_backup`.
    Returns the output path on success, None on failure (nothing to back up,
    or write error â€” caller should surface the outcome but not raise).
    z*automatic backup phase=scan status=startedFr‰   c                 ó$   — g | ]}|t           v¯|‘ŒS r-   rŒ   rŽ   s     r/   r‘   z1_write_full_zip_backup_locked.<locals>.<listcomp>z  s"   € ÐJÐJÐJ °!½>Ð2IÐ2I˜1Ð2IÐ2IÐ2Ir.   Nz Full-zip backup: walk failed: %szEautomatic backup phase=scan status=complete duration_ms=%.1f files=%drý   rÿ   r   r  r;   r  r  z6Full-zip backup aborted: SQLite snapshot failed for %sr  Trb   zSkipping %s in zip backup: %sr  r   zDautomatic backup phase=archive status=progress completed=%d total=%dz%Full-zip backup: zip write failed: %szQautomatic backup phase=archive status=complete duration_ms=%.1f files=%d bytes=%d)!r?   r@   rt   r  rB   r•   r   r  rz   r©   r|   rM   ru   rÙ   rl   r   r!  r"  r#  r  r$  r%  r²   r<   rC   r·   r1   rG   rh   rN   ÚdebugrE   rF   )r§   rö   r)  r*  rš   r›   rœ   r�   rž   rŸ   r/  r€   Úarchive_startedr8  r9  Úindexr¦   r¡   r;  r<  s                       r/   rõ  rõ  l  s  € õ ”>Ñ#Ô#€LÝ
‡K‚KÐ<Ñ=Ô=Ð=Ø,.€LðÝ,.¬G°KÈUÐ,SÑ,SÔ,Sð 	2ð 	2Ñ(ˆG�X˜yÝ�g‘”ˆBàJÐJ hÐJÑJÔJˆH�Q�Q�Q‰Kà"ð 
2ð 
2�Ø˜U™
�ðØ×+Ò+¨KÑ8Ô8�C�CøÝ!ð ð ð Ø�Hðøøøõ ,¨E°3¸ÑAÔAð Øà×#Ò# U¨C LÑ1Ô1Ð1Ð1ð
2ð	2øõ  ð ð ð Ý�ŠÐ9¸3Ñ?Ô?Ð?Øˆtˆtˆtˆtˆtøøøøðøøøð ð Øˆtå
‡K‚KØOÝ	ŒÑ	Ô	˜LÑ	(¨DÑ0ÝˆLÑÔñô ð õ ”nÑ&Ô&€Oð)Ý  Ñ*Ô*ð "	¨l½G¼OØ˜#�wÔ3À1ð=
ñ =
ô =
ð "	àÝ/8¸ÀqÑ/IÔ/Ið ñ Ñ+�Ñ+˜ (ðØ”¨%Ò/Ð/õ
 &Ô8Ø#(°½CÀÄÑ<PÔ<Pðñ ô ð 4à Ý%)¨#¬(¡^¤^˜Fð4ð 4ð 4ñ 4ô 4ð 4ð 4ð 4ð 4ð 4ð 4øøøð 4ð 4ð 4ð 4ð	;Ý#0°¸6Ñ#BÔ#Bð JÝ &§¢Ø$\Ø$,ñ!"ô !"ð !"õ ';½3¸x¹=¼=Ñ&IÔ&IÐ IØŸHšH VµS¸±]´]˜HÑCÔCÐCà"ŸMšM°T˜MÑ:Ô:Ð:Ð:ø˜FŸMšM°T˜MÑ:Ô:Ð:Ð:øøøàŸš µ3°x±=´=˜ÑAÔAÐAøøÝ'­µ*Ð=ð ð ð Ý—L’LÐ!@À(ÈCÑPÔPÐPØ�H�H�H‘Høøøøðøøøð ˜3‘; !Ò#Ð#Ý—K’KØ^ØÝ˜LÑ)Ô)ñô ð ùð7ð"	ð "	ð "	ñ "	ô "	ð "	ð "	ð "	ð "	ð "	ð "	øøøð "	ð "	ð "	ð "	ð "	ð "	ð "	ñ "	ô "	ð "	ð "	ð "	ð "	ð "	ð "	øøøð "	ð "	ð "	ð "	øøõF Õ)Ð*ð ð ð Ý�ŠÐ>ÀÑDÔDÐDð ˆtˆtˆtˆtˆtøøøøðøøøõ ‡K‚KØ[Ý	ŒÑ	Ô	˜OÑ	+¨tÑ3ÝˆLÑÔØ�Š‰ŒÔñ	ô ð ð €Os  ±AC	 Á7BÂC	 Â
BÂC	 ÂBÂ.C	 Ã	
C9ÃC4Ã4C9ÅM) Å##MÆMÆ!4KÇG6	Ç*KÇ6G:Ç:KÇ=G:Ç>KÈA+JÉ-KÊJ	Ê'KËMËL	ËK=	Ë7MË=L	Ì7MÌ:MÍM
	Í
MÍM
	ÍMÍM) ÍM!Í!M) Í$M!Í%M) Í)N Í:NÎN r   zpre-update-r×   c                 ó6   — | pt          ¦   «         }|t          z  S r“  )r   Ú_PRE_UPDATE_BACKUPS_DIRr•  s     r/   Ú_pre_update_backup_dirrþ  Ó  s   € ØÐ+�/Ñ+Ô+€DØÕ)Ñ)Ð)r.   Ú
backup_dirc                 ór  — t          |d¦  «        }|                      ¦   «         sdS t          d„ |                      ¦   «         D ¦   «         d„ d¬¦  «        }d}||d…         D ]T}	 |                     ¦   «          |dz  }Œ# t
          $ r+}t                               d|j        |¦  «         Y d}~ŒMd}~ww xY w|S )	uÞ  Remove oldest pre-update backups beyond the keep limit.

    Returns the number of files deleted.  Only touches files matching
    ``pre-update-*.zip`` so hand-made zips dropped in the same directory
    are never touched.

    ``keep`` is floored to 1 because this helper is only called immediately
    after a fresh backup is written: deleting that backup right after the
    user paid the disk/CPU cost to create it would leave them worse off
    than no backup at all (and the wrapper in ``main.py`` would still print
    a misleading ``Saved: <path>`` line for a file that no longer exists).
    Operators who genuinely don't want a backup should set
    ``updates.pre_update_backup: off`` in config â€” that gates creation.
    r;   r   c              3   óº   K  — | ]V}|                      ¦   «         r@|j                             t          ¦  «        r!|j                             ¦   «         d k    ¯R|V — ŒWdS ©rú   N)r’   rC   rÅ   Ú_PRE_UPDATE_PREFIXr  r  rP  s     r/   rÃ   z,_prune_pre_update_backups.<locals>.<genexpr>ì  st   è è € ð 	bð 	bˆqØ�IŠI‰KŒKð	bØœF×-Ò-Õ.@ÑAÔAð	bØFGÄhÇnÂnÑFVÔFVÐZ`ÒF`ÐF`ð 
ØF`ÐF`ÐF`ÐF`ð	bð 	br.   c                 ó   — | j         S r“  rç  ©r„   s    r/   rè  z+_prune_pre_update_backups.<locals>.<lambda>î  ré  r.   Trê  NzFailed to prune backup %s: %s©	rA   rx   r'  ru  rh   rM   rt   ru   rC   ©rÿ  r˜  r   rí  r„   r€   s         r/   Ú_prune_pre_update_backupsr  Ø  s  € õ ˆt�Q‰<Œ<€DØ×ÒÑÔð Øˆqåð	bð 	b�J×&Ò&Ñ(Ô(ð 	bñ 	bô 	bàÐØð	ñ ô €Gð €GØ�T�U�UŒ^ð Ið Iˆð	IØ�HŠH‰JŒJˆJØ�q‰LˆGˆGøÝð 	Ið 	Ið 	IÝ�NŠNÐ:¸A¼FÀCÑHÔHÐHÐHÐHÐHÐHÐHøøøøð	Iøøøð €Nó   Á%A?Á?
B4Â	!B/Â/B4c                 ó¾  — | pt          ¦   «         }|                     ¦   «         sdS t          |¦  «        }	 |                     dd¬¦  «         n4# t          $ r'}t
                               d||¦  «         Y d}~dS d}~ww xY wt          j        ¦   «          	                    d¦  «        }|t          › |› d�z  }t          ||¦  «        }|€dS t          ||¬¦  «         |S )uÕ  Create a full zip backup of HERMES_HOME under ``backups/``.

    Mirrors :func:`run_backup` (same exclusion rules, same SQLite safe-copy)
    but writes to ``<HERMES_HOME>/backups/pre-update-<timestamp>.zip`` and
    auto-prunes old pre-update backups.

    Returns the path to the created zip, or ``None`` if no files were
    found or the backup could not be created.  Never raises â€” the caller
    (``hermes update``) should continue even if the backup fails.
    NTr6   z-Could not create pre-update backup dir %s: %srù   rú   r­  )r   r”   rþ  r=   rM   rt   ru   r   r  r  r  rö  r  ©r3   r˜  rö   rÿ  r€   r(  r§   rá   s           r/   Úcreate_pre_update_backupr  ý  s  € ð Ð:Õ!8Ñ!:Ô!:€KØ×ÒÑÔð Øˆtå'¨Ñ4Ô4€JðØ×Ò °ÐÑ5Ô5Ð5Ð5øÝð ð ð Ý�ŠÐFÈ
ÐTWÑXÔXÐXØˆtˆtˆtˆtˆtøøøøðøøøõ ŒL‰NŒN×#Ò#Ð$5Ñ6Ô6€EØÕ1Ð>°5Ð>Ð>Ð>Ñ>€Hå# H¨kÑ:Ô:€FØ€~Øˆtå˜j¨tÐ4Ñ4Ô4Ð4Ø€Oó   ·A Á
B ÁA;Á;B zpre-migration-c                 ór  — t          |d¦  «        }|                      ¦   «         sdS t          d„ |                      ¦   «         D ¦   «         d„ d¬¦  «        }d}||d…         D ]T}	 |                     ¦   «          |dz  }Œ# t
          $ r+}t                               d|j        |¦  «         Y d}~ŒMd}~ww xY w|S )	z¶Remove oldest pre-migration backups beyond the keep limit.

    Only touches files matching ``pre-migration-*.zip`` so other backups in
    the same directory are never touched.
    r   c              3   óº   K  — | ]V}|                      ¦   «         r@|j                             t          ¦  «        r!|j                             ¦   «         d k    ¯R|V — ŒWdS r  )r’   rC   rÅ   Ú_PRE_MIGRATION_PREFIXr  r  rP  s     r/   rÃ   z/_prune_pre_migration_backups.<locals>.<genexpr>4  st   è è € ð 	eð 	eˆqØ�IŠI‰KŒKð	eØœF×-Ò-Õ.CÑDÔDð	eØIJÌÏÊÑIYÔIYÐ]cÒIcÐIcð 
ØIcÐIcÐIcÐIcð	eð 	er.   c                 ó   — | j         S r“  rç  r  s    r/   rè  z._prune_pre_migration_backups.<locals>.<lambda>6  ré  r.   Trê  Nr;   z+Failed to prune pre-migration backup %s: %sr  r  s         r/   Ú_prune_pre_migration_backupsr  )  s  € õ ˆt�Q‰<Œ<€DØ×ÒÑÔð Øˆqåð	eð 	e�J×&Ò&Ñ(Ô(ð 	eñ 	eô 	eàÐØð	ñ ô €Gð €GØ�T�U�UŒ^ð Wð Wˆð	WØ�HŠH‰JŒJˆJØ�q‰LˆGˆGøÝð 	Wð 	Wð 	WÝ�NŠNÐHÈ!Ì&ÐRUÑVÔVÐVÐVÐVÐVÐVÐVøøøøð	Wøøøð €Nr	  c                 ó¾  — | pt          ¦   «         }|                     ¦   «         sdS t          |¦  «        }	 |                     dd¬¦  «         n4# t          $ r'}t
                               d||¦  «         Y d}~dS d}~ww xY wt          j        ¦   «          	                    d¦  «        }|t          › |› d�z  }t          ||¦  «        }|€dS t          ||¬¦  «         |S )ug  Create a full zip backup of HERMES_HOME under ``backups/`` before a
    ``hermes claw migrate`` apply.

    Shares implementation with :func:`create_pre_update_backup` via
    ``_write_full_zip_backup`` â€” same exclusions, same SQLite safe-copy,
    restorable with ``hermes import <archive>``.  Writes to
    ``<HERMES_HOME>/backups/pre-migration-<timestamp>.zip`` and auto-prunes
    old pre-migration backups.

    Returns the path to the created zip, or ``None`` if nothing was found
    to back up (fresh install) or the write failed.  Never raises â€” the
    caller decides whether to abort or proceed.
    NTr6   z0Could not create pre-migration backup dir %s: %srù   rú   r­  )r   r”   rþ  r=   rM   rt   ru   r   r  r  r  rö  r  r  s           r/   Úcreate_pre_migration_backupr  E  s  € ð" Ð:Õ!8Ñ!:Ô!:€KØ×ÒÑÔð Øˆtõ (¨Ñ4Ô4€JðØ×Ò °ÐÑ5Ô5Ð5Ð5øÝð ð ð Ý�ŠÐIÈ:ÐWZÑ[Ô[Ð[Øˆtˆtˆtˆtˆtøøøøðøøøõ ŒL‰NŒN×#Ò#Ð$5Ñ6Ô6€EØÕ4ÐA°eÐAÐAÐAÑA€Hå# H¨kÑ:Ô:€FØ€~Øˆtå  °$Ð7Ñ7Ô7Ð7Ø€Or  )r2   )rm   Nr“  )NNNN)r‘  N)[r,   r¸  ÚloggingrB   r°  r°   rñ   r$  rf   r?   r   Ú
contextlibr   r   r   Úpathlibr   Útypingr   r   r	   r
   Úhermes_constantsr   r   r   Úhermes_cli.sizefmtr   r&  Ú	getLoggerr)   rt   r�   r˜   r–   rs  rq  r  ÚRuntimeErrorr(   r1   Úfloatr\   rl   r†   r    r®  r¥   r©   r·   ÚintrÉ   rÚ   Ú!DEFAULT_INTEGRITY_CHECK_MAX_BYTESrØ  rè   rì   r÷   ró   r!  Útupler²   rH  rX  rŽ  r²  r”  r»  r–  rœ  r›  rÏ  rÔ  rÞ  rÛ  rã  rº  rï  ró  rö  rõ  rý  r  Ú_PRE_UPDATE_DEFAULT_KEEPrþ  r  r  r  Ú_PRE_MIGRATION_DEFAULT_KEEPr  r  r-   r.   r/   ú<module>r#     sË  ððð ð €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø 
€
€
€
Ø €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø %Ð %Ð %Ð %Ð %Ð %Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,à ZÐ ZÐ ZÐ ZÐ ZÐ ZÐ ZÐ ZÐ ZÐ Zð <Ð ;Ð ;Ð ;Ð ;Ð ;à	ˆÔ	˜8Ñ	$Ô	$€ð.ð ð €ð.Ð ðð ð €ð>ð ð Ð ð 7Ð6Ð6Ð ð  Ð ðKð Kð Kð Kð K˜Lñ Kô Kð Kð	ð 	ð 	ð 	ð 	˜<ñ 	ô 	ð 	ð ð4ð 4¨ð 4¸uð 4ð 4ð 4ñ „ð4ðn ð Dð ð ð ñ „ðð3°°d´ð 3ð 3ð 3ð 3ðl˜tð ¨¨T¬
ð ð ð ð ð,˜dð  tð ð ð ð ð2 tð °tð Àtð ÐPTð ð ð ð ð(�tð  $ð ¨4ð ð ð ð ð> '*¸ð+ð +ð +Ø
ð+Ø #ð+Ø26ð+à	ð+ð +ð +ð +ð@ &€ð %,Ð !ð ØØ6ðHð Hð HØ
ðHð ðHð ð	Hð
 ðHð 
ðHð Hð Hð HðV˜Dð  tð °ð ð ð ð ð4%ð %ð %ð %ð E@¨$ð E@°4ð E@ð E@ð E@ð E@ðX˜Wœ_ð °°t¸S°yÔ1Að ð ð ð ð6�w”ð ¨3ð ð ð ð ð0YDð YDð YDð YDðRÐ ð> )Ð ØÐ ð'ð ' h¨t¤nð 'Àð 'ð 'ð 'ð 'ð  Ø"&ØØ#'ð	
ð 
Ø�CŒ=ð
à˜$”ð
ð �3Œ-ð
ð ˜C”=ð	
ð
 ˆc„]ð
ð 
ð 
ð 
ð$  Ø"&ØØ#'ð	`ð `Ø�CŒ=ð`à˜$”ð`ð �3Œ-ð`ð ˜C”=ð	`ð
 ˆc„]ð`ð `ð `ð `ðH Ø"&ðð Øðà˜$”ðð 
ˆ$ˆs�CˆxŒ.Ôðð ð ð ð8 #'ðIð IØðIà˜$”ðIð 
ðIð Ið Ið Ið\ "€ð˜4ð  H¨S¤Mð ð ð ð ðB #'ðISð ISØðISà˜$”ðISð ˆd�3˜�8ŒnÔðISð ISð ISð ISðX 4Gð ð  ð ¨Sð È3ð ð ð ð ð6 $Ø"&ðPð PØ
ðPà˜$”ðPð 	ðPð Pð Pð Pð
3ð 
3ð 
3ð 
3ð" Tð ¸ð ÀÈ$Äð ð ð ð ð[¨Dð [¸tð [ÈÐQUÌð [ð [ð [ð [ðD $Ð Ø"Ð ØÐ ð*ð *¨°¬ð *À$ð *ð *ð *ð *ð
"¨$ð "°cð "¸cð "ð "ð "ð "ðL #'Ø(ð!ð !Ø˜$”ð!à
ð!ð ˆd„^ð!ð !ð !ð !ðP )Ð ØÐ ð¨Tð ¸ð Àð ð ð ð ð: #'Ø+ð&ð &Ø˜$”ð&à
ð&ð ˆd„^ð&ð &ð &ð &ð &ð &r.   