+
    „WÝj9P  ã                   óÚ  € R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHtHtHt ^ RIHt ^ RIHt ]P(                  ! R4      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R6R R llt]P>                  R R l4       t R R lt!R  R! lt"R" R# lt#R$ R% lt$R& t%R' R( lt&R6R) R* llt'R+R	/R, R- llt(R. R/ lt)R0 R1 lt*R2 R3 lt+R4 R5 lt,R# )7zsDashboard cron helpers: per-profile scheduler I/O, job validation/normalisation, cron fire and
gateway forwarding.
N)ÚHTTPException©ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Úcfg_get)ÚCronJobCreatezhermes_cli.web_serverÚstrip_trailing_slashFc                óR   € V ^8„  d   QhR\         R\        R\        \        ,          /# )é   Úvaluer   Úreturn)r   Úboolr   Ústr)Úformats   "ÚNC:\Users\johnn\AppData\Local\hermes\hermes-agent\hermes_cli\web_server_cron.pyÚ__annotate__r      s&   € ÷ ñ œsð ¼Tð ÌhÔWZÍmñ ó    c                óŠ   € V f   R # \        V 4      P                  4       pV'       d   VP                  R4      pT;'       g    R # )NÚ/)r   ÚstripÚrstrip)r   r   Útexts   &$ r   Ú_cron_optional_textr      s9   € Ø‚}ÙÜˆu‹:×ÑÓ€DßØ�{‰{˜3ÓˆØ�<ˆ<�4Ðr   c                ó\   € V ^8„  d   QhR\         R\        \        \        ,          ,          /# )r   r   r   )r   r   Úlistr   )r   s   "r   r   r      s#   € ÷ ñ œSð ¤X¬d´3­iÕ%8ñ r   c                 óT  € \        V \        4      '       d   \        P                  ! R V 4      pM!\        V \        \
        34      '       d   T pMR# V Uu. uF=  p\        V4      P                  4       '       g   K$  \        V4      P                  4       NK?  	  ppT;'       g    R# u upi )z[\n,]N)Ú
isinstancer   ÚreÚsplitr   Útupler   )r   Ú	raw_itemsÚitemÚitemss   &   r   Ú_cron_string_listr'      sy   € Ü�%œ×ÒÜ—H’H˜X uÓ-‰	Ü	�EœD¤%˜=×	)Ò	)Ø‰	áÙ+4ÓJ©9 4¼¸D»	¿¹×8IÔŒS�‹Y�_‰_Ö©9€EÐJØ�=ˆ=�DÐùò Ks   Á!B%Á:B%c                óR   € V ^8„  d   QhR\         R\        R\        \        ,          /# )r   r   Úprofile_homer   )r   r   r   r   )r   s   "r   r   r   (   s&   € ÷ ñ ¬Cð ¼tð ÌÔQTÍñ r   c                ó  € \        V 4      pV'       g   R# VR,          P                  4       p\        V4      P                  4       pVP	                  4       '       d   VP                  4       MW4,          P                  4       p VP                  V4      pTP                  4       '       g   \        RRT 2R7      hTP                  4       '       g   \        RRT 2R7      h\        T4      #   \         d   p\        RRT 2R7      ThRp?ii ; i)zFValidate a dashboard-selected cron script against the profile sandbox.NÚscriptsé�  zscript must be inside ©Ústatus_codeÚdetailzscript does not exist: zscript is not a file: )r   Úresolver   Ú
expanduserÚis_absoluteÚrelative_toÚ
ValueErrorr   ÚexistsÚis_filer   )r   r)   r   Úscripts_rootÚraw_pathÚ	candidateÚrelativeÚexcs   &&      r   Ú _normalize_dashboard_cron_scriptr<   (   s÷   € ä˜uÓ%€DßÙØ  9Õ,×5Ñ5Ó7€LÜ�D‹z×$Ñ$Ó&€HØ&.×&:Ñ&:×&<Ò&<�× Ñ Ô"À<ÕCZ×BcÑBcÓBe€IðfØ×(Ñ(¨Ó6ˆð ×Ñ×ÒÜ¨Ð6MÈiÈ[Ð4YÔZÐZØ×Ñ×ÒÜ¨Ð6LÈYÈKÐ4XÔYÐYÜˆx‹=Ðøô ô fÜ¨Ð6LÈ\ÈNÐ4[Ô\ÐbeÐeûðfús   ÂC+ Ã+DÃ6DÄDc                óJ   € V ^8„  d   QhR\         \        \        3,          RR/# )r   Újobr   N)Údictr   r   )r   s   "r   r   r   ;   s&   € ÷ 	jñ 	j´´S¼#°Xµð 	jÀ4ñ 	jr   c                 ó”  € \        V P                  R 4      4      p\        V P                  R4      4      p\        V P                  R4      4      ;'       g    \        V P                  R4      4      pV P                  R4      '       d   V'       g   \        RRR7      hR# V'       g"   V'       g   V'       g   \        RR	R7      hR# R# R# )
ÚpromptÚscriptÚskillsÚskillÚno_agentr,   zno_agent=True requires a scriptr-   Nz2agent cron jobs require a prompt, skill, or script)r   Úgetr'   r   )r>   rA   rB   rC   s   &   r   Ú&_validate_dashboard_cron_effective_jobrG   ;   s“   € Ü  §¡¨Ó!2Ó3€FÜ  §¡¨Ó!2Ó3€FÜ˜sŸw™w xÓ0Ó1×XÐXÔ5FÀsÇwÁwÈwÓGWÓ5X€FØ
‡w�wˆz×ÒßÜ¨CÐ8YÔZÐZÙß—f§Ü¨Ð4hÔiÐiñ !'‘f‰Fr   c                ó`   € V ^8„  d   QhR\         \        \        ,          ,          R\        RR/# )r   ÚrefsÚprofile_namer   N)r   r   r   )r   s   "r   r   r   G   s1   € ÷ 	Zñ 	Z´¼¼c½Õ0Cð 	ZÔSVð 	ZÐ[_ñ 	Zr   c           	      óð   € T ;'       g    R Fe  p\        V\        4      '       d&   VP                  4       P                  4       R 8X  d   K>  \	        VRV4      '       d   KS  \        RRV RV R2R7      h	  R# )	ÚselfÚget_jobr,   zcontext_from job 'z' not found in profile 'Ú'r-   N© )r    r   r   ÚlowerÚ_call_cron_for_profiler   )rI   rJ   Úrefs   && r   Ú%_validate_dashboard_cron_context_fromrS   G   sq   € Ø�zˆz�rŠzˆô �cœ3×Ò C§I¡I£K×$5Ñ$5Ó$7¸6Ô$AÙÜ% l°I¸s×CÔCÜØØ+¨C¨5Ð0HÈÈÐVWÐXôZð Zó r   c                ó\   € V ^8„  d   QhR\         \        \        \        3,          ,          /# ©r   r   )r   r?   r   r   )r   s   "r   r   r   S   s    € ÷ 5ñ 5œT¤$¤s¬C x¥.Õ1ñ 5r   c                 óø   € ^ RI Hp  ^ RIHp  VP	                  RRRR7       UUu. uF  w  r#RVR\        V4      RVR8H  /NK  	  upp# u uppi   \         d!    \        P                  R	4       T ! T4      u # i ; i)
z«Minimal profile records (callers only consume ``name``); avoids ``list_profiles()``,
whose config parsing, gateway probes and skill counts are GIL pressure on large pools.)Ú_fallback_profile_dicts©ÚprofilesT)Ú	multiplexÚinclude_standaloneÚinclude_parkedÚnameÚpathÚ
is_defaultÚdefaultzJFailed to list profiles for cron dashboard; falling back to directory scan)	Úhermes_cli.web_server_profilesrW   Ú
hermes_clirY   Úprofiles_to_server   Ú	ExceptionÚ_logÚ	exception)rW   Úprofiles_modr]   Úhomes       r   Ú_cron_profile_dictsri   S   s�   € õ GÝ3ð5ð +×<Ñ<ÀtÐ`dÐuyÐ<Ôzô|áz‘
�ð �T˜6¤3 t£9¨l¸DÀIÑ<MÓNÙzò|ð 	|ùó |øô ô 5Ü�‰ÐcÔdÙ& |Ó4Ò4ð5ús"   ŽA ¥AÁA ÁA Á(A9Á8A9c                ó$   € V ^8„  d   QhR\         /# rU   ©r   )r   s   "r   r   r   a   s   € ÷ @ñ @œsñ @r   c                 óX   €  ^ RI Hp  V ! 4       pTR9   d   R# T#   \         d     R# i ; i)u£  Profile to target when a cron request carries no explicit ``profile``.

A desktop pool backend runs one process per profile, but these endpoints route storage through
the profiles tree via ``_cron_profile_home`` â€” a hardcoded "default" would write a non-default
profile's job into ~/.hermes. ``custom`` (HERMES_HOME outside the profiles tree) has no
profile-dir equivalent, so it keeps the legacy "default" fallback.
)Úget_active_profile_namer`   )r`   Úcustom)Úhermes_cli.profilesrm   rd   )rm   r]   s     r   Ú_cron_default_profilerp   a   s;   € ðÝ?Ù&Ó(ˆð Ð 5Ô5ˆ9Ð?¸4Ð?øô ô Úðús   ‚ š)¨)c                óh   € V ^8„  d   QhR\         \        ,          R\        \        \        3,          /# ©r   Úprofiler   )r   r   r#   r   )r   s   "r   r   r   q   s'   € ÷ 6ñ 6¤¬¥ð 6´%¼¼T¸	Õ2Bñ 6r   c                ór  € ^ RI Hp T ;'       g    \        4       P                  4       ;'       g    Rp VP	                  V4      pVP                  V4       TP                  T4      '       g   \        RRT R2R7      hY1P                  T4      3#   \         d   p\        R\        T4      R7      hRp?ii ; i)	z=Resolve a profile query value to (profile_name, HERMES_HOME).rX   r`   r,   r-   Ni”  z	Profile 'z' does not exist.)rb   rY   rp   r   Únormalize_profile_nameÚvalidate_profile_namer4   r   r   Úprofile_existsÚget_profile_dir)rs   rg   ÚrawÚcanonÚes   &    r   Ú_cron_profile_homer|   q   s«   € å3Ø×-Ð-Ô+Ó-×
4Ñ
4Ó
6×
CÐ
C¸)€Cð<Ø×3Ñ3°CÓ8ˆØ×*Ñ*¨5Ô1ð ×&Ñ& u×-Ò-Ü¨°iÀ¸wÐFWÐ4XÔYÐYØ×.Ñ.¨uÓ5Ð5Ð5øô	 ô <Ü¨´C¸³FÔ;Ð;ûð<ús   ´"B ÂB6ÂB1Â1B6c                ó®   € V ^8„  d   QhR\         \        \        3,          R\        R\        R\        \
        ,          R\         \        \        3,          /# )r   r>   rs   rh   Úheartbeat_ager   )r?   r   r   r   r   Úfloat)r   s   "r   r   r      sH   € ÷ 4ñ 4Ü	Œc”3ˆh�ð4Ü"%ð4Ü-1ð4ÜBJÌ5Å/ð4ä	Œ#Œsˆ(…^ñ4r   c                 ó:   € / V CR VRVR\        V4      RVR8H  RV/C# )rs   rJ   Úhermes_homeÚis_default_profiler`   Úscheduler_heartbeat_age_srk   )r>   rs   rh   r~   s   &&&&r   Ú_annotate_cron_jobr„      s=   € ð	4Ø
ð	4à�7Ø˜Ø”s˜4“yØ˜g¨Ñ2ð 	$ ]ñ	4ð 	4r   c                ó$   € V ^8„  d   QhR\         /# )r   rh   r   )r   s   "r   r   r   �   s   € ÷ *ñ *œDñ *r   c              #  óê   "  € ^ RI Hp ^ RIHpHp V! \        V 4      4      p VP                  V 4      ;_uu_ 4        Vx € RRR4       V! V4       R#   + '       g   i     L; i  T! T4       i ; i5i)zþPoint HERMES_HOME and the cron.jobs store at one profile's home for the block.

The dashboard is a single process inspecting many profiles; cron.jobs' execution-context
override keeps these calls from retargeting a concurrent desktop ticker's load/save.
)Újobs©Úreset_hermes_home_overrideÚset_hermes_home_overrideN)Úcronr‡   Úhermes_constantsr‰   rŠ   r   Úuse_cron_store)rh   Ú	cron_jobsr‰   rŠ   Útokens   &    r   Ú_cron_store_scoper�   Ž   sX   é € õ 'ßUÙ$¤S¨£YÓ/€Eð*Ø×%Ñ% d×+Õ+ØŠO÷ ,ñ 	# 5Ö)÷ ,×+ûñ 	# 5Õ)üs3   ‚ A3£A& ¼AÁA& Á	
A3ÁA#	ÁA& Á&
A0Á0A3c                óF   € V ^8„  d   QhR\         \        ,          R\        /# ©r   Útarget_profileÚ	func_name©r   r   )r   s   "r   r   r   Ÿ   s   € ÷ ñ ¬8´C­=ð ÄSñ r   c           	     ó¢  € \        V 4      w  rE\        V4      ;_uu_ 4       pVR8X  d   ^ RIHp V! V/ VB pM\	        Wa4      ! V/ VB pVP                  4       p	RRR4       \        X\        4      '       d   V U
u. uF  p
\        W¤VX	4      NK  	  up
# \        V\        4      '       d   \        W„VX	4      # V#   + '       g   i     Lh; iu up
i )zERun a cron.jobs helper against the selected profile's cron directory.Ú
create_job)Ú&create_job_with_scheduler_registrationN)
r|   r�   Úcron.schedulerr˜   ÚgetattrÚget_ticker_heartbeat_ager    r   r„   r?   )r“   r”   ÚargsÚkwargsrJ   rh   rŽ   r˜   Úresultr~   Újs   &&*,       r   rQ   rQ   Ÿ   s¼   € ä+¨NÓ;Ñ€LÜ	˜4×	 Ô	  IØ˜Ô$ÝMÙ;¸TÐLÀVÑL‰Fä˜YÔ2°DÐC¸FÑCˆFØ!×:Ñ:Ó<ˆ÷ 
!ô �&œ$×ÒÙRXÓYÑRXÈQÔ" 1°D¸-ÖHÑRXÑYÐYÜ�&œ$×ÒÜ! &¸¸mÓLÐLØ€M÷ 
!×	 üò Zs   ¡8B9Á;CÂ9C		c                ó>   € V ^8„  d   QhR\         \        ,          RR/# )r   r“   r   Nr•   )r   s   "r   r   r   °   s"   € ÷ hñ h´h¼sµmð hÈñ hr   c                óØ  €  \        V 4      w  r^ RIHpHp \	        V4      ;_uu_ 4        V! 4       p\        WS4      '       * pV'       dK   \        R \        4        4       4      ^8”  d,   \        P                  RWP                  4        RRR4       R# VP                  4        RRR4       R#   + '       g   i     R# ; i  \         d    \        P                  RT RR7        R# i ; i)aP  Best-effort provider reconcile against one profile's job store.

Fail-closed for external providers on a multi-profile dashboard: an external ``reconcile``
converges its REMOTE (non-profile-scoped) registry toward one profile's jobs.json and cancels
every remote entry absent from it, so reconciling profile B would disarm profile A's one-shots.
Until the provider contract carries a profile identity, skip unscoped external reconciles; the
affected profile re-arms on its next fire/start (idempotent via dedup_key). The built-in
provider re-reads jobs.json each tick and stays a no-op here.
)ÚInProcessCronSchedulerÚresolve_cron_schedulerc              3   óV   "  € T F  qP                  R 4      '       g   K  ^x € K!  	  R# 5i)r]   N©rF   )Ú.0Úps   & r   Ú	<genexpr>Ú4_notify_cron_provider_for_profile.<locals>.<genexpr>À   s   é € ÐRÑ+@ aÇEÁEÈ&ÇM§¢Ó+@ùs   ‚)Ÿ
)zÚSkipping cron provider reconcile for profile %s: external provider '%s' reconcile is not profile-scoped and would disarm other profiles' armed one-shots. The mutated profile re-arms idempotently on its next fire/start.Nz2Cron provider reconciliation failed for profile %sT©Úexc_info)r|   Úcron.scheduler_providerr¢   r£   r�   r    Úsumri   re   Úwarningr]   Úon_jobs_changedrd   Údebug)r“   Ú_profile_namerh   r¢   r£   ÚproviderÚexternals   &      r   Ú!_notify_cron_provider_for_profiler´   °   s½   € ðhÜ0°Ó@ÑˆßZÜ˜t×$Õ$Ù-Ó/ˆHÜ% hÓGÔGˆHßœCÑRÔ+>Ô+@ÓRÓRÐUVÔVÜ—‘ð;ð =KÏMÉMôð ÷ %Ñ$ð ×$Ñ$Ô&÷ %×$×$Ò$ûô ô hÜ�
‰
ÐGÈÐbfˆ
×gðhús;   ‚(C ªA B.Â
C ÂB.Â$C Â.B?	Â9C Â?C Ã#C)Ã(C)c                óF   € V ^8„  d   QhR\         \        ,          R\        /# r’   r•   )r   s   "r   r   r   Î   s   € ÷ ñ ¬X´c­]ð Äsñ r   c                óJ   € \        W.VO5/ VB pV'       d   \        V 4       V# )zAApply a cron store mutation and reconcile its scheduler provider.)rQ   r´   )r“   r”   rœ   r�   rž   s   &&*, r   Ú_mutate_cron_for_profiler·   Î   s&   € ä# NÐOÀÒOÈÑO€FßÜ)¨.Ô9Ø€Mr   c                óF   € V ^8„  d   QhR\         R\        \         ,          /# )r   Újob_idr   )r   r   )r   s   "r   r   r   Ö   s   € ÷ ñ ¤3ð ¬8´C­=ñ r   c                 ó(  a € \        4        F‚  p\        VP                  R 4      ;'       g    R4      pV'       g   K1  \        VRR4      p\        ;QJ d    V 3R lV 4       F  '       g   K   RM	  RM! V 3R lV 4       4      '       g   K€  Vu # 	  R# )r]   Ú Ú	list_jobsTc              3   ó‚   <"  € T F4  qP                  R 4      S8H  ;'       g    VP                  R4      S8H  x € K6  	  R# 5i)Úidr]   Nr¥   )r¦   rŸ   r¹   s   & €r   r¨   Ú)_find_cron_job_profile.<locals>.<genexpr>Ü   s4   øé € ÐNÉÀA�u‰u�T‹{˜fÑ$×?Ð?¨¯©¨f«¸Ñ(?Ô?Ëùs   ƒ?¢?FN)ri   r   rF   rQ   Úany)r¹   rs   r]   r‡   s   f   r   Ú_find_cron_job_profilerÁ   Ö   sj   ø€ Ü&Ö(ˆÜ�7—;‘;˜vÓ&×,Ð,¨"Ó-ˆßÙÜ% d¨K¸Ó>ˆß‹3ÔNÉÓN�3�3Š3ÔNÉÓN×NÔNØŠKñ )ñ r   c              �  óâ   "  € ^ RI Hp \        P                  ! V 4      '       d   \	        R4      hV! V .VO5/ VB G Rj  x€L
 p\        P
                  ! V4      '       d   \	        R4      hV#  L-5i)zBRun cron dashboard profile/job I/O outside the FastAPI event loop.)Úrun_in_threadpoolz2_run_cron_dashboard_io only accepts sync callablesNz:_run_cron_dashboard_io sync callable returned an awaitable)Ústarlette.concurrencyrÃ   ÚinspectÚiscoroutinefunctionÚ	TypeErrorÚisawaitable)Úfuncrœ   r�   rÃ   rž   s   &*,  r   Ú_run_cron_dashboard_iorÊ   á   sc   é € å7Ü×"Ò" 4×(Ò(ÜÐLÓMÐMÙ$ TÐ;¨DÒ;°FÑ;×;€FÜ×Ò˜6×"Ò"ÜÐTÓUÐUØ€Mñ <ùs   ‚=A/¿A-Á A/Á A/c                ó(   € V ^8„  d   QhR\         RR/# )r   r{   r   N)rd   )r   s   "r   r   r   ì   s   € ÷ Hñ H¬ð H°tñ Hr   c                ól   € ^ RI Hp \        W4      '       d   \        RV P	                  4       R7      V hR# )z¬Re-raise a cron partial failure (job saved, external scheduler registration failed) as
HTTP 424 with the structured envelope. Shared by every dashboard cron-create surface.)ÚCronSchedulerRegistrationErrori¨  r-   N)r™   rÍ   r    r   Úto_dict)r{   rÍ   s   & r   Ú!_raise_if_cron_registration_errorrÏ   ì   s,   € õ >Ü�!×4Ò4Ü¨°A·I±I³KÔ@ÀaÐGñ 5r   c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   Úbodyrs   )r   r   r   )r   s   "r   r   r   ô   s   € ÷ -<ñ -<¤ð -<¼Ä½ñ -<r   c                 ó�  €  \        V4      w  r#\        V P                  V4      p\        V P                  4      p\        V P
                  4      p\        Wb4       \        V P                  4      pV P                  pVeO   \        V\        4      '       d+   \        V\        4      '       g   V^8  d   \        RRR7      h^ RIHp	 V	! V4      p\        RV P                   RVRVRV/4       \#        VR	3RV P                   ;'       g    R
RV P$                  RV P&                  RV P                  R\)        V P*                  4      ;'       g    RRVR\)        V P,                  4      R\)        V P.                  4      R\)        V P0                  RR7      RVRVR\        V P2                  4      R\)        V P4                  4      RV/R U
u/ uF!  p
W P6                  9   g   K  V
\9        W
4      bK#  	  up
B # u up
i   \         d    h \:         d<   p\=        T4       \>        PA                  R4       \        R\C        T4      R7      hR p?ii ; i)Nr,   z!repeat must be a positive integerr-   )Únormalize_repeat_valuerA   rC   rB   rE   r—   r»   Úscheduler]   ÚrepeatÚdeliverÚlocalÚmodelr²   Úbase_urlT)r   Úcontext_fromÚenabled_toolsetsÚworkdirzPOST /api/cron/jobs failed)ÚpausedÚpaused_reason)"r|   r<   rB   r'   rC   rÚ   rS   r   rE   rÕ   r    Úintr   Ú	cron.jobsrÓ   rG   rA   r·   rÔ   r]   r   rÖ   rØ   r²   rÙ   rÛ   rÜ   Úmodel_fields_setrš   rd   rÏ   re   rf   r   )rÑ   rs   rJ   r)   rB   rC   rÚ   rE   rÕ   rÓ   Úkeyr{   s   &&          r   Ú_create_cron_job_syncrã   ô   sG  € ð,<Ü%7¸Ó%@Ñ"ˆÜ1°$·+±+¸|ÓLˆÜ" 4§;¡;Ó/ˆÜ(¨×):Ñ):Ó;ˆÜ-¨lÔIÜ˜Ÿ™Ó&ˆð
 —‘ˆØÒÜ˜&¤#×&Ò&¬z¸&Ä$×/GÒ/GÈFÐUVÌJÜ#Ø #Ð,OôQð Qå8Ù+¨FÓ3ˆFÜ.Ø�t—{‘{ H¨f°hÀÈ
ÐT\Ð]ô	_ä'ØØñ1ð —;‘;×$Ð$ "ð1ð —]‘]ð	1ð
 —‘ð1ð —;‘;ð1ô (¨¯©Ó5×@Ð@¸ð1ð ð1ô & d§j¡jÓ1ð1ô )¨¯©Ó7ð1ô )¨¯©ÈTÔRð1ð ð1ð &ð1ô /¨t×/DÑ/DÓEð1ô (¨¯©Ó5ð1ð  ð!1ñ" 3Nó 0Ñ2M¨3Ø×.Ñ.Ñ.ô 'ˆs”G˜DÓ&Ò&Ñ2Mñ 0ñ#1ð 	1ùò"0øäô ØÜô <Ü)¨!Ô,Ü�‰Ð3Ô4Ü¨´C¸³FÔ;Ð;ûð<úsD   ‚DG4 ÄAG4 Å	A:G4 ÇG/ÇG/Ç*G4 Ç/G4 Ç4IÈ	IÈ
6I É IÚforcec                óH   € V ^8„  d   QhR\         R\         R\        R\        /# )r   rs   r¹   rä   r   )r   r   )r   s   "r   r   r   $  s/   € ÷ Iñ I¬ð I´Sð IÄDð IÔUYñ Ir   c               óþ  € \        V 4      w  r4^ RIHpHpHp \        V4      ;_uu_ 4        V! 4       pV'       dS   V! V4      '       g   \        RR\        VRR4       R2R7      h\        VP                  VRRR	R
7      4      uuRRR4       # V! VR4      '       d)   \        VP                  VRRR	R7      4      uuRRR4       # \        VP                  VRRR7      4      uuRRR4       #   + '       g   i     R# ; i)u¶  Run ONE due cron job for ``profile`` via the scheduler provider's ``fire_due``.

DEPRECATED for NAS webhook fires â€” superseded by :func:`_forward_cron_fire_to_gateway`, since
fires must run in the GATEWAY process (it owns the live adapters; the standalone path here
cannot serve relay-fronted platforms or E2EE rooms). Retained for the dashboard trigger path
and external callers on the web_deps late-binding seam; do not add new uses.
)Úprovider_fire_due_acceptsÚprovider_supports_force_firer£   i™  zCron provider 'r]   rn   z6' does not support atomic forced firing of paused jobsr-   NT)ÚadaptersÚlooprä   Úmanual)ré   rê   rë   )ré   rê   )
r|   r¬   rç   rè   r£   r�   r   rš   r   Úfire_due)	rs   r¹   rä   r±   rh   rç   rè   r£   r²   s	   &&$      r   Ú_fire_cron_job_for_profilerí   $  sî   € ô -¨WÓ5Ñ€MßwÑwÜ	˜4×	 Õ	 Ù)Ó+ˆßÙ/°×9Ò9Ü#Ø #à)¬'°(¸FÀHÓ*MÐ)Nð OOð OôQð Qô
 ˜×)Ñ)¨&¸4ÀdÐRVÐ)ÓWÓX÷ 
!Ò	 ñ % X¨x×8Ò8Ü˜×)Ñ)¨&¸4ÀdÐSWÐ)ÓXÓY÷ 
!Ò	 ô �H×%Ñ% f°tÀ$Ð%ÓGÓH÷ 
!×	 ×	 Ó	 ús#   «C+Á	9C+ÂC+ÂC+ÃC+Ã+C<	c                ó<   € V ^8„  d   QhR\         R\        R\        /# )r   rh   râ   r   )r   r   )r   s   "r   r   r   ?  s!   € ÷ 5ñ 5œTð 5¬ð 5´ñ 5r   c                óL   € ^ RI Hp V! V R,          4      P                  VR4      # )z@One value from a profile's .env (``""`` when absent/unreadable).)Úload_env_filez.envr»   )Úagent.secret_scoperð   rF   )rh   râ   rð   s   && r   Ú_profile_env_valuerò   ?  s!   € å0á˜ �Ó'×+Ñ+¨C°Ó4Ð4r   c                ó<   € V ^8„  d   QhR\         R\        R\         /# )r   rs   rh   r   )r   r   )r   s   "r   r   r   F  s!   € ÷ ;4ñ ;4¤Cð ;4¬tð ;4¼ñ ;4r   c           	     óâ  € ^ RI Hp ^ RIpRp ^ RIHp V! 4       pYrvV'       d,   V R8w  d%   ^ R	IH	p RV! 4       rv\        P                  R
W4       ^ p	 ^ RIHp
Hp V! \        V4      4      p V! 4       pV
! V4       \        VRRRRRR7      pV'       d   \        V4      p	T	'       gD   T\!        4       8X  d   TP#                  RR4      M\%        TR4      p T'       d   \        T4      M^ p	T	;'       g    Rp	T'       d   T R8w  d
   RT	 RT  R2# RT	 R2#   \
         d    \        P                  RRR7        ELi ; i  T
! T4       i ; i  \
         d    ^ p	 Lºi ; i  \&         d    ^ p	 L‚i ; i)u¥  Loopback URL of the gateway api_server's cron-fire route.

Port resolution mirrors gateway/config.py's api_server load order for the LISTENER-OWNER
profile: ``platforms.api_server.extra.port`` in its config.yaml, then ``API_SERVER_PORT``
(process env for the active profile, the profile's own .env otherwise), then 8642. Loopback
is safe: dashboard and gateway share a network namespace in every supported deployment.

In multiplex mode only the DEFAULT profile's api_server is bound and exposes per-profile
mirrors under ``/p/<profile>/â€¦``, so a non-default profile's port must be read from the
default home (a secondary's own API_SERVER_PORT is a port nothing listens on).
)Úload_configNF)Údefault_gateway_multiplexesz>cron fire: multiplex detection failed; assuming single-profileTrª   r`   )Úget_default_hermes_rootuj   cron fire: multiplex gateway â€” resolving api_server port for %s from the default profile's listener (%s)rˆ   Ú	platformsÚ
api_serverÚextraÚport)r`   ÚAPI_SERVER_PORTr»   iÂ!  zhttp://127.0.0.1:z/p/z/api/cron/fire)Úhermes_cli.configrõ   ÚosÚ!hermes_cli.gateway_multiplex_moderö   rd   re   r°   rŒ   r÷   Úinfor‰   rŠ   r   r
   rß   rp   Úgetenvrò   r4   )rs   rh   rõ   Ú_osrZ   rö   Úlistener_profileÚlistener_homer÷   rû   r‰   rŠ   r�   Úprofile_cfgry   s   &&             r   Ú_gateway_fire_endpointr  F  sˆ  € õ .ÛØ€Iðdõ 	RÙ/Ó1ˆ	ð '.�mß�W 	Ô)Ý<Ø*3Ñ5LÓ5N˜-Ü�	‰	ð7Ø8?ô	
ð
 €Dð÷ 	ZÙ(¬¨]Ó);Ó<ˆð	.Ù%›-ˆKá& uÔ-Ü�k ;°¸gÀvÐW[Ô\ˆßÜ�s“8ˆD÷ ð  Ô#8Ó#:Ô:ð �J‰JÐ(¨"Ô-ä# MÐ3DÓEð 	ð	ß"”3�s”8¨ˆDð �<ˆ<�4€Dß�W 	Ô)Ø" 4 &¨¨G¨9°NÐCÐCØ˜t˜f NÐ3Ð3øôO ô dÜ�
‰
ÐSÐ^bˆ
×cðdûñ* ' uÕ-ûô ô ØŠðûô ô 	ØŠDð	úsS   ŽD ÁE Á-D> Á4,E ÃE ÃE Ä"D;Ä:D;Ä>
EÅE ÅEÅEÅE.Å-E.c                ó¢   € V ^8„  d   QhR\         R\         R\         R\        \        \        \        \         \
        3,          3,          ,          /# )r   rs   r¹   Úauthorizationr   )r   r   r#   rß   r?   r   )r   s   "r   r   r   „  sD   € ÷ "ñ "Üð"Üð"Ü.1ð"Ü6>¼uÄSÌ$ÌsÔTWÈxÍ.ÐEXÕ?YÕ6Zñ"r   c              ƒ  ó¼  "  € \        V 4      w  r4\        P                  ! \        W44      G Rj  x€L
 p^ RIp VP                  RR7      ;_uu_4       GRj  x€L
 pVP                  VRV/RV/R7      G Rj  x€L
 pRRR4      GRj  x€L
   XP                  4       p
\        T
\        4      '       g   RT
/p
TP                   T
3#  L• Lo LQ LC  + GRj  x€L 
 '       g   i     LZ; i  \         d7   p	\        P                  RT\        T	4      P                  T	4        Rp	?	R# Rp	?	ii ; i  \         d#    RXP                  ;'       g    R	R
,          /p
 L¼i ; i5i)u¸  Forward a Chronos fire callback byte-preserved to the gateway api_server on loopback.

The dashboard is the hosted deployment's only public HTTP door, but cron execution belongs to
the GATEWAY process (live adapters â†’ relay-fronted platforms and E2EE rooms work). Same job_id
and NAS bearer; the gateway re-verifies the JWT itself.

Returns ``(status_code, body)``, or ``None`` when the gateway is unreachable (scale-to-zero
wake, restart, api_server disabled). The caller maps None to 503 so NAS retries (store CAS
de-dupes a double fire) â€” unless :func:`_gateway_intentionally_stopped`, in which case it
drops the fire with 200: retrying into an operator-stopped gateway can never succeed.
Ng      $@)Útimeoutr¹   ÚAuthorization)ÚjsonÚheaderszDcron fire forward to %s failed (%s: %s); returning 503 for NAS retryry   r»   :Niô  N)r|   ÚasyncioÚ	to_threadr  ÚhttpxÚAsyncClientÚpostrd   re   r®   ÚtypeÚ__name__r  r   r    r?   r.   )rs   r¹   r  r±   rh   Úurlr  ÚclientÚrespr;   rÑ   s   &&&        r   Ú_forward_cron_fire_to_gatewayr  „  s'  é € ô -¨WÓ5Ñ€MÜ×!Ò!Ô"8¸-ÓN×
N€CÛðØ×$Ñ$¨TÐ$×2×2Ô2°fØŸ™ S°¸&Ð/AÈOÐ]jÐKk˜Ól×lˆD÷ 3×2ð
0Ø�y‰y‹{ˆô �dœD×!Ò!Ø�tˆ}ˆØ×Ñ˜TÐ!Ð!ñ Oñ 3Ùl÷ 3×2×2Ò2ûäô Ü�‰Ð[Ð]`ÔbfÐgjÓbk×btÑbtÐvyÔzÝûðûô
 ô 0Ø˜Ÿ	™	Ÿ˜ R¨Õ.Ð/Šð0üsÈ   ‚,E®C¯E¸C( ÁCÁC( ÁCÁ6CÁ7CÁ;C( ÂC
ÂC( ÂD, Â)EÃC( ÃCÃ
C( ÃC%	ÃCÃ
C%	ÃC%	Ã C( Ã$EÃ%C( Ã(D)Ã3+D$ÄEÄ$D)Ä)EÄ,EÅEÅEÅEÅEc                óF   € V ^8„  d   QhR\         \        ,          R\        /# rr   )r   r   r   )r   s   "r   r   r   £  s   € ÷ ñ ¬H´S­Mð ¼dñ r   c                óø   € ^ RI p VP                  \        V 4      ^,          R,          P                  RR7      4      p\	        V\
        4      ;'       d    VP                  R4      R8H  #   \         d     R# i ; i)uQ  True when the profile's gateway is stopped BY OPERATOR INTENT.

Reads the durable ``desired_state`` of gateway_state.json, written only by the s6 lifecycle
commands (``gateway stop`` persists "stopped"; start/restart persist "running") and never
during transient windows (crash loops, drains, wakes) â€” so it splits "retry will eventually
succeed" from "retry can never succeed". Deliberately does NOT fall back to the volatile
``gateway_state`` runtime field: a legacy/crashed file must stay on the retryable-503 path.
Any resolution or parse failure returns False (fail open toward retry).
Nzgateway_state.jsonz	utf-8-sig)ÚencodingÚdesired_stateÚstoppedF)r  Úloadsr|   Ú	read_textr    r?   rF   rd   )rs   Ú_jsonÚdatas   &  r   Ú_gateway_intentionally_stoppedr"  £  so   € ó ðØ�{‰{Ô.¨wÓ7¸Õ:Ð=QÕQ×\Ñ\ÐfqÐ\ÓrÓsˆÜ˜$¤Ó%×PÐP¨$¯(©(°?Ó*CÀyÑ*PÐPøÜô Úðús   †AA* ÁA* Á*A9Á8A9)N)-Ú__doc__r  Ú
contextlibÚloggingrÅ   r!   Úfastapir   Úpathlibr   Útypingr   r   r   r   r	   rý   r
   Úhermes_cli.web_modelsr   Ú	getLoggerre   r   r'   r<   rG   rS   ri   rp   r|   r„   Úcontextmanagerr�   rQ   r´   r·   rÁ   rÊ   rÏ   rã   rí   rò   r  r  r"  rO   r   r   Ú<module>r,     sÚ   ðñó Û Û Û Û 	Ý !Ý ß 3Õ 3Ý %Ý /ð ×ÒÐ0Ó1€ðÀE÷ õõõ&	jõ	Zõ5õ@õ 6÷4ð ×Ñô*ó ð*õ õ"hõ<õòõH÷-<ð`IÈ5÷ Iõ65õ;4õ|"÷>r   