Ë
    âß²jGZ  ã                  óÄ  — d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZ ddlmZ ddlmZ ddlmZmZmZmZ ddlmZ erdd	lmZ  ej8                  d
«      Zdd„Z	 	 	 	 	 	 	 	 dd„Zd d„Z dZ!d d„Z"d!d„Z#d"d„Z$d#d„Z%d$d„Z&d$d„Z'd$d„Z(d$d„Z)	 	 	 	 	 	 	 	 d%d„Z*d&d„Z+d'd„Z,	 	 d(	 	 	 	 	 	 	 d)d„Z-d*d„Z.	 	 d(	 	 	 	 	 	 	 	 	 d+d„Z/ddl0m1Z2 y),aÉ  Cron pre-run script execution: timeouts, Windows venv bootstrap, process-tree termination,
and the claim-heartbeat thread that keeps a long script's run claim alive.

Split out of ``cron.scheduler``. Import names from this module directly (``cron.scheduler`` only
imports the few it calls itself). Origin-resident helpers and sibling split modules are reached
late-bound (``_sched`` / module refs at the bottom) so monkeypatching the defining module works.
é    )ÚannotationsN)Úcron_env_setting)Ú_ensure_cron_dir)ÚPath)ÚAnyÚCallableÚOptionalÚTYPE_CHECKING)Úwindows_hide_flags)Ú_CancelEventLikezcron.schedulerc                ó<   — t        t        | «      «      }|dkD  r|S dS )zD``int(float(raw))`` when > 0, else None; raises on unparsable input.r   N)ÚintÚfloat)ÚrawÚtimeouts     ú:/home/ubuntu/.hermes/hermes-agent/cron/scheduler_script.pyÚ_positive_intr   #   s    € ä”%˜“*‹o€GØ ’kˆ7Ð+ tÐ+ó    c                ó´  — t        | «      j                  «       }|r	  ||«      }|�|S 	 	 t        j                  «       xs i }t        |t        «      r|j                  di «      ni }|j                  |«      }|� ||«      }|�|S y# t        $ r t        j	                  d| |«       Y Œw xY w# t        $ r!}	t        j                  d||	«       Y d}	~	yd}	~	ww xY w)u¹   Shared env â†’ ``cron.<config_key>`` resolution. ``parse`` returns the value or None to keep
    looking; a parse error on the env var WARNs, on config DEBUGs. None when neither yields.Nz#Invalid %s=%r; using config/defaultÚcronz!Failed to load %s from config: %s)r   ÚstripÚ	ExceptionÚloggerÚwarningÚ_schedÚload_configÚ
isinstanceÚdictÚgetÚdebug)
Úenv_varÚ
config_keyÚparseÚlabelÚ	env_valueÚvalueÚcfgÚcron_cfgÚ
configuredÚexcs
             r   Ú_timeout_from_env_or_configr+   )   sê   € ô ! Ó)×/Ñ/Ó1€IÙð	VÙ˜)Ó$ˆEØÐ Ø�ð !ð	FÜ× Ñ Ó"Ò( bˆÜ*4°S¼$Ô*?�3—7‘7˜6 2Ô&ÀRˆØ—\‘\ *Ó-ˆ
ØÐ!Ù˜*Ó%ˆEØÐ Ø�ð øô ò 	VÜ�N‰NÐ@À'È9ÖUð	Vûô ò FÜ�‰Ð8¸%À×EÐEØûðFús)   �B «AB- Â B*Â)B*Â-	CÂ6CÃCc                 ó4  — t         j                  t         j                  k7  r	 t        t         j                  «      } | �| S 	 t        ddt        d«      }|€t         j                  S |S # t        $ r' t
        j                  dt         j                  «       Y ŒUw xY w)zOResolve cron pre-run script timeout from module/env/config with a safe default.z<Invalid patched _SCRIPT_TIMEOUT=%r; using env/config/defaultÚHERMES_CRON_SCRIPT_TIMEOUTÚscript_timeout_secondszcron script timeout)r   Ú_SCRIPT_TIMEOUTÚ_DEFAULT_SCRIPT_TIMEOUTr   r   r   r   r+   )r   Úresolveds     r   Ú_get_script_timeoutr2   B   s“   € ä×Ñ¤×!?Ñ!?Ò?ð	(Ü#¤F×$:Ñ$:Ó;ˆGØÐ"Ø�ð #ô +Ø$Ð&>ÄØó€Hð .6Ð-=Œ6×)Ñ)ÐKÀ8ÐKøô ò 	(Ü�N‰NØNÜ×&Ñ&ö(ð	(ús   £A' Á'-BÂBi,  c                 ó:   — t        ddt        d«      } | €t        S | S )z¨Per-attachment media-send timeout: HERMES_CRON_MEDIA_SEND_TIMEOUT env, then
    ``cron.media_send_timeout_seconds``, then 300s (long TTS audio can exceed a 30s window).ÚHERMES_CRON_MEDIA_SEND_TIMEOUTÚmedia_send_timeout_secondszcron media-send timeout)r+   r   Ú_DEFAULT_MEDIA_SEND_TIMEOUT©r1   s    r   Ú_get_media_send_timeoutr8   W   s,   € ô +Ø(Ð*FÌØ!ó#€Hð +3Ð*:Ô&ÐHÀÐHr   c                 ó2   — t        ddt        d«      } | €dS | S )a   Bound on run_job's SessionDB init: HERMES_CRON_SESSION_DB_TIMEOUT env, then
    ``cron.session_db_timeout_seconds`` (in DEFAULT_CONFIG), then 10s. Unlike sibling timeouts,
    0 is meaningful (unlimited, debugging opt-in), so values pass through untouched.ÚHERMES_CRON_SESSION_DB_TIMEOUTÚsession_db_timeout_secondszcron.session_db_timeout_secondsg      $@)r+   r   r7   s    r   Ú_get_session_db_timeoutr<   `   s+   € ô +Ø(Ð*FÌØ)ó+€Hð Ð#ˆ4Ð1¨Ð1r   c                ó  — 	 | dz  j                  d¬«      j                  «       }d„ |D «       D ��ci c]2  \  }}|j                  «       j	                  «       |j                  «       “Œ4 c}}S # t        $ r i cY S w xY wc c}}w )Nz
pyvenv.cfgúutf-8)Úencodingc              3  óJ   K  — | ]  }d |v sŒ|j                  d d«      –— Œ y­w)Ú=é   N)Úsplit)Ú.0r   s     r   ú	<genexpr>z+_read_windows_pyvenv_cfg.<locals>.<genexpr>q   s!   è ø€ ÒL°ÀÈÂ˜3Ÿ9™9 S¨!×,ÑLùs   ‚	#Œ#)Ú	read_textÚ
splitlinesÚOSErrorr   Úlower)Úvenv_dirÚlinesÚkeyr&   s       r   Ú_read_windows_pyvenv_cfgrM   j   sƒ   € ðØ˜LÑ(×3Ñ3¸WÐ3ÓE×PÑPÓRˆñ
 M¸ÔL÷áˆC�ð 	�	‰	‹×ÑÓ˜UŸ[™[›]Ñ*óð øô ò ØŠ	ðüós   ‚#A, ±7A=Á,A:Á9A:c                óD  — t         j                  dk7  r| i fS t        j                  | «      }|j                  j                  }i }|j
                  j                  «       dk(  r#|j                  d«      }|j                  «       r|}t        |«      }|j                  dd«      }|dz  dz  }d|v rç|råt        j                  |«      dz  }|j                  «       r½|j                  «       r­|}t        |«      |d	<   t        t        j                  t        «      j                  «       j                  d
   «      t        |«      g}	t        j                   j                  dd«      }
|
r|	j#                  |
«       t        j$                  j'                  |	«      |d<   t        |«      |fS )a  Hidden, output-capable Python invocation for Windows cron scripts. ``pythonw.exe`` loses
    captured output; uv venv launchers can re-exec the base console python and flash a window
    even with CREATE_NO_WINDOW, so run the base python directly with venv paths overlaid in env.Úwin32zpythonw.exez
python.exeÚhomeÚ ÚLibúsite-packagesÚuvÚVIRTUAL_ENVrB   Ú
PYTHONPATH)ÚsysÚplatformr   r   ÚparentÚnamerI   Ú	with_nameÚexistsrM   r   ÚstrÚ__file__ÚresolveÚparentsÚosÚenvironÚappendÚpathsepÚjoin)Ú
python_exeÚinterpreterrJ   Úenv_overlayÚsiblingr'   rP   Úsite_packagesÚbase_pythonÚpythonpath_entriesÚexisting_pythonpaths              r   Ú_windows_cron_python_invocationrn   u   sg  € ô ‡|�|�wÒØ˜2ˆ~Ðä—+‘+˜jÓ)€KØ×!Ñ!×(Ñ(€HØ"$€Kà×Ñ×ÑÓ =Ò0Ø×'Ñ'¨Ó5ˆØ�>‰>ÔØ!ˆKä
" 8Ó
,€CØ�7‰7�6˜2Ó€DØ˜uÑ$ Ñ6€MØˆs�{‘tÜ—k‘k $Ó'¨,Ñ6ˆØ×ÑÔ M×$8Ñ$8Ô$:Ø%ˆKÜ),¨X«ˆK˜Ñ&ä”F—K‘K¤Ó)×1Ñ1Ó3×;Ñ;¸AÑ>Ó?ÄÀ]ÓASð"UÐä"$§*¡*§.¡.°¸rÓ"BÐÙ"Ø"×)Ñ)Ð*=Ô>Ü(*¯
©
¯©Ð8JÓ(KˆK˜Ñ%äˆ{Ó˜[Ð(Ð(r   c                óÔ  — | j                  «       �yt        j                  dk7  rt        | «       n;	 t	        j
                  ddt        | j                  «      ddgddt        «       d	¬
«       	 | j                  d¬«       y# t        t        j                  f$ r | j                  «        Y Œ?w xY w# t        j                  $ r% | j                  «        | j                  d¬«       Y yw xY w)zBBest-effort hard stop of a cron script and every child it spawned.NrO   Útaskkillz/PIDz/Tz/FTé
   F)Úcapture_outputr   ÚcreationflagsÚcheckç      ð?©r   )ÚpollrW   rX   Ú_terminate_process_groupÚ
subprocessÚrunr]   Úpidr   rH   ÚTimeoutExpiredÚkillÚwait)Úprocs    r   Ú_terminate_cron_script_processr€   —   sÀ   € à‡y�yƒ{ÐØÜ
‡|�|�wÒÜ  Õ&ð	Ü�N‰NØ˜V¤S¨¯©£]°D¸$Ð?ÐPTÐ^`Ü0Ó2¸%õAð
Ø�	‰	˜#ˆ	Õøô œ×2Ñ2Ð3ò 	Ø�I‰IŽKð	ûô ×$Ñ$ò Ø�	‰	ŒØ�	‰	˜#ˆ	Öðús#   ²:B  Á-B/ Â )B,Â+B,Â/5C'Ã&C'c           	     ó„  — 	 t        j                  | j                  «      }t        j                  |t        j
                  «       t        j                  t        j                  «      5  | j                  d¬«       ddd«       	 t        j                  |d«       t        j                  t        t        t        f«      5  t        j                  |t        t        dt        j
                  «      «       ddd«       y# t        t        t        f$ r Y yw xY w# 1 sw Y   Œ›xY w# t        t        f$ r Y yw xY w# 1 sw Y   yxY w)z³POSIX: TERM the script's process group, then KILL if ANY member survived (a survivor holds
    the pipe write ends open and the caller's communicate() would block on EOF forever).Nru   rv   r   ÚSIGKILL)ra   Úgetpgidr{   ÚkillpgÚsignalÚSIGTERMÚProcessLookupErrorÚPermissionErrorrH   Ú
contextlibÚsuppressry   r|   r~   Úgetattr)r   Úprocess_groups     r   rx   rx   «   sü   € ðÜŸ
™
 4§8¡8Ó,ˆÜ
�	‰	�-¤§¡Ô0ô 
×	Ñ	œZ×6Ñ6Ó	7ñ Ø�	‰	˜#ˆ	Ô÷ðÜ
�	‰	�- Ô#ô 
×	Ñ	Ô0´/Ä7ÐKÓ	Lñ MÜ
�	‰	�-¤¬°¼F¿N¹NÓ!KÔL÷Mð Møô ¤´Ð9ò Ùðú÷ð ûô ¤Ð(ò Ùðú÷Mð Mús<   ‚AC; Á(DÂD! Â>4D6Ã;DÄDÄDÄ!D3Ä2D3Ä6D?c                ó,  ‡ — ‰ j                  «       �yddœdˆ fd„}t        ‰ dd«      }t        |t        «      r|dk  r	 |d|«      S 	 ddlm} 	  ||«      ry	  |d|«       y# t        $ r  |d	d
¬«      cY S w xY w# t        $ r  |d|d
¬«      cY S w xY w)zHTerminate a script tree, then fall back to the local process-group path.NF©Úexc_infoc               óT   •— t        j                  | dz   g|¢­d|iŽ t        ‰«       y )Nz+; falling back to process-group terminationr�   )r   r   r€   )Úreasonr�   Úargsr   s      €r   Úfallbackz-_terminate_cron_script_tree.<locals>.fallbackÂ   s3   ø€ Ü�‰ØÐBÑBð	^ØEIò	^ØT\ò	^ä& tÕ,r   r{   r   z-Cron script tree-kill received invalid pid %r)Úkill_process_treez,agent.deadline.kill_process_tree unavailableTz'Cron script tree-kill failed for pid %sz3Cron script tree-kill reported no signal for pid %s)r‘   r]   r�   ÚboolÚreturnÚNone)rw   r‹   r   r   Úagent.deadliner”   r   )r   r“   r{   r”   s   `   r   Ú_terminate_cron_script_treer™   ½   s»   ø€ à‡y�yƒ{ÐàØ6;÷ -ô
 �$˜˜tÓ
$€CÜ�cœ3Ô 3¨!¢8ÙÐGÈÓMÐMðWõ 	5ðWÙ˜SÔ!Øð "ñ ÐBÀCÕHøô ò WÙÐFÐQUÔVÒVðWûô
 ò WÙÐAÀ3ÐQUÔVÒVðWús$   ÁA# ÁA< Á#A9Á8A9Á<BÂBc                óh  — t        j                  t        j                  «      5  | j	                  d¬«       	 ddd«       y# 1 sw Y   nxY wt        j                  t
        «      5  | j                  «        ddd«       n# 1 sw Y   nxY w| j                  | j                  fD ]B  }t        j                  t
        «      5  |�|j                  «        ddd«       Œ8# 1 sw Y   ŒAxY w t        j                  t        j                  «      5  | j                  d¬«       ddd«       y# 1 sw Y   yxY w)z±Reap a terminated script without blocking forever: a surviving descendant can hold the pipe
    write ends open, so bound the drain and abandon the pipes (output is not needed).g      @rv   N)r‰   rŠ   ry   r|   ÚcommunicaterH   r}   ÚstdoutÚstderrÚcloser~   )r   Ústreams     r   Ú_drain_script_pipesr    Ø   sö   € ô 
×	Ñ	œZ×6Ñ6Ó	7ñ Ø×Ñ ÐÔ%Ø÷÷ ñ úô 
×	Ñ	œWÓ	%ñ Ø�	‰	Œ÷÷ ñ úà—;‘; §¡Ð,ò ˆÜ× Ñ ¤Ó)ñ 	ØÐ!Ø—‘”÷	÷ 	ñ 	úðô 
×	Ñ	œZ×6Ñ6Ó	7ñ Ø�	‰	˜#ˆ	Ô÷÷ ñ ús/   ¤AÁA
Á&B Â B	Ã CÃC%	ÄD(Ä(D1c                óÖ   — t        j                  |j                  dd«      «      dz  dz  }|j                  «       st        j                  d|«       | |gS dt        |«      ›d�}| d||gS )	ax  Bootstrap a cron script under the base interpreter with ``.pth`` support. Overlay mode puts
    the venv on ``PYTHONPATH``, but ``.pth`` files are only processed by ``site.addsitedir()``, so
    editable installs would be invisible; bootstrap via addsitedir + ``runpy.run_path`` (keeps
    ``__file__``/``sys.path[0]`` semantics). Plain invocation if the venv is unresolvable.rU   rQ   rR   rS   z}Windows cron script: venv site-packages %s not found; running without .pth processing (editable installs may be unimportable)z,import os, runpy, site, sys;site.addsitedir(z¢);script = sys.argv[1];sys.argv = [script] + sys.argv[2:];sys.path.insert(0, os.path.dirname(os.path.abspath(script)));runpy.run_path(script, run_name='__main__')z-c)r   r   r   Úis_dirr   r   r]   )rf   rh   Úscript_pathrj   Ú	bootstraps        r   Ú_windows_cron_bootstrap_argvr¥   è   s…   € ô —K‘K §¡°¸rÓ BÓCÀeÑKÈoÑ]€MØ×ÑÔ!ä�‰ðNàô	ð ˜KÐ(Ð(ð	Ü˜}Ó-Ð0ð 16ð	6ð ð ˜˜i¨Ð5Ð5r   c                ó@  — t        j                  «       dz  }t        |«       |j                  «       }dt	        | «      v rdd| ›�fS 	 t        j
                  | «      j                  «       }|j                  «       r|j                  «       n||z  j                  «       }	 |j                  |«       |j                  «       sdd|› d	|› d
�fS |j                  «       sdd|› �fS |dfS # t        t        t        f$ r
 dd| ›�fcY S w xY w# t        $ r dd|› d| ›�fcY S w xY w)u  Validate a job script path; ``(path, None)`` or ``(None, error)``. Scripts MUST resolve
    inside HERMES_HOME/scripts/ (relative, absolute and ``~`` paths are all validated â€” path
    traversal / absolute-path injection); contract of lifecycle_guard._expand_candidate_path.Úscriptsú Nz*Blocked: script path contains a NUL byte: z5Blocked: script path is not a valid filesystem path: z=Blocked: script path resolves outside the scripts directory (z): zScript not found: z<. Cron scripts are looked up only in this profile's folder (zr); if the job was copied from another profile, copy the script there too, or edit the job with `hermes cron edit`.zScript path is not a file: )r   Ú_get_hermes_homer   r_   r]   r   Ú
expanduserÚ
ValueErrorÚRuntimeErrorrH   Úis_absoluteÚrelative_tor\   Úis_file)r£   Úscripts_dirÚscripts_dir_resolvedr   Úpaths        r   Ú_resolve_script_pathr³     sg  € ô ×)Ñ)Ó+¨iÑ7€KÜ�[Ô!Ø&×.Ñ.Ó0Ðð ”�[Ó!Ñ!ØÐAÀ+ÀÐQÐQÐQð]Ü�k‰k˜+Ó&×1Ñ1Ó3ˆð  ŸO™OÔ-ˆ3�;‰;Œ=°KÀ#Ñ4E×3NÑ3NÓ3P€Dð
Ø×ÑÐ-Ô.ð �;‰;Œ=ð Ø   ð 'Ø$Ð%ð &BðCð
ð 	
ð
 �<‰<Œ>ØÐ2°4°&Ð9Ð9Ð9Ø�ˆ:Ðøô/ œ¤gÐ.ò ]àÐLÈ[ÈOÐ\Ð\Ò\ð]ûô ò 
ØðØ$Ð% S¨¨ð9ð
ò 	
ð
ús$   Á#C& ÂD Ã&DÄDÄDÄDc                ó†  — | j                   j                  «       dv r_t        j                  d«      xs# t        j
                  j                  d«      rdnd}|€di d| j                  ›d�fS |t        | «      gi dfS t        t        j                  «      \  }}|rt        ||t        | «      «      |dfS |t        | «      g|dfS )u  ``(argv, env_overlay, error)`` for a validated script. Interpreter by extension â€” the
    shebang is deliberately NOT honoured (small, auditable surface): ``.sh``/``.bash`` â†’ bash,
    else ``sys.executable`` (Windows uv-venv overlay gets the .pth bootstrap).>   ú.shú.bashÚbashz	/bin/bashNzCannot run .sh/.bash script z{: bash not found on PATH. On Windows, install Git for Windows (which ships Git Bash) or rewrite the script as Python (.py).)ÚsuffixrI   ÚshutilÚwhichra   r²   ÚisfilerZ   r]   rn   rW   Ú
executabler¥   )r²   Ú_bashrf   rh   s       r   Ú_script_argvr¾   /  sÈ   € ð ‡{�{×ÑÓÐ.Ñ.ä—‘˜VÓ$Ò^¼¿¹¿¹À{Ô8S©ÐY]ˆØˆ=Ø˜Ø.¨t¯y©y¨mð <9ð 9ðð ð
 ”s˜4“yÐ! 2 tÐ+Ð+Ü=¼c¿n¹nÓMÑ€J�ÙÜ+¨J¸ÄSÈÃYÓOÐQ\Ð^bÐbÐbØœ˜D›	Ð" K°Ð5Ð5r   c           	     ó  — t        | «      \  }}|€d|fS t        «       }t        |«      \  }}}|€d|fS 	 ddlm} dddœ}	t
        j                  dk(  r t        «       t        t        d	d«      z  d
ddœ}	 |d¬«      }
|
j                  |«       t        j                  |ft        j                  t        j                  d|xs t        |j                  «      |
dœ|	¤Ž}t        j                   «       |z   }	 |�'|j#                  «       rt%        |«       t'        |«       y|t        j                   «       z
  }|dk  r t%        |«       t'        |«       dd|› d|› �fS 	 |j)                  t+        d|«      ¬«      \  }}	 |xs dj/                  «       }|xs dj/                  «       }	 ddlm}  ||«      } ||«      }|j:                  dk7  rOd|j:                  › �g}|r|j=                  d|› �«       |r|j=                  d|› �«       ddj?                  |«      fS d|fS # t        j,                  $ r Y �Œ>w xY w# t4        $ r$}t6        j9                  d|«       dx}}Y d}~Œ¤d}~ww xY w# t4        $ r}dd|› �fcY d}~S d}~ww xY w)uÊ  Execute a cron job's script and return ``(success, output)``; on failure *output* is the
    error message for the LLM to report. Env goes through ``build_subprocess_env`` (SECURITY.md
    Â§2.3). ``workdir`` sets the subprocess cwd only; the Python process cwd is NEVER mutated (an
    ``os.chdir()`` would leak into concurrent gateway sessions).

    Args: script_path: Path to the script. Relative paths are resolved against HERMES_HOME/scripts/.
    Absolute and ~-prefixed paths are also validated to ensure they stay within the scripts dir. workdir:
    Optional absolute path to use as the script's cwd. When set, the subprocess runs in this directory
    instead of the scripts-dir parent. See #69396.
    NFr   )Úbuild_subprocess_envTÚreplace)Ústart_new_sessionÚerrorsrO   ÚCREATE_NEW_PROCESS_GROUPr>   )rs   r?   rÃ   )Ústrip_launch_profile)rœ   r�   ÚtextÚcwdÚenv)Fz5Script cancelled because cron fire ownership was lostzScript timed out after zs: gš™™™™™¹?rv   rQ   )Úredact_sensitive_textz/Failed to redact sensitive text from output: %sz[REDACTED - redaction failed]zScript exited with code zstderr:
zstdout:
Ú
zScript execution failed: ) r³   r2   r¾   Útools.environments.localrÀ   rW   rX   r   r‹   ry   ÚupdateÚPopenÚPIPEr]   rY   ÚtimeÚ	monotonicÚis_setr™   r    r›   Úminr|   r   Úagent.redactrÉ   r   r   r   Ú
returncoderc   re   )r£   ÚworkdirÚcancel_eventr²   ÚerrÚscript_timeoutÚargvrh   rÀ   Úpopen_kwargsrÈ   r   ÚdeadlineÚ	remainingÚ
stdout_rawÚ
stderr_rawrœ   r�   rÉ   ÚeÚpartsr*   s                         r   Ú_run_job_scriptrá   C  sµ  € ô % [Ó1�I€Dˆ#Ø€|Ø�cˆzÐÜ(Ó*€NÜ)¨$Ó/Ñ€Dˆ+�sØ€|Ø�cˆzÐðQ8ÝAð "&Øñ(
ˆô �<‰<˜7Ò"ä!3Ó!5Üœ*Ð&@À!ÓDñ"Eð $Ø#ñ%ˆLñ #¸Ô=ˆØ�
‰
�;Ôô
 ×ÑØðFÜ#Ÿ™´·±ÀtØÒ+œ3˜tŸ{™{Ó+°ñFà8DñFˆô —>‘>Ó# nÑ4ˆØð Ð'¨L×,?Ñ,?Ô,AÜ+¨DÔ1Ü# DÔ)ØUØ ¤4§>¡>Ó#3Ñ3ˆIØ˜AŠ~Ü+¨DÔ1Ü# DÔ)ð Ð 7¸Ð7GÀsÈ4È&ÐQÐQÐQðØ)-×)9Ñ)9Ä#ÀcÈ9ÓBUÐ)9Ó)VÑ&�
˜JØð Ò" ×)Ñ)Ó+ˆØÒ" ×)Ñ)Ó+ˆð	>Ý:Ù*¨6Ó2ˆFÙ*¨6Ó2ˆFð
 �?‰?˜aÒØ/°·±Ð/@ÐAÐBˆEÙØ—‘˜y¨¨Ð1Ô2ÙØ—‘˜y¨¨Ð1Ô2Ø˜$Ÿ)™) EÓ*Ð*Ð*Ø�Vˆ|Ðøô- ×,Ñ,ò Úðûô ò 	>Ü�N‰NÐLÈaÔPØ=Ð=ˆF•Vûð	>ûô ò 8ØÐ1°#°Ð7Ð7Õ7ûð8úsy   µC'I% Ä;I% ÅH Å9)I% Æ#H5 Æ9AI% ÈI% ÈH2È.I% È1H2È2I% È5	I"È>IÉI% ÉI"É"I% É%	I?É.I:É4I?É:I?c                óÀ   — t        j                  t        j                  «       j                  | f|d¬«      }	 |j                  «        |S # t        $ r
  |«        Y yw xY w)zËStart ``loop_fn`` on a daemon thread inside a copy of the current context (multiplexed
    profile ContextVars). On failure calls ``fail_log()`` inside the except (traceback intact) and
    returns None.T)Útargetr’   rZ   ÚdaemonN)Ú	threadingÚThreadÚcontextvarsÚcopy_contextrz   Ústartr   )Úloop_fnrZ   Úfail_logÚthreads       r   Ú_start_heartbeat_threadrí   ­  s^   € ô ×ÑÜ×'Ñ'Ó)×-Ñ-°W°JÀTÐRVôX€FðØ�‰Œð €Møô ò ÙŒ
Ùðús   ¸A
 Á
AÁAc                óx  ‡‡	‡
— | j                  d«      }| j                  d«      }t        |t        «      rt        |j                  d«      xs d«      ndŠ	t        |t        «      r|j                  d«      dk(  r‰	st	        |||¬«      S t        | j                  d«      xs d«      Št        j                  «       Š
dˆˆ	ˆ
fd	„}t        |d
ˆfd„«      }|€t	        |||¬«      S 	 t	        |||¬«      ‰
j                  «        |j                  d¬«       S # ‰
j                  «        |j                  d¬«       w xY w)ux  Run a cron script while heartbeating its owned one-shot claim. A long script can outlive
    the stale-claim TTL; without a heartbeat another scheduler would re-dispatch the one-shot.
    Recurring/unclaimed runs have no durable claim â†’ no thread. The owner is captured from the
    dispatched job, never re-read, so a stale runner cannot extend a replacement owner's claim.ÚscheduleÚ	run_claimÚbyrQ   ÚkindÚonce)rÕ   rÖ   Úidc                 ó  •— ‰j                  t        j                  «      s9	 t        j                  ‰ ‰¬«       ‰j                  t        j                  «      sŒ8y y # t        $ r t
        j                  d‰ d¬«       Y ŒEw xY w)N)Úexpected_ownerz+Job '%s': script run_claim heartbeat failedTrŽ   )r~   r   Ú_RUN_CLAIM_HEARTBEAT_SECONDSÚheartbeat_run_claimr   r   r    )Újob_idÚownerÚstops   €€€r   Ú_heartbeat_loopz=_run_job_script_with_claim_heartbeat.<locals>._heartbeat_loopÌ  sf   ø€ Ø—)‘)œF×?Ñ?Ô@ðcÜ×*Ñ*¨6À%ÕHð —)‘)œF×?Ñ?×@øô ò cÜ—‘ÐJÈFÐ]a�Öbðcús   ¢A Á!A?Á>A?zcron-script-claim-heartbeatc                 ó4   •— t         j                  d‰ d¬«      S )Nz4Job '%s': could not start script run_claim heartbeatTrŽ   )r   r    )rù   s   €r   ú<lambda>z6_run_job_script_with_claim_heartbeat.<locals>.<lambda>Õ  s   ø€ ”—‘ØBÀFÐUYð ó [€ r   ru   rv   )r–   r—   )
r   r   r   r]   rá   rå   ÚEventrí   Úsetre   )Újobr£   rÕ   rÖ   rï   Úclaimrü   Úheartbeat_threadrù   rú   rû   s           @@@r   Ú$_run_job_script_with_claim_heartbeatr  »  s  ú€ ð �w‰w�zÓ"€HØ�G‰G�KÓ €EÜ*4°U¼DÔ*AŒC�—	‘	˜$“Ò% 2Ô&Àr€EÜ�x¤Ô&¨8¯<©<¸Ó+?À6Ò+IÉeÜ˜{°GÈ,ÔWÐWä�—‘˜“Ò$ "Ó%€FÜ�?‰?Ó€D÷cô /ØÐ6ó	[óÐð
 ÐÜ˜{°GÈ,ÔWÐWð+Ü˜{°GÈ,ÔWà�‰Œ
à×Ñ cÐÕ*øð 	�‰Œ
à×Ñ cÐÕ*ús   Ã%D Ä$D9)Ú	scheduler)r–   zOptional[int])r!   r]   r"   r]   r#   zCallable[[Any], Any]r$   r]   )r–   r   )r–   r   )rJ   r   r–   údict[str, str])rf   r]   r–   ztuple[str, dict[str, str]])r   zsubprocess.Popenr–   r—   )rf   r]   rh   r  r£   r]   r–   z	list[str])r£   r]   r–   z$tuple[Optional[Path], Optional[str]])r²   r   r–   z9tuple[Optional[list[str]], dict[str, str], Optional[str]])NN)r£   r]   rÕ   úOptional[str]rÖ   úOptional[_CancelEventLike]r–   útuple[bool, str])rZ   r]   r–   zOptional[threading.Thread])
r  r   r£   r]   rÕ   r  rÖ   r  r–   r	  )3Ú__doc__Ú
__future__r   r‰   rç   Úloggingra   r¹   r…   ry   rW   rå   rÏ   Úcron.env_settingsr   Ú	cron.jobsr   Úpathlibr   Útypingr   r   r	   r
   Úhermes_cli._subprocess_compatr   Úcron.schedulerr   Ú	getLoggerr   r   r+   r2   r6   r8   r<   rM   rn   r€   rx   r™   r    r¥   r³   r¾   rá   rí   r  r   r  r   © r   r   ú<module>r     se  ðñõ #ã Û Û Û 	Û Û Û Û 
Û Û Ý .Ý &Ý ß 9Ó 9å <áÝ/ð 
ˆ×	Ñ	Ð+Ó	,€ó,ðØðØ!ðØ*>ðØGJóó2Lð$ "Ð óIó2óó)óDó(Mó$Ió6ð 6Øð6Ø"0ð6Ø?Bð6ØGPó6ó2+ó\6ð* 04Ø/3ðg8Øðg8Ø,ðg8à,ðg8ð óg8óTð ;?Ø/3ð%+Ø	ð%+Øð%+Ø*7ð%+à,ð%+ð ó%+öT %r   