
    `gj5                        d 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	m
Z
 ddlmZmZ ddd	d
dddZ ej                  d      Z ej                  d      Z ej                  d      Zddddd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	e   fdZdede
e   defdZdddddded e	e   d!e	e   d"e	e   d#e	e
e      defd$Z	 d7d%d&ddddd'd(ed)ed*ed+e	e   d,e	e   d"e	e   d-e	e   ddfd.Zd&ddddd/d0ed)ed1ed e	e   d!e	e   d"e	e   d#e	e
e      defd2Zd0ed3edefd4Z dddddd0ed e	e   d!e	e   d"e	e   d#e	e
e      ddfd5Z!d8d6Z"y)9u  ``hermes logs`` — view and filter Hermes log files.

Supports tailing, following, session filtering, level filtering,
component filtering, and relative time ranges.  All log files live
under ``~/.hermes/logs/``.

Usage examples::

    hermes logs                    # last 50 lines of agent.log
    hermes logs -f                 # follow agent.log in real time
    hermes logs errors             # last 50 lines of errors.log
    hermes logs gateway -n 100    # last 100 lines of gateway.log
    hermes logs gui -f            # follow gui.log (dashboard/pty/ws)
    hermes logs desktop -f        # follow desktop.log (Electron app boot/backend)
    hermes logs --level WARNING    # only WARNING+ lines
    hermes logs --session abc123   # filter by session ID substring
    hermes logs --component tools  # only tool-related lines
    hermes logs --since 1h         # lines from the last hour
    hermes logs --since 30m -f     # follow, starting 30 min ago
    N)datetime	timedelta)Path)OptionalSequence)get_hermes_homedisplay_hermes_homez	agent.logz
errors.logzgateway.logzgui.logzdesktop.logzmcp-stderr.log)agenterrorsgatewayguidesktopmcpz(^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})z'\s(DEBUG|INFO|WARNING|ERROR|CRITICAL)\sz?\s(?:DEBUG|INFO|WARNING|ERROR|CRITICAL)(?:\s+\[.*?\])?\s+(\S+):            )DEBUGINFOWARNINGERRORCRITICAL	since_strreturnc                 X   | j                         j                         } t        j                  d|       }|syt	        |j                  d            }|j                  d      }t        |      t        |      t        |      t        |      d	|   }t        j                         |z
  S )
zParse a relative time string like '1h', '30m', '2d' into a datetime cutoff.

    Returns None if the string can't be parsed.
    z^(\d+)\s*([smhd])$Nr   r   )seconds)minutes)hours)days)smhd)	striplowerrematchintgroupr   r   now)r   r'   valueunitdeltas        B/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/logs.py_parse_sincer/   ?   s    
 !'')IHH*I6EAE;;q>Du%u%U#E"	
 E <<>E!!    linec                     t         j                  |       }|sy	 t        j                  |j	                  d      d      S # t
        $ r Y yw xY w)zAExtract timestamp from a log line. Returns None if not parseable.Nr   z%Y-%m-%d %H:%M:%S)_TS_REr'   r   strptimer)   
ValueErrorr1   r!   s     r.   _parse_line_timestampr7   S   sI    TA  -@AA s   $? 	A
Ac                 V    t         j                  |       }|r|j                  d      S dS )z"Extract the log level from a line.r   N)	_LEVEL_REsearchr)   r6   s     r.   _extract_levelr;   ^   s'    A1771:$$r0   c                 V    t         j                  |       }|r|j                  d      S dS )z(Extract the logger name from a log line.r   N)_LOGGER_NAME_REr:   r)   r6   s     r.   _extract_logger_namer>   d   s'    t$A1771:$$r0   prefixesc                 R    t        |       }|y|j                  t        |            S )z@Check if a log line's logger name starts with any of *prefixes*.F)r>   
startswithtuple)r1   r?   names      r.   _line_matches_componentrD   j   s'    %D|??5?++r0   	min_levelsession_filtersincecomponent_prefixesrF   rG   rH   rI   c                    |t        |       }|||k  ry|;t        |       }|.t        j                  |d      t        j                  |d      k  ry||| vry|t	        | |      syy)z.Check if a log line passes all active filters.Fr   T)r7   r;   _LEVEL_ORDERgetrD   )r1   rF   rG   rH   rI   tslevels          r.   _matches_filtersrO   r   s     "4(>b5jt$q)L,<,<Y,JJ!%%&t-?@r0   2   F)	num_linesfollowrN   sessionrH   	componentlog_namerQ   rR   rN   rS   rT   c          
         t         j                  |       }|Bt        d| ddj                  t	        t                             t        j                  d       t               dz  |z  }|j                         s.t        d|        t        d       t        j                  d       d}	|r1t        |      }	|	$t        d	|d
       t        j                  d       |r|j                         nd}
|
r,|
t        vr$t        d|d       t        j                  d       d}|r_ddlm} |j                         }||vr@dj                  t	        |            }t        d|d|        t        j                  d       ||   }|
duxs |duxs
 |	duxs |du}	 t        ||||
||	|      }g }|
r|j#                  d|
        |r|j#                  d|        |r|j#                  d|        |r|j#                  d|        |rddj                  |       dnd}|rt        dt%                d| | d       nt        dt%                d| | d| d       D ]  }t        |d        |sy	 t'        ||
||	|       y# t         $ r' t        d|        t        j                  d       Y 	w xY w# t(        $ r t        d        Y yw xY w)!ag  Read and display log lines, optionally following in real time.

    Parameters
    ----------
    log_name
        Which log to read: ``"agent"``, ``"errors"``, ``"gateway"``, ``"gui"``.
    num_lines
        Number of recent lines to show (before follow starts).
    follow
        If True, keep watching for new lines (Ctrl+C to stop).
    level
        Minimum log level to show (e.g. ``"WARNING"``).
    session
        Session ID substring to filter on.
    since
        Relative time string (e.g. ``"1h"``, ``"30m"``).
    component
        Component name to filter by (e.g. ``"gateway"``, ``"tools"``).
    NzUnknown log: z. Available: z, r   logszLog file not found: u?   (Logs are created when Hermes runs — try 'hermes chat' first)zInvalid --since value: z$. Use format like '1h', '30m', '2d'.zInvalid --level: z/. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL.r   )COMPONENT_PREFIXESzUnknown component: has_filtersrF   rG   rH   rI   zPermission denied: zlevel>=zsession=z
component=zsince=z [] z--- /logs/z (Ctrl+C to stop) ---z (last z) ---endrE   z
--- stopped ---)	LOG_FILESrL   printjoinsortedsysexitr   existsr/   upperrK   hermes_loggingrX   r%   
_read_tailPermissionErrorappendr	   _follow_logKeyboardInterrupt)rU   rQ   rR   rN   rS   rH   rT   filenamelog_pathsince_dtrF   rI   rX   component_lower	availablerZ   linesfilter_partsfilter_descr1   s                       r.   tail_logrv      s   : }}X&Hh\tyy	AR7S6TUV 6)H4H??$XJ/0OP H&+E94XYZHHQK!&DIYl2!%*YZ[ 5#//+"44		&);"<=I'	}M)MNHHQK/@ 	 	*$	*4	* T)	 8YK%.w!)>PR Lgi[12hwi01j45fUG,-5ABtyy./q1rK(*+6(K=H]^_(*+6(K=PY{Z_`a d #H	'#8J	L;  #H:./>  #!"#s$   J 4J8 ,J54J58KKrY   pathrZ   c          
          |r>t        | t        |dz  d            }|D cg c]  }t        |||||      r| }	}|	| d S t        | |      S c c}w )zRead the last *num_lines* matching lines from a log file.

    When filters are active, we read more raw lines to find enough matches.
       i  rE   N)_read_last_n_linesmaxrO   )
rw   rQ   rZ   rF   rG   rH   rI   	raw_lineslfiltereds
             r.   ri   ri      su      'tSR-FG	 
Y/=U3EG 
 
 
$$!$	22
s   Anc                    	 | j                         j                  }|dk(  rg S |dk  r.t        | ddd      5 }|j                         }ddd       | d S t        | d      5 }d	}g }|}|dkD  rt	        |      |d
z   k  rt        ||      }||z  }|j                  |       |j                  |      }	|	j                  d      }
|r|
d   |d   z   |d<   |
dd |z   }n|
}t        |dz  d      }|dkD  rt	        |      |d
z   k  rg }|D ]9  }|j                         s	 |j                  |j                  dd      dz          ; || d cddd       S # 1 sw Y   xY w# t        $ r& |j                  |j                  d      dz          Y w xY w# 1 sw Y   yxY w# t        $ r> t        | ddd      5 }|j                         }ddd       n# 1 sw Y   nxY w| d cY S w xY w)zEfficiently read the last N lines from a file.

    For files under 1MB, reads the whole file (fast, simple).
    For larger files, reads chunks from the end.
    r      rutf-8replaceencodingr   Nrbi    r      
r   i   )r   
zlatin-1)statst_sizeopen	readlineslenminseekreadsplitr$   rk   decode	Exception)rw   r   sizef	all_lines
chunk_sizers   pos	read_sizechunkchunk_linesdecodedraws                r.   rz   rz     s   /yy{""19I 9dC')D *KKM	*aRS>! $ 	 JEC'c%jAE1
C0	y sy)#kk%0  +2q9E!H',u4E'E a7
 'c%jAE1  G Ayy{ANN3::gi:#H4#OP	A A23<;	  	 * *@ ! ANN3::i#84#?@A7	  	 >  $gi@ 	&AI	& 	& 	&!~	s    F F EF F "B!FF%E F		F EF  ,FFFFFF F G%6G	G%G	G%$G%c                (   t        | ddd      5 }|j                  dd       	 |j                         }|r<t        |||||      rAt	        |d	       t
        j                  j                          nt        j                  d
       d# 1 sw Y   yxY w)z9Poll a log file for new content and print matching lines.r   r   r   r   r   r   rE   r\   r^   g333333?N)
r   r   readlinerO   ra   rd   stdoutflushtimesleep)rw   rF   rG   rH   rI   r   r1   s          r.   rl   rl   U  s     
dC')	<  	q!::<D#DI3A7IK $B'JJ$$&

3    s   A8BBc            	         t               dz  } | j                         st        dt                d       yt        dt                d       d}t	        | j                               D ]T  }|j                         s|j                  dk(  s%|j                         j                  }t        j                  |j                         j                        }|d	k  r| d
}n|dk  r
|d	z  dd}n	|dz  dd}t        j                         |z
  }|j                         dk  rd}nw|j                         dk  r t        |j                         dz         d}nD|j                         dk  r t        |j                         dz         d}n|j!                  d      }t        d|j"                  dd|dd|        d}W |st        d       yy)z%Print available log files with sizes.rW   zNo logs directory at r]   NzLog files in z/logs/:
Fz.logi   Br   z.1fKBMB<   zjust nowi  zm agoiQ zh agoz%Y-%m-%dz  z<25 z>8z   Tu;     (no log files yet — run 'hermes chat' to generate logs))r   rf   ra   r	   rc   iterdiris_filesuffixr   r   r   fromtimestampst_mtimer*   total_secondsr(   strftimerC   )log_dirfoundentryr   mtimesize_strageage_strs           r.   	list_logsr   m  s   &(G>>%&9&;%<FCD	M-/0	
:;E)* ==?u||v5::<''D**5::<+@+@AEd{"V1:#"Tk#.b1"k237r:,,.5(C  "R'$""$t+ !2!2!4r!9:;5A""$u, !2!2!4t!;<=UC..4Buzz#&a}CyABE), KL r0   )r
   )r   N)#__doc__r&   rd   r   r   r   pathlibr   typingr   r   hermes_constantsr   r	   r`   compiler3   r9   r=   rK   strr/   r7   r;   r>   boolrD   rO   r(   rv   listri   rz   rl   r    r0   r.   <module>r      s  * 
 
  (  % A  		 
?	@ BJJAB	
 "** A!aQO"C "HX$6 "( (: % %# %%s %x} %,# ,# ,4 ,  $$( $26
 } SM	
 H !#/ 
@ l# !#l#l# l# 	l#
 C=l# c]l# C=l# }l# 
l#f #$( $263
33 	3
 }3 SM3 H3 !#/3 
3:5T 5c 5d 5v  $$( $26 
  }  SM	 
 H  !#/  
 0 Mr0   