§
    ‰eîi“i  ã                   ó  — d Z ddlZddlZddlZddlZddlZddlZddlmZmZ ddl	m
Z
 ddlmZ ddlmZmZ ej        dk    rddlZnddlZdZd	Zd
Zej        dk    Z e¦   «         ZdZdade
fd„ZdSdee
         de
fd„Zde
fd„Zde
fd„Zdefd„Z ddœde!de"ddfd„Z#dedefd„Z$dedede
fd„Z%de!dee!         fd„Z&de!dee!         fd„Z'de!dee         fd„Z(de!de"fd„Z)d e*eef         de"fd!„Z+de*fd"„Z,de*eef         fd#„Z-d$e
dee*eef                  fd%„Z.d$e
d&e*eef         ddfd'„Z/dSdee
         dee*         fd(„Z0dSd)ee
         dee*eef                  fd*„Z1d ee*eef                  dee!         fd+„Z2de
d,e"ddfd-„Z3dTd.„Z4de"fd/„Z5dTd0„Z6de"fd1„Z7dTd2„Z8dSd)ee
         de"fd3„Z9dTd4„Z:eeeeeeeed5œd6ed7ed8ed9ed:ed;ed<ed=eddfd>„Z;dee*eef                  fd?„Z<dTd@„Z=dSdededAee*eef                  de>e"ee*eef                  f         fdB„Z?dededdfdC„Z@dddDœdEee!         dFee!         de!fdG„ZAdHZBdIZCde
fdJ„ZDdKe!de"fdL„ZEde"fdM„ZFdTdN„ZG	 dSdOdPœdee
         d,e"dee!         fdQ„ZH	 dSdOdPœdee
         d,e"de"fdR„ZIdS )Uu  
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ÚOptionalÚwin32úhermes-gatewayzgateway_state.jsonzgateway-lockszgateway.lockÚreturnc                  ó(   — t          ¦   «         } | dz  S )z@Return the path to the gateway PID file, respecting HERMES_HOME.zgateway.pidr   ©Úhomes    ú0/home/div/.hermes/hermes-agent/gateway/status.pyÚ_get_pid_pathr   '   s   € åÑÔ€DØ�-ÑÐó    Úpid_pathc                 ój   — | �|                       t          ¦  «        S t          ¦   «         }|t          z  S )z1Return the path to the runtime gateway lock file.)Ú	with_nameÚ_GATEWAY_LOCK_FILENAMEr   )r   r   s     r   Ú_get_gateway_lock_pathr   -   s4   € àÐØ×!Ò!Õ"8Ñ9Ô9Ð9ÝÑÔ€DØÕ(Ñ(Ð(r   c                  óN   — t          ¦   «                              t          ¦  «        S )z5Return the persisted runtime health/status file path.)r   r   Ú_RUNTIME_STATUS_FILE© r   r   Ú_get_runtime_status_pathr   5   s   € å‰?Œ?×$Ò$Õ%9Ñ:Ô:Ð:r   c                  óØ   — t          j        d¦  «        } | rt          | ¦  «        S t          t          j        dt          j        ¦   «         dz  dz  ¦  «        ¦  «        }|dz  t          z  S )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&   :   s`   € åŒyÐ2Ñ3Ô3€HØð Ý�H‰~Œ~ÐÝ•b”iÐ 0µ$´)±+´+ÀÑ2HÈ7Ñ2RÑSÔSÑTÔT€JØ˜Ñ ¥>Ñ1Ð1r   c                  ób   — t          j        t          j        ¦  «                             ¦   «         S ©N)r   Únowr   ÚutcÚ	isoformatr   r   r   Ú_utc_now_isor,   C   s    € ÝŒ<�œÑ%Ô%×/Ò/Ñ1Ô1Ð1r   F)ÚforceÚpidr-   c                óÜ  — |r¦t           rŸ	 t          j        ddt          | ¦  «        ddgddd¬¦  «        }n0# t          $ r# t          j        | t          j        ¦  «         Y dS w xY w|j	        d	k    r6|j
        p|j        pd
                     ¦   «         }t          |pd| › �¦  «        ‚dS |st          j        nt          t          dt          j        ¦  «        }t          j        | |¦  «         dS )záTerminate a PID with platform-appropriate force semantics.

    POSIX uses SIGTERM/SIGKILL. Windows uses taskkill /T /F for true force-kill
    because os.kill(..., SIGTERM) is not equivalent to a tree-killing hard stop.
    Útaskkillz/PIDz/Tz/FTé
   )Úcapture_outputÚtextÚtimeoutNr   Ú ztaskkill failed for PID ÚSIGKILL)Ú_IS_WINDOWSÚ
subprocessÚrunÚstrÚFileNotFoundErrorr!   ÚkillÚsignalÚSIGTERMÚ
returncodeÚstderrÚstdoutÚstripÚOSErrorÚgetattr)r.   r-   ÚresultÚdetailsÚsigs        r   Úterminate_pidrH   G   s  € ð ð •ð ð		Ý”^Ø˜V¥S¨¡X¤X¨t°TÐ:Ø#ØØð	ñ ô ˆFˆFøõ !ð 	ð 	ð 	ÝŒG�C�œÑ(Ô(Ð(ØˆFˆFð	øøøð Ô Ò!Ð!Ø”}Ð;¨¬Ð;¸×BÒBÑDÔDˆGÝ˜'ÐEÐ%EÀÐ%EÐ%EÑFÔFÐFØˆà %Ð
U�&Œ.ˆ.­7µ6¸9ÅfÄnÑ+UÔ+U€CÝ„GˆC�ÑÔÐÐÐs   ‹*6 ¶)A#Á"A#Úidentityc                 ó„   — t          j        |                      d¦  «        ¦  «                             ¦   «         d d…         S )Núutf-8é   )ÚhashlibÚsha256ÚencodeÚ	hexdigest)rI   s    r   Ú_scope_hashrQ   b   s3   € ÝŒ>˜(Ÿ/š/¨'Ñ2Ô2Ñ3Ô3×=Ò=Ñ?Ô?ÀÀÀÔDÐDr   Úscopec                 óJ   — t          ¦   «         | › dt          |¦  «        › d�z  S )Nú-z.lock)r&   rQ   )rR   rI   s     r   Ú_get_scope_lock_pathrU   f   s*   € Ý‰?Œ? ÐDÐD­°HÑ(=Ô(=ÐDÐDÐDÑDÐDr   c                 óð   — t          d| › d�¦  «        }	 t          |                     ¦   «                              ¦   «         d         ¦  «        S # t          t
          t          t          t          f$ r Y dS w xY w)z:Return the kernel start time for a process when available.ú/proc/z/staté   N)	r   ÚintÚ	read_textÚsplitr;   Ú
IndexErrorÚPermissionErrorÚ
ValueErrorrC   )r.   Ú	stat_paths     r   Ú_get_process_start_timer`   j   sx   € åÐ(˜cÐ(Ð(Ð(Ñ)Ô)€Iðå�9×&Ò&Ñ(Ô(×.Ò.Ñ0Ô0°Ô4Ñ5Ô5Ð5øÝ�z­?½JÍÐPð ð ð Øˆtˆtðøøøs   •8A Á#A5Á4A5c                 ó    — t          | ¦  «        S )zBPublic wrapper for retrieving a process start time when available.)r`   )r.   s    r   Úget_process_start_timerb   t   s   € å" 3Ñ'Ô'Ð'r   c                 ó  — t          d| › d�¦  «        }	 |                     ¦   «         }n# t          t          t          f$ r Y dS w xY w|sdS |                     dd¦  «                             dd¬¦  «                             ¦   «         S )	z<Return the process command line as a space-separated string.rW   z/cmdlineNó    ó    rK   Úignore)Úerrors)r   Ú
read_bytesr;   r]   rC   ÚreplaceÚdecoderB   )r.   Úcmdline_pathÚraws      r   Ú_read_process_cmdlinerm   y   sž   € åÐ. Ð.Ð.Ð.Ñ/Ô/€LðØ×%Ò%Ñ'Ô'ˆˆøÝ�µÐ8ð ð ð Øˆtˆtðøøøð ð ØˆtØ�;Š;�w Ñ%Ô%×,Ò,¨W¸XÐ,ÑFÔF×LÒLÑNÔNÐNs   •* ªAÁAc                 ód   ‡— t          | ¦  «        Š‰sdS d}t          ˆfd„|D ¦   «         ¦  «        S )zBReturn True when the live PID still looks like the Hermes gateway.F)úhermes_cli.main gatewayúhermes_cli/main.py gatewayúhermes gatewayr   úgateway/run.pyc              3   ó    •K  — | ]}|‰v V — Œ	d S r(   r   ©Ú.0ÚpatternÚcmdlines     €r   ú	<genexpr>z._looks_like_gateway_process.<locals>.<genexpr>“   ó(   øè è € Ð:Ð: gˆw˜'Ð!Ð:Ð:Ð:Ð:Ð:Ð:r   )rm   Úany)r.   Úpatternsrw   s     @r   Ú_looks_like_gateway_processr|   †   sI   ø€ å# CÑ(Ô(€GØð Øˆuð€Hõ Ð:Ð:Ð:Ð:°Ð:Ñ:Ô:Ñ:Ô:Ð:r   Úrecordc                 ó  ‡— |                       d¦  «        t          k    rdS |                       d¦  «        }t          |t          ¦  «        r|sdS d                     d„ |D ¦   «         ¦  «        Šd}t          ˆfd„|D ¦   «         ¦  «        S )zMValidate gateway identity from PID-file metadata when cmdline is unavailable.ÚkindFÚargvú c              3   ó4   K  — | ]}t          |¦  «        V — Œd S r(   )r:   )ru   Úparts     r   rx   z-_record_looks_like_gateway.<locals>.<genexpr>Ÿ   s(   è è € Ð2Ð2 T•s˜4‘y”yÐ2Ð2Ð2Ð2Ð2Ð2r   )ro   rp   rq   rr   c              3   ó    •K  — | ]}|‰v V — Œ	d S r(   r   rt   s     €r   rx   z-_record_looks_like_gateway.<locals>.<genexpr>¦   ry   r   )ÚgetÚ_GATEWAY_KINDÚ
isinstanceÚlistÚjoinrz   )r}   r€   r{   rw   s      @r   Ú_record_looks_like_gatewayrŠ   –   sš   ø€ à‡z‚z�&ÑÔ�]Ò*Ð*Øˆuà�:Š:�fÑÔ€DÝ�d�DÑ!Ô!ð ¨ð Øˆuà�hŠhÐ2Ð2¨TÐ2Ñ2Ô2Ñ2Ô2€Gð€Hõ Ð:Ð:Ð:Ð:°Ð:Ñ:Ô:Ñ:Ô:Ð:r   c                  ó¦   — t          j        ¦   «         t          t          t          j        ¦  «        t          t          j        ¦   «         ¦  «        dœS )N)r.   r   r€   Ú
start_time)r!   Úgetpidr†   rˆ   Úsysr€   r`   r   r   r   Ú_build_pid_recordr�   ©   s9   € åŒy‰{Œ{ÝÝ•S”X‘”Ý-­b¬i©k¬kÑ:Ô:ð	ð ð r   c            	      ór   — t          ¦   «         } |                      dd ddi t          ¦   «         dœ¦  «         | S )NÚstartingFr   )Úgateway_stateÚexit_reasonÚrestart_requestedÚactive_agentsÚ	platformsÚ
updated_at)r�   Úupdater,   )Úpayloads    r   Ú_build_runtime_status_recordrš   ²   sJ   € ÝÑ!Ô!€GØ‡N‚NØ#ØØ"ØØÝ"‘n”nðð ñ ô ð ð €Nr   Úpathc                 ó2  — |                       ¦   «         sd S 	 |                      ¦   «                              ¦   «         }n# t          $ r Y d S w xY w|sd S 	 t	          j        |¦  «        }n# t          j        $ r Y d S w xY wt          |t          ¦  «        r|nd S r(   )	ÚexistsrZ   rB   rC   ÚjsonÚloadsÚJSONDecodeErrorr‡   Údict)r›   rl   r™   s      r   Ú_read_json_filer¢   ¿   sº   € Ø�;Š;‰=Œ=ð ØˆtðØ�nŠnÑÔ×$Ò$Ñ&Ô&ˆˆøÝð ð ð Øˆtˆtðøøøàð ØˆtðÝ”*˜S‘/”/ˆˆøÝÔð ð ð Øˆtˆtðøøøå  ­$Ñ/Ô/Ð9ˆ7ˆ7°TÐ9s!   ˜&? ¿
AÁAÁA* Á*A=Á<A=r™   c                 óŒ   — | j                              dd¬¦  «         |                      t          j        |¦  «        ¦  «         d S )NT©ÚparentsÚexist_ok)ÚparentÚmkdirÚ
write_textrž   Údumps)r›   r™   s     r   Ú_write_json_filer«   Ï   s?   € Ø„K×Ò˜d¨TÐÑ2Ô2Ð2Ø‡O‚O•D”J˜wÑ'Ô'Ñ(Ô(Ð(Ð(Ð(r   c                 ó¤  — | pt          ¦   «         } |                      ¦   «         sd S |                      ¦   «                              ¦   «         }|sd S 	 t	          j        |¦  «        }n9# t          j        $ r' 	 dt          |¦  «        icY S # t          $ r Y Y d S w xY ww xY wt          |t          ¦  «        rd|iS t          |t          ¦  «        r|S d S ©Nr.   )r   r�   rZ   rB   rž   rŸ   r    rY   r^   r‡   r¡   )r   rl   r™   s      r   Ú_read_pid_recordr®   Ô   sþ   € ØÐ*�=™?œ?€HØ�?Š?ÑÔð Øˆtà
×
Ò
Ñ
Ô
×
$Ò
$Ñ
&Ô
&€CØð ØˆtðÝ”*˜S‘/”/ˆˆøÝÔð ð ð ð	Ø�3˜s™8œ8Ð$Ð$Ð$Ð$øÝð 	ð 	ð 	Ø�4�4�4ð	øøøðøøøõ �'�3ÑÔð  Ø�wÐÐÝ�'�4Ñ Ô ð ØˆØˆ4s0   ÁA' Á'BÁ7B
ÂBÂ

BÂBÂBÂBÚ	lock_pathc                 ó<   — t          | pt          ¦   «         ¦  «        S r(   )r®   r   )r¯   s    r   Ú_read_gateway_lock_recordr±   ì   s   € Ý˜IÐAÕ)?Ñ)AÔ)AÑBÔBÐBr   c                 ór   — | sd S 	 t          | d         ¦  «        S # t          t          t          f$ r Y d S w xY wr­   )rY   ÚKeyErrorÚ	TypeErrorr^   )r}   s    r   Ú_pid_from_recordrµ   ð   sO   € Øð ØˆtðÝ�6˜%”=Ñ!Ô!Ð!øÝ•i¥Ð,ð ð ð Øˆtˆtðøøøs   † ›6µ6Úcleanup_stalec                óÈ   — |sdS 	 |                       d¬¦  «         n# t          $ r Y nw xY w	 t          | ¦  «                              d¬¦  «         dS # t          $ r Y dS w xY w)aè  Delete a stale gateway PID file (and its sibling lock metadata).

    Called from ``get_running_pid()`` after the runtime lock has already been
    confirmed inactive, so the on-disk metadata is known to belong to a dead
    process.  Unlike ``remove_pid_file()`` (which defensively refuses to delete
    a PID file whose ``pid`` field differs from ``os.getpid()`` to protect
    ``--replace`` handoffs), this path force-unlinks both files so the next
    startup sees a clean slate.
    NT©Ú
missing_ok)ÚunlinkÚ	Exceptionr   ©r   r¶   s     r   Ú_cleanup_invalid_pid_pathr½   ù   s™   € ð ð ØˆðØ�Š 4ˆÑ(Ô(Ð(Ð(øÝð ð ð ØˆðøøøðÝ˜xÑ(Ô(×/Ò/¸4Ð/Ñ@Ô@Ð@Ð@Ð@øÝð ð ð Øˆˆðøøøs   † �
*©*®#A Á
A!Á A!c                 ó2  — |                       d¦  «         |                      ¦   «          t          j        t	          ¦   «         | ¦  «         |                      ¦   «          	 t          j        |                      ¦   «         ¦  «         d S # t          $ r Y d S w xY w)Nr   )
ÚseekÚtruncaterž   Údumpr�   Úflushr!   ÚfsyncÚfilenorC   ©Úhandles    r   Ú_write_gateway_lock_recordrÇ     sŒ   € Ø
‡K‚K��N„N€NØ
‡O‚OÑÔÐÝ„IÕÑ!Ô! 6Ñ*Ô*Ð*Ø
‡L‚L�N„N€NðÝ
Œ�—’‘”Ñ!Ô!Ð!Ð!Ð!øÝð ð ð Øˆˆðøøøs   Á &B Â
BÂBc                 ó  — 	 t           r©|                      dt          j        ¦  «         |                      ¦   «         dk    r)|                      d¦  «         |                      ¦   «          |                      d¦  «         t          j        |  	                    ¦   «         t          j
        d¦  «         n>t          j        |  	                    ¦   «         t          j        t          j        z  ¦  «         dS # t          t           f$ r Y dS w xY w)Nr   ú
é   TF)r7   r¿   r!   ÚSEEK_ENDÚtellÚwriterÂ   ÚmsvcrtÚlockingrÄ   ÚLK_NBLCKÚfcntlÚflockÚLOCK_EXÚLOCK_NBÚBlockingIOErrorrC   rÅ   s    r   Ú_try_acquire_file_lockrÖ     s×   € ðÝð 	HØ�KŠK˜�2œ;Ñ'Ô'Ð'Ø�{Š{‰}Œ} Ò!Ð!Ø—’˜TÑ"Ô"Ð"Ø—’‘”�Ø�KŠK˜‰NŒNˆNÝŒN˜6Ÿ=š=™?œ?­F¬O¸QÑ?Ô?Ð?Ð?åŒK˜Ÿš™œ­¬½¼Ñ)FÑGÔGÐGØˆtøÝ�WÐ%ð ð ð Øˆuˆuðøøøs   ‚C.C2 Ã2DÄDc                 ó,  — 	 t           rI|                      d¦  «         t          j        |                      ¦   «         t          j        d¦  «         d S t          j        |                      ¦   «         t          j        ¦  «         d S # t          $ r Y d S w xY w)Nr   rÊ   )
r7   r¿   rÎ   rÏ   rÄ   ÚLK_UNLCKrÑ   rÒ   ÚLOCK_UNrC   rÅ   s    r   Ú_release_file_lockrÚ   *  s†   € ðÝð 	8Ø�KŠK˜‰NŒNˆNÝŒN˜6Ÿ=š=™?œ?­F¬O¸QÑ?Ô?Ð?Ð?Ð?åŒK˜Ÿš™œ­¬Ñ7Ô7Ð7Ð7Ð7øÝð ð ð Øˆˆðøøøs   ‚AB Á1B Â
BÂBc                  óü   — t           �dS t          ¦   «         } | j                             dd¬¦  «         t	          | dd¬¦  «        }t          |¦  «        s|                     ¦   «          dS t          |¦  «         |a dS )zÌClaim the cross-process runtime lock for the gateway.

    Unlike the PID file, the lock is owned by the live process itself. If the
    process dies abruptly, the OS releases the lock automatically.
    NTr¤   úa+rK   ©ÚencodingF)Ú_gateway_lock_handler   r§   r¨   ÚopenrÖ   ÚcloserÇ   )r›   rÆ   s     r   Úacquire_gateway_runtime_lockrâ   5  s„   € õ Ð'Øˆtå!Ñ#Ô#€DØ„K×Ò˜d¨TÐÑ2Ô2Ð2Ý�$˜ wÐ/Ñ/Ô/€FÝ! &Ñ)Ô)ð Ø�Š‰ŒˆØˆuÝ˜vÑ&Ô&Ð&Ø!ÐØˆ4r   c                  óŠ   — t           } | €dS da t          | ¦  «         	 |                      ¦   «          dS # t          $ r Y dS w xY w)z<Release the gateway runtime lock when owned by this process.N)rß   rÚ   rá   rC   rÅ   s    r   Úrelease_gateway_runtime_lockrä   J  s_   € õ "€FØ€~ØˆØÐÝ�vÑÔÐðØ�Š‰ŒˆˆˆøÝð ð ð Øˆˆðøøøs   ž4 ´
AÁAc                 óÞ  — | pt          ¦   «         }t          �|t          ¦   «         k    rdS |                     ¦   «         sdS t          |dd¬¦  «        }	 t	          |¦  «        r8t          |¦  «         	 	 |                     ¦   «          dS # t          $ r Y dS w xY w	 	 |                     ¦   «          dS # t          $ r Y dS w xY w# 	 |                     ¦   «          w # t          $ r Y w w xY wxY w)zFReturn True when some process currently owns the gateway runtime lock.NTFrÜ   rK   rÝ   )r   rß   r�   rà   rÖ   rÚ   rá   rC   )r¯   Úresolved_lock_pathrÆ   s      r   Úis_gateway_runtime_lock_activerç   X  s4  € ð #Ð>Õ&<Ñ&>Ô&>ÐÝÐ'Ð,>ÕBXÑBZÔBZÒ,ZÐ,ZØˆtà×$Ò$Ñ&Ô&ð ØˆuåÐ$ d°WÐ=Ñ=Ô=€Fð	Ý! &Ñ)Ô)ð 	Ý˜vÑ&Ô&Ð&Øð	Ø�LŠL‰NŒNˆNˆNˆNøÝð 	ð 	ð 	ØˆDˆDð	øøøð	 ð	Ø�LŠL‰NŒNˆNˆNˆNøÝð 	ð 	ð 	ØˆDˆDð	øøøøð	Ø�LŠL‰NŒNˆNˆNøÝð 	ð 	ð 	ØˆDð	øøøøøøsT   ÁC Á5B Â
BÂBÂB4 Â4
CÃCÃC,ÃCÃC,Ã
C)Ã&C,Ã(C)Ã)C,c                  ó(  — t          ¦   «         } | j                             dd¬¦  «         t          j        t          ¦   «         ¦  «        }	 t          j        | t          j        t          j	        z  t          j
        z  ¦  «        }n# t          $ r ‚ w xY w	 t          j        |dd¬¦  «        5 }|                     |¦  «         ddd¦  «         dS # 1 swxY w Y   dS # t          $ r* 	 |                      d¬¦  «         n# t           $ r Y nw xY w‚ w xY w)zåWrite the current process PID and metadata to the gateway PID file.

    Uses atomic O_CREAT | O_EXCL creation so that concurrent --replace
    invocations race: exactly one process wins and the rest get
    FileExistsError.
    Tr¤   ÚwrK   rÝ   Nr¸   )r   r§   r¨   rž   rª   r�   r!   rà   ÚO_CREATÚO_EXCLÚO_WRONLYÚFileExistsErrorÚfdopenrÍ   r»   rº   rC   )r›   r}   ÚfdÚfs       r   Úwrite_pid_filerñ   o  sj  € õ ‰?Œ?€DØ„K×Ò˜d¨TÐÑ2Ô2Ð2ÝŒZÕ)Ñ+Ô+Ñ,Ô,€FðÝŒW�T�2œ:­¬	Ñ1µB´KÑ?Ñ@Ô@ˆˆøÝð ð ð ØðøøøðÝŒY�r˜3¨Ð1Ñ1Ô1ð 	°QØ�GŠG�F‰OŒOˆOð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	øåð ð ð ð	Ø�KŠK 4ˆKÑ(Ô(Ð(Ð(øÝð 	ð 	ð 	ØˆDð	øøøàðøøøs`   Á9B ÂBÂC Â-CÃC ÃCÃC ÃCÃC Ã
DÃ(C?Ã>DÃ?
DÄ	DÄDÄD)r’   r“   r”   r•   ÚplatformÚplatform_stateÚ
error_codeÚerror_messager’   r“   r”   r•   rò   ró   rô   rõ   c                 ó  — t          ¦   «         }t          |¦  «        pt          ¦   «         }	|	                     di ¦  «         |	                     dt          ¦  «         t          j        ¦   «         |	d<   t          t          j        ¦   «         ¦  «        |	d<   t          ¦   «         |	d<   | t          ur| |	d<   |t          ur||	d<   |t          urt          |¦  «        |	d<   |t          ur t          d	t          |¦  «        ¦  «        |	d
<   |t          urb|	d                              |i ¦  «        }
|t          ur||
d<   |t          ur||
d<   |t          ur||
d<   t          ¦   «         |
d<   |
|	d         |<   t          ||	¦  «         dS )zBPersist gateway runtime health information for diagnostics/status.r–   r   r.   rŒ   r—   r’   r“   r”   r   r•   r   rô   rõ   N)r   r¢   rš   Ú
setdefaultr†   r!   r�   r`   r,   Ú_UNSETÚboolÚmaxrY   r…   r«   )r’   r“   r”   r•   rò   ró   rô   rõ   r›   r™   Úplatform_payloads              r   Úwrite_runtime_statusrü   ˆ  s•  € õ $Ñ%Ô%€DÝ˜dÑ#Ô#ÐEÕ'CÑ'EÔ'E€GØ×Ò�{ BÑ'Ô'Ð'Ø×Ò�v�}Ñ-Ô-Ð-Ý”Y‘[”[€GˆE�NÝ3µB´I±K´KÑ@Ô@€GˆLÑÝ(™NœN€GˆLÑà�FÐ"Ð"Ø#0ˆ�Ñ Ø�&Ð Ð Ø!,ˆ�ÑØ¥Ð&Ð&Ý'+Ð,=Ñ'>Ô'>ˆÐ#Ñ$Ø�FÐ"Ð"Ý#& q­#¨mÑ*<Ô*<Ñ#=Ô#=ˆ�Ñ à•vÐÐØ" ;Ô/×3Ò3°H¸bÑAÔAÐØ¥Ð'Ð'Ø(6Ð˜WÑ%Ø�VÐ#Ð#Ø-7Ð˜\Ñ*Ø¥Ð&Ð&Ø0=Ð˜_Ñ-Ý)5©¬Ð˜Ñ&Ø)9ˆ�Ô˜XÑ&å�T˜7Ñ#Ô#Ð#Ð#Ð#r   c                  ó8   — t          t          ¦   «         ¦  «        S )z=Read the persisted gateway runtime health/status information.)r¢   r   r   r   r   Úread_runtime_statusrþ   ³  s   € åÕ3Ñ5Ô5Ñ6Ô6Ð6r   c                  ó6  — 	 t          ¦   «         } t          | ¦  «        }|�Q	 t          |d         ¦  «        }n# t          t          t
          f$ r d}Y nw xY w|�|t          j        ¦   «         k    rdS |                      d¬¦  «         dS # t          $ r Y dS w xY w)ah  Remove the gateway PID file, but only if it belongs to this process.

    During --replace handoffs, the old process's atexit handler can fire AFTER
    the new process has written its own PID file.  Blindly removing the file
    would delete the new process's record, leaving the gateway running with no
    PID file (invisible to ``get_running_pid()``).
    Nr.   Tr¸   )
r   r¢   rY   r³   r´   r^   r!   r�   rº   r»   )r›   r}   Úfile_pids      r   Úremove_pid_filer  ¸  s½   € ðÝ‰ŒˆÝ  Ñ&Ô&ˆØÐð Ý˜v eœ}Ñ-Ô-��øÝ�i­Ð4ð  ð  ð  Ø���ð øøøàÐ#¨µB´I±K´KÒ(?Ð(?à�Ø�Š˜tˆÑ$Ô$Ð$Ð$Ð$øÝð ð ð Øˆˆðøøøs7   ‚B
 ¢8 ·B
 ¸AÁB
 ÁAÁB
 Á2B
 Â

BÂBÚmetadatac                 óÊ  — t          | |¦  «        }|j                             dd¬¦  «         i t          ¦   «         ¥| t	          |¦  «        |pi t          ¦   «         dœ¥}t          |¦  «        }|€<|                     ¦   «         r(	 |                     d¬¦  «         n# t          $ r Y nw xY w|�rÛ	 t          |d         ¦  «        }n# t          t          t          f$ r d}Y nw xY w|t          j        ¦   «         k    r@|                     d¦  «        |                     d¦  «        k    rt#          ||¦  «         d|fS |du }|�s	 t          j        |d¦  «         t'          |¦  «        }|                     d¦  «        �|�||                     d¦  «        k    rd}|s 	 t)          d	|› d
�¦  «        }	|	                     ¦   «         r`|	                     ¦   «                              ¦   «         D ]9}
|
                     d¦  «        r"|
                     ¦   «         d         }|dv rd} nŒ:n7# t          t2          f$ r Y n$w xY wn# t4          t2          t          f$ r d}Y nw xY w|r(	 |                     d¬¦  «         n# t          $ r Y nw xY wd|fS 	 t          j        |t          j        t          j        z  t          j        z  ¦  «        }n!# t>          $ r dt          |¦  «        fcY S w xY w	 t          j         |dd¬¦  «        5 }tC          j"        ||¦  «         ddd¦  «         n# 1 swxY w Y   n7# tF          $ r* 	 |                     d¬¦  «         n# t          $ r Y nw xY w‚ w xY wdS )záAcquire 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¤   )rR   Úidentity_hashr  r—   Nr¸   r.   rŒ   r   rW   z/statuszState:rÊ   )ÚTÚtFré   rK   rÝ   )TN)$rU   r§   r¨   r�   rQ   r,   r¢   r�   rº   rC   rY   r³   r´   r^   r!   r�   r…   r«   r<   r`   r   rZ   Ú
splitlinesÚ
startswithr[   r]   ÚProcessLookupErrorrà   rê   rë   rì   rí   rî   rž   rÁ   r»   )rR   rI   r  r¯   r}   ÚexistingÚexisting_pidÚstaleÚcurrent_startÚ_proc_statusÚ_lineÚ_staterï   rÆ   s                 r   Úacquire_scoped_lockr  Ð  sp  € õ % U¨HÑ5Ô5€IØÔ×Ò 4°$ÐÑ7Ô7Ð7ðÝ
Ñ
Ô
ðàÝ$ XÑ.Ô.Ø�N Ý"‘n”nðð ð €Fõ ˜yÑ)Ô)€HØÐ˜I×,Ò,Ñ.Ô.Ðð
	Ø×Ò¨ÐÑ-Ô-Ð-Ð-øÝð 	ð 	ð 	ØˆDð	øøøàñ .#ð	 Ý˜x¨œÑ/Ô/ˆLˆLøÝ�)¥ZÐ0ð 	 ð 	 ð 	 ØˆLˆLˆLð	 øøøð �2œ9™;œ;Ò&Ð&¨8¯<ª<¸Ñ+EÔ+EÈÏÊÐT`ÑIaÔIaÒ+aÐ+aÝ˜Y¨Ñ/Ô/Ð/Ø˜�>Ð!à Ð$ˆØñ 	ðÝ”˜ aÑ(Ô(Ð(õ
 !8¸Ñ EÔ E�à—L’L Ñ.Ô.Ð:Ø%Ð1Ø%¨¯ª°lÑ)CÔ)CÒCÐCà �Eð ð ð
Ý'+Ð,J°\Ð,JÐ,JÐ,JÑ'KÔ'K˜Ø'×.Ò.Ñ0Ô0ð *Ø)5×)?Ò)?Ñ)AÔ)A×)LÒ)LÑ)NÔ)Nð *ð * Ø#(×#3Ò#3°HÑ#=Ô#=ð !*Ø-2¯[ª[©]¬]¸1Ô-= FØ'-°Ð';Ð';Ø04¨Ø$) Eð	!*øøõ
 $¥_Ð5ð ð ð Ø˜ðøøøðøõ '­½ÐAð ð ð à���ðøøøð4 ð 	#ðØ× Ò ¨DÐ Ñ1Ô1Ð1Ð1øÝð ð ð Ø�ðøøøð ˜(�?Ð"ð1ÝŒW�Y¥¤
­R¬YÑ 6½¼Ñ DÑEÔEˆˆøÝð 1ð 1ð 1Ø•o iÑ0Ô0Ð0Ð0Ð0Ð0ð1øøøðÝŒY�r˜3¨Ð1Ñ1Ô1ð 	&°VÝŒI�f˜fÑ%Ô%Ð%ð	&ð 	&ð 	&ñ 	&ô 	&ð 	&ð 	&ð 	&ð 	&ð 	&ð 	&øøøð 	&ð 	&ð 	&ð 	&øøåð ð ð ð	Ø×Ò¨ÐÑ-Ô-Ð-Ð-øÝð 	ð 	ð 	ØˆDð	øøøàðøøøð ˆ:sÁ   ÂB Â
B)Â(B)Â0C ÃC"Ã!C"ÅH= ÆBH% È%H9È8H9È=IÉIÉI6 É6
JÊJÊ9K ËK#Ë"K#Ë'L, Ë>L ÌL, Ì L$Ì$L, Ì'L$Ì(L, Ì,
M Ì7MÍM Í
MÍM ÍMÍM c                 óf  — t          | |¦  «        }t          |¦  «        }|sdS |                     d¦  «        t          j        ¦   «         k    rdS |                     d¦  «        t          t          j        ¦   «         ¦  «        k    rdS 	 |                     d¬¦  «         dS # t          $ r Y dS w xY w)zDRelease a previously-acquired scope lock when owned by this process.Nr.   rŒ   Tr¸   )rU   r¢   r…   r!   r�   r`   rº   rC   )rR   rI   r¯   r
  s       r   Úrelease_scoped_lockr  *  s¼   € å$ U¨HÑ5Ô5€IÝ˜yÑ)Ô)€HØð ØˆØ‡|‚|�EÑÔ�bœi™kœkÒ)Ð)ØˆØ‡|‚|�LÑ!Ô!Õ%<½R¼Y¹[¼[Ñ%IÔ%IÒIÐIØˆðØ×Ò DÐÑ)Ô)Ð)Ð)Ð)øÝð ð ð Øˆˆðøøøs   Â
B" Â"
B0Â/B0)Ú	owner_pidÚowner_start_timer  r  c                 óà  — t          ¦   «         }d}|                     ¦   «         rÉ|                     d¦  «        D ]³}| �ƒt          |¦  «        }t	          |t
          ¦  «        sŒ)	 t          |                     d¦  «        ¦  «        }n# t          t          f$ r Y Œ`w xY w|| k    rŒk|�|                     d¦  «        |k    rŒ‡	 | 
                    d¬¦  «         |dz  }Œ¤# t          $ r Y Œ°w xY w|S )	a&  Remove 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. When an
    ``owner_pid`` is provided, only lock records belonging to that gateway
    process are removed. ``owner_start_time`` further narrows the match to
    protect against PID reuse.

    When no owner is provided, preserves the legacy behavior and removes every
    scoped lock file in the directory.

    Returns the number of lock files removed.
    r   z*.lockNr.   rŒ   Tr¸   rÊ   )r&   r�   Úglobr¢   r‡   r¡   rY   r…   r´   r^   rº   rC   )r  r  Úlock_dirÚremovedÚ	lock_filer}   Ú
record_pids          r   Úrelease_all_scoped_locksr  :  s'  € õ$ ‰Œ€HØ€GØ‡‚ÑÔð Ø!Ÿš xÑ0Ô0ð 	ð 	ˆIØÐ$Ý(¨Ñ3Ô3�Ý! &­$Ñ/Ô/ð ØðÝ!$ V§Z¢Z°Ñ%6Ô%6Ñ!7Ô!7�J�JøÝ!¥:Ð.ð ð ð Ø�Hðøøøà Ò*Ð*Øà$Ð0ØŸ
š
 <Ñ0Ô0Ð4DÒDÐDàðØ× Ò ¨DÐ Ñ1Ô1Ð1Ø˜1‘��øÝð ð ð Ø�ðøøøà€Ns$   Á$"BÂBÂBÃCÃ
C+Ã*C+z.gateway-takeover.jsoné<   c                  ó2   — t          ¦   «         } | t          z  S )z6Return the path to the --replace takeover marker file.)r   Ú_TAKEOVER_MARKER_FILENAMEr   s    r   Ú_get_takeover_marker_pathr   ~  s   € åÑÔ€DØÕ+Ñ+Ð+r   Ú
target_pidc                 óÖ   — 	 t          | ¦  «        }| |t          j        ¦   «         t          ¦   «         dœ}t	          t          ¦   «         |¦  «         dS # t          t          f$ r Y dS w xY w)aä  Record that ``target_pid`` is being replaced by the current process.

    Captures the target's ``start_time`` so that PID reuse after the
    target exits cannot later match the marker. Also records the
    replacer's PID and a UTC timestamp for TTL-based staleness checks.

    Returns True on successful write, False on any failure. The caller
    should proceed with the SIGTERM even if the write fails (the marker
    is a best-effort signal, not a correctness requirement).
    )r!  Útarget_start_timeÚreplacer_pidÚ
written_atTF)r`   r!   r�   r,   r«   r   rC   r]   )r!  r#  r}   s      r   Úwrite_takeover_markerr&  „  s|   € ðÝ3°JÑ?Ô?Ðà$Ø!2ÝœI™KœKÝ&™.œ.ð	
ð 
ˆõ 	Õ2Ñ4Ô4°fÑ=Ô=Ð=ØˆtøÝ•_Ð%ð ð ð Øˆuˆuðøøøs   ‚AA ÁA(Á'A(c                  óP  — t          ¦   «         } t          | ¦  «        }|sdS 	 t          |d         ¦  «        }|                     d¦  «        }|                     d¦  «        pd}nF# t          t
          t          f$ r, 	 |                      d¬¦  «         n# t          $ r Y nw xY wY dS w xY wd}	 t          j
        |¦  «        }t          j        t          j        ¦  «        |z
                       ¦   «         }|t          k    rd}n# t
          t          f$ r d}Y nw xY w|r*	 |                      d¬¦  «         n# t          $ r Y nw xY wdS t!          j        ¦   «         }t%          |¦  «        }	||k    o|duo	|	duo||	k    }
	 |                      d¬¦  «         n# t          $ r Y nw xY w|
S )	aÁ  Check & unlink the takeover marker if it names the current process.

    Returns True only when a valid (non-stale) marker names this PID +
    start_time. A returning True indicates the current SIGTERM is a
    planned --replace takeover; the caller should exit 0 instead of
    signalling ``_signal_initiated_shutdown``.

    Always unlinks the marker on match (and on detected staleness) so
    subsequent unrelated signals don't re-trigger.
    Fr!  r#  r%  r5   Tr¸   N)r   r¢   rY   r…   r³   r´   r^   rº   rC   r   Úfromisoformatr)   r   r*   Útotal_secondsÚ_TAKEOVER_MARKER_TTL_Sr!   r�   r`   )r›   r}   r!  r#  r%  r  Ú
written_dtÚageÚour_pidÚour_start_timeÚmatchess              r   Ú consume_takeover_marker_for_selfr0  �  s'  € õ %Ñ&Ô&€DÝ˜TÑ"Ô"€FØð Øˆuð	Ý˜ Ô-Ñ.Ô.ˆ
Ø"ŸJšJÐ':Ñ;Ô;ÐØ—Z’Z Ñ-Ô-Ð3°ˆ
ˆ
øÝ•i¥Ð,ð ð ð ð	Ø�KŠK 4ˆKÑ(Ô(Ð(Ð(øÝð 	ð 	ð 	ØˆDð	øøøàˆuˆuðøøøð €EðÝÔ+¨JÑ7Ô7ˆ
ÝŒ|�HœLÑ)Ô)¨JÑ6×EÒEÑGÔGˆØÕ'Ò'Ð'ØˆEøøÝ•zÐ"ð ð ð Øˆˆˆðøøøð ð ð	Ø�KŠK 4ˆKÑ(Ô(Ð(Ð(øÝð 	ð 	ð 	ØˆDð	øøøàˆuõ Œi‰kŒk€GÝ,¨WÑ5Ô5€Nà�gÒð 	0Ø TÐ)ð	0à $Ð&ð	0ð  Ò/ð	 ðØ�Š˜tˆÑ$Ô$Ð$Ð$øÝð ð ð Øˆðøøøð €Nsm   £AA% Á%B(Á=BÂB(Â
B!ÂB(Â B!Â!B(Â'B(Â.AD ÄDÄDÄD6 Ä6
EÅEÅ?F Æ
F#Æ"F#c                  ón   — 	 t          ¦   «                              d¬¦  «         dS # t          $ r Y dS w xY w)zDRemove the takeover marker unconditionally. Safe to call repeatedly.Tr¸   N)r   rº   rC   r   r   r   Úclear_takeover_markerr2  Þ  sL   € ðÝ!Ñ#Ô#×*Ò*°dÐ*Ñ;Ô;Ð;Ð;Ð;øÝð ð ð Øˆˆðøøøs   ‚"& ¦
4³4T©r¶   c                ób  — | pt          ¦   «         }t          |¦  «        }t          |¦  «        }|st          ||¬¦  «         dS t	          |¦  «        }t          |¦  «        }||fD ]·}t          |¦  «        }|€Œ	 t          j        |d¦  «         n;# t          $ r Y Œ7t          $ r t          |¦  «        r|cY c S Y ŒWt          $ r Y Œbw xY w|                     d¦  «        }	t          |¦  «        }
|	�	|
�|
|	k    rŒ•t          |¦  «        st          |¦  «        r|c S Œ¸t          ||¬¦  «         dS )z±Return 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.
    r3  Nr   rŒ   )r   r   rç   r½   r®   r±   rµ   r!   r<   r	  r]   rŠ   rC   r…   r`   r|   )r   r¶   Úresolved_pid_pathræ   Úlock_activeÚprimary_recordÚfallback_recordr}   r.   Úrecorded_startr  s              r   Úget_running_pidr:  æ  s�  € ð !Ð3¥M¡O¤OÐÝ/Ð0AÑBÔBÐÝ0Ð1CÑDÔD€KØð Ý!Ð"3À=ÐQÑQÔQÐQØˆtå%Ð&7Ñ8Ô8€NÝ/Ð0BÑCÔC€Oà! ?Ð3ð ð ˆÝ˜vÑ&Ô&ˆØˆ;Øð	ÝŒG�C˜‰OŒOˆOˆOøÝ!ð 	ð 	ð 	ØˆHÝð 	ð 	ð 	õ *¨&Ñ1Ô1ð Ø�
�
�
�
�
ØˆHÝð 	ð 	ð 	ð ˆHð	øøøð
  Ÿš LÑ1Ô1ˆÝ/°Ñ4Ô4ˆØÐ%¨-Ð*CÈÐYgÒHgÐHgØå& sÑ+Ô+ð 	Õ/IÈ&Ñ/QÔ/Qð 	ØˆJˆJˆJð	õ Ð/¸}ÐMÑMÔMÐMØˆ4s   Á;BÂ
C	ÂC	Â=	C	ÃC	c                ó(   — t          | |¬¦  «        duS )z1Check if the gateway daemon is currently running.r3  N)r:  r¼   s     r   Úis_gateway_runningr<    s   € õ ˜8°=ÐAÑAÔAÈÐMÐMr   r(   )r   N)JÚ__doc__rM   rž   r!   r=   r8   rŽ   r   r   Úpathlibr   Úhermes_constantsr   Útypingr   r	   rò   rÎ   rÑ   r†   r   r#   r7   Úobjectrø   r   rß   r   r   r   r&   r:   r,   rY   rù   rH   rQ   rU   r`   rb   rm   r|   r¡   rŠ   r�   rš   r¢   r«   r®   r±   rµ   r½   rÇ   rÖ   rÚ   râ   rä   rç   rñ   rü   rþ   r  Útupler  r  r  r  r*  r   r&  r0  r2  r:  r<  r   r   r   ú<module>rC     sî  ððð ð €€€Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø 
€
€
€
Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø  Ð  Ð  Ð  Ð  Ð  Ð  Ð  à„<�7ÒÐØ€M€M€M€Mà€L€L€Là €Ø+Ð Ø €ØŒl˜gÒ%€Ø	ˆ‰Œ€Ø'Ð ØÐ ð �tð  ð  ð  ð  ð)ð ) X¨d¤^ð )¸tð )ð )ð )ð )ð; $ð ;ð ;ð ;ð ;ð
2�tð 2ð 2ð 2ð 2ð2�cð 2ð 2ð 2ð 2ð .3ð ð ð �sð  dð °tð ð ð ð ð6E˜#ð E #ð Eð Eð Eð EðE ð E¨sð E°tð Eð Eð Eð Eð ð ¨°#¬ð ð ð ð ð( ð (¨°¬ð (ð (ð (ð (ð

O˜sð 
O x°¤}ð 
Oð 
Oð 
Oð 
Oð; Sð ;¨Tð ;ð ;ð ;ð ;ð ; t¨C°¨H¤~ð ;¸$ð ;ð ;ð ;ð ;ð&˜4ð ð ð ð ð
 d¨3°¨8¤nð 
ð 
ð 
ð 
ð:˜$ð : 8¨D°°c°¬NÔ#;ð :ð :ð :ð :ð )˜4ð )¨$¨s°C¨x¬.ð )¸Tð )ð )ð )ð )ð
ð ˜x¨œ~ð ¸À$¼ð ð ð ð ð0Cð C¨°$¬ð CÀ8ÈDÐQTÐVYÐQYÌNÔC[ð Cð Cð Cð Cð˜X d¨3°¨8¤nÔ5ð ¸(À3¼-ð ð ð ð ð¨ð Àð Èð ð ð ð ð,ð ð ð ð dð ð ð ð ð ð ð ð ð dð ð ð ð ð*ð ð ð ðð ¨h°t¬nð Èð ð ð ð ð.ð ð ð ð6  ØØ#ØØØ ØØð($ð ($ð ($àð($ð ð($ð ð	($ð
 ð($ð ð($ð ð($ð ð($ð ð($ð 
ð($ð ($ð ($ð ($ðV7˜X d¨3°¨8¤nÔ5ð 7ð 7ð 7ð 7ð
ð ð ð ð0Wð W˜sð W¨cð W¸XÀdÈ3ÐPSÈ8ÄnÔ=Uð WÐafÐgkÐmuÐvzÐ{~ð  ADð  |Dô  wEô  nFð  hFô  bGð Wð Wð Wð Wðt˜sð ¨cð °dð ð ð ð ð$  $Ø&*ð*ð *ð *à˜Œ}ð*ð ˜s”mð*ð 	ð	*ð *ð *ð *ð@ 5Ð ØÐ ð, 4ð ,ð ,ð ,ð ,ð cð ¨dð ð ð ð ð2>¨$ð >ð >ð >ð >ðBð ð ð ð  $ð2ð ð2ð 2ð 2Ø�tŒnð2ð ð2ð ˆc„]ð	2ð 2ð 2ð 2ðl  $ðNð ðNð Nð NØ�tŒnðNð ðNð 
ð	Nð Nð Nð Nð Nð Nr   