+
    Ji0                        R t ^ RIt^ RIt^ RIt^ RIt^ RIHtHt ^ RIHt ^ RI	H
t
 ^ RIHtHt RtRtRtR	 R
 ltR R ltR R ltR R ltR R ltR R ltR R lt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R% R& ltR' R( lt R)RR*RR+RR,RR-RR.R/R/ R0 llt!R1 R2 lt"R3 R4 lt#R?R5 R6 llt$R7 R8 lt%R9 R: lt&R; R< lt'R= R> lt(R# )@u  
Gateway runtime status helpers.

Provides PID-file based detection of whether the gateway daemon is running,
used by send_message's check_fn to gate availability in the CLI.

The PID file lives at ``{HERMES_HOME}/gateway.pid``.  HERMES_HOME defaults to
``~/.hermes`` but can be overridden via the environment variable.  This means
separate HERMES_HOME directories naturally get separate PID files — a property
that will be useful when we add named profiles (multiple agents running
concurrently under distinct configurations).
N)datetimetimezonePathget_hermes_home)AnyOptionalzhermes-gatewayzgateway_state.jsonzgateway-locksc                $    V ^8  d   QhR\         /#    returnr   )formats   "+/home/ubuntu/hermes-agent/gateway/status.py__annotate__r      s        t      c                 (    \        4       p V R,          # )z@Return the path to the gateway PID file, respecting HERMES_HOME.zgateway.pidr   )homes    r   _get_pid_pathr      s    D-r   c                $    V ^8  d   QhR\         /# r   r   )r   s   "r   r   r   "   s     ; ;$ ;r   c                 <    \        4       P                  \        4      # )z5Return the persisted runtime health/status file path.)r   	with_name_RUNTIME_STATUS_FILE r   r   _get_runtime_status_pathr   "   s    ?$$%9::r   c                $    V ^8  d   QhR\         /# r   r   )r   s   "r   r   r   '   s     2 2t 2r   c                     \         P                  ! R4      p V '       d   \        V 4      # \        \         P                  ! R\        P                  ! 4       R,          R,          4      4      pVR,          \        ,          # )zBReturn the machine-local directory for token-scoped gateway locks.HERMES_GATEWAY_LOCK_DIRXDG_STATE_HOMEz.localstatehermes)osgetenvr   r   _LOCKS_DIRNAME)override
state_homes     r   _get_lock_dirr&   '   sT    yy23HH~bii 0$))+2H72RSTJ >11r   c                $    V ^8  d   QhR\         /# r   str)r   s   "r   r   r   0   s     2 2c 2r   c                  f    \         P                  ! \        P                  4      P	                  4       # N)r   nowr   utc	isoformatr   r   r   _utc_now_isor/   0   s    <<%//11r   c                0    V ^8  d   QhR\         R\         /# )r   identityr   r(   )r   s   "r   r   r   4   s     E E# E# Er   c                 v    \         P                  ! V P                  R 4      4      P                  4       R,          # )utf-8:N   N)hashlibsha256encode	hexdigest)r1   s   &r   _scope_hashr9   4   s(    >>(//'23==?DDr   c                <    V ^8  d   QhR\         R\         R\        /# )r   scoper1   r   )r)   r   )r   s   "r   r   r   8   s&     E E Es Et Er   c                 B    \        4       V  R \        V4       R2,          # )-z.lock)r&   r9   )r;   r1   s   &&r   _get_scope_lock_pathr>   8   s"    ?waH(='>eDDDr   c                F    V ^8  d   QhR\         R\        \         ,          /# r   pidr   )intr	   )r   s   "r   r   r   <   s       # r   c                    \        RV  R24      p \        VP                  4       P                  4       ^,          4      #   \        \
        \        \        \        3 d     R# i ; i)z:Return the kernel start time for a process when available./proc/z/statN)	r   rB   	read_textsplitFileNotFoundError
IndexErrorPermissionError
ValueErrorOSError)rA   	stat_paths   & r   _get_process_start_timerM   <   sY    vcU%()I9&&(..0455z?JP s   -?  A#"A#c                F    V ^8  d   QhR\         R\        \        ,          /# r@   )rB   r	   r)   )r   s   "r   r   r   F   s"     
O 
Os 
Ox} 
Or   c                    \        RV  R24      p VP                  4       pT'       g   R# TP                  RR4      P                  RRR7      P                  4       #   \        \        \        3 d     R# i ; i)	z<Return the process command line as a space-separated string.rD   z/cmdlineN        r3   ignore)errors)r   
read_bytesrG   rI   rK   replacedecodestrip)rA   cmdline_pathraws   &  r   _read_process_cmdlinerZ   F   su    &X./L%%' ;;w%,,WX,FLLNN 8 s   A A65A6c                0    V ^8  d   QhR\         R\        /# r@   )rB   bool)r   s   "r   r   r   S   s     ; ;S ;T ;r   c                   a \        V 4      oS'       g   R# Rp\        ;QJ d    V3R lV 4       F  '       g   K   R# 	  R# ! V3R lV 4       4      # )zBReturn True when the live PID still looks like the Hermes gateway.Fc              3   ,   <"   T F	  qS9   x  K  	  R # 5ir+   r   .0patterncmdlines   & r   	<genexpr>._looks_like_gateway_process.<locals>.<genexpr>_        :g'!   Tzhermes_cli.main gatewayzhermes_cli/main.py gatewayzhermes gatewayzgateway/run.py)rZ   any)rA   patternsrb   s   & @r   _looks_like_gateway_processrj   S   sE    #C(GH 3::33:3:3::::r   c                R    V ^8  d   QhR\         \        \        3,          R\        /# )r   recordr   )dictr)   r   r\   )r   s   "r   r   r   b   s"     ; ;tCH~ ;$ ;r   c                F  a V P                  R4      \        8w  d   R# V P                  R4      p\        V\        4      '       d	   V'       g   R# RP	                  R V 4       4      oRp\
        ;QJ d    V3R lV 4       F  '       g   K   R# 	  R# ! V3R lV 4       4      # )	zMValidate gateway identity from PID-file metadata when cmdline is unavailable.kindFargv c              3   8   "   T F  p\        V4      x  K  	  R # 5ir+   r(   )r`   parts   & r   rc   -_record_looks_like_gateway.<locals>.<genexpr>k   s     2TTs4yyTs   c              3   ,   <"   T F	  qS9   x  K  	  R # 5ir+   r   r_   s   & r   rc   rt   r   re   rf   Trg   )get_GATEWAY_KIND
isinstancelistjoinrh   )rl   rp   ri   rb   s   &  @r   _record_looks_like_gatewayr{   b   s}    zz&]*::fDdD!!hh2T22GH 3::33:3:3::::r   c                $    V ^8  d   QhR\         /# r   )rm   )r   s   "r   r   r   u   s      4 r   c                      R \         P                  ! 4       R\        R\        \        P
                  4      R\        \         P                  ! 4       4      /# )rA   ro   rp   
start_time)r!   getpidrw   ry   sysrp   rM   r   r   r   _build_pid_recordr   u   s:    ryy{SXX-biik:	 r   c                F    V ^8  d   QhR\         \        \        3,          /# r   )rm   r)   r   )r   s   "r   r   r   ~   s      d38n r   c                  \    \        4       p V P                  R RRRR/ R\        4       /4       V # )gateway_statestartingexit_reasonN	platforms
updated_at)r   updater/   )payloads    r   _build_runtime_status_recordr   ~   s8    !GNNtRln	  Nr   c                h    V ^8  d   QhR\         R\        \        \        \        3,          ,          /# )r   pathr   )r   r	   rm   r)   r   )r   s   "r   r   r      s'     : :$ :8DcN#; :r   c                 B   V P                  4       '       g   R #  V P                  4       P                  4       pT'       g   R #  \        P
                  ! T4      p\        T\        4      '       d   T# R #   \         d     R # i ; i  \        P                   d     R # i ; ir+   )	existsrE   rW   rK   jsonloadsJSONDecodeErrorrx   rm   )r   rY   r   s   &  r   _read_json_filer      s    ;;==nn$$& **S/ !$//79T9    s#   A3 B 3BBBBc                V    V ^8  d   QhR\         R\        \        \        3,          RR/# )r   r   r   r   N)r   rm   r)   r   )r   s   "r   r   r      s)     ) )4 )$sCx. )T )r   c                     V P                   P                  R R R7       V P                  \        P                  ! V4      4       R# )Tparentsexist_okN)parentmkdir
write_textr   dumps)r   r   s   &&r   _write_json_filer      s.    KKdT2OODJJw'(r   c                :    V ^8  d   QhR\         \        ,          /# r   )r	   rm   )r   s   "r   r   r      s      (4. r   c                     \        4       p V P                  4       '       g   R # V P                  4       P                  4       pV'       g   R #  \        P
                  ! V4      p\        T\        4      '       d   RT/# \        T\        4      '       d   T# R #   \        P                   d%     R\        T4      /u #   \         d      R # i ; ii ; i)NrA   )r   r   rE   rW   r   r   r   rB   rJ   rx   rm   )pid_pathrY   r   s      r   _read_pid_recordr      s    H??




$
$
&C**S/ '3w'4    	3s8$$ 		s0   B C,B;8C;CC
CCc                    V ^8  d   QhRR/# r   r   Nr   )r   s   "r   r   r      s     ; ; ;r   c                 >    \        \        4       \        4       4       R# )zCWrite the current process PID and metadata to the gateway PID file.N)r   r   r   r   r   r   write_pid_filer      s    ]_&7&9:r   r   r   platformplatform_state
error_codeerror_messagec                    V ^8  d   QhR\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          RR/# )	r   r   r   r   r   r   r   r   N)r	   r)   )r   s   "r   r   r      sh     "$ "$C="$ #"$ sm	"$
 SM"$ "$ C="$ 
"$r   c                    \        4       p\        V4      ;'       g    \        4       pVP                  R/ 4       VP                  R\        4       \
        P                  ! 4       VR&   \        \
        P                  ! 4       4      VR&   \        4       VR&   V e   WR&   Ve   WR&   VeJ   VR,          P                  V/ 4      pVe   W8R	&   Ve   WHR
&   Ve   WXR&   \        4       VR&   WR,          V&   \        Wg4       R# )zBPersist gateway runtime health information for diagnostics/status.r   ro   rA   r~   r   Nr   r   r   r   r   )r   r   r   
setdefaultrw   r!   r   rM   r/   rv   r   )	r   r   r   r   r   r   r   r   platform_payloads	   $$$$$$   r   write_runtime_statusr      s     $%Dd#EE'C'EG{B'v}-YY[GEN3BIIK@GL(NGL #0 !,";/33HbA%(6W%!-7\*$0=_-)5&)9X&T#r   c                \    V ^8  d   QhR\         \        \        \        3,          ,          /# r   )r	   rm   r)   r   )r   s   "r   r   r      s      7 7Xd38n5 7r   c                 (    \        \        4       4      # )z=Read the persisted gateway runtime health/status information.)r   r   r   r   r   read_runtime_statusr      s    3566r   c                    V ^8  d   QhRR/# r   r   )r   s   "r   r   r      s       r   c                 `     \        4       P                  RR7       R#   \         d     R# i ; i)z)Remove the gateway PID file if it exists.T
missing_okN)r   unlink	Exceptionr   r   r   remove_pid_filer      s,    $/ s    --c                    V ^8  d   QhR\         R\         R\        \        \         \        3,          ,          R\        \
        \        \        \         \        3,          ,          3,          /# )r   r;   r1   metadatar   )r)   r	   rm   r   tupler\   )r   s   "r   r   r      ss     M Ms Mc MXd3PS8n=U Mafgkmuvz{~  AD  |D  wE  nF  hF  bG Mr   c           
     f   \        W4      pVP                  P                  RRR7       / \        4       CRT R\	        V4      RT;'       g    / R\        4       /Cp\        V4      pV'       Edm    \        VR,          4      pV\        P                  ! 4       8X  d5   VP                  R	4      VP                  R	4      8X  d   \        W44       RV3# VRJ pV'       g    \        P                  ! V^ 4       \!        V4      pVP                  R	4      e   Ve   WP                  R	4      8w  d   RpV'       g    \#        R
V R24      p	V	P%                  4       '       d_   V	P'                  4       P)                  4        F<  p
V
P+                  R4      '       g   K  V
P-                  4       ^,          pVR9   d   Rp M	  M V'       d    VP5                  RR7       MRV3#  \        P6                  ! V\        P8                  \        P:                  ,          \        P<                  ,          4      p \        P@                  ! TRRR7      ;_uu_ 4       p\B        PD                  ! YM4       RRR4       R#   \        \        \        3 d    Rp ELi ; i  \.        \0        3 d     Li ; i  \2        \0        3 d    Rp Li ; i  \.         d     Li ; i  \>         d    R\        T4      3u # i ; i  + '       g   i     R# ; i  \F         d'     TP5                  RR7       h   \.         d     h i ; ii ; i)zAcquire a machine-local lock keyed by scope + identity.

Used to prevent multiple local gateways from using the same external identity
at once (e.g. the same Telegram bot token across different HERMES_HOME dirs).
Tr   r;   identity_hashr   r   rA   Nr~   rD   z/statuszState:r   Fwr3   )encoding)Tt)TN)$r>   r   r   r   r9   r/   r   rB   KeyError	TypeErrorrJ   r!   r   rv   r   killrM   r   r   rE   
splitlines
startswithrF   rK   rI   ProcessLookupErrorr   openO_CREATO_EXCLO_WRONLYFileExistsErrorfdopenr   dumpr   )r;   r1   r   	lock_pathrl   existingexisting_pidstalecurrent_start_proc_status_line_statefdhandles   &&&           r   acquire_scoped_lockr      s    %U5I4$7

X.HNNlnF y)Hx	 x/L 299;&8<<+ET`Ia+aY/>!$a( !8 ELL.:%1%l)CC E 
'+f\N',J'K'..00)5)?)?)A)L)L)N#(#3#3H#=#=-2[[]1-=F'-';04$) *O	    D 1 (?"1WWY

RYY 6 DEYYr311VIIf% 2 s )Z0 	 L	 D $_5 / '8 8    1oi0001 21   	- 	  		s   /I- J# 5AJ !J 6J J< AK +!K? K+#K? -J	J	J J #J98J9<K
	K
K('K(+K<	6K? <K? ?L0LL0L,)L0+L,,L0c                4    V ^8  d   QhR\         R\         RR/# )r   r;   r1   r   Nr(   )r   s   "r   r   r   =  s!      s c d r   c                N   \        W4      p\        V4      pV'       g   R# VP                  R4      \        P                  ! 4       8w  d   R# VP                  R4      \        \        P                  ! 4       4      8w  d   R#  VP                  RR7       R#   \         d     R# i ; i)zDRelease a previously-acquired scope lock when owned by this process.NrA   r~   Tr   )r>   r   rv   r!   r   rM   r   rK   )r;   r1   r   r   s   &&  r   release_scoped_lockr   =  s~    $U5Iy)H||Ebiik)||L!%<RYY[%IID) s   B B$#B$c                $    V ^8  d   QhR\         /# r   )rB   )r   s   "r   r   r   M  s      # r   c                     \        4       p ^ pV P                  4       '       d5   V P                  R4       F  p VP                  RR7       V^,          pK!  	  V#   \         d     K3  i ; i)zRemove all scoped lock files in the lock directory.

Called during --replace to clean up stale locks left by stopped/killed
gateway processes that did not release their locks gracefully.
Returns the number of lock files removed.
z*.lockTr   )r&   r   globr   rK   )lock_dirremoved	lock_files      r   release_all_scoped_locksr   M  sj     HG!x0I  D 11 1 N  s   AA('A(c                :    V ^8  d   QhR\         \        ,          /# r   )r	   rB   )r   s   "r   r   r   `  s     " "# "r   c                    \        4       p V '       g   \        4        R#  \        V R,          4      p \        P                  ! T^ 4       T P                  R4      p\        T4      pTe   Te   Y28w  d   \        4        R# \        T4      '       g   \        T 4      '       g   \        4        R# T#   \        \        \
        3 d    \        4         R# i ; i  \        \        3 d    \        4         R# i ; i)zReturn the PID of a running gateway instance, or ``None``.

Checks the PID file and verifies the process is actually alive.
Cleans up stale PID files automatically.
NrA   r~   )r   r   rB   r   r   rJ   r!   r   r   rI   rv   rM   rj   r{   )rl   rA   recorded_startr   s       r   get_running_pidr   `  s     F&- 

Q
 ZZ-N+C0M!m&?MDc&s++)&11J+ i,  0 s"   B0 C 0 CCC65C6c                $    V ^8  d   QhR\         /# r   )r\   )r   s   "r   r   r     s     ) )D )r   c                     \        4       RJ# )z1Check if the gateway daemon is currently running.N)r   r   r   r   is_gateway_runningr     s    D((r   r+   ))__doc__r5   r   r!   r   r   r   pathlibr   hermes_constantsr   typingr   r	   rw   r   r#   r   r   r&   r/   r9   r>   rM   rZ   rj   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>r      s      	 
 '  ,   +   ;
22EE
O;;&: )
0;
"$#'"$ "&"$ #	"$
 %)"$ !%"$ $("$J7
M` &"J)r   