
    JjI                    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Zddl	Z	ddl
Z
ddlZddlmZ ddlmZ ej        dk    rh dZ eej                            dd                              ej                            Zeez
  ZerOej                            dd          ej        z   ej                             ee                    z   ej        d<    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+m,Z, ddl-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5 ddl6m7Z7m8Z8  ej9        e:          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eAfdZBde?deAfdZCde?deDdeAfdZEdee?         fdZFdeGe?         de?dz  dee?         ddfd ZH	 	 d[dee?         d"eAd#eAdeGe?         fd$ZIdeGe?         deGe?         fd%ZJ	 d\dedz  d"eAdeGfd&ZK	 d]dedz  deGe=         fd'ZLd(eMdeGeM         fd)ZNde?deGeM         dz  fd*ZOd+e?d,eGeM         deAfd-ZPd(eMd+e?deAfd.ZQd+e?d,eGeM         deAfd/ZRd^d0eAdeSeAeAf         fd1ZTd^d0eAdeUeMeMf         fd2ZVd0eAddfd3ZW	 	 d_d0eAd5eSeMd6f         deUeMeMf         fd7ZXd8eUeMeMf         de?dz  fd9ZYd^d0eAde?dz  fd:ZZdeUdz  fd;Z[de?dz  deUdz  fd<Z\d!dd=d>d0eAd?e?dz  d@eDdeAfdAZ]d8eUeMeMf         deAfdBZ^dCej_        deAfdDZ`d^d0eAdeAfdEZad^d0eAddfdFZb	 d`d0eAd?e?dz  deAfdGZcdHeMde?dz  fdIZddeAfdJZed^d0eAde<fdKZfdLdMdeSe?d6f         eGe?         z  dNe?dz  deMfdOZgdPe<ddfdQZhdadRZidadSZj	 dbdTeAdedz  d"eAde?fdUZkdeAfdVZldeAfdWZmdeAfdXZnddYlompZpmqZqmrZr deAfdZZsd^d0eAdeAfd[ZtdeAfd\ZudeAfd]ZvdeAfd^ZwdeAfd_ZxdeAfd`ZydaZzdbZ{deMfdcZ|dcddeMdz  deeMez  dz  deMfdfZ}ddeMdgeMdeMfdhZ~deMfdiZd^d0eAdefdjZ G dk dle          Z G dm dne          ZdefdoZdefdpZdeAfdqZdadrZddd@eDdeAfdtZddudveAddfdwZdxeMdyeMdzeMddfd{Zd^d0eAdeGeM         fd|Zd^d0eAdeGeM         fd}Zd!d~deGeM         d0eAdej        fdZd^d0eAdeMfdZdeGeM         fdZdeAfdZdZeSeMd6f         ed<   dZeSeMd6f         ed<   deGeSeAef                  fdZdeGeSeMeeAf                  fdZdeAfdZdadZ	 	 dedeAdeAdeSe?eGe         f         fdZdadZdeMddfdZd]deMdz  deSeMeMeMf         fdZdedeMdz  fdZdeMdz  fdZdeMdz  fdZdfdTeAdeAdeSeMdz  eAf         fdZdeSeAdz  eMf         fdZdadZdefdZdefdZdedz  fdZdeMfdZdedeGeM         deGeM         fdZdeGeM         deGeM         fdZdeMdgeMdeMfdZdgeMdeMfdZd]dedz  deGeM         fdZdeMfdZd`d0eAdeMdz  deMfdZdeMdeMfdZdZdeMdeMfdZdeMdeMfdZd^d0eAdeAfdZdeMdeMdz  fdZdeMdeMdeAfdZd^d0eAdeAfdZd]dxeMdeMdz  ddfdZdadZd^d0eAdeAfdZd^d0eAdeAfdZdeMddfdZdeDfdZ	 	 	 	 	 dgdTeAd0eAdeMdz  deAdeAf
dZd^d0eAfdZd^deMd0eAddfdZd^d0eAfdZd^d0eAfdZd^d0eAfdÄZdhdeAd0eAdeAfdƄZdeMfdǄZdaeMdz  ed<   deMfdɄZ eh dʣ          Z eddh          ZdCej_        deAfd̈́Zde?deAfdτZdZddќdeMdeMd@e?ddfdԄZdefdՄZdeMddfdׄZdeMdeAfd؄ZdeMdeMdeDdeAfdڄZdefdۄZѐdad܄ZҐdad݄ZdeAfdބZdeGeM         fd߄ZdeAfdZdddyeMdeAdeAfdZdeMfdZdeAfdZdeAfdZڐd^dTeAfdZd Zd Zd Z	 did@eDdeDdz  deAfdZd Zd^deAfdZdeMdz  deAfdZdeAfdZdeAfdZd^dTeAddfdZd^dTeAddfdZd^deAddfdZdadZdjde?deAdeAdTeAfdZddd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ddddddddddg dddd!d dd!d"dd#dd$d%d!dd&dd'd(d!d)dgdd*d+d,d-g d.d-d/d!d0dd1d2dd3dd4d5d!dd6dd7d8d!d9dgdd:d;d<d=g d>d=d?d!d@ddAdBddCdgdgZdeGeU         fdDZdEeUdeMfdFZdeGeM         fdGZdHeMdIeMddfdJZdEeUfdKZdeAfdLZdeAfdMZdN ZdO ZdP ZdQeMfdRZdEeUddfdSZdT Z	 d]deMd(eMdz  deAfdUZdeMdeAfdVZdW ZdeAfdXZdadYZdZ ZdS (k  zu
Gateway subcommand for hermes CLI.

Handles: hermes gateway [run|start|stop|restart|status|install|uninstall|setup]
    N)	dataclass)Pathposix>   /bin/sbin/usr/bin	/usr/sbin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write_platform_config_field)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    8/home/rurouni/.hermes/hermes-agent/hermes_cli/gateway.pyrunningzGatewayRuntimeSnapshot.runningM   s    #>tD,='>'>>    c                 .    | j         o| j        o| j         S r,   )r%   r1   r&   r.   s    r0   has_process_service_mismatchz3GatewayRuntimeSnapshot.has_process_service_mismatchQ   s    %S$,St?S;SSr2   )__name__
__module____qualname__str__annotations__r%   r-   r&   r(   tupleintr)   propertyr1   r4   r'   r2   r0   r#   r#   E   s         LLL#t###!OT!!!$&L%S/&&& $M3:$$$? ? ? ? X? Td T T T XT T Tr2   r#   c                   .    e Zd ZU eed<   eed<   eed<   dS )ProfileGatewayProcessprofilepathpidN)r5   r6   r7   r8   r9   r   r;   r'   r2   r0   r>   r>   V   s+         LLL
JJJ	HHHHHr2   r>   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    rt#          |j                  }||dk    r| 
                    |           n|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listN      )setsupports_systemd_services
subprocessrunstdoutstrip
splitlinessplitendswithr;   add
ValueErrorTimeoutExpiredFileNotFoundErroris_macosget_launchd_label
returncode#_parse_launchd_pid_from_list_outputlen)	pids
scope_argsresultlinepartssvcrL   rA   labels	            r0   _get_service_pidsrk   ]   s    D !"" "'2[MB !	 !	J #   $(   #M//11<<>>  D JJLLE  !a(9(9*(E(E ! (C)~&&#7KY)WW+/!%$%	      "$+"3"3"5"56677 HHSMMM&
(AB   " &z'@A    zz 	%''E^fe,#	  F  A%%9&-HH?sQwwHHSMMMM !' 3 3 5 5 @ @ B B % % $

u::??uQx5/@/@%&)%(mm#&77$(HHSMMM#- % % % $%!:#<= 	 	 	D	 Ksn   BD-.A DD-D(%D-'D((D--EECI$ !0II$ 
II$ II$ $I=<I=rA   c           	         | dk    rdS 	 ddl }|                    |                                           pdS # t          $ r Y nt          $ r Y dS w xY wt                      rdS t          j        d          s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 )u  Return the parent PID for ``pid``, or ``None`` when unavailable.

    Uses psutil (core dependency) which works on every platform.  The
    older implementation shelled out to ``ps -o ppid= -p <pid>``, which
    silently fails on Windows (no ``ps``) so the ancestor walk terminated
    at self — the caller's dedup / exclude logic then couldn't distinguish
    "hermes CLI that invoked this scan" from "real gateway process".
       Nr   ps-ozppid=z-pTrF   rG   )psutilProcessppidImportError	Exception
is_windowsshutilwhichrT   rU   r8   r^   r]   ra   rV   rW   r;   rX   r\   )rA   rq   rf   raw
parent_pids        r0   _get_parent_pidr{      s    axxt~~c""''))1T1      tt
 || t< t4$C1	
 
 
 z89   ttAt
-



C t))"-335566

   tt#a::T1s9   ,7 
A	AA:*B% %B?>B?-9D' '
D54D5
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getpidrR   r[   r{   )r|   rA   seens      r0   #_is_pid_ancestor_of_current_processr      s~    Qu
)++CUUD
 (#T//*4c""'a	  (#T//
 5r2   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signalr   r~   killr   ProcessLookupErrorPermissionErrorOSError)rA   s    r0   _request_gateway_self_restartr      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   }ddlm} |	                                |k     r: ||           sdS |                    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.  Both systemd (``Restart=always``) and launchd
    (unconditional ``<key>KeepAlive</key><true/>``) restart on any 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).
    r   Fr   TNg      ?_pid_exists      ?)r   r   r~   r   r   r   r   r   time	monotonicmaxgateway.statusr   sleep)rA   r   _timedeadliner   s        r0   _graceful_restart_via_sigusr1r      s!   0 69%% u
axxu
V^$$$$   ttW%   uu   3}c#:#::H +*****
//

h
&
&{3 	4C //

h
&
&
 5s   A 
A"A"!A"c                      t                      } t          j                    }t          d          D ]6}|                     |           t          |          }|
|dk    s|| v r n|}7| S )ah  Return the set of PIDs in the current process's ancestor chain.

    Walks from the current PID up to PID 1 (init) so that process-table scans
    never match the calling CLI process or any of its parents.  This prevents
    ``hermes gateway status`` from falsely counting the ``hermes`` CLI that
    invoked it as a running gateway instance (see #13242).
    @   Nr   )rR   r~   r   ranger[   r{   )	ancestorsrA   _parents       r0   _get_ancestor_pidsr   .  su     %%I
)++C2YY  c %%>Vq[[Fi,?,?Er2   rd   exclude_pidsc                     ||dk    rd S |t          j                    k    s||v s|| v rd S |                     |           d S )Nr   )r~   r   append)rd   rA   r   s      r0   _append_unique_pidr   B  sV     {cQhh
bikkSL00C4KKKKr2   Fall_profilesinclude_restart_managersc           	      J    | t                      z  } g }ddlmm  t	          t                                                                }|                                t          |          }|r|	                                d         nd                                dt          dt          ffd}dt          dt          f fd}	 t                      rdd	lm} d
 |            i}	t          j        d          }
d}d}|
=	 t!          j        |
ddddgfdddddd|	}n# t$          t           j        f$ r d}Y nw xY w||j        dk    s|j        sm	 t          j        d          pt          j        d          }|g S d}	 t!          j        |dd|gfdddddd|	}n# t$          t           j        f$ r g cY S w xY wd}|j        dk    s|j        g S d}|j        	                    d          D ]}|                                }|                    d          r|t1          d          d         }C|                    d          ra|t1          d          d         } ||          r=|s ||          r0	 t3          |t5          |          |            n# t6          $ r Y nw xY wd}nd}t8          j                            d           r	 t9          j                    }t9          j         d           D ]}|!                                st5          |          }||k    s|| v r1	 tE          d!| d"d#          5 }|#                                $                    dd$%          }ddd           n# 1 swxY w Y   |%                    d&d'          } ||          r|s ||          rt3          |||            # t$          tL          f$ r Y w xY wd}n# tN          $ r Y nw xY w|s`t!          j        g d(ddd)          }|j        dk    rg S |j        	                    d          D ]}|                                }|rd*|v rd}d}|	                    dd+          }t1          |          d,k    r1	 t5          |d                   }|d+         }n# t6          $ r d}Y nw xY w|s|	                                }t1          |          dk    rL|d+         !                                r2t5          |d+                   }d'(                    |dd                   }| ||          r|s ||          rt3          |||            n# t$          t           j        f$ r g cY S w xY wt                      r"t1          |          d+k    rtS          |          }|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.
    r   )looks_like_gateway_command_line'looks_like_gateway_runtime_command_linerp   r   commandr*   c                     |                                  }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 FT)lower)r   
command_lccurrent_home_lccurrent_profile_namecurrent_profile_name_lcs     r0   _matches_current_profilez4_scan_gateway_pids.<locals>._matches_current_profilem  s    ]]__
 	6466*D B2022j@B3/33zA :%%:)=)=5j((000
BB5tr2   c                 8     |           rdS o
 |           S )NTr'   )r   r   r   r   s    r0   _matches_gateway_runtimez4_scan_gateway_pids.<locals>._matches_gateway_runtime  s3    **733 	4'\,S,ST[,\,\\r2   )windows_hide_flagscreationflagswmicFNprocessgetzProcessId,CommandLinez/FORMAT:LISTTutf-8ignore
   )rH   rI   encodingerrorsrJ   
powershellpwshzGet-CimInstance Win32_Process | ForEach-Object {   'CommandLine=' + ($_.CommandLine -replace "`r`n",' ' -replace "`n",' ');   'ProcessId=' + $_.ProcessId;   '' }z
-NoProfilez-Command   
zCommandLine=z
ProcessId=z/procz/proc/z/cmdlinerbreplacer     )rn   z-Aewwro   zpid=,command=rG   greprm   rQ   )*r   r   r   r   r8   r   resolver   _profile_argrY   r-   rv   hermes_cli._subprocess_compatr   rw   rx   rT   rU   r   r]   ra   rV   rW   
startswithrc   r   r;   r\   r~   r@   isdirr   listdirisdigitopenreaddecoder   r   ru   join_filter_venv_launcher_stubs)!r   r   r   rd   current_homecurrent_profile_argr   r   r   
_no_window	wmic_pathused_fallbackrf   r   ps_cmdcurrent_cmdrg   pid_str_found_via_procmy_pidentryrA   _fcmdlinestrippedr   rh   	aux_partsr   r   r   r   r   s!     `                         @@@@@r0   _scan_gateway_pidsr   L  s     "4"6"66LD        ((002233L"((**O&|44+>F!!##B''B  388::# $        .]# ]$ ] ] ] ] ] ] ] ]
P<< M	D IHHHHH)+=+=+?+?@JV,,I!MF$"'^%%!3* (,!!(' "  % FF  !:; " " "!FFF"~!2a!7!7!7!7 $\,77O6<;O;O
%I '^#\:vF'+!!(' "  % FF  !:;   III $ A%%)>	K++D11 % %zz||??>22 %"&s>':':'<'<"=KK__\22 	%"3|#4#4#6#67G//<< !$!(@(@(M(M!!.tS\\<PPPP) ! ! ! D!"$K%" $Ow}}W%% Y[[F!#G!4!4 % %$}} %$!%jj&==C<,?,?$	%!%&<s&<&<&<d!C!C Vr*,''))*:*:79*:*U*UV V V V V V V V V V V V V V V&-oofc&B&BG77@@ L ,L0H0H0Q0QL !34l K K K '9 % % %$H%&*OO    D # $D#>>>#'	   $))I"M//55 D DD#zz||H# !v'9'9 C G$NN433E5zzQ'"%eAh--C&+AhGG) ' ' '"&CCC' {$,NN$4$4	y>>B..9Q<3G3G3I3I."%il"3"3C&)hhy~&>&>G{ //88 D$D(@(@(I(ID +4lCCCZ./   			 || 1D		A*400Ks&  ;U D+ *U +EU EAU 
U F* )U *GU GU B#U J! U !
J.+U -J..*U AO! 2O*M<0O<N 	 ON 	AOO! OO! OO!  U !
O.+U -O..,U A%U RU R.+U -R..B&U U0/U0c                    	 ddl }n# t          $ r | cY S w xY wt          |           }i }| D ]I}	 |                    |                                          ||<   .# |j        |j        f$ r d||<   Y Fw xY wt                      |                                D ] \  }}|||v r                    |           !fd| D             S )a.  Drop venv-launcher ``pythonw.exe`` stubs that are parents of the real
    interpreter process.  See comment at the tail of ``_scan_gateway_pids``.

    Uses ``psutil`` (core dependency).  Safe on any platform; only invoked
    on Windows by the caller because the stub pattern is Windows-specific.
    r   Nc                     g | ]}|v|	S r'   r'   ).0pdrops     r0   
<listcomp>z/_filter_venv_launcher_stubs.<locals>.<listcomp>G  s    ---!q}}A}}}r2   )	rq   rt   rR   rr   rs   NoSuchProcessAccessDenieditemsr[   )rd   rq   pid_set	parent_ofrA   rs   r   s         @r0   r   r   ,  s       $iiG')I " "	"#^^C005577IcNN$f&9: 	" 	" 	"!IcNNN	" UUD__&&  	THHTNNN----t----s    *AA43A4c                    t          | pt                                }g }|s1	 ddlm} t          | |            |           n# t          $ r Y nw xY wt                      D ]}t          |||           	 t                       }n# t          $ r d}Y nw xY w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_pidF)r   r   )rR   r   r   r   ru   rk   rS   r   )r   r   _excluderd   r   rA   r   s          r0   find_gateway_pidsr   J  s&    <(355))HD 	666666t__%6%6AAAA 	 	 	D	 "" 0 04h////)'@'B'B#B   ) ) )#(   )!!!9   0 0
 	4h////Ks#   A 
AA6B BBc                    t          | pt                                }g }	 ddlm} ddlm} n# t
          $ r |cY S w xY wt                      } |            D ]~}	  ||j        dz  d          }n# t
          $ r Y %w xY w||dk    s||v s||v r:|                    |           |                    t          |j
        |j        |                     |S )	zDReturn running gateway PIDs mapped to Hermes profiles via PID files.r   r   )list_profilesgateway.pidFcleanup_staleN)r?   r@   rA   )rR   r   r   hermes_cli.profilesr   ru   r@   r[   r   r>   name)r   r   	processesr   r   r   r?   rA   s           r0   find_profile_gateway_processesr   p  s>    <(355))H-/I2222225555555    UUD =?? 

 

	!/',">eTTTCC 	 	 	H	;#((cXoo!',W\sSSS	
 	
 	
 	
 s   . ==A11
A>=A>r?   c                     t                      ddg}| dk    r|                    d| g           |                    g d           |S )N-mhermes_cli.maindefaultz	--profilegatewayrU   z	--replace)get_python_pathextend)r?   argss     r0   _gateway_run_args_for_profiler    sU    t%67D)['*+++KK///000Kr2   c                    | dk    rdS 	 ddl }n# t          $ r Y dS w xY w	 t          |                    |                                           pg           }n*# |j        |j        |j        f$ r Y dS t          $ r Y dS w xY w|sdS 	 ddl	m
}  |d                    |                    sdS n# t          $ r Y nw xY w|S )uM  Return the live argv of a running gateway process, or ``None``.

    Used to respawn gateways that have no profile→PID-file mapping (e.g. a
    Windows Scheduled Task running ``pythonw.exe -m hermes_cli.main gateway
    run``). ``_pause_windows_gateways_for_update`` force-kills such gateways
    before mutating the venv; without their original command line we cannot
    bring them back, so we snapshot it here before the kill.

    Best-effort: returns ``None`` if psutil is unavailable, the process is
    gone, access is denied, or the argv doesn't look like a gateway command.
    rm   Nr   )r   r   )rq   rt   rO   rr   r   r   r   ZombieProcessru   r   r   r   )rA   rq   argvr   s       r0   _capture_gateway_argvr
    s0    axxt   ttFNN3''//117R88 &"5v7KL   tt   tt tBBBBBB..sxx~~>> 	4	   Ks5    
6A A?2	A?>A?$B. .
B;:B;old_pidrun_argvc                 P    | dk    s|sdS t          | t          |                    S )u  Relaunch a gateway by replaying its captured command line after exit.

    Companion to ``launch_detached_profile_gateway_restart`` for gateways that
    have no profile→PID-file mapping (Scheduled-Task / manually-launched
    ``gateway run`` whose HERMES_HOME or argv doesn't match a known profile).
    Uses the identical detached-watcher mechanism; only the respawn argv
    differs (the process's own argv instead of a profile-derived one).
    r   F)_spawn_gateway_restart_watcherrO   )r  r  s     r0   *launch_detached_gateway_restart_by_cmdliner    s,     !||8|u)'4>>BBBr2   c                 L    |dk    rdS t          |t          |                     S )zDRelaunch a manually-run profile gateway after its current PID exits.r   F)r  r  )r?   r  s     r0   'launch_detached_profile_gateway_restartr    s*    !||u)'3PQX3Y3YZZZr2   c                    | dk    s|sdS ddl m}m} d}i }t          j        dk    r8	 ddlm}  |t          |                    \  }}}n# t          $ r d}i }Y nw xY wt          j
        |          }t          j
        |          }t          j        d                                                              ||          }	t          j        d	|	t!          |           g|}
	 t#          j        |
ft"          j        t"          j        d
 |             nm# t(          $ r` 	 t          j        dk    rd |            inddi}t#          j        |
ft"          j        t"          j        d
| n# t(          $ r Y Y dS w xY wY nw xY wdS )zMSpawn the detached watcher that respawns ``run_argv`` once ``old_pid`` exits.r   F)&windows_detach_flags_without_breakawaywindows_detach_popen_kwargsr   win32)windowless_gateway_restart_specuj
  
        import os
        import subprocess
        import sys
        import time
        from hermes_cli._subprocess_compat import (
            windows_detach_flags,
            windows_detach_flags_without_breakaway,
        )

        pid = int(sys.argv[1])
        cmd = sys.argv[2:]
        _respawn_cwd = {respawn_cwd_literal}
        _respawn_env_overlay = {respawn_env_literal}
        deadline = time.monotonic() + 120
        while time.monotonic() < deadline:
            # ``os.kill(pid, 0)`` is not a no-op on Windows — use the
            # cross-platform existence check.
            from gateway.status import _pid_exists
            if not _pid_exists(pid):
                break
            time.sleep(0.2)

        # Platform-appropriate detach for the respawned gateway.  On POSIX
        # start_new_session=True maps to os.setsid; on Windows we need
        # explicit creationflags because start_new_session is a no-op there.
        # CREATE_BREAKAWAY_FROM_JOB is critical: the watcher itself may have
        # been spawned inside a job object (Electron/Tauri parent), and
        # without breakaway the respawned gateway would die when that job
        # tears down. See _subprocess_compat.windows_detach_flags().
        _popen_kwargs = {{
            "stdout": subprocess.DEVNULL,
            "stderr": subprocess.DEVNULL,
        }}
        # Anchor the respawned gateway at the stable working dir and overlay
        # the env (VIRTUAL_ENV / PYTHONPATH / HERMES_HOME) the windowless
        # base interpreter needs to import hermes_cli.  Empty on POSIX, where
        # the venv python resolves imports without help.
        if _respawn_cwd:
            _popen_kwargs["cwd"] = _respawn_cwd
        if _respawn_env_overlay:
            _popen_kwargs["env"] = {{**os.environ, **_respawn_env_overlay}}
        if sys.platform == "win32":
            try:
                _popen_kwargs["creationflags"] = windows_detach_flags()
                subprocess.Popen(cmd, **_popen_kwargs)
            except OSError:
                # CREATE_BREAKAWAY_FROM_JOB can be rejected with
                # ERROR_ACCESS_DENIED when the parent's job object refuses
                # breakaway. Retry without it — DETACHED_PROCESS et al.
                # alone are enough in most setups. Mirrors the canonical
                # fallback in gateway_windows._spawn_detached.
                _popen_kwargs["creationflags"] = windows_detach_flags_without_breakaway()
                subprocess.Popen(cmd, **_popen_kwargs)
        else:
            _popen_kwargs["start_new_session"] = True
            subprocess.Popen(cmd, **_popen_kwargs)
        )respawn_cwd_literalrespawn_env_literal-c)rV   stderrr   start_new_sessionT)r   r  r  sysplatformhermes_cli.gateway_windowsr  rO   ru   jsondumpstextwrapdedentrW   format
executabler8   rT   PopenDEVNULLr   )r  r  r  r  respawn_cwdrespawn_env_overlayr  r  r  watcherwatcher_argvfallback_kwargss               r0   r  r    sn   !||8|u&        K*,
|w	%     
 0/X?? 7Hk#6#6  	% 	% 	% K"$	% *[11*%899o9	; ;v eggff//   w B 	G	
 
L	
%%	
 	
 *)++		
 	
 	
 	
    	 <7** !"H"H"J"JKK)40 
 !)!)  "	     	 	 	555	 ( 4sH   "A AA"1D 
E>A	E)(E>)
E83E>7E88E>=E>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,  rH   rI   rJ   active)
_select_systemd_scopeget_systemd_unit_pathexists_run_systemctlget_service_nameRuntimeErrorrT   r]   rV   rW   )r,  selected_systemunit_existsrf   s       r0   _probe_systemd_service_runningr:  t  s    +F33O'???FFHHK &%%	&*,,-"
 
 
 *34 & & &%%%%&FM//11X===s   "A A98A9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 ]{}|
                    d	          s|t          d	          d
                                         }|                                D ]%}d|vr|                    dd          \  }}|||<   &||S )a  Parse the gateway unit's ``Environment=`` directives.

    ``systemctl show -p Environment`` returns a single line of
    space-separated ``KEY=VALUE`` pairs; values are not quoted in the output
    even when the unit file quoted them. We split on whitespace and ``=``.
    rL   rE   
--propertyEnvironmentTr   r0  r   zEnvironment=N=rm   )r2  r5  r6  r7  rT   r]   r   ra   rV   rX   r   rc   rW   rY   )	r,  r8  rf   parsedrg   bodytokenkeyvalues	            r0   _read_systemd_unit_environmentrD    sG    ,F33O "" #
 
 
 *3W=   			A	F((**    ~.. 	C''))*0022ZZ\\ 	  	 E%S!,,JCF3KK		 
 Ms   %7 AAc                    | sdS t          d          }|                    dd                                          }|sdS t          j                            dd                                          }||k    rdS |t          j        d<   dS )u  When acting on a system-scope unit, adopt its ``HERMES_HOME``.

    Under ``sudo``, ``HERMES_HOME`` is stripped and ``HOME=/root``, so
    :func:`get_hermes_home` falls back to ``/root/.hermes`` — the wrong
    profile. The unit file pins ``HERMES_HOME`` for the actual gateway
    process, so we mirror that into our own environment to make
    ``read_runtime_status`` / ``get_running_pid`` read the correct files.
    NTr.  HERMES_HOMEr   )rD  r   rW   r~   environ)r,  env	unit_homecurrents       r0   #_sync_hermes_home_from_systemd_unitrK    s      
(
5
5
5Cr**0022I jnn]B//5577G) )BJ}r2   ActiveStateSubStateResultExecMainStatusMainPID
properties.c           	         t          |           }	 t          dt                      ddd                    |          g|ddd          }n$# t          t
          j        t          f$ r i cY S w xY w|j        dk    ri S i }|j	        
                                D ]7}d	|vr|                    d	d
          \  }}|                                ||<   8|S )zCReturn selected ``systemctl show`` properties for the gateway unit.rL   rE   r<  ,Tr   r0  r   r>  rm   )r2  r5  r6  r   r7  rT   r]   r   ra   rV   rX   rY   rW   )r,  rR  r8  rf   r?  rg   rB  rC  s           r0   _read_systemd_unit_propertiesrU    s    ,F33O ""$$ #
 
 
 *3W=   			 A	F((** $ $d??ZZQ''
UkkmmsMs   8A
 
A+*A+propsc                     	 t          |                     dd          pd          }n# t          t          f$ r Y d S w xY w|dk    r|nd S )NrQ  0r   )r;   r   	TypeErrorr\   )rV  rA   s     r0   _systemd_main_pid_from_propsrZ    sb    %))Is++2s33z"   tt''33t#s   %( ==c                 <    t          t          |                     S Nr.  )rZ  rU  r.  s    r0   _systemd_main_pidr]    s    '(EV(T(T(TUUUr2   c                  z    	 ddl m}   |             }n# t          $ r Y d S w xY wt          |t                    r|nd S )Nr   read_runtime_status)r   r`  ru   
isinstancedict)r`  states     r0   _read_gateway_runtime_statusrd    sd    666666##%%   ttud++5555s    
!!c                     | sd S t                      }|sd S 	 t          |                    dd          pd          }n# t          t          f$ r Y d S w xY w|| k    r|nd S )NrA   r   )rd  r;   r   rY  r\   )rA   rc  	state_pids      r0   _gateway_runtime_status_for_pidrg    s     t(**E t		%++0q11		z"   tt$$55$.s   %> AAg      N@)r,  previous_pidrJ   rh  rJ   c                    ddl }t                      }t          |                                           } |j                    |z   }d} |j                    |k     r}t          |           }|                    dd          }	|                    dd          }
d}	 ddlm}  |            }n# t          $ r d}Y nw xY w|st          |          }|	d	k    r|r|||k    rt          |          }|pi                     d
          }|dk    rt          d| d| d           dS |dk    r3|pi                     d          pd}t          d| d| d|            dS |st          d| d| d           d}|	dk    r|
dk    r |j        d           Lt          |          rt          |            dS  |j        d            |j                    |k     }t          d| dt!          |           d| rdnd d | sd!nd d"| d#           dS )$zFWait for the gateway service to become active after a restart handoff.r   NFr.  rM  r   rN  r   r1  gateway_stater1      ✓ z service restarted (PID )Tstartup_failedexit_reasonzstartup failed   ⚠ z  service process restarted (PID z), but gateway startup failed:    ⏳ z service process started (PID z!); waiting for gateway runtime...
activatingauto-restartrm   rQ   z& service did not become active within zs.
  Check status: sudo z1hermes gateway status
  Check logs:   journalctl --user -u z -l --since '2 min ago')r   r6  _service_scope_label
capitalizer   rU  r   r   r   ru   rZ  rg  printr   _systemd_unit_is_start_limited_print_systemd_start_limit_waitr;   )r,  rh  rJ   r   ri   scope_labelr   printed_runtime_waitrV  active_state	sub_statenew_pidr   runtime_staterj  reasons                   r0   !_wait_for_systemd_service_restartr    s    KKK


C&v..99;;Kt~')H 
$.

X
%
%-V<<<yy33IIj"--		666666%o''GG 	 	 	GGG	 	:2599G8## 0L0G|4K4K ? H H!.!4" 9 9/ J J I--PPPgPPPQQQ4 $444+1r66%  *)  |{||G||tz||   !5+ 0t{tt'ttt   ,0(<''I,G,GDJqMMM)%00 	+6::::5
1W $.

X
%
%Z 
	g{ 	g 	g#g,, 	g 	g&,477"	g 	g7=&Eii2	g 	gJM	g 	g 	g  
 5s   B, ,B;:B;c                     |                      dd                                          }|                      dd                                          }|dk    p|dk    S )NrO  r   rN  start-limit-hit)r   r   )rV  rf   r~  s      r0   ry  ry  Q  sW    YYx$$**,,F		*b))//11I&&H)7H*HHr2   excc                 8   g }dD ]c}t          | |d           }|st          |t                    r|                    d          }|                    t          |                     dd                    |                                          }d|v pd|v pd|v S )N)r  rV   outputr   r   r   r  z"start request repeated too quicklyzstart-limit)getattrra  bytesr   r   r8   r   r   )r  rh   attrrC  rI   s        r0   $_systemd_error_indicates_start_limitr  W  s    E. ! !T4(( 	eU## 	3LL	L22ESZZ    99U!!##DT! 	!/47	!D r2   c                 <    t          t          |                     S r\  )ry  rU  r.  s    r0   !_systemd_service_is_start_limitedr  h  s    )*Gv*V*V*VWWWr2   c                 T   t                      }t          |                                           }| rdnd}| rdnd}| rdnd}t          d| d           t          d	           t          d
| rdnd d|            t          d| d|            t          d| d| d           d S )N	 --systemr   z
systemctl zsystemctl --user zjournalctl zjournalctl --user rp  z0 service is temporarily rate-limited by systemd.zC  systemd is refusing another immediate start after repeated exits.z7  Wait for the start-limit window to expire, then run: rs  hermes gateway restartz&  Or clear the failed state manually: reset-failed z  Check logs: ru  z -l --since '5 min ago')r6  rv  rw  rx  )r,  ri   r{  
scope_flagsystemctl_prefixjournal_prefixs         r0   rz  rz  l  s    


C&v..99;;K &.BJ'-F||3F&,F]]2FN	
N
N
N
NOOO	
OPPP	VB[''Y[s}   

W3C
W
WRU
W
WXXX	
J>
J
Jc
J
J
JKKKKKr2   c                    t          |           }|sdS 	 ddlm} n# t          $ r Y dS w xY w |            pi }|                    d          sdS |                    dd          }|                    dd          }|                    d	d          }|                    d
d          }|dk    r&|dk    r t          d           t          | |          S |dk    r|t          t                    k    s|dk    rt                      }	t          |                                           }
t          d|
                                 d           t          d|	g| dd           t          d|	g| dd           t          | |          S dS )zARecover a planned service restart that is stuck in systemd state.r.  Fr   r_  restart_requestedrM  r   rN  rP  rO  rq  rr  uG   ⏳ Service restart already pending — waiting for systemd relaunch...r,  rh  failedz	exit-codeu&   ↻ Clearing failed state for pending z service restart...reset-failed   r,  checkrJ   startZ   )rU  r   r`  ru   r   rx  r  r8   r   r6  rv  rw  r   r5  )r,  rh  rV  r`  r  r}  r~  exec_main_statusrf   ri   r{  s              r0    _recover_pending_systemd_restartr  {  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==??][5F5F5H5H]]]	
 	
 	
 	S!		
 	
 	
 	
 	cN		
 	
 	
 	
 1%
 
 
 	

 5s    
++r  c                    |                                  D ]}|                                }|                    d          s|                    d          r|                    dd          }t	          |          dk    rn|d                                                             d                              d          }	 t          |          }|dk    r|nd	c S # t          $ r Y  d	S w xY wd	S )
a  Extract the PID from ``launchctl list <label>`` output.

    When launchd is actively supervising a process, the output includes a
    ``"PID" = <number>;`` line.  When the service definition is only *registered*
    but not running (macOS 26+ with an unmanageable domain, fallback active),
    the output lacks a PID field entirely.  Returns ``None`` when no PID is
    found or the PID is non-positive (e.g. ``-1`` for a recently-crashed service).
    z"PID"PIDr>  rm   rQ   ;"r   N)rX   rW   r   rY   rc   rstripr;   r\   )r  rg   r   rh   valrA   s         r0   rb   rb     s     !!## 
  
 ::<<w'' 	 8+>+>u+E+E 	 NN3**E5zzQAhnn&&--c2288== c((C"%''33t333!      444 4s   ?C
C)(C)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    rdS t          | j                  duS )	u}  Return True when launchd is actively supervising the gateway process.

    ``launchctl list <label>`` returns exit 0 whenever the service definition is
    registered with launchd — even when ``state = not running`` (macOS 26+).
    We additionally require a PID in the output to confirm launchd is actually
    managing a live process, not just holding a static definition.
    FrN   rO   Tr   rG   r   N)	get_launchd_plist_pathr4  rT   rU   r`   r]   ra   rb   rV   )rf   s    r0   _probe_launchd_service_runningr    s     "##**,, u&"3"5"56	
 
 
 $   uuAu.v}==TIIs   'A AAc                    t          t                                }t                      rt          d|          S ddlm} t                      r |            r	 ddlm}m	}  |            dk    rt                      pd}d| } |            }d	}d	}		 t          |d
d          }
|
|
|z                                  }n# t          $ r d	}Y nw xY w|r6	 t          |                    |                    }	n# t          $ r d	}	Y nw xY wt          d||	|d          S n# t          $ r Y nw xY w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_containerdetect_service_managerget_service_managers6r   gateway-FscandirNzs6 (container supervisor))r$   r%   r&   r(   r)   zdocker (foreground)r.  z	systemd (rl  launchdzmanual process)r:   r   	is_termuxr#   hermes_constantsr  is_linuxhermes_cli.service_managerr  r  _profile_suffixr  is_dirru   r-   
is_runningrS   r:  rv  r3  r4  r_   r  r  )r,  r(   r  r  r  r?   service_namemgrr%   r&   service_dirr8  r{  s                r0   get_gateway_runtime_snapshotr    s   *,,--L{{ 
%-%
 
 
 	

 .-----zz #
llnn #
	^^^^^^^^%%''4//)++8y3'33))++$)!"'.")#y$"?"?K".-8<-G,O,O,Q,Q)  . . .(-%%%.$ 00*.s~~l/K/K*L*L$ 0 0 0*/0-7&7$3!-"&   # 00  	 	 	D	%)%
 
 
 	

 !"" 	
+IQW+X+X+X(*?;;%....3?KKKRRTT+%%
 
 
 	
 zz 
%466==??:<<%#
 
 
 	
 " !   sZ   9D *B? >D ?CD CD "C7 6D 7DD DD 
D+*D+rP   limitr  c                    |d | d |         D             nd | D             }|(t          |           |k    r|                    d           d                    |          S )Nc                 8    g | ]}|d k    t          |          S r   r8   r   rA   s     r0   r   z(_format_gateway_pids.<locals>.<listcomp>2  s#    555cS1WWSWWWr2   c                 8    g | ]}|d k    t          |          S r  r  r  s     r0   r   z(_format_gateway_pids.<locals>.<listcomp>4  s#    2223#''c#hh'''r2   z..., )rc   r   r   )rd   r  rendereds      r0   _format_gateway_pidsr  .  sz    
  	65T&5&\555522$222 
 SYY..99Xr2   snapshotc                    | j         sd S t                       t                      rUt          d           t          dt          | j        d                       t          d           t          d           d S t          d           t          dt          | j        d                       t          d           t          d           d S )	NuU   ⚠ Gateway is running as a detached fallback process — launchd cannot supervise itz
  PID(s): r  zB  Auto-start at login and auto-restart on crash are NOT available.#  Stop it with: hermes gateway stopuN   ⚠ Gateway process is running for this profile, but the service is not activezI  This is usually a manual foreground/tmux/nohup run, so `hermes gateway`z<  can refuse to start another copy until this process stops.)r4   rx  "_launchd_unsupported_marker_existsr  r(   )r  s    r0   _print_gateway_process_mismatchr  ;  s    0 	GGG *++ N*	
 	
 	
 	T/0ETRRRTTUUURSSS344444\	
 	
 	
 	T/0ETRRRTTUUUYZZZLMMMMMr2   c                     	 ddl m}   |             fdt                      D             }|sdS t                       t          d           |D ]"}t          d|j        dd|j                    #dS # t          $ r Y dS w xY w)	a  Print a summary of gateway status across all profiles.

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

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

    Args:
        force: Use the platform's force-kill mechanism instead of graceful terminate.
        exclude_pids: PIDs to skip (e.g. service-managed PIDs that were just
            restarted and should not be killed).
        all_profiles: When ``True``, kill across all profiles.  Passed
            through to :func:`find_gateway_pids`.
    )r   r   r   r  rm   "   ⚠ Permission denied to kill PID zFailed to kill PID : N)r   r   r   r   rx  r   )r  r   r   rd   killedrA   r  s          r0   kill_gateway_processesr    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            	      r   	 t                      rdS n# t          $ r Y dS w xY wddlmm}  t          j                    h}	 d t          |          D             }n# t          $ r Y dS w xY w|sdS d}|D ]o}	  | |           n# t          $ r Y nw xY w	 t          j        |t          j
                   n-# t          $ r Y Lt          $ r t          d|            Y iw xY wd}pt          j                    dz   }t!          |          }|rTt          j                    |k     r=fd	|D             }|rt          j        d
           |rt          j                    |k     =|D ]S}	 t          j        |t%          t          dt          j
                             7# t          t          t&          f$ r Y Pw xY w|S )u:  Kill no-supervisor gateway orphans the pidfile/runtime record can't see.

    On WSL/no-systemd hosts the manual restart fallback runs the gateway
    in-process under a ``gateway restart`` argv (hermes_cli/gateway.py restart
    branch → ``run_gateway()``). If its pidfile or runtime record goes missing
    or stale, ``get_running_pid()`` returns ``None`` even though a live orphan
    still holds the webhook port, so a follow-up restart stacks a duplicate on
    the same port (#51325). This is a no-op on hosts WITH a service supervisor,
    where a ``gateway restart`` argv is a transient management command, not the
    running gateway — gating on ``supports_systemd_services()`` keeps the
    orphan-aware scan from killing live management processes there.

    Returns True if at least one orphan was reaped.
    Fr   )r   write_planned_stop_markerc                 "    g | ]}||d k    
|S r  r'   r   r   s     r0   r   z6_reap_unsupervised_gateway_orphans.<locals>.<listcomp>  s"    QQQQQ1q551555r2   )r   u3   ⚠ Permission denied to kill orphaned gateway PID T      @c                 *    g | ]} |          |S r'   r'   )r   r   r   s     r0   r   z6_reap_unsupervised_gateway_orphans.<locals>.<listcomp>  s&    <<<1[[^^<Q<<<r2   皙?SIGKILL)rS   ru   r   r   r  r~   r   r   r   r   SIGTERMr   r   rx  r   r   rO   r   r  r   )r  ownorphansreapedrA   r   	survivorsr   s          @r0   "_reap_unsupervised_gateway_orphansr    sl   $&& 	5	   uu FEEEEEEE9;;-C RQ/SAAAQQQ   uu uF  	%%c**** 	 	 	D		GC((((! 	 	 	H 	 	 	MMMNNNH	 
 ~#%HWI
 ((833<<<<	<<<	 	JsOOO  ((833   	GCFNCCDDDD"OW= 	 	 	D	 MsZ    
""A 
A+*A+9B
BBB66
C C C &3FF43F4c                     	 ddl m} m} n# t          $ r Y dS w xY w |             }|t	                      S 	 ddl m}  ||           n# t          $ r Y nw xY w	 t          j        |t          j
                   n.# t          $ r Y n"t          $ r t          d|            Y dS w xY wddl}ddl m} t!          d          D ]$} ||          s n|                    d	           % |             
 |             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.

    On hosts without a service supervisor (e.g. WSL/no-systemd, where the
    manual restart fallback runs the gateway in-process under a ``gateway
    restart`` argv), the pidfile/runtime record can be missing or stale while
    a live orphan still holds the webhook port. In that case fall back to the
    orphan-aware process scan so the replacement reaps the prior instance
    instead of stacking a duplicate on the same port (#51325).
    r   )r   remove_pid_fileFN)r  r  r      r   T)r   r   r  rt   r  r  ru   r~   r   r   r  r   r   rx  r   r   r   r   )r   r  rA   r  r   r   r   s          r0   stop_profile_gatewayr    s   CCCCCCCCC   uu /

C
{1333<<<<<<!!#&&&&   
V^$$$$      8388999uu ******2YY  {3 	EC 4s5    
A	 	
AAA: :
B%B%$B%c                  @    t           j                            d          S )Nlinux)r  r  r   r'   r2   r0   r  r  )  s    <""7+++r2   )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'   r2   r0   _wsl_systemd_operationalr  0  s      t,,,,r2   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-runningTrF   r0  >   r1   degradedstartinginitializingF)r5  rV   rW   r   r7  rT   r]   r   )r,  rf   statuss      r0   r  r  9  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 )ur  Return True when a container exposes working user or system systemd.

    This is NOT our Hermes Docker image — that one runs s6-overlay as
    PID 1 (since Phase 2 of the s6-overlay supervision plan) and is
    detected via ``service_manager.detect_service_manager() == "s6"``.
    This function handles the "container managed by something else"
    case: systemd-nspawn, certain k8s pods, containers built FROM
    systemd-bearing distros where the user has wired systemd as their
    init. In those environments systemctl behaves identically to the
    host case, so we fall through to the normal systemd code paths.
    Fr.  Tr  r'   r2   r0   _container_systemd_operationalr  J  s9     5))) t4((( t5r2   c                      t                      rt                      rdS t          j        d          dS t	                      rt                      S t                      rt                      S dS )NFrC   T)r  r  rw   rx   r  r  r  r  r'   r2   r0   rS   rS   ]  sh    ::  u|K  (uxx *')))~~ 0-///4r2   c                  "    t           j        dk    S )Ndarwinr  r  r'   r2   r0   r_   r_   i  s    <8##r2   c                  "    t           j        dk    S )Nr  r  r'   r2   r0   rv   rv   m  s    <7""r2   c                  B   t                      sdS t          j        dd                                                                          } | dv rdS 	 t          t          j        ot          j                                                   S # t          t          f$ r Y dS w xY w)a^  Return True for detached Windows gateway runs that should ignore Ctrl+C.

    Foreground ``hermes gateway run`` must remain interruptible from
    PowerShell/CMD. Detached service-style launches opt in via
    ``HERMES_GATEWAY_DETACHED=1``; older wrappers without the env marker are
    treated as detached when no interactive stdin is attached.
    FHERMES_GATEWAY_DETACHEDr   >   1onyestrueT)rv   r~   getenvrW   r   r-   r  stdinisattyr\   r   )detacheds    r0   /_windows_gateway_should_absorb_console_controlsr  q  s     << uy2B77==??EEGGH---t	8ci&6&6&8&89999    tts   7B	 	BB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   r  rm   ^[a-z0-9][a-z0-9_-]{0,63}$   )hashlibrer  r  r   r   relative_torh   rc   matchr\   sha256r8   encode	hexdigest)r  r  r  homer   profiles_rootrelrh   s           r0   r  r    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_homedefault_rootc                    ddl }ddlm} t          | pt	          t                                                                          }|r!t          |                                          n |                                            }||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).
        default_root: Optional Hermes root to compare against. Used when
            generating a system service for another user from a sudo/root
            process, where ``Path.home()`` and ``get_default_hermes_root()``
            refer to root but the target profile lives under the service user.
    r   Nr  r   r  rm   r  r   )r  r  r  r   r8   r   r   r  rh   rc   r  r\   )	r!  r"  r  r  r  r   r  r   rh   s	            r0   r   r     s%    III8888885s?#4#45566>>@@D.:cd<  ((***@W@W@Y@Y@a@a@c@cGwrz)2244M}--	u::??rxx(EuQxPP?*a***   2s   &AC= =
D
	D
target_home_dirc                    t          |          dz  }	 t          |                                                               |                                           t          | |          S # t          $ r t          |           cY S w xY w)zDReturn the profile arg for a system service running as another user..hermes)r"  )r   r   r  r   r\   )r!  r$  target_roots      r0   _profile_arg_for_target_userr(    s    '')3K)[!!##//0C0C0E0EFFFKkBBBB ) ) )K((((()s   AA+ +BBc                  H    t                      } | st          S t           d|  S )a  Derive a systemd service name scoped to this HERMES_HOME.

    Default ``~/.hermes`` returns ``hermes-gateway`` (backward compatible).
    Profile ``~/.hermes/profiles/coder`` returns ``hermes-gateway-coder``.
    Any other HERMES_HOME appends a short hash for uniqueness.
    -)r  _SERVICE_BASEsuffixs    r0   r6  r6    s1     F &&f&&&r2   c                     t                      }| rt          d          | dz  S t          j                    dz  dz  dz  | dz  S )N/etc/systemd/systemrK   .configsystemduser)r6  r   r  )r,  r   s     r0   r3  r3    s[    D ?)**->->->>>9;;"Y.7T:K:K:KKKr2   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)r5   r6   r7   __doc__r'   r2   r0   r4  r4    s           r2   r4  c                       e Zd ZdZdefdZdS )SystemScopeRequiresRootErrora'  Raised when a system-scope gateway operation is attempted as non-root.

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

    ``args[0]`` carries the user-facing message, ``args[1]`` the action name.
    ``str(e)`` returns only the message (not the tuple repr) so format
    strings like ``f"Failed: {e}"`` render cleanly.
    r*   c                 .    | j         r| j         d         ndS )Nr   r   )r  r.   s    r0   __str__z$SystemScopeRequiresRootError.__str__  s    #y0ty||b0r2   N)r5   r6   r7   r5  r8   r9  r'   r2   r0   r7  r7    s9          1 1 1 1 1 1 1r2   r7  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r~   rG  r   getuidr   xdgs    r0   _user_dbus_socket_pathrB    s;    
*..*
+
+
I/IBIKK/I/IC99ur2   c                      t           j                            d          pdt          j                     } t	          |           dz  dz  S )zJReturn the per-user systemd private socket path (regardless of existence).r;  r<  r1  privater>  r@  s    r0   !_user_systemd_private_socket_pathrE    s@    
*..*
+
+
I/IBIKK/I/IC99y 9,,r2   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.
    )rB  r4  rE  r'   r2   r0   _user_systemd_socket_readyrG    s8     	  '')) 	8,..5577r2   c                     t          j                    } dt           j        vr5d|  }t          |                                          r|t           j        d<   dt           j        vr]t           j                            dd|            }t          |          dz  }|                                rd| t           j        d<   dS dS dS )a  Ensure DBUS_SESSION_BUS_ADDRESS and XDG_RUNTIME_DIR are set for systemctl --user.

    On headless servers (SSH sessions), these env vars may be missing even when
    the user's systemd instance is running (via linger).  Without them,
    ``systemctl --user`` fails with "Failed to connect to bus: No medium found".
    We detect the standard socket path and set the vars so all subsequent
    subprocess calls inherit them.
    r;  r<  DBUS_SESSION_BUS_ADDRESSr=  z
unix:path=N)r~   r?  rG  r   r4  r   )uidruntime_dirxdg_runtimebus_paths       r0   _ensure_user_systemd_envrN     s     )++C
**(3((##%% 	8,7BJ()!33jnn%68JS8J8JKK$$u,?? 	M5L(5L5LBJ1222	 43	M 	Mr2         @c                     ddl } |j                    | z   } |j                    |k     rAt                      rt                       dS  |j        d            |j                    |k     At                      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   NTr  )r   r   rG  rN  r   )rJ   r   r   s      r0   _wait_for_user_dbus_socketrQ  6  s     KKKt~')H
$.

X
%
%%'' 	$&&&4
3	 $.

X
%
%
 &'''r2   )auto_enable_lingerrR  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   TrO  rJ   zGUser systemd control sockets are missing even though linger is enabled.z  systemctl start user@zC.service
  (may require sudo; try again after the command succeeds))r  fix_hintloginctlenable-lingerFr  rH   rI   r  rJ   r  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)rN  rG  getpassgetuserget_systemd_linger_statusrQ  _raise_user_systemd_unavailabler~   r?  rw   rx   rT   rU   ra   rx  r6  r  rV   rW   ru   )rR  r[  usernamelinger_enabledlinger_detailrf   detailr  s           r0   _preflight_user_systemdrc  H  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M2M&:K2M2Megg  ,EVEED(DD    /  	 	 	+@@@@D(DD        	: $72!27 7 7 =(<<     s   E 
E9E44E9r_  r  rU  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)r4  )r_  r  rU  msgs       r0   r^  r^    s:    
  	! 	! 		! 	! 	!  &c
*
**r2   c                 4    | st                       | rdgnddgS )NrC   rD   )rN  r.  s    r0   _systemctl_cmdrg    s,     # """"?K==h(??r2   c                     | rdgnddgS )N
journalctlrD   r'   r.  s    r0   _journalctl_cmdrj    s    #AL>>,)AAr2   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)rT   rU   rg  r^   r7  )r  r,  kwargss      r0   r5  r5    s]    R~nV44t;FFvFFF R R RFGGTQRs   !$ ?c                     | rdndS )Nr,  r2  r'   r.  s    r0   rv  rv    s    )886)r2   c                      g } t                      }dD ]X\  }}t          |          }||v r|                                r*|                     |           |                    |           Y| S )N))Fr2  )Tr,  r.  )rR   r3  r4  r   r[   )scopes
seen_pathsr,  rj   	unit_paths        r0   get_installed_systemd_scopesrr    s    FEEJ< & &)888	
"" 	&MM%   NN9%%%Mr2   c                  @    t          t                                dk    S )Nrm   )rc   rr  r'   r2   r0   has_conflicting_systemd_unitsrt    s    +--..22r2   )zhermes.service_LEGACY_SERVICE_NAMES)zhermes_cli.main gatewayzhermes_cli/main.py gatewayzgateway/run.pyz 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.
    Fr0  r1  r2  Tr/  )r   r  r'   r2   r0   _legacy_unit_search_pathsrx    s<     
	i')3f<=	t)**+ r2   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   r  rI   s     r0   	<genexpr>z,_find_legacy_hermes_units.<locals>.<genexpr>  s'      SS&v~SSSSSSr2   )	rx  ru  r4  	read_textr   r   anyrv  r   )results	is_systembaser   rq  rI   s        @r0   _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'   r2   r0   has_legacy_hermes_unitsr  #  s    )++,,,r2   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,  r2  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   r@   r  scopes        r0   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3r2   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       r0   r   z.remove_legacy_hermes_units.<locals>.<listcomp>T  s'    BBB\Q66B1a&BBBr2   c                 "    g | ]\  }}}|||fS r'   r'   r  s       r0   r   z.remove_legacy_hermes_units.<locals>.<listcomp>U  s'    @@@|q!V@QF@@@r2   z$Legacy Hermes gateway unit(s) found:r,  r2  r  r  r  u   (dry-run — nothing removed)c                     g | ]\  }}}|	S r'   r'   r   r   r   s      r0   r   z.remove_legacy_hermes_units.<locals>.<listcomp>`      +++Aq1+++r2   zRemove these legacy units?Tz6Skipped. Run again with: hermes gateway migrate-legacyc                     g | ]\  }}}|	S r'   r'   r  s      r0   r   z.remove_legacy_hermes_units.<locals>.<listcomp>d  r  r2   stopFr  r  disabler  
missing_oku     ✓ Removed rm   u     ⚠ Could not remove r  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  rx  r   r5  unlinkr   r7  r   r~   geteuidr   r   rc   r   )r  r  r  
user_unitssystem_unitsr   r@   r  r  removed	remaininger   s                r0   remove_legacy_hermes_unitsr  ;  s   ( '((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""D)$eR    KK4K0001411222qLGG. + + +?D??A??@@@$$T********+0UTVWWWWW    
GGG <9~~TTT	
 	
 	
 	
 	::::;;;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 )
NrQ   z + z5Both user and system gateway services are installed (rZ  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)rr  rc   r   r   r   )ro  rendered_scopess     r0   $print_systemd_scope_conflict_warningr    s    )++F
6{{Qjj((OSSSS   WXXXV   %&&&-...;<<<<<r2   actionc                 \    t          j                    dk    rt          d|  d|           d S )Nr   zSystem gateway z! requires root. Re-run with sudo.)r~   r  r7  )r  s    r0    _require_root_for_system_servicer    s>    	z||q*GfGGG
 
 	
 r2   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pwdr~   r  r\  rW   r\   r   r   getpwnamKeyErrorgetgrgidpw_gidgr_namepw_dir)r  r[  r  r  r_  	user_infor  
group_names           r0   _system_service_identityr    so   NNNJJJJJJ 	 	9[!!	9V	 9Y	 ??egg   
N
 
 	
 6+ C
 
 	
 6BCCCd	
 	
 	
=LL**		 = = =4(44551<= i.//7JZ!111s   >C 
C6C11C6rq  c                    |                                  sd S |                     d                                          D ]K}|                    d          r4|                    dd          d                                         }|pd c S Ld S )Nr   r   zUser=r>  rm   )r4  r|  rX   r   rY   rW   )rq  rg   rC  s      r0   _read_systemd_user_from_unitr    s     t##W#55@@BB ! !??7## 	!JJsA&&q)//11E=D   	! 4r2   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  )r~   r  rW   )	candidates    r0   _default_system_service_userr    s    i,,bi.?.?9AUAUV % %	 	%** 	%y/@/@F/J/J??$$$$$4r2   c                      t          j                    dk    } | s4t          dddgd          }|dk    rt          d           dd d|         S t          dg d	d          }dd
d d|         S )Nr   z6  Choose how the gateway should run in the background:PUser service (no sudo; best for laptops/dev boxes; may need linger after logout)Skip service install for nowr   zb  Tip: for a boot-time system service, re-run setup as root (e.g. from a root shell or `sudo -i`).r2  )r   rm   )r  z9System service (starts on boot; runs as your chosen user)r  r,  )r   rm   rQ   )r~   r  r   r   )is_rootchoices     r0   "prompt_linux_gateway_install_scoper    s     jllaG ,Db. 
 
 
 Q;;9   d##F++@	
 	
 	

   F (t,,V44r2   enable_on_startupc                 v   t                      }|dS |dk    rt                      }t          j                    dk    r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   z  System service install requires root. Re-run setup from a root shell, or install a user service instead: hermes gateway installFTz/  Run the system gateway service as which user?r   r  z  Enter a username.)r  r,  r  r  )r  r,  r  )	r  r  r~   r  r   r   rW   r   systemd_install)r  r  r  r  s       r0    install_linux_gateway_from_setupr  	  s   .00E}{244:<<1 X   %< 	33$Er    +0b7799 12223 	eDk]nooood{%ARSSSS$;r2   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 platformrV  )Nloginctl not foundr  r  r   N)Nz could not determine current userz	show-userz--property=LingerrM   TFr   rX  rY  zloginctl query failedr   >   r	  r  r  )Tr   >   rX  nofalse)Fr   z<empty>zunexpected loginctl output: )r  r  rw   rx   r~   r  r  getpwuidr?  pw_nameru   rT   rU   r8   ra   r  rV   rW   r   )r_  r  rf   r  rb  rC  r  s          r0   r]  r]  5	  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 (rl  z>  If you want the gateway user service to survive logout, run:z#  sudo loginctl enable-linger $USERN)r]  rx  )r`  ra  s     r0   print_systemd_linger_guidancer  g	  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4r2   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  r~   r?  r  )r  s    r0   _launchd_user_homer  u	  s0     JJJRY[[))0111r2   c                  d    t                      } | rd|  nd}t                      dz  dz  | dz  S )u   Return the launchd plist path, scoped per profile.

    Default ``~/.hermes`` → ``ai.hermes.gateway.plist`` (backward compatible).
    Profile ``~/.hermes/profiles/coder`` → ``ai.hermes.gateway-coder.plist``.
    ai.hermes.gateway-ai.hermes.gatewayLibraryLaunchAgentsz.plist)r  r  )r-  r   s     r0   r  r  	  sI     F,2K((((8KD)+n<$NNr2   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   r  r~   rG  r   PROJECT_ROOT)r  _virtual_envr  s      r0   _detect_venv_dirr  	  s     zS_$$CJ;;== 	K
 :>>-00L L!!;;== 	K '  	i';;== 	KKK	 4r2   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  rv   r4  r8   r  r$  )r  venv_pythons     r0   r  r  	  sh    D<< 	2*\9KK,1K 	${###>r2   r  path_entriesc                     t          | dz  dz            t          | dz  dz            t          | dz  dz            t          | dz  dz            g}fd|D             S )zKReturn user-local bin dirs that exist and aren't already in *path_entries*.z.localr  z.cargogoz.npm-globalc                 ^    g | ])}|vt          |                                          '|*S r'   )r   r4  )r   r   r  s     r0   r   z+_build_user_local_paths.<locals>.<listcomp>	  s8    PPP!Ql%:%:tAww~~?O?O%:A%:%:%:r2   r  )r  r  
candidatess    ` r0   _build_user_local_pathsr  	  sz     	D8Oe#$$D8Oe#$$D4K%  D= 5())	J QPPPzPPPPr2   c                    t                      sg S g }t          j                            dd                              t          j                  D ],}|                    d          r|                    |           -dD ]L}t          j	        |          }|r4|                    t          t          |          j                             MdD ]8}t          |                                          r|                    |           9g }t          |           }|D ]2}|r.||vr*|                    |           |                    |           3|S )av  Return WSL Windows interop PATH entries for generated systemd units.

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

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

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

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

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

    When installing a system service via sudo, get_hermes_home() resolves to
    root's home.  This translates it to the target user's equivalent path:
      /root/.hermes                    → /home/alice/.hermes
      /root/.hermes/profiles/coder     → /home/alice/.hermes/profiles/coder
      /opt/custom-hermes               → /opt/custom-hermes  (kept as-is)
    r&  )r   r   r   r  r8   r  r\   )r$  current_hermescurrent_defaulttarget_defaultr  s        r0   _hermes_home_for_target_userr  

  s     %&&..00Ny{{Y.7799O/**Y6N ((>"""#!--o>>>H,--- # # #>"""""#s   1&B B43B4project_rootc                    | t           } dt          dt          fd}g }| dz  dz  } ||          r#|                    t	          |                     nVt
          j        t
          j        k    r<|                    t	          t          t
          j                  dz                       | dz  dz  } ||          r"|                    t	          |                     t                      }|d	z  dz  } ||          r"|                    t	          |                     |dz  dz  } ||          r"|                    t	          |                     |S )
zIBuild PATH directory list for service units, excluding non-existent dirs.Nr@   r*   c                 N    	 |                                  S # t          $ r Y dS w xY wr  )r  r   )r@   s    r0   _is_dirz)_build_service_path_dirs.<locals>._is_dir)
  s7    	;;==  	 	 	55	s    
$$r  r  node_modulesz.binnode)	r  r   r-   r   r8   r  r  r  r   )r  r  r  venv_binnode_binr!  hermes_node	hermes_nms           r0   _build_service_path_dirsr  $
  sr   #d t     Jf$u,Hwx 9#h--((((	s	&	&#d3:..677888n,v5Hwx )#h--(((!##K&.Kw{ ,#k**+++n,v5Iwy *#i..)))r2   c                     	 t                      } | rOt          |                                           r.t          t          |                                                     S n# t
          $ r Y nw xY wt          t                    S )uo  Return a WorkingDirectory that will not disappear out from under systemd.

    The gateway does NOT need its cwd to be the source checkout — ``ExecStart``
    uses an absolute python interpreter and ``-m hermes_cli.main``, so module
    resolution does not depend on cwd. Pinning ``WorkingDirectory`` to
    ``PROJECT_ROOT`` (``Path(__file__).parent.parent``) is actively harmful:
    when the unit is generated from a transient checkout — a ``.worktrees/``
    dir, or a clone that ``hermes update`` later relocates/removes — the path
    rots. systemd then fails the start at the CHDIR step (``status=200/CHDIR``,
    "Changing to the requested working directory failed") *before* Python
    loads, so the on-boot ``refresh_systemd_unit_if_needed()`` self-heal never
    runs and ``Restart=always`` crash-loops forever on a dead directory.

    ``HERMES_HOME`` is the stable anchor: it is where config/state/logs live,
    it never moves, and it is guaranteed to exist whenever the gateway is
    meaningfully installed. Fall back to ``PROJECT_ROOT`` only if HERMES_HOME
    cannot be resolved (it always can in practice).
    )r   r   r  r8   r   ru   r  )r  s    r0   _stable_service_working_dirr
  F
  s    &   	-DJJ%%'' 	-tDzz))++,,,   |s   AA" "
A/.A/c                    t                      }t                      }t                      }|rt          |          nt          t          dz            }t                      }t          j        d          }|r:t          t          |          j	                  }||vr|
                    |           g d}	t          t                      pd          }
t          d|
          dz   }| rpt          |          \  }}t                    }t!          |          }t#          |          }|rt          |          nt#          |          }t#          |          }fd|D             }|                    t'          t                    |                     |                    t)          |                     |                    |	           d                    |          }d	                    g d
t,           d| d| d| d|rd| nd	 d| d d| d| d| d| d| dt.           d| d| d          S t          t1                                                                }t5          |          }|                    t'          t          j                    |                     |                    t)          |                     |                    |	           d                    |          }d
t,           d| d|rd| nd	 d| d| d| d| dt.           d| d| dS )Nr  r  )z/usr/local/sbinz/usr/local/binr	   r   r   r   r   <   r  c                 0    g | ]}t          |          S r'   )r  )r   r   home_dirs     r0   r   z)generate_systemd_unit.<locals>.<listcomp>
  s$    PPPa,Q99PPPr2   :r   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   z gateway run
WorkingDirectory=z
Environment="HOME=z"
Environment="USER=z"
Environment="LOGNAME=z"
Environment="PATH=z"
Environment="VIRTUAL_ENV=z"
Environment="HERMES_HOME=z5"
Restart=always
RestartSec=5
RestartForceExitStatus=zU
KillMode=mixed
KillSignal=SIGTERM
ExecReload=/bin/kill -USR1 $MAINPID
ExecStopPost=-z* -m gateway.cgroup_cleanup
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  r
  r  r8   r  r  rw   rx   r   r   r   r;   _get_restart_drain_timeoutr   r  r  r(  r  r  r  r  r   SERVICE_DESCRIPTIONr   r   r   r   r  )r,  r  python_pathworking_dirdetected_venvvenv_dirr  resolved_noderesolved_node_dircommon_bin_paths_drain_timeoutrestart_timeoutr_  r  r!  profile_arg	sane_pathr  s                    @r0   generate_systemd_unitr  b
  s   !##K-//K$&&M%2Rs=!!!L6<Q8R8RH+--LL((M 3  ] 3 3 :;;L00 1222   355:;;N"n--2O 1)A+)N)N&*h28<<2;II +;AA +6fc+&&&;OP[]e;f;f'(;;PPPP<PPP3DNNLQQRRR4\BBCCC,---HH\**	                      AL+S+<{+<+<+<QS                         #!   " &#   ( :)   0 1   2  3      	B o''//1122K{++K/	\JJKKK0>>???()))&&I     AL+S+<{+<+<+<QS      #  &  : & ' (  )   r2   rI   c                     d                     d |                                                                 D                       S )Nr   c              3   >   K   | ]}|                                 V  d S r,   )r  )r   rg   s     r0   r{  z0_normalize_service_definition.<locals>.<genexpr>
  s*      IItT[[]]IIIIIIr2   )r   rW   rX   )rI   s    r0   _normalize_service_definitionr   
  s7    99IItzz||/F/F/H/HIIIIIIr2   )RestartMaxDelaySecRestartStepsc                 R   |                                  }g }|D ]z}|                                }|rM|                    d          s8|                    dd          d                                         }|t          v re|                    |           {d                    |          S )z9Remove systemd directives that older hosts silently drop.#r>  rm   r   r   )rX   rW   r   rY   _SYSTEMD_OPTIONAL_DIRECTIVESr   r   )rI   linesfilteredrg   r   rB  s         r0   "_strip_optional_systemd_directivesr(  
  s    OOEH  ::<< 	H//44 	..a((+1133C22299Xr2   c                 d    ddl }t          |           }|                    dd||j                  S )a`  Normalize launchd plist text for staleness checks.

    The generated plist intentionally captures a broad PATH assembled from the
    invoking shell so user-installed tools remain reachable under launchd.
    That makes raw text comparison unstable across shells, so ignore the PATH
    payload when deciding whether the installed plist is stale.
    r   Nz,(<key>PATH</key>\s*<string>)(.*?)(</string>)z\1__HERMES_PATH__\3flags)r  r   subS)rI   r  
normalizeds      r0   '_normalize_launchd_plist_for_comparisonr/  
  sC     III.t44J667d	    r2   c                 >   t          |           }|                                sdS |                    d          }| rt          |          nd }t	          | |          }t          t          |                    }t          t          |                    }||k    S )Nr.  Fr   r  r,  r  )r3  r4  r|  r  r  r   r(  )r,  rq  	installedexpected_userexpectednorm_installednorm_expecteds          r0   systemd_unit_is_currentr7    s    %V444I u##W#55I?EO0;;;4M$FNNNH 3*955 N 2*844 M ]**r2   
definitionc                 z   ddl }ddl}|                    d|           }||                    d| |j                  z  }t	          |                                                                          t	          d          t	          d          t	          d          t	          d	          h}|D ]}	 t	          |                                                    d
                                                    }n# t          t          f$ r Y ]w xY w|D ])}||k    s	||j
        v r|                                c c S *dS )u  Return the temp-dir HERMES_HOME baked into a service definition, or None.

    A generated systemd unit / launchd plist carries the resolved HERMES_HOME
    in its environment block. If that path lives under the system temp dir,
    the definition was almost certainly generated by a test/E2E harness that
    exported a throwaway ``HERMES_HOME=/tmp/...`` — writing it to the real
    service file silently breaks the user's gateway on the next (re)start:
    the gateway comes back "active (running)" but pointed at an empty temp
    home ("No messaging platforms enabled"), deaf to every platform.
    Seen live 2026-06-11: an E2E guard probe ran ``hermes gateway restart``
    with ``HERMES_HOME=/tmp/hermes-e2e-<pr>`` exported; the restart path's
    unit refresh baked the temp path into the production unit and the
    post-update restart produced a zombie gateway for 7+ hours.

    Matches both systemd ``Environment="HERMES_HOME=..."`` lines and launchd
    ``<key>HERMES_HOME</key><string>...</string>`` pairs.
    r   NzHERMES_HOME=([^"\n]+)z/<key>HERMES_HOME</key>\s*<string>(.*?)</string>r*  z/tmpz/var/tmpz/private/tmpz/private/var/tmpr  )r  tempfilefindallr-  r   
gettempdirr   rW   r   r\   parents)r8  r  r:  r  
temp_rootsry   r  r  s           r0    _temp_home_in_service_definitionr?     sg   $ IIIOOO4jAAJ"**:Jbd    J 	X  ""##++--VZ^  J  # #	CIIKK--c2233;;==HH$ 	 	 	H	 	# 	#D448+;#;#;yy{{""""" $<	# 4s   0AC77D
Dkindc                 v    t          |           }|dS t          d| d| d           t          d           dS )zLRefuse (with guidance) when a service definition carries a temp HERMES_HOME.NFu"   ✗ Refusing to write the gateway z1: HERMES_HOME resolves to a temporary directory (rZ  zq  This usually means a test/E2E environment exported HERMES_HOME. Unset it (or run from a clean shell) and retry.T)r?  rx  )r8  r@  	temp_homes      r0   _refuse_temp_home_service_writerC  K  so    0<<Iu		.T 	. 	. )	. 	. 	.   
	:   4r2   c                    t          |           }|                                rt          |           rdS | rt          |          nd}t	          | |          }| sd|v sd|v sd|v rdS t          |d          rdS |                    |d	
           t          dg| dd           t          dt          |            d           dS )zMRewrite the installed systemd unit when the generated definition has changed.r.  FNr1  z/pytest-of-z/hermes_test"z/hermes_test/systemd unitr   r  r  Tr  r  u   ↻ Updated gateway z7 service definition to match the current Hermes install)
r3  r4  r7  r  r  rC  
write_textr5  rx  rv  )r,  rq  r3  new_units       r0   refresh_systemd_unit_if_neededrH  [  s   %V444I !8!G!G!G u?EO0;;;4M$FNNNH   !!h&&h&&u
 'x@@ uG444O$V4LLLL	t3F;;ttt   4r2   rb  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 rK   )rx  r6  )r_  rb  s     r0   _print_linger_enable_warningrK    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	GGGGGr2   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  TrV  r  z5Enabling linger so the gateway survives SSH logout...rW  Fr  rX  u8   ✓ Linger enabled — gateway will persist after logoutrY  )r  r  r[  r\  r   r4  rx  r]  rw   rx   rK  rT   rU   ru   r8   ra   r  rV   rW   )r[  r_  linger_filer`  ra  rf   r  rb  s           r0   _ensure_linger_enabledrN    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)r3  r4  r.  s    r0   r2  r2    sP     tT***1133 	=%U333::<<<r2   c                 T    t          j                    dk    rdS t          |           S )a  True when the setup wizard is about to trigger a system-scope operation
    as a non-root user.

    Replicates the decision ``_select_systemd_scope`` makes inside
    ``systemd_start`` / ``systemd_restart`` / ``systemd_stop`` so the wizard
    can detect the dead-end BEFORE prompting, rather than letting
    ``SystemScopeRequiresRootError`` propagate out and leave the user
    staring at a bare shell.
    r   Fr.  )r~   r  r2  r.  s    r0   $_system_scope_wizard_would_need_rootrQ    s+     
z||qu ////r2   c                    t                      }t          d|  d           t          d           t          d|                                  d           | dk    rt          d|            nG| dk    rt          d	|            n.| d
k    rt          d|            nt          d|  d|            t          d           t          d           t          d           t          d           dS )zPrint actionable remediation when the wizard skips a system-scope
    prompt because the user isn't root. Keeps the wizard flowing instead of
    aborting.
    u2   Gateway is installed as a system-wide service — z requires root.z
  Options:z    1. z it this time:r  z         sudo systemctl start r  z         sudo systemctl stop restartz          sudo systemctl restart z         sudo systemctl r   zC    2. Switch to a per-user service (recommended for personal use):z/         sudo hermes gateway uninstall --systemz         hermes gateway installz         hermes gateway startN)r6  r   r   rw  )r  ri   s     r0   _print_system_scope_remediationrT    s5   
 

CXXXX   |<**,,<<<===9C99::::	6		83889999	9		;c;;<<<<<f<<s<<===TUUU@AAA0111./////r2   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)
r~   r  rW   r   ra  rb  r   r8   r   r   )ry   cfg	agent_cfgs      r0   r  r    s    
)2B
7
7
=
=
?
?C 
,6sD,A,AICGGGR(((r	MM')N 
 

 's+++r2   non_interactivec                 b   |rt          d           t                      rZt                       t                       t                       |st	          dd          rt          d           t                       t          |          }|rdnd}|                                r| st          |          st          d	t          |           d
|            t          |           |r!t          dt                      g|dd           t          dt          |                                           d           d S t          d|            t          d           d S |j                            dd           t!          ||          }t#          |d          rd S t          dt          |           d|            |                    |d           t          dg|dd           |r!t          dt                      g|dd           t                       |rdnd}t          dt          |                                           d| 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.  r  r   u   ↻ Repairing outdated z systemd service at: enabler  r  rk  z service definition updatedService already installed at: Use --force to reinstallr=  exist_okr1  rE  zInstalling z systemd service to: r   r  r  zinstalled and enabledr2  z	 service !Next steps:r  rs  hermes gateway startz!              # Start the servicezhermes gateway statusz             # Check statusri  zjournalctl --userz -u z -f  # View logsConfigured to run as: )r  r  rx  r  r   r  r3  r4  r7  rv  rH  r5  r6  rw  r   mkdirr  rC  rF  r  rN  r  )
r  r,  r  r  r[  rq  r  rG  enable_labelconfigured_users
             r0   r  r    s     4(333    !### 	m,Z\`aa 	&59999GGG%V444I &.BJ % &f555 	h*>v*F*Fhh]fhh   +&9999  f*:*<*<=fTXbdeeee_-f55@@BB___```F:y::;;;()))4$777$FLLLH&x@@ 	
V,V44
V
V9
V
VWWWG444O$V4LLLL ^"2"4"45fDZ\]]]]	GGG.?P**[L	
T%f--88::
T
T\
T
T
TUUU	GGG	-	i&WWBiiJiii   
d&WWBddZddd   
fV<\\)<ffBRBTBTfff   
GGG !6yAA 	><?<<===   (***r2   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  Trk  z service uninstalled)
r2  r  r5  r6  r3  r4  r  rx  rv  rw  )r,  rq  s     r0   systemd_uninstallrm  M  s   "6**F 6(555F,../eUWXXXX	$&&'eR    &V444I *(Y(()))O$V4LLLL	
P%f--88::
P
P
PQQQQQr2   c                     t          |          }|                                sD|rdnd}t          d           t          d|rdnd d|            t          j        d           d S d S )	Nr.  r  r   $   ✗ Gateway service is not installed  Run: rs  hermes gateway installrm   )r3  r4  rx  r  exit)r  r,  rq  r  s       r0   _require_service_installedrs  `  s    %V444I $*2[[
4555U61rUUUUVVV	 r2   c                 L   t          |           } | rt          d           nt                       t          d|            t	          |            t          dt                      g| dd           t          dt          |           	                                 d           d S )Nr  r.  Tr  r  rk  z service started)
r2  r  rc  rs  rH  r5  r6  rx  rv  rw  r.  s    r0   systemd_startru  i  s    "6**F "(1111
 	 !!!wv6666"&1111G-//0tUWXXXX	
L%f--88::
L
L
LMMMMMr2   c                    t          |           } | rt          d           t          d|            t          |            	 ddlm}m}  |d          }| ||           n# t          $ r Y nw xY w	 t          dt                      g| dd	           n8# t          j        $ r& t          |           }t          d
| d           Y d S w xY wt          dt          |                                            d           d S )Nr  r.  r   r   r  Fr   Tr  r  Gateway ze service is still stopping after 90s; check `hermes gateway status` or logs for final shutdown state.rk  z service stopped)r2  r  rs  rK  r   r   r  ru   r5  r6  rT   r]   rv  rx  rw  )r,  r   r  rA   rj   s        r0   systemd_stopry  x  s~   "6**F 1(000vf5555'v6666MMMMMMMMoE222?%%c***   
%''(tR	
 	
 	
 	
 	
 $   $V,,Nu N N N	
 	
 	
 	 

L%f--88::
L
L
LMMMMMs$   !A% %
A21A26!B 1CCc                 
   t          |           } | rt          d           nt                       t          d|            t	          |            t          |            ddlm}  |            pt          |           }|t          |           
                                }t                      }t                      }t          d| d| d           t          ||dz             rOt          d	|g| d
d           t          d|g| d
d           t!          | |          rd S t#          |           rd S t          dt%          |dz              d           t          d	|g| d
d           	 t          d|g| dd           n# t&          j        $ r;}t+          |          st#          |           rt-          |            Y d }~d S  d }~wt&          j        $ r& t          |           }t          d| d           Y d S w xY wt!          | |           d S t1          | |          rd S t          d	t                      g| d
d           	 t          dt                      g| dd           n# t&          j        $ r;}t+          |          st#          |           rt-          |            Y d }~d S  d }~wt&          j        $ r& t          |           }t          d| d           Y d S w xY wt!          | |           d S )NrS  r.  r   r   rp  z$ service restarting gracefully (PID z)...rF   r  Fr  r  r  r  u-   ⚠ Graceful restart did not complete within zs; forcing a service restart...Trx  z^ service is still restarting after 90s; check `hermes gateway status` or logs for final state.)r2  r  rc  rs  rH  rK  r   r   r]  rv  rw  r6  r  rx  r   r5  r  r  r;   rT   CalledProcessErrorr  rz  r]   r  )r,  r   rA   r{  ri   r   r  rj   s           r0   systemd_restartr|    sR   "6**F "(3333!!!y8888"&1111'v6666......
/


?0???C
*622==??  244O[OOcOOOPPP(ma.?@@ 	
 %	    C 	    1SQQQ 0??? +CPQ@Q<R<R + + +	
 	
 	
 	S!		
 	
 	
 	
	Is+F$PRSSSSS, 	 	 	3  2&AAA 0v>>>>( 	 	 	(00EI5 I I I   FF	 	*cJJJJ'vCHHH 	)++,	   (**+F$PR	
 	
 	
 	
 	
 (   /
 
 	.f===	 ,6::::FFFFF$   $V,,Eu E E E	
 	
 	
 	 &V#FFFFFFsH   2F H/GG4HH!I2 2K//J76J774K/.K/deepfullc           
      R	   t          |          }t          |          }|rdnd}|                                s*t          d           t          d|rdnd d|            d S t	          |           t                      rt                       t                       t                      rt                       t                       t          |          s7t          d           t          d|rdnd d	| d
           t                       dt                      dg}|r|                    d           t          ||dd           t          dt                      g|ddd          }|j                                        }|dk    r3t          dt          |                                           d           nKt          dt          |                                           d           t          d|rdnd d|            |rt#          |          nd }|rt          d|            t%                      }	|	r4t                       t          d           |	D ]}
t          d|
            t'          |          }|                    dd          }|                    dd          }|                    dd          }|                    d d          }|d!k    r|d"k    rt          d#           nt+          |          rNt          d$           t          d%|rdnd d	|            t          d&|sd'nd d(t                                  nw|d)k    rW|t-          t.                    k    r?t          d*           t          d+|sd'nd d(t                       d,|rdnd d|            n|d)k    r|rt          d-|            |rt          d.           nX| rt1                       nGt3                      \  }}|du rt          d/           n"|du rt          d0           t          d1           | rot                       t          d2           t5          |          d3t                      d4d5dgz   }|r|                    d           t7          j        |d6           d S d S )7Nr.  r  r   ro  rp  rs  rq  u4   ⚠ Installed gateway service definition is outdatedr  z  # auto-refreshes the unitr  rE   z-lFr   )r,  rH   rJ   r/  Tr0  r1  rk  z gateway service is running   ✗ z gateway service is stoppedre  rf  Recent gateway health:r  rM  rN  rP  rO  rq  rr  uA     ⏳ Restart pending: systemd is waiting to relaunch the gatewayuB     ⏳ Restart pending: systemd is temporarily rate-limiting startsz,  Run after the start-limit window expires: z"  Or clear it manually: systemctl rt  r  r  u@     ⚠ Planned restart is stuck in systemd failed state (exit 75)z  Run: systemctl z && u     ⚠ Systemd unit result: uB   ✓ System service starts at boot without requiring systemd lingerr  r  r  Recent logs:z-uz-n20rT  )r2  r3  r4  rx  rK  rt  r  r  r  r7  r6  r   r5  rV   rW   rv  rw  r  _runtime_health_linesrU  r   ry  r8   r   r  r]  rj  rT   rU   )r}  r,  r~  rq  r  
status_cmdrf   r  ri  runtime_linesrg   
unit_propsr}  r~  r  result_coder`  r   log_cmds                      r0   systemd_statusr    s   "6**F%V444I &.BJ 4555U61rUUUUVVV'v6666$&& ,...   !###"&111 DEEEn/ggRnnznnn	
 	
 	
 	,..=J  $	    	&(()  F ]  ""FY'//::<<YYY	
 	
 	
 	
 	Y'//::<<YYY	
 	
 	
 	S61rSSzSSTTTAGQ29===TO :888999)++M &'''! 	 	D+t++.f===J>>-44Lz2..I!~~&6;;..2..K|##	^(C(CQRRRR	'
	3	3 ;RSSSxf;T77RTxxlvxx	
 	
 	
 	s&1Pbss_o_q_qss	
 	
 	
 	
 
	!	!&6#); ; ' ' 	PQQQ b ?		R  b  bN^N`N`  b  bqwffmfm}  b  b  V`  b  b	
 	
 	
 	
 
	!	!k	!9K99::: 
>RSSSS	 >%''''577T!!KLLLLu$$VWWW<=== ,n!&))-
 
  	!NN4   w++++++, ,r2   c                  0    t                      } | rd|  ndS )z5Return the launchd service label, scoped per profile.r  r  )r  r,  s    r0   r`   r`   u  s'    F,2K((((8KKr2   _resolved_launchd_domainc                  P   t           t           S t          j                    } t                      }d|  }d|  }	 t	          j        dd| d| gddd	           |a |S # t          j        t          j        t          f$ r Y nw xY w	 t	          j        dd| d| gddd	           |a |S # t          j        t          j        t          f$ r Y nw xY w	 t	          j        dd
gddd          }d|j	        pdv r|a |S n'# t          j        t          j        t          f$ r Y nw xY w|a |S )a  Return the launchd domain that actually manages the gateway service.

    Probes ``gui/<uid>`` first (Aqua sessions), then ``user/<uid>``
    (Background/SSH sessions).  When neither domain contains a loaded
    service, falls back to ``launchctl managername`` as a heuristic.

    The result is cached for the lifetime of the process so that repeated
    calls (``start``, ``stop``, ``restart``) use a consistent domain.

    See #40831, #23387.
    Nzgui/zuser/rN   rx  /TrF   )r  rJ   rH   managernamerG   Aquar   )
r  r~   r?  r`   rT   rU   r{  r]   r^   rV   )rJ  rj   
gui_domainuser_domainrf   s        r0   _launchd_domainr    s     +''
)++CEJ#--K
'j#:#:5#:#:;		
 	
 	
 	
 $. ):+DFWX   
'k#;#;E#;#;<		
 	
 	
 	
 $/ ):+DFWX   
-(	
 
 
 fm)r**'1$ + ):+DFWX   
  +s5   #A !BB#B+ +!CC(C= =!D! D!>   rP   q   }   rF   r  c                     | j         t          v S )zJTrue when launchctl failed because the job isn't loaded (retry bootstrap).)ra    _LAUNCHD_JOB_UNLOADED_EXIT_CODES)r  s    r0   !_launchd_error_indicates_unloadedr    s    >===r2   ra   c                     | t           v S )a  True when launchctl can't manage the domain even after a fresh bootstrap.

    Codes 5 and 125 persist on macOS hosts where neither `gui/<uid>` nor
    `user/<uid>` supports service management; treat these as "launchd
    unavailable" and degrade gracefully to a detached process.
    )#_LAUNCHCTL_DOMAIN_UNSUPPORTED_CODES)ra   s    r0   _launchctl_domain_unsupportedr    s     <<<r2   r  rT  domainrj   c          	      B   	 t          j        dd| t          |          gd|           dS # t           j        $ rc}|j        t
          k    r t          j        dd|  d| gd|           t          j        dd| t          |          gd|           Y d}~dS d}~ww xY w)	u  Bootstrap a launchd job, recovering from a stale already-loaded label.

    On modern macOS, ``launchctl bootstrap`` of a label that is still
    registered in ``domain`` fails with ``5: Input/output error`` (EIO). That
    is the *already loaded* case — distinct from the domain being unmanageable,
    which callers handle via :func:`_launchctl_domain_unsupported`. A leftover
    registration from an interrupted restart leaves the job
    loaded-but-not-running, so the next bootstrap hits EIO; without this retry
    we misclassify it as "launchd cannot manage this macOS version" and degrade
    to a detached process, silently losing auto-start and crash-restart.

    Recover by booting the stale label out and bootstrapping once more. If the
    retry still fails, the ``CalledProcessError`` propagates so callers apply
    their domain-unsupported fallback for a genuinely broken domain.
    rN   	bootstrapTr  rJ   Nbootoutr  F)rT   rU   r8   r{  ra   _LAUNCHCTL_BOOTSTRAP_EIO)r  
plist_pathrj   rJ   r  s        r0   _launchctl_bootstrapr    s    $
+vs:?	
 	
 	
 	

 	( 
 
 
>555)%8%8%8%89	
 	
 	
 	

 	+vs:?	
 	
 	
 	
 	
 	
 	
 	
 	
 	

s   (, BABBc                  *    t                      dz  dz  S )zGPath the launchd reload watchdog tails for persistent-orphan detection.logslaunchd-reload.logr   r'   r2   r0   _launchd_reload_log_pathr    s    v%(<<<r2   messagec                    t                      }	 |j                            dd           ddlm} |                                                                                    d          }|                    dd          5 }|                    d	| d
|  d           ddd           dS # 1 swxY w Y   dS # t          $ r Y dS w xY w)zBAppend a timestamped line to the launchd reload log (best-effort).Tra  r   )datetimez%Y-%m-%d %H:%M:%S %zar   r  [z] r   N)
r  r   rg  r  now
astimezonestrftimer   writer   )r  r@   _dtstampfhs        r0   _append_launchd_reload_logr    s&   #%%D$666,,,,,,		$$&&//0FGGYYsWY-- 	/HH---'---...	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/   s6   A2B9 B,B9 ,B00B9 3B04B9 9
CCc                     	 t          j        dd| gddt           j        t           j                  }|j        dk    S # t           j        t
          f$ r Y dS w xY w)zCTrue when ``launchctl list <label>`` reports the job as registered.rN   rO   Fr   )r  rJ   rV   r  r   )rT   rU   r&  ra   r]   r   )rj   rf   s     r0   _launchctl_label_registeredr  +  ss    
&%(%%
 
 
  A%%%w/   uus   := AAr   c                   d}	 |dz  }	 t          | ||d           t          |          rdS t          d| d|  d| d	           nf# t          j        $ r+}t          d| d
|j         d|  d| d	           Y d}~n1d}~wt          j        $ r t          d| d|  d| d           Y nw xY wt          j                    |k    rdS t          j	        d           )u)  Bootstrap with retry until the label is registered or ``deadline`` passes.

    Wraps :func:`_launchctl_bootstrap` (which already recovers the EIO
    "already loaded" case) in a wall-clock retry loop for the *transient*
    failure mode: under high load or a launchd race the bootstrap can fail
    even after ``bootout`` already tore down the prior registration, leaving
    the service orphaned from ``KeepAlive`` supervision. The reported incident
    happened during a graceful drain (default ``agent.restart_drain_timeout``
    = 180s), so a fixed ~10s window is too short — retry until ``deadline``.

    Both ``CalledProcessError`` and ``TimeoutExpired`` are treated as
    retryable: a ``bootstrap`` that times out after ``bootout`` still leaves
    the service unloaded, so it must be retried, not allowed to escape. On
    each failure a timestamped line is appended to the reload log; success is
    confirmed with ``launchctl list`` (not merely a zero bootstrap exit).
    Returns True once the label is registered, False if the deadline is hit.
    r   Trm   r  rT  zbootstrap attempt z exited 0 but r  u0    is not registered (launchctl list) — retryingz failed (rc=z) for u    — retryingNz timed out for FrQ   )
r  r  r  rT   r{  ra   r]   r   r   r   )r  r  rj   r   attemptr  s         r0   +_retry_launchctl_bootstrap_until_registeredr  :  s   ( G1	 UBGGGG*511 t&CW C CF C CU C C C    , 	 	 	&5W 5 5#. 5 55 5 %5 5 5        ( 	 	 	& W    V    e          	
 >x''5
1-s"   "A A B+!A==+B+*B+c                  $    t                      dz  S )Nz.gateway-launchd-unsupportedr  r'   r2   r0    _launchd_unsupported_marker_pathr  p  s    ===r2   c                      ddl } ddlm}m} 	 t                                           | j        |                    |j                                                  dd          d           dS # t          $ r Y dS w xY w)z?Persist that launchd cannot supervise the gateway on this host.r   Nr  timezonez'launchd domain unsupported (exit 5/125))
written_atr  r   r  )
r  r  r  r  rF  r   r  utc	isoformatr   )r  r  r  s      r0   !_write_launchd_unsupported_markerr  t  s    KKK++++++++	(**55DJ&ll8<88BBDDC     	6 	
 	
 	
 	
 	
    s   AA. .
A<;A<c                  n    	 t                                          d           dS # t          $ r Y dS w xY w)z=Clear the unsupported marker when launchd bootstrap succeeds.Tr  N)r  r  r   r'   r2   r0   !_clear_launchd_unsupported_markerr    sL    (**11T1BBBBB   s   "& 
44c                  B    t                                                      S r,   )r  r4  r'   r2   r0   r  r    s    +--44666r2   c                      t                      ddg} t                      }|r'|                     |                                           |                     g d           | S )zBuild the `python -m hermes_cli.main [--profile X] gateway run --replace` argv.

    Profile-aware: honors the active HERMES_HOME via `_profile_arg()` so the
    detached fallback launches into the same profile as the CLI invocation.
    r   r   r  )r  r   r  rY   )cmdr  s     r0   _gateway_run_commandr    sd     d$5
6C..K (

;$$&&'''JJ...///Jr2   c            	         ddl m}  t                      dz  }|                    dd           |dz  }|dz  }	 t	          |d          }t	          |d          }n# t
          $ r Y d	S w xY w	 |5  |5  t          j        t                      ft          j	        ||d
 |              ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   n# t
          $ r Y d	S w xY wdS )a  Launch the gateway as a detached background process (launchd fallback).

    Used when launchctl can no longer bootstrap/kickstart the gateway on
    macOS 26+ (issue #23387). Mirrors the `nohup hermes gateway run --replace`
    workaround but keeps it CLI-managed: stdout/stderr go to the profile's
    gateway logs and the PID is tracked via the gateway.pid file that
    `run_gateway` writes, so stop/status/restart keep working.
    r   )r  r  Tra  gateway.logzgateway.error.logabF)r  rV   r  N)
r   r  r   rg  r   r   rT   r%  r  r&  )r  log_dirout_patherr_pathouterrs         r0   _spawn_detached_gatewayr    s    JIIIII&(GMM$M...&H,,H8T""8T""   uu
 	 	# 	 	$&& (	 
 .-//  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	    uu4sk    A 
A)(A)-C /C
25B3'C
3B7	7C
:B7	;C
>C 
CC CC 
C$#C$)exit_on_failurer  c                   ddl m} t                       t          d|  d           t	                      rJt          d           t          d           t          d |             d           t          d	           d
S t          d           t          d |             d           |rt          j        d           dS )a  Start the gateway detached when launchd can't manage it, with guidance.

    Returns True if the detached gateway was launched. When it can't be
    launched, prints the manual workaround and (by default) exits non-zero so
    the failure surfaces instead of silently doing nothing.
    r   display_hermes_homeu=   ⚠ launchd cannot manage the gateway on this macOS version (rZ  u3   ✓ Started gateway as a background process insteadz;  It will NOT auto-start at login or auto-restart on crash.z  Logs: z/logs/gateway.logr  Tz4Failed to start the gateway as a background process.z5  Try manually: nohup hermes gateway run --replace > /logs/gateway.log 2>&1 &rm   F)r  r  r  rx  r  r   r  rr  )r  r  _dhhs      r0   _launchd_fallback_to_detachedr    s     =<<<<<%'''	
T&
T
T
TUUU   CDDDKLLL22223333444tFGGG		.TVV	. 	. 	.    5r2   c                      t                      } t                      }t          t                                                                }t                      dz  }|                    dd           t                      }t          |          }t                      }|rt          |          nt          t          dz            }t                      }t          j        d          }	|	r:t          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 )Nr  Tra  r  r  r  c                     g | ]}||S r'   r'   r  s     r0   r   z*generate_launchd_plist.<locals>.<listcomp>  s    SSS1QRSQSSSr2   r
   r   z<string>z	</string>z<string>-m</string>z <string>hermes_cli.main</string>)z<string>gateway</string>z<string>run</string>z<string>--replace</string>z	
        z<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>z?</string>

    <key>ProgramArguments</key>
    <array>
        z?
    </array>
    
    <key>WorkingDirectory</key>
    <string>zf</string>
    
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>z9</string>
        <key>VIRTUAL_ENV</key>
        <string>z9</string>
        <key>HERMES_HOME</key>
        <string>a  </string>
    </dict>

    <key>LimitLoadToSessionType</key>
    <array>
        <string>Aqua</string>
        <string>Background</string>
    </array>
    
    <key>RunAtLoad</key>
    <true/>
    
    <key>KeepAlive</key>
    <true/>
    
    <key>StandardOutPath</key>
    <string>zH/gateway.log</string>
    
    <key>StandardErrorPath</key>
    <string>z-/gateway.error.log</string>
</dict>
</plist>
)r  r
  r8   r   r   rg  r`   r   r  r  r  rw   rx   r   r   r   r   rb  fromkeysr~   rG  r   rY   r  )r  r  r!  r  rj   r  r  r  priority_dirsr  r  r  	prog_argspartprog_args_xmls                  r0   generate_launchd_plistr    s   !##K .//Ko''//1122K&(GMM$M...E{++K %&&M%2Rs=!!!L6<Q8R8RH -..ML((M 	4  ] 3 3 :;;M11  !2333SS
vr(B(B(H(H(M(MSSSS	
 	
 I 	*;)))*I
  9%%'' 	9 	9D77778888	
 	
 	
   !%%i00M,
 , , 
, , , ,$ %, ,( ), ,, -, ,L M, ,R S, , , ,r2   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  r4  r|  r  r/  )r  r2  r4  s      r0   launchd_plist_is_currentr  C  sl    '))J u$$g$66I%''H2 	0	:	:; ;r2   c                     t                      } |                                 rt                      rdS t                      }t	          |d          rdS |                     |d           t                      }t                      }| d| }d}	 ddlm	}  |            }n# t          $ r d}Y nw xY w|t          |          rt          t          d	          rt                      d
z  dz  }	 |j                            dd           n# t"          $ r Y nw xY wt%          t'          dt)                                          }dt+          j        |           d| dt+          j        |           dt+          j        t/          |                      dt+          j        |           dt+          j        |           dt+          j        t/          |                     dt+          j        |           dt+          j        |           d| dt+          j        t/          |                     d}		 t1          j        dd|	gdt0          j        t0          j                   n3# t          $ r&}
t6                              d|
           Y d}
~
dS d}
~
ww xY wt;          d           dS t1          j        d d!|gdd"#           t'          dt)                                }t?          j                     |z   }tC          || ||$          sYtE          d%| d&t%          |           d'           t6          #                    d(|t%          |          tI                                 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.
    Flaunchd plistr   r  r  Nr   r   setsidr  r  Tra  g      >@zsleep 2; launchctl bootout z2 2>/dev/null; sleep 1; _deadline=$(($(date +%s) + z&)); while :; do   launchctl bootstrap r   z" 2>/dev/null;   if launchctl list zm >/dev/null 2>&1; then break; fi;   echo "[$(date '+%Y-%m-%d %H:%M:%S %z')] bootstrap not yet registered for u    — retrying" >> z\;   if [ $(date +%s) -ge $_deadline ]; then break; fi;   sleep 2; done; if ! launchctl list z[ >/dev/null 2>&1; then   echo "[$(date '+%Y-%m-%d %H:%M:%S %z')] FAILED launchd reload for u"    — service NOT registered after zs of retries" >> z; fiz	/bin/bashr  )r  rV   r  z0Deferred launchd reload could not be spawned: %su   ↻ Updated gateway launchd service definition; reload deferred to a detached helper (refresh ran inside the gateway process tree)rN   r  r  r  )r   zFAILED launchd reload of u/    — service NOT registered after retrying for z,s (refresh ran outside gateway process tree)uS   launchd reload of %s failed — service not registered after %ds of retries; see %suR   ↻ Updated gateway launchd service definition to match the current Hermes install)%r  r4  r  r  rC  rF  r`   r  r   r   ru   r   r   r~   r   r   rg  r   r;   r   r  shlexquoter8   rT   r%  r&  loggerwarningrx  rU   r   r   r  r  errorr  )r  	new_plistrj   r  targetgateway_pidr   reload_log_path_reload_budgetreload_scriptr  	_deadlines               r0   refresh_launchd_plist_if_neededr  P  s    ())J ":"<"< u&((I&y/BB u)g666EF    F K222222%o''    	/<< 	 B!! 	 $ *++f47KK	"(((EEEE 	 	 	D	 S'A'C'CDDEE!&V!4!4  +9 
 &+[%8%8 
 <A;s:;W;W  $);u#5#5  \a[fgm[n[n  DI  DO  PS  Tc  Pd  Pd  De  De  $);u#5#5  UZT_`fTgTg  LZ  ns  ny  z}  ~M  zN  zN  nO  nO   	 		dM2"&!)!)	      	 	 	NNMqQQQ55555	 	L	
 	
 	
 t N	i(    9;;<<N  >1I6
EI   
 	#  //  	
 	
 	

 	$&&	
 	
 	
 
\   4s<   B! !B0/B01D 
DD5/I% %
J/JJc                    t                      }|                                rd| sbt                      s1t          d|            t	                       t          d           d S t          d|            t          d           d S |j                            dd           t                      }t          |d          rd S t          d|            |	                    |           	 t          t                      |t                      d	
           nI# t          j        $ r7}t          |j                  s t#          d|j                    Y d }~d S d }~ww xY wt                       t          d           t%                       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`  Tra  r  zInstalling launchd service to: r  rT  zlaunchctl bootstrap exit u!   ✓ Service installed and loaded!rd  z2  hermes gateway status             # Check statusr   r  z
  tail -f z/logs/gateway.log  # View logs)r  r4  r  rx  r  r   rg  r  rC  rF  r  r  r`   rT   r{  r  ra   r  r  r  r  )r  r  r  r  r  s        r0   launchd_installr    s   '))J 5 ')) 	L
LLMMM+---2333F;z;;<<<()))D4888&((I&y/BB 	
8J
8
8999)$$$z+<+>+>	
 	
 	
 	
 	
 (   ,Q\:: 	%&P!,&P&PQQQ	 
GGG	
-...%'''	GGG	-	
>???<<<<<<	
=ttvv
=
=
=>>>>>s   ++D E&,EEc                  &   t                      } t                      }t          j        ddt	                       d| gdd           |                                 r&|                                  t          d|             t          d           d S )	NrN   r  r  Fr  r  rl  u   ✓ Service uninstalled)r  r`   rT   rU   r  r4  r  rx  )r  rj   s     r0   launchd_uninstallr    s    '))JEN	iO$5$5!?!?!?!?@     +)Z))***	
#$$$$$r2   c                     t                      } t                      }|                                 s(t                      }t	          |d          rt          j        d           t          d           | j        	                    dd           | 
                    |d           	 t          t                      | |d	           t          j        d
dt                       d| gdd           nI# t          j        $ r7}t!          |j                  s t%          d|j                    Y d }~d S d }~ww xY wt          d           t'                       d S t)                       	 t          j        d
dt                       d| gdd           n# t          j        $ r}t+          |          s t          d           	 t          t                      | |d	           t          j        d
dt                       d| gdd           nM# t          j        $ r;}t!          |j                  s t%          d|j                    Y d }~Y d }~d S d }~ww xY wY d }~nd }~ww xY wt          d           t'                       d S )Nr  rm   u:   ↻ launchd plist missing; regenerating service definitionTra  r   r  r  rT  rN   	kickstartr  r  launchctl exit u   ✓ Service startedu:   ↻ launchd job was unloaded; reloading service definition)r  r`   r4  r  rC  r  rr  rx  r   rg  rF  r  r  rT   rU   r{  r  ra   r  r  r  r  )r  rj   r  r  e2s        r0   launchd_startr    s#   '))JE  *,,	*9oFF 	HQKKKJKKKt<<<i':::	 !2!2JrRRRRNko.?.?+I+I%+I+IJ    
 , 	 	 	0>> )*JAL*J*JKKKFFFFF		
 	#$$$)+++#%%%+/*;*;'E'Ee'E'EF	
 	
 	
 	
 	

 (   033 	JKKK	 !2!2JrRRRRNko.?.?+I+I%+I+IJ    
 , 	 	 	 1?? )*KBM*K*KLLLFFFFFFFFF	    & 

   %'''''s\   'A
C2 2D8,D33D8)+F I&$I!A
HI!I,I
I!II!!I&c                     t                      } t                       d|  }	 ddlm}m}  |d          }| ||           n# t
          $ r Y nw xY w	 t          j        dd|gdd	
           nA# t          j        $ r/}t          |          st          |j                  rn Y d }~nd }~ww xY wt          dd           t          d           d S )Nr  r   rw  Fr   rN   r  Tr  r        $@r  rJ   force_afteru   ✓ Service stopped)r`   r  r   r   r  ru   rT   rU   r{  r  r  ra   _wait_for_gateway_exitrx  )rj   r  r   r  rA   r  s         r0   launchd_stopr  H  s<   E!!++E++FMMMMMMMMoE222?%%c***   Y7tRPPPPP( 	 	 	 -Q// 	3PL4
 4
 	  DDDD	 4S9999	
     s)   !A 
AAA1 1B/ %B**B/r  r  r   c                    ddl }ddlm}  |j                    | z   }| |j                    |z   nd}d} |j                    |k     r |            }|dS |[|sY |j                    |k    rF	 t	          |d           t          d| d           n# t          t          t          f$ r Y dS w xY wd} |j	        d	            |j                    |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   FTr  u   ⚠ Gateway PID z& did not exit gracefully; sent SIGKILLg333333?z still running after u   s — restart may fail)
r   r   r   r   r   rx  r   r   r   r   )	rJ   r   r   r   r   force_deadline
force_sentrA   remaining_pids	            r0   r  r  g  s~    KKK......t~')H,7,C		K	'	'  J
$.

X
%
%o;4 # $  N22c....TTTTUUUU&A   ttJ
3% $.

X
%
%* $O%%M b}bb7bbb	
 	
 	
 u4s   .$B B.-B.c            	         t                      } t                       d|  }t                      }ddlm} 	  |            }|.t          |          rt          d           t                       d S |rt          d| d|dd           	 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           t                       d S # t          j        $ r=}t!          |          s2t#          |j                  rt'          d|j                    Y d }~d S  t          d           t)                      }	 t          j        dd|gd	d           t          j        ddt                      t+          |          gdd           t          j        dd|gdd           nM# t          j        $ r;}t#          |j                  s t'          d|j                    Y d }~Y d }~d S d }~ww xY wt          d           t                       Y d }~d S d }~ww xY w)Nr  r   r   u   ✓ Service restart requestedu   → Stopping gateway (PID u%   ) — draining in-flight runs (up to z.0fzs)...Fr  r  u"   ⚠ Gateway drain timed out after u   s — forcing launchd restartrN   r  z-kTr  r  u   ✓ Service restartedzlaunchctl kickstart exit u'   ↻ launchd job was unloaded; reloadingr  r  r  r  )r`   r  r  r   r   r   rx  r  r   r   r   r   r  rT   rU   r{  r  r  ra   r  r  r8   )	rj   r  r   r   rA   exitedr  r  r  s	            r0   launchd_restartr	    s?   E!!++E++F.00M......A,o?<SAA?1222-///F? 3S 3 3'23 3 3  c/////&A   /SWXXX m]mmmm   	[$?tUWXXXX%&&&)+++++( #, #, #,033 	 -Q\:: -.X!,.X.XYYY7888+--
	 Ni0   
 Nk?+<+<c*ooN   
 NKf=TSUVVVVV, 	 	 	0?? )*KBM*K*KLLLFFFFFFFFF		
 	%&&&)+++++++++G#,ss   8D 1D B D B96D 8B99A$D I,/:I'/I'A(G76I'7I,H<2I'<I I''I,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|rt          |          nd }dd	lm	}  |d
          }|||k    rd }t                      }	t          d|            t                      rt          d           nt          d           t          d           |r|4t          d| d           t          d           |	rt          d           n|	rqt          d           t          d           |r#t          d| d           t          d           nt          d           t          d           t          d           nvt          d           t          |           |rt          d| d           nBt          d           t          d           t          d           |rt          d| d           | rmt                      dz  dz  }
|
                                rGt                       t          d            t          j        d!d"t          |
          gd#           d S d S d S )$NrN   rO   Tr   rG   r   Fr   r   r   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 is supervised by launchd (PID rl  z>  Auto-start at login and auto-restart on crash are available.z@  (launchd domain was previously unavailable but is now working)uC   ⚠ Gateway service is registered but launchd is not supervising itz:  launchd cannot manage the gateway on this macOS version.u.   ✓ Detached fallback process is running (PID z5  Cron jobs will fire. Stop with: hermes gateway stopu"   ✗ No fallback process is runninguF     ⚠ Auto-start at login and auto-restart on crash are NOT available.u.   ✓ Gateway service is registered with launchdz+  Detached gateway process is running (PID u!   ✗ Gateway service is not loadedzB  Service definition exists locally but launchd has not loaded it.z3  Note: a detached gateway process is running (PID r  r  r  tailz-20rT  )r  r`   rT   rU   ra   rV   r]   rb   r   r   r  rx  r  r   r4  r8   )r}  r  rj   rf   service_listedlist_outputlaunchd_pidr   fallback_pidlaunchd_unsupportedlog_files              r0   launchd_statusr    s>   '))JE&%(	
 
 
  *a/m$    GU^5kBBBZ^K /....."?777L
 <;#>#>
 =>> 

(J
(
()))!! -IJJJJVWWW+,,, Y"M{MMMNNNRSSS" ZXYYY  	UWXXXNOOO 5V|VVVWWWMNNNN:;;;3444Z[[[[BCCC+ USLSSSTTT1222RSSS+,,, 	YWWWWXXX G"$$v-=?? 	GGGG.!!!NFE3x==92FFFFFFG G	G 	Gs   -A A"!A"rC  c                 p    t          | pd                                                                          dv S )Nr   >   r	  r
  r  r  )r8   rW   r   )rC  s    r0   _truthy_envr  <  s2    u{!!##))++/IIIr2   c                  p    t          t                    dk    ot          dz  dz                                  S )Nz/opt/hermesdockerzentrypoint.sh)r8   r  is_filer'   r2   r0   _is_official_docker_checkoutr  @  s6    L]* 	BH$6??AAr2   c                      t           j                            d          rdS t           j                            d          rdS t           j                            dd          } | r| dk    rdS dS )a  Return True when this process IS the gateway a service manager launched.

    The conflict guard below must never fire on the service's own startup, or
    it would wedge the unit into a respawn/refuse loop. Each supervisor exports
    a reliable marker into the child's environment:

      - systemd sets ``INVOCATION_ID`` for every unit it launches (the same
        marker ``gateway/run.py`` already uses to pick the restart path).
      - launchd sets ``XPC_SERVICE_NAME`` to the job label for jobs it spawns;
        interactive shells inherit the sentinel ``"0"`` instead.
      - the s6-overlay container longrun exports ``HERMES_S6_SUPERVISED_CHILD``.
    INVOCATION_IDTHERMES_S6_SUPERVISED_CHILDXPC_SERVICE_NAMEr   rX  F)r~   rG  r   )xpc_services    r0   !_running_under_gateway_supervisorr  G  sm     
z~~o&& t	z~~233 t*..!3R88K {c))t5r2   c                    | rdS 	 t                      }n# t          $ r Y dS w xY w|sdS 	 ddlm}  |            }ddlm} n# t          $ r Y dS w xY w	 ddl}ddlm} |dz  } ||          }|sdS ddlm}	m	}
  |
|          }|r |	|          sdS |dz  }|
                                sdS t          |d	
          5 }|                    |          pi }ddd           n# 1 swxY w Y   t          |                    d          p*|                    di           pi                     d                    }|sdS n-# t          $ r  t                              dd           Y dS w xY wt#          d| d           t%          d           t%          d           t%                       t%          d           t%                       t%          d           t%          d           t'          j        d           dS )a  Refuse a named-profile gateway when a multiplexer is already serving it.

    When the default profile's gateway runs with gateway.multiplex_profiles=on,
    it is the sole inbound process for EVERY profile on the host. Starting a
    separate gateway for a named profile would double-bind that profile's
    platforms (two pollers on one bot token, port fights). In that mode a
    named-profile ``hermes gateway run`` is always a misconfiguration, so we
    hard-error with a pointer to the multiplexer. ``--force`` overrides.

    Inert unless ALL of: (a) this invocation is a named profile, (b) a default-
    profile gateway is running, (c) that gateway's config has multiplexing on.
    Nr   r  r   )_read_pid_recordr   )r   _pid_from_recordzconfig.yamlr   r  multiplex_profilesr  z!Multiplexer-conflict probe failedTexc_infozTThe default gateway is running as a profile multiplexer and already serves profile 'z'.z  When gateway.multiplex_profiles is on, the default gateway is the
  single inbound process for every profile. Starting a separate
  gateway for this profile would double-bind its platforms (two
  pollers on one bot token, port conflicts).
z<  Manage the multiplexer instead (from the default profile):    hermes gateway restartz>  Pass --force to start a separate profile gateway anyway (notz0  recommended while the multiplexer is running).rm   )r  ru   r  r  r   r   yamlr   r   r!  r4  r   	safe_loadr-   r   r  debugr   rx  r  rr  )r  r-  r  r"  _default_running_pid_yamlr   default_pid_pathrecr   r!  rA   cfg_pathfrY  	multiplexs                   r0   &_guard_named_profile_under_multiplexerr0  ^  s      ""    <<<<<<..00JJJJJJJ   333333 (-7/00 	F@@@@@@@@s## 	++c** 	F  -/   	F(W--- 	+//!$$*C	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+GG()) H	2&&,"112FGG
 
	  	F	   84HHH 	&!	& 	& 	&   
	9   

HIII	GGG	
&'''	GGG	
JKKK	
<===HQKKKKKsf    
##A 
AAE 2 E E /E  C$E $C((E +C(,AE &E+*E+c                    | st                      rdS 	 t                      }n-# t          $ r  t                              dd           Y dS w xY w|j        r|j        sdS t          d|j         d           t          d           t                       t          d           t                       t          d	           t          j        d
           dS )u  Refuse a foreground gateway when a service manager already supervises one.

    Running ``hermes gateway run [--replace]`` (or the manual-restart fallback)
    from a shell on a systemd/launchd host spawns a second, long-lived
    dispatcher that escapes the service cgroup, survives
    ``systemctl restart``, and becomes a silent concurrent writer on the shared
    kanban DB — the documented root cause of multi-writer SQLite WAL corruption
    (issue #35240). Pass ``--force`` to start anyway.
    Nz(Supervised-gateway conflict probe failedTr#  z#A gateway is already running under z for this profile.u     Starting another one from a shell leaves an orphan dispatcher that
  escapes the service, survives restarts, and writes to the same kanban
  DB concurrently — which can corrupt it. Restart the supervised gateway
  instead:r%  ze  Pass --force to start a foreground gateway anyway (not recommended
  while the service is running).rm   )r  r  ru   r  r(  r%   r&   r   r$   rx  r  rr  )r  r  s     r0   "_guard_supervised_gateway_conflictr2    s     133 /11   ?$OOO & 8+C Rh.>RRR   
	   
GGG	
&'''	GGG		+   HQKKKKKs   # &AAr   c                 X   | st                      rdS 	 ddlm}  |            }n-# t          $ r  t                              dd           Y dS w xY w|dS t          d| d           t          d	           t          d
           t          d           t          j	        d           dS )aQ  Refuse duplicate foreground startup before importing gateway.run.

    ``gateway.run`` performs the authoritative PID/lock check, but importing it
    is expensive: it pulls in model_tools/plugin discovery first. On small
    instances, a supervisor or dashboard loop repeatedly running bare
    ``hermes gateway run`` can burn memory/CPU just to fail with "already
    running" after plugin discovery. This cheap PID-file preflight preserves the
    same user-facing contract while avoiding that startup work without scanning
    unrelated gateway processes from other HERMES_HOME roots.
    Nr   r   z%Existing-gateway process probe failedTr#  z1Another gateway instance is already running (PID rZ  z-  Use 'hermes gateway restart' to replace it,z!  or 'hermes gateway stop' first.z8  Or use 'hermes gateway run --replace' to auto-replace.rm   )
r  r   r   ru   r  r(  r   rx  r  rr  )r   r   rA   s      r0   (_guard_existing_gateway_process_conflictr4    s      355 222222o   <tLLL {CCCCC   

9:::	
-...	
DEEEHQKKKKKs   % &AAc                  h   t          t          d          rt          j                    dk    rdS t          t          j        d                    rdS t                      sdS t          d           t          d           t          d           t          d           t          j	        d	           dS )
zLRefuse gateway startup when the official Docker privilege drop was bypassed.r  r   NHERMES_ALLOW_ROOT_GATEWAYzLRefusing to run the Hermes gateway as root inside the official Docker image.z  The image entrypoint normally drops privileges to the 'hermes' user. If you override entrypoint in Docker Compose, include /opt/hermes/docker/entrypoint.sh before the Hermes command.zy  Running the gateway as root can leave root-owned files in $HERMES_HOME and break later non-root dashboard/gateway runs.zM  Set HERMES_ALLOW_ROOT_GATEWAY=1 only if you intentionally accept this risk.rm   )
r   r~   r  r  r  r  r   rx  r  rr  r'   r2   r0   #_guard_official_docker_root_gatewayr7    s    2y!! RZ\\Q%6%629899:: ')) V   
	F  
 
	H   
W   HQKKKKKr2   verbosequietc           	      z   t                       t          |           t          |           t          |           t          j                            dt          t                               	 t          t          j
        ot          j
                                                  }n# t          t          f$ r d}Y nw xY wt                      }|r	 t          j        t          j        t          j                   t%          t          d          r)t          j        t          j        t          j                   n# t          t          f$ r Y nw xY w	 ddl}|j        j        }|                    dd           n# t          t0          f$ r Y nw xY wt3                      r"	 t5          d           n# t6          $ r Y nw xY wdd	lm} t=          d
           t=          d           t=          d           t=          d           t=          d           t=          d           t=                       |rdn| }	ddl}
ddl }ddl!m!m" dt          dtF          ddffd d|t          j$        ||           d%fd}|
%                    |           d}	 tM          j'         |||	                    } d|           n# tP          $ r2  d|)                                           t=          d           Y dS tT          $ r5} dtW          |dd          |)                                             d}~wtX          $ rF} d!t[          |          j.        t_          |          |)                                "            d}~ww xY w|s d#           t	          j0        d            d$           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.
        force: Skip the supervised-gateway conflict guard and start even when a
               systemd/launchd service is already supervising this profile.
    r  )r   r   FSIGBREAKNrm   r.  )start_gateway   ┌─────────────────────────────────────────────────────────┐u@   │           ⚕ Hermes Gateway Starting...                 │   ├─────────────────────────────────────────────────────────┤u@   │  Messaging platforms + cron scheduler                    │u?   │  Press Ctrl+C to stop                                   │   └─────────────────────────────────────────────────────────┘r  tagextrar*   c                 z   t           j                            dd          dk    rd S 	 ddlm}  |            dz  }|                    dd                               	j                                                  }|| t          j	                    t          j                                        d         t          j        d|}dd l}t          |d	z  d
d          5 }|                    |                    |t$                    dz              d d d            d S # 1 swxY w Y   d S # t&          $ r Y d S w xY w)NHERMES_GATEWAY_EXIT_DIAGr	  r   r  r  Tra  )tsr@  rA   r  r  zgateway-exit-diag.logr  r   r  r  r   )r~   rG  r   r  r   rg  r  r  r  r   r  versionrY   r  r  r   r  r   r8   ru   )
r@  rA  _ghhr  rD  rg   _jsonr.  r  _tzs
           r0   
_exit_diagzrun_gateway.<locals>._exit_diagu  s   :>>4c::cAAF	@@@@@@dffvoGMM$M666!!++--By{{+++--a0L  D !   g 77wOOO ?STD#66=>>>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 	 	 	DD	s6   B6D, 3DD, D##D, &D#'D, ,
D:9D:zgateway.start)r   r	  stdin_is_ttyabsorb_windows_console_controlsc                  ^      dt          t          j                                         d S )Nzatexit.hook)sys_exc)reprr  r$  )rI  s   r0   _atexit_hookz!run_gateway.<locals>._atexit_hook  s,    
=$s|~~*>*>??????r2   )r   	verbosityzasyncio.run.returned)successzasyncio.run.KeyboardInterrupt)	tracebackz
Gateway stopped.zasyncio.run.SystemExitcode)rS  rR  zasyncio.run.exception)exc_typeexc_reprrR  zgateway.exit_nonzerozgateway.exit_cleanr*   N)1r7  r0  r2  r4  r  r@   insertr8   r  r-   r  r  r\   r   r  r   SIGINTSIG_IGNr   r;  ctypeswindllkernel32SetConsoleCtrlHandlerAttributeErrorrS   rH  ru   gateway.runr<  rx  atexitrR  r  r  objectr	  registerasynciorU   KeyboardInterrupt
format_exc
SystemExitr  BaseExceptiontyper5   rN  rr  )r8  r9  r   r  _stdin_is_tty _absorb_windows_console_controlsrZ  r\  r<  rP  _atexit
_tracebackrO  rQ  r  r  rI  rH  s                  @@@r0   run_gatewayrm    s    ()))*7777&U3333,W====HOOAs<(()))SY=39+;+;+=+=>>    'V'X'X$' 	M&-888vz** ?fov~>>>$ 	 	 	 D		MMM}-H **43333( 	 	 	D	 !"" 	*%88888 	 	 	D	 *)))))	  ~      	
LMMM	  ~      	
LMMM	
KLLL	  ~      	GGG *7I """"99999999 f        0 JX"(H   @ @ @ @ @ @ \"""G+mmGyQQQRR
)7;;;;;    	
+ ++--	
 	
 	
 	
 	"###   
$FD)) ++--	
 	
 	
 	

 	 	 	 	 	
#!WW%!WW ++--		
 	
 	
 	
 		  
)***J#$$$$$st   57B- -CCA'D? ?EE&E> >FF$F5 5
GG,K 8N 	N	0L99NANN
mattermost
Mattermostu   💬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.)r   r   passwordhelpz	Bot tokenz&Paste the bot token from step 2 above.MATTERMOST_ALLOWED_USERSz"Allowed user IDs (comma-separated)z*Your Mattermost user ID from step 4 above.)r   r   rr  is_allowlistrs  MATTERMOST_HOME_CHANNELzVHome channel ID (for cron/notification delivery, or empty to set later with /set-home)z@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.)rB  rj   emoji	token_varsetup_instructionsvarsr   Signalu   📡SIGNAL_HTTP_URL)rB  rj   rx  ry  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 WebSocketzApp IDz0The App ID from your Yuanbao IM Bot credentials.YUANBAO_APP_SECRETz
App Secretz@The App Secret (used for HMAC signing) from your Yuanbao IM Bot.c            
      6   	 ddl m}   |              n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wd t
          D             }t          j        dk    rd |D             }d |D             }	 dd	lm	} n# t          $ r |cY S w xY w|
                                D ]l}|j        |v rt          j        dk    r|j        d
k    r(|                    |j        |j        |j        |j        r|j        d         nd|j        |d           m|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.

    Platform-specific gating: some platforms can't be configured on
    every host. Currently:
      - Matrix is hidden on Windows. The [matrix] extra pulls
        ``mautrix[encryption]`` -> ``python-olm``, which has no Windows
        wheel and needs ``make`` + libolm to build from sdist. There's
        no native Windows path that works, so we don't offer it in the
        picker. Users who want Matrix on Windows can run hermes under
        WSL.
    r   )discover_pluginsz7plugin discovery failed during platform enumeration: %sNc                 ,    g | ]}t          |          S r'   )rb  r  s     r0   r   z"_all_platforms.<locals>.<listcomp>  s    ---Qa---r2   r  c                 D    g | ]}|                     d           dk    |S )rB  matrix)r   r  s     r0   r   z"_all_platforms.<locals>.<listcomp>  s,    FFF1QUU5\\X-E-EQ-E-E-Er2   c                      i | ]}|d          |S )rB  r'   r  s     r0   
<dictcomp>z"_all_platforms.<locals>.<dictcomp>  s    ---aah---r2   )platform_registryr  r   )rB  rj   rx  ry  install_hint_registry_entry)hermes_cli.pluginsr  ru   r  r(  
_PLATFORMSr  r  gateway.platform_registryr  all_entriesr   r   rj   rx  required_envr  )r  r  	platformsby_keyr  r   s         r0   _all_platformsr    s   0S777777 S S SNPQRRRRRRRRS .-*---I |wFF	FFF	--9---F???????    #..00 
 
: <7""uzX'='=z6;6HPU/22b % 2#( 		
 		
 		
 		
 s$    
A=A?B BBr  c                    |                      d          }|d}|j        H	 ddlm}  |d          }t	          |                    |                    }nG# t
          $ r d}Y n9w xY w	 t	          |                                          }n# t
          $ r d}Y nw xY w|rdnd	S |                      d
d          }|sd	S t          |          }|dk    rK|rG|                                dk    r/t                      dz  dz  dz  }|
                                rdS dS d	S |                      d          dk    rt          d          }|r|rdS |s|rdS d	S |                      d          dk    rYt          d          }	t          d          }
t          d          }t          ||	|
|g          rdS t          ||	|
|g          rdS d	S |                      d          dk    r^t          d          }t          d          }|s|r2|r0t          d          }|r|                                dv rd nd}d| S |s|s|rdS d	S |                      d          d!k    rt          d"          }|r|rdS |s|rdS d	S |rdS d	S )#zReturn a plain-text status string for a platform.

    Returns uncolored text so it can safely be embedded in
    curses menu items (ANSI codes break width calculation).
    r  NFr   )PlatformConfigT)enabled
configurednot configuredry  r   WHATSAPP_ENABLEDr  whatsappsessionz
creds.jsonzconfigured + pairedzenabled, not pairedrB  r   SIGNAL_ACCOUNTzpartially configuredemailEMAIL_PASSWORDEMAIL_IMAP_HOSTEMAIL_SMTP_HOSTr  MATRIX_HOMESERVERMATRIX_PASSWORDMATRIX_ENCRYPTION>   r	  r  r  z + E2EEr~  WEIXIN_TOKEN)r   is_connectedgateway.configr  r-   ru   check_fnr   r   r   r4  allr}  )r  r   r  r  	syntheticry  r  session_fileaccountr  imapsmtp
homeserverrr  e2eer-  rA  s                    r0   _platform_statusr    sc    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}ddl m}  ||          }|                    d| d| d           n |dk    r|r|                    d|            |S )z<Summarize the latest persisted gateway runtime health state.r   r_  rj  rn  active_agentsr  r  rc  fatalerror_messagezunknown errorro  r  rm  u   ⚠ Last startup issue: drainingrS  shutdown)parse_active_agentsu   ⏳ Gateway draining for z (z active agent(s))stoppedu   ⚠ Last shutdown reason: )r   r`  ru   r   r   r   r  )r`  rc  r&  rj  rn  r  r  r  r  pdatar  r  r  counts                 r0   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666666##M22SSS5SSSTTTT	)	#	#	#?+??@@@Ls   	 platform_keybehaviorc                 ,    t          | d|d           dS )zBPersist a platform-specific unauthorized-DM policy in config.yaml.unauthorized_dm_behaviorT)ry   N)r   )r  r  s     r0   &_set_platform_unauthorized_dm_behaviorr  :  s!    .H(X\]]]]]]r2   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          dk    rt                       t          d           t          d           t          d           t          dd          }	|	
                                dk    r	 ddlm}
m}  |
            }|r> ||j                  r.t          ||j                   t          d           d}|j        }nN|rt#          d           t                       t          d           n# t$          $ r t#          d           Y nw xY wd}| d         D ]B}t                       t          d|d                     t          |d                   }|r|d         |k    rt          d |            |r|d         |k    rt          d!           ||                    d"          rd#|d         v r|rt'          |          }t          d$|            t          d%d          rt          d&d'          }|g}|                    dd(                              d)          D ]}|r||vr|                    |           d)                    |          }t          |d         |           t          d*           |}ht          d+           t          d,           t          d-           t          d|d.          d'          }|r%|                    dd(          }d/|d         v rg }|                    d)          D ]}|
                                }|                    d0          r=|                    d1          r(|                    d2                              d1          }|                                                    d3          r
|d4d         }|r|                    |           d)                    |          }t          |d         |           t          d*           |}nt                       |                     d          d5k    }|rg d6}d7}ng d8}d9}t;          d:||          }|dk    r3|rt          d;d<           nt          d=d<           t#          d>           nX|d9k    r1|rt=          d5d?           t          d@           t          dA           n!|rt          dB           nt          dC           t          d|d.          |                    dDd          '          }|r0t          |d         |           t          dE|d                     |d         |k    rt#          dF| dG            dS t          dH           D|                                 dI}t          |          }|rm|sk|dJk    re|                    d)          d         
                                }|r6t          dK| dLd          r"t          ||           t          dM|            t                       t          | d| dN           dS )Oz2Interactive setup for Telegram, Discord, or Slack.rx  rj   ry       ─── r       Setup ───rz  r  z is already configured.z  Reconfigure ?FNrB  telegramz>  Telegram can be configured automatically with a managed bot:u:     [1] Automatic (scan QR → confirm in Telegram → done)z  [2] Manual BotFather tokenz  Choice [1/2]r	  r  r   )auto_setup_telegram_bot_resultis_valid_telegram_bot_tokenz  Saved TELEGRAM_BOT_TOKENTz5  Automatic setup returned an invalid Telegram token.z!  Falling back to manual setup...z1  Automatic setup is unavailable in this install.r{  rs  r   z  Current: z!  Token saved by automatic setup.ru  TELEGRAMz"  Detected your Telegram user ID: z-  Allow this Telegram account to use the bot?z9  Additional allowed user IDs (comma-separated, optional)rr  r   rT  u7     Saved — only these users can interact with the bot.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   DISCORDz<@>z<@!zuser:rF   r  )z9Enable open access (any email sender can message the bot)z=Use DM pairing (unknown email senders receive a pairing code)zKeep unknown senders silentrQ   )z/Enable open access (anyone can message the bot)zXUse DM pairing (unknown users request access, you approve with 'hermes pairing approve')z7Skip for now (bot will deny all users until configured)rm   z+  How should unauthorized users be handled?EMAIL_ALLOW_ALL_USERSr  GATEWAY_ALLOW_ALL_USERSu2     Open access enabled — anyone can use your bot!pairuB     DM pairing mode — users will receive a code to request access.z8  Approve with: hermes pairing approve <platform> <code>z(  Unknown email senders will be ignored.u9     Skipped — configure later with 'hermes gateway setup'rr  z  Saved u     Skipped — z won't work without this.z  Skipped (can configure later)_HOME_CHANNELTelegramz  Use your user ID () as the home channel?  Home channel set to z configured!) rx  r    r   CYANr   r   r   r   r   r   rW   hermes_cli.telegram_managed_botr  r  rA  r   owner_user_idr   rt   r8   r   rY   r   r   r   rZ   lstripr  r   r   r  upper)r  rx  rj   ry  instructionsrg   existing_tokenauto_token_savedauto_owner_user_idr  r  r  rf   allowed_val_setvarexistingdetected_idrA  idsrJ  cleanedrC  rh   is_emailaccess_choicesdefault_access_idx
access_idxhome_varhome_valfirst_ids                                 r0   _setup_standard_platformr  ?  se   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||Ej((STTTOPPP1222(#666<<>>S  D        8799 	D99&,GG 	D"9fl;;;!">???'+$)/)=&& _%&]^^^GGGBCCCC  S S SQRRRRRS O j: j:%F%%&&& V-- 	1Fy00/X//000 	Fy 8 8:;;; 77>"" T	S[((-?(!"455P;PPQQQ !PRVWW "S!&  E '-C$}}S"55;;C@@ , , ,3c>>JJsOOO!hhsmmG"3v;888!"[\\\&-O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") #<<..'9 +& & &N
 *+&&& & &N
 *+&*A"& 

 ?? J&'>GGGG&'@&III!"VWWWW1__ P>wOOO!\   R     !"LMMMMS   +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 	?C8CCCT
 
 	? 8X...=8==>>>	GGGU00U00011111s   G G87G8c                  V   t                      rDt          d                                          p!t          d                                          S t                      r t	                                                      S t                      rddlm}  |                                 S dS )z6Check if the gateway is installed as a system service.Fr.  Tr   gateway_windows)	rS   r3  r4  r_   r  rv   
hermes_clir  is_installedr  s    r0   _is_service_installedr    s     "" 
.!///6688 ;$D11188::	
 
 .%''..000	 .......++---5r2   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                      r9ddlm} |                                rt'          t)                                dk    S t'          t)                                dk    S )z2Check if the gateway service is currently running.Fr.  Tr/  r   r0  r1  rN   rO   rG   r   r  )rS   r3  r4  r5  r6  rV   rW   r7  rT   r]   r_   r  rU   r`   ra   rv   r  r  r  rc   r   )user_unit_existssystem_unit_existsrf   r  s       r0   _is_service_runningr  
  s]    "" 200>>>EEGG2$???FFHH 	' "2"4"45 #'   =&&((H444 5 *";<     	' "2"4"45#'   =&&((H444 5 *";<    u	 0.007799 0		^f&7&9&9:#	  F $))( 	 	 	55		 0......'')) 	0 (**++a// ""##a''s6   ?B B21B28?C: :DD1E9 9FFc                     t                       t          t          dt          j                             t                       t	          d           t	          d           t	          d           t	          d           t          d          } t          d          }| r1|r/t                       t          d           t          d	d
          sdS 	 ddlm	}m
} n9# t          $ r,}t          d|            t	          d           Y d}~dS d}~ww xY w |            s t          d           t	          d           dS t                       t          dd          st	          d           dS ddl}	  |j         |t          t!                                                    }nS# t"          $ r! t                       t%          d           Y dS t          $ r}t          d|            Y d}~dS d}~ww xY w|st%          d           dS |                    dd          }|                    dd          }|                    dd          }	|                    dd          }
t)          d|           t)          d|           |	rt)          d|	           t)          dt          d          pd           t                       g d }t+          d!|d          }|dk    rOt)          d"d#           t)          d$d%           t)          d&d           t          d'           t	          d(           n|d)k    r@t)          d"d*           t)          d$d+           t)          d&d           t%          d,           n|d-k    rj|
pd}t-          d.|d
/                              d0d          }t)          d"d1           t)          d$d%           t)          d&|           t          d2           n?t)          d"d3           t)          d$d%           t)          d&d           t%          d4           t                       t	          d5           t	          d6           t	          d7           t	          d8           t	          d9           t	          d:           g d;}t+          d<|d          }|dk    r0t)          d=d3           t)          d>d           t	          d?           n|d)k    r0t)          d=d*           t)          d>d           t%          d@           nUt-          dAdd
/                              d0d          }t)          d=d1           t)          d>|           t          dB           |
rDt                       t          dC|
 dDd          r"t)          dE|
           t          dF|
            t                       t          dG           t	          dH|            |
rt	          dI|
            dS dS )Jz8Interactive setup for Weixin / WeChat personal accounts.u0     ─── 💬 Weixin / WeChat Setup ───z>  1. Hermes will open Tencent iLink QR login in this terminal.z0  2. Use WeChat to scan and confirm the QR code.zG  3. Hermes will store the returned account_id/token in ~/.hermes/.env.zL  4. This adapter supports native text, image, video, and document delivery.r  r  zWeixin is already configured.z  Reconfigure Weixin?FNr   )check_weixin_requirementsqr_loginz   Weixin adapter import failed: z1  Install gateway dependencies first, then retry.z>  Missing dependencies: Weixin needs aiohttp and cryptography.z2  Install them, then rerun `hermes gateway setup`.z  Start QR login now?Tz  Cancelled.z  Weixin setup cancelled.z  QR login failed: z  QR login did not complete.
account_idr   rA  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 messageszOnly allow listed user IDszDisable direct messages+  How should direct messages be authorized?WEIXIN_DM_POLICYpairing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`.rm   r   r  z$  Open DM access enabled for Weixin.rQ   z+  Allowed Weixin user IDs (comma-separated)r  r   	allowlistz  Weixin allowlist saved.disabledz  Direct messages disabled.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 IDsz$  How should group chats be handled?WEIXIN_GROUP_POLICYWEIXIN_GROUP_ALLOWED_USERSz  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: )rx  r    r   r  r   r   r   r   gateway.platforms.weixinr  r  ru   r   rc  rU   r8   r   rd  r   r   r   r   r   r   )existing_accountr  r  r  r  rc  credentialsr  rA  r  r  r  r  default_allowr  group_choices	group_idxallow_groupss                     r0   _setup_weixinr  C  s5   	GGG	%BFK
P
PQQQ	GGGOPPPABBBQ   V   %%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+,,W0W  
 
GGG  N 5~q J Q)9555/999-r222-...g	
 	
 	
 	
 
q)6222/888-r222<====	q29=SX
 
 

'#r

 	 	);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R   W   X   S   Y   =>>>  M
 DmUVWWIA~~,j9993R888,----	a,f5553R888[	
 	
 	
 	
 M
 
 
 '#r

	 	
 	,k:::3\BBBY	
 	
 	
  >I'III4
 
 	> 0':::<7<<===	GGG&''',
,,--- ,***+++++, ,s6   C 
D%!DD22F% %'G5	G5G00G5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)z-Enter existing App ID and App Secret manuallyz&  How would you like to set up QQ Bot?r   )qr_registerz  QQ Bot setup cancelled.z:  QR setup did not complete. Continuing with manual input.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.z  App IDr  u2     Skipped — QQ Bot won't work without an App ID.z  App SecretTu6     Skipped — QQ Bot won't work without an App Secret.r   )app_idclient_secret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  zJ  Unknown users can request access; approve with `hermes pairing approve`.rm   r  z$  Open DM access enabled for QQ Bot.z(  Allowed user OpenIDs (comma-separated)r   z  Allowlist saved.z  Use your QQ user ID (r  r  r  z8  Home channel OpenID (for cron/notifications, or empty)u   🐧 QQ Bot configured!z
  App ID: )rx  r    r   r  r   r   r   r   gateway.platforms.qqbotr  rd  r   r   r   rW   r   r   r   )existing_app_idexisting_secretmethod_choices
method_idxr  r  r  
app_secretr!  r  r  r  r  home_channels                 r0   _setup_qqbotr*    s   	GGG	%96;
G
GHHH#K00O#$677O ? 56664e<< 	F 
GGG=7N 0.! J KQ	;;;;;;%+--KK  	 	 	GGG5666FF	  	USTTT  
OPPPQRRR
U333 	NOOOFNT:::
 	RSSSFllnn'--//
 
 ;H 5666%{?'CDDD//-44K 
GGG  N
 5~q J Q+W555 
	3GGGD;DDDd  7 1;???B[BBCCCC126666-r222-...X	
 	
 	
 	
 
q+V444)2...<====#)r6PU
 
 

'#r

 	 	+W555)9555*+++  KIkIII4
 
 	B /===@;@@AAAFQV
 
 
  	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  rT     z!  signal-cli daemon is reachable!z#  signal-cli responded with status .z  Continue anyway?z   Could not reach signal-cli at r  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  )rw   rx  r    r   r  r   r   r   rx   r   r   inputrW   EOFErrorrd  httpxr   r  status_coderu   r   r   )rw   existing_urlr  default_urlurlr4  respr  default_accountr  existing_alloweddefault_allowedallowedexisting_groupsgroupss                  r0   _setup_signalr@  l  s7   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'(((T	
 	
 	
 	8999>???PQQQ9:::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F
 
 	 FFFFF	 	 	 	 	 $c*** 
GGGJKKK()))&,"OU/Q6O6666rUUU
 

%'' 	  	&%G'(   $%%%  3444#W--- 
GGGHIIISTTT$%;<<B&1'O:o:::;;AACCV 	 '(   $%%% )7333 
GGGF  = 	EFFF'(DEEK	Ao&<AAABBHHJJ " F
 +, 	 	 	()))FF	 	3V<<<	GGG&'''&W&&'''ABBB_-0L"M"M]YYS]__    sa   >'F& & G
	G
A4I 
J%JJ1L	 	 L-,L-'N= = O! O!+Q- - RRrB  c                 v    ddl m} |j        |j        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  webhooksr   r~  r  )r  rB  _setup_bluebubbles_setup_webhooksr@  r  r*  r   )rB  _ss     r0   _builtin_setup_fnrG    sM     '&&&&&" ,&  
 5 6 
c#hh7r2   c           	         |                      d          }||j        |                                 dS t          | d                   }| |             dS |                      d          rt          |            dS |                      d| d                   }|                      dd          }t	                       t	          t          d| d	| d
t          j                             |r|j        ng }|r&t          dd
                    |                      nt          d| d| d                     |                      d          rt          d| d                     dS dS )a?  Run the interactive setup flow for a single platform.

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

    Bundled platform plugins (e.g. IRC) auto-load, so no plugin enable step
    is needed here. User-installed platform plugins under ~/.hermes/plugins/
    must already be in ``plugins.enabled`` before they appear in this menu.
    r  NrB  r{  rj   rx  u   🔌r  r   r  z(  Set these env vars in ~/.hermes/.env: r  z  Configure z( in config.yaml under gateway.platforms.r  r  )r   setup_fnrG  r  rx  r    r   r  r  r   r   )r  r   fnrj   rx  requireds         r0   _configure_platformrL    s    LL*++EU^7	8E?	+	+B	~
||F  *** LL(5/22ELL&))E	GGG	%>u>>u>>>
L
LMMM%*2u!!H 
Sdii>Q>QSSTTTT[5[[(SX/[[	
 	
 	
 ||N## 42022333334 4r2   c                  H   t                      rt          d           dS t                       t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t                       t                      } t                      }t                      r*t                      rt                       t                       t                      r*t                      rt                       t                       | r|rt          d	           na| r@t          d
           t                      rt                      rt!          d           n"t#          dd          r	 t                      rt%                       nt'                      rt)                       n# t*          $ rO}t-          d           t/          |                                          D ]}t          d|            Y d}~nd}~wt2          $ r+}t-          d|            t!          d           Y d}~nPd}~wt4          j        $ r}t-          d|            Y d}~n'd}~ww xY wnt9          d           t9          d           	 t                       t;          d           t=                      }d |D             }|                    d           tA          d|tC          |          dz
            }|tC          |          k    rntE          ||                    dt.          dtF          fdtI          fdt=                      D                       }|rzt                       t          t          dt          j%                             t                      } t                      }|r~t                      rt                      rt!          d           nt#          dd          r>	 t                      rtM                       nct'                      rtO                       nFtQ                      rdd l)m*} |+                                 ntY                       t9          d!           n# t*          $ rP}t-          d"           t/          |                                          D ]}t          d|            Y d}~n-d}~wt2          $ r,}t-          d#|            t!          d           Y d}~nd}~wt4          j        $ r}t-          d#|            Y d}~nd}~ww xY wn| r`t                      rt                      rt!          d           nt#          d$d          r 	 t                      rt%                       nEt'                      rt)                       n(tQ                      rdd l)m*} |-                                 n# t*          $ rP}t-          d%           t/          |                                          D ]}t          d|            Y d}~nd}~wt2          $ r,}t-          d&|            t!          d           Y d}~nd}~wt4          j        $ r}t-          d&|            Y d}~nld}~ww xY wnbt                       t                      st'                      stQ                      rTt                      rd'}	nt'                      rd(}	nd)}	t]                      rd*nd+}
t#          d,d          }t#          d-|	 d.|
 d          }|s|r	 d}d/}t                      rt_          d/|0          \  }}n?t'                      rta          d/1           d}ndd l)m*} |1                    d/1           d}t                       |r|r	 t                      rt%          |d2k    3           nEt'                      rt)                       n(tQ                      rdd l)m*} |-                                 n# t*          $ rO}t-          d%           t/          |                                          D ]}t          d|            Y d}~n1d}~wt4          j        $ r}t-          d&|            Y d}~nd}~ww xY wn^# t4          j        $ r,}t-          d4|            t9          d5           Y d}~n(d}~ww xY wt9          d6           t9          d7           t                      rt9          d8           t9          d9           nt]                      r=t9          d:           t9          d;           t9          d<           t9          d=           nte                      r@dd>l3m4} t9          d?           t9          d;           t9          d@ |             dA           n<t9          dB           t9          d;           nt                       t9          dC           t                       dS )Dz<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.r  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 )rx  r   rj   r  rl  r  r  s     r0   r   z!gateway_setup.<locals>.<listcomp>  sO     
 
 
FGqzAAAgJAA+;A+>+>AAA
 
 
r2   DonezSelect a platform to configure:rm   r  r*   c                     |                                  }|dk    p)|                    d          p|                    d           S )Nr  	partiallyzplugin disabled)r   r   )r  ss     r0   _is_progressz#gateway_setup.<locals>._is_progress  sK    LLNN!! /||K((/||-..
 	
r2   c              3   H   K   | ]} t          |                    V  d S r,   rO  )r   r   rT  s     r0   r{  z gateway_setup.<locals>.<genexpr>  s6      UUq&6q&9&9::UUUUUUr2   u   ──────────────────────────────────────────────────────────rS  z)  Restart the gateway to pick up changes?r   r  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: r1  r  zScheduled Taskz. (note: services may not survive WSL restarts)r   z  Start the gateway now?z5  Start the gateway automatically on login/boot as a z	 service?F)r  r  r  r,  r.  z  Install failed: z.  You can try manually: hermes gateway installz%  Skipped start and auto-start setup.z/  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  z/  Termux does not use systemd/launchd services.zR  Or start it manually in the background (best effort): nohup hermes gateway run >r  z1  Service install not supported on this platform.z?No platforms configured. Run 'hermes gateway setup' when ready.)5r   r   rx  r    r   MAGENTAr  r  rS   rt  r  r  r  r   r   rQ  rT  r   ru  r_   r  r4  r   r8   rX   r7  rT   r{  r   r   r  r   r   rc   rL  r-   r}  DIMr|  r	  rv   r  r  rS  r  r  r  r  r  r]  r  r  r  )r%   r&   r  rg   r  
menu_itemsr  any_configuredr  platform_namewsl_note	start_nowstart_on_logininstalled_scopedid_installr  rT  s                   @r0   gateway_setupr`  A  s   || )***	GGG	 @N	
 	
   
NPVP^	
 	
  
 
 @N	
 	
   
MN	
 	
   
Lfn	
 	
  
 
 @N	
 	
   
GGG-//)++O "" 'D'F'F ,... "" '>'@'@ !### S_ SABBBB	 SEFFF$&& 	7+O+Q+Q 	7+G4444,d33 	77,.. $!OOOOZZ $!OOO. ' ' 'OPPPFF--// ' 'D+t++&&&&' ' ' ' '/ 9 9 9 5!55666/888888880 7 7 75!55666666667!	7& 	:;;;QRRR/*+++"$$	
 
KT
 
 

 	&!!!-z3z??Q;N
 
 S^^##If-...#/0
S 
T 
 
 
 
 UUUUNDTDTUUUUUN HVeJ
++,,,133-// 	F(** :/S/U/U :/	::::JDQQ ::022 
E'))))! E'))))# E>>>>>>'//1111,..."#CDDD2 + + + RSSS #A 1 1 3 3 + +k4kk****+ + + + +3 ? ? ? 8Q 8 89993I>>>>>>>>!4 : : : 8Q 8 899999999:):,  f	F(** 8/S/U/U 8/8888=tDD 88022 0%! 0%# 0>>>>>>'--///2 + + + PQQQ #A 1 1 3 3 + +k4kk****+ + + + +3 = = = 61 6 67773G<<<<<<<<!4 8 8 8 61 6 6777777778#8( GGG(** NFhjj NFJLL NF,.. 5$-MMZZ 5$-MM$4MOUxx_KK]_)*DdKK	!.nMnndlnn" "  -N -N$U*.&+466 /;[&+2@< < <8O[[ &ZZ /+%8888*.KKBBBBBB+33%3@@@*.K& D9 DD#<#>#> !<$1H9T$U$U$U$U$U%-ZZ !<$1OOOO%/\\ !<$J$J$J$J$J$J$3$9$9$;$;$;#> 7 7 7 +$T!" !" !" -0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 FGGGPQQQ022 "_   LMMMM FGHHHDEEER   c     
FHHHHHHLMMMDEEE Jimimioio  J  J  J    NOOODEEEETUUU	GGGGGs   9I 
LAJ&&L3!KL+LLB T 
W
AU''W
4!VW
-WW
A"Y5 5
\-?A[

\-![>>\-\((\-A8d6 A(b/ .d6 /
d19Ad>d6 d1d,'d6 ,d11d6 6e1!e,,e1c                    ddl m}m}m}m}  |            dk    rdS |t                      pd} |            }d| }	 | dk    r|                    |           n:| d	k    r|                    |           n| d
k    r|                    |           ndS nk# |$ r0}t          d|            t          j        d           Y d}~n;d}~w|$ r0}t          d|            t          j        d           Y d}~nd}~ww xY wdS )u  If we're in a container with s6, dispatch gateway lifecycle via s6.

    Returns True iff dispatched (caller should ``return``); False
    otherwise — caller continues with the host-side code path.

    ``action`` is one of ``start`` / ``stop`` / ``restart``. The
    profile defaults to the current one (resolved via ``_profile_arg``).
    The s6 service slot was created either by the Phase 4 profile-create
    hook or by the container-boot reconciler (cont-init.d/02-…). If it
    doesn't exist or s6 returns an error, the named errors from
    :mod:`hermes_cli.service_manager` are caught and surfaced as
    actionable CLI messages (no raw ``CalledProcessError`` traceback).
    r   )GatewayNotRegisteredErrorS6CommandErrorr  r  r  FNr   r  r  r  rS  r  rm   T)r  rb  rc  r  r  r  r  r  rS  rx  r  rr  )	r  r?   rb  rc  r  r  r  r  r  s	            r0   #_dispatch_via_service_manager_if_s6rd  H  s                4''u
 "##0y



C'g''LWIIl####vHH\""""y  KK%%%%5$   lSll   lSll 4s$   AB C>&CC>&C99C>c           	      >   ddl m}m}  |            dk    rdS | dvrdS  |            }|                                }|st	          d           dS | dk    r|j        n|j        }g }|D ]A}d	| }	  ||           # t          $ r!}	|                    ||	f           Y d
}	~	:d
}	~	ww xY wt          |          t          |          z
  }
| dk    rdnd}|
r(t	          d|
                                 d|
 d           |D ]\  }}	t	          d|  d| d|	            dS )u  Inside a container with s6, dispatch ``--all`` lifecycle to every
    registered profile gateway.

    Returns True iff dispatched (caller should ``return``); False
    otherwise — caller continues with the host-side code path.

    Without this, ``hermes gateway stop --all`` and ``... restart --all``
    fall through to ``kill_gateway_processes(all_profiles=True)``, which
    just ``pkill``s every gateway process. s6-supervise observes the
    crash and restarts each one ~1s later — so ``--all`` ends up
    *kicking* every gateway instead of *stopping* it. By iterating
    ``list_profile_gateways()`` and sending the lifecycle command
    through the service manager we get the intended semantics (s6's
    ``want up``/``want down`` flips correctly so supervise stays down
    after a stop).

    ``action`` is one of ``stop`` / ``restart`` (``start --all`` isn't
    a supported CLI surface).
    r   r  r  F)r  rS  u+   ✗ No profile gateways registered under s6Tr  r  Nr  	restartedrk  r   z profile gateway(s) under s6u   ✗ Could not z	 gateway-r  )r  r  r  list_profile_gatewaysrx  r  rS  ru   r   rc   rw  )r  r  r  r  r  rJ  r   r?   r  r  	succeededverbs               r0   '_dispatch_all_via_service_manager_if_s6rj  {  s   (       
 4''u(((u



C((**H ;<<<tv%%3;B*,F * *+'++	*B| 	* 	* 	*MM7C.))))))))	*HF+I&((99kD RPT__&&PPPPPQQQ B B@v@@@@3@@AAAA4s   0A<<
B'B""B'c                    	 t          |           S # t          $ rd}t          d           t          |                                          D ]}t          d|            t          j        d           Y d}~dS d}~wt          $ r;}t          t          |                     t          j        d           Y d}~dS d}~ww xY w)zHandle gateway subcommands.zUser systemd not reachable:r  rm   N)	_gateway_command_innerr4  r   r8   rX   rx  r  rr  r7  )r  r  rg   s      r0   gateway_commandrm    s    %d+++&    	1222FF%%'' 	 	D+t++'    	c!ffs!    
CAA::C0B==Cc                    t          | dd          p3t          j                            dd                                          dv }|rdS t          j                            d          rdS t          d          sdS t          dt          j        d	
           	 t          j	        dddg           n:# t          $ r- t          dt          j        d	
           t                       Y nw xY wd	S )u[  Inside an s6 container, redirect bare ``gateway run`` to the
    supervised path.

    Background. Before the s6 image landed, ``docker run <image> gateway
    run`` was the standard way to start a containerized gateway: the
    gateway was the container's main process, tini reaped zombies, and
    container exit code == gateway exit code. With s6-overlay as PID 1,
    we'd much rather have the gateway run as a supervised s6 longrun
    (auto-restart on crash, dashboard supervised alongside, multiple
    profile gateways under the same /init). This redirect upgrades the
    old invocation transparently — the user gets the new behavior
    without changing their docker run command.

    Three gates make this a no-op outside the intended scope:

      1. ``_dispatch_via_service_manager_if_s6`` returns False unless
         we're in a container with s6 as PID 1. Host runs of
         ``hermes gateway run`` are unaffected.
      2. ``HERMES_S6_SUPERVISED_CHILD`` is exported by
         ``S6ServiceManager._render_run_script`` for the supervised
         process itself — i.e. when s6-supervise execs ``hermes gateway
         run --replace`` as a longrun, this guard short-circuits the
         redirect so the supervised gateway actually runs in
         foreground (otherwise we'd recurse: run → start → run → start
         → ...).
      3. ``--no-supervise`` (or ``HERMES_GATEWAY_NO_SUPERVISE=1``) opts
         out for users who genuinely want pre-s6 semantics — CI smoke
         tests, debugging the foreground startup path, etc.

    Returns True iff dispatched (caller should ``return``).
    no_superviseFHERMES_GATEWAY_NO_SUPERVISEr   )r	  r  r  r  r  uo  → gateway is now running under s6 supervision (auto-restart on crash,
  dashboard supervised alongside if HERMES_DASHBOARD is set).
  This is the recommended setup for the s6 container image — the
  gateway will keep running even if it crashes.
  Use `--no-supervise` (or HERMES_GATEWAY_NO_SUPERVISE=1) to opt out
  and get the pre-s6 foreground behavior instead.T)fileflushr   infinityug   → `sleep` is unavailable; keeping the s6 CMD process alive in-process until the container is stopped.)r  r~   rG  r   r   rd  rx  r  r  execvpr   _block_until_terminated)r  ro  s     r0   %_maybe_redirect_run_to_s6_supervisionrv    s(   @ 477 Z

4b99??AAEYY  u	z~~233  u.w77 u 
	< Z	 	 	 	6"
	'GZ01111 
" 
" 
" 	9		
 	
 	
 	
 	 !!!!!
" 4s   B1 14C('C(c                      t          j         t           j        d            t          t           dd          } | 	  |              ddl}|                                                                 dS )ue  Keep the s6 CMD process alive until the container is stopped.

    Fallback heartbeat for when ``os.execvp("sleep", ...)`` can't run
    (``sleep`` missing from PATH — issue #36208). Installs a SIGTERM
    handler that exits with the conventional 128+signum code so
    ``docker stop`` produces a clean, expected exit, then blocks on
    ``signal.pause()``. Falls back to ``threading.Event().wait()`` on
    platforms without ``signal.pause()`` (e.g. Windows) — although this
    path only runs inside the s6 Linux container image, the fallback
    keeps the helper safe to import and unit-test anywhere.
    c                 0    t          j        d| z             S )N   )r  rr  )signum_frames     r0   <lambda>z)_block_until_terminated.<locals>.<lambda>,  s    #,9O9O r2   pauseNTr   )r   r  r  	threadingEventwait)r}  r~  s     r0   ru  ru     sx     M&."O"OPPPFGT**E	EGGG	 	     r2   c           
      +   t          | dd           }||dk    rjt          |           rd S t          | dd          }t          | dd          }t          | dd          }t          | dd          }t          ||||	           d S |d
k    rt                       d S |dk    rt	                      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          t          j        d          ot          j                                         }t          | dd           }	|	|	}
n|st#          dd          }
nd}
t          | dd           }||}n|st#          dd          }nd}t%          |||||           |
rt'          |           d S d S t)                      rt+          |           d S t-                      rNddlm} |                    |t          | dd           t          | dd           t          | dd                     d S t                      r~t          d            t          d!           t          d"           t                       t          d#           t          d$           t          d%           t          j	        d           d S t5                      rdd&lm}  |            d'k    rLt          d(           t                       t          d)           t          d*           t          d+           d S t          d,           t          d-           t                       t          d.           t          d/           t                       t          d0           t          j	        d           d S t          d1           t          d2           t          j	        d           d S |d3k    rt	                      rt          d4           d S t          | dd          }t                      r2t          d5           t          d6           t          j	        d           t                      rt;          |           d S t)                      rt=                       d S t-                      rddlm} |                                 d S t5                      rdd&lm}  |            d'k    r=t          d7           t                       t          d8           t          d9           d S 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          }|stA          d?          rd S |r6tC          dA          }|r$t          dB| dC           tE          dDdEF           t                      r2t          dG           t          d           t          j	        d           t                      rt'          |           d S t)                      rtG                       d S t-                      rddlm} |$                                 d S t                      rt          dH           t          dI           t                       t          d#           t          d$           t          d%           t                       t          dJ           t          j	        d           d S t5                      r}t          dK           t          dL           t                       t          dM           t          dN           t                       t          dO           t          j	        d           d S t          d>           t          j	        d           d S |dPk    rvtK          j&        dQ          dRk    r#tO          dS           t          j	        d           t          | d@d          }t          | dd          }|rtQ          dP          rd S |stA          dP          rd S |rod}t                      rmtS          d          *                                s"tS          d          *                                r)	 tW          |           d}n# tX          j-        $ r Y nw xY wt)                      rGt]                      *                                r'	 t_                       d}nq# tX          j-        $ r Y n`w xY wt-                      rNddlm} |0                                r4	 |1                                 d}n# tX          j-        td          f$ r Y nw xY wtC          dA          }||rdndz   }|rt          dT| dU           d S t          dV           d S d}t                      rmtS          d          *                                s"tS          d          *                                r)	 tW          |           d}n# tX          j-        $ r Y nw xY wt)                      rGt]                      *                                r'	 t_                       d}nq# tX          j-        $ r Y n`w xY wt-                      rNddlm} |0                                r4	 |1                                 d}n# tX          j-        td          f$ r Y nw xY w|s0tg                      rt          dW           d S t          dX           d S t          dTti                       dY           d S |dZk    rptK          j&        dQ          dRk    r#tO          d[           t          j	        d           d}t          | dd          }t          | d@d          }d}|rtQ          dZ          rd S |stA          dZ          rd S |rWd}t                      rmtS          d          *                                s"tS          d          *                                r)	 tW          |           d}n# tX          j-        $ r Y nw xY wt)                      rGt]                      *                                r'	 t_                       d}nq# tX          j-        $ r Y n`w xY wt-                      rNddlm} |0                                r4	 |1                                 d}n# tX          j-        td          f$ r Y nw xY wtC          dA          }||rdndz   }|rt          dT| dU           tE          dDdEF           t          d\           t                      rUtS          d          *                                s"tS          d          *                                rt'          |           nvt)                      r/t]                      *                                rtG                       n9t-                      rddlm} |$                                 nt          d]           d S t                      rotS          d          *                                s"tS          d          *                                r+d}	 tk          |           d}n# tX          j-        $ r Y nw xY wt)                      rIt]                      *                                r)d}	 tm                       d}nv# tX          j-        $ r Y new xY wt-                      rSddlm} |0                                }	 |7                                 d S # tX          j-        td          tp          f$ r Y nw xY w|sUt                      rts                      \  }}|durdd l:}|;                                }t                       t          d^           t          d_           t                       t          d`|            t                       t          da           t          db           d S |rOt                       t          dc           t          dd           t          de           t          j	        d           tg                      rt          dW           tE          dDdEF           t          d\           t          d]           d S d S |dfk    rt          | dgd          }t          | dhd          }t          | dd          }ty          |          }d}t-                      rddlm} |0                                }t                      rgtS          d          *                                s"tS          d          *                                r#t{          |||i           t}          |           nt)                      r@t]                      *                                r t          |           t}          |           n|r-ddlm} |@                    |j           t}          |           nt          |jB                  }|rbt          dkdlC                    t          t          |                     dm           t          dn           t                      } | r4t                       t          do           | D ]}!t          dp|!            t                       t                      r t          dq           t          dr           nt                      r/t          ds           t          dt           t          du           nct-                      r t          dv           t          dw           n5t          dx           t          dw           t          dy           nt          dz           t                      } | r4t                       t          do           | D ]}!t          dp|!            t                       t          d{           t          d|           t                      rt          d}           nit                      rt          d$           t          d%           n<t-                      rt          d~           nt          d           t          d           t                       d S |dk    rt                       d S |dk    rct          | dd          }"t          | dd          }#t                      st)                      st          d           d S t          |# |"           d S d S )Nrm  rU   r8  r   r9  Fr   r  )r9  r   r  rB  r]  z*install gateway service (managed by NixOS)r,  r  z8Gateway service installation is not supported on Termux.zRun manually: hermes gatewayrm   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  r\  z3Start the gateway now after installing the service?Tr]  z;Start the gateway automatically on login/boot with systemd?)r  r,  r  r  r[  r.  r  elevated_handoff)r  r\  r]  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 &  # background)r  r  zCPer-profile gateways are auto-registered when you create a profile.z@  hermes profile create <name>     # creates the s6 service slotz7  hermes -p <name> gateway start   # bring it up via s6zF  hermes status                    # see currently-supervised gatewaysz=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 runrk  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 stopzGPer-profile gateways are auto-unregistered when you delete the profile.zC  hermes profile delete <name>     # tears down the s6 service slotzF  hermes -p <name> gateway stop    # stop without deleting the profilez>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  r  )r   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  _HERMES_GATEWAYr	  zRefusing to stop the gateway from inside the gateway process.
This command was blocked to prevent restart loops.
Use `hermes gateway stop` from a shell outside the running gateway.u   ✓ Stopped z( gateway process(es) across all profilesu   ✗ No gateway processes foundu$   ✓ Stopped gateway for this profileu'   ✗ No gateway running for this profilez servicerS  zRefusing to restart the gateway from inside the gateway process.
This command was blocked to prevent restart loops.
Use `hermes gateway restart` from a shell outside the running gateway.zStarting gateway...)r8  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 rJ  r%  u#   ✗ Gateway service restart failed.zL  The service definition exists, but the service manager did not recover it.z3  Fix the service, then retry: hermes gateway startr  r}  r~  )r,  r~  )r}  u   ✓ Gateway is running (PID: r  rl  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 Windows Scheduled Task (auto-start on login):z  hermes gateway installzTo install as a service:z&  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 startzS  hermes gateway install  # Install as Windows Scheduled Task (auto-start on login)z3  hermes gateway install  # Install as user servicezM  sudo hermes gateway install --system  # Install as boot-time system servicerO   zmigrate-legacyr  r  z@Legacy unit migration only applies to systemd-based Linux hosts.)r  r  )Jr  rv  rm  r`  r   r   r  rx  r  rr  rS   r  r   r   r   r  r  r   r  ru  r_   r  rv   r  r  r]  r  r  r  rm  r  rk  rd  r  r  r  r  r~   r  r   rj  r3  r4  ry  rT   r{  r  r  r  r  r7  r  r6  r|  r	  rS  r   r]  r[  r\  r  r  r  r  r  rO   r(   r   mapr8   r  r  r  r  )$r  subcmdr8  r9  r   r  r,  r  r[  _snr\  _solr]  r  r  	start_allr  stop_allservice_availabletotalrestart_allservice_configuredservice_stopped	linger_ok_detailr[  	_usernamer}  r~  r  _windows_service_installedrd   r  rg   r  r  s$                                       r0   rl  rl  7  sH   T,d33F ~5066 	F$	1--gu--$	511gu--G5'GGGG << 	FGGGFgu--x//dM488;; 	LMMM0111HQKKK$&& c	xx 
U   R   c    $+39h#?#?#VCIDTDTDVDVWO$T22C		$ !)*_aeff		 	4!1488D!%$ &!./lnr!s!s!%'"0 /     -V,,,,,,- -ZZ ;	E"""""\\ 9	222222##!$T::&t-=tDD!(/A5!I!I	 $      XX 0	<===[   :;;;GGGW   Y   ^   HQKKKKK^^ 	 JIIIII%%''4//[\\\XYYYOPPP^___
 QRRRh   GGG\   PQQQGGG:;;;HQKKKKKHIII4555HQKKKKK	;		<< 	HIIIFx//;; 	u   >???HQKKK$&& 	V,,,,,,ZZ 	\\ 	222222%%'''''^^ 	IIIIII%%''4//_```[\\\^___RSSSFGGGGGG-...+,,,HQKKKKK3444HQKKKKK	7		x//D%//	  	@II 	F 	F+>>>F FX&XXX   'tEEEE;; 	n   0111HQKKK$&& *	((((((ZZ (	OOOOO\\ &	222222!!#####XX "	>????@@@GGGW   Y   ^   GGGp   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		 9&''3..V  
 HQKKK4..x//  	?GG 	F 	?GG 	F D	C %(** %U333::<<(555<<>> ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D 666666"//11 ',,...,0))&9<H   +>>>F#4;aa!<E 8TUTTTUUUUU677777 !&(** %U333::<<(555<<>> ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D 666666"//11 ',,...,0))&9<H    % 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		 9&''3..Y  
 HQKKK "x//dE511"  	B9MM 	F 	B9MM 	F 6	#O(** %U333::<<(555<<>> ////&*OO!4   D  6 8 8 ? ? A A  NNN&*OO!4   D 666666"//11 ',,...*.&9<H   +>>>F?9aa:E VTUTTTUUU"4SAAAA '((((** '%U333::<<'(555<<>>' V,,,,, ' 6 8 8 ? ? A A ' '666666  %%''''A&&&&F$&&  	!///6688 	$D11188:: 	 "&v....$(!!0   ZZ 	244;;== 	!%!!!$(!!0   \\ 	222222 "1!=!=!?!?'')))1<I    ! '	#(** %>%@%@"	7D(("NNN ' 1 1IGGG\   e   GGGLLLMMMGGG78886777F! ;<<<b   KLLL $%% ><==="4SAAAA '(((""""""O'	# '	#R 
8		tVU++tVU++x///v>>> &+"<< 	H222222)8)E)E)G)G&$&& M	!///6688M	$D11188::M	 4T::::+H5555ZZ G	244;;== G	4   +H5555' D	222222"""---+H5555 -..D <RdiiC6O6ORRRSSSEFFF 5 7 7  +GGG2333 - + +k4kk****;; D.)))WXXXXXX D+&&&c   V     \\ DW   4555545554555BCCCC2333 5 7 7  +GGG2333 - + +k4kk****k"""EFFF;; x    XX a   f     \\ m    OPPPg  
 	-.....	6			#	#	# $	511dE5))(** 	8:: 	TUUUF"3wHHHHHH 
$	#s   5f ffg g/.g/h2 2i
i$k7 7l	l	;m mm
n! !n:9n:6t	 	ttu u0/u0v3 3ww}   }21}2&~7 7		5A@ @A@*@)A@*)FFr  r,   )F)FrL  )FNrV  )FNF)NN)rO  )TF)FT)FFNTF)FFF)r  r  )r   FFF)r5  rc  r  loggingr~   r  rw   r   rT   r  r!  r   dataclassesr   pathlibr   r   	_sys_dirsrR   rG  r   rY   r  
_path_dirs_missingr   sorted__file__r   r   r  r   r   gateway.restartr   r   r   hermes_cli.configr   r   r   r   r   r   r   hermes_cli.setupr   r   r   r   r   r   r   r   hermes_cli.colorsr   r    	getLoggerr5   r  r#   r>   rk   r;   r{   r-   r   r   floatr   r   rO   r   r   r   r   r   r8   r  r
  r  r  r  r:   r:  rb  rD  rK  rU  rZ  r]  rd  rg  r  ry  r{  r  r  rz  r  rb   r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  rS   r_   rv   r  r+  r  r  r   r(  r6  r3  r7  r4  r7  rB  rE  rG  rN  rQ  rc  r^  rg  rj  CompletedProcessr5  rv  rr  rt  ru  r9   rv  rx  r  r  r  r  r  r  r  r  r  r  r  r]  r  r  r  r  r  r  r  r  r  r  r
  r  r   r%  r(  r/  r7  r?  rC  rH  rK  rN  r2  rQ  rT  r  r  rm  rs  ru  ry  r|  r  r`   r  r  	frozensetr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r  r  r  r0  r2  r4  r7  rm  r  r  r  r  r  r  r  r  r  r*  r@  rG  rL  r`  rd  rj  rm  rv  ru  rl  r'   r2   r0   <module>r     s        				        



   ! ! ! ! ! !       7g:::IRZ^^FB//55bjAABBJ:%H iZ^^FB77"*DrzW]W]^fWgWgGhGhh
6tH~~$+3355 ( ( ( ( ( (         
                 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 , + + + + + + +		8	$	$ $T T T T T T T T  $       M3 M M M M`,2 ,2t ,2 ,2 ,2 ,2^C D    
s 
t 
 
 
 
3s 35 3T 3 3 3 3lCH    (
s)*47H	    %*] ]c(]] #] 
#Y	] ] ] ]@.d3i .DI . . . .> ;@# #*#37#	# # # #N  $ *	
    63 49    $s $tCy4'7 $ $ $ $NCC IC	C C C C [S [3 [4 [ [ [ [`C `49 ` ` ` ` `F> >4 >E$*<M > > > >$$ $4 $DcN $ $ $ $N* * * * * *, #& &&c3h& 
#s(^& & & &R$S#X $3: $ $ $ $V Vd VsTz V V V V6dTk 6 6 6 6
/t 
/t 
/ 
/ 
/ 
/ #	@ @ @@ *@ 	@
 
@ @ @ @FI$sCx. IT I I I Ij.K PT    "X Xd Xt X X X XL LD LT L L L L  6:7 77(+d
7	7 7 7 7t d
    .J J J J J0G G G:P G G G GV ?@
 
 

S/DI
%
14t

 
 
 
N.D N N N N N.   2&# &# &# &#T PU '*TzHL   <?D ? ? ? ?D2d 2 2 2 2j,$ , , , , = < < < < < < < < <-$ - - - -  $    "    &	4 	 	 	 	$$ $ $ $ $#D # # # #    2 !M > > > > >8 cDj sTzD?P \_    B)c )C )C ) ) ) )
'# 
' 
' 
' 
'L L$ L4 L L L L    ,   1 1 1 1 1< 1 1 1*    -4 - - - -
D 
 
 
 
M M M M,( ( ( ( ( ( ($ ;? U U U4 U4 U U U Up++!+-0+	+ + + +$@ @4 @DI @ @ @ @B BD BT#Y B B B B
 (-R R R
s)R $R R R R R* * *# * * * *
d3i 
 
 
 
3t 3 3 3 3 *= uS#X < < <3 c3h   	4dDj(9#: 	 	 	 	#4c4o(>#? # # # #L- - - - -
4 4 4 4( c ccc 3T
?c c c cL= = = =$
S 
T 
 
 
 
 2  2#*  2c3PSm@T  2  2  2  2FD S4Z    cDj     5C$J  5  5  5  5F D T ]bcfimcmoscs]t    D/;5c)9#: /; /; /; /;d5 5 5 52D 2 2 2 2O O O O O$+    B	 	 	 	 	"Q$ Qd3i QDI Q Q Q Q%49 %c % % % %Ps S S    4## ## # # # #4 4$; $s)    DS    8y y$ yS4Z ySV y y y yxJ J J J J J  S S    # #    &+ +D +T + + + +(( (t ( ( ( (V 3 4     ) )4 )D ) ) ) )X 3 d
 d    (D (D (D (DV $ 4    0 0 0$ 0 0 0 00C 0D 0 0 0 02,E , , , , ""!K  K K K  tK  	K 
 K  K  K  K \R Rd R R R R& s D T    N N$ N N N NN N N N N N8`G `GD `G `G `G `GFv, v, v,t v,4 v, v, v, v,|L3 L L L L (, #* + + +A A A A AN $-9]]]#;#;   '0iC&9&9 #>:+H >T > > > >
=c =d = = = =   <>&
 &
 &
&
$'&
58&
	&
 &
 &
 &
R=$ = = = =
     s t    ++$'+6;+	+ + + +l>$ > > > >   "   7D 7 7 7 7d3i        D KO   # 4 SW    6d d d d dN
;$ 
; 
; 
; 
;B B B B BJ&? &?4 &? &? &? &?R% % % 7( 7( 7(t! ! !@ 8;1 11(-1	1 1 1 1hG, G, G,TOG OG OG OG OG OGnJsTz Jd J J J Jd    4    .M M$ M4 M M M M`% %d %t % % % %P d t    @   6j% j% j% j% j%UY j% j% j% j%B '	
 	
 	
 )D!Z	  +% @	  3>! $D  2r!Z	  0p!`	 5 
0 0j &	  "(	  )-

 

 

 1R!H	  /7 M	  4y! $y  3g!`	 )
!+ +Z  
 
 
 $)!;	  +- ?	  +_! $S  -X!N	 )
& &P %
 
 
 )"!J	  -& Z	 
 G}
@>T
 > > > >BMt M M M M M`"tCy " " " "J^ ^ ^PT ^ ^ ^ ^
o2t o2 o2 o2 o2vt     6(T 6( 6( 6( 6(rd, d, d,V{5 {5 {5|@ @ @F#3 # # # #L)4$ )44 )4 )4 )4 )4X@ @ @P (,0 00*0	0 0 0 0f0C 0D 0 0 0 0h  *Y4 Y Y Y Yx! ! ! !.T	I T	I T	I T	I T	Ir2   