
    ig                    >   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mZ  ee	          j
        j
                                        ZddlmZ ddlmZmZmZ ddlmZmZmZmZmZmZ ddlmZmZmZmZmZm Z m!Z!m"Z" ddl#m$Z$m%Z% d	e&fd
Z'de(d	e(dz  fdZ)de(d	e*fdZ+de(d	e*fdZ,dXde&dz  de*d	e-fdZ.	 	 dYde*de&dz  de*d	e(fdZ/d	e*fdZ0d	e*fdZ1ddl2m3Z3m4Z4m5Z5 d	e*fdZ6d	e*fdZ7d	e*fdZ8d	e*fdZ9dZ:dZ;d	e<fdZ=dZd e<dz  d	e<fd!Z>d	e<fd"Z?d[d#e*d	efd$Z@d\d%ZAd[d#e*d	e-e<         fd&ZBd[d#e*d	e-e<         fd'ZCdd(d)e-e<         d#e*d	ejD        fd*ZEd[d#e*d	e<fd+ZFd	e-e<         fd,ZGd	e*fd-ZHd\d.ZId/e<d	dfd0ZJdZd1e<dz  d	eKe<e<e<f         fd2ZLd3ed	e<dz  fd4ZMd	e<dz  fd5ZNd	e<dz  fd6ZOd[de*d	eKe<dz  e*f         fd7ZPd	eKe*dz  e<f         fd8ZQd\d9ZRd	efd:ZSd	efd;ZTd	edz  fd<ZUd	e<fd=ZVd>ed?e-e<         d	e-e<         fd@ZWdAe<dBe<d	e<fdCZXdBe<d	e<fdDZYd]d#e*d1e<dz  d	e<fdEZZdFe<d	e<fdGZ[dFe<d	e<fdHZ\d[d#e*d	e*fdIZ]d[d#e*d	e*fdJZ^dZdKe<dLe<dz  d	dfdMZ_d\dNZ`d[d#e*d	e*fdOZad	ebfdPZcd^de*d#e*d1e<dz  fdQZdd[d#e*fdRZed[d#e*fdSZfd[d#e*fdTZgd[d#e*fdUZhd_dVe*d#e*fdWZid	e<fdXZjd	e<fdYZkd	e<fdZZld	e*fd[Zmd	e*fd\Znd[de*fd]Zod^ Zpd_ Zqd` Zrd`dcebddebdz  d	e*fdeZsdf Ztd[dVe*fdgZudadhe(die*dje*fdkZvdldmdndog dpdodqdrdsdtdudvddrdwdxdydzdd{dtgd|d}d~ddg dddqdrddtddddrddxddzdddtgd|ddddg ddddrddtdddrddtddvddrddxgd|ddddg ddddddtdddrddtdddddtddddrddxdddddtgd|ddddg ddddddtddqdrddtddvddrddxddzdddtdddddtgd|ddddddddddddddg dâdddddtdddrddtdddddtdddddtddddrddxgd|dddndg dբdddddtdddrddtdddddtddddrddxdddddtgd|ddddg ddddddtdddrddtgd|ddddg ddddddtdddrddtdddddtdddddtddddrddxd ddddtgd|ddddg ddddddtd	d
drddtddddrddxdddddtgd|ddddg ddddddtdddrddtdddddtdddrddtdddrd dtd!d"dd#dtd$dddrd%dxgd|d&d'dd(dd)d*dd+g d,d+d-dd.dtd/d0drd1dtd2d3ddrd4dxd5d6dd7dtgd|d8d9d:d;g d<d;d=dd>dtd?d@drdAdtdBdCddrdDdxdEdFddGdtgd|gZwdHexd	e<fdIZyd	e-e<         fdJZzdHexfdKZ{dL Z|dM Z}dN Z~dO ZdP Zd	e*fdQZd	e*fdRZdS ZdT ZdU ZdV ZdW ZdS (b  zu
Gateway subcommand for hermes CLI.

Handles: hermes gateway [run|start|stop|restart|status|install|uninstall|setup]
    N)Path)terminate_pid)%DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT!GATEWAY_SERVICE_RESTART_EXIT_CODEparse_restart_drain_timeout)get_env_valueget_hermes_home
is_managedmanaged_errorread_raw_configsave_env_value)print_header
print_infoprint_successprint_warningprint_errorpromptprompt_choiceprompt_yes_no)Colorscolorreturnc            	         t                      } t                      r,ddgdgfD ]#}	 t          j        |g dz   ddd          }|j                                                                        D ]}|                                }|r|d                             d          s4|d         }	 t          j        |d	|d
dgz   ddd          }t          |j                                                  }|dk    r| 
                    |           # t          t          j        f$ r Y w xY w# t          t          j        f$ r Y !w xY wt                      r	 t                      }t          j        dd|gddd          }|j        dk    r|j                                                                        D ]w}|                                }t#          |          dk    rN|d         |k    rB	 t          |d                   }|dk    r| 
                    |           g# t          $ r Y sw xY wxn# t          t          j        f$ r Y nw xY w| S )ao  Return PIDs currently managed by systemd or launchd gateway services.

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

   tt#a::T1s!   *5 AA=9B7 7
CC
target_pidc                     | dk    rdS t          j                    }t                      }|r8||vr4|| k    rdS |                    |           t	          |          pd}|r||v4dS )zHReturn True when ``target_pid`` is this process or one of its ancestors.r   FT)osgetpidr)   r3   rM   )rN   rA   seens      rC   #_is_pid_ancestor_of_current_processrS      s~    Qu
)++CUUD
 (#T//*4c""'a	  (#T//
 5    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signalrS   rP   killrV   ProcessLookupErrorPermissionErrorOSError)rA   s    rC   _request_gateway_self_restartr]      sx    69%% u.s33 u
V^$$$$9   uu4s   A
 
A%$A%Fexclude_pidsall_profilesc                    | pt                      fdt                      D             }g d}t          t                                                                t                    }|r|                                d         nddt          dt          ffd}	 t                      r't          j
        g dd	d	d
          }d|j                            d          D ]}|                                }|                    d          r|t          d          d         C|                    d          r|t          d          d         }t          fd|D                       rb|s |          rU	 t!          |          }	|	t#          j                    k    r|	|vr|	vr|                    |	           n# t(          $ r Y nw xY wdnt          j
        g dd	d	d
          }|j                            d          D ]R}|                                }
|
rd|
v rd}	d|
                    dd          }t          |          dk    r1	 t!          |d                   }	|d         n# t(          $ r d}	Y nw xY w|	s|
                                }t          |          d
k    rL|d                                         r2t!          |d                   }	d                    |d
d                   |	|	t#          j                    k    s|	|v s|	v rt          fd|D                       r"|s |          r|                    |	           Tn# t.          t          j        f$ r Y nw xY w|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.
    c                     g | ]}|v|	S  rb   ).0rA   _excludes     rC   
<listcomp>z%find_gateway_pids.<locals>.<listcomp>   s#    FFFC#X2E2EC2E2E2ErT   )zhermes_cli.main gatewayzhermes_cli.main --profilezhermes_cli.main -pzhermes_cli/main.py gatewayzhermes_cli/main.py --profilezhermes_cli/main.py -pzhermes gatewayzgateway/run.pyrI    commandr   c                 d    rd | v pd | v pd | v S d| v sd| v rdS d| v r	d | vrdS dS )N
--profile z-p zHERMES_HOME=z -p FTrb   )rg   current_homecurrent_profile_names    rC   _matches_current_profilez3find_gateway_pids.<locals>._matches_current_profile   s     	3133w> </-//7:<0,00G; 7""f&7&75W$$)F)F)Fg)U)U5trT   )wmicprocessgetzProcessId,CommandLinez/FORMAT:LISTT
   r   
zCommandLine=Nz
ProcessId=c              3       K   | ]}|v V  	d S Nrb   )rc   pcurrent_cmds     rC   	<genexpr>z$find_gateway_pids.<locals>.<genexpr>   s(      >>1+>>>>>>rT   )rG   z-AewwrH   zpid=,command=greprF   r(   r    c              3       K   | ]}|v V  	d S rs   rb   )rc   patternrg   s     rC   rv   z$find_gateway_pids.<locals>.<genexpr>   s(      BBgw')BBBBBBrT   )r)   rD   rJ   r	   resolve_profile_argr0   bool
is_windowsr+   r,   r-   r.   
startswithr:   anyr2   rP   rQ   appendr4   isdigitjoinr\   r5   )r^   r_   r;   patternscurrent_profile_argrl   r=   r>   pid_strrA   strippedr?   	aux_partsrd   rg   ru   rj   rk   s                @@@@@rC   find_gateway_pidsr      s2    $suuHFFFF,..FFFD	 	 	H ((002233L&|44>QY.4466r::WY# $       9<< 6	%^SSS#$  F K++D11 % %zz||??>22 %"&s>':':';';"<KK__\22 	%"3|#4#4#5#56G>>>>X>>>>> !L !TlTlmxTyTy !!"%g,,C"bikk11coo#U]J]J] $C 0 0 0) ! ! ! D!"$K%  ^:::#	  F ++D11 % %::<< 6X#5#5 tQ//u::??#!%(mm"'(% # # #"# ; ( 0 0I9~~**y|/C/C/E/E*!)A,//"%((9RSS>":":;")++%%xBBBBBBBBB % %XpXpqxXyXy %KK$$$Z./    Ks^   $CM# AGM# 
GM# GBM# I87M# 8JM# JCM# #M<;M<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   rF   "   ⚠ Permission denied to kill PID zFailed to kill PID : N)r   r   rZ   r[   printr\   )r   r^   r_   r;   killedrA   excs          rC   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                     	 ddl m} m} n# t          $ r Y dS w xY w |             }|dS 	 t	          j        |t          j                   n.# t          $ r Y n"t          $ r t          d|            Y dS w xY wddl}t          d          D ]E}	 t	          j        |d           |                    d           .# t          t          f$ r Y  nw xY w |             dS )	u   Stop only the gateway for the current profile (HERMES_HOME-scoped).

    Uses the PID file written by start_gateway(), so it only kills the
    gateway belonging to this profile — not gateways from other profiles.
    Returns True if a process was stopped, False if none was found.
    r   )get_running_pidremove_pid_fileFNr      g      ?T)gateway.statusr   r   ImportErrorrP   rY   rX   SIGTERMrZ   r[   r   timerangesleep)r   r   rA   _time_s        rC   stop_profile_gatewayr   %  sQ   CCCCCCCCC   uu /

C
{u
V^$$$$      8388999uu
 2YY  	GCOOOKK"O4 	 	 	EE	 O4s5    
A 
A6A65A6*B::CCc                  @    t           j                            d          S )Nlinux)sysplatformr   rb   rT   rC   is_linuxr   J  s    <""7+++rT   )is_container	is_termuxis_wslc                      	 t          j        ddgddd          } | j                                                                        }|dv S # t
          t           j        t          f$ r Y dS w xY w)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.
    r   zis-system-runningTr   r   )runningdegradedstartinginitializingF)r+   r,   r-   r.   lowerr6   r5   r\   )r=   statuss     rC   _wsl_systemd_operationalr   Q  s    	-.dA
 
 

 $$&&,,..LLLz8'B   uus   AA A+*A+c                      t                      rt                      st                      rdS t          j        d          dS t                      rt                      S dS )NFr   T)r   r   r   shutilwhichr   r   rb   rT   rC   r*   r*   c  s[    ::   u|K  (uxx *')))4rT   c                  "    t           j        dk    S )Ndarwinr   r   rb   rT   rC   r7   r7   m  s    <8##rT   c                  "    t           j        dk    S )Nwin32r   rb   rT   rC   r   r   p  s    <7""rT   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_rootrf   profilesrF   ^[a-z0-9][a-z0-9_-]{0,63}$   )hashlibrehermes_constantsr   r	   r|   relative_tor?   r:   matchr4   sha256rJ   encode	hexdigest)r   r   r   homedefaultprofiles_rootrelr?   s           rC   _profile_suffixr   |  s'    NNNIII888888$$&&D%%''//11Gwrz)2244M}--	u::??rxx(EuQxPP?8O    >>#d))**,,--7799"1"==s   +AB? ?
CChermes_homec                    ddl }ddlm} t          | pt	          t                                                                          } |                                            }||k    rdS |dz                                  }	 |                    |          }|j        }t          |          dk    r'|
                    d|d                   rd|d          S n# t          $ r Y nw xY wdS )	a  Return ``--profile <name>`` only when HERMES_HOME is a named profile.

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

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

    Default ``~/.hermes`` returns ``hermes-gateway`` (backward compatible).
    Profile ``~/.hermes/profiles/coder`` returns ``hermes-gateway-coder``.
    Any other HERMES_HOME appends a short hash for uniqueness.
    -)r   _SERVICE_BASEsuffixs    rC   get_service_namer     s1     F &&f&&&rT   systemc                     t                      }| rt          d          | dz  S t          j                    dz  dz  dz  | dz  S )Nz/etc/systemd/systemr"   z.configsystemduser)r   r   r   )r   names     rC   get_systemd_unit_pathr     s[    D ?)**->->->>>9;;"Y.7T:K:K:KKKrT   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.
    XDG_RUNTIME_DIRz
/run/user/DBUS_SESSION_BUS_ADDRESSbusz
unix:path=N)rP   getuidenvironr   existsro   )uidruntime_dirxdg_runtimebus_paths       rC   _ensure_user_systemd_envr     s     )++C
**(3((##%% 	8,7BJ()!33jnn%68JS8J8JKK$$u,?? 	M5L(5L5LBJ1222	 43	M 	MrT   c                 4    | st                       | rdgnddgS )Nr   r   )r   r   s    rC   _systemctl_cmdr     s,     # """"?K==h(??rT   c                     | rdgnddgS )N
journalctlr   rb   r   s    rC   _journalctl_cmdr     s    #AL>>,)AArT   r   argsc                    	 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)r+   r,   r   r6   RuntimeError)r   r   kwargss      rC   _run_systemctlr     s_    ~nV44t;FFvFFF   7
 
	s   !$ ?c                     | rdndS )Nr   r   rb   r   s    rC   _service_scope_labelr     s    )886)rT   c                      g } t                      }dD ]X\  }}t          |          }||v r|                                r*|                     |           |                    |           Y| S )N))Fr   )Tr   r   )r)   r   r   r   r3   )scopes
seen_pathsr   rB   	unit_paths        rC   get_installed_systemd_scopesr     s    FEEJ< & &)888	
"" 	&MM%   NN9%%%MrT   c                  @    t          t                                dk    S )NrF   )r:   r   rb   rT   rC   has_conflicting_systemd_unitsr     s    +--..22rT   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 )
Nr(   z + z5Both user and system gateway services are installed (z).zF  This is confusing and can make start/stop/status behavior ambiguous.zL  Default gateway commands target the user service unless you pass --system.z  Keep one of these:z    hermes gateway uninstallz*    sudo hermes gateway uninstall --system)r   r:   r   r   r   )r   rendered_scopess     rC   $print_systemd_scope_conflict_warningr     s    )++F
6{{Qjj((O]/]]]^^^WXXX]^^^%&&&-...;<<<<<rT   actionc                     t          j                    dk    r)t          d|  d           t          j        d           d S d S )Nr   zSystem gateway z! requires root. Re-run with sudo.rF   )rP   geteuidr   r   exit)r   s    rC    _require_root_for_system_servicer    sF    	z||qIIIIJJJ rT   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: )getpassgrppwdrP   getenvgetuserr.   r4   r   r   getpwnamKeyErrorgetgrgidpw_gidgr_namepw_dir)r  r  r	  r
  username	user_infoe
group_names           rC   _system_service_identityr     sY   NNNJJJJJJwry55w69J9JwbiXaNbNbwfmfufufwfw~~  A  AH ][\\\6+  P  Q  Q  	Q6BCCCklll=LL**		 = = =4(44551<= i.//7JZ!111s   >C 
C6C11C6r   c                    |                                  sd S |                     d                                          D ]K}|                    d          r4|                    dd          d                                         }|pd c S Ld S )Nutf-8encodingzUser==rF   )r   	read_textr/   r   r0   r.   )r   r>   values      rC   _read_systemd_user_from_unitr  7  s     t##W#55@@BB ! !??7## 	!JJsA&&q)//11E=D   	! 4rT   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  )rP   r  r.   )	candidates    rC   _default_system_service_userr"  B  s    i,,bi.?.?9AUAUV % %	 	%** 	%y/@/@F/J/J??$$$$$4rT   c                  B    t          dg dd          } ddd d|          S )Nz6  Choose how the gateway should run in the background:)zPUser service (no sudo; best for laptops/dev boxes; may need linger after logout)zGSystem service (starts on boot; requires sudo; still runs as your user)zSkip service install for nowr   r   r   r   )r   rF   r(   )r   )choices    rC   "prompt_linux_gateway_install_scoper&  I  sD    @	
 	
 	

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

    Returns:
        (True, "") when linger is enabled.
        (False, "") when linger is disabled.
        (None, detail) when the status could not be determined.
    )Nznot supported in Termux)Nznot supported on this platformr   Nloginctl)Nloginctl not foundr  r  )Nz could not determine current userz	show-userz--property=Lingerr$   TFrp   r   r    checkr!   exit zloginctl query failedrf   >   1yestrue)Trf   >   0nofalse)Frf   z<empty>zunexpected loginctl output: )r   r   r   r   rP   r  r
  getpwuidr   pw_name	Exceptionr+   r,   rJ   r9   stderrr-   r.   r   )r   r  r
  r=   r  detailr  rendereds           rC   get_systemd_linger_statusr?  u  s    {{ /..:: 655MMM6<
## *))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 ()z>  If you want the gateway user service to survive logout, run:z#  sudo loginctl enable-linger $USERN)r?  r   )linger_enabledlinger_details     rC   print_systemd_linger_guidancerG    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4rT   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   r9  rP   r   r  )r
  s    rC   _launchd_user_homerI    s0     JJJRY[[))0111rT   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   rI  )r   r   s     rC   get_launchd_plist_pathrO    sI     F,2K((((8KD)+n<$NNrT   c                  r   t           j        t           j        k    r/t          t           j                  } |                                 r| S t
          j                            d          }|r%t          |          } |                                 r| S dD ]$}t          |z  } |                                 r| c S %dS )ab  Detect the active virtualenv directory.

    Checks ``sys.prefix`` first (works regardless of the directory name),
    then ``VIRTUAL_ENV`` env var (covers uv-managed environments where
    sys.prefix == sys.base_prefix), then falls back to probing common
    directory names under PROJECT_ROOT.
    Returns ``None`` when no virtualenv can be found.
    VIRTUAL_ENV)z.venvvenvN)	r   prefixbase_prefixr   is_dirrP   r   ro   PROJECT_ROOT)rR  _virtual_envr!  s      rC   _detect_venv_dirrX    s     zS_$$CJ;;== 	K
 :>>-00L L!!;;== 	K '  	i';;== 	KKK	 4rT   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)rX  r   r   rJ   r   
executable)rR  venv_pythons     rC   get_python_pathr_    sh    D<< 	2*\9KK,1K 	${###>rT   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 rb   )r   r   )rc   rt   r`  s     rC   re   z+_build_user_local_paths.<locals>.<listcomp>  s8    PPP!Ql%:%:tAww~~?O?O%:A%:%:%:rT   )rJ   )r   r`  
candidatess    ` rC   _build_user_local_pathsre    sz     	D8Oe#$$D8Oe#$$D4K%  D= 5())	J QPPPzPPPPrT   pathtarget_home_dirc                    t          j                    }t          |                                           }	 |                    |          }t	          t          |          |z            S # t
          $ r t	          |          cY S w xY w)aZ  Remap *path* from the current user's home to *target_home_dir*.

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

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

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

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

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

[Service]
Type=simple
User=z
Group=z
ExecStart=z -m hermes_cli.mainry   rf   z( gateway run --replace
WorkingDirectory=z
Environment="HOME=z"
Environment="USER=z"
Environment="LOGNAME=z"
Environment="PATH=z"
Environment="VIRTUAL_ENV=z"
Environment="HERMES_HOME=z:"
Restart=on-failure
RestartSec=30
RestartForceExitStatus=zV
KillMode=mixed
KillSignal=SIGTERM
ExecReload=/bin/kill -USR1 $MAINPID
TimeoutStopSec=zT
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
zc
After=network.target
StartLimitIntervalSec=600
StartLimitBurst=5

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

[Install]
WantedBy=default.target
)r_  rJ   rV  rX  r   r   r   r|   parentr   maxr2   _get_restart_drain_timeoutr  rp  r}   rk  extendre  r   SERVICE_DESCRIPTIONr   r	   r   )r   r  python_pathworking_dirdetected_venvvenv_dirvenv_binnode_binr`  resolved_noderesolved_node_dircommon_bin_pathsrestart_timeoutr  r  r   profile_arg	sane_pathrw  s                     @rC   generate_systemd_unitr  :  s&   !##Kl##K$&&M%2Rs=!!!L6<Q8R8RH-:bs=5()))LSYDY\aDa@b@bH<.069::Hh'LL((M 3] 3 3 ; ; = = DEEL00 1222fff"c"<">">"C!DDEEO /)A+)N)N&*h28<<";// +;AA*;AA'(;;'(;;'(;;PPPP<PPP3DNNLQQRRR,---HH\**	         AL+S+<{+<+<+<QS           ! " ## $ &% * :+ 2  3   	B o''//1122K{++K/	\JJKKK()))&&I     AL+S+<{+<+<+<QS      #  &  : &  '   rT   r    c                     d                     d |                                                                 D                       S )Nrq   c              3   >   K   | ]}|                                 V  d S rs   )rstrip)rc   r>   s     rC   rv   z0_normalize_service_definition.<locals>.<genexpr>  s*      IItT[[]]IIIIIIrT   )r   r.   r/   )r    s    rC   _normalize_service_definitionr    s7    99IItzz||/F/F/H/HIIIIIIrT   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)r    r   
normalizeds      rC   '_normalize_launchd_plist_for_comparisonr    sC     III.t44J667d	    rT   c                    t          |           }|                                sdS |                    d          }| rt          |          nd }t	          | |          }t          |          t          |          k    S )Nr   Fr  r  r   r  )r   r   r  r  r  r  )r   r   	installedexpected_userexpecteds        rC   systemd_unit_is_currentr    s    %V444I u##W#55I?EO0;;;4M$FNNNH(337TU]7^7^^^rT   c                 L   t          |           }|                                rt          |           rdS | rt          |          nd}|                    t          | |          d           t          dg| dd	
           t          dt          |            d           dS )zMRewrite the installed systemd unit when the generated definition has changed.r   FNr  r  r  daemon-reloadT   r   r1  r!   u   ↻ Updated gateway z7 service definition to match the current Hermes install)	r   r   r  r  
write_textr  r   r   r   )r   r   r  s      rC   refresh_systemd_unit_if_neededr    s    %V444I !8!G!G!G u?EO0;;;4M.f-XXXcjkkkO$V4LLLL	
v!5f!=!=
v
v
vwww4rT   r  r=  c                 Z   t                       t          d           |rt          d|            t                       t          d           t          d|             t                       t          d           t          dt                       d           t                       d S )NuI   ⚠ Linger not enabled — gateway may stop when you close this terminal.z  Auto-enable failed: z1  On headless servers (VPS, cloud instances) run:z     sudo loginctl enable-linger   Then restart the gateway:z    systemctl --user restart r"   )r   r   )r  r=  s     rC   _print_linger_enable_warningr    s    	GGG	
UVVV 1/v//000	GGG	
=>>>	
7X
7
7888	GGG	
'(((	
F*:*<*<
F
F
FGGG	GGGGGrT   c                     t                      st                      sdS ddl} ddl}|                                 }t          d|           }|                                rt          d           dS t                      \  }}|du rt          d           dS  |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/rA  Tr.  r/  z5Enabling linger so the gateway survives SSH logout...zenable-lingerFr  r0  u8   ✓ Linger enabled — gateway will persist after logoutr2  )r   r   r  r   r  r   r   r   r?  r   r  r+   r,   r;  rJ   r9   r<  r-   r.   )	r  r   r  linger_filerE  rF  r=   r  r=  s	            rC   _ensure_linger_enabledr    s   {{ (** NNNMMM  H<(<<==K GHHH$=$?$?!NMGHHH6<
## $X}/T@TUUU	
ABBB
(3
 
 
    $Xs1vv666 AHIIImKv}K0K8I0K0KRRTTF 6+B]CCCCCs   C) )
D3DDc                     | rdS t          d                                          o"t          d                                           S )NTr   F)r   r   r   s    rC   _select_systemd_scoper  	  sM     t ---4466k?T\a?b?b?b?i?i?k?k;kkrT   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_TIMEOUTrf   agentrestart_drain_timeout)
rP   r  r.   r   
isinstancedictro   rJ   r   r   )rK   cfg	agent_cfgs      rC   r{  r{    s    
)2B
7
7
=
=
?
?C 
,6sD,A,AICGGGR(((r	MM')N 
 

 's+++rT   c                 4   |rt          d           t          |          }|rdnd}|                                r| st          |          st	          dt          |           d|            t          |           t          dt                      g|dd	
           t	          dt          |          	                                 d           d S t	          d|            t	          d           d S |j
                            dd           t	          dt          |           d|            |                    t          ||          d           t          dg|dd	
           t          dt                      g|dd	
           t	                       t	          dt          |          	                                 d           t	                       t	          d           t	          d|rdnd d| d           t	          d|rdnd d| d           t	          d|rdnd d t                       d!           t	                       |r$t          |          }|rt	          d"|            nt                       t!                       d S )#Ninstallr   	 --systemrf   u   ↻ Repairing outdated z systemd service at: enableTr  r     ✓ z service definition updatedService already installed at: Use --force to reinstallparentsexist_okzInstalling z systemd service to: r  r  r  r  z service installed and enabled!Next steps:  sudo hermes gateway startz!              # Start the servicezhermes gateway statusz             # Check statusr   zjournalctl --userz -u z -f  # View logsConfigured to run as: )r  r   r   r  r   r   r  r   r   
capitalizery  mkdirr  r  r  r  r   )r   r   r  r   
scope_flagconfigured_users         rC   r*  r*    s     4(333%V444I &.BJ 	% 	&f555 	j,@,H,Hjj_hjjkkk*&9999H&6&8&89&PT^`aaaa_-f55@@BB___```F:y::;;;()))4$777	
V,V44
V
V9
V
VWWW.f+VVVahiiiO$V4LLLLH.001&VXYYYY	GGG	
[%f--88::
[
[
[\\\	GGG	-	
k&(wwb
k
kj
k
k
klll	
f&(wwb
f
fz
f
f
fggg	
hv>||+>
h
hDTDVDV
h
h
hiii	GGG !6yAA 	><?<<===   (*****rT   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	uninstallstopFZ   r  disabler  r      ✓ Removed r  Tr  z service uninstalled)
r  r  r   r   r   r   unlinkr   r   r  )r   r   s     rC   systemd_uninstallr  I  s	   "6**F 6(555F,../eUWXXXXI/1126XZ[[[[%V444I *(Y(()))O$V4LLLL	
P%f--88::
P
P
PQQQQQrT   c                    t          |           } | rt          d           t          |            t          dt	                      g| dd           t          dt          |                                            d           d S )Nstartr   Tr  r  r  z service started)r  r  r  r   r   r   r   r  r   s    rC   systemd_startr  Z  s    "6**F 2(111"&1111G-//0tUWXXXX	
L%f--88::
L
L
LMMMMMrT   c                     t          |           } | rt          d           t          dt                      g| dd           t	          dt          |                                            d           d S )Nr  Tr  r  r  z service stopped)r  r  r   r   r   r   r  r   s    rC   systemd_stopr  d  s~    "6**F 1(000F,../dTVWWWW	
L%f--88::
L
L
LMMMMMrT   c                 &   t          |           } | rt          d           t          |            ddlm}  |            }|zt          |          rjdd l}t          |                                           }t                      }t          |           }t          d| d           |                                dz   }|                                |k     r[	 t          j        |d           |                    d           n# t          t           f$ r Y n/w xY w|                                |k     [t          d	| d
           t          d| d           |                                dz   }|                                |k     r	 t#          j        |d|gz   ddd          }|j                                        dk    r* |            }	|	r|	|k    rt          d| d|	 d           d S n# t"          j        t,          f$ r Y nw xY w|                    d           |                                |k     	 t#          j        |d|gz   ddd          }|j                                        dk    rt          d| d           d S n# t.          $ r Y nw xY wt          d| d| rdnd d| sdnd d| d	           d S t1          d t                      g| dd!           t          dt          |                                            d           d S )"Nrestartr   r   r   u   ⏳ z  service draining active work...r  rF   u   ⚠ Old process (PID z) still alive after 90su   ⏳ Waiting for z to restart...ru  	is-activeTr   r   activer  z service restarted (PID rD  r(   z service restarted   ⚠ z; service did not become active within 60s.
  Check status: r  rf   z1hermes gateway status
  Check logs:   journalctl z--user z-u z --since '2 min ago'zreload-or-restartr  )r  r  r  r   r   r]   r   r   r  r   r   r   rP   rY   r   rZ   r[   r+   r,   r-   r.   r5   r6   r;  r   )
r   r   rA   r   scope_labelr@   	scope_cmddeadliner=   new_pids
             rC   systemd_restartr  m  s   "6**F 4(333"&1111......
/

C
8==
 	*622==??  "6**	 	B[BBBCCC99;;#iikkH$$Q

1&8   	 iikkH$$ F#FFFGGG 	444455599;;#iikkH$$#c 22#'dA   =&&((H44-o//G 7c>>T[TT'TTTUUU-/@A   JJqMMM iikkH$$"		^[#..#$  F }""$$00<[<<<=== 1  	 	 	D	h; h h*08wwbh h;A*I))rh hNQh h h	
 	
 	

 	')9););<VSWacdddd	
N%f--88::
N
N
NOOOOOs8   *D DD	A"G. .HH8AJ 
JJdeepc                    t          |          }t          |          }|rdnd}|                                s*t          d           t          d|rdnd d|            d S t	                      rt                       t                       t          |          s7t          d           t          d|rdnd d	| d
           t                       t          dt                      dg|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|            |rt          d           nX| rt                       nGt!                      \  }	}
|	du rt          d           n"|	du rt          d           t          d           | rVt                       t          d            t#          j        t'          |          d!t                      d"d#dgz   d$           d S d S )%Nr   r  rf   u$   ✗ Gateway service is not installedz  Run: r  zhermes gateway installu4   ⚠ Installed gateway service definition is outdatedzhermes gateway restartz  # auto-refreshes the unitr   r   Frp   )r   r   r!   r  Tr   r   r    r!   r  r  z gateway service is runningu   ✗ z gateway service is stoppedr  r  Recent gateway health:r  uB   ✓ System service starts at boot without requiring systemd lingerrA  rB  rC  Recent logs:z-uz-n20r!   )r  r   r   r   r   r   r  r   r   r-   r.   r   r  r  _runtime_health_linesrG  r?  r+   r,   r   )r  r   r   r  r=   r   r  runtime_linesr>   rE  r   s              rC   systemd_statusr    sy   "6**F%V444I &.BJ 4555U61rUUUUVVV$&& ,..."&111 DEEEp61rpppppqqq	#%%|4	    	&(()  F ]  ""F[)&11<<>>[[[\\\\[)&11<<>>[[[\\\S61rSSzSSTTTAGQ29===TO :888999)++M &'''! 	 	D+t++ 
>RSSSS	 >%''''577T!!KLLLLu$$VWWW<=== snv..$8H8J8JDRVXd1eeoqrrrrrrs srT   c                  0    t                      } | rd|  ndS )z5Return the launchd service label, scoped per profile.rK  rL  )r   r   s    rC   r8   r8     s'    F,2K((((8KKrT   c                  .    dd l } d | j                     S )Nr   zgui/)rP   r   )rP   s    rC   _launchd_domainr    s!    III)")++rT   c                     t                      } t          t                    }t          t                                                                }t                      dz  }|                    dd           t                      }t          |          }t                      }|rt          |dz            nt          t          dz  dz            }|rt          |          nt          t          dz            }t          t          dz  dz            }	||	g}
t          j
        d          }|rLt          t          |                                          j                  }||
vr|
                    |           d	                    t                              |
d
 t"          j                            dd                              d	          D             z                       }d|  dddg}|r0|                                D ]}|                    d| d           |                    g d           d                    |          }d| d| d| d| d| d| d| d| dS )NlogsTr  r[  rR  rr  rs  rt  rx  c                     g | ]}||S rb   rb   rc   rt   s     rC   re   z*generate_launchd_plist.<locals>.<listcomp>$  s    &]&]&]Q[\&]q&]&]&]rT   PATHrf   z<string>z	</string>z<string>-m</string>z <string>hermes_cli.main</string>)z<string>gateway</string>z<string>run</string>z<string>--replace</string>z	
        z<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>z?</string>

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

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

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

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

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

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

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

                    dd          }d|d         v rg }|                    d          D ]}|                                }|                    d          r=|                    d          r(|                    d                              d          }|                                                    d          r
|dd         }|r|                    |           d                    |          }t)          |d         |           t          d            |}n}t                       g d!}t+          d"|d#          }|d$k    r t)          d%d&           t-          d'           n4|d#k    rt          d(           t          d)           nt          d*           bt          d|d          |                    d+d                    }
|
r0t)          |d         |
           t          d,|d                     |d         |k    rt-          d-| d.            dS t          d/           |                                 d0}t          |          }|rm|sk|d1k    re|                    d          d$                                         }|r6t          d2| d3d4          r"t)          ||           t          d5|            t                       t          | d| d6           dS )7z2Interactive setup for Telegram, Discord, or Slack.r:  rB   r;  u     ─── ry   u    Setup ───r<  r  z is already configured.z  Reconfigure ?FNr=  r5  r   z  Current: r7  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   r4  rf   DISCORD,z<@>z<@!zuser:r   u7     Saved — only these users can interact with the bot.)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)z+  How should unauthorized users be handled?rF   r   GATEWAY_ALLOW_ALL_USERSr5  u2     Open access enabled — anyone can use your bot!uB     DM pairing mode — users will receive a code to request access.z8  Approve with: hermes pairing approve <platform> <code>u9     Skipped — configure later with 'hermes gateway setup'r4  z  Saved u     Skipped — z won't work without this.z  Skipped (can configure later)_HOME_CHANNELr2  z  Use your user ID () as the home channel?T  Home channel set to z configured!)r   r   r   CYANro   r   r   r   r   r   r%  r0   r.   r   r1   lstripr  r   r   r   r   r   r   upper)r   r:  rB   r;  instructionsr>   existing_tokenallowed_val_setvarexistingr  cleanedr?   r   access_choices
access_idxhome_varhome_valfirst_ids                      rC   _setup_standard_platformr  0  s   WEWE%I	GGG	%>u>>u>>>
L
LMMM << 455L $  	$ 	$D{D{{####"9--N 7778886e666>> 	FO 9: 9:%F%%&&& V-- 	1Fy00/X//000 77>"" '	PQQQPQQQFGGG/H//%@@@E !\--R00F++E&}}S11 . .!iikk>>$// @CLL4E4E @"%**U"3"3":":3"?"?C99;;11':: *"%abb'C .!LL---!hhuooGs6{G444WXXX") " " "
 ++XZhjkll
??"#<fEEE!"VWWWW1__!"fgggYZZZZZ[[[+CM++cggj%6P6PQQQ 	:3v;...2S[223333[I%%K5KKKLLLFF89999 ++--...HX&&H ?x ?EZ,?,?"((--a06688 	?&]X&]&]&]_cdd 	?8X...=8==>>>	GGGU00U00011111rT   c                  T    ddl m}  ddl} | |                                           dS )z-Delegate to the existing WhatsApp setup flow.r   )cmd_whatsappN)hermes_cli.mainr  argparse	Namespace)r  r  s     rC   _setup_whatsappr    s?    ,,,,,,OOOL##%%&&&&&rT   c                  `    t          d t          D                       } t          |            dS )z0Configure Email via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r9  r[  Nrb   r  s     rC   rv   z_setup_email.<locals>.<genexpr>  0      GG1U8w3F3F!3F3F3F3FGGrT   Nnext
_PLATFORMSr  )email_platforms    rC   _setup_emailr    2    GGZGGGGGN^,,,,,rT   c                  `    t          d t          D                       } t          |            dS )z7Configure SMS (Twilio) via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r9  rb  Nrb   r  s     rC   rv   z_setup_sms.<locals>.<genexpr>  s0      CCa5U1B1B1B1B1B1BCCrT   Nr  )sms_platforms    rC   
_setup_smsr    s2    CC:CCCCCL\*****rT   c                  `    t          d t          D                       } t          |            dS )z3Configure DingTalk via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r9  rh  Nrb   r  s     rC   rv   z"_setup_dingtalk.<locals>.<genexpr>  s0      MM1ah*6L6LQ6L6L6L6LMMrT   Nr  )dingtalk_platforms    rC   _setup_dingtalkr    s4    MM
MMMMM./////rT   c                  `    t          d t          D                       } t          |            dS )zDConfigure WeCom (Enterprise WeChat) via the standard platform setup.c              3   2   K   | ]}|d          dk    |V  dS )r9  rs  Nrb   r  s     rC   rv   z_setup_wecom.<locals>.<genexpr>  r  rT   Nr  )wecom_platforms    rC   _setup_wecomr    r  rT   c                     t                      rDt          d                                          p!t          d                                          S t                      r t	                                                      S dS )z6Check if the gateway is installed as a system service.Fr   T)r*   r   r   r7   rO  rb   rT   rC   _is_service_installedr    st     "" 1$E22299;;j?T\`?a?a?a?h?h?j?jj	 1%''..0005rT   c                  \   t                      rt          d                                          } t          d                                          }| r_	 t          dt	                      gdddd          }|j                                        dk    rdS n# t          t          j	        f$ r Y nw xY w|r_	 t          dt	                      gdddd          }|j                                        dk    rdS n# t          t          j	        f$ r Y nw xY wdS t                      rit                                                      rI	 t          j        dd	t                      gddd
          }|j        dk    S # t          j	        $ r Y dS w xY wt          t!                                dk    S )z2Check if the gateway service is currently running.Fr   Tr  rp   r  r  r%   r&   r   r   )r*   r   r   r   r   r-   r.   r   r+   r5   r7   rO  r,   r8   r9   r:   r   )user_unit_existssystem_unit_existsr=   s      rC   _is_service_runningr    s	    "" #0>>>EEGG2$???FFHH 		' "2"4"45 D"   =&&((H444 5 *";<     		' "2"4"454   =&&((H444 5 *";<    u	 .007799 	^f&7&9&9:#$  F $))( 	 	 	55	  ""##a''s6   ?B B21B28?C: :DD1E9 9FFc                  $   t                       t          t          dt          j                             t                       t	          d           t	          d           t	          d           t	          d           t          d          } t          d          }| r1|r/t                       t          d           t          d	d
          sdS 	 ddlm	}m
} n9# t          $ r,}t          d|            t	          d           Y d}~dS d}~ww xY w |            s t          d           t	          d           dS t                       t          dd          st	          d           dS ddl}	  |j         |t          t!                                                    }nS# t"          $ r! t                       t%          d           Y dS t          $ r}t          d|            Y d}~dS d}~ww xY w|st%          d           dS |                    dd          }|                    dd          }|                    dd          }	|                    dd          }
t)          d|           t)          d|           |	rt)          d|	           t)          dt          d          pd           t                       g d }t+          d!|d          }|dk    rOt)          d"d#           t)          d$d%           t)          d&d           t          d'           t	          d(           n|d)k    r@t)          d"d*           t)          d$d+           t)          d&d           t%          d,           n|d-k    rj|
pd}t-          d.|d
/                              d0d          }t)          d"d1           t)          d$d%           t)          d&|           t          d2           n?t)          d"d3           t)          d$d%           t)          d&d           t%          d4           t                       g d5}t+          d6|d          }|dk    r0t)          d7d3           t)          d8d           t	          d9           n|d)k    r0t)          d7d*           t)          d8d           t%          d:           nUt-          d;dd
/                              d0d          }t)          d7d1           t)          d8|           t          d<           |
rDt                       t          d=|
 d>d          r"t)          d?|
           t          d@|
            t                       t          dA           t	          dB|            |
rt	          dC|
            dS dS )Dz8Interactive 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_idrf   r  base_urluser_idWEIXIN_BASE_URLWEIXIN_CDN_BASE_URLz%https://novac2c.cdn.weixin.qq.com/c2c)%Use DM pairing approval (recommended)Allow all direct messagesOnly allow listed user IDszDisable direct messages+  How should direct messages be authorized?WEIXIN_DM_POLICYpairingWEIXIN_ALLOW_ALL_USERSr8  WEIXIN_ALLOWED_USERS  DM pairing enabled.zY  Unknown DM users can request access and you approve them with `hermes pairing approve`.rF   openr5  z$  Open DM access enabled for Weixin.r(   z+  Allowed Weixin user IDs (comma-separated)r  ry   	allowlistz  Weixin allowlist saved.disabledz  Direct messages disabled.)z!Disable group chats (recommended)zAllow all group chatsz Only allow listed group chat IDs$  How should group chats be handled?WEIXIN_GROUP_POLICYWEIXIN_GROUP_ALLOWED_USERS  Group chats disabled.z  All group chats enabled.z*  Allowed group chat IDs (comma-separated)z  Group allowlist saved.z  Use your Weixin user ID (r  WEIXIN_HOME_CHANNELr  zWeixin configured!z  Account ID: z  User ID: )r   r   r   r  r   r   r   r   gateway.platforms.weixinr  r  r;  r   r.  r,   rJ   r	   KeyboardInterruptr   ro   r   r   r   r%  )existing_accountr  r  r  r   r.  credentialsr  r  r  r  r  r  default_allowr  group_choices	group_idxallow_groupss                     rC   _setup_weixinr    sa   	GGG	%BFK
P
PQQQ	GGGOPPPABBBXYYY]^^^$%899">22N N 56664e<< 	FPPPPPPPPP   <s<<===FGGG
 %$&& TUUUGHHH	GGG0$77 >"""NNN!gk((3/@/@+A+A"B"BCC   1222   /#//000  4555r22JOOGR((Ez2..Hooi,,G&
333>5))) 4((333(-8M*N*N*yRyzzz	GGG  N Ln^_``JQ)9555/999-r222-...noooo	q)6222/888-r222<====	q2H-bghhhppqtvxyy	);777/999-y99912222):666/999-r2223444	GGG  M
 DmUVWWIA~~,j9993R888,----	a,f5553R88823333JBY^___gghkmopp,k:::3\BBB0111 >VwVVVX\]] 	>0':::<7<<===	GGG&''',
,,--- ,***+++++, ,s6   C 
D%!DD22F% %'G5	G5G00G5c                  *   t                       t          t          dt          j                             t	          d          } t	          d          }| r1|r/t                       t          d           t          dd          sdS t                       dd	g}t          d
|d          }d}d}|dk    r	 ddlm	} n+# t          $ r}t          d|            d}Y d}~nd}~ww xY w|^	  |            }nR# t          $ r! t                       t          d           Y dS t          $ r}t          d|            Y d}~nd}~ww xY w|rd}|st          d           |s$t                       t          d           t          d           t                       t          dd          }|st          d           dS t          dd          }	|	st          d           dS ddg}
t          d|
d          }|dk    rdnd}d}	 ddlm}  |||	|          }|r*|                    d           }t          d!|pd"            nt          d#           n)# t          $ r}t          d$|            Y d}~nd}~ww xY w||	|d|d%}|d&         }|d'         }	|                    d(d          }|                    d)          }|                    d           }t%          d|           t%          d|	           t%          d*|           |rd+}n`t                       d,d-g}t          d.|d          }|dk    rd/nd+}|d/k    r-t          d0           t          d1           t          d2           t%          d3|           |r t                       t          d4|            t                       g d5}t          d6|d          }|dk    r?t%          d7d8           t%          d9d:           t          d;           t          d<           n|dk    r0t%          d7d=           t%          d9d:           t          d>           nYt%          d7d8           |pd:}t          d?|d                              d@d:          }t%          d9|           t          dA           t                       dBdCg}t          dD|d          }|dk    r t%          dEdF           t          dG           nt%          dEdH           t          dI           t                       t          dJd          }|r"t%          dK|           t          dL|            t                       t          dM           t          dN|            t          dO|            |rt          dP|            dS dS )QuM   Interactive setup for Feishu / Lark — scan-to-create or manual credentials.u.     ─── 🪽 Feishu / Lark Setup ───rm  rn  z$Feishu / Lark is already configured.z  Reconfigure Feishu / Lark?FNz<Scan QR code to create a new bot automatically (recommended)z-Enter existing App ID and App Secret manuallyz-  How would you like to set up Feishu / Lark?r   )qr_registerz'  Feishu / Lark onboard import failed: z   Feishu / Lark setup cancelled.z  QR registration failed: Tz:  QR setup did not complete. Continuing with manual input.zI  Go to https://open.feishu.cn/ (or https://open.larksuite.com/ for Lark)zE  Create an app, enable the Bot capability, and copy the credentials.z  App IDr  u9     Skipped — Feishu / Lark won't work without an App ID.z  App Secretu=     Skipped — Feishu / Lark won't work without an App Secret.zfeishu (China)zlark (International)z  DomainrF   larkrl  )	probe_botbot_nameu      Credentials verified — bot: unnamedz<  Could not verify bot connection. Credentials saved anyway.z#  Credential verification skipped: )app_id
app_secretdomainopen_idr  r  r  r  r  ro  	websocketu0   WebSocket (recommended — no public URL needed)z,Webhook (requires a reachable HTTP endpoint)z  Connection modewebhookz1  Webhook defaults: 127.0.0.1:8765/feishu/webhookzO  Override with FEISHU_WEBHOOK_HOST / FEISHU_WEBHOOK_PORT / FEISHU_WEBHOOK_PATHzR  For signature verification, set FEISHU_ENCRYPT_KEY and FEISHU_VERIFICATION_TOKENrp  z  Bot created: )r  r  r  r  FEISHU_ALLOW_ALL_USERSr8  rq  rf   r  zJ  Unknown users can request access; approve with `hermes pairing approve`.r5  z+  Open DM access enabled for Feishu / Lark.z$  Allowed user IDs (comma-separated)ry   z  Allowlist saved.z4Respond only when @mentioned in groups (recommended)zDisable group chatsr   FEISHU_GROUP_POLICYr  z/  Group chats enabled (bot must be @mentioned).r  r  z1  Home chat ID (optional, for cron/notifications)rr  r  u   🪽 Feishu / Lark configured!z
  App ID: z
  Domain: z  Bot: )r   r   r   r  r   r   r   r   gateway.platforms.feishur  r;  r   r  r   r   r   r  ro   r   r%  )existing_app_idexisting_secretmethod_choices
method_idxr  used_qrr  r   r  r  domain_choices
domain_idxr  r  r  bot_infor  connection_modemode_choicesmode_idxr  r  r	  r  r
  r  home_channels                              rC   _setup_feishur)  c	  s   	GGG	%@&+
N
NOOO#O44O#$788O ? <===;UCC 	F 
GGGF7N NP^`abbJKGQ	<<<<<<< 	 	 	G#GGHHHKKKKKK	 "B)kmm$   @AAA B B B@3@@AAAAAAAAB 	G 	USTTT  %
^___Z[[[
U333 	UVVVFNT:::
 	YZZZF*,BC":~qAA
%?? 		G:::::: yV<<H ^#<<
33XAVYXXYYYY\]]] 	G 	G 	GEEEFFFFFFFF	G $ 
 
 "F\*J__Xx00Fooi((Gz**H?F+++&
333?F+++  m%>:
 !!4lAFF'/1}}))+i''JKKKhiiiklll+_=== 4222333 
GGG  N
 Ln^_``JQ/999-r222-..._````	q/888-r222CDDDD/9992A=[`aaaiijmoqrr	-y999*+++ 
GGG>M DmUVWWIA~~,f555DEEEE,j999,--- 
GGGMX]^^^L ?,l;;;=|==>>>	GGG2333$F$$%%%$F$$%%% )'X''((((() )sO   5B< <
C$CC$*
C5 5'E	E(D??EAI 
I8I33I8c                  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 ───rZ  r  zSignal is already configured.z  Reconfigure Signal?Fz
signal-clizsignal-cli found on PATH.zsignal-cli not found on PATH.z7  Signal requires signal-cli running as an HTTP daemon.z  Install options:zF    Linux:  download from https://github.com/AsamK/signal-cli/releasesz#    macOS:  brew install signal-cliz)    Docker: bbernhard/signal-cli-rest-apiz;  After installing, link your account and start the daemon:z$    signal-cli link -n "HermesAgent"zA    signal-cli --account +YOURNUMBER daemon --http 127.0.0.1:8080z8  Enter the URL where signal-cli HTTP daemon is running.zhttp://127.0.0.1:8080z  HTTP URL [z]: z
  Setup cancelled.z  Testing connection...r  z/api/v1/checkr  r     z!  signal-cli daemon is reachable!z#  signal-cli responded with status .z  Continue anyway?z   Could not reach signal-cli at 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: +15551234567rf   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: enabledr  )r   r   r   r   r  r   r   r   r   r   r   inputr.   EOFErrorr  httpxro   r  status_coder;  r   r   )r   existing_urlr  default_urlurlr4  respr  default_accountr  existing_alloweddefault_allowedallowedexisting_groupsgroupss                  rC   _setup_signalr@  
  s   MMM	GGG	%96;
G
GHHH !233L$%566 ( 56664e<< 	F 
GGGv|L!! 122225666LMMM'((([\\\8999>???PQQQ;<<<VWWW 
GGGIJJJ9"9K3;33344::<<K'(   $%%%
 ()))yyCJJsOO:::DyIIs""=>>>>S@PSSSTTT !5u==    CCCCCDDDWY]^^ 	FFFFF	 	 	 	 	
 $c*** 
GGGJKKK()))&,"Oao+]+B+B+B+B+B[]aaabbhhjj 	&%G'(   $%%%  3444#W--- 
GGGHIIISTTT$%;<<B&1'O@O@@@AAGGII\_'(   $%%% )7333 
GGGSUZ[[ 	=EFFF'(DEEK	F?+AcFFFGGMMOOiSbifiFF+, 	 	 	()))FF	 	3V<<<	GGG&'''&W&&'''ABBBf7S)T)TdIIZdffgggggsa   >'F& & G
	G
A4I 
J%JJ1L	 	 L-,L-'N= = O! O!+Q- - RRc                  @   t                      rt          d           dS t                       t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t          t          dt          j                             t                       t                      } t                      }t                      r*t                      rt                       t                       | r|rt          d	           n| rt          d
           t          dd          ri	 t                      rt                       nt                      rt!                       nM# t"          j        $ r}t'          d|            Y d}~n'd}~ww xY wnt)          d           t)          d           	 t                       t+          d           g }t,          D ]2}t/          |          }|                    |d          d| d           3|                    d           t3          d|t5          |          dz
            }|t5          t,                    k    rnt,          |         }|d         dk    rt7                       n`|d         dk    rt9                       nE|d         dk    rt;                       n*|d         dk    rt=                       nt?          |           2tA          d t,          D                       p&tC          d          pd"                                dk    }|rt                       t          t          d t          j#                             t                      } t                      }|rt          d!d          r	 t                      rtI                       n:t                      rtK                       ntM                       t)          d"           n!# t"          j        $ r}t'          d#|            Y d}~nd}~ww xY wn| r}t          d$d          rk	 t                      rt                       nt                      rt!                       n# t"          j        $ r}t'          d%|            Y d}~n{d}~ww xY wnqt                       t                      st                      rqt                      rd&nd'}	tO                      rd(nd}
t          d)|	 d*|
 d+d          r	 d}d,}t                      rtQ          d,-          \  }}ntS          d,-           d}t                       |rqt          d.d          ra	 t                      rt          |d/k    0           nt!                       n.# t"          j        $ r}t'          d%|            Y d}~nd}~ww xY wnO# t"          j        $ r,}t'          d1|            t)          d2           Y d}~nd}~ww xY wt)          d3           t                      rt)          d4           t)          d5           ntO                      r=t)          d6           t)          d7           t)          d8           t)          d9           ntU                      r@d:d;l+m,} t)          d<           t)          d7           t)          d= |             d>           n<t)          d?           t)          d7           nt                       t)          d@           t                       dS )Az<Interactive setup for messaging platforms + gateway service.zrun gateway setupNr(  u@   │             ⚕ Gateway Setup                            │r)  u?   │  Configure messaging platforms and the gateway service. │u>   │  Press Ctrl+C at any time to exit.                     │r*  z)Gateway service is installed and running.z-Gateway service is installed but not running.z  Start it now?Tz  Failed to start: z%Gateway service is not installed yet.z<You'll be offered to install it after configuring platforms.zMessaging PlatformsrB   z  (rD  DonezSelect a platform to configure:rF   r9  rV  rX   r  rl  c              3   r   K   | ]2}|d          dk    t          t          |d                             V  3dS )r9  rV  r;  N)r~   r   r  s     rC   rv   z gateway_setup.<locals>.<genexpr>
  sQ        U8z!! 	]1[>**++!!!! rT   rX  rf   r5  u   ──────────────────────────────────────────────────────────z)  Restart the gateway to pick up changes?zStart manually: hermes gatewayz  Restart failed: z  Start the gateway service?z  Start failed: r   launchdz. (note: services may not survive WSL restarts)z  Install the gateway as a z	 service?z% (runs in background, starts on boot)Fr   z  Start the service now?r   r   z  Install failed: z.  You can try manually: hermes gateway installz/  You can install later: hermes gateway installzA  Or as a boot-time service: sudo hermes gateway install --systemz+  Or run in foreground:  hermes gateway runz*  WSL detected but systemd is not running.z'  Run in foreground: hermes gateway runz<  For persistence:   tmux new -s hermes 'hermes gateway run'zM  To enable systemd: add systemd=true to /etc/wsl.conf, then 'wsl --shutdown'r   r   z/  Termux does not use systemd/launchd services.zR  Or start it manually in the background (best effort): nohup hermes gateway run >z/logs/gateway.log 2>&1 &z1  Service install not supported on this platform.z?No platforms configured. Run 'hermes gateway setup' when ready.)-r
   r   r   r   r   MAGENTAr  r  r*   r   r   r   r   r   r  r7   r  r+   r
  r   r   r   r  r  r   r   r:   r  r@  r  r)  r  r   r   r   DIMr  r  r   r   r,  r  r   r   r  )service_installedservice_runningr  
menu_itemsplatr   r%  r   any_configuredplatform_namewsl_noteinstalled_scopedid_installr  s                 rC   gateway_setuprP  
  s   || )***	GGG	%  D  FL  FT  U  U  V  V  V	%RTZTb
c
cddd	%  D  FL  FT  U  U  V  V  V	%QSYSa
b
bccc	%PRXR`
a
abbb	%  D  FL  FT  U  U  V  V  V 
GGG-//)++O "" 'D'F'F ,... S_ SABBBB	 SEFFF*D11 	77,.. $!OOOOZZ $!OOO0 7 7 75!55666666667	7 	:;;;QRRR/*+++
 	> 	>D%d++Fg<<6<<<====&!!!@*cR\oo`aNabbS__$$f%E?j((e_((OOOOe_((OOOOe_((OOOO$X...5/:        E *
+
+
1r	8	8	:	:f	D	   JVeJ
++,,,133-// A	JH$OO 
:	:022 E'))))! E')))),..."#CDDD!4 : : : 8Q 8 899999999:
:  5	J;TBB 88022 (%! (%!4 8 8 8 61 6 67777777788 GGG(** *Jhjj *J-F-H-H W		iOUxx_KK]_   "G}  "G  "GW_  "G  "G  "G  IM  N  N NU*.&+466 /;[bg;h;h;h8O[[+%8888*.K& D=9SUY+Z+Z DD#<#>#> !4$1H9T$U$U$U$U$U$1OOO#-#@ D D D +,Bq,B,B C C C C C C C CD%8 U U U#$<$<$<==="#STTTTTTTTU PQQQ022 h"#fgggLMMMM JGHHHDEEEYZZZjkkkk;; JLLLLLLPQQQHIII   Utxtxtztz   U   U   U  V  V  V  VRSSSHIIIITUUU	GGGGGs   09G* *H9HHAQ/ /R>RR39S. .T=TTAY 1X Y X< X72Y 7X<<Y Y<!Y77Y<c           	         t          | dd          }||dk    rGt          | dd          }t          | dd          }t          | dd          }t          |||	           dS |d
k    rt                       dS |dk    rgt                      rt	          d           dS t          | dd          }t          | dd          }t          | dd          }t                      r2t          d           t          d           t          j        d           t                      r]t                      r;t          d           t          d           t          d           t                       t          |||           dS t                      rt          |           dS t                      r~t          d           t          d           t          d           t                       t          d           t          d           t          d           t          j        d           dS t!                      r}t          d           t          d           t                       t          d           t          d            t                       t          d!           t          j        d           dS t          d"           t          d#           t          j        d           dS |d$k    rAt                      rt	          d%           dS t          | dd          }t                      r2t          d&           t          d'           t          j        d           t                      rt#          |(           dS t                      rt%                       dS t!                      r`t          d)           t          d*           t                       t          d+           t          d,           t          j        d           dS t          d-           t          j        d           dS |d.k    r"t          | dd          }t          | d/d          }|r6t'          d01          }	|	r$t          d2|	 d3           t)          d4d56           t                      r2t          d7           t          d           t          j        d           t                      rt+          |(           dS t                      rt-                       dS t                      rt          d8           t          d9           t                       t          d           t          d           t          d           t                       t          d:           t          j        d           dS t!                      r}t          d;           t          d<           t                       t          d=           t          d>           t                       t          d?           t          j        d           dS t          d-           t          j        d           dS |d@k    r]t          | d/d          }
t          | dd          }|
rd}t                      rmt/          d(                                          s"t/          d0(                                          r)	 t3          |(           d0}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d0}n# t4          j        $ r Y nw xY wt'          d01          }	|	|rdndz   }|rt          dA| dB           dS t          dC           dS d}t                      rmt/          d(                                          s"t/          d0(                                          r)	 t3          |(           d0}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d0}n# t4          j        $ r Y nw xY w|s0t=                      rt          dD           dS t          dE           dS t          dAt?                       dF           dS |dGk    r)d}t          | dd          }t          | d/d          }d}|rd}t                      rmt/          d(                                          s"t/          d0(                                          r)	 t3          |(           d0}nj# t4          j        $ r Y nYw xY wt                      rGt9                                                      r'	 t;                       d0}n# t4          j        $ r Y nw xY wt'          d01          }	|	|rdndz   }|rt          dA| dB           t)          d4d56           t          dH           t                      rUt/          d(                                          s"t/          d0(                                          rt+          |(           nMt                      r/t9                                                      rt-                       nt          dI           dS t                      rot/          d(                                          s"t/          d0(                                          r+d0}	 tA          |(           d0}nl# t4          j        $ r Y n[w xY wt                      rIt9                                                      r)d0}	 tC                       d0}n# t4          j        $ r Y nw xY w|sUt                      rtE                      \  }}|d0urddl#}|$                                }t                       t          dJ           t          dK           t                       t          dL|            t                       t          dM           t          dN           dS |rOt                       t          dO           t          dP           t          dQ           t          j        d           t=                      rt          dD           t)          d4d56           t          dH           t          dI           dS dS |dRk    rt          | dSd          }t          | dd          }t                      rWt/          d(                                          s"t/          d0(                                          rtK          ||(           dS t                      r1t9                                                      rtM          |           dS tO                      }|r4t          dTdU(                    tS          tT          |                     dV           t          dW           tW                      }|r4t                       t          dX           |D ]}t          dY|            t                       t                      r t          dZ           t          d[           dS t                      r/t          d\           t          d]           t          d^           dS t          d_           t          d`           t          da           dS t          db           tW                      }|r4t                       t          dX           |D ]}t          dY|            t                       t          dc           t          dd           t                      rt          de           dS t                      r t          d           t          d           dS t          df           t          dg           dS dS )hzHandle gateway subcommands.gateway_commandNr,   r#  r   r$  Fr%  )r$  r%  setupr  z*install gateway service (managed by NixOS)r   r   r  z8Gateway service installation is not supported on Termux.zRun manually: hermes gatewayrF   u?   WSL detected — systemd services may not survive WSL restarts.z<  Consider running in foreground instead: hermes gateway runzM  Or use tmux/screen for persistence: tmux new -s hermes 'hermes gateway run'r)  z(WSL detected but systemd is not running.zIEither enable systemd (add systemd=true to /etc/wsl.conf and restart WSL)z&or run the gateway in foreground mode:zE  hermes gateway run                              # direct foregroundzG  tmux new -s hermes 'hermes gateway run'         # persistent via tmuxzL  nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &  # backgroundz=Service installation is not needed inside a Docker container.uV   The container runtime is your service manager — use Docker restart policies instead:zJ  docker run --restart unless-stopped ...   # auto-restart on crash/rebootz<  docker restart <container>                # manual restartz&To run the gateway: hermes gateway runz4Service installation not supported on this platform.z Run manually: hermes gateway runr  z,uninstall gateway service (managed by NixOS)zcGateway service uninstall is not supported on Termux because there is no managed service to remove.z*Stop manual runs with: hermes gateway stopr   z>Service uninstall is not applicable inside a Docker container.z2To stop the gateway, stop or remove the container:z  docker stop <container>z  docker rm <container>zNot supported on this platform.r  r  T)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  u   ✓ Stopped z( gateway process(es) across all profilesu   ✗ No gateway processes foundu$   ✓ Stopped gateway for this profileu'   ✗ No gateway running for this profilez servicer  zStarting gateway...)r#  uB   ⚠ Cannot restart gateway as a service — linger is not enabled.zK  The gateway user service requires linger to function on headless servers.z$  Run:  sudo loginctl enable-linger r  z    hermes gateway restartu#   ✗ Gateway service restart failed.zL  The service definition exists, but the service manager did not recover it.z3  Fix the service, then retry: hermes gateway startr   r  u   ✓ Gateway is running (PID: z, rD  z-  (Running manually, not as a system service)r  r  zTermux note:z;  Android may stop background jobs when Termux is suspendedz	WSL note:zI  The gateway is running in foreground/manual mode (recommended for WSL).z<  Use tmux or screen for persistence across terminal closes.zTo install as a service:z  hermes gateway installz&  sudo hermes gateway install --systemu   ✗ Gateway is not runningz	To start:z-  hermes gateway run      # Run in foregroundz^  nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &  # Best-effort background startz3  hermes gateway install  # Install as user servicezM  sudo hermes gateway install --system  # Install as boot-time system service),getattrr0  rP  r
   r   r   r   r   r   r*   r   r   r   r*  r7   r  r   r  r  r   r  r  r  r   r   r  r+   r
  rO  r  r   r   r  r  r?  r  r  r  r"  r   r   maprJ   r  )r   subcmdr#  r$  r%  r   r   r  	start_allr   stop_allservice_availabletotalrestart_allservice_configuredservice_stopped	linger_ok_detailr  	_usernamer  r;   r  r>   s                           rC   rR  rR    s   T,d33F ~5$	1--gu--$	511G5':::: << 	FGGGFgu--x//dM488;; 	LMMM0111HQKKK$&& 	xx _```YZZZjkkk%KPPPPPPZZ 	E"""""XX 	<===]^^^:;;;GGGYZZZ[\\\`aaaHQKKKKK^^ 	QRRRjkkkGGG^___PQQQGGG:;;;HQKKKKKHIII4555HQKKKKK	;		<< 	HIIIFx//;; 	wxxx>???HQKKK$&& 	V,,,,,,ZZ 	^^ 		RSSSFGGGGGG-...+,,,HQKKKKK3444HQKKKKK	7		x//D%//	 	F+>>>F FZFZZZ[[[&tEEEE;; 	pqqq0111HQKKK$&& 	((((((ZZ 	OOOOOXX 	>????@@@GGGYZZZ[\\\`aaaGGGrsssHQKKKKK^^ 	NOOOEFFFGGGNOOOPQQQGGGCDDDHQKKKKK3444HQKKKKK	6		4..x// ,	C %(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D+>>>F#4;aa!<E 8TUTTTUUUUU677777 !&(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////(,%%!4   D  6 8 8 ? ? A A  NNN(,%%!4   D % C')) E@AAAAACDDDDDA%5%7%7AAABBBBB	9		!x//dE511" 	#O(** 0EU0S0S0S0Z0Z0\0\ `u  ~B  aC  aC  aC  aJ  aJ  aL  aL  ////&*OO!4   D  6 8 8 ? ? A A  NNN&*OO!4   D+>>>F?9aa:E VTUTTTUUU"4SAAAA '((((** '0EU0S0S0S0Z0Z0\0\ '`u  ~B  aC  aC  aC  aJ  aJ  aL  aL 'V,,,,, ' 6 8 8 ? ? A A 'A&&&&F$&& 	,A,O,O,O,V,V,X,X 	\qy}\~\~\~  ]F  ]F  ]H  ]H 	!%v....$(!!0   ZZ 	244;;== 	!%!!!$(!!0    !  	#(** %>%@%@"	7D(("NNN ' 1 1IGGG^___ghhhGGGLLLMMMGGG78886777F! ;<<<deeeKLLL $%% ><==="4SAAAA '(((""""""A 	#  	#D 
8		tVU++x// %&& .	k,A,O,O,O,V,V,X,X .	k\qy}\~\~\~  ]F  ]F  ]H  ]H .	k4//////ZZ ,	k244;;== ,	k4      %&&D 'kRdiiC6O6ORRRSSSEFFF 5 7 7  +GGG2333 - + +k4kk****;; 
D.)))WXXXXXXX D+&&&efffXYYYYY45554555BCCCCC2333 5 7 7  +GGG2333 - + +k4kk****k"""EFFF;; kz{{{{{XX kcdddhiiiiiOPPPijjjjjg 
	s   [* *[<;[<.\? ?]]*_= =``a a$#a$?e e$#e$f' 'f98f9l$ $l65l6*m; ;nnr(  )FNFrs   )F)r   N)FN)FFN)FF)r  r  )r   FF)__doc__r.  rP   r   rX   r+   r   pathlibr   __file__ry  r|   rV  r   r   gateway.restartr   r   r   hermes_cli.configr   r	   r
   r   r   r   hermes_cli.setupr   r   r   r   r   r   r   r   hermes_cli.colorsr   r   r)   rD   r2   rM   r~   rS   r]   r&   r   r   r   r   r   r   r   r   r   r*   r7   r   r   r}  rJ   r   r}   r   r   r   r   r   CompletedProcessr   r   r   r   r   r  tupler  r  r"  r&  r,  r?  rG  rI  rO  rX  r_  re  rk  rp  r  r  r  r  r  r  r  r  floatr{  r*  r  r  r  r  r  r8   r  r  r  r  r  r  r  r  r  r  r"  r0  r  r  r  r  r  r  r  r  r  r  r  r  r  r)  r@  rP  rR  rb   rT   rC   <module>rk     s     				       



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