
    i                    X   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e4e-         de-dz  de+e-         ddfdZ5dde+e-         de/de4e-         fdZ6dde+dz  de/de4fdZ7	 dde+dz  de4e*         fd Z8d!e9de4e9         fd"Z:d!e9d#e-de/fd$Z;dd%e/de<e/e/f         fd&Z=	 	 dd%e/d(e<e9d)f         de>e9e9f         fd*Z?ddd+d,d%e/d-e-dz  d.e2de/fd/Z@dd%e/d-e-dz  de/fd0ZAde/fd1ZBdd%e/de)fd2ZCd3d4de<e-d)f         e4e-         z  d5e-dz  de9fd6ZDd7e)ddfd8ZE	 	 dd9e/de+dz  de/de-fd:ZFde/fd;ZGde/fd<ZHdd=lImJZJmKZKmLZL de/fd>ZMdd%e/de/fd?ZNde/fd@ZOde/fdAZPde/fdBZQde/fdCZRdDZSdEZTde9fdFZUddGe9dz  de9fdHZVde9fdIZWdd%e/defdJZX G dK dLeY          ZZdefdMZ[defdNZ\de/fdOZ]ddPZ^dd.e2de/fdRZ_d
dSdTe/ddfdUZ`dVe9dWe9dXe9ddfdYZadd%e/de4e9         fdZZbdd%e/de4e9         fd[Zcdd\d]e4e9         d%e/dejd        fd^Zedd%e/de9fd_Zfde4e9         fd`Zgde/fdaZhdbZie<e9d)f         ejdc<   ddZke<e9d)f         ejde<   de4e<e/ef                  fdfZlde4e<e9ee/f                  fdgZmde/fdhZnddiZo	 	 ddje/dke/de<e-e4e         f         fdlZpddmZqdne9ddfdoZrddpe9dz  de<e9e9e9f         fdqZsdrede9dz  fdsZtde9dz  fdtZude9dz  fduZvdd9e/de<e9dz  e/f         fdvZwde<e/dz  e9f         fdwZxddxZydefdyZzdefdzZ{dedz  fd{Z|de9fd|Z}d}ed~e4e9         de4e9         fdZ~de9de9de9fdZde9de9fdZdd%e/dpe9dz  de9fdZde9de9fdZde9de9fdZdd%e/de/fdZdd%e/de/fdZddVe9de9dz  ddfdZddZdd%e/de/fdZde2fdZdd9e/d%e/dpe9dz  fdZdd%e/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e9fdZde9fdZde9fdZde/fdZde/fdZdd9e/fdZd Zd Zd Zdd.e2de2dz  de/fdZd Zdde/fd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dddddddd
dddddd
ddddddddddddgdddddddddddddddg 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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gdd*d+d,d-g d.d-d/dd0dd1d2d
d3dd4d5dd6dd7d8dd9dd:d;dd
d<dd=d>dd?dgdd@dAddBg dCdBdDddEddFdGd
dHddId;dd
dJddKd>dd?dgddLdMddNg dOdNdPddQddRdSd
dTddUdVddWddXdYd
dZdd[d\d
d]dd^d_dd`ddad;dd
dbdgddcddddeddfdgddhg didhdjddkddldmd
dnddodpdd
dqddrdsddtdgddudvdwdxg dydxdzdd{dd|d}d
d~ddddd
dddddddgdddddg ddd/ddddd2d
ddgdgZde4e>         fdZde>de9fdZde4e9         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de9f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/ubuntu/.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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                 f   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>/  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>X  s(      BBgw')BBBBBBr*   )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    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   b  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_profilesz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
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+g      N@)r   previous_pidrB   r   rB   c                    ddl }t                      }t          |                                           }|                                 |z   }|                                 |k     rt	          |           }|                    dd          }|                    dd          }	d}
	 ddlm}  |            }
n# t          $ r d}
Y nw xY w|dk    r9|
r ||
|k    rt          d	| d
|
 d           dS |t          d	| d           dS |dk    r|	dk    r|
                    d           |
                    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   Nr   r   r   r   r   r      ✓ z service restarted (PID )T service restarted
activatingauto-restartrd   rI      ⚠ z& service did not become active within zs.
  Check status: sudo z1hermes gateway status
  Check logs:   journalctl --user z-u z -l --since '2 min ago'F)r}   r   _service_scope_label
capitalizer   r   r   r   r   printr   r3   )r   r   rB   r}   r`   scope_labelr   propsactive_state	sub_statenew_pidr   s               r(   !_wait_for_systemd_service_restartr    s    KKK


C&v..99;;Kyy{{W$H
))++
 
 -V<<<yy33IIj"--		666666%o''GG 	 	 	GGG	 8## L0G|4K4KL[LL'LLLMMMt#<[<<<===t<''I,G,GJJqMMM

11 ))++
 
 4 
	g{ 	g 	g#g,, 	g 	g&,477"	g 	g7=&Eii2	g 	gJM	g 	g 	g  
 5s   "B3 3CC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   read_runtime_status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   lowerr   )r   r   r   r  runtime_stater  r  exec_main_statusr]   r`   r   s              r(    _recover_pending_systemd_restartr  4  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_runningr  j  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_constantsr  is_linuxrK   r   r   r   r   rW   r  r  )r   r    r  r   r   r   s         r(   get_gateway_runtime_snapshotr  y  so   *,,--L{{ 
%-%
 
 
 	

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

 !"" 	
+IQW+X+X+X(*?;;%....3?KKKRRTT+%%
 
 
 	
 zz 
%466==??:<<%#
 
 
 	
 " !   r*   rH   limitr   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 r#  r$  r%  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[   r   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): r  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_mismatchr-    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*   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   r.  rd   "   ⚠ Permission denied to kill PID zFailed to kill PID : N)r   r   rv   rw   r   rx   )r.  r   r   r[   killedr9   excs          r(   kill_gateway_processesr5    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 	 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_fileFNr1     r|   T)r   r   r7  ImportErrorrm   ru   rt   SIGTERMrv   rw   r   r}   ranger   )r   r7  r9   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	  4s5    
A 
A6A65A6*B::CCc                  @    t           j                            d          S )Nlinux)r   platformr   r   r*   r(   r  r    s    <""7+++r*   )r  r  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_operationalrE    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(   rD  rD    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   TrC  r   r*   r(   _container_systemd_operationalrL    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)r  r  shutilwhichrA  rE  r  rL  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win32rR  r   r*   r(   r   r   1  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   profilesrd   ^[a-z0-9][a-z0-9_-]{0,63}$   )hashlibrer  rW  r
   r   relative_tor_   rZ   matchrT   sha256r0   encode	hexdigest)r[  r\  rW  homer   profiles_rootrelr_   s           r(   _profile_suffixre  =  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   NrV  r   rX  rd   rY  r   )r\  r  rW  r   r0   r
   r   r]  r_   rZ   r^  rT   )rf  r\  rW  rb  r   rc  rd  r_   s           r(   r   r   X  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.
    -)re  _SERVICE_BASEsuffixs    r(   r   r   t  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   rb  )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(   rs  rs    s           r*   rs  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~    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).rv  rw  rp  privatery  r|  s    r(   !_user_systemd_private_socket_pathr    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    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.
    rv  rw  DBUS_SESSION_BUS_ADDRESSrx  z
unix:path=N)rm   r{  rz  r   r   r   )uidruntime_dirxdg_runtimebus_paths       r(   _ensure_user_systemd_envr    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))reasonfix_hintloginctlenable-lingerFr  r@   rA   r  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{  rN  rO  rL   rM   rY   r   r   r   rN   rO   r   )r  r  usernamelinger_enabledlinger_detailr]   detailr4  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)rs  )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  6  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   rq  r   r   s    r(   r   r   O  s    )886)r*   c                      g } t                      }dD ]X\  }}t          |          }||v r|                                r*|                     |           |                    |           Y| S )N))Frq  )Tr   r   )rJ   r   r   r   rS   )scopes
seen_pathsr   ra   	unit_paths        r(   get_installed_systemd_scopesr  S  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.
    Fro  rp  rq  Trn  )r   rb  r   r*   r(   _legacy_unit_search_pathsr  u  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   markerrA   s     r(   r   z,_find_legacy_hermes_units.<locals>.<genexpr>  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    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    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   rq  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   rq    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*   stopFr  r  disabler  )
missing_oku     ✓ Removed rd   u     ⚠ Could not remove r2  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           t          j        d           d S d S )Nr   zSystem gateway z! requires root. Re-run with sudo.rd   )rm   r  r   r   exit)r  s    r(    _require_root_for_system_servicer  /  sF    	z||qIIIIJJJ 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_identityr  5  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  L  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  W  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   rq  r   )r   rd   rI   )r   )choices    r(   "prompt_linux_gateway_install_scoper  ^  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.r.  r   r  )r.  r   )
r  r  rm   r  r   r   r   rO   r   systemd_install)r.  r  r  s      r(    install_linux_gateway_from_setupr  k  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   >   0nofalse)Fr   z<empty>zunexpected loginctl output: )r  r  rN  rO  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_guidancer    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   r  rm   r{  r  )r  s    r(   _launchd_user_homer    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)re  r  )rl  r   s     r(   r  r    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   rz  r   PROJECT_ROOT)r   _virtual_envr  s      r(   _detect_venv_dirr&    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)r&  r   r   r0   r   r   )r   venv_pythons     r(   r   r     sh    D<< 	2*\9KK,1K 	${###>r*   rb  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.localr)  z.cargogoz.npm-globalc                 ^    g | ])}|vt          |                                          '|*S r   )r   r   )r   r   r,  s     r(   r&  z+_build_user_local_paths.<locals>.<listcomp>  s8    PPP!Ql%:%:tAww~~?O?O%:A%:%:%:r*   r$  )rb  r,  
candidatess    ` r(   _build_user_local_pathsr1    sz     	D8Oe#$$D8Oe#$$D4K%  D= 5())	J QPPPzPPPPr*   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   rb  
expanduserr]  r0   rT   )r8   r2  r   r   relatives        r(   _remap_path_for_userr6    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   rb  r0   r]  rT   )r2  current_hermescurrent_defaulttarget_defaultr5  s        r(   _hermes_home_for_target_userr;  3  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   }| r't          |          \  }}t                    }t          |          }t!          |          }t!          |          }t!          |          }t!          |          }t!          |          }fd
|D             }|                    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                    |                     |                    |           d                    |          }dt(           d| d|rd| nd d| d| d| d| dt*           d| dS ) Nr   r)  node_modules.binnode)z/usr/local/sbinz/usr/local/binz	/usr/sbinz/usr/binz/sbinz/binr   <   r  c                 0    g | ]}t          |          S r   )r6  )r   r   home_dirs     r(   r&  z)generate_systemd_unit.<locals>.<listcomp>r  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   r$  r&  rN  rO  r   r   parentr   r3   _get_restart_drain_timeoutr   r  r;  r   r6  r   r1  r   SERVICE_DESCRIPTIONr   r
   rb  )r   r  python_pathworking_dirdetected_venvvenv_dirvenv_binnode_binr,  resolved_noderesolved_node_dircommon_bin_paths_drain_timeoutrestart_timeoutr  r  rf  profile_arg	sane_pathrB  s                      @r(   generate_systemd_unitrT  M  s2   !##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 0)A+)N)N&*h28<<";// +;AA*;AA'(;;'(;;'(;;PPPP<PPP3DNNLQQRRR,---HH\**	                  AL+S+<{+<+<+<QS                         #!   " &#   , :-   4  5       	D o''//1122K{++K/	\JJKKK()))&&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_definitionrX    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\  rX  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  rT  rX  )r   r  	installedexpected_userexpecteds        r(   systemd_unit_is_currentrd    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  Tr  r  u   ↻ Updated gateway z7 service definition to match the current Hermes install)	r   r   rd  r  
write_textrT  r   r   r   )r   r  rb  s      r(   refresh_systemd_unit_if_neededrg    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_warningrj    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/r  Tr  r	  z5Enabling linger so the gateway survives SSH logout...r  Fr  r  u8   ✓ Linger enabled — gateway will persist after logoutr  )r  r  r  r  r   r   r   r  rN  rO  rj  rL   rM   r   r0   rY   r   rN   rO   )r  r  linger_filer  r  r]   r  r  s           r(   _ensure_linger_enabledrm    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                  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   
isinstancedictr   r0   r   r   )rh   cfg	agent_cfgs      r(   rE  rE  +  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   	 --systemr   u   ↻ Repairing outdated z systemd service at: enabler  r  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:r  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   rd  r   rg  r   r   r   rD  mkdirrf  rT  r  rm  r  )r.  r   r  r  
scope_flagconfigured_users         r(   r  r  9  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  Fr  r  r  r  r      ✓ Removed r  Tr   z service uninstalled)
r   r  r   r   r   r   r  r   r   r   )r   r  s     r(   systemd_uninstallr  s  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          |           } | rt          d           nt                       t          |            t	          dt                      g| dd           t          dt          |                                            d           d S )Nr  r   Tr  r  r   z service started)	r   r  r  rg  r   r   r   r   r   r   s    r(   systemd_startr    s    "6**F "(1111
 	 !!!"&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          |           } | rt          d           t          dt                      g| dd           t	          dt          |                                            d           d S )Nr  Tr  r  r   z service stopped)r   r  r   r   r   r   r   r   s    r(   systemd_stopr    s~    "6**F 1(000F,../dTVWWWW	
L%f--88::
L
L
LMMMMMr*   c                    t          |           } | rt          d           nt                       t          |            ddlm}  |            }|0t          |          r dd l}t          |           	                                }t                      }t          d| d           |                                dz   }|                                |k     r[	 t          j        |d           |                    d           n# t          t           f$ r Y n/w xY w|                                |k     [t          d	| d
           t#          d|g| dd           t#          d|g| dd           t%          | |           d S t'          | |          rd S t#          dt                      g| dd           t#          dt                      g| dd           t          dt          |           	                                 d           d S )Nrestartr   r   r   u   ⏳ z  service draining active work...r  rd   u   ⚠ Old process (PID z) still alive after 90sr  Fr  r  r  r	  zreload-or-restartTr   r   )r   r  r  rg  r   r   ry   r}   r   r   r   r   rm   ru   r   rv   rw   r   r  r  )r   r   r9   r}   r   r`   r   s          r(   systemd_restartr    sr   "6**F "(3333!!!"&1111......
/

C
8==*622==??   	B[BBBCCC99;;#iikkH$$Q

1&8   	 iikkH$$ F#FFFGGG 	S!		
 	
 	
 	
 	cN		
 	
 	
 	
 	*cJJJJ'vCHHH 	)++,	    ')9););<VSWacdddd	
N%f--88::
N
N
NOOOOOs   *D DDdeepfullc           
      x   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#           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-                       nGt/                      \  }}|du rt          d,           n"|du rt          d-           t          d.           | rot                       t          d/           t1          |          d0t                      d1d2dgz   }|r|
                    d           t3          j        |d3           d S d S )4Nr   ry  r   u$   ✗ Gateway service is not installedz  Run: r   zhermes gateway installu4   ⚠ Installed gateway service definition is outdatedzhermes gateway restartz  # auto-refreshes the unitrJ  r=   z-lFr   )r   r@   rB   r   Tr   r   r   z gateway service is runningu   ✗ z gateway service is stoppedr  r  Recent gateway health:r  r   r   r   r   r   r   uA     ⏳ Restart pending: systemd is waiting to relaunch the gatewayr
  u@     ⚠ Planned restart is stuck in systemd failed state (exit 75)z  Run: systemctl r   zreset-failed z && u     ⚠ Systemd unit result: uB   ✓ System service starts at boot without requiring systemd lingerr  r  r  Recent logs:z-uz-n20r  )r   r   r   r   r  r  r  r  rd  r   r   r   rN   rO   r   r   r  _runtime_health_linesr   r   r0   r   r  r  r  rL   rM   )r  r   r  r  r  
status_cmdr]   rJ  r  runtime_linesr^   
unit_propsr  r  r  result_coder  r<  log_cmds                      r(   systemd_statusr    s4   "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		!	!&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.r  r  )re  rk  s    r(   rX   rX   <  s'    F,2K((((8KKr*   c                  .    dt          j                     S )Nzgui/)rm   r{  r   r*   r(   _launchd_domainr  B  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}  r)  r   r=  r>  r?  rC  c                     g | ]}||S r   r   r   r   s     r(   r&  z*generate_launchd_plist.<locals>.<listcomp>`  s    &]&]&]Q[\&]q&]&]&]r*   PATHr   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   r$  r
   r   r  rX   r   r&  rN  rO  r   rD  r   r   rt  fromkeysrm   rz  r   rQ   r   )rG  rH  rf  log_dirra   rR  rI  rK  rJ  rL  priority_dirsrM  rN  rS  	prog_argspartprog_args_xmls                    r(   generate_launchd_plistr  F  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  )r  r   r  r  r^  )
plist_pathra  rc  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/r  r  rB   	bootstrapr  uR   ↻ Updated gateway launchd service definition to match the current Hermes installT)r  r   r  rf  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  r  r  u!   ✓ Service installed and loaded!r  z2  hermes gateway status             # Check statusr   display_hermes_homez
  tail -f z/logs/gateway.log  # View logs)r  r   r  r   r  rD  r  rf  r  rL   rM   r  r0   r  r  )r.  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  Fr  r  r  u   ✓ Service uninstalled)r  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  r  r  	kickstartr  u   ✓ Service startedrH   q   u:   ↻ launchd job was unloaded; reloading service definition)r  rX   r   r   rD  r  rf  r  rL   rM   r  r0   r  CalledProcessErrorrY   )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                       d|  }	 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  rF   r  Tr  r  r        $@r  rB   force_afteru   ✓ Service stopped)rX   r  rL   rM   r  rY   _wait_for_gateway_exitr   )ra   targetr  s      r(   launchd_stopr    s    E!!++E++F
Y7tRPPPPP(   <8## DDDD
 4S9999	
     s   > A"AA"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   FTr0  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 requestedFr0  r  u"   ⚠ Gateway drain timed out after z.0fu   s — forcing launchd restartrF   r  z-kTr  r  u   ✓ Service restartedr  u'   ↻ launchd job was unloaded; reloadingr  r  )rX   r  rE  r   r   ry   r   r   rv   rw   rx   r  rL   rM   r  rY   r  r0   )ra   r  rz   r   r9   exitedr  r  s           r(   launchd_restartr  7	  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  )r  rX   rL   rM   rY   rN   rU   r   r  r
   r   r0   )r  r  ra   r]   loadedloaded_outputlog_files          r(   launchd_statusr  W	  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"verbosequietreplacec                    t           j                            dt          t                               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   )start_gateway   ┌─────────────────────────────────────────────────────────┐u@   │           ⚕ Hermes Gateway Starting...                 │   ├─────────────────────────────────────────────────────────┤u@   │  Messaging platforms + cron scheduler                    │u?   │  Press Ctrl+C to stop                                   │   └─────────────────────────────────────────────────────────┘N)r  	verbosityz
Gateway stopped.rd   )r   r8   insertr0   r$  gateway.runr  r   asynciorM   KeyboardInterruptr  )r  r  r  r  r  successs         r(   run_gatewayr  	  sF    HOOAs<(()))))))))	  ~      	
LMMM	  ~      	
LMMM	
KLLL	  ~      	GGG *7I+mmGyQQQRR   "###   s   (C C%$C%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   r  r  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   )rt  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   r  r  install_hint_registry_entry)hermes_cli.pluginsrG  r   loggerdebug
_PLATFORMSgateway.platform_registryrK  all_entriesr   r   ra   r  required_envrL  )rG  r  	platformsby_keyrK  entrys         r(   _all_platformsrX  (  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).
    rM  NFr   )PlatformConfigT)enabled
configurednot configuredr  r   r  r  r  sessionz
creds.jsonzconfigured + pairedzenabled, not pairedr   rt   SIGNAL_ACCOUNTzpartially configuredr  r  r  r  r  r   MATRIX_PASSWORDMATRIX_ENCRYPTION)r  r
  r  z + E2EEr6  WEIXIN_TOKEN)r   is_connectedgateway.configrZ  r%   r   check_fnr	   r  r
   r   allr   )r@  rW  r\  rZ  	syntheticr  valsession_fileaccountr  imapsmtp
homeserverr  e2eerl  tokens                    r(   _platform_statusrp  S  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  gateway_stateexit_reasonactive_agentsr  rU  statefatalerror_messagezunknown errorr   r2  startup_failedu   ⚠ Last startup issue: drainingr  shutdownu   ⏳ Gateway draining for z (z active agent(s))stoppedu   ⚠ Last shutdown reason: )r   r  r   r   itemsr   r3   )r  ru  linesrr  rs  rt  r  rU  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.r  ra   r       ─── r       Setup ───r  r  z is already configured.  Reconfigure ?FNr  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_USERSr  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   lstriprW  r  r   r   r   r   r   upper)r@  r  ra   r  instructionsr^   existing_tokenallowed_val_setvarexistingr   cleanedr_   r  access_choices
access_idxhome_varhome_valfirst_ids                      r(   _setup_standard_platformr    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   r  Nr   r  s     r(   r   z_setup_email.<locals>.<genexpr>+  s0      GG1U8w3F3F!3F3F3F3FGGr*   NnextrQ  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   r  Nr   r  s     r(   r   z_setup_sms.<locals>.<genexpr>1  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   r  Nr   r  s     r(   r   z"_setup_dingtalk.<locals>.<genexpr>;  s0      MM1ah*6L6LQ6L6L6L6LMMr*   r  ra   r  r   r  r  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.r   DINGTALK_ALLOW_ALL_USERSr  z configured via QR scan!)hermes_cli.setupr   r   r   r   r  rQ  r   r   r   r  r	   hermes_cli.dingtalk_authr  r9  r  r   )r   r   r   r   dingtalk_platformr  ra   r  methodr  r4  r]   	client_idclient_secrets                 r(   _setup_dingtalkr  5  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  r  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   r  r   r   r   r   r   r  )existing_bot_idexisting_secretmethod_choices
method_idxr  r  r  r4  credentialsallowedr  r  r  rb  s                 r(   _setup_wecomr  p  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   rB  Nr   r  s     r(   r   z!_setup_yuanbao.<locals>.<genexpr>  s0      KK!QuX5J5JA5J5J5J5JKKr*   Nr  )yuanbao_platforms    r(   _setup_yuanbaor    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   r  r   r*   r(   _is_service_installedr    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   r  rM   rX   rY   rZ   r   )user_unit_existssystem_unit_existsr]   s      r(   _is_service_runningr    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.r7  rb  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   ro  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_USERSr  WEIXIN_ALLOWED_USERS  DM pairing enabled.zY  Unknown DM users can request access and you approve them with `hermes pairing approve`.rd   r  r  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   r  rM   r0   r
   r  r   r   r   r   r   r  )existing_accountr  r  r  r4  r  r  r  ro  r  r  r  r  default_allowr  group_choices	group_idxallow_groupss                     r(   _setup_weixinr    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 ───r"  r#  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   larkr!  )	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_idr  r  r  r  r	  r$  	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_TOKENr%  z  Bot created: )r  r  r  r  FEISHU_ALLOW_ALL_USERSr  r&  r   r  J  Unknown users can request access; approve with `hermes pairing approve`.r  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  r'  r  u   🪽 Feishu / Lark configured!
  App ID: z
  Domain: z  Bot: )r   r   r   r  r	   r   r   r   gateway.platforms.feishur  r   r   r  r   r   r   r  r   r   r  )existing_app_idr  r  r  r  used_qrr  r4  r  r  domain_choices
domain_idxr  r  r  bot_infor	  connection_modemode_choicesmode_idxr  r  r  r  r  r  home_channels                              r(   _setup_feishur    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)r  z&  How would you like to set up QQ Bot?r   r  z  QQ Bot setup cancelled.r  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.r   r  u2     Skipped — QQ Bot won't work without an App ID.r  Tu6     Skipped — QQ Bot won't work without an App Secret.r   )r  r  user_openidr  r  r  )r  r  zOnly allow listed user OpenIDsr  QQ_ALLOW_ALL_USERSr  z  Add yourself (z) to the allow list?r@  z  Allow list set to r  r  rd   r  z$  Open DM access enabled for QQ Bot.z(  Allowed user OpenIDs (comma-separated)r   r  z  Use your QQ user ID (r  rA  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.qqbotr  r  r   r   r   rO   r   r   r  )r  r  r  r  r  r  r  r  r  r  r  r  r  r  s                 r(   _setup_qqbotr   O  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 ───r  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 r2  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  )rN  r   r   r   r  r	   r   r   rO  r   r   inputrO   EOFErrorr  httpxr   rW  status_coder   r   r   )rN  existing_urlr  default_urlurlr*  respr  default_accountrj  existing_alloweddefault_allowedr  existing_groupsgroupss                  r(   _setup_signalr5    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  r  r  r  r  r8  webhooksrt   r  r6  r  r!  r(  r=  )
hermes_clir7  _setup_telegram_setup_discord_setup_slack_setup_matrix_setup_mattermost_setup_bluebubbles_setup_webhooksr5  r  r  r  r  r  r   r   )r   _ss     r(   _builtin_setup_fnrB  ,  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.
    rM  Nr   r  ra   r  u   🔌r  r   r  z(  Set these env vars in ~/.hermes/.env: r(  z  Configure z( in config.yaml under gateway.platforms.rL  r  )r   setup_fnrB  r  r   r   r   r  rT  r   r   )r@  rW  fnra   r  requireds         r(   _configure_platformrG  C  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                  $   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	           n | rt          d
           t          dd          r	 t                      rt!                       nt#                      rt%                       n# t&          $ rO}t)          d           t+          |                                          D ]}t          d|            Y d}~nPd}~wt.          j        $ r}t)          d|            Y d}~n'd}~ww xY wnt3          d           t3          d           	 t                       t5          d           t7                      }d |D             }|                    d           t;          d|t=          |          dz
            }|t=          |          k    rnt?          ||                    dt*          dt@          fdtC          fdt7                      D                       }|rt                       t          t          dt          j"                             t                      } t                      }|rt          dd          r	 t                      rtG                       n:t#                      rtI                       ntK                       t3          d           n)# t&          $ rP}t)          d           t+          |                                          D ]}t          d|            Y d}~nd}~wt.          j        $ r}t)          d|            Y d}~nd}~ww xY wn| rt          d d          r	 t                      rt!                       nt#                      rt%                       nR# t&          $ rP}t)          d!           t+          |                                          D ]}t          d|            Y d}~nd}~wt.          j        $ r}t)          d"|            Y d}~nd}~ww xY wnt                       t                      st#                      rt                      rd#nd$}tM                      rd%nd&}	t          d'| d(|	 d)d          rQ	 d}
d*}t                      rtO          d*+          \  }
}ntQ          d*+           d}t                       |rt          d,d          r	 t                      rt!          |
d-k    .           nt%                       n# t&          $ rO}t)          d!           t+          |                                          D ]}t          d|            Y d}~n1d}~wt.          j        $ r}t)          d"|            Y d}~nd}~ww xY wnO# t.          j        $ r,}t)          d/|            t3          d0           Y d}~nd}~ww xY wt3          d1           t                      rt3          d2           t3          d3           ntM                      r=t3          d4           t3          d5           t3          d6           t3          d7           ntS                      r@d8d9l*m+} t3          d:           t3          d5           t3          d; |             d<           n<t3          d=           t3          d5           nt                       t3          d>           t                       dS )?z<Interactive setup for messaging platforms + gateway service.zrun gateway setupNr  u@   │             ⚕ Gateway Setup                            │r  u?   │  Configure messaging platforms and the gateway service. │u>   │  Press Ctrl+C at any time to exit.                     │r  z)Gateway service is installed and running.z-Gateway service is installed but not running.z  Start it now?Tu1     Failed to start — user systemd not reachable:r  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 )r  r   ra   r  r   rp  r  s     r(   r&  z!gateway_setup.<locals>.<listcomp>  sR     
 
 
 zAAAgJAA+;A+>+>AAA
 
 
r*   DonezSelect a platform to configure:rd   rJ  r"   c                     |                                  }|dk    p)|                    d          p|                    d           S )Nr]  	partiallyzplugin disabled)r  r   )rJ  ss     r(   _is_progressz#gateway_setup.<locals>._is_progress  sK    LLNN!! /||K((/||-..
 	
r*   c              3   H   K   | ]} t          |                    V  d S r$   rJ  )r   r   rO  s     r(   r   z gateway_setup.<locals>.<genexpr>  sG        ./%a(())     r*   u   ──────────────────────────────────────────────────────────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: rp  r  z. (note: services may not survive WSL restarts)r   z  Install the gateway as a z	 service?z% (runs in background, starts on boot)Fr0  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  rW   r  rs  r   r0   rP   rL   r  r   r   rX  r   r   rZ   rG  r%   r   DIMr  r  r=  rA  r  r  r  r  r  )r   r   r  r^   rU  
menu_itemsr  any_configuredplatform_namewsl_noteinstalled_scopedid_installr  rO  s                @r(   gateway_setuprY  m  sW
   || )***	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*D11 	7
7,.. $!OOOOZZ $!OOO. ' ' 'OPPPFF--// ' 'D+t++&&&&' ' ' ' '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VeJ
++,,,133-// M	JH$OO ::022 E'))))! E')))),..."#CDDD2 + + + RSSS #A 1 1 3 3 + +k4kk****+ + + + +!4 : : : 8Q 8 899999999::  =	J;TBB 8
8022 (%! (%2 + + + PQQQ #A 1 1 3 3 + +k4kk****+ + + + +!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   *9H$ $
J&.AI88J&
J!!J& AQ 
S#AR..S SS59T0 0
V4:AVV4V//V4!A\3 :1Z, +\3 ,
\.6A\ ;\3  \.\)$\3 )\..\3 3].!]))].c                    	 t          |           S # t          $ rd}t          d           t          |                                          D ]}t          d|            t          j        d           Y d}~dS d}~ww xY w)zHandle gateway subcommands.zUser systemd not reachable:r  rd   N)_gateway_command_innerrs  r   r0   rP   r   r   r  )r   r  r^   s      r(   gateway_commandr\    s    %d+++&    	1222FF%%'' 	 	D+t++s    
A?AA::A?c           	      2!   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    r=t          | dRd          }t          | dSd          }t          | dd          }tK          |'          }t                      rgt/          d'                                          s"t/          d/'                                          r#tM          |||T           tO          |           d S t                      r@t9                                                      r tQ          |           tO          |           d S tS          |j*                  }|r4t          dUdV+                    tY          tZ          |                     dW           t          dX           t]                      }|r4t                       t          dY           |D ]}t          dZ|            t                       t                      r t          d[           t          d\           d S t                      r/t          d]           t          d^           t          d_           d S t          d`           t          da           t          db           d S t          dc           t]                      }|r4t                       t          dY           |D ]}t          dZ|            t                       t          dd           t          de           t                      rt          df           d S t                      r t          d           t          d           d S t          dg           t          dh           d S |dik    rct          | djd          }t          | dkd          }t                      st                      st          dl           d S t_          | |m           d S d S )nNr\  rM   r  r   r  Fr  )r  r  r7  rx  z*install gateway service (managed by NixOS)r.  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'r  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.r  rf  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 ri  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 startrJ  r  r  )r   r  u   ✓ Gateway is running (PID: r(  r   z-  (Running manually, not as a system service)r  r  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 servicezmigrate-legacyr  r  z@Legacy unit migration only applies to systemd-based Linux hosts.)r  r  )0getattrr  rY  r   r   r  r   r   r  rK   rA  r   r   r  rW   r  r  r  r  r5  r  r  r  r   r   r  rL   r  r  r  r=  r   r  r  r  r  r  r  r  r-  r  rG   r    r   mapr0   r  r  )r   subcmdr  r  r  r.  r   r  	start_allr3  stop_allservice_availabletotalrestart_allservice_configuredservice_stopped	linger_ok_detailr  	_usernamer  r  r+  r[   r  r^   r  r  s                               r(   r[  r[  ,  sD   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5ZZ -	k244;;== -	k4   +H5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XXX D+&&&efffXYYYYY45554555BC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iOPPPijjjjj	#	#	# $	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)Fr  r$   )Fr   )FN)FNF)r"   N)r  )TF)FFN)FFF)r  r  )r   FF)rt  r  rm   rN  rt   rL   r   r   dataclassesr   pathlibr   __file__rD  r   r$  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   rG   r   r   r   r   r0   r   r   r2   r   rt  r   r  r  r  r  r*  r-  r5  r=  r  r  r  r  rA  rE  rD  rL  rK   rW   r   rj  rF  re  r   r   r   r   rs  r~  r  r  r  r  r  r  r  r  CompletedProcessr   r   r  r  r  r1   r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r&  r   r1  r6  r;  rT  rX  r^  rd  rg  rj  rm  r   rE  r  r  r  r  r  r  rX   r  r  r  r  r  r  r  r  r  r  r  r  rQ  rX  rp  r  r  r  r  r  r  r  r  r  r  r  r  r   r5  rB  rG  rY  r\  r[  r   r*   r(   <module>rs     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T#Y S4Z s3x TX    g gSX gT gdSVi g g g gT C$J T VZ    :  $ *	
    23 49    )S )3 )4 ) ) ) )X> >4 >E$*<M > > > >& #% %%c3h% 
#s(^% % % %T #	, , ,, *, 	,
 
, , , ,^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 LP05 $ cDj )-:=   :#d # # # #L,$ , , , , = < < < < < < < < <-$ - - - -  $    "    	4 	 	 	 	$$ $ $ $ $#D # # # # !M > > > > >6 cDj C    8
'# 
' 
' 
' 
'L L$ L4 L L L L    ,       -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s S S    4## ## # # # #4k k$ kS4Z kSV k k k kZ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,E , , , ,7  7 4 7  7 CRVJ 7  7  7  7 tR Rd R R R R"N N$ N N N NN N N N N N9P 9PD 9P 9P 9P 9Pz\, \, \,t \,4 \, \, \, \,FL3 L L L L         V V V V Vps$ s s s s    (? ?4 ? ? ? ?6	% 	% 	%! ! !2! ! !"' 'E 'ut| 'VZ ' ' ' 'T' ' '@$G $G $G $G $G $GV       T )
 
 
 *[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k k kd
 
 
HI HI HI HI HIr*   