Ë
    âß²jž  ã                  ó¬   — d Z ddlm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	  ej                  e«      ZdZdZdd„Zdd„Zdd	„Zdd
„Zdd„Zdd„Zdd„Zy)aÆ  Scratch-dir retention: idle detection plus the reaping an idle tree needs before it goes.

Deleting an idle ``cache/scratch/<entry>`` is not enough on its own: a lane's e2e run leaves
headless browsers whose cwd was inside the tree (they survived for days with a ``(deleted)``
cwd), and a repo whose linked worktree lived in the tree keeps a dangling registration until
someone runs ``git worktree prune``. Both are reaped here, right before ``rmtree``.
é    )ÚannotationsN)ÚPathg      @é   c                óR  — 	 t        j                  | «      j                  |k\  ry| j                  «       r| j	                  «       ry	 t        | «      g}|r‘	 t        j                  |j                  «       «      5 }|D ][  }	 |j                  d¬«      j                  |k\  r
 ddd«       y	 |j                  d¬«      sŒA|j                  |j                  «       Œ] 	 ddd«       |rŒ‘y# t
        $ r Y yw xY w# t
        $ r Y  ddd«       yw xY w# 1 sw Y   Œ4xY w# t
        $ r Y yw xY w)a¯  True when *path* or anything beneath it has an mtime at or after *cutoff*.

    Stops at the first recent entry, so a live tree costs one hit and only a truly idle
    tree pays for the full walk (once, right before it is deleted). Symlinks are never
    followed: a link into the repo would make the target's activity keep the entry alive.
    An unreadable entry is kept: an incomplete scan cannot establish that it is idle.
    TF©Úfollow_symlinksN)ÚosÚlstatÚst_mtimeÚis_dirÚ
is_symlinkÚOSErrorÚstrÚscandirÚpopÚstatÚappendÚpath)r   ÚcutoffÚstackÚitÚchilds        ú=/home/ubuntu/.hermes/hermes-agent/hermes_constants_scratch.pyÚsubtree_touched_sincer      s&  € ðÜ�8‰8�D‹>×"Ñ" fÒ,ØØ�{‰{Œ} §¡Ô 1Øð !2ô �‹YˆK€EÙ
ð	Ü—‘˜EŸI™I›KÓ(ð 1¨BØò 1�Eð$Ø Ÿ:™:°e˜:Ó<×EÑEÈÒOØ#'÷	1ð 1ð Pð —|‘|°E�|Õ:ØŸ™ U§Z¡ZÕ0ñ1÷1ò ð øô! ò Ùðûô #ò $Ù#÷1ð 1ð
$ú÷1ð 1ûô ò 	Ùð	ús|   ‚"C' ¥ C' Á#D Á9DÂ C6ÂDÂ D Â)DÂ=DÃD Ã'	C3Ã2C3Ã6	DÃ?DÄD Ä
DÄDÄDÄD Ä	D&Ä%D&c                óŽ   — | |k(  xs? | j                  |j                  t        j                  «      t        j                  z   «      S ©N)Ú
startswithÚrstripr	   Úsep)r   Úroots     r   Ú_underr!   9   s0   € Ø�4‰<ÒH˜4Ÿ?™?¨4¯;©;´r·v±vÓ+>ÄÇÁÑ+GÓHÐHó    c                 ó  — ddl } t        «       }	 | j                  «       }|�J|j                  |vr<|j	                  |j                  «       |j                  «       }|�|j                  |vrŒ<|S # | j                  t        f$ r Y |S w xY w)zOThis process and its ancestors: never reap the shell that is running the prune.r   N)ÚpsutilÚsetÚProcessÚpidÚaddÚparentÚErrorr   )r$   ÚpidsÚprocs      r   Ú_own_lineager-   =   s…   € ãä“U€DðØ�~‰~ÓˆØÐ 4§8¡8°4Ñ#7Ø�H‰H�T—X‘XÔØ—;‘;“=ˆDð Ð 4§8¡8°4Ò#7ð
 €Køð �L‰Lœ'Ð"ò ØØ€Kðús   �AA. Á.BÂBc                ój  ‡— ddl }t        j                  j                  t	        | «      «      }|D �cg c]*  }t        j                  j                  t	        |«      «      ‘Œ, }}t        «       }t        t        d«      rt        j                  «       nd}g }|j                  dg«      D ]½  }	|	j                  |v rŒ	 |�|	j                  «       j                  |k7  rŒ3|	j                  «       }
|
sŒF|
j                  d«      }|r|
dt!        d«        n|
Št#        ‰|«      sŒw|s4t        j                  j%                  ‰«      rt'        ˆfd„|D «       «      sŒ­|j)                  |	«       Œ¿ |sy|D ]  }		 |	j+                  «        Œ |j-                  |t.        ¬«      \  }}|D ]  }		 |	j1                  «        Œ t2        j5                  dt!        |«      «       t!        |«      S c c}w # |j                  t        f$ r Y �ŒTw xY w# |j                  t        f$ r Y Œªw xY w# |j                  t        f$ r Y Œ“w xY w)	a   TERM (then KILL) same-user processes whose cwd is inside an entry about to be
    pruned, or is a path under *scratch_root* that no longer exists. Returns the count.

    Only the cwd is consulted: a process merely holding a file open inside scratch (an
    editor, a log tail) is not ours to kill, but one *living* in a directory we are
    about to delete, or in one already gone, has nothing left to run for.
    r   NÚgetuidr'   z
 (deleted)c              3  ó6   •K  — | ]  }t        ‰|«      –— Œ y ­wr   )r!   )Ú.0ÚtÚcwd_paths     €r   ú	<genexpr>z+reap_processes_rooted_in.<locals>.<genexpr>j   s   øè ø€ Ò9_ÐRS¼&ÀÈ1×:MÑ9_ùs   ƒ)Útimeoutz=scratch prune: reaped %d process(es) rooted in pruned entries)r$   r	   r   Úrealpathr   r-   Úhasattrr/   Úprocess_iterr'   ÚuidsÚrealÚcwdr*   r   ÚendswithÚlenr!   ÚexistsÚanyr   Ú	terminateÚ
wait_procsÚ_REAP_GRACE_SECONDSÚkillÚloggerÚinfo)Úscratch_rootÚdoomedr$   r    ÚpÚtargetsÚskipÚuidÚvictimsr,   r;   ÚdeletedÚ_Úaliver3   s                 @r   Úreap_processes_rooted_inrP   L   s  ø€ ó ä�7‰7×ÑœC Ó-Ó.€DØ17Ö8¨AŒr�w‰w×Ñ¤ A£Õ'Ð8€GÐ8Ü‹>€DÜ ¤ XÔ.Œ"�)‰)Œ+°D€CØ$&€GØ×#Ñ# U GÓ,ò !ˆØ�8‰8�tÑØð	Øˆ 4§9¡9£;×#3Ñ#3°sÒ#:ØØ—(‘(“*ˆCñ ØØ—,‘,˜|Ó,ˆÙ07�3Ð+œ#˜lÓ+Ð+Ñ,¸SˆÜ�h Ô%ØÙœ"Ÿ'™'Ÿ.™.¨Ô2´cÓ9_ÐW^Ô9_Õ6_Ø�N‰N˜4Õ ð!!ñ" ØØò ˆð	Ø�N‰NÕðð
 × Ñ  Ô2EÐ ÓF�H€A€uØò ˆð	Ø�I‰I�Kðô
 ‡K�KÐOÔQTÐU\ÓQ]Ô^Üˆw‹<ÐùòG 9øð —‘œgÐ&ò 	Úð	ûð —‘œgÐ&ò 	Ùð	ûð —‘œgÐ&ò 	Ùð	úsA   ²/GÂ:G#ÃG#Å0G?Æ"HÇ#G<Ç;G<Ç?HÈHÈH2È1H2c                ó¼  — t        «       }t        | «      dfg}|�r”|j                  «       \  }}	 t        j                  |«      5 }|D �]W  }|j
                  dk(  rö|j                  d¬«      rä	 t        |j                  «      j                  dd¬«      j                  «       }|j                  d«      sŒlt        |t        d«      d	 j                  «       «      }|j                  j
                  d
k(  sŒ«|j                  j                  j
                  dk(  sŒÏ|j                  t        |j                  j                  j                  «      «       �Œ	|j!                  d¬«      s�Œ|t"        k  s�Œ(|j
                  dvs�Œ8|j%                  |j                  |dz   f«       �ŒZ 	 d	d	d	«       |r�Œ”|S # t        $ r Y �Œvw xY w# 1 sw Y   ŒxY w# t        $ r Y �ŒÁw xY w)ziRepos whose linked worktrees live inside *entry* (``.git`` FILES, ``gitdir: <repo>/.git/worktrees/<n>``).r   z.gitFr   úutf-8Úreplace)ÚencodingÚerrorszgitdir:NÚ	worktrees)Únode_modulesz.venvÚvenvé   )r%   r   r   r	   r   ÚnameÚis_filer   r   Ú	read_textÚstripr   r   r=   r)   r(   r   Ú_GIT_FILE_MAX_DEPTHr   )	ÚentryÚreposr   ÚcurrentÚdepthr   r   ÚlineÚgitdirs	            r   Ú_linked_worktree_reposre   }   sŽ  € ä“e€EÜ�%‹j˜!ˆ_Ð€EÚ
ØŸ™›‰ˆ�ð	Ü—‘˜GÓ$ð >¨Øó >�EØ—z‘z VÒ+°·±Èe°Ô0Tð%Ü#'¨¯
©
Ó#3×#=Ñ#=ÀwÐW`Ð#=Ó#a×#gÑ#gÓ#i˜Dð  Ÿ?™?¨9Õ5Ü%)¨$¬s°9«~¨Ð*?×*EÑ*EÓ*GÓ%H˜Fà%Ÿ}™}×1Ñ1°[Ó@ÀVÇ]Á]×EYÑEY×E^ÑE^ÐbhÓEhØ %§	¡	¬#¨f¯m©m×.BÑ.B×.IÑ.IÓ*JÖ KØŸ™°e˜Ö<ÀÔI\ÔA\Ø %§
¡
Ð2SÓ SØŸ™ e§j¡j°%¸!±)Ð%<Ö=ñ>÷>ó ð( €Løô  'ò %Ú$ð%ú÷>ð >ûô ò 	Úð	úsr   °G Á(GÁ.4F2Â"GÂ4>GÃ3#GÄAGÅ%	GÅ0GÆ #GÆ$G Æ2	F?Æ;GÆ>F?Æ?GÇGÇG Ç	GÇGc                ó@  — t        | «      D ]S  }t        j                  j                  |«      sŒ#	 t	        j
                  dd|ddgt        j                  dddddd	¬
«       ŒU y# t        t        j                  f$ r!}t        j                  d||«       Y d}~ŒŒd}~ww xY w)zT``git worktree prune`` in each repo: drops registrations whose tree we just deleted.Úgitz-CÚworktreeÚpruneTrR   rS   é   F)ÚstdinÚcapture_outputÚtextrT   rU   r5   Úcheckz#git worktree prune in %s failed: %sN)Úsortedr	   r   ÚisdirÚ
subprocessÚrunÚDEVNULLr   ÚSubprocessErrorrD   Údebug)r`   ÚrepoÚexcs      r   Úrelease_git_worktreesrx   ˜   s•   € ä�u“ò 
KˆÜ�w‰w�}‰}˜TÔ"Øð	KÜ�N‰NØ˜˜d J°Ð8Ü ×(Ñ(¸ÀDÐSZÐclØ %öñ	
Køô œ×3Ñ3Ð4ò 	KÜ�L‰LÐ>ÀÀc×JÑJûð	Kús   °0A#Á#BÁ<BÂBc                óˆ  — t        j                   «       |dz  z
  }	 | j                  «       D �cg c]  }|j                  |vsŒ|‘Œ }}|D �cg c]  }t	        ||«      rŒ|‘Œ }}	 t        | |«       |syt        «       }d}	|D ]^  }
	 |
j                  «       r6|
j                  «       s&|t        |
«      z  }t        j                  |
d¬«       n|
j                  «        |	dz  }	Œ` t!        |«       |	S c c}w # t        $ r Y yw xY wc c}w # t        $ r }t        j                  d|«       Y d}~Œ¼d}~ww xY w# t        $ r Y Œ¾w xY w)zÆDelete top-level entries of *root* with no write anywhere in their subtree for
    *max_idle_hours*, reaping processes and worktree registrations rooted in them first.
    Returns the count removed.i  r   z'scratch prune: process reap skipped: %sNT)Úignore_errorsrY   )ÚtimeÚiterdirrZ   r   r   rP   Ú	ExceptionrD   ru   r%   r   r   re   ÚshutilÚrmtreeÚunlinkrx   )r    Úmax_idle_hoursÚ
skip_namesr   ÚeÚentriesrG   rw   r`   Úremovedr_   s              r   Úprune_idle_entriesr†   §   s?  € ô �Y‰Y‹[˜>¨DÑ0Ñ0€FðØ"Ÿl™l›nÖI˜°·±¸jÒ0H’1ÐIˆÐIð !ÖI�AÔ(=¸aÀÕ(HŠaÐI€FÐIðEÜ   vÔ.ñ ØÜ“e€EØ€GØò 	ˆð	Ø�|‰|Œ~ e×&6Ñ&6Ô&8ØÔ/°Ó6Ñ6�Ü—‘˜e°4Ö8à—‘”Ø�q‰L‰Gð	ô ˜%Ô Ø€Nùò3 JøÜò ÙðüâIøô
 ò EÜ�‰Ð>À×DÑDûðEûô ò 	Ùð	ús_   œC5 ®C0ÁC0ÁC5 ÁDÁDÁ%D	 ÂAD5Ã0C5 Ã5	DÄ DÄ		D2ÄD-Ä-D2Ä5	EÅ E)r   r   r   ÚfloatÚreturnÚbool)r   r   r    r   rˆ   r‰   )rˆ   zset[int])rF   r   rG   z
list[Path]rˆ   Úint)r_   r   rˆ   úset[str])r`   r‹   rˆ   ÚNone)r    r   r�   r‡   r‚   zfrozenset[str]rˆ   rŠ   )Ú__doc__Ú
__future__r   Úloggingr	   r~   rq   r{   Úpathlibr   Ú	getLoggerÚ__name__rD   rB   r^   r   r!   r-   rP   re   rx   r†   © r"   r   ú<module>r”      sh   ðñõ #ã Û 	Û Û Û Ý à	ˆ×	Ñ	˜8Ó	$€ð Ð àÐ óó@Ióó.óbó6Kôr"   