+
    „WÝj9È  ã                   óþ  € R t ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIHt ]	! RRRRR7      t
R R	 ltR
 R ltR R ltRR/R R lltR R ltR R ltR R ltRR/R R lltR R ltR R ltR R  ltR! R" ltR# R$ ltR% R& ltR' R( ltRsR) R* lltR+R/R, R- lltR. R/ lt]! 0 Rtm4      tR0 R1 ltR2 R3 lt R4 R5 lt!R6 R7 lt"R8t#R9t$Rut%R: R; lt&R< R= lt'R> R? lt(R@ RA lt)RvRBRRCRRDR/RE RF lllt*RGR/RH RI llt+RJ RK lt,RLR/RM RN llt-RO RP lt.RQ RR lt/RS^/RT RU llt0RV RW lt1^t2^t3RXt4RYt5]6! RZ4      t7R[ R\ lt8R] R^ lt9R_ R` lt:Ra Rb lt;RsRc Rd llt<Re Rf lt=Rgt>Rh Ri lt?RjRkRlRRmRRnRRoRRpR/Rq Rr llt@R# )wu  Dashboard process-hygiene helpers â€” extracted from ``hermes_cli/main.py``.

Helpers defined in ``hermes_cli.main_dashboard`` / ``hermes_cli.main_install_repair`` are imported at
call time so imports stay one-way (both of those modules import this one lazily).
N©ÚPath)Úis_desktop_ssh_backend_argvTúutf-8Úreplace)Úcapture_outputÚtextÚencodingÚerrorsc                óF   € V ^8„  d   QhR\         \        \        3,          /# ©é   Úreturn)ÚdictÚstrÚlist)Úformats   "ÚNC:\Users\johnn\AppData\Local\hermes\hermes-agent\hermes_cli\dashboard_procs.pyÚ__annotate__r      s   € ÷ 7ñ 7”tœC¤˜I•ñ 7ó    c                  ó   € R . R. R. /# )ÚmatchedÚkilledÚfailed© r   r   r   Ú_empty_resultr      s   € Ø�r˜8 R¨°2Ð6Ð6r   c                óx   € V ^8„  d   QhR\         \        \        \        3,          ,          R\        R\        RR/# )r   ÚrowsÚpid_textÚcommandr   N©r   ÚtupleÚintr   )r   s   "r   r   r      s5   € ÷ ñ ”dœ5¤¤c �?Õ+ð ´sð ÄSð ÈTñ r   c                 ód   €  V P                  \        V4      V34       R #   \         d     R # i ; i©N)Úappendr"   Ú
ValueError)r   r   r   s   &&&r   Ú_append_rowr'      s.   € ðØ�‰”S˜“] GÐ,Ö-øÜô Úðús   ‚   /®/c                ó\   € V ^8„  d   QhR\         \        \        \        3,          ,          /# r   r    )r   s   "r   r   r      s    € ÷ %ñ %œT¤%¬¬S¨¥/Õ2ñ %r   c                 ó  € . p \         P                  R8X  dÇ   ^ RIHp V! . RO^
RR7      pVe    VP                  ^ 8w  g   VP
                  f   V # RpVP
                  P                  R4       Fk  pVP                  4       pVP                  R4      '       d   V\        R4      R pK:  VP                  R	4      '       g   KS  \        W\        R	4      R V4       Km  	  V # \        P                  ! . RO3R
^
/\        BRR/B pVP                  ^ 8X  dy   \        VRR4      P                  R4       FX  pVP                  4       P                  R^4      p\        V4      ^8X  g   K5  RV9  g   K>  \        W^ ,          V^,          4       KZ  	  V # )zW``(pid, cmdline)`` for every process, via wmic (Windows) or ps. Raises on scan failure.Úwin32)Úbounded_probe_runÚignore)Útimeoutr
   NÚ Ú
zCommandLine=z
ProcessId=r-   ÚcheckFÚstdoutÚgrep)ÚwmicÚprocessÚgetzProcessId,CommandLinez/FORMAT:LIST)Úpsú-Aú-ozpid=,command=)ÚsysÚplatformÚhermes_cli._subprocess_compatr+   Ú
returncoder1   ÚsplitÚstripÚ
startswithÚlenr'   Ú
subprocessÚrunÚ_PS_RUN_KWARGSÚgetattr)r   r+   ÚresultÚcurrent_cmdÚlineÚpartss         r   Ú_iter_process_tablerI      sR  € à"$€DÜ
‡|�|�wÔõ 	DÙ"ÚOØ˜xô)ˆð Š>˜V×.Ñ.°!Ô3°v·}±}Ò7LØˆKØˆØ—M‘M×'Ñ'¨Ö-ˆDØ—:‘:“<ˆDØ�‰˜~×.Ò.Ø"¤3 ~Ó#6Ð#8Ð9’Ø—‘ ×.Ô.Ü˜D¤s¨<Ó'8Ð':Ð";¸[ÖIñ .ð ˆä�^Š^Ò?ÑkÈÐkÌ~ÑkÐejÒk€FØ×Ñ˜AÔÜ˜F H¨bÓ1×7Ñ7¸Ö=ˆDØ—J‘J“L×&Ñ& t¨QÓ/ˆEÜ�5‹z˜QŽ 6°Ö#5Ü˜D¨¥(¨E°!­HÖ5ñ >ð €Kr   Úexclude_pidsc                óŒ   € V ^8„  d   QhR\         \        ,          R,          R\        \        \        \        3,          ,          /# )r   rJ   Nr   )Úsetr"   r   r!   r   )r   s   "r   r   r   E   s2   € ÷ %ñ %¬s´3­x¸$­ð %Ì$ÌuÔUXÔZ]ÐU]ÍÕJ_ñ %r   c                ó  € \         P                  ! 4       0T ;'       g    Rmp^ RIHp  \	        4        UUu. uF  w  r4W19  g   K  V! V4      R	9   g   K  W43NK!  	  ppp\        P                  ! \        4      ;_uu_ 4        ^ RIHp T UUu0 uF  w  r7TkK	  	  uppT,          pT! 4        F�  p	T	P                  R4      pT	P                  R4      R
9   g   K,  \        T\         4      '       g   KD  Y89  g   KL  TP#                  T\%        T	P                  R4      ;'       g    R4      34       Kƒ  	  RRR4       T# u uppi   \
        \        P                  \        3 d    . u # i ; iu uppi   + '       g   i     T# ; i)a  ``(pid, cmdline)`` of running ``dashboard``/``serve`` processes; empty on any scan error.

A forgotten dashboard keeps the old Python backend against the new JS bundle after
``hermes update`` (every API call 401s). *exclude_pids* (Desktop's HERMES_DESKTOP_CHILD_PID
backends) are never returned.

*exclude_pids* is an optional set of PIDs that must never be returned. This is used by the Hermes
Desktop Electron app to protect its own backend child process: when the desktop spawns ``hermes serve``
as a backend and triggers an auto-update, the update must not kill the backend that the desktop itself
manages. The desktop sets the environment variable ``HERMES_DESKTOP_CHILD_PID`` on the spawned backend
process; ``_kill_stale_dashboard_processes`` reads it and passes it here. (#37532)
©Ú_hermes_holder_subcommand©Úledger_entriesÚpidÚpurposeÚargvr.   Nr   )Ú	dashboardÚserve©rV   rU   )ÚosÚgetpidÚhermes_cli.update_cmd_windowsrO   rI   ÚFileNotFoundErrorrA   ÚTimeoutExpiredÚOSErrorÚ
contextlibÚsuppressÚ	ExceptionÚhermes_cli.process_identityrQ   r5   Ú
isinstancer"   r%   r   )
rJ   ÚskiprO   rR   ÚcmdÚfoundrQ   Ú_ÚseenÚentrys
   $         r   Ú_scan_dashboard_processesri   E   sJ  € ô �IŠI‹KÐ/˜<×-Ð-¨2Ð/€Dõ HðÜ,?Ô,Aô bÑ,A¡ Ø‘Oô Ù(AÀ#Ó(FÐJ`Ñ(`ô �#“Ñ,Aˆñ bô 
×	Ò	œY×	'Õ	'õ
 	?Ù"'Ô(¡%™˜“¡%Ò(¨4Õ/ˆÙ#Ö%ˆEØ—)‘)˜EÓ"ˆCØ—	‘	˜)Ó$Ð(>Ö>Ä:ÈcÔSV×CWÔCWØžØ—‘˜c¤3 u§y¡y°Ó'8×'>Ð'>¸BÓ#?Ð@ÖAñ	 &÷ 
(ð €Lùó%bøäœz×8Ñ8¼'ÐBô ØŠ	ðüó )÷ 
(Ö	'ð €Lúsd   ªE ·
D;Á
D;ÁD;ÁE ÂE/ÂE)
Â9E/ÃE/Ã0E/Ã8(E/Ä!E/Ä;E Å"E&Å%E&Å)E/Å/F 	c                óh   € V ^8„  d   QhR\         \        \        \        \        3,          3,          /# r   )r   r"   r!   r   )r   s   "r   r   r   m   s%   € ÷ ñ œT¤#¤u¬S´#¨X¥Ð"6Õ7ñ r   c                 óä  € / p \         P                  ! \        4      ;_uu_ 4        ^ RIHp V! 4        F�  pVP                  R4      VP                  R4      rCVP                  R4      R9   g   K<  \        V\        4      '       g   KT  \        V\        4      '       g   Kl  V^ 8”  g   Ku  \        VP                  R4      ;'       g    R4      V3W&   KŸ  	  RRR4       V #   + '       g   i     T # ; i)	a  ``pid -> (host, port)`` recorded in the spawn ledger for live serve/dashboard backends.

The entry is written after the bind, so it carries the real port where argv only says
``--port 0`` (Desktop SSH backends ask the OS for a port). Empty when the ledger is unavailable.
rP   rR   ÚportrS   Úhostr.   NrW   )	r^   r_   r`   ra   rQ   r5   rb   r"   r   )ÚbindsrQ   rh   rR   rl   s        r   Ú_ledger_serve_bindsro   m   s³   € ð )+€EÜ	×	Ò	œY×	'Õ	'Ý>Ù#Ö%ˆEØŸ	™	 %Ó(¨%¯)©)°FÓ*;�Ø—	‘	˜)Ó$Ð(>Ö>Ä:ÈcÔSV×CWÔCWÜ" 4¬×-Ô-°$¸¶(Ü! %§)¡)¨FÓ"3×"9Ð"9°rÓ:¸DÐA�“
ñ	 &÷ 
(ð €L÷ 
(Ö	'ð €Lús*   ¥ACÁ/CÂCÂCÂ(CÃCÃC/	c                ó`   € V ^8„  d   QhR\         R\        \        \        3,          R,          /# ©r   rR   r   N)r"   r   r   )r   s   "r   r   r   ~   s'   € ÷ ñ ”cð œd¤3¬ 8�n¨tÕ3ñ r   c                óü  € \         P                  ! \        4      ;_uu_ 4        ^ RIp\	        VP                  V 4      P                  4       4      uuRRR4       #   + '       g   i     M; i \        RT  R24      P                  4       pM  \         d     R# i ; i/ pTP                  R4       FG  pTP                  R4      w  rVpT'       g   K!  TP                  RRR7      Y5P                  RRR7      &   KI  	  T# )	zNExec-time environment of *pid* (psutil, then /proc); ``None`` when unreadable.Nú/proc/z/environó    ó   =r   r   )r
   )r^   r_   r`   Úpsutilr   ÚProcessÚenvironr   Ú
read_bytesr]   r=   Ú	partitionÚdecode)rR   rv   ÚrawÚenvÚpartÚkeyÚsepÚvalues   &       r   Ú_pid_environr‚   ~   sË   € ä	×	Ò	œY×	'Õ	'ÛÜ�F—N‘N 3Ó'×/Ñ/Ó1Ó2÷ 
(×	'×	'Ò	'úðÜ�V˜C˜5 Ð)Ó*×5Ñ5Ó7‰øÜô Úðúà€CØ—	‘	˜'Ö"ˆØŸ.™.¨Ó.‰ˆ�%ß‰3Ø9>¿¹ÀgÐV_¸Ó9`ˆC—
‘
˜7¨9�
Ó5Ó6ñ #ð €Js   £,AÁA*	Á.B ÂBÂBc                ó>   € V ^8„  d   QhR\         R\        R,          /# rq   ©r"   r   )r   s   "r   r   r   �   s   € ÷ ñ œ#ð ¤#¨¥*ñ r   c                ó†  € Rp\         P                  ! \        4      ;_uu_ 4        ^ RIpVP	                  V 4      P                  4       P                  pRRR4       VfO   \         P                  ! \        4      ;_uu_ 4        \        P                  ! RV  24      P                  pRRR4       Vf   R# \         P                  ! \        4      ;_uu_ 4        ^ RIpVP                  V4      P                  ;'       g    RuuRRR4       #   + '       g   i     L¾; i  + '       g   i     L; i  + '       g   i     R# ; i)a²  Login home of the user *pid* runs as, from the password database (psutil, then /proc).

A service unit with a scrubbed environment exports no ``HOME``; the target resolves its own
default home through ``Path.home()``, which falls back to this entry. The inspecting process's
home belongs to a different user and must never stand in for it. ``None`` when the owner or the
entry is unreadable, leaving the caller its existing fallback.
Nrs   )r^   r_   r`   rv   rw   ÚuidsÚrealr]   rX   ÚstatÚst_uidÚpwdÚgetpwuidÚpw_dir)rR   Úuidrv   rŠ   s   &   r   Ú_pid_passwd_homerŽ   �   sÔ   € ð €CÜ	×	Ò	œY×	'Õ	'ÛØ�n‰n˜SÓ!×&Ñ&Ó(×-Ñ-ˆ÷ 
(ð ‚{Ü× Ò ¤×)Õ)Ü—'’'˜F 3 %˜.Ó)×0Ñ0ˆC÷ *à
‚{ÙÜ	×	Ò	œY×	'Õ	'ÛØ�|‰|˜CÓ ×'Ñ'×/Ð/¨4÷ 
(Ò	'÷ 
(×	'ú÷ *×)ú÷ 
(Ö	'ñ ús#   ¥.D	Â$DÃ)D/Ä	D	ÄD,	Ä/E 	Úrequire_launch_identityFc                óJ   € V ^8„  d   QhR\         R\        R\        R,          /# )r   rR   r�   r   N)r"   Úboolr   )r   s   "r   r   r   ¦   s-   € ÷ 8Oñ 8Oœcð 8O¼tð 8OÔPSÐVZÕPZñ 8Or   c               ój  a€ \        V 4      oSf   R# ^ RIHp ^ RIHpHpHp V! V 4      pV'       d(   Vf$   ^ RIpVP                  V 4      P                  4       pV'       d   V'       g   R# T;'       g    . pSP                  RR4      P                  4       p\        V4      p	V'       d«   \        P                  R8X  d+   SP                  R4      ;'       g    SP                  R4      MSP                  R	4      p
V'       d"   \        V4      P!                  4       '       g   R# V'       g*   V
'       d    \        V
4      P!                  4       '       g   R# V	fE   V'       d=   \        V4      P"                  P$                  R
8X  g   SP                  R4      R8X  d   V# \        P                  R8X  dƒ   SP                  RR4      P                  4       pV'       d   \        V4      MD\        SP                  R4      ;'       g    \        P&                  ! 4       4      R,          R,          pVR,          pMQ\        SP                  R	4      ;'       g)    \)        V 4      ;'       g    \        P&                  ! 4       4      R,          pV! W�4      p\*        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      ;'       g    \-        V4      pV	f   V'       g   V'       d   R# V! V4      p	V	'       d	   V! V	4      MRpVR8X  d   \/        V4      # \/        VR
,          V,          4      # )a7  Resolve a process home for discovery, or require pinned launch identity for termination.

By default, ``None`` means an unreadable environment (another user, hardened ``/proc``).
``require_launch_identity`` also refuses missing argv, relative/missing home anchors and
any profile selection that would depend on today's sticky ``active_profile``.

A readable environment always resolves, replaying ``_apply_profile_override`` on the target's
exec-time env + argv (``hermes -p X serve`` rewrites ``HERMES_HOME`` in ``os.environ`` AFTER
startup, which ``/proc/<pid>/environ`` never reflects): a profile-shaped ``HERMES_HOME``
without a flag is the home; otherwise the root is ``HERMES_HOME`` (its grandparent when
profile-shaped) or the platform default of the process's own ``HOME`` / ``LOCALAPPDATA``
(its owner's password-database home when a scrubbed unit environment exports neither), and
the profile is the ``--profile``/``-p`` flag, else the root's sticky ``active_profile`` unless
the process has a fixed identity (supervised child, post-swap updater, Desktop SSH backend).
N)Ú_dashboard_cmdline_for_pid)Úget_active_profileÚnormalize_profile_nameÚprofile_root_for_env_homeÚHERMES_HOMEr.   r*   ÚLOCALAPPDATAÚUSERPROFILEÚHOMEÚprofilesÚHERMES_UPDATE_POST_SWAPÚ1ÚAppDataÚLocalÚhermesz.hermesc              3   óF   <"  € T F  pSP                  V4      x € K  	  R # 5ir$   ©r5   )Ú.0Úkr}   s   & €r   Ú	<genexpr>Ú'_hermes_home_for_pid.<locals>.<genexpr>×   s&   øé € ð Vñ .U¨˜Ÿ™ Ÿ˜ó .Uùs   ƒ!TFÚdefault)ÚHERMES_SUPERVISED_CHILDÚHERMES_S6_SUPERVISED_CHILDÚ"HERMES_GATEWAY_EXTERNAL_SUPERVISOR)r‚   Úhermes_cli.main_dashboardr“   Úhermes_cli.profilesr”   r•   r–   rv   rw   Úcmdliner5   r>   Ú_profile_flag_valuer9   r:   r   Úis_absoluteÚparentÚnameÚhomerŽ   Úanyr   r   )rR   r�   r“   r”   r•   r–   rT   rv   Úenv_homeÚprofileÚanchorÚlocal_appdataÚbaseÚdefault_homeÚrootÚfixed_identityÚcanonr}   s   &$               @r   Ú_hermes_home_for_pidr½   ¦   sg  ø€ ô" �sÓ
€CØ
‚{ÙÝDßiÑiÙ% cÓ*€Dß 4¢<ãØ�~‰~˜cÓ"×*Ñ*Ó,ˆß§tÙØ�:ˆ:�2€DØ�w‰w�} bÓ)×/Ñ/Ó1€HÜ! $Ó'€GßÜHKÏÉÐX_ÔH_�#—'‘'˜.Ó)×CÐC¨S¯W©W°]Ó-CøÐeh×elÑelÐmsÓetˆßœD ›N×6Ñ6×8Ò8Ùß§¬t°F«|×/GÑ/G×/IÒ/IÙØ‚Ÿ8ÜˆX‹×Ñ×"Ñ" jÔ0°C·G±GÐ<UÓ4VÐZ]Ô4]àˆÜ
‡|�|�wÔØŸ™ °Ó3×9Ñ9Ó;ˆß&3Œt�MÔ"¼¸c¿g¹gÀmÓ>T×>cÐ>cÔX\×XaÒXaÓXcÓ9dÐgpÕ9pÐszÕ9zˆØ˜h•‰ä˜CŸG™G F›O×SÐSÔ/?ÀÓ/D×SÐSÌÏ	Ê	ËÓTÐW`Õ`ˆÙ$ XÓ<€Dß“Sô Vñ .Uó V—S—S’Sô Vñ .Uó Vó V÷ {ð {ÜYtÐuyÓYzð à‚Ÿ~ß"ÙÙ$ TÓ*ˆß/6Ñ" 7Ô+¸I€EØ Ô*Œ3ˆt‹9ÐN´°D¸:Õ4EÈÕ4MÓ0NÐNr   c                óT   € V ^8„  d   QhR\         \        ,          R\        R,          /# ©r   rT   r   N)r   r   r"   )r   s   "r   r   r   á   s&   € ÷ Zñ Z¤d¬3¥ið Z´C¸$µJñ Zr   c                 ó:   € \        R  \        V 4       4       R4      # )c              3   ó<   "  € T F  w  rVR9   g   K  Vx € K  	  R# 5i)rV   NrW   r   )r£   ÚiÚtoks   &  r   r¥   Ú._dashboard_subcommand_index.<locals>.<genexpr>â   s   é € ÐR¡‘v�q°CÐ;QÑ4Q—’£ùs   ‚’
N)ÚnextÚ	enumerate©rT   s   &r   Ú_dashboard_subcommand_indexrÈ   á   s   € ÜÑR¤¨4¤ÓRÐTXÓYÐYr   c                óT   € V ^8„  d   QhR\         \        ,          R\        R,          /# r¿   ©r   r   )r   s   "r   r   r   å   s"   € ÷ ñ œd¤3�ið ¬C°$­Jñ r   c                ó  € \        V 4       Fo  w  rVR9   d2   V^,           \        V 4      8  d   \        W^,           ,          4      u # VP                  R4      '       g   KV  VP	                  R^4      ^,          u # 	  R# )zJValue of the first ``--profile X`` / ``-p X`` / ``--profile=X`` in *argv*.ú
--profile=Ú=N©z	--profileú-p)rÆ   r@   r   r?   r=   )rT   rÂ   rÃ   s   &  r   r®   r®   å   se   € ä˜D–/‰ˆØÐ%Ô%¨!¨a­%´#°d³)Ô*;Ü�t �E•{Ó#Ò#Ø�>‰>˜,×'Ô'Ø—9‘9˜S !Ó$ QÕ'Ò'ñ	 "ñ
 r   c                óF   € V ^8„  d   QhR\         \        ,          R\        /# )r   rT   r   )r   r   r‘   )r   s   "r   r   r   ï   s   € ÷ 
.ñ 
.¬$¬s­)ð 
.¼ñ 
.r   c                óº   a € \        S 4      f   R# \        ;QJ d(    V 3R l\        S 4       4       F  '       g   K   R# 	  R# ! V 3R l\        S 4       4       4      # )u¼   True for Desktop-style ``serve|dashboard --port 0`` backends â€” replaying them after
``hermes update`` multiplies listening backends because ``--port 0`` binds a fresh port.

See #78821.
Fc              3   óJ  <"  € T F˜  w  rVR 8H  ;'       dB    V^,           \        S4      8  ;'       d%    \        SV^,           ,          4      R8H  ;'       gD    VP                  R4      ;'       d+    VP                  R^4      ^,          P	                  4       R8H  x € Kš  	  R# 5i)ú--portÚ0z--port=rÍ   N)r@   r   r?   r=   r>   )r£   rÂ   rÃ   rT   s   &  €r   r¥   Ú2_is_ephemeral_port_zero_backend.<locals>.<genexpr>÷   s’   øé € ð .á,‘6�1ð �x‘×QÐQ A¨¥E¬C°«IÑ$5×QÐQ¼#¸dÀ1ÀqÅ5½kÓ:JÈcÑ:Q÷ Vð VØ—>‘> )Ó,×TÐT°·±¸3ÀÓ1BÀ1Õ1E×1KÑ1KÓ1MÐQTÑ1TôVã,ùs   ƒB#–B#³#B#ÁB#Á03B#T)rÈ   r³   rÆ   rÇ   s   fr   Ú_is_ephemeral_port_zero_backendrÖ   ï   sV   ø€ ô # 4Ó(Ò0Ùß‹3ô .ä& tœ_ó.�3Œ3ð .Š3ð .ˆ3ô .ä& tœ_ó.ó .ð .r   c                ó`   € V ^8„  d   QhR\         \        ,          R\        \        R3,          /# )r   rT   r   .)r   r   r!   )r   s   "r   r   r   ü   s&   € ÷ ,ñ ,¤t¬C¥yð ,´U¼3À¸8µ_ñ ,r   c                óz  € \        V 4      pVf   \        V 4      # . p^ pW18  d|   W,          pVR9   d:   V^,           V8  d,   VP                  W@V^,           ,          .4       V^,          pKN  VP                  R4      '       d   VP	                  V4       V^,          pK�  \        V\        WR 4      ,           4      # )zACollapse argv to profile flags + serve/dashboard tail for dedupe.NrÌ   rÎ   )rÈ   r!   Úextendr?   r%   r   )rT   ÚidxÚprefixrÂ   rÃ   s   &    r   Ú_normalize_dashboard_cmdlinerÜ   ü   s¤   € ä
% dÓ
+€CØ
‚{Ü�T‹{ÐØ€FØ	€AØ
Œ'Ø�gˆØÐ%Ô%¨!¨a­%°#¬+Ø�M‰M˜3 Q¨¥U¥Ð,Ô-Ø��FˆAÙØ�>‰>˜,×'Ò'Ø�M‰M˜#ÔØ	ˆQ�ŠÜ�œ$˜t D˜zÓ*Õ*Ó+Ð+r   c                ó0   € V ^8„  d   QhR\         R\        /# ©r   r²   r   )r   r   )r   s   "r   r   r     s   € ÷ ñ œð ¤ñ r   c                 ó„   €  \        V 4      P                  4       #   \        \        \        3 d    \        T 4      u # i ; ir$   )r   Úresolver]   ÚRuntimeErrorr&   ©r²   s   &r   Ú_resolved_homerã     s:   € ðÜ�D‹z×!Ñ!Ó#Ð#øÜ”\¤:Ð.ô Ü�D‹zÒðús   ‚ ›!?¾?c                ó0   € V ^8„  d   QhR\         R\         /# rÞ   ©r   )r   s   "r   r   r     s   € ÷ 7ñ 7¤sð 7¬sñ 7r   c                ód   € \         P                  P                  \        \	        V 4      4      4      # )zcInstall-identity key for *home*: symlinked / differently-spelled roots compare equal.

See #94030.
)rX   ÚpathÚnormcaser   rã   râ   s   &r   Ú_normalized_home_for_compareré     s#   € ô
 �7‰7×ÑœC¤¨tÓ 4Ó5Ó6Ð6r   c                óh   € V ^8„  d   QhR\         \        ,          R\        R\         \        ,          /# )r   Úpidsr²   r   )r   r"   r   )r   s   "r   r   r     s)   € ÷ ñ ¤T¬#¥Yð ´cð ¼dÄ3½iñ r   c                ó”   € \        V4      pV  Uu. uF,  p\        V4      ;p'       g   K  \        V4      V8X  g   K*  VNK.  	  up# u upi )a„  Return only *pids* whose resolved Hermes home (``_hermes_home_for_pid``) is ``home``.

Dashboard argv is discovery-only: it is not an ownership proof because
several Hermes installs and profiles can run the same command on one
machine.  An unreadable process environment is deliberately not treated
as a match, so a stop request fails closed rather than taking down an
unrelated backend.
)ré   r½   )rë   r²   ÚtargetrR   Úpid_homes   &&   r   Ú_pids_owned_by_hermes_homerï     sV   € ô *¨$Ó/€FáóÙ�Ü,¨SÓ1Ð1ˆHÓ1ô 	ä(¨Ó2°fÑ<÷ 	ˆ‘tñð ùò s   �AªA¼Ac                ó`   € V ^8„  d   QhR\         \        ,          R\        R,          R\        /# )r   rT   Úhermes_homeNr   rÊ   )r   s   "r   r   r   /  s*   € ÷ ?ñ ?¤4¬¥9ð ?¼3À½:ð ?ÔQTñ ?r   c                óú   € V'       d]   \        V4      P                  p\        V4      ^8¼  d*   VR,          R8X  d   VR,          '       d   RVR,           2# R\        V4       2# R\	        V 4      ;'       g    R 2# )u  Stable owner key: ``HERMES_HOME`` when known, else ``--profile`` / ``-p``.

A home ending in ``profiles/<name>`` â†’ ``profile:<name>`` (shares a cap with an explicit
``--profile``); other homes keep a ``home:`` key so unrelated installs never collapse.

See #78821.
r›   zprofile:zhome:r§   éþÿÿÿéÿÿÿÿ)rã   rH   r@   ré   r®   )rT   rñ   rH   s   && r   Ú_profile_key_for_respawnrõ   /  sv   € ÷ Ü˜{Ó+×1Ñ1ˆÜˆu‹:˜Œ?˜u R�y¨JÔ6¸5À¿9¼9Ø˜e B�i˜[Ð)Ð)ØÔ3°KÓ@ÐAÐBÐBØÔ)¨$Ó/×<Ð<°9Ð=Ð>Ð>r   Úown_homec                óÜ   € V ^8„  d   QhR\         \        \        \         \        ,          \        R,          3,          ,          R\        R,          R\         \         \        ,          ,          /# )r   Ú
candidatesNrö   r   r    )r   s   "r   r   r   ?  sI   € ÷ )ñ )Ü”Uœ3¤¤S¥	¬3°­:Ð5Õ6Õ7ð)ÜFIÈDÅjð)ä	Œ$Œs�)…_ñ)r   c               ó
  € Vf    ^ RI Hp \        V! 4       4      pV'       d   \	        V4      MRp. p\        4       p\        4       pV  F£  w  rxp	V'       d   \        V4      '       d   K!  V'       d   V	'       d   \	        V	4      V8w  d   KC  \        V4      p
\        W‰4      pW¥9   g   W¶9   d   Kg  VP                  V
4       VP                  V4       VP                  \        V4      4       K¥  	  V#   \         d    Rp Låi ; i)u„  Select which killed manual backends ``(pid, argv, hermes_home)`` to respawn after update.

Rules: never resurrect Desktop ``--port 0`` backends; never replay a backend from a
**foreign** ``HERMES_HOME`` (the argv-only respawn would come back on this install's home
and steal the foreign install's fixed port â†’ EADDRINUSE crash-loop; unreadable ``None``
stays eligible); dedupe by normalized cmdline; one backend per profile / home. PPID-1 is
NOT skipped: a prior respawn detaches, so fixed-port manual backends sit under init.

1. Never resurrect Desktop ephemeral ``serve|dashboard --port 0`` backends â€” Desktop
(``HERMES_DESKTOP_CHILD_PID``) owns their lifecycle. These are also the PPID-1 orphans that previously
multiplied across updates because ``--port 0`` always binds a fresh free port. 2. A foreign install's
backend is owned by that install's supervisor/user. 3. 4. See #78821, #94030.
Intentionally does **not** blanket-skip every PPID-1 process: a prior ``hermes update`` respawn detaches
with ``start_new_session=True``, so fixed-port manual backends are reparented to init and must still be
eligible for the next update's #40449 restart.
)Úget_hermes_homer.   )Úhermes_constantsrú   r   r`   ré   rL   rÖ   rÜ   rõ   Úaddr%   r   )rø   rö   rú   Úown_keyÚselectedÚseen_cmdlinesÚseen_profilesÚ_pidrT   rñ   ÚnormÚprofile_keys   &$          r   Ú$_filter_dashboard_respawn_candidatesr  ?  så   € ð& Òð	Ý8Ü™?Ó,Ó-ˆH÷ 9AÔ*¨8Ô4Àb€GØ "€HÜ*-«%€MÜ!›e€MÛ#-Ñˆ�KßÔ6°t×<Ò<Ùß—{Ô'CÀKÓ'PÐT[Ô'[ÙÜ+¨DÓ1ˆÜ.¨tÓAˆØÔ  KÔ$@ÙØ×Ñ˜$ÔØ×Ñ˜+Ô&Ø�‰œ˜T›
Ö#ñ $.ð €Oøô% ô 	ØŠHð	ús   †C2 Ã2DÄDc                ó:   € V ^8„  d   QhR\         \        ,          /# r   ©rL   r"   )r   s   "r   r   r   k  s   € ÷ ñ ¤¤C¥ñ r   c                 ó8  € \        4       p \        P                  P                  RR4      P	                  R4       FH  p\
        P                  ! \        4      ;_uu_ 4        V P                  \        V4      4       RRR4       KJ  	  V #   + '       g   i     K_  ; i)zTPIDs Desktop marks as live backends (``HERMES_DESKTOP_CHILD_PID``, comma-separated).ÚHERMES_DESKTOP_CHILD_PIDr.   Ú,N)
rL   rX   rx   r5   r=   r^   r_   r&   rü   r"   )Úoutr~   s     r   Ú_exclude_pids_from_envr  k  sk   € ä“E€CÜ—
‘
—‘Ð9¸2Ó>×DÑDÀSÖIˆÜ× Ò ¤×,Õ,Ø�G‰G”C˜“IÔ÷ -Ñ,ñ Jð €J÷ -×,Ð,ús   ÁBÂBc                ó:   € V ^8„  d   QhR\         \        ,          /# r   r  )r   s   "r   r   r   ~  s   € ÷ ñ œs¤3�xñ r   c                 ón  €  ^ RI p V P                  4       P                  4        Uu0 uF  qP                  kK  	  up# u upi   \         d     Mi ; i\        4       p\        P                  ! 4       p\        R4       F´  p \        RT R24      P                  RRR7      pTP                  4        F<  pTP                  R4      '       g   K  \        TP                  4       ^,          4      p M&	  Tu #   \        \         \"        3 d    Tu u # i ; iT^8:  d   Tu # TP%                  T4       K¶  	  T# )	zÿPIDs of THIS process's ancestors (self excluded), best-effort; empty on any failure.

``--stop`` and the update sweep must never kill the process tree they were invoked
from. psutil is primary; the ``/proc`` walk keeps the answer when psutil is unusable.
Ni   rs   z/statusr   r   )r	   r
   zPPid:)rv   rw   ÚparentsrR   r`   rL   rX   rY   Úranger   Ú	read_textÚ
splitlinesr?   r"   r=   r]   r&   Ú
IndexErrorrü   )rv   ÚpÚ	ancestorsÚcurrf   Ústatus_textrG   s          r   Ú_caller_ancestor_pidsr  ~  s  € ðÛà%Ÿ~™~Ó/×7Ñ7Ô9Ó:Ñ9˜!—”Ñ9Ñ:Ð:ùÒ:øÜô Ùðúä›%€IÜ
�)Š)‹+€CÜ�4Ž[ˆð
	Ü ¨ u¨GÐ4Ó5×?Ñ?Ø ¨ð @ó 4ˆKà#×.Ñ.Ö0�Ø—?‘? 7×+Ô+Ü˜dŸj™j›l¨1�oÓ.�CÙñ 1ð
 !Ò øÜœ¤ZÐ0ô 	ØÔð	úà�!Œ8ØÒØ�‰�cÖñ ð Ðs>   ‚$A ¦=ºA ½A ÁAÁAÂAC5Ã!C5Ã/C5Ã5DÄDc                ó>   € V ^8„  d   QhR\         R\        R,          /# rq   r„   )r   s   "r   r   r   ž  s   € ÷ +ñ +œCð +¤C¨$¥Jñ +r   c                óÔ  €  ^ RI pVP                  V 4      P                  4       pV'       d0   \        P                  P                  \        V^ ,          4      4      #   \        RV  R24      P                  4       pTP                  R^4      ^ ,          P                  RR4      pTP                  R^4      R,          ;'       g    R#   \         d     Lwi ; i  \         d     R# i ; i)	zLBasename of *pid*'s first argv token, best-effort; ``None`` when unreadable.Nrs   z/cmdlinert   r   r   Ú/rô   )rv   rw   r­   rX   rç   Úbasenamer   r`   r   ry   r]   r=   r{   Úrsplit)rR   rv   rT   r|   Úheads   &    r   Ú_argv_head_commandr  ž  sÐ   € ðÛà�~‰~˜cÓ"×*Ñ*Ó,ˆßÜ—7‘7×#Ñ#¤C¨¨Q­£LÓ1Ð1ð ðÜ�V˜C˜5 Ð)Ó*×5Ñ5Ó7ˆð �9‰9�W˜aÓ  Õ#×*Ñ*¨7°IÓ>€DØ�;‰;�s˜AÓ˜rÕ"×*Ð* dÐ*øô ô Ùðûô ô Úðús(   ‚*C ­.C ÁC ÃCÃCÃC'Ã&C'c                óR   € V ^8„  d   QhR\         R\        \         ,          R\        /# )r   rR   r  r   )r"   rL   r‘   )r   s   "r   r   r   °  s*   € ÷ 
Eñ 
E¤#ð 
E´#´cµ(ð 
E¼tñ 
Er   c                óJ   € W9  d   R# \        V 4      ;'       g    R\        9   # )u�  True when *pid* is a caller ancestor headed by a wrapper executable.

Root selection is a substring match, so the shell a ``--stop`` was typed into (or a
``bash -c 'hermes dashboard --stop'`` wrapper) matches on its own argv. Ancestor alone
is not a spare: the backend hosting a shell-escaped TUI is also the caller's ancestor
and must stay stoppable â€” only a wrapper-headed ancestor is spared.
Fr.   )r  Ú_WRAPPER_HEAD_COMMANDS)rR   r  s   &&r   Ú_is_caller_wrapper_shellr"  °  s&   € ð ÔÙÜ˜sÓ#×)Ð) rÔ.DÑDÐDr   c          	      ó¤   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        \        \        3,          ,          RR/# ©r   rë   r   r   r   N©r   r"   r!   r   )r   s   "r   r   r   ½  s?   € ÷ )ñ )œT¤#�Yð )´´Sµ	ð )Ä4ÌÌcÔSVÈhÍÕCXð )Ð]añ )r   c                óö  € ^ RI Hp ^ RIHpHp V  Uu/ uF  qfV! V4      bK  	  ppV  Fý  p VP                  V4      pVf   VP                  VR34       K.  V! WhR7      '       g   VP                  VR34       KR  \        P                  ! RR\        V4      R	.R
\        P                  R
RR^
V! 4       RR7	      p	V	P                  ^ 8X  d   VP                  V4       Kµ  TP                  YiP                  ;'       g    V	P                  ;'       g    RP                  4       34       Kÿ  	  R# u upi   \        \        P                   \"        3 d)   p
TP                  T\        T
4      34        Rp
?
EKK  Rp
?
ii ; i)z9``taskkill /F`` each PID after re-verifying its identity.©Úget_process_start_time)Úpid_is_hermesÚwindows_hide_flagsNz!could not verify process identity)Úexpected_start_timez,not hermes-owned or process identity changedÚtaskkillz/PIDz/FTr   r   F)r   Ústdinr   r	   r
   r-   Úcreationflagsr0   r.   )Úgateway.statusr(  r;   r)  r*  r5   r%   rA   rB   r   ÚDEVNULLr<   Ústderrr1   r>   r[   r\   r]   )rë   r   r   r(  r)  r*  rR   Úpid_start_timesr+  rE   Úes   &&&        r   Ú_kill_pids_windowsr4  ½  s>  € å5ßOáCGÓHÁ4¸CÑ2°3Ó7Ò7Á4€OÐHÛˆð	)Ø"1×"5Ñ"5°cÓ":ÐØ"Ò*Ø—‘˜sÐ$GÐHÖIÙ" 3×PÓPØ—‘˜sÐ$RÐSÖTä#ŸšØ ¬¨S«°4Ð8ÈÜ$×,Ñ,°4À'Ø$¨bÑ@RÓ@TÐ\aôc�ð ×$Ñ$¨Ô)Ø—M‘M #Ö&à—M‘M 3¯©×)MÐ)M¸&¿-¹-×)MÐ)MÈ2×(TÑ(TÓ(VÐ"WÖXó ùò Iøô" "¤:×#<Ñ#<¼gÐFô 	)Ø�M‰M˜3¤ A£˜-×(Ó(ûð	)ús;   “D+¬(D0Á"D0Á:A!D0ÃD0Ã;D0ÄD0Ä0 E8ÅE3Å3E8g      $@g       @c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r   rR   Úttyr   )r"   r   r‘   )r   s   "r   r   r   ç  s!   € ÷ ñ ¤Sð ¬sð ´tñ r   c                ót   € V\         9  d   R#  \        P                  ! V 4      V 8H  #   \         d     R# i ; i)aÄ  True for a process the dashboard launched with ``start_new_session`` (own session, no tty).

Messaging-gateway bots and profile actions started from ``/api/gateway/*`` are such processes:
they are the user's, not the dashboard's, and must survive a dashboard stop. A hosted
``hermes --tui`` child is a session leader too (``pty.fork``) but owns the pts whose master the
dashboard held, so its tty column is set and it stays in the sweep.

Known gap: the turn-isolation ``tui_gateway.compute_host`` and ``slash_worker`` children are
launched the same way (``start_new_session=True``, no tty; ``compute_host`` also holds
``state.db``), so a wedged WAL holder of that class is spared here too. Their exit relies on
their own ppid watchdogs (``compute_host._parent_guard_loop``,
``slash_worker._start_parent_death_watchdog``), not on this sweep. Discriminating by WAL-holder
identity (``iter_deleted_sqlite_sidecar_holders``) was deliberately not done in this change.
F)Ú_NO_TTYrX   Úgetsidr]   )rR   r6  s   &&r   Ú_is_detached_session_leaderr:  ç  s9   € ð ”'ÔÙðÜ�yŠy˜‹~ Ñ$Ð$øÜô Úðús   �( ¨7¶7c          	      ó˜   € V ^8„  d   QhR\         \        ,          R\        \        \        \        \        R,          3,          3,          /# )r   Úrootsr   N)r   r"   r   r!   )r   s   "r   r   r   þ  s5   € ÷ %ñ %œd¤3�ið %¬D´´e¼CÄÀtÅ¸OÕ6LÐ1LÕ,Mñ %r   c                ó|  € ^ RI Hp  \        P                  ! . RO3R^
/\        BRR/B p/ p/ pTP                  ;'       g    RP                  4        FÒ  pTP                  4       p\        T4      ^8X  g   K%  T^ ,          P                  4       '       g   KD  T^,          P                  4       '       g   Kc  TP                  \        T^,          4      . 4      P                  \        T^ ,          4      T^,          34       \        T^,          4      T\        T^ ,          4      &   KÔ  	  \         P"                  ! 4       p\%        4       pTp	TP'                  T	^ 4      ;p	^8”  d   Y˜9  d   TP)                  T	4       K2  / p
T  Uu. uF  q»T3NK  	  ppT'       dx   TP+                  4       w  r¹TP'                  T	R4       FM  w  rÞYÚ9   g   YÐ9   g   Y×8X  g   \-        YÞ4      '       d   K*  YØ9  d   Y±! T4      3Y­&   TP                  Y½34       KO  	  K  T
#   \
        \        P                  \        3 d    / u # i ; iu upi )uð  ``{pid: (root, start_time)}`` of every dashboard-owned descendant of *roots*, snapshotted
BEFORE the kill: once the root dies its children are reparented and the PPID link is gone.
Detached session leaders (see ``_is_detached_session_leader``) are pruned together with their own
subtrees. So is the calling process with its subtree and its ancestor chain: ``hermes dashboard
--stop`` / ``hermes update`` run from a shell escape inside the hosted Chat TUI are same-session
descendants of the backend, and sweeping them would SIGTERM the caller mid-run (POSIX twin of the
Windows #98814 hazard). The start-time fingerprint is the PID-reuse guard (same one
``_kill_pids_windows`` uses). Empty on scan failure â†’ root-only kill, the historical behaviour.
r'  r-   r0   Fr.   )r6   r7   r8   zpid=,ppid=,tty=r   )r/  r(  rA   rB   rC   r[   r\   r]   r1   r  r=   r@   ÚisdigitÚ
setdefaultr"   r%   rX   rY   rL   r5   rü   Úpopr:  )r<  r(  rE   Úchildrenr°   rG   rH   Úmer  r  re   rº   ÚpendingrR   r6  s   &              r   Ú_posix_descendantsrD  þ  sÔ  € õ 6ðÜ—’Ò EÑqÈrÐqÔUcÑqÐkpÒqˆð 24€HØ€FØ—‘×$Ð$ "×0Ñ0Ö2ˆØ—
‘
“ˆÜˆu‹:˜Ž?˜u Q�x×/Ñ/×1Ô1°e¸Aµh×6FÑ6F×6HÔ6HØ×Ñ¤ E¨!¥H£¨rÓ2×9Ñ9¼3¸uÀQ½x»=È%ÐPQÍ(Ð:SÔTÜ$'¨¨a­£MˆF”3�u˜Q•x“=Ó!ñ	 3ô
 
�Š‹€BÜ›%€IØ
€CØ—*‘*˜S !Ó$Ð$ˆ3¨Ô
)¨cÔ.BØ�‰�cÖØ/1€EÙ(-Ó.© �d‹|©€GÐ.ß
Ø—K‘K“M‰	ˆØ Ÿ™ S¨"Ö-‰HˆCØŒ|˜sœ|¨s¬yÔ<WÐX[×<aÒ<aÙØÔ#Ø"Ð$:¸3Ó$?Ð@�‘
Ø�N‰N˜D˜;Ö'ó .ð €Løô1 œz×8Ñ8¼'ÐBô ØŠ	ðüò /s   ˆ#H ÆH9È"H6È5H6c                óh   € V ^8„  d   QhR\         \        ,          R\        R\         \        ,          /# )r   rë   Úsecondsr   )r   r"   Úfloat)r   s   "r   r   r   &  s)   € ÷ ñ ”Tœ#•Yð ¬ð ´4¼µ9ñ r   c                ó  € ^ RI p^ RIHp VP                  4       V,           p\	        V 4      pV'       dI   VP                  4       V8  d4   VP                  R4       V Uu. uF  qc! V4      '       g   K  VNK  	  ppKP  V# u upi )zJPoll up to *seconds*; return the PIDs still alive (zombies count as gone).N)Ú_pid_existsgš™™™™™¹?)Útimer/  rI  Ú	monotonicr   Úsleep)rë   rF  Ú_timerI  ÚdeadlineÚaliver  s   &&     r   Ú
_wait_gonerP  &  sg   € ãå*à�‰Ó  7Õ*€HÜ�‹J€Eß
�E—O‘OÓ%¨Ô0Ø�‰�CÔÙ!Ó4™E�q [°§^—�™EˆÐ4‰Ø€Lùò 5s   ÁA?Á3A?c          	      ó¤   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        \        \        3,          ,          RR/# r$  r%  )r   s   "r   r   r   4  s?   € ÷ )(ñ )(œ4¤�9ð )(¬d´3­ið )(ÄÄeÌCÔQTÈHÅoÕAVð )(Ð[_ñ )(r   c           
     óp  aaaa€ ^ RI o^ RIHp \        V 4      pR VVV3R llpV  F  pV! VSP                  4       K  	  V  UUu. uF,  qwS9  g   K  TS Uu0 uF  qˆ^ ,          kK  	  up9  g   K*  VNK.  	  p	pp\        V	\        4      oSP                  V3R lV	 4       4       S F  pV! VSP                  4       K  	  VP                  4        UU
Uu. uF  w  pw  r«Vf   K  V! V4      V8X  g   K  VNK!  	  pp
ppSP                  SP                  3 F]  pV FD  p\        P                  ! \        4      ;_uu_ 4        \        P                  ! Wm4       RRR4       KF  	  \        V\        4      pK_  	  SP                  R V 4       4       R# u upi u uppi u upp
pi   + '       g   i     K™  ; i)aÏ  SIGTERM, wait up to ``_POSIX_TERM_GRACE_SECONDS`` for graceful exit, SIGKILL survivors, then
sweep the dashboard-owned descendants that outlived the root and wait for the tree to be gone.

*killed* reports the roots only; swept descendants are the roots' own teardown debt. A descendant
still alive after its SIGKILL grace is appended to *failed*: the stop must not be declared
complete while a wedged ui-tui still holds the deleted state.db-wal inode (#112631).
Nr'  c                ó(   € V ^8„  d   QhR\         RR/# rq   ©r"   )r   s   "r   r   Ú&_kill_pids_posix.<locals>.__annotate__B  s   € ÷ )ñ )”3ð ) ñ )r   c                 ó0  <€  \         P                  ! W4       VSP                  8X  d   SP                  V 4       R # R #   \         d    SP                  T 4        R # \
        \        3 d(   pSP                  T \        T4      34        R p?R # R p?ii ; ir$   )rX   ÚkillÚSIGKILLr%   ÚProcessLookupErrorÚPermissionErrorr]   r   )rR   Úsigr3  Ú_signalr   r   s   && €€€r   Ú_sendÚ_kill_pids_posix.<locals>._sendB  sq   ø€ ð	)Ü�GŠG�CÔØ�g—o‘oÔ%Ø—‘˜cÖ"ñ &øä!ô 	Ø�M‰M˜#×Ü¤Ð)ô 	)Ø�M‰M˜3¤ A£˜-×(Ò(ûð	)ús!   ƒ8? ¿BÁBÁ-BÁ.BÂBc              3   ó8   <"  € T F  qS9  g   K  Vx € K  	  R # 5ir$   r   )r£   r  rO  s   & €r   r¥   Ú#_kill_pids_posix.<locals>.<genexpr>P  s   øé € Ð7™W˜°©—!’!›Wùs   ƒ�
c              3   ó*   "  € T F	  pVR 3x € K  	  R# 5i)z;descendant of the stopped backend still alive after SIGKILLNr   )r£   rR   s   & r   r¥   r`  \  s   é € ð (Ù&�cð ÐUÕVÛ&ùs   ‚)Úsignalr/  r(  rD  ÚSIGTERMrP  Ú_POSIX_TERM_GRACE_SECONDSrÙ   rX  Úitemsr^   r_   r]   rX   rW  Ú_POSIX_DESCENDANT_GRACE_SECONDS)rë   r   r   r(  Údescendantsr]  rR   r  ÚfrC  Ú_rootÚstartÚ	survivorsr[  r\  rO  s   &ff           @@r   Ú_kill_pids_posixrl  4  sz  û€ ó å5ä$ TÓ*€K÷)ò )ó ˆÙˆc�7—?‘?Ö#ñ áÔV™$�Q¨6¡/Œq°aÉfÓ?UÉfÈÀ!ÇÀÉfÑ?UÑ6U�qˆq™$€GÑVÜ�wÔ 9Ó:€EØ
‡M�MÔ7™WÓ7Ô7ÛˆÙˆc�7—?‘?Ö#ñ ð -8×,=Ñ,=Ô,?õ NÑ,?Ñ(�q™.˜5Øô Ù*@ÀÓ*CÀuÑ*L÷ �Ñ,?€Iò Nà—‘ §¡Ó1ˆÛˆCÜ×$Ò$¤W×-Õ-Ü—’˜Ô!÷ .Ñ-ñ ô ˜yÔ*IÓJŠ	ñ	 2ð
 ‡M�Mñ (Ù&ó(ö (ùò @VùÓVùôN÷ .×-Ð-úsB   ÁFÁFÁFÁ)FÁ2FÃFÃ&
FÃ5FÅF$	ÆFÆ$F5Úrestart_managedÚalready_restarted_unitsÚ
scope_homec                ó¨   € V ^8„  d   QhR\         R\        R\        \         ,          R,          R\         R,          R\        \         \        3,          /# )r   Úreasonrm  rn  Nro  r   )r   r‘   rL   r   r   )r   s   "r   r   r   `  sS   € ÷ i.ñ i.Üði.äði.ä<?Ä½HÀt½Oði.ô �d•
ði.ô 
Œ#Œtˆ)…_ñ	i.r   c               óœ  aaa€ ^ RI Ho \        \        SRR4      4      P	                  R4      pV'       dE   YB;'       g    R9  d5   SP                  V 4      '       d   \        T;'       g    R4      V0,          p\        4       pV'       d   V\        4       ,          pSP                  T;'       g    RVR7      pV'       g   \        4       # / p/ p/ o/ p	/ p
\        P                  R8w  d   SP                  4       M. oR VV3R	 llpV'       dü   \        P                  R8w  dç   V F„  pSP                  V4      W|&   SP                  V4      WŒ&   WŒ,          '       d   K9  SP!                  V4      pS'       d   V! WÍ4      ;p'       d   VSV&   Ki  V'       g   Ks  WÙV&   \#        V4      W¬&   K†  	  V'       dT   V Uu. uF5  qÈP%                  V4      ;'       g    R
P	                  R4      V9  g   K3  VNK7  	  ppV'       g   \        4       # M8S'       d1   V F*  pV! VSP!                  V4      4      ;p'       g   K%  VSV&   K,  	  \'        R\)        V4       RV  R24       . p. p\        P                  R8X  d   \*        M\,        ! WoV4       V F  p\'        RV 24       K  	  V F  w  pp\'        RV RV 24       K  	  V'       d   V'       d   \/        WøWyV
SR7      pMµ\1        V4      p\3        V UUUUu0 uF+  pSP%                  V4      ;p'       g   K  Vw  pppV RV 2kK-  	  upppp4       F  p\'        RV RV 24       K  	  \4        ;QJ d    V3R lV 4       F  '       g   K   RM	  RM! V3R lV 4       4      '       d   \'        R4       R\1        V4      R\1        V4      R\1        V4      R\1        V4      /# u upi u uppppi )u  Kill running ``hermes dashboard`` / ``hermes serve`` processes (update end, ``--stop``).

With ``restart_managed`` (update only) systemd-owned PIDs get their unit restarted after the
kill (systemd treats our SIGTERM as a clean stop, so ``Restart=on-failure`` never fires) and
manual PIDs are respawned from captured argv. PIDs owned by *already_restarted_units* (no
``.service`` suffix) are left untouched, not killed twice.

When *scope_home* is supplied, only processes with that exact live
``HERMES_HOME`` are candidates; unknown ownership fails closed. This is
used by ``dashboard --stop`` and the per-profile update cleanup.

Manually-started dashboards are not auto-restarted because we don't know the original launch args
(--host, --port, --insecure, --tui, --no-open). See #68934.
*already_restarted_units* names units (no ``.service`` suffix) the caller already handled directly â€”
e.g. ``hermes update``'s systemd fleet-restart loop, which restarts, declines (without root) or fails
``hermes-serve*``/``hermes-dashboard*`` units, reporting each, before this function runs. Without excluding them,
a Serve-only install's freshly restarted process is found again here and restarted a second time for
no benefit (review on #83595), and a declined system unit is retried into a polkit prompt (#141021).
©Úmain_dashboardÚ_DASHBOARD_SYSTEMD_UNITzhermes-dashboard.servicez.serviceN)rJ   ro  r*   c                óT   € V ^8„  d   QhR\         R\        \        ,          R,          /# )r   rR   r­   N)r"   r   r   )r   s   "r   r   Ú5_kill_stale_dashboard_processes.<locals>.__annotate__˜  s'   € ÷ pñ pœCð p¬$¬s­)°dÕ*:ñ pr   c                 ó>   <€ SP                  WS\        V 4      R 7      # ))r  )Ú_launchd_job_owning_backendÚ_process_ancestors)rR   r­   Ú_dashÚlaunchd_jobss   &&€€r   Ú_launchd_ownerÚ7_kill_stale_dashboard_processes.<locals>._launchd_owner˜  s"   ø€ Ø×0Ñ0°¸|ÔWiÐjmÓWnÐ0ÓoÐor   r.   u   
âŸ² Stopping z dashboard process(es) (Ú)u       âœ“ stopped PID u       âœ— failed to stop PID ú: )Úpid_launchdr  u'     âš  PID(s) supervised by launchd job zJ: a KeepAlive job restarts itself.
    To keep it down: launchctl bootout c              3   ó,   <"  € T F	  qS9  x € K  	  R # 5ir$   r   )r£   r  r�  s   & €r   r¥   Ú2_kill_stale_dashboard_processes.<locals>.<genexpr>Æ  s   øé € Ð4©V¨˜Ö#«Vùs   ƒTFzM  Restart the dashboard when you're ready:
    hermes dashboard --port <port>r   r   r   Úunrecoveredr   )Ú
hermes_clirt  r   rD   ÚremovesuffixÚ"_restart_managed_dashboard_servicerL   r  Ú_lock_owned_serve_pidsÚ_find_stale_dashboard_pidsr   r9   r:   Ú_loaded_launchd_backend_jobsÚ_get_pid_cgroup_pathÚ_get_systemd_service_for_pidr“   r½   r5   Úprintr@   r4  rl  Ú_restart_killed_backendsr   Úsortedr³   )rq  rm  rn  ro  Ú
_dash_unitÚexcluderë   Ú
pid_cgroupÚpid_serviceÚpid_cmdlinerî   r}  rR   r­   Újobr   r   Úerr_msgr„  r  ÚdÚlrf   Újrí   r{  r|  r�  s   &$$$                     @@@r   Ú_kill_stale_dashboard_processesrš  `  s„  ú€ õ0 3ä”W˜UÐ$=Ð?YÓZÓ[×hÑhÐisÓt€J÷ 	˜J×/LÐ/LÈ"ÔMØ×8Ñ8¸×@Ò@ô #&Ð&=×&CÐ&CÀÓ"DÈ
À|Õ"SÐÜ$Ó&€Gßð 	Ô)Ó+Õ+ˆØ×+Ñ+¸¿¸ÀDÐU_Ð+Ó`€DßÜ‹Ðà(*€JØ)+€KØ:<€KØ(*€KØ&(€Hô <?¿<¹<È7Ô;R�5×5Ñ5Ô7ÐXZ€L÷pñ p÷ œ3Ÿ<™<¨7Ô2ÛˆCØ#×8Ñ8¸Ó=ˆJ‰OØ$×AÑAÀ#ÓFˆKÑØ×ÔÙØ×6Ñ6°sÓ;ˆGß©°sÓ(DÐ!D Ö!DØ#&�˜CÓ ß‘ð
 $+˜CÑ Ü 4°SÓ 9�“ñ ÷ #Ù#'ó 4¡4˜C¯O©O¸CÓ,@×,FÐ,FÀB×+TÑ+TÐU_Ó+`Ø2ñ,3—C�C¡4ˆDð 4çÜ$“Ð&øß	ÛˆCÙ$ S¨%×*JÑ*JÈ3Ó*OÓPÐPˆs×PÐPØ#&�˜CÓ ñ ô 
ˆOœC ›I˜;Ð&>¸v¸hÀaÐ
HÔIØ€FØ$&€FÜŸ<™<¨7Ô2ÕÔ8HÐHÈ$ÐX^Ô_ÛˆÜÐ$ S EÐ*Ö+ñ ã‰ˆˆWÜÐ+¨C¨5°°7°)Ð<Ö=ñ ç—/Ü.Ø ¸(ÐP[ô]‰ô ˜6“lˆô ±&Öj±&¨QÀ+Ç/Á/ÐRSÓBTÐ=T¸QÓ=TœjÐfgÑZaÐZ[Ð]^Ð`a   1 Q C›j±&ÔjÖkˆFÜÐ;¸F¸8ð D<Ø<B¸8ðEö Fñ l÷ ‹3Ô4©VÓ4�3�3Š3Ô4©VÓ4×4Ò4ÜÐbÔcØ”t˜D“z 8¬T°&«\¸8ÄTÈ&Ã\Øœ4 Ó,ð.ð .ùò94ùõ. ks   Æ;OÇOÇ0OË5OÌOr�  c                óÊ  € V ^8„  d   QhR\         \        ,          R\        \        \        R,          3,          R\        \        \        R,          3,          R\        \        \         \        ,          3,          R\        \        \        R,          3,          R\        \        \        \        \        \        R,          3,          3,          R,          R\         \        ,          /# )	r   r   r“  Nr’  r”  rî   r�  r   )r   r"   r   r   r!   )r   s   "r   r   r   Ì  s¨   € ÷ :ñ :Ü”�Ið:Ü$(¬¬c°D­j¨Õ$9ð:ÜGKÌCÔQTÐW[ÕQ[ÈOÕG\ð:ä”cœ4¤�9�nÕ%ð:ä15´c¼3À½:°oÕ1Fð:ô ”cœ5¤¤c¬3°­:Ð!5Õ6Ð6Õ7¸$Õ>ð:ô KOÌsÍ)ñ:r   c               óž  aa€ ^ RI Hp . p. p\        4       p	. p
V  EF«  pVP                  V4      pT;'       g    / P                  V4      pV'       dv   WÉ9   d   K@  V	P	                  V4       VP                  WÂP                  V4      4      '       d   \        RV 24       K‡  VP                  VR34       VP                  V4       K­  V'       d²   Vw  rïpV RV 2pVV	9   d   KÊ  V	P	                  V4       VP                  WïV4      '       d   \        RV 24       EK  VP                  R4      '       d   \        P                  ! 4       ^ 8w  d   RMRpVP                  VR	V R
V 234       VP                  V4       EKf  VS9   d.   V
P                  VSV,          VP                  V4      34       EKš  VP                  V4       EK®  	  V F  w  pp\        RV RV 24       K  	  \        V
4      pV'       d   VP                  V4      MRoS'       d   VP                  VV3R lV  4       4       V'       g	   V'       d   \        R4       V# )zÔUpdate path: restart systemd units, kickstart launchd jobs (macOS), respawn manual argv
(detached, headless, logged to logs/dashboard-restart.log; one per profile, no ``--port 0``).
Returns PIDs not brought back.rs  u"       âœ“ restarted systemd service z#systemctl restart returned non-zeror  u       âœ“ restarted launchd job Úsystemzsudo r.   z1launchd is not supervising a fresh process; run: zlaunchctl kickstart -k u       âš  r€  Nc              3   óX   <"  € T F  pSP                  V4      S9   g   K  Vx € K!  	  R # 5ir$   r¢   )r£   r  Úfailed_cmdsr”  s   & €€r   r¥   Ú+_restart_killed_backends.<locals>.<genexpr>  s#   øé € ÐR¡f °·±ÀÓ0BÀkÑ0QŸ1š1£fùs   ƒ* 
*z[  Restart anything not auto-restarted when you're ready:
    hermes dashboard --port <port>)r…  rt  rL   r5   rü   Ú_try_restart_systemd_servicer�  r%   Ú_restart_launchd_jobr?   rX   Úgeteuidr  Ú_respawn_dashboard_processesrÙ   )r   r“  r’  r”  rî   r�  r{  r„  Úfailed_restartsÚseen_servicesÚrespawn_candidatesrR   Úsvc_nameÚlaunchd_jobÚdomainÚlabelÚold_pidrí   ÚsudoÚsvcÚerrÚrespawn_cmdsrŸ  s   &&&f&$                @r   rŽ  rŽ  Ì  s  ù€ õ 3Ø€KØ-/€OÜ!›e€MØBDÐÜˆØ—?‘? 3Ó'ˆØ"×(Ð( b×-Ñ-¨cÓ2ˆßØÔ(ÙØ×Ñ˜hÔ'Ø×1Ñ1°(¿N¹NÈ3Ó<O×PÒPÜÐ:¸8¸*ÐEÖFà×&Ñ&¨Ð2WÐ'XÔYØ×"Ñ" 3Ö'ßð &1Ñ"ˆF˜7Ø�x˜q  Ð(ˆFØ˜Ô&ÙØ×Ñ˜fÔ%Ø×)Ñ)¨&¸×AÒAÜÐ6°v°hÐ?×@ð #)×"3Ñ"3°H×"=Ò"=Ä"Ç*Â*Ã,ÐRSÔBS‘wÐY[�Ø×&Ñ&ØÐPÐQUÐPVÐVmÐntÐmuÐvÐwôyà×"Ñ" 3×'Ø�KÔØ×%Ñ% s¨K¸Õ,<¸h¿l¹lÈ3Ó>OÐ&P×Qà×Ñ˜s×#ñC óD $‰ˆˆSÜ�˜˜˜R ˜uÐ%Ö&ñ $ä7Ð8JÓK€LßFR�%×4Ñ4°\ÔBÐX\€KßØ×ÑÕR¡fÓRÔRßŸ+ÜÐlÔmØÐr   c                ó$   € V ^8„  d   QhR\         /# r   rå   )r   s   "r   r   r   	  s   € ÷ !ñ !”sñ !r   c                óÄ   €  \        \        V 4      P                  4       4      P                  4       #   \        \
        3 d    \        T 4      P                  4       u # i ; i)z5Canonical lower-cased executable path for comparison.)r   r   rà   Úlowerr]   r&   )rç   s   &r   Ú	_norm_exer´  	  sM   € ð!Ü”4˜“:×%Ñ%Ó'Ó(×.Ñ.Ó0Ð0øÜ”ZÐ ô !Ü�4‹y�‰Ó Ò ð!ús   ‚/2 ²*AÁAÚexclude_pidc          	      ó‚   € V ^8„  d   QhR\         R\        R,          R\        \        \        \        3,          ,          /# )r   Úscripts_dirrµ  Nr   )r   r"   r   r!   r   )r   s   "r   r   r     s5   € ÷ ñ ÜðÜ'*¨T¥zðÜ=AÄ%ÌÌSÈÅ/Õ=Rñr   c               ó    € ^ RI Hp V! 4        R# )zÚHistorical main export: stop old updaters without scanning live shims.

PM stages a fresh generation instead of replacing a mapped hermes.exe.
Returning an empty list would let old callers continue into that mutation.
)Ústop_for_relaunchN)Úhermes_cli._old_updaterr¹  )r·  rµ  r¹  s   &$ r   Ú#_detect_concurrent_hermes_instancesr»    s   € õ :ÙÖr   c                ó0   € V ^8„  d   QhR\         R\        /# )r   r   r   )r   r‘   )r   s   "r   r   r     s   € ÷ Wñ W¬Sð W´Tñ Wr   c                ó¸   € ^ RI Hp V! V 4      R8w  d   R# V P                  4       P                  4       p\	        VR4      pVR9   ;'       d    \	        VR4      R8H  # )u  True for the Desktop-local shape ``hermes serve [--isolated] --host 127.0.0.1 --port 0``.

Long-lived headless serves (``--host <tailscale-ip> --port 9119``) must never match â€”
those are operator-managed remote backends that legitimately run with ppid 1.
rN   rV   Fz--hostrÓ   rÔ   )z	127.0.0.1Ú	localhost)rZ   rO   r³  r=   Ú_flag_value)r   rO   Útokensrm   s   &   r   Ú_is_desktop_local_serve_cmdlinerÁ    s[   € õ Hñ ! Ó)¨WÔ4ÙØ�]‰]‹_×"Ñ"Ó$€FÜ�v˜xÓ(€DØÐ-Ñ-×VÐV´+¸fÀhÓ2OÐSVÑ2VÐVr   c                ó`   € V ^8„  d   QhR\         \        ,          R\        R\        R,          /# )r   rÀ  Úflagr   NrÊ   )r   s   "r   r   r   ,  s)   € ÷ ñ œœS�	ð ¬ð ´°tµñ r   c                óú   € \        V 4       Fk  w  r#W18X  d)   V^,           \        V 4      8  d   W^,           ,          u # VP                  VR,           4      '       g   KS  VP                  R4      ^,          u # 	  R# )z=``--flag value`` / ``--flag=value`` from split argv, or None.rÍ   N)rÆ   r@   r?   rz   )rÀ  rÃ  rÂ   rÃ   s   &&  r   r¿  r¿  ,  s_   € ä˜FÖ#‰ˆØŒ;˜1˜q�5¤3 v£;Ô.Ø˜a�%•=Ò Ø�>‰>˜$ �*×%Ô%Ø—=‘= Ó% aÕ(Ò(ñ	 $ñ
 r   Ú	max_depthc                óR   € V ^8„  d   QhR\         R\         R\        \         ,          /# )r   rR   rÅ  r   )r"   r   )r   s   "r   r   r   6  s%   € ÷ 
ñ 
œCð 
¬sð 
¼4Ä½9ñ 
r   c               ó¢   € . pT p\        V4      V8  d;   \        V4      pVe   V^8:  g   W@8X  g   WB9   d    V# VP                  V4       TpKJ  V# )zYParent chain of *pid* (nearest first), stopping at init / a lookup failure / *max_depth*.)r@   Ú_process_ppidr%   )rR   rÅ  ÚchainÚcurrentr°   s   &$   r   rz  rz  6  sW   € à€EØ€GÜ
ˆe‹*�yÔ
 Ü˜wÓ'ˆØŠ>˜V qœ[¨F¬M¸V¼_Øð €Lð 	�‰�VÔØŠØ€Lr   c                ó>   € V ^8„  d   QhR\         R\         R,          /# rq   rT  )r   s   "r   r   r   C  s   € ÷ 
ñ 
”sð 
œs T�zñ 
r   c           	     ó¢  €  \         P                  R8X  d   R# \        P                  ! RRRR\	        V 4      .3R^/\
        BRR	/B pVP                  ^ 8w  g   VP                  '       g   R# \        VP                  P                  4       P                  4       ^ ,          4      #   \        \        \        P                  \        3 d     R# i ; i)
zZBest-effort parent pid; None on failure (always None on Windows: desktop tree-kill reaps).r*   Nr6   r8   zppid=rÏ   r-   r0   F)r9   r:   rA   rB   r   rC   r<   r1   r"   r>   r=   r&   r[   r\   r]   )rR   rE   s   & r   rÈ  rÈ  C  s¥   € ðÜ�<‰<˜7Ô"ÙÜ—’  t¨W°d¼CÀ»HÐ EÑpÈqÐpÔTbÑpÐjoÒpˆØ×Ñ Ô!¨¯¯¨ÙÜ�6—=‘=×&Ñ&Ó(×.Ñ.Ó0°Õ3Ó4Ð4øÜÔ)¬:×+DÑ+DÄgÐNô Úðús   ‚B% ™AB% Á-7B% Â%%CÃCzdesktop-sshzbackend.lock.jsonÚ0123456789abcdefc                ó$   € V ^8„  d   QhR\         /# r   r   )r   s   "r   r   r   [  s   € ÷ %ñ %œ$ñ %r   c                 ó   € ^ RI Hp  V ! 4       # )z“The process's Hermes home: remote-backend locks are a process-level asset, so a request scoped
to another profile must still see the same lock dir.©Úget_process_hermes_home)rû   rÑ  rÐ  s    r   Ú_hermes_home_dirrÒ  [  s   € õ 9Ù"Ó$Ð$r   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r   r�   Úlengthr   )Úobjectr"   r‘   )r   s   "r   r   r   b  s&   € ÷ Yñ Y”6ð Y¤3ð Y¬4ñ Yr   c                 ó”   € \        V \        4      ;'       d2    \        V 4      V8H  ;'       d    \        V 4      \        ,
          '       * # r$   )rb   r   r@   rL   Ú_HEX32)r�   rÔ  s   &&r   Ú_is_hexrØ  b  s5   € Ü�eœSÓ!×XÐX¤c¨%£j°FÑ&:×XÐXÄCÈÃJÔQWÕDWÔ?XÐXr   c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r   ÚparsedÚownership_idr   )rÕ  r   r‘   )r   s   "r   r   r   f  s&   € ÷ Uñ U¤Fð U¼#ð UÄ$ñ Ur   c                ó  a € \        S \        4      '       d�   S P                  R4      \        8w  gs   S P                  R4      \        8w  gY   S P                  R4      V8w  gC   \        S P                  R4      ^4      '       d"   \        S P                  R4      ^ 4      '       g   R# S P                  R4      S P                  R4      r2\        V\        4      '       d=   ^ Tu;8  d   R	8:  d.   M R# \        V\        4      '       d   ^ Tu;8:  d
   R
8:  g    R#  R# \        ;QJ d    V 3R lR 4       F  '       g   K   RM	  RM! V 3R lR 4       4      '       d   R# S R,          P                  RV RS R,           R24      # )zGValidate a parsed ``backend.lock.json`` body, mirroring readLockfile().ÚschemaVersionÚprotocolVersionÚownershipIdÚ
spawnNonceÚtokenFingerprintFrR   rl   i  @ iÿÿ  c              3   ó¦   <"  € T FF  p\        SP                  V4      \        4      '       * ;'       g    \        SV,          4      R 8„  x € KH  	  R# 5i)i   N)rb   r5   r   r@   )r£   rh  rÚ  s   & €r   r¥   Ú*_valid_lockfile_payload.<locals>.<genexpr>u  sC   øé € ð UÙSˆqô ˜&Ÿ*™* Q›-¬Ó-Ô-×FÐF´°V¸AµY³À$Ñ1FÔFÛSùs
   ƒ/A³AÚlogPathTr  z.log)rµ   Ú
hermesPathÚ
hermesHomerä  Ú	startedAt)	rb   r   r5   Ú_LOCKFILE_SCHEMA_VERSIONÚ_PROTOCOL_VERSIONrØ  r"   r³   Úendswith)rÚ  rÛ  rR   rl   s   f&  r   Ú_valid_lockfile_payloadrë  f  s8  ø€ ô �vœt×$Ò$Ø�:‰:�oÓ&Ô*BÔBØ�:‰:Ð'Ó(Ô,=Ô=Ø�:‰:�mÓ$¨Ô4Ü�v—z‘z ,Ó/°×4Ò4Ü�v—z‘zÐ"4Ó5°r×:Ò:ÙØ—
‘
˜5Ó! 6§:¡:¨fÓ#5ˆÜ�sœC× Ò  Q¨Ö%7°Ö%7áô =GÀtÌS×<QÒ<QØ�TÖ"˜UÕ"Ùð #Ùç
ƒsô UÙSóU‡s‡s‚sô UÙSóU÷ Uò Uáà�)Õ×%Ñ%¨¨,¨°q¸ÀÕ9MÐ8NÈdÐ&SÓTÐTr   c                óT   € V ^8„  d   QhR\         R,          R\        \         ,          /# ©r   Úbase_dirNr   )r   r   )r   s   "r   r   r   |  s"   € ÷ ñ ¤¨¥ð ´´dµñ r   c                ó    € V e   V .# ^ RI Hp . p\        4       V! 4       3 F)  pV\        ,          pWB9  g   K  VP	                  V4       K+  	  V# )u¥  Every dir the Desktop may have written ``desktop-ssh/<ownershipId>/backend.lock.json`` under.

The Desktop writes SSH locks beneath the ROOT home (``~/.hermes/desktop-ssh``), but a profile
backend (``hermes --profile X serve``) runs with ``HERMES_HOME=<root>/profiles/X`` â€” scanning only
the process home found no lock there and its reaper killed the sibling profile's live SSH
backend on every profile switch (#89811).)Úget_default_hermes_root)rû   rð  rÒ  Ú_REMOTE_LOCK_SUBDIRr%   )rî  rð  r<  r²   rº   s   &    r   Ú_remote_lock_rootsrò  |  sU   € ð ÒØˆzÐÝ8Ø€EÜ!Ó#Ñ%<Ó%>Ó?ˆØÔ)Õ)ˆØÖØ�L‰L˜Öñ @ð €Lr   c                óT   € V ^8„  d   QhR\         R,          R\        \        ,          /# rí  )r   rL   r"   )r   s   "r   r   r   Ž  s"   € ÷ ñ ¤T¨D¥[ð ¼CÄ½Hñ r   c                óP  € \        4       p. p\        V 4       F;  p TP                  VP                  4       '       d   VP	                  4       M. 4       K=  	  V F7  p\        V\        ,          4      pVf   K  VP                  VR,          4       K9  	  V#   \
         d     KŒ  i ; i)z™PIDs claimed by valid ``{hermes_home}/desktop-ssh/<ownershipId>/backend.lock.json`` records
(best-effort: a bad record contributes no PID; never raises).rR   )	rL   rò  rÙ   Úis_dirÚiterdirr]   Úread_valid_backend_lockÚBACKEND_LOCK_NAMErü   )rî  ÚownedÚentriesrº   rh   Úlocks   &     r   rˆ  rˆ  Ž  s�   € ô “e€EØ€GÜ" 8Ö,ˆð	Ø�N‰N¨T¯[©[¯]ª]˜4Ÿ<™<œ>ÀÖCñ -ó
 ˆÜ& uÔ/@Õ'@ÓAˆØÔØ�I‰I�d˜5•kÖ"ñ ð €Løô ô 	Úð	ús   œ7BÂB%Â$B%c                ó>   € V ^8„  d   QhR\         R\        R,          /# )r   Ú	lock_pathr   N)r   r   )r   s   "r   r   r   Ÿ  s"   € ÷ Mñ M¤tð M´°tµñ Mr   c                óÂ  € ^ RI pV P                  P                  p \        V^ 4      '       d   V P	                  4       '       g   R# V P                  R4      ;_uu_ 4       pVP                  R4      pRRR4       \        X4      R8”  d   R# VP                  V4      p\        YR4      '       d   T# R#   + '       g   i     LH; i  \        \        \        3 d     R# i ; i)z²The validated body of one ``desktop-ssh/<ownershipId>/backend.lock.json``, or None when it is
missing, unreadable, oversized or does not match the writer's schema (never raises).NÚrbi  i   )Újsonr°   r±   rØ  Úis_fileÚopenÚreadr@   Úloadsr]   ÚUnicodeDecodeErrorr&   rë  )rý  r   rÛ  rh  ÚdatarÚ  s   &     r   r÷  r÷  Ÿ  s¶   € ó Ø×#Ñ#×(Ñ(€Lð	Ü�| R×(Ò(°	×0AÑ0A×0CÒ0CÙØ�^‰^˜D×!Ô! QØ—6‘6˜%“=ˆD÷ "äˆt‹9�uÔÙØ—‘˜DÓ!ˆô -¨V×BÒBˆ6ÐLÈÐL÷ "×!ûô
 Ô'¬Ð4ô Úðús5   œ'C ÁC ÁB1Á1C ÂC Â1C	Â<C ÃCÃCg     €f@c                ó0   € V ^8„  d   QhR\         R\        /# ©r   rR   r   )r"   rG  )r   s   "r   r   r   µ  s   € ÷ Gñ Gœcð G¤eñ Gr   c                óŽ   € ^ RI p^ RIp\        RVP                  4       VP                  V 4      P	                  4       ,
          4      # )z9Process age from psutil's cross-platform start timestamp.Ng        )rJ  rv   Úmaxrw   Úcreate_time)rR   rM  Ú_psutils   &  r   Ú_process_age_secondsr  µ  s2   € ããÜˆs�E—J‘J“L 7§?¡?°3Ó#7×#CÑ#CÓ#EÕEÓFÐFr   rq  z#orphaned desktop-local hermes serveÚsignal_termÚsignal_killÚsleep_fnÚlock_owned_pids_fnÚprocess_age_seconds_fnc                óR   € V ^8„  d   QhR\         R\        \         \        3,          /# )r   rq  r   )r   r   r   )r   s   "r   r   r   ½  s/   € ÷ SDñ SDÜðSDäLPÔQTÔVZÐQZÍOñSDr   c                ó¦  aa€ ^ RI p^ RIpVf   VP                  MTpVf   \        VRVP                  4      MTpT;'       g    VP                  pS;'       g    \
        oS;'       g    \        o\        P                  R8X  d   \        4       # R V3R llpR V3R llp	\        4       \        P                  ! 4       0,          V! 4       ,          p
\        P                  ! \        4      ;_uu_ 4        V
P!                  \        P"                  ! 4       4       RRR4        \%        V
R7      pT! 4       pT UUu. uFD  w  rÞ\'        T4      '       g   K  YÜ9  g   K   \)        T4      R9   g   K2  T	! T4      '       g   KB  TNKF  	  pppT'       g   \        4       # \+        T4      p. p. pT F  p \        P,                  ! YÑ4       K  	  T! R	4       ^ RIpT FK  pTT9   d   K   TP7                  T4      '       d   \        P,                  ! YÒ4       TP3                  T4       KM  	  ^ R
IHp TP=                  4        Fh  w  pw  ppTT9  g   K  Tf   K  T! T4      T8X  g   K'  \        P                  ! \0        4      ;_uu_ 4        \        P,                  ! YÒ4       RRR4       Kj  	  \        P                  ! \        4      ;_uu_ 4        \?        R\A        T4       RT  RT;'       g    T 24       RRR4       RTRTRT/#   + '       g   i     ELñ; i  \         d    \        4       u # i ; iu uppi   \.         d     EK›  \0         d    TP3                  T4        EKº  i ; i  \.         d    TP3                  T4        EK²  \0         d    TP3                  T4        EKÑ  i ; i  + '       g   i     EK‚  ; i  + '       g   i     L×; i)a   Kill leftover Desktop-local ``hermes serve`` backends with no parent. Never raises.

When Electron dies uncleanly its ``serve --host 127.0.0.1 --port 0`` children are
reparented to pid 1 with their MCP trees alive; each Desktop boot then stacks a fresh
backend on the corpses until EMFILE. Reaped only if ALL hold: Desktop-local shape; ppid
0/1; not self / parent / HERMES_DESKTOP_CHILD_PID; not claimed by a valid
``backend.lock.json`` (SSH backends other clients started legitimately sit at ppid 1);
older than ``_REAP_MIN_AGE_SECONDS`` with a determinable age (Desktop writes the lock only
after HERMES_BACKEND_READY, so a live sibling mid-startup is briefly unowned).
NrX  r*   c                ó:   € V ^8„  d   QhR\         \        ,          /# r   r  )r   s   "r   r   Ú9_reap_orphaned_desktop_local_serves.<locals>.__annotate__Ô  s   € ÷ ñ œœS�ñ r   c                  ó\   <€  \        S ! 4       4      #   \         d    \        4       u # i ; ir$   )rL   r`   )r  s   €r   Ú_owned_pidsÚ8_reap_orphaned_desktop_local_serves.<locals>._owned_pidsÔ  s-   ø€ ð	ÜÑ)Ó+Ó,Ð,øÜô 	Ü“5ŠLð	ús   ƒ “+ª+c                ó0   € V ^8„  d   QhR\         R\        /# r  )r"   r‘   )r   s   "r   r   r  Ú  s   € ÷ ñ œcð ¤dñ r   c                 óH   <€  S! V 4      \         8¬  #   \         d     R # i ; i)F)Ú_REAP_MIN_AGE_SECONDSr`   )rR   r  s   &€r   Ú_is_stale_orphanÚ=_reap_orphaned_desktop_local_serves.<locals>._is_stale_orphanÚ  s+   ø€ ð	Ù)¨#Ó.Ô2GÑGÐGøÜô 	Úð	ús   ƒ ’! !)rJ   g      ø?r'  u   âŸ² Reaped z* orphaned desktop-local serve backend(s) (z): r   r   r   )é    é   )!rb  rJ  rc  rD   rL  rˆ  r  r9   r:   r   r  rX   rY   r^   r_   r`   rü   Úgetppidri   rÁ  rÈ  rD  rW  rY  r]   r%   rv   Ú
pid_existsr/  r(  re  r�  r@   )rq  r  r  r  r  r  r\  rM  r  r  r‘  ÚscannedÚ	owned_nowrR   rd   r   rg  r   r   rv   r(  rº   rj  s   $$$$dd                 r   Ú#_reap_orphaned_desktop_local_servesr%  ½  s  ù€ ó ÛØ%0Ò%8�'—/’/¸k€KØBMÒBU”'˜' 9¨g¯o©oÔ>Ð[f€KØ×&Ð&˜5Ÿ;™;€HØ+×EÐEÔ/EÐØ3×KÐKÔ7KÐÜ
‡|�|�wÔÜ‹Ð÷ð ÷ð ô %Ó&¬"¯)ª)«+¨Õ6¹»ÕF€GÜ	×	Ò	œY×	'Õ	'Ø�‰”B—J’J“LÔ!÷ 
(ðÜ+¸ÔAˆñ “€IÙ#*ô K¡7‘x�sÜ1°#×6ô Ø;>Ñ;Oô ä  Ó%¨Ñ/ô á4DÀS×4I÷ ˆs¡7€Gñ K÷ Ü‹ÐÜ$ WÓ-€KØ€FØ€FÛˆð	Ü�GŠG�CÖ%ñ ñ ˆS„MÛÛˆØ�&Œ=Ùð	Ø× Ñ  ×%Ò%Ü—’˜Ô)Ø�M‰M˜#Öñ õ 6Ø)×/Ñ/Ö1Ñˆ‰]ˆd�EØ�vÖ %Ô"3Ñ8NÈsÓ8SÐW\Ö8\Ü×$Ò$¤W×-Õ-Ü—’˜Ô)÷ .Ñ-ñ 2ô 
×	Ò	œY×	'Õ	'Ü�œC ›K˜=Ð(RÐSYÐRZÐZ]Ð^d×^oÐ^oÐhoÐ]pÐqÔr÷ 
(à�w ¨&°(¸FÐCÐC÷_ 
(×	'Ð	'ûô ô Ü‹ÒðüóKøô "ô 	ÛÜô 	Ø�M‰M˜#×Ðð	ûô "ô 	Ø�M‰M˜#×ÐÜô 	Ø�M‰M˜#×Ðð	ú÷ .×-Ñ-úç	'×	'ús–   Ã4%LÄ"L Ä:L0ÅL0ÅL0Å.L0Å>L0Æ.L6Ç#M(Ç:'M(ÊN+Ë	(O ÌL	ÌL-Ì,L-Ì6M%ÍM%ÍM%Í$M%Í(N(ÎN(ÎN(Î'N(Î+N=Ï O	r$   >   ÚshÚashÚcshr}   ÚkshÚzshÚbashÚdashÚfishÚnicer­  ÚtcshÚtmuxÚnohupÚwatchÚxargsÚscreenÚstdbufr-   )Ú?z??Ú-)z:the running backend no longer matches the updated frontend)AÚ__doc__r^   rX   rA   r9   Úpathlibr   Úhermes_cli._startup_fastr   r   rC   r   r'   rI   ri   ro   r‚   rŽ   r½   rÈ   r®   rÖ   rÜ   rã   ré   rï   rõ   r  r  Ú	frozensetr!  r  r  r"  r4  rd  rf  r8  r:  rD  rP  rl  rš  rŽ  r´  r»  rÁ  r¿  rz  rÈ  rè  ré  rñ  rø  rL   r×  rÒ  rØ  rë  rò  rˆ  r÷  r  r  r%  r   r   r   Ú<module>r<     s  ðñó Û 	Û Û 
Ý å @á T°¸wÈyÔY€õ7õõ%ðP%À÷ %õPõ"õ"ð.8OÀe÷ 8OõvZõõ
.õ,õ&õ7õ÷"?ð )ØSW÷)õXñ #ò $ó Ð õõ@+õ$
Eõ)ðB !Ð ð
 #&Ð Ø
€õõ.%õPõ)(ñXi.à!ði.àNRði.ð "÷i.ð i.ðX:ð BF÷:õz!ðØ48÷õWõ ð
°Q÷ 
õ
ð" Ð ØÐ Ø#Ð Ø'Ð Ù	ÐÓ	 €õ%õYõUõ,÷$õ"Mð& Ð õGðSDØ:ðSDØHLðSDØZ^ðSDàðSDà&*ðSDàCG÷SDñ SDr   