
    `gjvL                        d Z ddlZddlZddlmZ ddlmZmZmZ  ee	      j                  j                  j                         Zej                  j                  d ee             ddlmZmZ ddlmZ ddeee      deee      fd	Zd
 ZdefdZddZddefdZd Zddee   defdZd ZddZ d Z!d Z"dedededefdZ#d Z$y)z
Cron subcommand for hermes CLI.

Handles standalone cron management commands like list, create, edit,
pause/resume/run/remove, status, and tick.
    N)Path)IterableListOptional)Colorscolor)"contains_gateway_lifecycle_commandskillsreturnc                     || y | g}nt        |      }g }|D ]8  }t        |xs d      j                         }|s#||vs(|j                  |       : |S )N )liststrstripappend)single_skillr
   	raw_items
normalizeditemtexts         B/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/cron.py_normalize_skillsr      sj    ~!N	L	J $4:2$$&D
*d#$     c                  D    ddl m} t        j                   |di |       S )Nr   )cronjob )tools.cronjob_toolsr   jsonloads)kwargscronjob_tools     r   	_cron_apir"   -   s    ;::l,V,--r   c                  X    	 ddl m}   |        j                  xs dS # t        $ r Y yw xY w)u8  Name of the resolved cron scheduler provider ('builtin', 'chronos', …).

    Best-effort + offline (``resolve_cron_scheduler`` reads config and the
    provider's ``is_available()`` contract forbids network). Returns 'builtin'
    on any failure so callers fall back to the historical ticker-based checks.
    r   resolve_cron_schedulerbuiltin)cron.scheduler_providerr%   name	Exceptionr$   s    r   _active_cron_provider_namer*   3   s0    B%',,9	9 s    	))c                  v   	 t               dk7  ryddlm}   |        ry	 t	        t        dt        j                               t	        t        dt        j                               t	        t        dt        j                               t	        t        dt        j                               y# t        $ r Y yw xY w)	u  Warn that scheduled jobs won't fire unless the gateway is running.

    The cron ticker only runs inside the gateway (``_start_cron_ticker`` in
    gateway/run.py); there is no standalone cron daemon. Without a running
    gateway, ``next_run_at`` passes but jobs never fire and ``last_run_at``
    stays null — the most common cron support report (#51038). Surfacing this
    at create/list time, when the user is right there, prevents it.

    An external provider (e.g. Chronos) fires jobs via a NAS-mediated webhook,
    NOT the in-process ticker, so a momentarily-absent gateway process does not
    mean jobs won't fire — the warning would be a false alarm. Stay quiet for
    any non-builtin provider; the gateway-process heuristic only speaks to the
    built-in ticker's trigger.
    r&   Nr   find_gateway_pidsu@     ⚠  Gateway is not running — jobs won't fire automatically.z*     Start it with: hermes gateway installzI                    sudo hermes gateway install --system  # Linux serversz&     Check status:  hermes cron status)	r*   hermes_cli.gatewayr-   r)   printr   r   YELLOWDIMr,   s    r   _warn_if_gateway_not_runningr2   B   s    
%'948  
%RTZTaTa
bc	%<fjj
IJ	%[]c]g]g
hi	%8&**
EF  s   B, B, ,	B87B8show_allc           
      ~	   ddl m}  ||       }|sGt        t        dt        j
                               t        t        dt        j
                               yt                t        t        dt        j                               t        t        dt        j                               t        t        d	t        j                               t                |D ]  }|j                  d
d      }|j                  dd      }|j                  d|j                  di       j                  dd            }|j                  d|j                  dd      rdnd      }|j                  dd      }|j                  d      xs i }	|	j                  d      }
|	j                  dd      }|
r| d|
 nd}|j                  d      xs dg}t        |t              r|g}dj                  |      }|j                  d      xs |j                  d       r|d    gng }|dk(  rt        d!t        j                        }ng|dk(  rt        d"t        j                        }nG|j                  dd      rt        d#t        j                        }nt        d$t        j                        }t        d%t        |t        j                         d&|        t        d'|        t        d(|        t        d)|        t        d*|        t        d+|        |rt        d,dj                  |              |j                  d-      }|rt        d.|        |j                  d/      r't        d0t        d1t        j
                         d2       |j                  d3      }|rt        d4|        |j                  d5      }|rr|j                  d6d      }|d7k(  rt        d7t        j                        }n/t        | d8|j                  d9d       t        j                        }t        d:| d%|        |j                  d;      }|r1t        d<|j                  d=d       d%|j                  d
d              |j                  d>      }|r)t        d?t        d@t        j                         d&|        t                 t                y)AzList all scheduled jobs.r   	list_jobsinclude_disabledzNo scheduled jobs.zFCreate one with 'hermes cron create ...' or the /cron command in chat.Nu   ┌─────────────────────────────────────────────────────────────────────────┐uO   │                         Scheduled Jobs                                  │u   └─────────────────────────────────────────────────────────────────────────┘id?r(   z	(unnamed)schedule_displayschedulevaluestateenabledT	scheduledpausednext_run_atrepeattimes	completed/u   ∞deliverlocal, r
   skillz[paused]z[completed]z[active]z
[disabled]   z    Name:      z    Schedule:  z    Repeat:    z    Next run:  z    Deliver:   z    Skills:    scriptz    Script:    no_agentz    Mode:      zno-agentz# (script stdout delivered directly)workdirz    Workdir:   last_statuslast_run_atokz: 
last_errorz    Last run:  latest_executionz    Execution: statuslast_delivery_error    u   ⚠ Delivery failed:)	cron.jobsr6   r/   r   r   r1   CYANget
isinstancer   joinr0   BLUEGREENREDr2   )r3   r6   jobsjobjob_idr(   r<   r>   next_runrepeat_inforepeat_timesrepeat_completed
repeat_strrG   deliver_strr
   rU   rM   rO   rP   last_runstatus_displayrT   delivery_errs                           r   	cron_listrl   c   sD   #h/De(&**56e\^d^h^hij	G	%  t  v|  vA  vA  B  C	%acicncn
op	%  t  v|  vA  vA  B  C	G Hs#wwv{+77-swwz2/F/J/J7TW/XY	40HhW77=#.
 ggh'-2"w/&??;:=I()<.9u
 '')$1	gs#iGii("R9IG~rH:v}}5Fk!=&++6FWWY%:v||4F<4F5/0&:;v&'z*+
|,-z*+}-.ODIIf$5#678"OF8,-77:OE*fjj$A#BBefg'')$OG9-. ggm,ww}c2Hd"!&tV\\!:!&+bs9S8T'UW]WaWa!bOH:R/?@A77#56!"2"6"6x"E!Fb#''c235
 ww45D5v}}EFa~VWQHT !"r   c                  "    ddl m}   | d       y)zRun due jobs once and exit.r   tickT)verboseN)cron.schedulerro   rn   s    r   	cron_tickrr      s    #r   rb   limitc                 h   ddl m}  || |      }|st        d       y|D ]  }t        |j                  dd       d|j                  d	d      d
d|j                  dd       d|j                  dd       d|j                  dd       	       |j                  d      st        d|d            y)z,Show indexed durable cron execution history.r   )list_executions)rb   rs   z$No cron execution attempts recorded.Nr9   r:   rK   rU   z<9z  job=rb   z	  source=source
claimed_aterrorrW   )cron.executionsru   r/   rZ   )rb   rs   ru   recordsrecords        r   	cron_runsr|      s    /V59G45 ,zz$$%R

8S(A"'E F::h,-Yvzz(C7P6QQSzz,,-/	

 ::gD)*+,r   c            	         ddl m}  ddlm} t	                t               }|dk7  rqt	        t        d| dt        j                               t	        t        dt        j                               t	                t         | d	             t	                y
 |       }|rhddl m}m}m} |dz  dz   } |       } |       }	|m||kD  rht	        t        dt        |       dt        j                               t	        ddj!                  t#        t$        |                    t	        d       n2|n|	l|	|kD  rgt	        t        dt        |	       dt        j                               t	        ddj!                  t#        t$        |                    t	        d       nt	        t        dt        j                               t	        ddj!                  t#        t$        |                    |rt	        dt        |       d       nYt	        t        dt        j&                               t	                t	        d       t	        d       t	        d       t	        d       t	                t         | d	             t	                y
)zShow cron execution status.r   r5   r,   r&   u   ✓ Cron provider: uD    — jobs fire via the managed scheduler, not the in-process ticker.zq  (No ticker heartbeat is expected for an external provider; due jobs are delivered by an authenticated webhook.)Fr7   N)get_ticker_heartbeat_ageget_ticker_success_ageTICKER_INTERVAL_SECONDS      uN   ⚠ Gateway is running but the cron ticker looks STALLED — no heartbeat for zs (expected every ~60s).z  PID: rI   z>  Cron jobs may NOT be firing. Restart: hermes gateway restartuF   ⚠ Gateway and cron ticker are running, but no tick has succeeded in u   s — ticks may be failing.z.  Check the gateway log for 'Cron tick error'.u<   ✓ Gateway is running — cron jobs will fire automaticallyz  Ticker heartbeat: zs agou6   ✗ Gateway is not running — cron jobs will NOT firez   To enable automatic execution:z9    hermes gateway install    # Install as a user servicezS    sudo hermes gateway install --system  # Linux servers: boot-time system servicez4    hermes gateway            # Or run in foreground)rX   r6   r.   r-   r/   r*   r   r   r^   r1   _print_active_jobs_summaryr~   r   r   intr0   r\   mapr   r_   )
r6   r-   providerpidsr~   r   r   STALE_AFTERhb_ageok_ages
             r   cron_statusr      s+   #4	G)+H9 	e!( ,) )LL
 	
 	eCJJ
 	
 	"9e#DED	
 	
 .1B6)+')&;"6%$$'K=0HJ 
 GDIIc#tn5678RSF$66K;O %  #F},GI 
 GDIIc#tn5678BC%VX^XdXdefGDIIc#tn5678!,S[M?@eLfjjYZ01IJcdDE	Gy%@A	Gr   c                     | rc| D cg c]%  }|j                  d      s|j                  d      ' }}t        dt        |        d       |rt        dt        |              yyt        d       yc c}w )zvPrint the '<N> active job(s)' + next-run line shared by every status
    path (built-in ticker AND external provider).rB   rK   z active job(s)  Next run: z  No active jobsN)rZ   r/   lenmin)r`   j	next_runss      r   r   r   3  sp     37Pa155;OQUU=)P	P3t9+^,-LY 012  	 ! Qs
   A2A2c                    t        d| j                  | j                  t        | dd       t        | dd       t        | dd       t        | dd       t	        t        | dd       t        | dd             t        | dd       t        | dd       t        | d	d
      xs d       }|j                  d      s7t        t        d|j                  dd       t        j                               yt        t        d|d    t        j                               t        d|d           t        d|d           |j                  d      r t        ddj                  |d                 |j                  di       }|j                  d      rt        d|d           |j                  d	      rt        d       |j                  d      rt        d|d           t        d|d           t                y)Ncreater(   rG   rC   rJ   r
   rM   rO   rN   F)actionr<   promptr(   rG   rC   rJ   r
   rM   rO   rN   successzFailed to create job: rx   unknown error   zCreated job: rb     Name:   Schedule: r<   
  Skills: rI   ra   
  Script: 3  Mode: no-agent (script stdout delivered directly)  Workdir: r   rB   r   )r"   r<   r   getattrr   rZ   r/   r   r   r_   r^   r\   r2   )argsresultjob_datas      r   cron_creater   ?  s    {{T64(i.tXt,dGT* w!=wtXW[?\]tXt,i.z519TF ::i e,VZZ-Q,RSU[U_U_`a	%-x 012FLL
AB	HVF^$
%&	L
+,
-.zz(
499VH%56789zz%$H||H
8H-./0||JCD||IHY/012	L./
01 "r   c                    ddl m}m} 	  || j                        }|s1t	        t        d	| j                   t        j                               yt        |j                  d
      xs% |j                  d      sg n|j                  d      g      }t        t        | dd       t        | d
d             }t        d t        | dd             xs g }t        t        d t        | dd             xs g       }	d }
t        | dd      rg }
n:||}
n5|s|	r1|D cg c]	  }||	vs| }
}|D ]  }||
vs|
j                  |        t!        d| j                  t        | dd       t        | dd       t        | dd       t        | dd       t        | dd       |
t        | dd       t        | dd       t        | dd             }|j                  d      s7t	        t        d|j                  dd       t        j                               y|d   }t	        t        d|d    t        j"                               t	        d |d           t	        d!|d           |j                  d
      r!t	        d"d#j%                  |d
                 nt	        d$       |j                  d      rt	        d%|d           |j                  d      rt	        d&       |j                  d      rt	        d'|d           y# |$ rk}t	        t        t        |      t        j                               |j                  D ]&  }t	        d|d    d|j                  d      d       ( Y d }~yd }~ww xY wc c}w )(Nr   )AmbiguousJobReferenceresolve_job_refrK   r9   z	  (name: r(   )r   zJob not found: r
   rJ   
add_skillsremove_skillsclear_skillsFupdater<   r   rG   rC   rM   rO   rN   )r   rb   r<   r   r(   rG   rC   r
   rM   rO   rN   r   zFailed to update job: rx   r   ra   zUpdated job: rb   r   r   r   rI   z  Skills: noner   r   r   )rX   r   r   rb   r/   r   r   r   r_   matchesrZ   r   r   r   setr   r"   r^   r\   )r   r   r   ra   excmexisting_skillsreplacement_skillsr   r   final_skillsrJ   r   updateds                 r   	cron_editr   g  s1   @dkk* eodkk]3VZZ@A3778,d3777;KRURYRYZaRbQceO*74$+GQUW_aeIfg"4|T)JKQrJ)$ot0TU[Y[\MLt^U+		')	}+:Y%e=>XYY 	+EL(##E*	+ {{z40tXt,T64(i.tXt,tXt,i.z40F ::i e,VZZ-Q,RSU[U_U_`aUmG	%- 123V\\
BC	HWV_%
&'	L,-
./{{8
499WX%6789:{{8
78,-./{{:CD{{9GI./01q ! eCHfjj)* 	=ABqwiyv(9;<	=	( Zs$   K+ 	MM+M0A!MMr   success_verbc                    t        | |      }|j                  d      s:t        t        d|  d|j                  dd       t        j
                               y|j                  d      xs |j                  d	      xs i }t        t        | d|j                  d
|       d| dt        j                               | dv r5|j                  di       j                  d      rt        d|d   d           | dk(  rv|j                  di       }|j                  d      r%|j                  d      rdnd}t        d| d       y|j                  d      rt        d|d           yt        d       y)N)r   rb   r   z
Failed to z job: rx   r   r   ra   removed_jobr(   z (r   >   runresumerB   r   r   executedexecution_success	succeededfailedz  Ran now: .execution_skippedrK   z)  It will run on the next scheduler tick.r   )r"   rZ   r/   r   r   r_   r^   )r   rb   r   r   ra   outcomes         r   _job_actionr     s]   fV4F::i ejvzz'?/S.TUW]WaWabc
**U

>vzz-8
>BC	%<.swwvv'>&?r&KV\\
Z[""vzz%'<'@'@'OVE]=9:;<jj#77:%(WW-@%AkxGKy*+
 	 WW()Bs./012  =>r   c                 d   t        | dd      }||dk(  rt        | dd      }t        |       y|dk(  rt                y|dk(  rt                y|d	v r#t	        t        | d
d      t        | dd             y|dv rt        |       S |dk(  rt        |       S |dk(  rt        d| j                  d      S |dk(  rt        d| j                  d      S |dk(  rt        d| j                  d      S |dv rt        d| j                  d      S t        d|        t        d       t        j                  d       y)zHandle cron subcommands.cron_commandNr   allFr   rU   ro   >   runshistoryrb   rs   r   >   addr   editpausePausedr   Resumedr   	Triggered>   rmdeleteremover   RemovedzUnknown cron command: zNUsage: hermes cron [list|create|edit|pause|resume|run|remove|status|runs|tick]r   )r   rl   r   rr   r|   r   r   r   rb   r/   sysexit)r   subcmdr3   s      r   r   r     s3   T>40F~6)4.($$'$$/w1KL""4  7DKK::8T[[)<<5$++{;;++8T[[)<<	"6(
+,	
Z[HHQKr   )NN)r   N)F)Nr   )%__doc__r   r   pathlibr   typingr   r   r   __file__parentresolvePROJECT_ROOTpathinsertr   hermes_cli.colorsr   r   cron.lifecycle_guardr	   #_contains_gateway_lifecycle_commandr   r"   r*   r2   boolrl   rr   r   r|   r   r   r   r   r   r   r   r   r   <module>r      s    
  + +H~$$++335 3|$ % +
(3-1H T\]abe]fTg  .C GB[# [#|,hsm ,3 ,$Wt	"%P=@ S   *)r   