
    i	                    4   U 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Zddlm	Z	 ddl
mZ  ee          j        j                                        ZddlmZ ddlmZmZmZ ddlmZmZmZmZmZmZ ddlmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z% dd	l&m'Z'm(Z(  e	d
           G d d                      Z) e	d
           G d d                      Z*de+fdZ,de-de-dz  fdZ.de-de/fdZ0de-de/fdZ1de-de2de/fdZ3de+e-         fdZ4de5e-         de-dz  de+e-         ddfdZ6dde+e-         de/de5e-         fdZ7dde+dz  de/de5fd Z8	 dde+dz  de5e*         fd!Z9d"e:de5e:         fd#Z;d"e:d$e-de/fd%Z<dd&e/de=e/e/f         fd'Z>	 	 dd&e/d)e=e:d*f         de?e:e:f         fd+Z@d,e?e:e:f         de-dz  fd-ZAdd&e/de-dz  fd.ZBde?dz  fd/ZCde-dz  de?dz  fd0ZDddd1d2d&e/d3e-dz  d4e2de/fd5ZEd,e?e:e:f         de/fd6ZFd7ejG        de/fd8ZHdd&e/de/fd9ZIdd&e/ddfd:ZJdd&e/d3e-dz  de/fd;ZKde/fd<ZLdd&e/de)fd=ZMd>d?de=e-d*f         e5e-         z  d@e-dz  de:fdAZNdBe)ddfdCZOddDZPddEZQ	 	 ddFe/de+dz  de/de-fdGZRde/fdHZSde/fdIZTddJlUmVZVmWZWmXZX de/fdKZYdd&e/de/fdLZZde/fdMZ[de/fdNZ\de/fdOZ]de/fdPZ^dQZ_dRZ`de:fdSZaddTe:dz  de:fdUZbde:fdVZcdd&e/defdWZd G dX dYee          Zf G dZ d[ee          Zgdefd\Zhdefd]Zide/fd^Zjdd_Zkdd4e2de/fdaZld
dbdce/ddfddZmdee:dfe:dge:ddfdhZndd&e/de5e:         fdiZodd&e/de5e:         fdjZpddkdle5e:         d&e/dejq        fdmZrdd&e/de:fdnZsde5e:         fdoZtde/fdpZudqZve=e:d*f         ewdr<   dsZxe=e:d*f         ewdt<   de5e=e/ef                  fduZyde5e=e:ee/f                  fdvZzde/fdwZ{ddxZ|	 	 ddye/dze/de=e-e5e         f         fd{Z}dd|Z~d}e:ddfd~Zdde:dz  de=e:e:e:f         fdZdede:dz  fdZde:dz  fdZde:dz  fdZddFe/de=e:dz  e/f         fdZde=e/dz  e:f         fdZddZdefdZdefdZdedz  fdZde:fdZdede5e:         de5e:         fdZde5e:         de5e:         fdZde:de:de:fdZde:de:fdZdd&e/de:dz  de:fdZde:de:fdZde:de:fdZdd&e/de/fdZdd&e/de/fdZddee:de:dz  ddfdZddZdd&e/de/fdZdd&e/de/fdZd}e:ddfdZde2fdZddFe/d&e/de:dz  fdZdd&e/fdZdd}e:d&e/ddfdZdd&e/fdZdd&e/fdZdd&e/fdZdde/d&e/de/fdZde:fdZde:fdZde:fdZde/fdZde/fdZddFe/fdZd Zd Zd Zdd4e2de2dz  de/fdZd Zdde/fdZde:dz  de/fdZde/fdZddZdde-de/de/fdZddddg dŢddd
ddȜdddd
dd̜dddddȜgdМddddg dբddd
ddȜdddd
dd̜dddddȜgdМddddg dddd
ddȜddd
ddȜdddd
dd̜gdМddddg ddddddȜddd
ddȜdddddȜdddd
dd̜dddddȜgdМddddg ddd dddȜddd
ddȜdddd
dd̜dddddȜdddd	dȜgdМd
dddddddddddddg ddddddȜddd
ddȜdddddȜd d!dd"dȜd#d$dd
d%d̜gdМd&d'dÐd(g d)d(d*dd+dȜd,d-d
d.dȜd/d0dd1dȜd2d3dd
d4d̜d5d6dd7dȜgdМd8d9dӐd:g d;d:d<dd=dȜd>d?d
d@dȜgdМdAdBdCdDg dEdDdFddGdȜdHdId
dJdȜdKdLddMdȜdNdOddPdȜdQdRdd
dSd̜dTdUddVdȜgdМdWdXdӐdYg dZdYd[dd\dȜd]d^d
d_dȜd`dRdd
dad̜dbdUddVdȜgdМdcdddӐdeg dfdedgddhdȜdidjd
dkdȜdldmddndȜdodpd
dqdȜdrdsd
dtdȜdudvddwdȜdxdRdd
dyd̜gdМdzd{dӐd|dd}d~dӐdg ddddddȜddd
ddȜdddd
dd̜dddddȜgdМddddg ddddddȜddd
ddȜdddd
dd̜dddddȜgdМddddg dddFdddȜddId
ddȜgdМgZde5e?         fdZde?de:fdZde5e:         fdZde?fdZd Zd Zd Zd Zd Zd Zde/fdZde/fdZd Zd Zd Zd Zde:fdZde?ddfdZÐd ZĐd ZŐd ZdS (  zu
Gateway subcommand for hermes CLI.

Handles: hermes gateway [run|start|stop|restart|status|install|uninstall|setup]
    N)	dataclass)Path)terminate_pid)%DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT!GATEWAY_SERVICE_RESTART_EXIT_CODEparse_restart_drain_timeout)get_env_valueget_hermes_home
is_managedmanaged_errorread_raw_configsave_env_value)print_header
print_infoprint_successprint_warningprint_errorpromptprompt_choiceprompt_yes_no)ColorscolorT)frozenc                       e Zd ZU eed<   dZeed<   dZeed<   dZe	e
df         ed<   dZedz  ed	<   ed
efd            Zed
efd            ZdS )GatewayRuntimeSnapshotmanagerFservice_installedservice_running .gateway_pidsNservice_scopereturnc                 8    | j         pt          | j                  S N)r   boolr    selfs    7/home/piyush/.hermes/hermes-agent/hermes_cli/gateway.pyrunningzGatewayRuntimeSnapshot.running7   s    #>tD,='>'>>    c                 .    | j         o| j        o| j         S r$   )r   r)   r   r&   s    r(   has_process_service_mismatchz3GatewayRuntimeSnapshot.has_process_service_mismatch;   s    %S$,St?S;SSr*   )__name__
__module____qualname__str__annotations__r   r%   r   r    tupleintr!   propertyr)   r,   r   r*   r(   r   r   /   s         LLL#t###!OT!!!$&L%S/&&& $M3:$$$? ? ? ? X? Td T T T XT T Tr*   r   c                   .    e Zd ZU eed<   eed<   eed<   dS )ProfileGatewayProcessprofilepathpidN)r-   r.   r/   r0   r1   r   r3   r   r*   r(   r6   r6   @   s+         LLL
JJJ	HHHHHr*   r6   r"   c            	         t                      } t                      r,ddgdgfD ]#}	 t          j        |g dz   ddd          }|j                                                                        D ]}|                                }|r|d                             d          s4|d         }	 t          j        |d	|d
dgz   ddd          }t          |j                                                  }|dk    r| 
                    |           # t          t          j        f$ r Y w xY w# t          t          j        f$ r Y !w xY wt                      r	 t                      }t          j        dd|gddd          }|j        dk    r|j                                                                        D ]w}|                                }t#          |          dk    rN|d         |k    rB	 t          |d                   }|dk    r| 
                    |           g# t          $ r Y sw xY wxn# t          t          j        f$ r Y nw xY w| S )ao  Return PIDs currently managed by systemd or launchd gateway services.

    Used to avoid killing freshly-restarted service processes when sweeping
    for stale manual gateway processes after a service restart.  Relies on the
    service manager having committed the new PID before the restart command
    returns (true for both systemd and launchd in practice).
    	systemctl--user)z
list-unitszhermes-gateway*z--plainz--no-legend
--no-pagerT   capture_outputtexttimeoutr   .serviceshowz--property=MainPID--value	launchctllist      )setsupports_systemd_services
subprocessrunstdoutstrip
splitlinessplitendswithr3   add
ValueErrorTimeoutExpiredFileNotFoundErroris_macosget_launchd_label
returncodelen)	pids
scope_argsresultlinepartssvcrD   r9   labels	            r(   _get_service_pidsrb   F   s    D !"" '2[MB 	 	J# "J "J "J J#'dA  
 #M//11<<>>  D JJLLE  !a(9(9*(E(E ! (C
)~&&#*>	*K K+/dA     
 "$+"3"3"5"56677 HHSMMM&
(AB     &z'@A    zz 	%''E^fe,#$  F  A%%"M//11<<>> ! !D JJLLE5zzQ58u+<+<!"%eAh--C"Qww $) ! ! ! D!!:#<= 	 	 	D	 Ksn   BD-.A DD-D(%D-'D((D--EEBH1 .0HH1 
H,)H1 +H,,H1 1I
	I
r9   c           	         | dk    rdS 	 t          j        ddddt          |           gddd	          }n# t          t           j        f$ r Y dS w xY w|j        d
k    rdS |j                                        }|sdS 	 t          |	                                d                                                   }n# t          $ r Y dS w xY w|d
k    r|ndS )z@Return the parent PID for ``pid``, or ``None`` when unavailable.   Nps-ozppid=z-pTr>   r?   r   )rL   rM   r0   rV   rU   rY   rN   rO   r3   rP   rT   )r9   r]   raw
parent_pids       r(   _get_parent_pidrj      s   
axxt4$C1	
 
 
 z89   ttAt
-



C t))"-335566

   tt#a::T1s!   *5 AA=9B7 7
CC
target_pidc                     | dk    rdS t          j                    }t                      }|r8||vr4|| k    rdS |                    |           t	          |          pd}|r||v4dS )zHReturn True when ``target_pid`` is this process or one of its ancestors.r   FT)osgetpidrJ   rS   rj   )rk   r9   seens      r(   #_is_pid_ancestor_of_current_processrp      s~    Qu
)++CUUD
 (#T//*4c""'a	  (#T//
 5r*   c                     t          t          d          sdS t          |           sdS 	 t          j        | t          j                   n# t          t          t          f$ r Y dS w xY wdS )z@Ask a running gateway ancestor to restart itself asynchronously.SIGUSR1FT)	hasattrsignalrp   rm   killrr   ProcessLookupErrorPermissionErrorOSError)r9   s    r(   _request_gateway_self_restartry      sx    69%% u.s33 u
V^$$$$9   uu4s   A
 
A%$A%drain_timeoutc                    t          t          d          sdS | dk    rdS 	 t          j        | t          j                   n$# t
          $ r Y dS t          t          f$ r Y dS w xY wddl}|	                                t          |d          z   }|	                                |k     r`	 t          j        | d           n# t
          $ r Y dS t          $ r Y nw xY w|                    d           |	                                |k     `dS )a~  Send SIGUSR1 to a gateway PID and wait for it to exit gracefully.

    SIGUSR1 is wired in gateway/run.py to ``request_restart(via_service=True)``
    which drains in-flight agent runs (up to ``agent.restart_drain_timeout``
    seconds), then exits with code 75.  Both systemd (``Restart=always``
    + ``RestartForceExitStatus=75``) and launchd (``KeepAlive.SuccessfulExit
    = false``) relaunch the process after the graceful exit.

    This is the drain-aware alternative to ``systemctl restart`` / ``SIGTERM``,
    which SIGKILL in-flight agents after a short timeout.

    Args:
        pid: Gateway process PID (systemd MainPID, launchd PID, or bare
            process PID).
        drain_timeout: Seconds to wait for the process to exit after sending
            SIGUSR1.  Should be slightly larger than the gateway's
            ``agent.restart_drain_timeout`` to allow the drain loop to
            finish cleanly.

    Returns:
        True if the PID was signalled and exited within the timeout.
        False if SIGUSR1 couldn't be sent or the process didn't exit in
        time (caller should fall back to a harder restart path).
    rr   Fr   TNg      ?      ?)rs   rt   rm   ru   rr   rv   rw   rx   time	monotonicmaxsleep)r9   rz   _timedeadlines       r(   _graceful_restart_via_sigusr1r      sI   2 69%% u
axxu
V^$$$$   ttW%   uu   3}c#:#::H
//

h
&
&	GCOOOO! 	 	 	44 	 	 	 D	 	C //

h
&
& 5s/   A 
A"A"!A"'B= =
C
	CCc                      t                      } t          j                    }t          d          D ]6}|                     |           t          |          }|
|dk    s|| v r n|}7| S )ah  Return the set of PIDs in the current process's ancestor chain.

    Walks from the current PID up to PID 1 (init) so that process-table scans
    never match the calling CLI process or any of its parents.  This prevents
    ``hermes gateway status`` from falsely counting the ``hermes`` CLI that
    invoked it as a running gateway instance (see #13242).
    @   Nr   )rJ   rm   rn   rangerS   rj   )	ancestorsr9   _parents       r(   _get_ancestor_pidsr      su     %%I
)++C2YY  c %%>Vq[[Fi,?,?Er*   r[   exclude_pidsc                     ||dk    rd S |t          j                    k    s||v s|| v rd S |                     |           d S )Nr   )rm   rn   append)r[   r9   r   s      r(   _append_unique_pidr     sT    
{cQhh
bikkSL00C4KKKKr*   Fall_profilesc                    | t                      z  } g }g d}t          t                                                                t	                    }|r|                                d         nddt          dt          ffd}	 t                      rt          j	        g dddd	d
d          }|j
        dk    s|j        g S d|j                            d          D ]}|                                }|                    d          r|t          d          d         C|                    d          rq|t          d          d         }t          fd|D                       r=|s |          r0	 t!          |t#          |          |            n# t$          $ r Y nw xY wdʐnpt          j	        g dddd          }|j
        dk    rg S |j                            d          D ]-}|                                }	|	rd|	v rd}
d|	                    dd          }t          |          dk    r1	 t#          |d                   }
|d         n# t$          $ r d}
Y nw xY w|
s|	                                }t          |          dk    rL|d                                         r2t#          |d                   }
d                    |dd                   |
t          fd|D                       r|s |          rt!          ||
|            /n# t*          t          j        f$ r g cY S w xY w|S )a	  Best-effort process-table scan for gateway PIDs.

    This supplements the profile-scoped PID file so status views can still spot
    a live gateway when the PID file is stale/missing, and ``--all`` sweeps can
    discover gateways outside the current profile.
    )hermes_cli.main gatewayzhermes_cli.main --profilezhermes_cli.main -phermes_cli/main.py gatewayzhermes_cli/main.py --profilezhermes_cli/main.py -pzhermes gatewaygateway/run.pyrg    commandr"   c                 d    rd | v pd | v pd | v S d| v sd| v rdS d| v r	d | vrdS dS )N
--profile z-p zHERMES_HOME=z -p FTr   )r   current_homecurrent_profile_names    r(   _matches_current_profilez4_scan_gateway_pids.<locals>._matches_current_profile&  s     	3133w> </-//7:<0,00G; 7""f&7&75W$$)F)F)Fg)U)U5tr*   )wmicprocessgetzProcessId,CommandLinez/FORMAT:LISTTutf-8ignore
   )r@   rA   encodingerrorsrB   r   N
zCommandLine=z
ProcessId=c              3       K   | ]}|v V  	d S r$   r   ).0pcurrent_cmds     r(   	<genexpr>z%_scan_gateway_pids.<locals>.<genexpr>G  s(      >>1+>>>>>>r*   )re   z-Aewwrf   zpid=,command=r?   greprd   rI    c              3       K   | ]}|v V  	d S r$   r   )r   patternr   s     r(   r   z%_scan_gateway_pids.<locals>.<genexpr>p  s(      BBgw')BBBBBBr*   )r   r0   r
   resolve_profile_argrQ   r%   
is_windowsrL   rM   rY   rN   rO   
startswithrZ   anyr   r3   rT   isdigitjoinrx   rU   )r   r   r[   patternscurrent_profile_argr   r]   r^   pid_strstrippedr9   r_   	aux_partsr   r   r   r   s                @@@@r(   _scan_gateway_pidsr     s     "4"6"66LD	 	 	H ((002233L&|44>QY.4466r::WY# $       A<< >	@^SSS#   F  A%%)>	K++D11 % %zz||??>22 %"&s>':':';';"<KK__\22 	%"3|#4#4#5#56G>>>>X>>>>> !$!(@(@(M(M!!.tS\\<PPPP) ! ! ! D!"$K%  ^:::#	  F  A%%	++D11 @ @::<< 6X#5#5 tQ//u::??#!%(mm"'(% # # #"# ; ( 0 0I9~~**y|/C/C/E/E*!)A,//"%((9RSS>":":;BBBBBBBBB @ @$<$<W$E$E@ 'tS,???Z./   			 Ksi   >L$ B3L$ ?FL$ 
F+(L$ *F++.L$ A%L$  IL$ I-*L$ ,I--B6L$ $L?>L?c                 8   t          | pt                                }g }|s1	 ddlm} t          | |            |           n# t          $ r Y nw xY wt                      D ]}t          |||           t          ||          D ]}t          |||           |S )a  Find PIDs of running gateway processes.

    Args:
        exclude_pids: PIDs to exclude from the result (e.g. service-managed
            PIDs that should not be killed during a stale-process sweep).
        all_profiles: When ``True``, return gateway PIDs across **all**
            profiles (the pre-7923 global behaviour).  ``hermes update``
            needs this because a code update affects every profile.
            When ``False`` (default), only PIDs belonging to the current
            Hermes profile are returned.
    r   get_running_pidr   )rJ   gateway.statusr   r   	Exceptionrb   r   )r   r   _excluder[   r   r9   s         r(   find_gateway_pidsr   z  s     <(355))HD 	666666t__%6%6AAAA 	 	 	D	 "" 0 04h////!(FFF 0 04h////Ks   A 
AAc                    t          | pt                                }g }	 ddlm} ddlm} n# t
          $ r |cY S w xY wt                      } |            D ]~}	  ||j        dz  d          }n# t
          $ r Y %w xY w||dk    s||v s||v r:|                    |           |                    t          |j
        |j        |                     |S )	zDReturn running gateway PIDs mapped to Hermes profiles via PID files.r   r   )list_profilesgateway.pidFcleanup_staleN)r7   r8   r9   )rJ   r   r   hermes_cli.profilesr   r   r8   rS   r   r6   name)r   r   	processesr   r   ro   r7   r9   s           r(   find_profile_gateway_processesr     s7    <(355))H-/I2222225555555    UUD =?? b b	!/',">eTTTCC 	 	 	H	;#((cXoo.w|',\_```aaaas   . ==A11
A>=A>r7   c                     t                      ddg}| dk    r|                    d| g           |                    g d           |S )Nz-mzhermes_cli.maindefaultz	--profile)gatewayrM   z	--replace)get_python_pathextend)r7   argss     r(   _gateway_run_args_for_profiler     sU    t%67D)['*+++KK///000Kr*   old_pidc                 4   |dk    rdS t          j        d                                          }	 t          j        t
          j        d|t          |          gt          |           t          j	        t          j	        d           n# t          $ r Y dS w xY wdS )zDRelaunch a manually-run profile gateway after its current PID exits.r   FaV  
        import os
        import subprocess
        import sys
        import time

        pid = int(sys.argv[1])
        cmd = sys.argv[2:]
        deadline = time.monotonic() + 120
        while time.monotonic() < deadline:
            try:
                os.kill(pid, 0)
            except ProcessLookupError:
                break
            except PermissionError:
                pass
            time.sleep(0.2)
        subprocess.Popen(
            cmd,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            start_new_session=True,
        )
        z-cT)rN   stderrstart_new_session)textwrapdedentrO   rL   Popensys
executabler0   r   DEVNULLrx   )r7   r   watchers      r(   'launch_detached_profile_gateway_restartr     s    !||uo	 2 egg3 6^T7CLLb;XY`;a;ab%%"		
 	
 	
 	
 	
    uu4s   AB 
BBsystemc                 6   t          |           }t          |                                          }|s|dfS 	 t          dt	                      g|ddd          }n # t
          t          j        f$ r |dfcY S w xY w||j        	                                dk    fS )Nr   F	is-activeTr   r   r@   rA   rB   active)
_select_systemd_scopeget_systemd_unit_pathexists_run_systemctlget_service_nameRuntimeErrorrL   rU   rN   rO   )r   selected_systemunit_existsr]   s       r(   _probe_systemd_service_runningr     s    +F33O'???FFHHK &%%	&*,,-"
 
 
 *34 & & &%%%%&FM//11X===s   "A A98A9ActiveStateSubStateResultExecMainStatusMainPID
properties.c           	         t          |           }	 t          dt                      ddd                    |          g|ddd          }n$# t          t
          j        t          f$ r i cY S w xY w|j        dk    ri S i }|j	        
                                D ]7}d	|vr|                    d	d
          \  }}|                                ||<   8|S )zCReturn selected ``systemctl show`` properties for the gateway unit.rD   r=   z
--property,Tr   r   r   =rd   )r   r   r   r   r   rL   rU   rx   rY   rN   rP   rQ   rO   )r   r   r   r]   parsedr^   keyvalues           r(   _read_systemd_unit_propertiesr     s    ,F33O ""$$ #
 
 
 *3W=   			 A	F((** $ $d??ZZQ''
UkkmmsMs   8A
 
A+*A+propsc                     	 t          |                     dd          pd          }n# t          t          f$ r Y d S w xY w|dk    r|nd S )Nr   0r   )r3   r   	TypeErrorrT   )r   r9   s     r(   _systemd_main_pid_from_propsr     sb    %))Is++2s33z"   tt''33t#s   %( ==c                 <    t          t          |                     S Nr   )r   r   r   s    r(   _systemd_main_pidr  &  s    '(EV(T(T(TUUUr*   c                  z    	 ddl m}   |             }n# t          $ r Y d S w xY wt          |t                    r|nd S )Nr   read_runtime_status)r   r  r   
isinstancedict)r  states     r(   _read_gateway_runtime_statusr
  *  sd    666666##%%   ttud++5555s    
!!c                     | sd S t                      }|sd S 	 t          |                    dd          pd          }n# t          t          f$ r Y d S w xY w|| k    r|nd S )Nr9   r   )r
  r3   r   r   rT   )r9   r	  	state_pids      r(   _gateway_runtime_status_for_pidr  4  s     t(**E t		%++0q11		z"   tt$$55$.s   %> AAg      N@)r   previous_pidrB   r  rB   c                 P   ddl }t                      }t          |                                           }|                                |z   }d}|                                |k     rt          |           }|                    dd          }	|                    dd          }
d}	 ddlm}  |            }n# t          $ r d}Y nw xY w|st          |          }|	d	k    r|r|||k    rt          |          }|pi                     d
          }|dk    rt          d| d| d           dS |dk    r3|pi                     d          pd}t          d| d| d|            dS |st          d| d| d           d}|	dk    r|
dk    r|                    d           Vt          |          rt          |            dS |                    d           |                                |k     t          d| dt!          |           d| rdnd d | sd!nd d"| d#           dS )$zFWait for the gateway service to become active after a restart handoff.r   NFr   r   r   r   r   r   gateway_stater)      ✓ z service restarted (PID )Tstartup_failedexit_reasonzstartup failed   ⚠ z  service process restarted (PID z), but gateway startup failed:    ⏳ z service process started (PID z!); waiting for gateway runtime...
activatingauto-restartrd   rI   z& service did not become active within zs.
  Check status: sudo z1hermes gateway status
  Check logs:   journalctl --user -u z -l --since '2 min ago')r}   r   _service_scope_label
capitalizer~   r   r   r   r   r   r   r  printr   _systemd_unit_is_start_limited_print_systemd_start_limit_waitr3   )r   r  rB   r}   r`   scope_labelr   printed_runtime_waitr   active_state	sub_statenew_pidr   runtime_stater  reasons                   r(   !_wait_for_systemd_service_restartr(  A  s    KKK


C&v..99;;K~~')H 
..

X
%
%-V<<<yy33IIj"--		666666%o''GG 	 	 	GGG	 	:2599G8## 0L0G|4K4K ? H H!.!4" 9 9/ J J I--PPPgPPPQQQ4 $444+1r66}EEYIYF~~~g~~v|~~ 5+ 0vvvGvvvwww+/(<''I,G,GJJqMMM)%00 	+6::::5

1K ..

X
%
%N 
	g{ 	g 	g#g,, 	g 	g&,477"	g 	g7=&Eii2	g 	gJM	g 	g 	g  
 5s   %B6 6CCc                     |                      dd                                          }|                      dd                                          }|dk    p|dk    S )Nr   r   r   start-limit-hit)r   lower)r   r]   r$  s      r(   r  r  ~  sW    YYx$$**,,F		*b))//11I&&H)7H*HHr*   excc                 8   g }dD ]c}t          | |d           }|st          |t                    r|                    d          }|                    t          |                     dd                    |                                          }d|v pd|v pd|v S )N)r   rN   outputreplace)r   r   r*  z"start request repeated too quicklyzstart-limit)getattrr  bytesdecoder   r0   r   r+  )r,  r_   attrr   rA   s        r(   $_systemd_error_indicates_start_limitr4    s    E. ! !T4(( 	eU## 	3LL	L22ESZZ    99U!!##DT! 	!/47	!D r*   c                 <    t          t          |                     S r  )r  r   r   s    r(   !_systemd_service_is_start_limitedr6    s    )*Gv*V*V*VWWWr*   c                 T   t                      }t          |                                           }| rdnd}| rdnd}| rdnd}t          d| d           t          d	           t          d
| rdnd d|            t          d| d|            t          d| d| d           d S )N	 --systemr   z
systemctl zsystemctl --user zjournalctl zjournalctl --user r  z0 service is temporarily rate-limited by systemd.zC  systemd is refusing another immediate start after repeated exits.z7  Wait for the start-limit window to expire, then run: r  hermes gateway restartz&  Or clear the failed state manually: reset-failed z  Check logs: r  z -l --since '5 min ago')r   r  r  r  )r   r`   r!  
scope_flagsystemctl_prefixjournal_prefixs         r(   r   r     s   


C&v..99;;K &.BJ'-F||3F&,F]]2FN	
N
N
N
NOOO	
OPPP	  BvD]GG[]  B  Bu  B  B  C  C  C	
W3C
W
WRU
W
WXXX	
J>
J
Jc
J
J
JKKKKKr*   c                    t          |           }|sdS 	 ddlm} n# t          $ r Y dS w xY w |            pi }|                    d          sdS |                    dd          }|                    dd          }|                    d	d          }|                    d
d          }|dk    r&|dk    r t          d           t          | |          S |dk    r|t          t                    k    s|dk    rt                      }	t          |                                           }
t          d|
                                 d           t          d|	g| dd           t          d|	g| dd           t          | |          S dS )zARecover a planned service restart that is stuck in systemd state.r   Fr   r  restart_requestedr   r   r   r   r   r  r  uG   ⏳ Service restart already pending — waiting for systemd relaunch...r   r  failedz	exit-codeu&   ↻ Clearing failed state for pending z service restart...reset-failed   r   checkrB   startZ   )r   r   r  r   r   r  r(  r0   r   r   r  r  r+  r   )r   r  r   r  r&  r#  r$  exec_main_statusr]   r`   r!  s              r(    _recover_pending_systemd_restartrI    s   )888E u6666666   uu ('))/RM011 u99]B//L		*b))Iyy!1266YYx$$F|##	^(C(CWXXX0%
 
 
 	

 xC ABBBB[    *622==??_{7H7H7J7J___```S!		
 	
 	
 	
 	cN		
 	
 	
 	
 1%
 
 
 	

 5s    
++c                      t                                                      sdS 	 t          j        ddt	                      gddd          } n# t          j        $ r Y dS w xY w| j        dk    S )NFrF   rG   Tr   r?   r   )get_launchd_plist_pathr   rL   rM   rX   rU   rY   )r]   s    r(   _probe_launchd_service_runningrL    s    !##**,, u&"3"5"56	
 
 
 $   uu!!s   'A AAc                 ^   t          t                                }t                      rt          d|          S ddlm} t                      r |            rt          d|          S t                      rZt          |           \  }}t          |          }t          d| dt          |                                          |||	          S t                      r>t          d
t                                                      t                      |d
	          S t          d|          S )zBReturn a unified view of gateway liveness for the current profile.zTermux / manual process)r   r    r   )is_containerzdocker (foreground)r   z	systemd (r  )r   r   r   r    r!   launchdzmanual process)r2   r   	is_termuxr   hermes_constantsrN  is_linuxrK   r   r  r   r   rW   rK  rL  )r   r    rN  r   r   r!  s         r(   get_gateway_runtime_snapshotrS    so   *,,--L{{ 
%-%
 
 
 	

 .-----zz 
llnn 
%)%
 
 
 	

 !"" 	
+IQW+X+X+X(*?;;%....3?KKKRRTT+%%
 
 
 	
 zz 
%466==??:<<%#
 
 
 	
 " !   r*   rH   limitrU  c                    |d | d |         D             nd | D             }|(t          |           |k    r|                    d           d                    |          S )Nc                 8    g | ]}|d k    t          |          S r   r0   r   r9   s     r(   
<listcomp>z(_format_gateway_pids.<locals>.<listcomp>  s#    <<<SC!GGCGGGr*   c                 8    g | ]}|d k    t          |          S rX  rY  rZ  s     r(   r[  z(_format_gateway_pids.<locals>.<listcomp>  s/    W|W|W|ehtwz{t{t{X[\_X`X`t{t{t{r*   z..., )rZ   r   r   )r[   rU  rendereds      r(   _format_gateway_pidsr_    sr    @E@Q<<D%L<<<<W|W|lpW|W|W|HSYY..99Xr*   snapshotc                     | j         sd S t                       t          d           t          dt          | j        d                       t          d           t          d           d S )NuN   ⚠ Gateway process is running for this profile, but the service is not activez
  PID(s): rT  zI  This is usually a manual foreground/tmux/nohup run, so `hermes gateway`z<  can refuse to start another copy until this process stops.)r,   r  r_  r    )r`  s    r(   _print_gateway_process_mismatchrb    sv    0 	GGG	
Z[[[	
P+H,ANNN
P
PQQQ	
UVVV	
HIIIIIr*   c                     	 ddl m}   |             fdt                      D             }|sdS t                       t          d           |D ]"}t          d|j        dd|j                    #dS # t          $ r Y dS w xY w)	a  Print a summary of gateway status across all profiles.

    Shown at the bottom of ``hermes gateway status`` output so users with
    multiple profiles can tell at a glance which gateways are running and
    avoid confusing another profile's process with the current one.
    r   )get_active_profile_namec                 *    g | ]}|j         k    |S r   )r7   )r   r   currents     r(   r[  z8_print_other_profiles_gateway_status.<locals>.<listcomp>2  s0     
 
 
yG## ###r*   NzOther profiles:u     ✓ z<16su	    — PID )r   rd  r   r  r7   r9   r   )rd  other_processesprocrf  s      @r(   $_print_other_profiles_gateway_statusri  '  s    ??????))++
 
 
 
577
 
 
  	F   # 	C 	CDA4<AAAtxAABBBB	C 	C   s   ,A5 AA5 5
BBc                  2   	 ddl m} m} n # t          $ r t	          d           Y dS w xY w |             }|st	          d           dS  |            }t	          d           |D ]}|j        rdnd}|j        }|j        |k    r|d	z  }d
| d|dg}|j        rG	 ddlm}  ||j	        dz  d          }	|	r|
                    d|	            n%# t          $ r Y nw xY w|
                    d           t	          d                    |                     dS )zList all profiles and their gateway running status.

    Provides a single-command overview of every known profile and whether
    its gateway is currently running, so multi-profile users don't have to
    check each profile individually.
    r   )r   rd  zUnable to list profiles.NzNo profiles found.z	Gateways:u   ✓u   ✗z
 (current)  r   z<24sr   r   Fr   zPID znot runningu    — )r   r   rd  r   r  gateway_runningr   r   r   r8   r   r   )
r   rd  profilesrf  profmarkerra   r_   r   r9   s
             r(   _gateway_listrp  A  s   NNNNNNNNN   ())) }H "###%%''G	+ # #.9E	9\!E+f++u+++, 		(::::::%odi-&?uUUU /LL...    LL'''gll5!!""""!# #s    ((5C
CCforcec                    t          ||          }d}|D ]m}	 t          ||            |dz  }# t          $ r Y &t          $ r t	          d|            Y Ct
          $ r}t	          d| d|            Y d}~fd}~ww xY w|S )	a  Kill any running gateway processes. Returns count killed.

    Args:
        force: Use the platform's force-kill mechanism instead of graceful terminate.
        exclude_pids: PIDs to skip (e.g. service-managed PIDs that were just
            restarted and should not be killed).
        all_profiles: When ``True``, kill across all profiles.  Passed
            through to :func:`find_gateway_pids`.
    )r   r   r   rq  rd   "   ⚠ Permission denied to kill PID zFailed to kill PID : N)r   r   rv   rw   r  rx   )rq  r   r   r[   killedr9   r,  s          r(   kill_gateway_processesrw  i  s     ,\RRRDF 6 6
	6#U++++aKFF! 	 	 	D 	> 	> 	><s<<===== 	6 	6 	6444s4455555555	6Ms    0
BB	B"A<<Bc                     	 ddl m} m} n# t          $ r Y dS w xY w |             }|dS 	 ddl m}  ||           n# t
          $ r Y nw xY w	 t          j        |t          j	                   n.# t          $ r Y n"t          $ r t          d|            Y dS w xY wddl}t          d          D ]E}	 t          j        |d           |                    d           .# t          t          f$ r Y  nw xY w |             
 |             d	S )
u   Stop only the gateway for the current profile (HERMES_HOME-scoped).

    Uses the PID file written by start_gateway(), so it only kills the
    gateway belonging to this profile — not gateways from other profiles.
    Returns True if a process was stopped, False if none was found.
    r   )r   remove_pid_fileFN)write_planned_stop_markerrt     r|   T)r   r   ry  ImportErrorrz  r   rm   ru   rt   SIGTERMrv   rw   r  r}   r   r   )r   ry  r9   rz  r   r   s         r(   stop_profile_gatewayr~    s   CCCCCCCCC   uu /

C
{u<<<<<<!!#&&&&   
V^$$$$      8388999uu
 2YY  	GCOOOKK"O4 	 	 	EE	  4sE    
= 
A
	A
A. .
B:BB2*CC21C2c                  @    t           j                            d          S )Nlinux)r   platformr   r   r*   r(   rR  rR    s    <""7+++r*   )rN  rP  is_wslc                  "    t          d          S )u   Check if systemd is actually running as PID 1 on WSL.

    WSL2 with ``systemd=true`` in wsl.conf has working systemd.
    WSL2 without it (or WSL1) does not — systemctl commands fail.
    Tr   _systemd_operationalr   r*   r(   _wsl_systemd_operationalr    s      t,,,,r*   c                     	 t          dg| ddd          }|j                                                                        }|dv S # t          t
          j        t          f$ r Y dS w xY w)z7Return True when the requested systemd scope is usable.zis-system-runningTr>   r   )r)   degradedstartinginitializingF)r   rN   rO   r+  r   rL   rU   rx   )r   r]   statuss      r(   r  r    s     !
 
 
 $$&&,,..LLL*3W=   uus   AA A&%A&c                  N    t          d          rdS t          d          rdS dS )zDReturn True when a container exposes working user or system systemd.Fr   Tr  r   r*   r(   _container_systemd_operationalr    s7    5))) t4((( t5r*   c                      t                      rt                      rdS t          j        d          dS t	                      rt                      S t                      rt                      S dS )NFr;   T)rR  rP  shutilwhichr  r  rN  r  r   r*   r(   rK   rK     sh    ::  u|K  (uxx *')))~~ 0-///4r*   c                  "    t           j        dk    S )Ndarwinr   r  r   r*   r(   rW   rW     s    <8##r*   c                  "    t           j        dk    S )Nwin32r  r   r*   r(   r   r     s    <7""r*   zhermes-gatewayz5Hermes Agent Gateway - Messaging Platform Integrationc                  :   ddl } ddl}ddlm} t	                                                      } |                                            }||k    rdS |dz                                  }	 |                    |          }|j        }t          |          dk    r$|	                    d|d                   r|d         S n# t          $ r Y nw xY w|                     t          |                                                                                    dd         S )	a  Derive a service-name suffix from the current HERMES_HOME.

    Returns ``""`` for the default root, the profile name for
    ``<root>/profiles/<name>``, or a short hash for any other path.
    Works correctly in Docker (HERMES_HOME=/opt/data) and standard deployments.
    r   Nget_default_hermes_rootr   rm  rd   ^[a-z0-9][a-z0-9_-]{0,63}$   )hashlibrerQ  r  r
   r   relative_tor_   rZ   matchrT   sha256r0   encode	hexdigest)r  r  r  homer   profiles_rootrelr_   s           r(   _profile_suffixr    s'    NNNIII888888$$&&D%%''//11Gwrz)2244M}--	u::??rxx(EuQxPP?8O    >>#d))**,,--7799"1"==s   +AB? ?
CChermes_homec                    ddl }ddlm} t          | pt	          t                                                                          } |                                            }||k    rdS |dz                                  }	 |                    |          }|j        }t          |          dk    r'|
                    d|d                   rd|d          S n# t          $ r Y nw xY wdS )	a  Return ``--profile <name>`` only when HERMES_HOME is a named profile.

    For ``~/.hermes/profiles/<name>``, returns ``"--profile <name>"``.
    For the default profile or hash-based custom paths, returns the empty string.

    Args:
        hermes_home: Optional explicit HERMES_HOME path. Defaults to the current
            ``get_hermes_home()`` value. Should be passed when generating a
            service definition for a different user (e.g. system service).
    r   Nr  r   rm  rd   r  r   )r  rQ  r  r   r0   r
   r   r  r_   rZ   r  rT   )r  r  r  r  r   r  r  r_   s           r(   r   r     s    III8888885s?#4#45566>>@@D%%''//11Gwrz)2244M}--	u::??rxx(EuQxPP?*a***   2s   AC 
C'&C'c                  H    t                      } | st          S t           d|  S )a  Derive a systemd service name scoped to this HERMES_HOME.

    Default ``~/.hermes`` returns ``hermes-gateway`` (backward compatible).
    Profile ``~/.hermes/profiles/coder`` returns ``hermes-gateway-coder``.
    Any other HERMES_HOME appends a short hash for uniqueness.
    -)r  _SERVICE_BASEsuffixs    r(   r   r   .  s1     F &&f&&&r*   c                     t                      }| rt          d          | dz  S t          j                    dz  dz  dz  | dz  S )N/etc/systemd/systemrC   .configsystemduser)r   r   r  )r   r   s     r(   r   r   <  s[    D ?)**->->->>>9;;"Y.7T:K:K:KKKr*   c                       e Zd ZdZdS )UserSystemdUnavailableErrora#  Raised when ``systemctl --user`` cannot reach the user D-Bus session.

    Typically hit on fresh RHEL/Debian SSH sessions where linger is disabled
    and no user@.service is running, so ``/run/user/$UID/bus`` never exists.
    Carries a user-facing remediation message in ``args[0]``.
    N)r-   r.   r/   __doc__r   r*   r(   r  r  C  s           r*   r  c                       e Zd ZdZdefdZdS )SystemScopeRequiresRootErrora'  Raised when a system-scope gateway operation is attempted as non-root.

    System-scope units live in ``/etc/systemd/system/`` and require root for
    install / uninstall / start / stop / restart via ``systemctl``. The
    previous behavior was ``sys.exit(1)`` which blew past the wizard's
    ``except Exception`` guards and dumped the user at a bare shell prompt
    with no guidance. Raising a typed exception lets callers that can
    recover (the setup wizard) print actionable remediation instead, while
    ``gateway_command`` still exits 1 with the same message for the direct
    CLI path.

    ``args[0]`` carries the user-facing message, ``args[1]`` the action name.
    ``str(e)`` returns only the message (not the tuple repr) so format
    strings like ``f"Failed: {e}"`` render cleanly.
    r"   c                 .    | j         r| j         d         ndS )Nr   r   )r   r&   s    r(   __str__z$SystemScopeRequiresRootError.__str__]  s    #y0ty||b0r*   N)r-   r.   r/   r  r0   r  r   r*   r(   r  r  L  s9          1 1 1 1 1 1 1r*   r  c                      t           j                            d          pdt          j                     } t	          |           dz  S )zIReturn the expected per-user D-Bus socket path (regardless of existence).XDG_RUNTIME_DIR
/run/user/busrm   environr   getuidr   xdgs    r(   _user_dbus_socket_pathr  a  s;    
*..*
+
+
I/IBIKK/I/IC99ur*   c                      t           j                            d          pdt          j                     } t	          |           dz  dz  S )zJReturn the per-user systemd private socket path (regardless of existence).r  r  r  privater  r  s    r(   !_user_systemd_private_socket_pathr  g  s@    
*..*
+
+
I/IBIKK/I/IC99y 9,,r*   c                      t                                                      pt                                                      S )a6  Return True when user-scope systemd has a reachable control socket.

    Some distros expose only the per-user systemd private socket even when the
    D-Bus session bus socket is absent. ``systemctl --user`` can still work in
    that configuration, so preflight checks must treat either socket as valid.
    )r  r   r  r   r*   r(   _user_systemd_socket_readyr  m  s4     "##**,,\0Q0S0S0Z0Z0\0\\r*   c                     t          j                    } dt           j        vr5d|  }t          |                                          r|t           j        d<   dt           j        vr]t           j                            dd|            }t          |          dz  }|                                rd| t           j        d<   dS dS dS )a  Ensure DBUS_SESSION_BUS_ADDRESS and XDG_RUNTIME_DIR are set for systemctl --user.

    On headless servers (SSH sessions), these env vars may be missing even when
    the user's systemd instance is running (via linger).  Without them,
    ``systemctl --user`` fails with "Failed to connect to bus: No medium found".
    We detect the standard socket path and set the vars so all subsequent
    subprocess calls inherit them.
    r  r  DBUS_SESSION_BUS_ADDRESSr  z
unix:path=N)rm   r  r  r   r   r   )uidruntime_dirxdg_runtimebus_paths       r(   _ensure_user_systemd_envr  w  s     )++C
**(3((##%% 	8,7BJ()!33jnn%68JS8J8JKK$$u,?? 	M5L(5L5LBJ1222	 43	M 	Mr*         @c                    ddl }|                                | z   }|                                |k     rKt                      rt                       dS |                    d           |                                |k     Kt                      S )a3  Poll for the user systemd runtime socket(s), up to ``timeout`` seconds.

    Linger-enabled user@.service can take a second or two to spawn its control
    socket(s) after ``loginctl enable-linger`` runs. Returns True once either
    the user D-Bus socket or the per-user systemd private socket exists.
    r   NTg?)r}   r~   r  r  r   )rB   r}   r   s      r(   _wait_for_user_dbus_socketr    s     KKK~~')H
..

X
%
%%'' 	$&&&4

3	 ..

X
%
%
 &'''r*   )auto_enable_lingerr  c                 2   t                       t                      rdS ddl}|                                }t	                      \  }}|du r9t          d          rdS t          |ddt          j                     d	           | rt          j
        d
          r	 t          j        d
d|gdddd          }|j        dk    rFt          d          rt          d| d           dS t          |ddt                       	           |j        p|j        p	d|j                                         }t          |d| d| 	           n0# t&          $ r#}t          |d| dd| 	           Y d}~nd}~ww xY wt          |d|pd dd| 	           dS )a9  Ensure ``systemctl --user`` will reach the user-scope systemd instance.

    No-op when the user D-Bus socket or per-user systemd private socket is
    already there (the common case on desktops and linger-enabled servers). On
    fresh SSH sessions where both are missing:

    * If linger is already enabled, wait briefly for user@.service to spawn
      the socket.
    * If linger is disabled and ``auto_enable_linger`` is True, try
      ``loginctl enable-linger $USER`` (works as non-root when polkit permits
      it, otherwise needs sudo).
    * If the socket is still missing afterwards, raise
      :class:`UserSystemdUnavailableError` with a precise remediation message.

    Callers should treat the exception as a terminal condition for user-scope
    systemd operations and surface the message to the user.
    Nr   Tr  rB   zGUser systemd control sockets are missing even though linger is enabled.z  systemctl start user@zC.service
  (may require sudo; try again after the command succeeds))r'  fix_hintloginctlenable-lingerFrC  r@   rA   rE  rB         @u   ✓ Enabled linger for u    — user D-Bus now availablez=Linger was enabled, but the user D-Bus socket did not appear.z`  Log out and log back in, then re-run the command.
  Or reboot and run: systemctl --user start exit z#loginctl enable-linger was denied: z  sudo loginctl enable-linger zloginctl enable-linger failed ().z%User D-Bus session is not available (zlinger disabled)r  r  getpassgetuserget_systemd_linger_statusr  _raise_user_systemd_unavailablerm   r  r  r  rL   rM   rY   r  r   r   rN   rO   r   )r  r  usernamelinger_enabledlinger_detailr]   detailr,  s           r(   _preflight_user_systemdr    s   $ !## NNN  H$=$?$?!NM%c222 	F'\M")++ M M M		
 	
 	
 	
  "fl:66 "!	^_h7#  F  A%%-c::: [H[[[\\\F/Z\GWGYGY\ \	    mSv}S8S@Q8S8SZZ\\F+EVEED(DD    +  	 	 	+@@@@D(DD        	6 $72!27 7 7 =(<<     s   E 
E9E44E9r  r'  r  c                0    | d| d}t          |          )zHBuild a user-facing error message and raise UserSystemdUnavailableError.zR
  systemctl --user cannot reach the user D-Bus session in this shell.

  To fix:
z{

  Alternative: run the gateway in the foreground (stays up until
  you exit / close the terminal):
    hermes gateway run)r  )r  r'  r  msgs       r(   r  r    s:      	! 	! 		! 	! 	!  &c
*
**r*   c                 4    | st                       | rdgnddgS )Nr;   r<   )r  r   s    r(   _systemctl_cmdr    s,     # """"?K==h(??r*   c                     | rdgnddgS )N
journalctlr<   r   r   s    r(   _journalctl_cmdr    s    #AL>>,)AAr*   r   r   c                    	 t          j        t          |          | z   fi |S # t          $ r t	          d          dw xY w)a%  Run a systemctl command, raising RuntimeError if systemctl is missing.

    Defense-in-depth: callers are gated by ``supports_systemd_services()``,
    but this ensures any future caller that bypasses the gate still gets a
    clear error instead of a raw ``FileNotFoundError`` traceback.
    z)systemctl is not available on this systemN)rL   rM   r  rV   r   )r   r   kwargss      r(   r   r     s_    ~nV44t;FFvFFF   7
 
	s   !$ ?c                     | rdndS )Nr   r  r   r   s    r(   r  r    s    )886)r*   c                      g } t                      }dD ]X\  }}t          |          }||v r|                                r*|                     |           |                    |           Y| S )N))Fr  )Tr   r   )rJ   r   r   r   rS   )scopes
seen_pathsr   ra   	unit_paths        r(   get_installed_systemd_scopesr  "  s    FEEJ< & &)888	
"" 	&MM%   NN9%%%Mr*   c                  @    t          t                                dk    S )Nrd   )rZ   r  r   r*   r(   has_conflicting_systemd_unitsr  /  s    +--..22r*   )zhermes.service_LEGACY_SERVICE_NAMES)r   r   r   z hermes gateway z/hermes gateway _LEGACY_UNIT_EXECSTART_MARKERSc                  `    dt          j                    dz  dz  dz  fdt          d          fgS )u   Return ``[(is_system, base_dir), ...]`` — directories to scan for legacy units.

    Factored out so tests can monkeypatch the search roots without touching
    real filesystem paths.
    Fr  r  r  Tr  )r   r  r   r*   r(   _legacy_unit_search_pathsr  D  s<     
	i')3f<=	t)**+ r*   c                  N   g } t                      D ]\  }}t          D ]}||z  }	 |                                s|                    dd          n# t          t
          f$ r Y Hw xY wt          fdt          D                       sm|                     |||f           | S )u  Return ``[(unit_name, unit_path, is_system)]`` for legacy Hermes gateway units.

    Detects unit files installed by older Hermes versions that used a
    different service name (e.g. ``hermes.service`` before the rename to
    ``hermes-gateway.service``). When both a legacy unit and the current
    ``hermes-gateway.service`` are active, they fight over the same bot
    token — the PR #5646 signal-recovery change turns this into a 30-second
    SIGTERM flap loop.

    Safety guards:

    * Explicit allowlist of legacy names (no globbing). Profile units such
      as ``hermes-gateway-coder.service`` and unrelated third-party
      ``hermes-*`` services are never matched.
    * ExecStart content check — only flag units that invoke our gateway
      entrypoint. A user-created ``hermes.service`` running an unrelated
      binary is left untouched.
    * Results are returned purely for caller inspection; this function
      never mutates or removes anything.
    r   r   )r   r   c              3       K   | ]}|v V  	d S r$   r   )r   ro  rA   s     r(   r   z,_find_legacy_hermes_units.<locals>.<genexpr>o  s'      SS&v~SSSSSSr*   )	r  r  r   	read_textrx   rw   r   r  r   )results	is_systembaser   r  rA   s        @r(   _find_legacy_hermes_unitsr   P  s    * -/G466 9 9	4) 	9 	9DtI ''))  **GH*MM_-   SSSS4RSSSSS NND)Y78888	9 Ns   AAA'&A'c                  8    t          t                                S )z<Return True when any legacy Hermes gateway unit files exist.)r%   r   r   r*   r(   has_legacy_hermes_unitsr  v  s    )++,,,r*   c                  
   t                      } | sdS t          d           | D ]"\  }}}|rdnd}t          d| d| d           #t          d           t          d	           t          d
           t          d           dS )zWarn about legacy Hermes gateway unit files if any are installed.

    Idempotent: prints nothing when no legacy units are detected. Safe to
    call from any status/install/setup path.
    Nz=Legacy Hermes gateway unit(s) detected from an older install:r   r  z      ( scope)z<  These run alongside the current hermes-gateway service anduB     cause SIGTERM flap loops — both try to use the same bot token.z  Remove them with:z!    hermes gateway migrate-legacy)r   r   r   )legacyr   r8   r  scopes        r(   print_legacy_unit_warningr  {  s     '((F QRRR!' 3 3dI%161$1151112222MNNNSTTT$%%%233333r*   interactivedry_runc                 N   t                      }|st          d           dg fS d |D             }d |D             }t                       t          d           |D ]"\  }}}|rdnd}t          d| d	| d
           #t                       |rt          d           dd |D             fS | r-t          dd          st          d           dd |D             fS d}	g }
|D ]\  }}	 t          d|gddd           t          d|gddd           |                    d           t          d|            |	dz  }	^# t
          t          f$ r4}t          d| d|            |
                    |           Y d}~d}~ww xY w|r&	 t          dgddd           n# t          $ r Y nw xY w|r/t          j	                    dk    rJt                       t          d           t          d           |D ]\  }}|
                    |           n|D ]\  }}	 t          d|gddd           t          d|gddd           |                    d           t          d|            |	dz  }	^# t
          t          f$ r4}t          d| d|            |
                    |           Y d}~d}~ww xY w	 t          dgddd           n# t          $ r Y nw xY wt                       |
r t          t          |
           d            nt          d!|	 d"           |	|
fS )#u  Stop, disable, and remove legacy Hermes gateway unit files.

    Iterates over whatever ``_find_legacy_hermes_units()`` returns — which is
    an explicit allowlist of legacy names (not a glob). Profile units and
    unrelated third-party services are never touched.

    Args:
        interactive: When True, prompt before removing. When False, remove
            without asking (used when another prompt has already confirmed,
            e.g. from the install flow).
        dry_run: When True, list what would be removed and return.

    Returns:
        ``(removed_count, remaining_paths)`` — remaining includes units we
        couldn't remove (typically system-scope when not running as root).
    z%No legacy Hermes gateway units found.r   c                 "    g | ]\  }}}|||fS r   r   r   nr   is_syss       r(   r[  z.remove_legacy_hermes_units.<locals>.<listcomp>  s'    BBB\Q66B1a&BBBr*   c                 "    g | ]\  }}}|||fS r   r   r  s       r(   r[  z.remove_legacy_hermes_units.<locals>.<listcomp>  s'    @@@|q!V@QF@@@r*   z$Legacy Hermes gateway unit(s) found:r   r  rk  r  r  u   (dry-run — nothing removed)c                     g | ]\  }}}|	S r   r   r   r   r   s      r(   r[  z.remove_legacy_hermes_units.<locals>.<listcomp>      +++Aq1+++r*   zRemove these legacy units?Tz6Skipped. Run again with: hermes gateway migrate-legacyc                     g | ]\  }}}|	S r   r   r  s      r(   r[  z.remove_legacy_hermes_units.<locals>.<listcomp>  r  r*   stopFrG  rD  disablerC  )
missing_oku     ✓ Removed rd   u     ⚠ Could not remove ru  Ndaemon-reloadz1System-scope legacy units require root to remove.z1  Re-run with: sudo hermes gateway migrate-legacyu5    legacy unit(s) still present — see messages above.zRemoved z legacy unit(s).)r   r  r   r   unlinkrx   r   r   rm   geteuidr   r   rZ   r   )r	  r
  r  
user_unitssystem_unitsr   r8   r  r  removed	remaininger   s                r(   remove_legacy_hermes_unitsr     ss   ( '((F 5666"uBBVBBBJ@@v@@@L	GGG	
0111!' , ,dI%16*4**E***++++	GGG ,-...++F+++++ ,=)EtLL ,FGGG++F+++++GI ! 	# 	#
d	#FD>%ubQQQQIt,U%QSTTTTKK4K((()4))***qLGG& 	# 	# 	#7D77A77888T""""""""	#  	O,U%QSTTTTT 	 	 	D	  :<<1GGGMNNNJKKK' ' '4  &&&&' + 	+ 	+
d+"FD>$eUWXXXX"It#4TXZ[[[[KK4K0001411222qLGG. + + +?D??A??@@@$$T********+0UTVWWWWW    
GGG <Y^^^____::::;;;IsV   &AD>>F*E>>F	F 
F+*F+AI22J7*J22J7;K 
KKc                  2   t                      } t          |           dk     rd S d                    |           }t          d| d           t	          d           t	          d           t	          d           t	          d           t	          d	           d S )
NrI   z + z5Both user and system gateway services are installed (r  zF  This is confusing and can make start/stop/status behavior ambiguous.zL  Default gateway commands target the user service unless you pass --system.z  Keep one of these:z    hermes gateway uninstallz*    sudo hermes gateway uninstall --system)r  rZ   r   r   r   )r  rendered_scopess     r(   $print_systemd_scope_conflict_warningr#    s    )++F
6{{Qjj((O]/]]]^^^WXXX]^^^%&&&-...;<<<<<r*   actionc                 \    t          j                    dk    rt          d|  d|           d S )Nr   zSystem gateway z! requires root. Re-run with sudo.)rm   r  r  )r$  s    r(    _require_root_for_system_servicer&    s>    	z||q*GfGGG
 
 	
 r*   run_as_userc                 D   dd l }dd l}dd l}| pOt          j        d          p;t          j        d          p't          j        d          p|                                                                }|st          d          |dk    r| st          d          |dk    rt          d           t          d	           	 |
                    |          }n%# t          $ r}t          d
|           |d }~ww xY w|                    |j                  j        }|||j        fS )Nr   	SUDO_USERUSERLOGNAMEz@Could not determine which user the gateway service should run asrootztRefusing to install the gateway system service as root; pass --run-as-user root to override (e.g. in LXC containers)z*Installing gateway service to run as root.zV  This is fine for LXC/container environments but not recommended on bare-metal hosts.zUnknown user: )r  grppwdrm   getenvr  rO   rT   r   r   getpwnamKeyErrorgetgrgidpw_gidgr_namepw_dir)r'  r  r-  r.  r  	user_infor  
group_names           r(   _system_service_identityr8    sY   NNNJJJJJJwry55w69J9JwbiXaNbNbwfmfufufwfw~~  A  AH ][\\\6+  P  Q  Q  	Q6BCCCklll=LL**		 = = =4(44551<= i.//7JZ!111s   >C 
C6C11C6r  c                    |                                  sd S |                     d                                          D ]K}|                    d          r4|                    dd          d                                         }|pd c S Ld S )Nr   r   zUser=r   rd   )r   r  rP   r   rQ   rO   )r  r^   r   s      r(   _read_systemd_user_from_unitr;    s     t##W#55@@BB ! !??7## 	!JJsA&&q)//11E=D   	! 4r*   c                  
   t          j        d          t          j        d          t          j        d          fD ]F} | rB|                                 r.|                                 dk    r|                                 c S Gd S )Nr)  r*  r+  r,  )rm   r/  rO   )	candidates    r(   _default_system_service_userr>  (  s    i,,bi.?.?9AUAUV % %	 	%** 	%y/@/@F/J/J??$$$$$4r*   c                  B    t          dg dd          } ddd d|          S )Nz6  Choose how the gateway should run in the background:)zPUser service (no sudo; best for laptops/dev boxes; may need linger after logout)zGSystem service (starts on boot; requires sudo; still runs as your user)zSkip service install for nowr   r   r  r   )r   rd   rI   )r   )choices    r(   "prompt_linux_gateway_install_scoperB  /  sD    @	
 	
 	

   F (t,,V44r*   c                    t                      }|dS |dk    rt                      }t          j                    dk    rFt	          d           |rt          d|            nt          d           t          d           |dfS |s;	 t          d
d          }|pd                                }|rnt          d           :t          | d	|           |d	fS t          | d           |d	fS )NNFr   r   zY  System service install requires sudo, so Hermes can't create it from this user session.zG  After setup, run: sudo hermes gateway install --system --run-as-user zR  After setup, run: sudo hermes gateway install --system --run-as-user <your-user>z8  Then start it with: sudo hermes gateway start --systemFTz/  Run the system gateway service as which user?r   r@  z  Enter a username.rq  r   r'  )rq  r   )
rB  r>  rm   r  r   r   r   rO   r   systemd_install)rq  r  r'  s      r(    install_linux_gateway_from_setuprG  <  s-   .00E}{244:<<1uvvv qreprrssssopppQRRR%< 	33$%V`bccc*0b7799 12223 	eDkJJJJd{%....$;r*   c                     t                      rdS t                      sdS t          j        d          sdS t	          j        d          pt	          j        d          } | sB	 ddl}|                    t	          j                              j	        } n# t          $ r Y d	S w xY w	 t          j        dd
| ddgdddd          }n(# t          $ r}dt          |          fcY d}~S d}~ww xY w|j        dk    r0|j        p|j        p	d|j                                         }d|pdfS |j        pd                                                                }|dv rdS |dv rdS |pd}dd| fS )zReturn systemd linger status for the current user.

    Returns:
        (True, "") when linger is enabled.
        (False, "") when linger is disabled.
        (None, detail) when the status could not be determined.
    )Nznot supported in Termux)Nznot supported on this platformr  )Nloginctl not foundr*  r+  r   N)Nz could not determine current userz	show-userz--property=LingerrE   TFr   r  r  zloginctl query failedr   >   1yestrue)Tr   >   r   nofalse)Fr   z<empty>zunexpected loginctl output: )rP  rR  r  r  rm   r/  r.  getpwuidr  pw_namer   rL   rM   r0   rY   r   rN   rO   r+  )r  r.  r]   r  r  r   r^  s          r(   r  r  [  s    {{ /..:: 655<
## *))y  8BIi$8$8H <	<JJJ||BIKK008HH 	< 	< 	<;;;	<	h0CYO
 
 
    SVV| A-O6=O4OF<M4O4OVVXXV6666] b''))//11E$$$x$$$y!	H:::::s0   "/B 
B B $C 
C(C#C(#C(c                      t                      \  } }| du rt          d           d
S | du r t          d           t          d           d
S t          d| d           t          d           t          d	           d
S )z@Print the current linger status and the fix when it is disabled.T7   ✓ Systemd linger is enabled (service survives logout)FB   ⚠ Systemd linger is disabled (gateway may stop when you log out)(  Run: sudo loginctl enable-linger $USERu%   ⚠ Could not verify systemd linger (r  z>  If you want the gateway user service to survive logout, run:z#  sudo loginctl enable-linger $USERN)r  r  )r  r  s     r(   print_systemd_linger_guidancerU    s    $=$?$?!NMGHHHHH	5	 	 RSSS899999FmFFFGGGNOOO344444r*   c                  z    ddl } t          |                     t          j                              j                  S )zReturn the real macOS user home for launchd artifacts.

    Profile-mode Hermes often sets ``HOME`` to a profile-scoped directory, but
    launchd user agents still live under the actual account home.
    r   N)r.  r   rO  rm   r  r5  )r.  s    r(   _launchd_user_homerW    s0     JJJRY[[))0111r*   c                  d    t                      } | rd|  nd}t                      dz  dz  | dz  S )u   Return the launchd plist path, scoped per profile.

    Default ``~/.hermes`` → ``ai.hermes.gateway.plist`` (backward compatible).
    Profile ``~/.hermes/profiles/coder`` → ``ai.hermes.gateway-coder.plist``.
    ai.hermes.gateway-ai.hermes.gatewayLibraryLaunchAgentsz.plist)r  rW  )r  r   s     r(   rK  rK    sI     F,2K((((8KD)+n<$NNr*   c                  r   t           j        t           j        k    r/t          t           j                  } |                                 r| S t
          j                            d          }|r%t          |          } |                                 r| S dD ]$}t          |z  } |                                 r| c S %dS )ab  Detect the active virtualenv directory.

    Checks ``sys.prefix`` first (works regardless of the directory name),
    then ``VIRTUAL_ENV`` env var (covers uv-managed environments where
    sys.prefix == sys.base_prefix), then falls back to probing common
    directory names under PROJECT_ROOT.
    Returns ``None`` when no virtualenv can be found.
    VIRTUAL_ENV)z.venvvenvN)	r   prefixbase_prefixr   is_dirrm   r  r   PROJECT_ROOT)r_  _virtual_envr=  s      r(   _detect_venv_dirre    s     zS_$$CJ;;== 	K
 :>>-00L L!!;;== 	K '  	i';;== 	KKK	 4r*   c                      t                      } | Bt                      r	| dz  dz  }n| dz  dz  }|                                rt          |          S t          j        S )NScriptsz
python.exebinpython)re  r   r   r0   r   r   )r_  venv_pythons     r(   r   r     sh    D<< 	2*\9KK,1K 	${###>r*   r  path_entriesc                     t          | dz  dz            t          | dz  dz            t          | dz  dz            t          | dz  dz            g}fd|D             S )zKReturn user-local bin dirs that exist and aren't already in *path_entries*.z.localrh  z.cargogoz.npm-globalc                 ^    g | ])}|vt          |                                          '|*S r   )r   r   )r   r   rk  s     r(   r[  z+_build_user_local_paths.<locals>.<listcomp>  s8    PPP!Ql%:%:tAww~~?O?O%:A%:%:%:r*   rY  )r  rk  
candidatess    ` r(   _build_user_local_pathsrp    sz     	D8Oe#$$D8Oe#$$D4K%  D= 5())	J QPPPzPPPPr*   c                    t                      sg S g }t          j                            dd                              t          j                  D ],}|                    d          r|                    |           -dD ]L}t          j	        |          }|r4|                    t          t          |          j                             MdD ]8}t          |                                          r|                    |           9g }t          |           }|D ]2}|r.||vr*|                    |           |                    |           3|S )av  Return WSL Windows interop PATH entries for generated systemd units.

    WSL shells normally inherit Windows PATH entries such as
    ``/mnt/c/WINDOWS/System32``. systemd user services do not, so gateway tools
    that call ``powershell.exe``/``cmd.exe`` work in a terminal but fail in the
    background service unless we persist the relevant entries at install time.
    PATHr   z/mnt/)zpowershell.exezcmd.exezexplorer.exezwsl.exe)z/mnt/c/WINDOWS/system32z/mnt/c/WINDOWSz/mnt/c/WINDOWS/System32/Wbemz//mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/z /mnt/c/WINDOWS/System32/OpenSSH/)r  rm   r  r   rQ   pathsepr   r   r  r  r0   r   r   r   rJ   rS   )rk  ro  entryr   resolvedr]   ro   s          r(   _build_wsl_interop_pathsrv    s\    88 	J++11"*== % %G$$ 	%e$$$N : :
<
++ 	:c$x.."788999 % % ;; 	%e$$$F|D ! ! 	!U$&&HHUOOOMM%   Mr*   r8   target_home_dirc                    t          j                    }t          |                                           }	 |                    |          }t	          t          |          |z            S # t
          $ r t	          |          cY S w xY w)aZ  Remap *path* from the current user's home to *target_home_dir*.

    If *path* lives under ``Path.home()`` the corresponding prefix is swapped
    to *target_home_dir*; otherwise the path is returned unchanged.

      /root/.hermes/hermes-agent  -> /home/alice/.hermes/hermes-agent
      /opt/hermes                 -> /opt/hermes  (kept as-is)

    Note: this function intentionally does NOT resolve symlinks. A venv's
    ``bin/python`` is typically a symlink to the base interpreter (e.g. a
    uv-managed CPython at ``~/.local/share/uv/python/.../python3.11``);
    resolving that symlink swaps the unit's ``ExecStart`` to a bare Python
    that has none of the venv's site-packages, so the service crashes on
    the first ``import``. Keep the symlinked path so the venv activates
    its own environment. Lexical expansion only via ``expanduser``.
    )r   r  
expanduserr  r0   rT   )r8   rw  r   r   relatives        r(   _remap_path_for_userr{    s    " 9;;LT

A==..4((83444   1vvs   3A* *BBc                 n   t                                                      }t          j                    dz                                  }t          |           dz  }||k    rt	          |          S 	 |                    |          }t	          ||z            S # t          $ r t	          |          cY S w xY w)u  Remap the current HERMES_HOME to the equivalent under a target user's home.

    When installing a system service via sudo, get_hermes_home() resolves to
    root's home.  This translates it to the target user's equivalent path:
      /root/.hermes                    → /home/alice/.hermes
      /root/.hermes/profiles/coder     → /home/alice/.hermes/profiles/coder
      /opt/custom-hermes               → /opt/custom-hermes  (kept as-is)
    z.hermes)r
   r   r   r  r0   r  rT   )rw  current_hermescurrent_defaulttarget_defaultrz  s        r(   _hermes_home_for_target_userr  ,  s     %&&..00Ny{{Y.7799O/**Y6N ((>"""#!--o>>>H,--- # # #>"""""#s   1&B B43B4c                    t                      }t          t                    }t                      }|rt          |          nt          t          dz            }|rt          |dz            nt          t          dz  dz            }t          t          dz  dz            }||g}t	          j        d          }	|	rLt          t          |	                                          j                  }
|
|vr|	                    |
           g d}t          t                      pd          }t          d|          d	z   }| rIt          |          \  }}t                    }t          |          }t!          |          }t!          |          }t!          |          }t!          |          }t!          |          }fd
|D             }|                    t%          t                    |                     |                    t'          |                     |                    |           d                    |          }dt*           d| d| d| d|rd| nd d| d d| d| d| d| d| dt,           d| dS t          t/                                                                }t          |          }|                    t%          t          j                    |                     |                    t'          |                     |                    |           d                    |          }dt*           d| d|rd| nd d| d| d| d| dt,           d| dS ) Nr_  rh  node_modules.binnode)z/usr/local/sbinz/usr/local/binz	/usr/sbinz/usr/binz/sbinz/binr   <   rC  c                 0    g | ]}t          |          S r   )r{  )r   r   home_dirs     r(   r[  z)generate_systemd_unit.<locals>.<listcomp>k  s$    PPPa,Q99PPPr*   :z[Unit]
Description=zm
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
User=z
Group=z
ExecStart=z -m hermes_cli.mainr   r   z( gateway run --replace
WorkingDirectory=z
Environment="HOME=z"
Environment="USER=z"
Environment="LOGNAME=z"
Environment="PATH=z"
Environment="VIRTUAL_ENV=z"
Environment="HERMES_HOME=z\"
Restart=always
RestartSec=60
RestartMaxDelaySec=300
RestartSteps=5
RestartForceExitStatus=zV
KillMode=mixed
KillSignal=SIGTERM
ExecReload=/bin/kill -USR1 $MAINPID
TimeoutStopSec=zT
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
zr
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=z
Environment="PATH=zQ
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=default.target
)r   r0   rc  re  r  r  r   r   r   r   r3   _get_restart_drain_timeoutr   r8  r  r   r{  r   rp  rv  r   SERVICE_DESCRIPTIONr   r
   r  )r   r'  python_pathworking_dirdetected_venvvenv_dirvenv_binnode_binrk  resolved_noderesolved_node_dircommon_bin_paths_drain_timeoutrestart_timeoutr  r7  r  profile_arg	sane_pathr  s                      @r(   generate_systemd_unitr  F  sl   !##Kl##K$&&M%2Rs=!!!L6<Q8R8RH-:bs=5()))LSYDY\aDa@b@bH<.069::Hh'LL((M 3] 3 3 ; ; = = DEEL00 1222fff 355:;;N"n--2O 1)A+)N)N&*h28<<";// +;AA*;AA'(;;'(;;'(;;PPPP<PPP3DNNLQQRRR4\BBCCC,---HH\**	                  AL+S+<{+<+<+<QS                         #!   " &#   , :-   4  5       	D o''//1122K{++K/	\JJKKK0>>???()))&&I     AL+S+<{+<+<+<QS      #  & " :# *  +   r*   rA   c                     d                     d |                                                                 D                       S )Nr   c              3   >   K   | ]}|                                 V  d S r$   )rstrip)r   r^   s     r(   r   z0_normalize_service_definition.<locals>.<genexpr>  s*      IItT[[]]IIIIIIr*   )r   rO   rP   )rA   s    r(   _normalize_service_definitionr    s7    99IItzz||/F/F/H/HIIIIIIr*   c                 d    ddl }t          |           }|                    dd||j                  S )a`  Normalize launchd plist text for staleness checks.

    The generated plist intentionally captures a broad PATH assembled from the
    invoking shell so user-installed tools remain reachable under launchd.
    That makes raw text comparison unstable across shells, so ignore the PATH
    payload when deciding whether the installed plist is stale.
    r   Nz,(<key>PATH</key>\s*<string>)(.*?)(</string>)z\1__HERMES_PATH__\3)flags)r  r  subS)rA   r  
normalizeds      r(   '_normalize_launchd_plist_for_comparisonr    sC     III.t44J667d	    r*   c                    t          |           }|                                sdS |                    d          }| rt          |          nd }t	          | |          }t          |          t          |          k    S )Nr   Fr   r:  r   r'  )r   r   r  r;  r  r  )r   r  	installedexpected_userexpecteds        r(   systemd_unit_is_currentr    s    %V444I u##W#55I?EO0;;;4M$FNNNH(337TU]7^7^^^r*   c                 L   t          |           }|                                rt          |           rdS | rt          |          nd}|                    t          | |          d           t          dg| dd	
           t          dt          |            d           dS )zMRewrite the installed systemd unit when the generated definition has changed.r   FNr  r   r:  r  TrC  rD  u   ↻ Updated gateway z7 service definition to match the current Hermes install)	r   r   r  r;  
write_textr  r   r  r  )r   r  r  s      r(   refresh_systemd_unit_if_neededr    s    %V444I !8!G!G!G u?EO0;;;4M.f-XXXcjkkkO$V4LLLL	
v!5f!=!=
v
v
vwww4r*   r  c                 Z   t                       t          d           |rt          d|            t                       t          d           t          d|             t                       t          d           t          dt                       d           t                       d S )NuI   ⚠ Linger not enabled — gateway may stop when you close this terminal.z  Auto-enable failed: z1  On headless servers (VPS, cloud instances) run:z     sudo loginctl enable-linger   Then restart the gateway:z    systemctl --user restart rC   )r  r   )r  r  s     r(   _print_linger_enable_warningr    s    	GGG	
UVVV 1/v//000	GGG	
=>>>	
7X
7
7888	GGG	
'(((	
F*:*<*<
F
F
FGGG	GGGGGr*   c                     t                      st                      sdS ddl} |                                 }t	          d|           }|                                rt          d           dS t                      \  }}|du rt          d           dS t          j	        d          st          ||pd           dS t          d           	 t          j        dd	|gddd
d          }n5# t          $ r(}t          |t          |                     Y d}~dS d}~ww xY w|j        dk    rt          d           dS |j        p|j        p	d|j                                         }t          ||p|           dS )z@Enable linger when possible so the user gateway survives logout.Nr   z/var/lib/systemd/linger/rR  Tr  rI  z5Enabling linger so the gateway survives SSH logout...r  FrC  r  u8   ✓ Linger enabled — gateway will persist after logoutr  )rP  rR  r  r  r   r   r  r  r  r  r  rL   rM   r   r0   rY   r   rN   rO   )r  r  linger_filer  r  r]   r  r  s           r(   _ensure_linger_enabledr    s   {{ (** NNN  H<(<<==K GHHH$=$?$?!NMGHHH<
## $X}/T@TUUU	
ABBB
(3
 
 
    $Xs1vv666 AHIIImKv}K0K8I0K0KRRTTF 6+B]CCCCCs   C) )
D3DDc                     | rdS t          d                                          o"t          d                                           S )NTr   F)r   r   r   s    r(   r   r      sM     t ---4466k?T\a?b?b?b?i?i?k?k;kkr*   c                 T    t          j                    dk    rdS t          |           S )a  True when the setup wizard is about to trigger a system-scope operation
    as a non-root user.

    Replicates the decision ``_select_systemd_scope`` makes inside
    ``systemd_start`` / ``systemd_restart`` / ``systemd_stop`` so the wizard
    can detect the dead-end BEFORE prompting, rather than letting
    ``SystemScopeRequiresRootError`` propagate out and leave the user
    staring at a bare shell.
    r   Fr   )rm   r  r   r   s    r(   $_system_scope_wizard_would_need_rootr  &  s+     
z||qu ////r*   c                    t                      }t          d|  d           t          d           t          d|                                  d           | dk    rt          d|            nG| dk    rt          d	|            n.| d
k    rt          d|            nt          d|  d|            t          d           t          d           t          d           t          d           dS )zPrint actionable remediation when the wizard skips a system-scope
    prompt because the user isn't root. Keeps the wizard flowing instead of
    aborting.
    u2   Gateway is installed as a system-wide service — z requires root.z
  Options:z    1. z it this time:rF  z         sudo systemctl start r  z         sudo systemctl stop restartz          sudo systemctl restart z         sudo systemctl r   zC    2. Switch to a per-user service (recommended for personal use):z/         sudo hermes gateway uninstall --systemz         hermes gateway installz         hermes gateway startN)r   r   r   r  )r$  r`   s     r(   _print_system_scope_remediationr  5  s>   
 

C	#	# 	# 	#   |<**,,<<<===9C99::::	6		83889999	9		;c;;<<<<<f<<s<<===TUUU@AAA0111./////r*   c                  8   t          j        dd                                          } | sct                      }t	          |t
                    r|                    di           ni }t          |                    dt                              } t          |           S )z?Return the configured gateway restart drain timeout in seconds.HERMES_RESTART_DRAIN_TIMEOUTr   agentrestart_drain_timeout)
rm   r/  rO   r   r  r  r   r0   r   r   )rh   cfg	agent_cfgs      r(   r  r  O  s    
)2B
7
7
=
=
?
?C 
,6sD,A,AICGGGR(((r	MM')N 
 

 's+++r*   c                    |rt          d           t                      rXt                       t                       t                       t	          dd          rt          d           t                       t          |          }|rdnd}|                                r| st          |          st          d	t          |           d
|            t          |           t          dt                      g|dd           t          dt          |                                           d           d S t          d|            t          d           d S |j                            dd           t          dt          |           d|            |                    t#          ||          d           t          dg|dd           t          dt                      g|dd           t                       t          dt          |                                           d           t                       t          d           t          d|rdnd d| d           t          d|rdnd d| d            t          d|rd!nd" d#t                       d$           t                       |r$t%          |          }|rt          d%|            nt'                       t)                       t                       d S )&Ninstallz,Remove the legacy unit(s) before installing?TF)r	  r   r8  r   u   ↻ Repairing outdated z systemd service at: enablerC  rD  r  z service definition updatedService already installed at: Use --force to reinstallparentsexist_okzInstalling z systemd service to: r  r   r:  r  z service installed and enabled!Next steps:rk  r  hermes gateway startz!              # Start the servicezhermes gateway statusz             # Check statusr  zjournalctl --userz -u z -f  # View logsConfigured to run as: )r&  r  r  r  r   r   r   r   r  r  r  r   r   r  r   mkdirr  r  r;  r  r#  )rq  r   r'  r  r;  configured_users         r(   rF  rF  ]  sg    4(333    !###GNN 	&59999GGG%V444I &.BJ 	% 	&f555 	j,@,H,Hjj_hjjkkk*&9999H&6&8&89&PT^`aaaa_-f55@@BB___```F:y::;;;()))4$777	
V,V44
V
V9
V
VWWW.f+VVVahiiiO$V4LLLLH.001&VXYYYY	GGG	
[%f--88::
[
[
[\\\	GGG	-	
k&(wwb
k
kj
k
k
klll	
f&(wwb
f
fz
f
f
fggg	
hv>||+>
h
hDTDVDV
h
h
hiii	GGG !6yAA 	><?<<===   (***r*   c                    t          |           } | rt          d           t          dt                      g| dd           t          dt                      g| dd           t	          |           }|                                r&|                                 t          d	|            t          d
g| dd           t          dt          |           	                                 d           d S )N	uninstallr  FrG  rD  r  rC  r      ✓ Removed r  Tr  z service uninstalled)
r   r&  r   r   r   r   r  r  r  r  )r   r  s     r(   systemd_uninstallr    s	   "6**F 6(555F,../eUWXXXXI/1126XZ[[[[%V444I *(Y(()))O$V4LLLL	
P%f--88::
P
P
PQQQQQr*   c                     t          |          }|                                sD|rdnd}t          d           t          d|rdnd d|            t          j        d           d S d S )	Nr   r8  r   $   ✗ Gateway service is not installed  Run: r  hermes gateway installrd   )r   r   r  r   exit)r$  r   r  r;  s       r(   _require_service_installedr    s    %V444I $*2[[
5666U61rUUUUVVV	 r*   c                 L   t          |           } | rt          d           nt                       t          d|            t	          |            t          dt                      g| dd           t          dt          |           	                                 d           d S )NrF  r   TrC  rD  r  z service started)
r   r&  r  r  r  r   r   r  r  r  r   s    r(   systemd_startr    s    "6**F "(1111
 	 !!!wv6666"&1111G-//0tUWXXXX	
L%f--88::
L
L
LMMMMMr*   c                 t   t          |           } | rt          d           t          d|            	 ddlm}m}  |d          }| ||           n# t          $ r Y nw xY wt          dt                      g| dd	           t          d
t          |                                            d           d S )Nr  r   r   r   rz  Fr   TrG  rD  r  z service stopped)r   r&  r  r   r   rz  r   r   r   r  r  r  )r   r   rz  r9   s       r(   systemd_stopr    s    "6**F 1(000vf5555MMMMMMMMoE222?%%c***   F,../dTVWWWW	
L%f--88::
L
L
LMMMMMs   !A 
A"!A"c                    t          |           } | rt          d           nt                       t          d|            t	          |            ddlm}  |            pt          |           }|dt          |           	                                }t                      }t                      }t          d| d| d           t          ||dz             rOt          d	|g| d
d           t          d|g| d
d           t          | |          rd S t!          |           rd S t          dt#          |dz              d           t          d	|g| d
d           	 t          d|g| dd           nM# t$          j        $ r;}t)          |          st!          |           rt+          |            Y d }~d S  d }~ww xY wt          | |           d S t-          | |          rd S t          d	t                      g| d
d           	 t          dt                      g| dd           nM# t$          j        $ r;}t)          |          st!          |           rt+          |            Y d }~d S  d }~ww xY wt          | |           d S )Nr  r   r   r   r  z$ service restarting gracefully (PID z)...r>   rB  FrC  rD  rG  r@  u-   ⚠ Graceful restart did not complete within zs; forcing a service restart...T)r   r&  r  r  r  r   r   r  r  r  r   r  r  r   r   r(  r6  r3   rL   CalledProcessErrorr4  r   rI  )r   r   r9   r!  r`   rz   r,  s          r(   systemd_restartr    sz   "6**F "(3333!!!y8888"&1111......
/


?0???C
*622==??  244O[OOcOOOPPP(ma.?@@ 	
 %	    C 	    1SQQQ 0??? +CPQ@Q<R<R + + +	
 	
 	
 	S!		
 	
 	
 	
	Is+F$PRSSSSS, 	 	 	3C88 <]ek<l<l<l /v>>>>		
 	*cJJJJ'vCHHH 	)++,	   	#3#5#56vT[]^^^^^(   /44 	8Yag8h8h8h 	+6::::FFFFF	
 &V#FFFFFFs<   "E8 8G/F=<F==G!H/ /I9>/I43I44I9deepfullc           
      2	   t          |          }t          |          }|rdnd}|                                s*t          d           t          d|rdnd d|            d S t	                      rt                       t                       t                      rt                       t                       t          |          s7t          d           t          d|rdnd d	| d
           t                       dt                      dg}|r|
                    d           t          ||dd           t          dt                      g|ddd          }|j                                        }|dk    r3t          dt          |                                           d           nKt          dt          |                                           d           t          d|rdnd d|            |rt!          |          nd }|rt          d|            t#                      }	|	r4t                       t          d           |	D ]}
t          d|
            t%          |          }|                    dd          }|                    dd          }|                    dd          }|                    d d          }|d!k    r|d"k    rt          d#           nt)          |          rNt          d$           t          d%|rdnd d	|            t          d&|sd'nd d(t                                  nw|d)k    rW|t+          t,                    k    r?t          d*           t          d+|sd'nd d(t                       d,|rdnd d|            n|d)k    r|rt          d-|            |rt          d.           nX| rt/                       nGt1                      \  }}|du rt          d/           n"|du rt          d0           t          d1           | rot                       t          d2           t3          |          d3t                      d4d5dgz   }|r|
                    d           t5          j        |d6           d S d S )7Nr   r8  r   r  r  r  r  u4   ⚠ Installed gateway service definition is outdatedr9  z  # auto-refreshes the unitr  r=   z-lFr   )r   r@   rB   r   Tr   r   r  z gateway service is runningu   ✗ z gateway service is stoppedr  r  Recent gateway health:rk  r   r   r   r   r  r  uA     ⏳ Restart pending: systemd is waiting to relaunch the gatewayuB     ⏳ Restart pending: systemd is temporarily rate-limiting startsz,  Run after the start-limit window expires: z"  Or clear it manually: systemctl r  r:  rA  u@     ⚠ Planned restart is stuck in systemd failed state (exit 75)z  Run: systemctl z && u     ⚠ Systemd unit result: uB   ✓ System service starts at boot without requiring systemd lingerrR  rS  rT  Recent logs:z-uz-n20r  )r   r   r   r  r  r#  r  r  r  r   r   r   rN   rO   r  r  r;  _runtime_health_linesr   r   r  r0   r   rU  r  r  rL   rM   )r  r   r  r  r;  
status_cmdr]   r  r  runtime_linesr^   
unit_propsr#  r$  rH  result_coder  r   log_cmds                      r(   systemd_statusr  !	  s   "6**F%V444I &.BJ 4555U61rUUUUVVV$&& ,...   !###"&111 DEEEp61rpppppqqq,..=J  $	    	&(()  F ]  ""F[)&11<<>>[[[\\\\[)&11<<>>[[[\\\S61rSSzSSTTTAGQ29===TO :888999)++M &'''! 	 	D+t++.f===J>>-44Lz2..I!~~&6;;..2..K|##	^(C(CQRRRR	'
	3	3 ;RSSSz=VWWTVzznxzz{{{uF3R99PRuuaqasasuuvvvv		!	!&6#>_:`:`&`&`PQQQ  d6"A))r  d  dP`PbPb  d  dsy  iBhoho  @B  d  d  Xb  d  d  	e  	e  	e  	e		!	!k	!9K99::: 
>RSSSS	 >%''''577T!!KLLLLu$$VWWW<=== ,n!&))T3C3E3EtTS_,`` 	!NN4   w++++++, ,r*   c                  0    t                      } | rd|  ndS )z5Return the launchd service label, scoped per profile.rY  rZ  )r  r  s    r(   rX   rX   	  s'    F,2K((((8KKr*   c                  .    dt          j                     S )Nzgui/)rm   r  r   r*   r(   _launchd_domainr  	  s    ")++r*   c                     t                      } t          t                    }t          t                                                                }t                      dz  }|                    dd           t                      }t          |          }t                      }|rt          |dz            nt          t          dz  dz            }|rt          |          nt          t          dz            }t          t          dz  dz            }	||	g}
t          j
        d          }|rLt          t          |                                          j                  }||
vr|
                    |           d	                    t                              |
d
 t"          j                            dd                              d	          D             z                       }d|  dddg}|r0|                                D ]}|                    d| d           |                    g d           d                    |          }d| d| d| d| d| d| d| d| dS )NlogsTr  rh  r_  r  r  r  r  c                     g | ]}||S r   r   r   r   s     r(   r[  z*generate_launchd_plist.<locals>.<listcomp>	  s    &]&]&]Q[\&]q&]&]&]r*   rr  r   z<string>z	</string>z<string>-m</string>z <string>hermes_cli.main</string>)z<string>gateway</string>z<string>run</string>z<string>--replace</string>z	
        z<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>z?</string>

    <key>ProgramArguments</key>
    <array>
        z?
    </array>
    
    <key>WorkingDirectory</key>
    <string>zf</string>
    
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>z9</string>
        <key>VIRTUAL_ENV</key>
        <string>z9</string>
        <key>HERMES_HOME</key>
        <string>z</string>
    </dict>
    
    <key>RunAtLoad</key>
    <true/>
    
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <false/>
    </dict>
    
    <key>StandardOutPath</key>
    <string>zH/gateway.log</string>
    
    <key>StandardErrorPath</key>
    <string>z-/gateway.error.log</string>
</dict>
</plist>
)r   r0   rc  r
   r   r  rX   r   re  r  r  r   r   r   r   r  fromkeysrm   r  r   rQ   r   )r  r  r  log_dirra   r  r  r  r  r  priority_dirsr  r  r  	prog_argspartprog_args_xmls                    r(   generate_launchd_plistr  	  s   !##Kl##Ko''//1122K&(GMM$M...E{++K %&&M-:bs=5()))LSYDY\aDa@b@bH%2Rs=!!!L6<Q8R8RH<.069::H x(ML((M 4] 3 3 ; ; = = DEEM11  !2333m&]&]"*..2L2L2R2RSV2W2W&]&]&]]^^ I 	*;)))*I
  9%%'' 	9 	9D77778888      
 !%%i00M)
 ) ) 
) ) ) )$ %) )( )) ), -) )F G) )L M) ) ) )r*   c                      t                      } |                                 sdS |                     d          }t                      }t	          |          t	          |          k    S )zICheck if the installed launchd plist matches the currently generated one.Fr   r:  )rK  r   r  r  r  )
plist_pathr  r  s      r(   launchd_plist_is_currentr  	  sd    '))J u$$g$66I%''H29==AhiqArArrrr*   c                     t                      } |                                 rt                      rdS |                     t	                      d           t                      }t          j        ddt                       d| gdd           t          j        dd	t                      t          |           gdd
           t          d           dS )u@  Rewrite the installed launchd plist when the generated definition has changed.

    Unlike systemd, launchd picks up plist changes on the next ``launchctl kill``/
    ``launchctl kickstart`` cycle — no daemon-reload is needed. We still bootout/
    bootstrap to make launchd re-read the updated plist immediately.
    Fr   r:  rF   bootout/rG  rE  rB   	bootstraprC  uR   ↻ Updated gateway launchd service definition to match the current Hermes installT)rK  r   r  r  r  rX   rL   rM   r  r0   r  r  ra   s     r(   refresh_launchd_plist_if_neededr  	  s     ())J ":"<"< u022WEEEENK/@/@,J,J5,J,JKSXbdeeeeNKo.?.?ZQY^hjkkkk	
^___4r*   c                    t                      }|                                rd| sbt                      s1t          d|            t	                       t          d           d S t          d|            t          d           d S |j                            dd           t          d|            |                    t                                 t          j
        dd	t                      t          |          gdd
           t                       t          d           t                       t          d           t          d           ddlm} t          d |             d           d S )Nu+   ↻ Repairing outdated launchd service at: u   ✓ Service definition updatedr  r  Tr  zInstalling launchd service to: rF   r  rC  r  u!   ✓ Service installed and loaded!r  z2  hermes gateway status             # Check statusr   display_hermes_homez
  tail -f z/logs/gateway.log  # View logs)rK  r   r  r  r  r   r  r  r  rL   rM   r  r0   rQ  r  )rq  r  _dhhs      r(   launchd_installr  	
  s   '))J 5 ')) 	L
LLMMM+---2333F;z;;<<<()))D4888	
8J
8
8999022333NKo.?.?ZQY]gijjjj	GGG	
-...	GGG	-	
>???<<<<<<	
=ttvv
=
=
=>>>>>r*   c                  &   t                      } t                      }t          j        ddt	                       d| gdd           |                                 r&|                                  t          d|             t          d           d S )	NrF   r  r  FrG  r  r  u   ✓ Service uninstalled)rK  rX   rL   rM   r  r   r  r  r   s     r(   launchd_uninstallr  $
  s    '))JENK/@/@,J,J5,J,JKSXbdeeee +)Z))***	
#$$$$$r*   c            	         t                      } t                      }|                                 st          d           | j                            dd           |                     t                      d           t          j	        ddt                      t          |           gdd	           t          j	        dd
t                       d| gdd	           t          d           d S t                       	 t          j	        dd
t                       d| gdd	           n# t          j        $ r}|j        dvr t          d           t          j	        ddt                      t          |           gdd	           t          j	        dd
t                       d| gdd	           Y d }~nd }~ww xY wt          d           d S )Nu:   ↻ launchd plist missing; regenerating service definitionTr  r   r:  rF   r  rC  r  	kickstartr  u   ✓ Service startedrH   q   u:   ↻ launchd job was unloaded; reloading service definition)rK  rX   r   r  r   r  r  r  rL   rM   r  r0   r  r  rY   )r  ra   r  s      r(   launchd_startr  /
  s   '))JE  JKKKt<<<466III[/2C2CS__U]akmnnnn[_5F5F2P2P2P2PQY]gijjjj#$$$#%%%k[_5F5F2P2P2P2PQY]gijjjjj( k k k<x''JKKK[/2C2CS__U]akmnnnn[_5F5F2P2P2P2PQY]gijjjjjjjjjk 

     s   >+D* *F;9A8F66F;c                  t   t                      } t                       d|  }	 ddlm}m}  |d          }| ||           n# t
          $ r Y nw xY w	 t          j        dd|gdd	
           n'# t          j        $ r}|j	        dv rn Y d }~nd }~ww xY wt          dd           t          d           d S )Nr  r   r  Fr   rF   r  TrG  r  r        $@r  rB   force_afteru   ✓ Service stopped)rX   r  r   r   rz  r   rL   rM   r  rY   _wait_for_gateway_exitr  )ra   targetr   rz  r9   r  s         r(   launchd_stopr  H
  s   E!!++E++FMMMMMMMMoE222?%%c***   Y7tRPPPPP(   <8## DDDD
 4S9999	
     s)   !A 
AAA1 1B BBr  r  r  c                 4   ddl }ddlm} |                                | z   }||                                |z   nd}d}|                                |k     r |            }|dS |`|s^|                                |k    rF	 t	          |d           t          d| d           n# t          t          t          f$ r Y dS w xY wd}|	                    d	           |                                |k      |            }|t          d| d
|  d           dS dS )u  Wait for the gateway process (by saved PID) to exit.

    Uses the PID from the gateway.pid file — not launchd labels — so this
    works correctly when multiple gateway instances run under separate
    HERMES_HOME directories.

    Args:
        timeout: Total seconds to wait before giving up.
        force_after: Seconds of graceful waiting before escalating to force-kill.
    r   Nr   FTrs  u   ⚠ Gateway PID z& did not exit gracefully; sent SIGKILLg333333?z still running after u   s — restart may fail)
r}   r   r   r~   r   r  rv   rw   rx   r   )	rB   r  r}   r   r   force_deadline
force_sentr9   remaining_pids	            r(   r  r  `
  sl    KKK......~~')H9D9Pdnn&&44VZNJ
..

X
%
%o;4":"$..:J:Jn:\:\c....TTTTUUUU&A   ttJ

3 ..

X
%
%" $O%%M dddWdddeeeu4s   $B' 'CCc            	         t                      } t                       d|  }t                      }ddlm} 	  |            }| t          |          rt          d           d S |[	 t          |d           n# t          t          t          f$ r d }Y nw xY w|'t          |d           }|st          d|d	d
           t          j        ddd|gdd           t          d           d S # t          j        $ r}|j        dvr t          d           t!                      }t          j        ddt                      t#          |          gdd           t          j        dd|gdd           t          d           Y d }~d S d }~ww xY w)Nr  r   r   u   ✓ Service restart requestedFrs  r  u"   ⚠ Gateway drain timed out after z.0fu   s — forcing launchd restartrF   r
  z-kTrG  r  u   ✓ Service restartedr  u'   ↻ launchd job was unloaded; reloadingr  rC  )rX   r  r  r   r   ry   r  r   rv   rw   rx   r  rL   rM   r  rY   rK  r0   )ra   r  rz   r   r9   exitedr  r  s           r(   launchd_restartr  
  s   E!!++E++F.00M......'o?<SAA?1222F?c/////&A   /SWXXX qo}ooooppp[$?tUWXXXX%&&&&&( ' ' '<x''7888+--
[/2C2CS__U]akmnnnn[&9rRRRR%&&&&&&&&&'sC   *C, #C, &A8 7C, 8BC, BAC, ,F
;BFF
c                    t                      }t                      }	 t          j        dd|gddd          }|j        dk    }|j        }n# t          j        $ r d}d}Y nw xY wt          d	|            t                      rt          d
           nt          d           t          d           |rt          d           t          |           n-t          d           t          d           t          d           | rmt                      dz  dz  }|
                                rGt                       t          d           t          j        ddt          |          gd           d S d S d S )NrF   rG   Tr   r?   r   Fr   zLaunchd plist: u9   ✓ Service definition matches the current Hermes installuF   ⚠ Service definition is stale relative to the current Hermes installz  Run: hermes gateway startu   ✓ Gateway service is loadedu!   ✗ Gateway service is not loadedzB  Service definition exists locally but launchd has not loaded it.r  zgateway.logr  tailz-20r  )rK  rX   rL   rM   rY   rN   rU   r  r  r
   r   r0   )r  r  ra   r]   loadedloaded_outputlog_files          r(   launchd_statusr!  
  s   '))JE&%(	
 
 
 "a'$    

(J
(
()))!! -IJJJJVWWW+,,, --...m1222RSSS+,,, G"$$v-=?? 	GGGG.!!!NFE3x==92FFFFFFG G	G 	Gs   -A A"!A"r   c                 p    t          | pd                                                                          dv S )Nr   >   rJ  onrK  rL  )r0   rO   r+  )r   s    r(   _truthy_envr$  
  s2    u{!!##))++/IIIr*   c                  p    t          t                    dk    ot          dz  dz                                  S )Nz/opt/hermesdockerzentrypoint.sh)r0   rc  is_filer   r*   r(   _is_official_docker_checkoutr(  
  s6    L]* 	BH$6??AAr*   c                  h   t          t          d          rt          j                    dk    rdS t          t          j        d                    rdS t                      sdS t          d           t          d           t          d           t          d           t          j	        d	           dS )
zLRefuse gateway startup when the official Docker privilege drop was bypassed.r  r   NHERMES_ALLOW_ROOT_GATEWAYzLRefusing to run the Hermes gateway as root inside the official Docker image.z  The image entrypoint normally drops privileges to the 'hermes' user. If you override entrypoint in Docker Compose, include /opt/hermes/docker/entrypoint.sh before the Hermes command.zy  Running the gateway as root can leave root-owned files in $HERMES_HOME and break later non-root dashboard/gateway runs.zM  Set HERMES_ALLOW_ROOT_GATEWAY=1 only if you intentionally accept this risk.rd   )
rs   rm   r  r$  r/  r(  r   r  r   r  r   r*   r(   #_guard_official_docker_root_gatewayr+  
  s    2y!! RZ\\Q%6%629899:: ')) V   
	F  
 
	H   

YZZZHQKKKKKr*   verbosequietr/  c                    t                       t          j                            dt	          t
                               t                      r"	 t          d           n# t          $ r Y nw xY wddl	m
} t          d           t          d           t          d           t          d           t          d	           t          d
           t                       |rdn| }	 t          j         |||                    }n # t          $ r t          d           Y dS w xY w|st          j        d           dS dS )a  Run the gateway in foreground.
    
    Args:
        verbose: Stderr log verbosity count added on top of default WARNING (0=WARNING, 1=INFO, 2+=DEBUG).
        quiet: Suppress all stderr log output.
        replace: If True, kill any existing gateway instance before starting.
                 This prevents systemd restart loops when the old process
                 hasn't fully exited yet.
    r   Fr   )start_gateway   ┌─────────────────────────────────────────────────────────┐u@   │           ⚕ Hermes Gateway Starting...                 │   ├─────────────────────────────────────────────────────────┤u@   │  Messaging platforms + cron scheduler                    │u?   │  Press Ctrl+C to stop                                   │   └─────────────────────────────────────────────────────────┘N)r/  	verbosityz
Gateway stopped.rd   )r+  r   r8   insertr0   rc  rK   r  r   gateway.runr/  r  asynciorM   KeyboardInterruptr  )r,  r-  r/  r/  r3  successs         r(   run_gatewayr9  
  s    ()))HOOAs<(())) !"" 	*%88888 	 	 	D	 *)))))	  ~      	
LMMM	  ~      	
LMMM	
KLLL	  ~      	GGG *7I+mmGyQQQRR   "###   s$   A! !
A.-A.&D D#"D#telegramTelegramu   📱TELEGRAM_BOT_TOKEN)z'1. Open Telegram and message @BotFatherz92. Send /newbot and follow the prompts to create your botz)3. Copy the bot token BotFather gives youuQ   4. To find your user ID: message @userinfobot — it replies with your numeric IDz	Bot tokenz/Paste the token from @BotFather (step 3 above).)r   r   passwordhelpTELEGRAM_ALLOWED_USERSz"Allowed user IDs (comma-separated)z%Paste your user ID from step 4 above.)r   r   r=  is_allowlistr>  TELEGRAM_HOME_CHANNELzVHome channel ID (for cron/notification delivery, or empty to set later with /set-home)zPFor DMs, this is your user ID. You can set it later by typing /set-home in chat.)r   ra   emoji	token_varsetup_instructionsvarsdiscordDiscordu   💬DISCORD_BOT_TOKEN)uH   1. Go to https://discord.com/developers/applications → New Applicationu3   2. Go to Bot → Reset Token → copy the bot tokenuH   3. Enable: Bot → Privileged Gateway Intents → Message Content Intentz!4. Invite the bot to your server:u2      OAuth2 → URL Generator → check BOTH scopes:z
     - botz<     - applications.commands  (required for slash commands!)zE   Bot Permissions: Send Messages, Read Message History, Attach Filesz6   Copy the URL and open it in your browser to invite.z?5. Get your user ID: enable Developer Mode in Discord settings,u)      then right-click your name → Copy IDz"Paste the token from step 2 above.DISCORD_ALLOWED_USERSz/Allowed user IDs or usernames (comma-separated)z%Paste your user ID from step 5 above.DISCORD_HOME_CHANNELuD   Right-click a channel → Copy Channel ID (requires Developer Mode).slackSlacku   💼SLACK_BOT_TOKEN)uG   1. Go to https://api.slack.com/apps → Create New App → From Scratchu:   2. Enable Socket Mode: Settings → Socket Mode → EnableuR      Create an App-Level Token with scope: connections:write → copy xapp-... tokenuD   3. Add Bot Token Scopes: Features → OAuth & Permissions → ScopeszL   Required: chat:write, app_mentions:read, channels:history, channels:read,zU   groups:history, im:history, im:read, im:write, users:read, files:read, files:writeuC   4. Subscribe to Events: Features → Event Subscriptions → Enablez=   Required events: message.im, message.channels, app_mentionz2   Optional: message.groups (for private channels)u>      ⚠ Without message.channels the bot will ONLY work in DMs!uI   5. Install to Workspace: Settings → Install App → copy xoxb-... tokenz56. Reinstall the app after any scope or event changesuJ   7. Find your user ID: click your profile → three dots → Copy member IDz/8. Invite the bot to channels: /invite @YourBotzBot Token (xoxb-...)z&Paste the bot token from step 3 above.SLACK_APP_TOKENzApp Token (xapp-...)z,Paste the app-level token from step 4 above.SLACK_ALLOWED_USERSz'Paste your member ID from step 7 above.matrixMatrixu   🔐MATRIX_ACCESS_TOKEN)zX1. Works with any Matrix homeserver (self-hosted Synapse/Conduit/Dendrite or matrix.org)z@2. Create a bot user on your homeserver, or use your own accountuN   3. Get an access token: Element → Settings → Help & About → Access TokenzI   Or via API: curl -X POST https://your-server/_matrix/client/v3/login \zK     -d '{"type":"m.login.password","user":"@bot:server","password":"..."}'zL4. Alternatively, provide user ID + password and Hermes will log in directlyzT5. For E2EE: set MATRIX_ENCRYPTION=true (requires pip install 'mautrix[encryption]')zN6. To find your user ID: it's @username:your-server (shown in Element profile)MATRIX_HOMESERVERz0Homeserver URL (e.g. https://matrix.example.org)z@Your Matrix homeserver URL. Works with any self-hosted instance.z8Access token (leave empty to use password login instead)zMPaste your access token, or leave empty and provide user ID + password below.MATRIX_USER_IDu5   User ID (@bot:server — required for password login)z4Full Matrix user ID, e.g. @hermes:matrix.example.orgMATRIX_ALLOWED_USERSz4Allowed user IDs (comma-separated, e.g. @you:server)z.Matrix user IDs who can interact with the bot.MATRIX_HOME_ROOMzSHome room ID (for cron/notification delivery, or empty to set later with /set-home)zLRoom ID (e.g. !abc123:server) for delivering cron results and notifications.
mattermost
MattermostMATTERMOST_TOKEN)uC   1. In Mattermost: Integrations → Bot Accounts → Add Bot AccountuE      (System Console → Integrations → Bot Accounts must be enabled)z:2. Give it a username (e.g. hermes) and copy the bot tokenuK   3. Works with any self-hosted Mattermost instance — enter your server URLuA   4. To find your user ID: click your avatar (top-left) → Profileu8      Your user ID is displayed there — click it to copy.uI      ⚠ This is NOT your username — it's a 26-character alphanumeric ID.uL   5. To get a channel ID: click the channel name → View Info → copy the IDMATTERMOST_URLz(Server URL (e.g. https://mm.example.com)z@Your Mattermost server URL. Works with any self-hosted instance.z&Paste the bot token from step 2 above.MATTERMOST_ALLOWED_USERSz*Your Mattermost user ID from step 4 above.MATTERMOST_HOME_CHANNELz@Channel ID where Hermes delivers cron results and notifications.MATTERMOST_REPLY_MODEuT   Reply mode — 'off' for flat messages, 'thread' for threaded replies (default: off)zFoff = flat channel messages, thread = replies nest under your message.whatsappWhatsAppu   📲WHATSAPP_ENABLED)r   ra   rB  rC  rt   Signalu   📡SIGNAL_HTTP_URLemailEmailu   📧EMAIL_ADDRESS)z61. Use a dedicated email account for your Hermes agentz82. For Gmail: enable 2FA, then create an App Password atz,   https://myaccount.google.com/apppasswordszH3. For other providers: use your email password or app-specific passwordz-4. IMAP must be enabled on your email accountzEmail addressz;The email address Hermes will use (e.g., hermes@gmail.com).EMAIL_PASSWORDz Email password (or app password)z;For Gmail, use an App Password (not your regular password).EMAIL_IMAP_HOSTz	IMAP hostzBe.g., imap.gmail.com for Gmail, outlook.office365.com for Outlook.EMAIL_SMTP_HOSTz	SMTP hostz?e.g., smtp.gmail.com for Gmail, smtp.office365.com for Outlook.EMAIL_ALLOWED_USERSz'Allowed sender emails (comma-separated)z3Only emails from these addresses will be processed.smszSMS (Twilio)TWILIO_ACCOUNT_SID)z51. Create a Twilio account at https://www.twilio.com/zH2. Get your Account SID and Auth Token from the Twilio Console dashboardz93. Buy or configure a phone number capable of sending SMSz+4. Set up your webhook URL for inbound SMS:uF      Twilio Console → Phone Numbers → Active Numbers → your numberu`      → Messaging → A MESSAGE COMES IN → Webhook → https://your-server:8080/webhooks/twiliozTwilio Account SIDz&Found on the Twilio Console dashboard.TWILIO_AUTH_TOKENzTwilio Auth Tokenz8Found on the Twilio Console dashboard (click to reveal).TWILIO_PHONE_NUMBERz5Twilio phone number (E.164 format, e.g. +15551234567)z)The Twilio phone number to send SMS from.SMS_ALLOWED_USERSz5Allowed phone numbers (comma-separated, E.164 format)z9Only messages from these phone numbers will be processed.SMS_HOME_CHANNELzDHome channel phone number (for cron/notification delivery, or empty)z>Phone number to deliver cron job results and notifications to.dingtalkDingTalkDINGTALK_CLIENT_ID)u=   1. Go to https://open-dev.dingtalk.com → Create ApplicationzQ2. Under 'Credentials', copy the AppKey (Client ID) and AppSecret (Client Secret)z.3. Enable 'Stream Mode' under the bot settingsz54. Add the bot to a group chat or message it directlyzAppKey (Client ID)z6The AppKey from your DingTalk application credentials.DINGTALK_CLIENT_SECRETzAppSecret (Client Secret)z9The AppSecret from your DingTalk application credentials.feishuzFeishu / Larku   🪽FEISHU_APP_ID)zJ1. Go to https://open.feishu.cn/ (or https://open.larksuite.com/ for Lark)z32. Create an app and copy the App ID and App Secretz(3. Enable the Bot capability for the appz<4. Choose WebSocket (recommended) or Webhook connection modez55. Add the bot to a group chat or message it directlyz?6. Restrict access with FEISHU_ALLOWED_USERS for production usezApp IDz-The App ID from your Feishu/Lark application.FEISHU_APP_SECRETz
App Secretz1The App Secret from your Feishu/Lark application.FEISHU_DOMAINu+   Domain — feishu or lark (default: feishu)z@Use 'feishu' for Feishu China, or 'lark' for Lark international.FEISHU_CONNECTION_MODEu=   Connection mode — websocket or webhook (default: websocket)zCwebsocket is recommended unless you specifically need webhook mode.FEISHU_ALLOWED_USERSz,Allowed user IDs (comma-separated, or empty)z;Restrict which Feishu/Lark users can interact with the bot.FEISHU_HOME_CHANNELz/Home chat ID (optional, for cron/notifications)z0Chat ID for scheduled results and notifications.wecomzWeCom (Enterprise WeChat)WECOM_BOT_ID)u?   1. Go to WeCom Admin Console → Applications → Create AI Botz=2. Copy the Bot ID and Secret from the bot's credentials pageu?   3. The bot connects via WebSocket — no public endpoint neededz>4. Add the bot to a group chat or message it directly in WeComz>5. Restrict access with WECOM_ALLOWED_USERS for production usezBot IDz"The Bot ID from your WeCom AI Bot.WECOM_SECRETSecretz"The secret from your WeCom AI Bot.WECOM_ALLOWED_USERSz5Restrict which WeCom users can interact with the bot.WECOM_HOME_CHANNELwecom_callbackzWeCom Callback (Self-Built App)WECOM_CALLBACK_CORP_ID)uG   1. Go to WeCom Admin Console → Applications → Create Self-Built AppzC2. Note the Corp ID (top of admin console) and create a Corp SecretzM3. Under Receive Messages, configure the callback URL to point to your serverzD4. Copy the Token and EncodingAESKey from the callback configurationuN   5. The adapter runs an HTTP server — ensure the port is reachable from WeComzG6. Restrict access with WECOM_CALLBACK_ALLOWED_USERS for production usezCorp IDzYour WeCom enterprise Corp ID.WECOM_CALLBACK_CORP_SECRETzCorp Secretz+The secret for your self-built application.WECOM_CALLBACK_AGENT_IDzAgent IDz,The Agent ID of your self-built application.WECOM_CALLBACK_TOKENzCallback Tokenz1The Token from your WeCom callback configuration.WECOM_CALLBACK_ENCODING_AES_KEYzEncoding AES Keyz:The EncodingAESKey from your WeCom callback configuration.WECOM_CALLBACK_PORTz$Callback server port (default: 8645)z"Port for the HTTP callback server.WECOM_CALLBACK_ALLOWED_USERSz5Restrict which WeCom users can interact with the app.weixinzWeixin / WeChatWEIXIN_ACCOUNT_IDbluebubbleszBlueBubbles (iMessage)BLUEBUBBLES_SERVER_URL)	zF1. Install BlueBubbles on a Mac that will act as your iMessage server:z   https://bluebubbles.app/uG   2. Complete the BlueBubbles setup wizard — sign in with your Apple IDuD   3. In BlueBubbles Settings → API, note the Server URL and passwordz84. The server URL is typically http://<your-mac-ip>:1234z<5. Hermes connects via the BlueBubbles REST API and receivesz(   incoming messages via a local webhookzJ6. To authorize users, use DM pairing: hermes pairing generate bluebubblesuD      Share the code — the user sends it via iMessage to get approvedz6BlueBubbles server URL (e.g. http://192.168.1.10:1234)u.   The URL shown in BlueBubbles Settings → API.BLUEBUBBLES_PASSWORDzBlueBubbles server passwordu3   The password shown in BlueBubbles Settings → API.BLUEBUBBLES_ALLOWED_USERSz]Pre-authorized phone numbers or iMessage IDs (comma-separated, or leave empty for DM pairing)u_   Optional — pre-authorize specific users. Leave empty to use DM pairing instead (recommended).BLUEBUBBLES_HOME_CHANNELzKHome channel (phone number or iMessage ID for cron/notifications, or empty)zFPhone number or Apple ID to deliver cron results and notifications to.qqbotzQQ Botu   🐧	QQ_APP_ID)z,1. Register a QQ Bot application at q.qq.comz<2. Note your App ID and App Secret from the application pagez;3. Enable the required intents (C2C, Group, Guild messages)z'4. Configure sandbox or publish the botzQQ Bot App IDz!Your QQ Bot App ID from q.qq.com.QQ_CLIENT_SECRETzQQ Bot App Secretz%Your QQ Bot App Secret from q.qq.com.QQ_ALLOWED_USERSzCAllowed user OpenIDs (comma-separated, leave empty for open access)u9   Optional — restrict DM access to specific user OpenIDs.QQBOT_HOME_CHANNELz<Home channel (user/group OpenID for cron delivery, or empty)z4OpenID to deliver cron results and notifications to.yuanbaoYuanbaou   💎YUANBAO_APP_ID)z=1. Download the Yuanbao app from https://yuanbao.tencent.com/u8   2. In the app, go to PAI → My Bot and create a new botz;3. After the bot is created, copy the App ID and App SecretzH4. Enter them below and Hermes will connect automatically over WebSocketz0The App ID from your Yuanbao IM Bot credentials.YUANBAO_APP_SECRETz@The App Secret (used for HMAC signing) from your Yuanbao IM Bot.c            
         	 ddl m}   |              n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wd t
          D             }d |D             }	 ddlm} n# t          $ r |cY S w xY w|                                D ]P}|j	        |v r|
                    |j	        |j        |j        |j        r|j        d         nd|j        |d	           Q|S )
a  Return the full list of platforms for setup menus.

    Combines the built-in ``_PLATFORMS`` with plugin platforms registered via
    ``platform_registry``. Plugins are discovered on first call so bundled
    platforms (like IRC, which auto-load via ``kind: platform``) appear in
    ``hermes setup gateway`` without needing the gateway to be running.
    Built-ins keep their dict shape; plugin entries are adapted to the same
    shape with ``_registry_entry`` holding the source.
    r   )discover_pluginsz7plugin discovery failed during platform enumeration: %sNc                 ,    g | ]}t          |          S r   )r  r  s     r(   r[  z"_all_platforms.<locals>.<listcomp>  s    ---Qa---r*   c                      i | ]}|d          |S )r   r   r  s     r(   
<dictcomp>z"_all_platforms.<locals>.<dictcomp>  s    ---aah---r*   )platform_registryr   )r   ra   rB  rC  install_hint_registry_entry)hermes_cli.pluginsr  r   loggerdebug
_PLATFORMSgateway.platform_registryr  all_entriesr   r   ra   rB  required_envr  )r  r  	platformsby_keyr  rt  s         r(   _all_platformsr    si   S777777 S S SNPQRRRRRRRRS .-*---I--9---F???????    #..00 
 
::[[272DL+A.."!.$
 
 	 	 	 	 s$    
A=A#A* *A98A9r  c                    |                      d          }|d}|j        H	 ddlm}  |d          }t	          |                    |                    }n# t
          $ r d}Y nw xY w|s5	 t	          |                                          }n# t
          $ r d}Y nw xY w|rdnd	S |                      d
d          }|sd	S t          |          }|dk    rK|rG|                                dk    r/t                      dz  dz  dz  }|
                                rdS dS d	S |                      d          dk    rt          d          }|r|rdS |s|rdS d	S |                      d          dk    rYt          d          }	t          d          }
t          d          }t          ||	|
|g          rdS t          ||	|
|g          rdS d	S |                      d          dk    r^t          d          }t          d          }|s|r2|r0t          d          }|r|                                dv rd nd}d| S |s|s|rdS d	S |                      d          d!k    rt          d"          }|r|rdS |s|rdS d	S |rdS d	S )#zReturn a plain-text status string for a platform.

    Returns uncolored text so it can safely be embedded in
    curses menu items (ANSI codes break width calculation).
    r  NFr   )PlatformConfigT)enabled
configurednot configuredrC  r   r`  rL  r^  sessionz
creds.jsonzconfigured + pairedzenabled, not pairedr   rt   SIGNAL_ACCOUNTzpartially configuredrc  rf  rg  rh  rP  rS  MATRIX_PASSWORDMATRIX_ENCRYPTION)rL  rJ  rK  z + E2EEr  WEIXIN_TOKEN)r   is_connectedgateway.configr  r%   r   check_fnr	   r+  r
   r   allr   )r  rt  r  r  	syntheticrC  valsession_fileaccountr.  imapsmtp
homeserverr=  e2eer  tokens                    r(   _platform_statusr    sk    LL*++E
 )#999999*N4888	!%"4"4Y"?"?@@

 # # #"


# 	##!%.."2"233

 # # #"


#)?||/??["--I  
	
"
"C&&& 	)399;;&((*,,z9IETL""$$ -,,((||Eh&& 011 	 7 	 < 	*' 	*))||Eg%%,--.//.//S$%&& 	 <S$%&& 	*))||Eh&&"#677
 !233 	)8 	) 	) !455D"&W4::<<;O+O+OYYUWF(((( 	*( 	*j 	*))||Eh&&n-- 	 5 	 < 	*% 	*))
 |s#   4A A&%A&,!B BBc                     	 ddl m}  n# t          $ r g cY S w xY w |             }|sg S g }|                    d          }|                    d          }|                    d          }|                    d          }|                    di           pi }|                                D ]P\  }}	|	                    d          d	k    r2|	                    d
          pd}
|                    d| d|
            Q|dk    r|r|                    d|            nZ|dk    r4|rdnd}t          |pd          }|                    d| d| d           n |dk    r|r|                    d|            |S )z<Summarize the latest persisted gateway runtime health state.r   r  r  r  active_agentsr?  r  r	  fatalerror_messagezunknown errorr  ru  r  u   ⚠ Last startup issue: drainingr  shutdownu   ⏳ Gateway draining for z (z active agent(s))stoppedu   ⚠ Last shutdown reason: )r   r  r   r   itemsr   r3   )r  r	  linesr  r  r  r?  r  r  pdatamessager$  counts                r(   r  r  #  s   6666666   			  !!E 	EIIo..M))M**KIIo..M		"566		+r**0bI$??,, 7 7%99W((ii00COGLL555G55666((([(===>>>>	*	$	$/?ZM&Q''SSS5SSSTTTT	)	#	#	#?+??@@@Ls   	 c           	      "	   | d         }| d         }| d         }t                       t          t          d| d| dt          j                             |                     d          }|r%t                       |D ]}t          d|            t          |          }|r6t                       t          | d	           t          d
| dd          sdS d}| d         D ]}t                       t          d|d                     t          |d                   }	|	r|d         |k    rt          d|	            |                    d          rt          d           t          d           t          d           t          d|d          d          }
|
r%|

                    dd          }d|d         v rg }|                    d          D ]}|                                }|                    d          r=|                    d          r(|                    d                              d          }|                                                    d          r
|dd         }|r|                    |           d                    |          }t)          |d         |           t          d            |}n}t                       g d!}t+          d"|d#          }|d$k    r t)          d%d&           t-          d'           n4|d#k    rt          d(           t          d)           nt          d*           bt          d|d          |                    d+d                    }
|
r0t)          |d         |
           t          d,|d                     |d         |k    rt-          d-| d.            dS t          d/           |                                 d0}t          |          }|rm|sk|d1k    re|                    d          d$                                         }|r6t          d2| d3d4          r"t)          ||           t          d5|            t                       t          | d| d6           dS )7z2Interactive setup for Telegram, Discord, or Slack.rB  ra   rC       ─── r       Setup ───rD  rk  z is already configured.  Reconfigure ?FNrE  r>  r   z  Current: r@  7  The gateway DENIES all users by default for security.z7  Enter user IDs to create an allowlist, or leave emptyz-  and you'll be asked about open access next.r   r=  r   DISCORDr   z<@>z<@!zuser:r>   7     Saved — only these users can interact with the bot.)/Enable open access (anyone can message the bot)XUse DM pairing (unknown users request access, you approve with 'hermes pairing approve')7Skip for now (bot will deny all users until configured)+  How should unauthorized users be handled?rd   r   GATEWAY_ALLOW_ALL_USERSrL  2     Open access enabled — anyone can use your bot!B     DM pairing mode — users will receive a code to request access.8  Approve with: hermes pairing approve <platform> <code>9     Skipped — configure later with 'hermes gateway setup'r=  z  Saved u     Skipped — z won't work without this.z  Skipped (can configure later)_HOME_CHANNELr;  z  Use your user ID () as the home channel?T  Home channel set to z configured!)r  r   r   CYANr   r   r	   r   r   r   r/  rQ   rO   r   rR   lstripr  r+  r   r   r   r   r   upper)r  rB  ra   rC  instructionsr^   existing_tokenallowed_val_setvarexistingr   cleanedr_   r  access_choices
access_idxhome_varhome_valfirst_ids                      r(   _setup_standard_platformr  F  s   WEWE%I	GGG	%>u>>u>>>
L
LMMM << 455L $  	$ 	$D{D{{####"9--N 7778886e666>> 	FO 9: 9:%F%%&&& V-- 	1Fy00/X//000 77>"" '	PQQQPQQQFGGG/H//%@@@E !\--R00F++E&}}S11 . .!iikk>>$// @CLL4E4E @"%**U"3"3":":3"?"?C99;;11':: *"%abb'C .!LL---!hhuooGs6{G444WXXX") " " "
 ++XZhjkll
??"#<fEEE!"VWWWW1__!"fgggYZZZZZ[[[+CM++cggj%6P6PQQQ 	:3v;...2S[223333[I%%K5KKKLLLFF89999 ++--...HX&&H ?x ?EZ,?,?"((--a06688 	?&]X&]&]&]_cdd 	?8X...=8==>>>	GGGU00U00011111r*   c                  T    ddl m}  ddl} | |                                           dS )z-Delegate to the existing WhatsApp setup flow.r   )cmd_whatsappN)hermes_cli.mainr  argparse	Namespace)r  r  s     r(   _setup_whatsappr    s?    ,,,,,,OOOL##%%&&&&&r*   c                  `    t          d t          D                       } t          |            dS )z0Configure Email via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r   rc  Nr   r  s     r(   r   z_setup_email.<locals>.<genexpr>  s0      GG1U8w3F3F!3F3F3F3FGGr*   Nnextr  r  )email_platforms    r(   _setup_emailr    s2    GGZGGGGGN^,,,,,r*   c                  `    t          d t          D                       } t          |            dS )z7Configure SMS (Twilio) via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r   rj  Nr   r  s     r(   r   z_setup_sms.<locals>.<genexpr>  s0      CCa5U1B1B1B1B1B1BCCr*   Nr  )sms_platforms    r(   
_setup_smsr    s2    CC:CCCCCL\*****r*   c            	         ddl m} m}m}m} t          d t          D                       }|d         }|d         }t                       t          t          d| d| dt          j
                             t          d	          }|r1t                        || d
| d            |d| dd          sdS t                        | dddgd          }|dk    r	 ddlm}	 n6# t          $ r)}
 |d|
 d           t          |           Y d}
~
dS d}
~
ww xY w |	            }| |d           t          |           dS |\  }}t!          d	|           t!          d|           t!          dd           t                        || d| d           dS t          |           t          d	          rt!          dd           dS dS )uH   Configure DingTalk — QR scan (recommended) or manual credential entry.r   )r   r   r   r   c              3   2   K   | ]}|d          dk    |V  dS )r   rp  Nr   r  s     r(   r   z"_setup_dingtalk.<locals>.<genexpr>  s0      MM1ah*6L6LQ6L6L6L6LMMr*   rB  ra   r  r   r  rr  z# is already configured (Client ID: r  r  r  FNz  Choose setup methodzCQR Code Scan (Recommended, auto-obtain Client ID and Client Secret)z*Manual Input (Client ID and Client Secret)r@  )dingtalk_qr_authz!  QR auth module failed to load (z ), falling back to manual input.z3  QR auth incomplete, falling back to manual input.rs  DINGTALK_ALLOW_ALL_USERSrL  z configured via QR scan!)hermes_cli.setupr   r   r   r   r  r  r  r   r   r  r	   hermes_cli.dingtalk_authr  r|  r  r   )r   r   r   r   dingtalk_platformrB  ra   r  methodr  r,  r]   	client_idclient_secrets                 r(   _setup_dingtalkr    s               MM
MMMMMg&Eg&E	GGG	%>u>>u>>>
L
LMMM122H OO8OOOPPP}6e666>> 	F	GGG]Q8	
   F {{	AAAAAAA 	 	 	Mcccccddd$%6777FFFFF	
 "!##>MOPPP$%6777F#) 	=+Y777/???16:::@@@@@AAAAA 	!!2333-.. 	?5v>>>>>	? 	?s   C% %
D/DDc                     t                       t          t          dt          j                             t	          d          } t	          d          }| r1|r/t                       t          d           t          dd          sdS t                       dd	g}t          d
|d          }d}d}|dk    r	 ddlm	} n+# t          $ r}t          d|            d}Y d}~nd}~ww xY w|	  |            }nT# t          $ r! t                       t          d           Y dS t          $ r}t          d|            d}Y d}~nd}~ww xY w|r;|                    dd          }|                    dd          }t          d           |r|st          d           d}d}|r|st                       t          d           t          d           t          d           t          d           t                       t!          dd          }|st          d           dS t!          dd          }|st          d           dS t#          d|           t#          d|           t                       t          d           t          d            t!          d!d          }	|	r6|	                    d"d          }
t#          d#|
           t          d$           nt                       g d%}t          d&|d'          }|dk    r0t#          d(d)           t#          d*d+           t          d,           nj|d'k    r/t#          d(d-           t          d.           t          d/           n5|d0k    r t#          d(d1           t          d2           nt          d3           t                       t          d4           t!          d5d          }|r"t#          d6|           t          d7|            t                       t          d8           dS )9uH   Interactive setup for WeCom — scan QR code or manual credential input.u:     ─── 💬 WeCom (Enterprise WeChat) Setup ───r|  r}  zWeCom is already configured.z  Reconfigure WeCom?FNzDScan QR code to obtain Bot ID and Secret automatically (recommended)z)Enter existing Bot ID and Secret manuallyz%  How would you like to set up WeCom?r   )qr_scan_for_bot_infoz  WeCom QR scan import failed: z  WeCom setup cancelled.z  QR scan failed: bot_idr   secretu5     ✔ QR scan successful! Bot ID and Secret obtained.z9  QR scan did not complete. Continuing with manual input.uQ     1. Go to WeCom Application → Workspace → Smart Robot -> Create smart robotsz  2. Select API Modez?  3. Copy the Bot ID and Secret from the bot's credentials infouA     4. The bot connects via WebSocket — no public endpoint neededz  Bot IDr  u0     Skipped — WeCom won't work without a Bot ID.z  SecretTu0     Skipped — WeCom won't work without a Secret.r  z8  Enter user IDs to create an allowlist, or leave empty.z.  Allowed user IDs (comma-separated, or empty)r   r  r  )r  r  Disable direct messagesr  r  rd   WECOM_DM_POLICYopenr  rL  r  pairingr  r  rI   disabled  Direct messages disabled.r  z2  Chat ID for scheduled results and notifications.1  Home chat ID (optional, for cron/notifications)r  r  u   💬 WeCom configured!)r  r   r   r  r	   r   r   r   gateway.platforms.wecomr  r   r   r7  r   r   r   r   r   r/  )existing_bot_idexisting_secretmethod_choices
method_idxr  r  r  r,  credentialsallowedr  r  r  r  s                 r(   _setup_wecomr!    s   	GGG	%Lfk
Z
Z[[[#N33O#N33O ? 45553U;; 	F 
GGGN3N FXYZZJFFQ	(DDDDDDD 	( 	( 	(?#??@@@#'      	(  +#2244$   8999 # # #8388999"#  W$266$266UVVV 	V 	RSSSFF   fggg)***TUUUVWWW
U333 	LMMMF
T222 	LMMMF >6***>6*** 
GGGHIIIIJJJEPUVVVG T//#r**,g666OPPPP
 
 
 ##PR`bcdd
??,f5554f===NOOOO1__,i888^___QRRRR1__,j99978888RSSS 
GGGCDDDEPUVVVD 7+T2225t55666	GGG*+++++s6   5B< <
C$CC$*
C5 5'E	E(EEc                  `    t          d t          D                       } t          |            dS )z2Configure Yuanbao via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r   r  Nr   r  s     r(   r   z!_setup_yuanbao.<locals>.<genexpr>m  s0      KK!QuX5J5JA5J5J5J5JKKr*   Nr  )yuanbao_platforms    r(   _setup_yuanbaor%  k  s4    KKzKKKKK-.....r*   c                     t                      rDt          d                                          p!t          d                                          S t                      r t	                                                      S dS )z6Check if the gateway is installed as a system service.Fr   T)rK   r   r   rW   rK  r   r*   r(   _is_service_installedr'  q  st     "" 1$E22299;;j?T\`?a?a?a?h?h?j?jj	 1%''..0005r*   c                  \   t                      rt          d                                          } t          d                                          }| r_	 t          dt	                      gdddd          }|j                                        dk    rdS n# t          t          j	        f$ r Y nw xY w|r_	 t          dt	                      gdddd          }|j                                        dk    rdS n# t          t          j	        f$ r Y nw xY wdS t                      rit                                                      rI	 t          j        dd	t                      gddd
          }|j        dk    S # t          j	        $ r Y dS w xY wt          t!                                dk    S )z2Check if the gateway service is currently running.Fr   Tr   r   r   r   rF   rG   r?   r   )rK   r   r   r   r   rN   rO   r   rL   rU   rW   rK  rM   rX   rY   rZ   r   )user_unit_existssystem_unit_existsr]   s      r(   _is_service_runningr+  z  s	    "" #0>>>EEGG2$???FFHH 		' "2"4"45 D"   =&&((H444 5 *";<     		' "2"4"454   =&&((H444 5 *";<    u	 .007799 	^f&7&9&9:#$  F $))( 	 	 	55	  ""##a''s6   ?B B21B28?C: :DD1E9 9FFc                     t                       t          t          dt          j                             t                       t	          d           t	          d           t	          d           t	          d           t          d          } t          d          }| r1|r/t                       t          d           t          d	d
          sdS 	 ddlm	}m
} n9# t          $ r,}t          d|            t	          d           Y d}~dS d}~ww xY w |            s t          d           t	          d           dS t                       t          dd          st	          d           dS ddl}	  |j         |t          t!                                                    }nS# t"          $ r! t                       t%          d           Y dS t          $ r}t          d|            Y d}~dS d}~ww xY w|st%          d           dS |                    dd          }|                    dd          }|                    dd          }	|                    dd          }
t)          d|           t)          d|           |	rt)          d|	           t)          dt          d          pd           t                       g d }t+          d!|d          }|dk    rOt)          d"d#           t)          d$d%           t)          d&d           t          d'           t	          d(           n|d)k    r@t)          d"d*           t)          d$d+           t)          d&d           t%          d,           n|d-k    rj|
pd}t-          d.|d
/                              d0d          }t)          d"d1           t)          d$d%           t)          d&|           t          d2           n?t)          d"d3           t)          d$d%           t)          d&d           t%          d4           t                       t	          d5           t	          d6           t	          d7           t	          d8           t	          d9           t	          d:           g d;}t+          d<|d          }|dk    r0t)          d=d3           t)          d>d           t	          d?           n|d)k    r0t)          d=d*           t)          d>d           t%          d@           nUt-          dAdd
/                              d0d          }t)          d=d1           t)          d>|           t          dB           |
rDt                       t          dC|
 dDd          r"t)          dE|
           t          dF|
            t                       t          dG           t	          dH|            |
rt	          dI|
            dS dS )Jz8Interactive setup for Weixin / WeChat personal accounts.u0     ─── 💬 Weixin / WeChat Setup ───z>  1. Hermes will open Tencent iLink QR login in this terminal.z0  2. Use WeChat to scan and confirm the QR code.zG  3. Hermes will store the returned account_id/token in ~/.hermes/.env.zL  4. This adapter supports native text, image, video, and document delivery.r  r  zWeixin is already configured.z  Reconfigure Weixin?FNr   )check_weixin_requirementsqr_loginz   Weixin adapter import failed: z1  Install gateway dependencies first, then retry.z>  Missing dependencies: Weixin needs aiohttp and cryptography.z2  Install them, then rerun `hermes gateway setup`.z  Start QR login now?Tz  Cancelled.z  Weixin setup cancelled.z  QR login failed: z  QR login did not complete.
account_idr   r  base_urluser_idWEIXIN_BASE_URLWEIXIN_CDN_BASE_URLz%https://novac2c.cdn.weixin.qq.com/c2c)%Use DM pairing approval (recommended)Allow all direct messagesOnly allow listed user IDsr  +  How should direct messages be authorized?WEIXIN_DM_POLICYr  WEIXIN_ALLOW_ALL_USERSrN  WEIXIN_ALLOWED_USERS  DM pairing enabled.zY  Unknown DM users can request access and you approve them with `hermes pairing approve`.rd   r  rL  z$  Open DM access enabled for Weixin.rI   z+  Allowed Weixin user IDs (comma-separated)r  r   	allowlistz  Weixin allowlist saved.r  r  zH  Note: QR login connects an iLink bot identity (e.g. ...@im.bot), not azM  scriptable personal WeChat account. Ordinary WeChat groups typically cannotzN  invite an @im.bot identity, and iLink does not deliver ordinary-group eventszI  to most bot accounts. The settings below only apply when iLink actuallyuO     delivers group events for your account type — otherwise DM remains the onlyz,  working channel regardless of this choice.)z!Disable group chats (recommended)zAllow all group chatsz Only allow listed group chat IDs$  How should group chats be handled?WEIXIN_GROUP_POLICYWEIXIN_GROUP_ALLOWED_USERS  Group chats disabled.zM  All group chats enabled (only takes effect if iLink delivers group events).z?  Allowed group chat IDs (comma-separated, not member user IDs)zK  Group allowlist saved (only takes effect if iLink delivers group events).z  Use your Weixin user ID (r  WEIXIN_HOME_CHANNELr  zWeixin configured!z  Account ID: z  User ID: )r  r   r   r  r   r	   r   r   gateway.platforms.weixinr-  r.  r   r   r6  rM   r0   r
   r7  r   r   r   r   r   r/  )existing_accountr  r-  r.  r,  r6  r  r/  r  r0  r1  r  r  default_allowr<  group_choices	group_idxallow_groupss                     r(   _setup_weixinrH    s   	GGG	%BFK
P
PQQQ	GGGOPPPABBBXYYY]^^^$%899">22N N 56664e<< 	FPPPPPPPPP   <s<<===FGGG
 %$&& TUUUGHHH	GGG0$77 >"""NNN!gk((3/@/@+A+A"B"BCC   1222   /#//000  4555r22JOOGR((Ez2..Hooi,,G&
333>5))) 4((333(-8M*N*N*yRyzzz	GGG  N Ln^_``JQ)9555/999-r222-...noooo	q)6222/888-r222<====	q2H-bghhhppqtvxyy	);777/999-y99912222):666/999-r2223444	GGGYZZZ^____```Z[[[`aaa=>>>  M
 DmUVWWIA~~,j9993R888,----	a,f5553R888effff_acnsttt||  ~A  CE  F  F,k:::3\BBBcddd >VwVVVX\]] 	>0':::<7<<===	GGG&''',
,,--- ,***+++++, ,s6   C 
D%!DD22F% %'G5	G5G00G5c                  *   t                       t          t          dt          j                             t	          d          } t	          d          }| r1|r/t                       t          d           t          dd          sdS t                       dd	g}t          d
|d          }d}d}|dk    r	 ddlm	} n+# t          $ r}t          d|            d}Y d}~nd}~ww xY w|^	  |            }nR# t          $ r! t                       t          d           Y dS t          $ r}t          d|            Y d}~nd}~ww xY w|rd}|st          d           |s$t                       t          d           t          d           t                       t          dd          }|st          d           dS t          dd          }	|	st          d           dS ddg}
t          d|
d          }|dk    rdnd}d}	 ddlm}  |||	|          }|r*|                    d           }t          d!|pd"            nt          d#           n)# t          $ r}t          d$|            Y d}~nd}~ww xY w||	|d|d%}|d&         }|d'         }	|                    d(d          }|                    d)          }|                    d           }t%          d|           t%          d|	           t%          d*|           |rd+}n`t                       d,d-g}t          d.|d          }|dk    rd/nd+}|d/k    r-t          d0           t          d1           t          d2           t%          d3|           |r t                       t          d4|            t                       g d5}t          d6|d          }|dk    r?t%          d7d8           t%          d9d:           t          d;           t          d<           n|dk    r0t%          d7d=           t%          d9d:           t          d>           nYt%          d7d8           |pd:}t          d?|d                              d@d:          }t%          d9|           t          dA           t                       dBdCg}t          dD|d          }|dk    r t%          dEdF           t          dG           nt%          dEdH           t          dI           t                       t          dJd          }|r"t%          dK|           t          dL|            t                       t          dM           t          dN|            t          dO|            |rt          dP|            dS dS )QuM   Interactive setup for Feishu / Lark — scan-to-create or manual credentials.u.     ─── 🪽 Feishu / Lark Setup ───ru  rv  z$Feishu / Lark is already configured.z  Reconfigure Feishu / Lark?FNz<Scan QR code to create a new bot automatically (recommended)-Enter existing App ID and App Secret manuallyz-  How would you like to set up Feishu / Lark?r   qr_registerz'  Feishu / Lark onboard import failed: z   Feishu / Lark setup cancelled.z  QR registration failed: T:  QR setup did not complete. Continuing with manual input.zI  Go to https://open.feishu.cn/ (or https://open.larksuite.com/ for Lark)zE  Create an app, enable the Bot capability, and copy the credentials.  App IDr  u9     Skipped — Feishu / Lark won't work without an App ID.  App Secretu=     Skipped — Feishu / Lark won't work without an App Secret.zfeishu (China)zlark (International)z  Domainrd   larkrt  )	probe_botbot_nameu      Credentials verified — bot: unnamedz<  Could not verify bot connection. Credentials saved anyway.z#  Credential verification skipped: )app_id
app_secretdomainopen_idrR  rT  rU  rV  rW  rw  	websocketu0   WebSocket (recommended — no public URL needed)z,Webhook (requires a reachable HTTP endpoint)z  Connection modewebhookz1  Webhook defaults: 127.0.0.1:8765/feishu/webhookzO  Override with FEISHU_WEBHOOK_HOST / FEISHU_WEBHOOK_PORT / FEISHU_WEBHOOK_PATHzR  For signature verification, set FEISHU_ENCRYPT_KEY and FEISHU_VERIFICATION_TOKENrx  z  Bot created: )r4  r5  r6  r7  FEISHU_ALLOW_ALL_USERSrN  ry  r   r;  J  Unknown users can request access; approve with `hermes pairing approve`.rL  z+  Open DM access enabled for Feishu / Lark.z$  Allowed user IDs (comma-separated)r     Allowlist saved.z4Respond only when @mentioned in groups (recommended)zDisable group chatsr=  FEISHU_GROUP_POLICYr  z/  Group chats enabled (bot must be @mentioned).r  r@  r  rz  r  u   🪽 Feishu / Lark configured!
  App ID: z
  Domain: z  Bot: )r  r   r   r  r	   r   r   r   gateway.platforms.feishurL  r   r   r7  r   r   r   rQ  r   r   r/  )existing_app_idr  r  r  r  used_qrrL  r,  rT  rU  domain_choices
domain_idxrV  rR  rQ  bot_inforW  connection_modemode_choicesmode_idxr  r  rD  r<  rE  rF  home_channels                              r(   _setup_feishuri  )  s   	GGG	%@&+
N
NOOO#O44O#$788O ? <===;UCC 	F 
GGGF7N NP^`abbJKGQ	<<<<<<< 	 	 	G#GGHHHKKKKKK	 "B)kmm$   @AAA B B B@3@@AAAAAAAAB 	G 	USTTT  %
^___Z[[[
U333 	UVVVFNT:::
 	YZZZF*,BC":~qAA
%?? 		G:::::: yV<<H ^#<<
33XAVYXXYYYY\]]] 	G 	G 	GEEEFFFFFFFF	G $ 
 
 "F\*J__Xx00Fooi((Gz**H?F+++&
333?F+++  m%>:
 !!4lAFF'/1}}))+i''JKKKhiiiklll+_=== 4222333 
GGG  N
 Ln^_``JQ/999-r222-..._````	q/888-r222CDDDD/9992A=[`aaaiijmoqrr	-y999*+++ 
GGG>M DmUVWWIA~~,f555DEEEE,j999,--- 
GGGMX]^^^L ?,l;;;=|==>>>	GGG2333$F$$%%%$F$$%%% )'X''((((() )sO   5B< <
C$CC$*
C5 5'E	E(D??EAI 
I8I33I8c                  N   t                       t          t          dt          j                             t	          d          } t	          d          }| r1|r/t                       t          d           t          dd          sdS t                       dd	g}t          d
|d          }d}|dk    rQ	 ddlm	}  |            }n.# t          $ r! t                       t          d           Y dS w xY w|st          d           |st                       t          d           t          d           t                       t          dd          }|st          d           dS t          dd          }|st          d           dS |                                |                                dd}t          d|d                    t          d|d                    |                    dd          }t                       g d}	t          d|	d          }
|
dk    rt          dd           |rVt                       t          d | d!d          r#t          d"|           t          d#|            n!t          d"d           nt          d"d           t          d$           t          d%           n|
d&k    r0t          dd'           t          d"d           t          d(           nY|pd}t          d)|d                              d*d          }t          dd           t          d"|           t          d+           |rEt                       t          d,| d-d          r"t          d.|           t          d/|            ngt                       t          d0d          }|rFt          d.|                                           t          d/|                                            t                       t          d1           t          d2|d                     dS )3uI   Interactive setup for QQ Bot — scan-to-configure or manual credentials.u'     ─── 🐧 QQ Bot Setup ───r  r  zQQ Bot is already configured.z  Reconfigure QQ Bot?FNz3Scan QR code to add bot automatically (recommended)rJ  z&  How would you like to set up QQ Bot?r   rK  z  QQ Bot setup cancelled.rM  z:  Go to https://q.qq.com to register a QQ Bot application.z<  Note your App ID and App Secret from the application page.rN  r  u2     Skipped — QQ Bot won't work without an App ID.rO  Tu6     Skipped — QQ Bot won't work without an App Secret.r   )rT  r  user_openidrT  r  rk  )r4  r5  zOnly allow listed user OpenIDsr7  QQ_ALLOW_ALL_USERSrN  z  Add yourself (z) to the allow list?r  z  Allow list set to r;  r[  rd   rL  z$  Open DM access enabled for QQ Bot.z(  Allowed user OpenIDs (comma-separated)r   r\  z  Use your QQ user ID (r  r  r  z8  Home channel OpenID (for cron/notifications, or empty)u   🐧 QQ Bot configured!r^  )r  r   r   r  r	   r   r   r   gateway.platforms.qqbotrL  r7  r   r   r   rO   r   r   r/  )r`  r  r  r  r  rL  rT  rU  rk  r  r  rD  r<  rh  s                 r(   _setup_qqbotrn    s   	GGG	%96;
G
GHHH#K00O#$677O ? 56664e<< 	F 
GGG=7N GYZ[[JKQ	;;;;;;%+--KK  	 	 	GGG5666FF	  	USTTT  iOPPPQRRR
U333 	NOOOFNT:::
 	RSSSF!'*BRBRBTBTeghh ;H 5666%{?'CDDD//-44K 
GGG  N
 Ln^_``JQ+W555 	3GGGQQQQSWXX 71;???B[BBCCCC126666-r222-..._````	q+V444)2...<====#)rE}_deeemmnqsuvv	+W555)9555*+++  
KV;VVVX\]] 	B/===@;@@AAAXchiii 	K/1C1C1E1EFFFI<3E3E3G3GIIJJJ	GGG+,,,3K13344444s   3C 'C/.C/c                  2
   ddl } t                       t          t          dt          j                             t          d          }t          d          }|r1|r/t                       t          d           t          dd          sdS t                        | j        d	          rt          d
           nt          d           t          d           t          d           t          d           t          d           t          d           t                       t          d           t          d           t          d           t                       t                       t          d           |pd}	 t          d| d                                          p|}n'# t          t          f$ r t          d           Y dS w xY wt          d           	 ddl}|                    |                    d           dd          }|j        dk    rt          d           n*t          d |j         d!           t          d"d          sdS nB# t&          $ r5}t          d#| d$|            t          d%d&          sY d}~dS Y d}~nd}~ww xY wt)          d|           t                       t          d'           t          d(           |pd)}	 t          d*|rd+| d,nd) d$                                          }	|	s|}	n'# t          t          f$ r t          d           Y dS w xY w|	st+          d-           dS t)          d|	           t                       t          d.           t          d/           t          d0          pd)}
|
p|	}	 t          d1| d                                          p|}n'# t          t          f$ r t          d           Y dS w xY wt)          d0|           t                       t          d2d          rt                       t          d3           t          d4          pd)}	 t          d5|pd6 d                                          p|pd6}n'# t          t          f$ r t          d           Y dS w xY wt)          d4|           t                       t          d7           t          d8|            t          d9|	            t          d:           t          d;t          d4          rd<nd=            dS )>z'Interactive setup for Signal messenger.r   Nu'     ─── 📡 Signal Setup ───rb  r  zSignal is already configured.z  Reconfigure Signal?Fz
signal-clizsignal-cli found on PATH.zsignal-cli not found on PATH.z7  Signal requires signal-cli running as an HTTP daemon.z  Install options:zF    Linux:  download from https://github.com/AsamK/signal-cli/releasesz#    macOS:  brew install signal-cliz)    Docker: bbernhard/signal-cli-rest-apiz;  After installing, link your account and start the daemon:z$    signal-cli link -n "HermesAgent"zA    signal-cli --account +YOURNUMBER daemon --http 127.0.0.1:8080z8  Enter the URL where signal-cli HTTP daemon is running.zhttp://127.0.0.1:8080z  HTTP URL [z]: z
  Setup cancelled.z  Testing connection...r  z/api/v1/checkr  r     z!  signal-cli daemon is reachable!z#  signal-cli responded with status .z  Continue anyway?z   Could not reach signal-cli at ru  z8  Save this URL anyway? (you can start signal-cli later)Tz9  Enter your Signal account phone number in E.164 format.z  Example: +15551234567r   z  Account numberz []z  Account number is required.r  zB  Enter phone numbers or UUIDs of allowed users (comma-separated).SIGNAL_ALLOWED_USERSz  Allowed users [z<  Enable group messaging? (disabled by default for security)z0  Enter group IDs to allow, or * for all groups.SIGNAL_GROUP_ALLOWED_USERSz  Group IDs [*zSignal configured!z  URL: z  Account: z0  DM auth: via SIGNAL_ALLOWED_USERS + DM pairingz
  Groups: r  r  )r  r  r   r   r  r	   r   r   r  r   r   inputrO   EOFErrorr7  httpxr   r  status_coder   r   r   )r  existing_urlrC  default_urlurlrx  respr  default_accountr  existing_alloweddefault_allowedr   existing_groupsgroupss                  r(   _setup_signalr  @  s   MMM	GGG	%96;
G
GHHH !233L$%566 ( 56664e<< 	F 
GGGv|L!! 122225666LMMM'((([\\\8999>???PQQQ;<<<VWWW 
GGGIJJJ9"9K3;33344::<<K'(   $%%%
 ()))yyCJJsOO:::DyIIs""=>>>>S@PSSSTTT !5u==    CCCCCDDDWY]^^ 	FFFFF	 	 	 	 	
 $c*** 
GGGJKKK()))&,"Oao+]+B+B+B+B+B[]aaabbhhjj 	&%G'(   $%%%  3444#W--- 
GGGHIIISTTT$%;<<B&1'O@O@@@AAGGII\_'(   $%%% )7333 
GGGSUZ[[ 	=EFFF'(DEEK	F?+AcFFFGGMMOOiSbifiFF+, 	 	 	()))FF	 	3V<<<	GGG&'''&W&&'''ABBBf7S)T)TdIIZdffgggggsa   >'F& & G
	G
A4I 
J%JJ1L	 	 L-,L-'N= = O! O!+Q- - RRr   c                     ddl m} |j        |j        |j        |j        |j        |j        |j        t          t          t          t          t          t          t          d                    |           S )zResolve the interactive setup function for a built-in platform key.

    Late-bound to avoid a circular import with ``hermes_cli.setup`` (which
    imports from this module for the remaining bespoke flows).
    r   )setup)r:  rF  rK  rP  rW  r  webhooksrt   r^  r  rp  rt  r{  r  )
hermes_clir  _setup_telegram_setup_discord_setup_slack_setup_matrix_setup_mattermost_setup_bluebubbles_setup_webhooksr  r  rH  r  ri  r!  rn  r   )r   _ss     r(   _builtin_setup_fnr    sp     '&&&&&&$"*,&##  
c#hhr*   c           	         |                      d          }||j        |                                 dS t          | d                   }| |             dS |                      d          rt          |            dS |                      d| d                   }|                      dd          }t	                       t	          t          d| d	| d
t          j                             |r|j        ng }|r&t          dd
                    |                      nt          d| d| d                     |                      d          rt          d| d                     dS dS )a?  Run the interactive setup flow for a single platform.

    Dispatch order:
      1. Plugin-provided ``setup_fn`` on the registry entry.
      2. Built-in setup function matched by platform key.
      3. ``_setup_standard_platform`` when the entry has a ``vars`` schema.
      4. Env-var hint fallback for plugins that offer no setup helper.

    Bundled platform plugins (e.g. IRC) auto-load, so no plugin enable step
    is needed here. User-installed platform plugins under ~/.hermes/plugins/
    must already be in ``plugins.enabled`` before they appear in this menu.
    r  Nr   rE  ra   rB  u   🔌r  r   r  z(  Set these env vars in ~/.hermes/.env: r]  z  Configure z( in config.yaml under gateway.platforms.r  rk  )r   setup_fnr  r  r  r   r   r  r  r   r   )r  rt  fnra   rB  requireds         r(   _configure_platformr    s    LL*++EU^7	8E?	+	+B	~
||F  *** LL(5/22ELL&))E	GGG	%>u>>u>>>
L
LMMM%*2u!!H dSdii>Q>QSSTTTTb%bbQYZ_Q`bbccc||N## 42022333334 4r*   c                  p   t                      rt          d           dS t                       t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t                       t                      } t                      }t                      r*t                      rt                       t                       t                      r*t                      rt                       t                       | r|rt          d	           na| r@t          d
           t                      rt                      rt!          d           n"t#          dd          r	 t                      rt%                       nt'                      rt)                       n# t*          $ rO}t-          d           t/          |                                          D ]}t          d|            Y d}~nd}~wt2          $ r+}t-          d|            t!          d           Y d}~nPd}~wt4          j        $ r}t-          d|            Y d}~n'd}~ww xY wnt9          d           t9          d           	 t                       t;          d           t=                      }d |D             }|                    d           tA          d|tC          |          dz
            }|tC          |          k    rntE          ||                    dt.          dtF          fdtI          fdt=                      D                       }|rt                       t          t          dt          j%                             t                      } t                      }|rUt                      rt                      rt!          d           n*t#          dd          r	 t                      rtM                       n:t'                      rtO                       ntQ                       t9          d           n# t*          $ rP}t-          d            t/          |                                          D ]}t          d|            Y d}~njd}~wt2          $ r,}t-          d!|            t!          d           Y d}~n6d}~wt4          j        $ r}t-          d!|            Y d}~nd}~ww xY wn| r6t                      rt                      rt!          d           nt#          d"d          r	 t                      rt%                       nt'                      rt)                       n# t*          $ rP}t-          d#           t/          |                                          D ]}t          d|            Y d}~n1d}~wt2          $ r,}t-          d$|            t!          d           Y d}~nd}~wt4          j        $ r}t-          d$|            Y d}~nd}~ww xY wnt                       t                      st'                      rt                      rd%nd&}tS                      rd'nd(}	t#          d)| d*|	 d+d          rQ	 d}
d,}t                      rtU          d,-          \  }
}ntW          d,-           d}t                       |rt#          d.d          r	 t                      rt%          |
d/k    0           nt)                       n# t*          $ rO}t-          d#           t/          |                                          D ]}t          d|            Y d}~n1d}~wt4          j        $ r}t-          d$|            Y d}~nd}~ww xY wnO# t4          j        $ r,}t-          d1|            t9          d2           Y d}~nd}~ww xY wt9          d3           t                      rt9          d4           t9          d5           ntS                      r=t9          d6           t9          d7           t9          d8           t9          d9           ntY                      r@d:d;l-m.} t9          d<           t9          d7           t9          d= |             d>           n<t9          d?           t9          d7           nt                       t9          d@           t                       dS )Az<Interactive setup for messaging platforms + gateway service.zrun gateway setupNr0  u@   │             ⚕ Gateway Setup                            │r1  u?   │  Configure messaging platforms and the gateway service. │u>   │  Press Ctrl+C at any time to exit.                     │r2  z)Gateway service is installed and running.z-Gateway service is installed but not running.rF  z  Start it now?Tu1     Failed to start — user systemd not reachable:rk  z  Failed to start: z%Gateway service is not installed yet.z<You'll be offered to install it after configuring platforms.zMessaging Platformsc           	      V    g | ]&}|d           d|d          dt          |           d'S )rB  r   ra   r  r  r  r  s     r(   r[  z!gateway_setup.<locals>.<listcomp>1  sR     
 
 
 zAAAgJAA+;A+>+>AAA
 
 
r*   DonezSelect a platform to configure:rd   r  r"   c                     |                                  }|dk    p)|                    d          p|                    d           S )Nr  	partiallyzplugin disabled)r+  r   )r  ss     r(   _is_progressz#gateway_setup.<locals>._is_progressB  sK    LLNN!! /||K((/||-..
 	
r*   c              3   H   K   | ]} t          |                    V  d S r$   r  )r   r   r  s     r(   r   z gateway_setup.<locals>.<genexpr>J  sG        ./%a(())     r*   u   ──────────────────────────────────────────────────────────r  z)  Restart the gateway to pick up changes?zStart manually: hermes gatewayu0     Restart failed — user systemd not reachable:z  Restart failed: z  Start the gateway service?u.     Start failed — user systemd not reachable:z  Start failed: r  rO  z. (note: services may not survive WSL restarts)r   z  Install the gateway as a z	 service?z% (runs in background, starts on boot)Frs  z  Start the service now?r   r   z  Install failed: z.  You can try manually: hermes gateway installz/  You can install later: hermes gateway installzA  Or as a boot-time service: sudo hermes gateway install --systemz+  Or run in foreground:  hermes gateway runz*  WSL detected but systemd is not running.z'  Run in foreground: hermes gateway runz<  For persistence:   tmux new -s hermes 'hermes gateway run'zM  To enable systemd: add systemd=true to /etc/wsl.conf, then 'wsl --shutdown'r   r  z/  Termux does not use systemd/launchd services.zR  Or start it manually in the background (best effort): nohup hermes gateway run >z/logs/gateway.log 2>&1 &z1  Service install not supported on this platform.z?No platforms configured. Run 'hermes gateway setup' when ready.)/r   r   r  r   r   MAGENTAr'  r+  rK   r  r#  r  r  r   r   r  r  r   r  rW   r  r  r   r0   rP   r  rL   r  r   r   r  r   r   rZ   r  r%   r   DIMr  r  r~  r  rG  r  rP  rQ  r  )r   r   r  r^   r  
menu_itemsrA  any_configuredplatform_namewsl_noteinstalled_scopedid_installr  r  s                @r(   gateway_setupr    s   || )***	GGG	%  D  FL  FT  U  U  V  V  V	%RTZTb
c
cddd	%  D  FL  FT  U  U  V  V  V	%QSYSa
b
bccc	%PRXR`
a
abbb	%  D  FL  FT  U  U  V  V  V 
GGG-//)++O "" 'D'F'F ,... "" '>'@'@ !### S_ SABBBB	 SEFFF$&& 	7+O+Q+Q 	7+G4444,d33 	77,.. $!OOOOZZ $!OOO. ' ' 'OPPPFF--// ' 'D+t++&&&&' ' ' ' '/ 9 9 9 5!55666/888888880 7 7 75!55666666667!	7& 	:;;;QRRR/*+++"$$	
 

 
 

 	&!!!@*cR\oo`aNabbS^^##If-...!/.
S 
T 
 
 
 
     3A3C3C    N  `VeJ
++,,,133-// W	J(** :/S/U/U :/	::::JDQQ ::022 E'))))! E')))),..."#CDDD2 + + + RSSS #A 1 1 3 3 + +k4kk****+ + + + +3 ? ? ? 8Q 8 89993I>>>>>>>>!4 : : : 8Q 8 899999999:!:$  B	J(** 8/S/U/U 8/8888=tDD 88022 (%! (%2 + + + PQQQ #A 1 1 3 3 + +k4kk****+ + + + +3 = = = 61 6 67773G<<<<<<<<!4 8 8 8 61 6 67777777788  GGG(** .Jhjj .J-F-H-H W		iOUxx_KK]_   "G}  "G  "GW_  "G  "G  "G  IM  N  N NU*.&+466 /;[bg;h;h;h8O[[+%8888*.K& D=9SUY+Z+Z D
D#<#>#> !4$1H9T$U$U$U$U$U$1OOO#> 7 7 7 +,\ ] ] ],/FF,=,=,?,? !7 !7D$)+t++$6$6$6$6!7 !7 !7 !7 !7#-#@ D D D +,Bq,B,B C C C C C C C CD%8 U U U#$<$<$<==="#STTTTTTTTU PQQQ022 h"#fgggLMMMM JGHHHDEEEYZZZjkkkk;; JLLLLLLPQQQHIII   Utxtxtztz   U   U   U  V  V  V  VRSSSHIIIITUUU	GGGGGs   9I 
LAJ&&L3!KL+LLAS) )
V!3AT>>V!!U22V!VV!'9X" "
[,AY77[!Z++[=[[Aa  1_ a 
aA`&!a &a8a
a aa b(!bbc                    	 t          |           S # t          $ rd}t          d           t          |                                          D ]}t          d|            t          j        d           Y d}~dS d}~wt          $ r;}t          t          |                     t          j        d           Y d}~dS d}~ww xY w)zHandle gateway subcommands.zUser systemd not reachable:rk  rd   N)	_gateway_command_innerr  r   r0   rP   r  r   r  r  )r   r  r^   s      r(   gateway_commandr    s    %d+++&    	1222FF%%'' 	 	D+t++'    	c!ffs!    
CAA::C0B==Cc           	      t!   t          | dd           }||dk    rGt          | dd          }t          | dd          }t          | dd          }t          |||           d S |d	k    rt                       d S |d
k    rgt                      rt	          d           d S t          | dd          }t          | dd          }t          | dd           }t                      r2t          d           t          d           t          j        d           t                      r]t                      r;t          d           t          d           t          d           t                       t          |||           d S t                      rt          |           d S t                      r~t          d           t          d           t          d           t                       t          d           t          d           t          d           t          j        d           d S t!                      r}t          d           t          d           t                       t          d           t          d           t                       t          d            t          j        d           d S t          d!           t          d"           t          j        d           d S |d#k    rAt                      rt	          d$           d S t          | dd          }t                      r2t          d%           t          d&           t          j        d           t                      rt#          |'           d S t                      rt%                       d S t!                      r`t          d(           t          d)           t                       t          d*           t          d+           t          j        d           d S t          d,           t          j        d           d S |d-k    r"t          | dd          }t          | d.d          }|r6t'          d/0          }	|	r$t          d1|	 d2           t)          d3d45           t                      r2t          d6           t          d           t          j        d           t                      rt+          |'           d S t                      rt-                       d S t                      rt          d7           t          d8           t                       t          d           t          d           t          d           t                       t          d9           t          j        d           d S t!                      r}t          d:           t          d;           t                       t          d<           t          d=           t                       t          d>           t          j        d           d S t          d,           t          j        d           d S |d?k    r]t          | d.d          }
t          | dd          }|
rd}t                      rmt/          d'                                          s"t/          d/'                                          r)	 t3          |'           d/}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d/}n# t4          j        $ r Y nw xY wt'          d/0          }	|	|rdndz   }|rt          d@| dA           d S t          dB           d S d}t                      rmt/          d'                                          s"t/          d/'                                          r)	 t3          |'           d/}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d/}n# t4          j        $ r Y nw xY w|s0t=                      rt          dC           d S t          dD           d S t          d@t?                       dE           d S |dFk    r)d}t          | dd          }t          | d.d          }d}|rd}t                      rmt/          d'                                          s"t/          d/'                                          r)	 t3          |'           d/}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d/}n# t4          j        $ r Y nw xY wt'          d/0          }	|	|rdndz   }|rt          d@| dA           t)          d3d45           t          dG           t                      rUt/          d'                                          s"t/          d/'                                          rt+          |'           nMt                      r/t9                                                      rt-                       nt          dH           d S t                      rot/          d'                                          s"t/          d/'                                          r+d/}	 tA          |'           d/}nl# t4          j        $ r Y n[w xY wt                      rIt9                                                      r)d/}	 tC                       d/}n# t4          j        $ r Y nw xY w|sUt                      rtE                      \  }}|d/urdd l#}|$                                }t                       t          dI           t          dJ           t                       t          dK|            t                       t          dL           t          dM           d S |rOt                       t          dN           t          dO           t          dP           t          j        d           t=                      rt          dC           t)          d3d45           t          dG           t          dH           d S d S |dQk    rHt          | dRd          }t          | dSd          }t          | dd          }tK          |'          }t                      rgt/          d'                                          s"t/          d/'                                          r#tM          |||T           tO          |           nt                      r@t9                                                      r tQ          |           tO          |           n2tS          |j*                  }|r3t          dUdV+                    tY          tZ          |                     dW           t          dX           t]                      }|r4t                       t          dY           |D ]}t          dZ|            t                       t                      r t          d[           t          d\           nSt                      r/t          d]           t          d^           t          d_           nt          d`           t          da           t          db           nt          dc           t]                      }|r4t                       t          dY           |D ]}t          dZ|            t                       t          dd           t          de           t                      rt          df           nKt                      rt          d           t          d           nt          dg           t          dh           t_                       d S |dik    rta                       d S |djk    rct          | dkd          }t          | dld          }t                      st                      st          dm           d S tc          | |n           d S d S )oNr  rM   r,  r   r-  Fr/  )r-  r/  r  r  z*install gateway service (managed by NixOS)rq  r   r'  z8Gateway service installation is not supported on Termux.zRun manually: hermes gatewayrd   u?   WSL detected — systemd services may not survive WSL restarts.z<  Consider running in foreground instead: hermes gateway runzM  Or use tmux/screen for persistence: tmux new -s hermes 'hermes gateway run'rE  z(WSL detected but systemd is not running.zIEither enable systemd (add systemd=true to /etc/wsl.conf and restart WSL)z&or run the gateway in foreground mode:zE  hermes gateway run                              # direct foregroundzG  tmux new -s hermes 'hermes gateway run'         # persistent via tmuxzL  nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &  # backgroundz=Service installation is not needed inside a Docker container.uV   The container runtime is your service manager — use Docker restart policies instead:zJ  docker run --restart unless-stopped ...   # auto-restart on crash/rebootz<  docker restart <container>                # manual restartz&To run the gateway: hermes gateway runz4Service installation not supported on this platform.z Run manually: hermes gateway runr  z,uninstall gateway service (managed by NixOS)zcGateway service uninstall is not supported on Termux because there is no managed service to remove.z*Stop manual runs with: hermes gateway stopr   z>Service uninstall is not applicable inside a Docker container.z2To stop the gateway, stop or remove the container:z  docker stop <container>z  docker rm <container>zNot supported on this platform.rF  r  Tr   u   ✓ Killed z. stale gateway process(es) across all profilesr  r  r  z\Gateway service start is not supported on Termux because there is no system service manager.z*WSL detected but systemd is not available.z+Run the gateway in foreground mode instead:z^To enable systemd: add systemd=true to /etc/wsl.conf and run 'wsl --shutdown' from PowerShell.z:Service start is not applicable inside a Docker container.z1The gateway runs as the container's main process.z:  docker start <container>     # start a stopped containerz<  docker restart <container>   # restart a running containerz/Or run the gateway directly: hermes gateway runr  u   ✓ Stopped z( gateway process(es) across all profilesu   ✗ No gateway processes foundu$   ✓ Stopped gateway for this profileu'   ✗ No gateway running for this profilez servicer  zStarting gateway...)r,  uB   ⚠ Cannot restart gateway as a service — linger is not enabled.zK  The gateway user service requires linger to function on headless servers.z$  Run:  sudo loginctl enable-linger r  z    hermes gateway restartu#   ✗ Gateway service restart failed.zL  The service definition exists, but the service manager did not recover it.z3  Fix the service, then retry: hermes gateway startr  r  r  )r   r  u   ✓ Gateway is running (PID: r]  r  z-  (Running manually, not as a system service)r  rk  zTermux note:z;  Android may stop background jobs when Termux is suspendedz	WSL note:zI  The gateway is running in foreground/manual mode (recommended for WSL).z<  Use tmux or screen for persistence across terminal closes.zTo install as a service:z  hermes gateway installz&  sudo hermes gateway install --systemu   ✗ Gateway is not runningz	To start:z-  hermes gateway run      # Run in foregroundz^  nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &  # Best-effort background startz3  hermes gateway install  # Install as user servicezM  sudo hermes gateway install --system  # Install as boot-time system servicerG   zmigrate-legacyr
  rK  z@Legacy unit migration only applies to systemd-based Linux hosts.)r	  r
  )2r0  r9  r  r   r   rP  r  r   r  rK   r  r   r   rF  rW   r  rN  r  r  rw  r  r  r  r   r   r  rL   r  rK  r  r~  r   r  r  r  r  r  rS  r  rb  r!  rG   r    r   mapr0   r  ri  rp  r   )r   subcmdr,  r-  r/  rq  r   r'  	start_allrv  stop_allservice_availabletotalrestart_allservice_configuredservice_stopped	linger_ok_detailr  	_usernamer  r  r`  r[   r  r^   r
  rK  s                               r(   r  r    sU   T,d33F ~5$	1--gu--$	511G5':::: << 	FGGGFgu--x//dM488;; 	LMMM0111HQKKK$&& 	xx _```YZZZjkkk%KPPPPPPZZ 	E"""""XX 	<===]^^^:;;;GGGYZZZ[\\\`aaaHQKKKKK^^ 	QRRRjkkkGGG^___PQQQGGG:;;;HQKKKKKHIII4555HQKKKKK	;		<< 	HIIIFx//;; 	wxxx>???HQKKK$&& 	V,,,,,,ZZ 	^^ 		RSSSFGGGGGG-...+,,,HQKKKKK3444HQKKKKK	7		x//D%//	 	F+>>>F FZFZZZ[[[&tEEEE;; 	pqqq0111HQKKK$&& 	((((((ZZ 	OOOOOXX 	>????@@@GGGYZZZ[\\\`aaaGGGrsssHQKKKKK^^ 	NOOOEFFFGGGNOOOPQQQGGGCDDDHQKKKKK3444HQKKKKK	6		4..x// ,	C %(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D+>>>F#4;aa!<E 8TUTTTUUUUU677777 !&(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D % C')) E@AAAAACDDDDDA%5%7%7AAABBBBB	9		!x//dE511" 	#O(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////&*OO!4   D  6 8 8 ? ? A A  NNN&*OO!4   D+>>>F?9aa:E VTUTTTUUU"4SAAAA '((((** '0EU0S0S0S0Z0Z0\0\ '`u  ~B  aC  aC  aC  aJ  aJ  aL  aL 'V,,,,, ' 6 8 8 ? ? A A 'A&&&&F$&& 	,A,O,O,O,V,V,X,X 	\qy}\~\~\~  ]F  ]F  ]H  ]H 	!%v....$(!!0   ZZ 	244;;== 	!%!!!$(!!0    !  	#(** %>%@%@"	7D(("NNN ' 1 1IGGG^___ghhhGGGLLLMMMGGG78886777F! ;<<<deeeKLLL $%% ><==="4SAAAA '(((""""""A 	#  	#D 
8		tVU++tVU++x///v>>> %&& 0	k,A,O,O,O,V,V,X,X 0	k\qy}\~\~\~  ]F  ]F  ]H  ]H 0	k4T::::+H5555ZZ -	k244;;== -	k4   +H5555 -..D 'kRdiiC6O6ORRRSSSEFFF 5 7 7  +GGG2333 - + +k4kk****;; 
D.)))WXXXXXX D+&&&efffXYYYY45554555BCCCC2333 5 7 7  +GGG2333 - + +k4kk****k"""EFFF;; kz{{{{XX kcdddhiiiiOPPPijjj 	-.....	6			#	#	# $	511dE5))(** 	8:: 	TUUUF"3wHHHHHH 
$	#s   [* *[<;[<.\? ?]]*_= =``a a$#a$?e e$#e$f' 'f98f9l$ $l65l6*m; ;nn)FrD  r$   )Fr   )FN)r"   N)FNF)r  )TF)FFN)FFF)r  r  )r   FF)r  r6  rm   r  rt   rL   r   r   dataclassesr   pathlibr   __file__r   r   rc  r   r   gateway.restartr   r   r   hermes_cli.configr	   r
   r   r   r   r   r  r   r   r   r   r   r   r   r   hermes_cli.colorsr   r   r   r6   rJ   rb   r3   rj   r%   rp   ry   floatr   r   rG   r   r   r   r   r0   r   r   r2   r   r  r   r   r  r
  r  r(  r  r  r4  r6  r   rI  rL  rS  r_  rb  ri  rp  rw  r~  rR  rQ  rN  rP  r  r  r  r  rK   rW   r   r  r  r  r   r   r   r   r  r  r  r  r  r  r  r  r  r  r  CompletedProcessr   r  r  r  r  r1   r  r  r   r  r  r   r#  r&  r8  r;  r>  rB  rG  r  rU  rW  rK  re  r   rp  rv  r{  r  r  r  r  r  r  r  r  r   r  r  r  rF  r  r  r  r  r  r  rX   r  r  r  r  r  r  r  r  r  r  r!  r$  r(  r+  r9  r  r  r  r  r  r  r  r  r  r!  r%  r'  r+  rH  ri  rn  r  r  r  r  r  r  r   r*   r(   <module>r     s      				       



  ! ! ! ! ! !      tH~~$+3355 ( ( ( ( ( (         
                                   , + + + + + + + $T T T T T T T T  $       
<3 < < < <~2 2t 2 2 2 22C D    
s 
t 
 
 
 
3s 35 3T 3 3 3 3lCH    (T#Y S4Z s3x TX    k kSX kT kdSVi k k k k\ C$J T VZ    :  $ *	
    23 49    )S )3 )4 ) ) ) )X> >4 >E$*<M > > > >& #& &&c3h& 
#s(^& & & &R$S#X $3: $ $ $ $V Vd VsTz V V V V6dTk 6 6 6 6
/t 
/t 
/ 
/ 
/ 
/ #	: : :: *: 	:
 
: : : :zI$sCx. IT I I I Ij.K PT    "X Xd Xt X X X X
L 
LD 
LT 
L 
L 
L 
L3 3T 3t 3_c 3 3 3 3l" " " " "( ( (:P ( ( ( (V TU   uS#Xc: cDj Y\    J.D J J J J J   4%# %# %# %#P LP05 $ cDj )-:=   :)d ) ) ) )X,$ , , , , = < < < < < < < < <-$ - - - -  $    "    	4 	 	 	 	$$ $ $ $ $#D # # # # !M > > > > >6 cDj C    8
'# 
' 
' 
' 
'L L$ L4 L L L L    ,   1 1 1 1 1< 1 1 1*    -4 - - - -]D ] ] ] ]M M M M,( ( ( ( ( ( ($ ;? S S S4 S4 S S S Sl+c +c +S +UY + + + + @ @4 @DI @ @ @ @B BD BT#Y B B B B 7<   c t *Je    * * *# * * * *
d3i 
 
 
 
3t 3 3 3 3 *= uS#X < < <3 c3h   	4dDj(9#: 	 	 	 	#4c4o(>#? # # # #L- - - - -
4 4 4 4( _ ___ 3T
?_ _ _ _D= = = =
S 
T 
 
 
 
2 2#* 2c3PSm@T 2 2 2 2.D S4Z    cDj    
5C$J 
5 
5 
5 
5 D U3:tCS=T    >.;5c)9#: .; .; .; .;b5 5 5 52D 2 2 2 2O O O O O$+    B	 	 	 	 	 Q$ Qd3i QDI Q Q Q Q%49 %c % % % %Ps S S    4## ## # # # #4m m$ mS4Z mSV m m m m^J J J J J J# #    &_ _D _T _ _ _ _
 
4 
D 
 
 
 
 3 d
 d    (D (D (D (DVl l$ l4 l l l l0 0 0$ 0 0 0 00C 0D 0 0 0 04,E , , , ,7  7 4 7  7 CRVJ 7  7  7  7 tR Rd R R R R" s D T    N N$ N N N N N N N N N N"KG KGD KG KG KG KG^`, `, `,t `,4 `, `, `, `,NL3 L L L L         V V V V Vps$ s s s s    (? ?4 ? ? ? ?6	% 	% 	%! ! !2! ! !0' 'E 'ut| 'VZ ' ' ' 'T' ' '@$G $G $G $G $G $GVJsTz Jd J J J Jd       2. . . . . . . .r )
 
 
 *[dFH H-9]kp!<> > -  9Q  _dgi i
 , (
 
 
 )KT9; ;,8iw|!<> > ,  8P  ^c[] ]
% : &
 
 
" '2HVZ=? ?&2HVZCE E*6Zhm!>@ @
+ @ *	
 	
 	
 )4ftyWY Y*6p  Cdf f%1hv{KM M+7m  |A!EG G (  4I  W\ce e
 < '	
 	
 	
 &1[inWY Y';D=? ?/;_mr!AC C /  ;S  afWY Y,  9O  ]b]_ _
 < '	  &	  $
 
 
 %URT T%1SaeRT T&+5Y[ [&+5VX X*6_mr!JL L
 6 )
 
 
 *5IW\=? ?(4GUYOQ Q*6m  |A@B B(4ky~!PR R (3y  HMUW W
 8 )
 
 
 *5IW\MO O-9TbfPR R
 &  $
 
 
 %eDF F(LdHJ J$0]kpWY Y-9x  GLZ\ \+7esx!RT T +6guzGI I
 < ,#
 
 
 $xU9; ;#xT9; ;*6drw!LN N *5ftyGI I

 2  2-
 
 
 .PU57 71]X\BD D.*RWCE E+7GUYHJ J6BTbfQS S*6\jo9; ;3?m  |A!LN N
 @ "(	  )-

 

 

 .9q  @EEG G+7TbfJL L0  =\  jo!vx x 0  <I  W\]_ _

! :  
 
 
 !O8: :'3FTX<> >'3x  GL!PR R *5s  BGKM M

 0 %
 
 
 &uGI I)\tWY Y
 W}
|(T
 ( ( ( (VGt G G G G GT tCy        F_2t _2 _2 _2 _2D' ' '- - -+ + +8? 8? 8?vr, r, r,j/ / /t    '(T '( '( '( '(TB, B, B,Ji) i) i)Xh5 h5 h5Voh oh ohd3    .'4$ '44 '4 '4 '4 '4T} } }H  *NI NI NI NI NIr*   