+
    Ӄi1                         R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ^ RI
Ht ^ RIHt ^ RIHt ]P                   ! ]4      tR R lt ! R R	]]4      tR# )
zKSSH remote execution environment with ControlMaster connection persistence.N)Path)BaseEnvironment)PersistentShellMixin)is_interruptedc                    V ^8  d   QhRR/#    returnN )formats   "3/home/ubuntu/hermes-agent/tools/environments/ssh.py__annotate__r      s     
 
t 
    c                 T    \         P                  ! R4      '       g   \        R4      hR# )z@Fail fast with a clear error when the SSH client is unavailable.sshzWSSH is not installed or not in PATH. Install OpenSSH client: apt install openssh-clientN)shutilwhichRuntimeErrorr
   r   r   _ensure_ssh_availabler      s&    <<e
 	
 r   c                   8  a a ] tR t^t oRtRV3R lV 3R llltRV3R lR lltR tV3R lR	 ltV3R
 lR lt	R RRRR/V3R lV 3R llllt
Rt]V3R lR l4       tV3R lR ltV3R lR ltR tR tR RRRR/V3R lR llltV 3R ltV3R ltRtVtV ;t# )!SSHEnvironmenta  Run commands on a remote machine over SSH.

Uses SSH ControlMaster for connection persistence so subsequent
commands are fast. Security benefit: the agent cannot modify its
own code since execution happens on a separate machine.

Foreground commands are interruptible: the local ssh process is killed
and a remote kill is attempted over the ControlMaster socket.

When ``persistent=True``, a single long-lived bash shell is kept alive
over SSH and state (cwd, env vars, shell variables) persists across
``execute()`` calls.  Output capture uses file-based IPC on the remote
host (stdout/stderr/exit-code written to temp files, polled via fast
ControlMaster one-shot reads).
c                D   < V ^8  d   QhRS[ RS[ RS[ RS[RS[RS[ RS[/# )r   hostusercwdtimeoutportkey_path
persistent)strintbool)r   __classdict__s   "r   r   SSHEnvironment.__annotate__,   sB     * *S * *# ***-*>A*!*r   c                  < \         SV `  W4R 7       Wn        W n        WPn        W`n        Wpn        \        \        P                  ! 4       4      R,          V n
        V P                  P                  RRR7       V P                  V RV RV R2,          V n        \        4        V P                  4        V P                  4       V n        V P#                  4        V P                  '       d   V P%                  4        R# R# ))r   r   z
hermes-sshT)parentsexist_ok@:z.sockN)super__init__r   r   r   r   r   r   tempfile
gettempdircontrol_dirmkdircontrol_socketr   _establish_connection_detect_remote_home_remote_home_sync_skills_and_credentials_init_persistent_shell)	selfr   r   r   r   r   r   r   	__class__s	   &&&&&&&&r   r*   SSHEnvironment.__init__,   s     	S2			 $ 3 3 56Etd;"..D64&$u1MM""$ 446))+???'') r   Nc                4   < V ^8  d   QhRS[ R,          RS[ /# )r   
extra_argsNr	   )list)r   r"   s   "r   r   r#   A   s      TD[ D r   c                \   R .pVP                  RRV P                   2.4       VP                  RR.4       VP                  RR.4       VP                  RR.4       VP                  RR.4       VP                  RR.4       V P                  ^8w  d'   VP                  R\        V P                  4      .4       V P                  '       d   VP                  R	V P                  .4       V'       d   VP                  V4       VP                  V P                   R
V P                   24       V# )r   -oControlPath=zControlMaster=autozControlPersist=300zBatchMode=yesz StrictHostKeyChecking=accept-newzConnectTimeout=10z-pz-ir'   )extendr/   r   r   r   appendr   r   )r5   r9   cmds   && r   _build_ssh_command!SSHEnvironment._build_ssh_commandA   s    g

DL)<)<(=>?@

D./0

D./0

D/*+

D<=>

D-./99?JJc$))n-.===JJdmm,-JJz"

dii[$))-.
r   c                   V P                  4       pVP                  R 4        \        P                  ! VRR^R7      pVP                  ^ 8w  dK   VP
                  P                  4       ;'       g    VP                  P                  4       p\        RV 24      hR#   \        P                   d(    \        RT P                   RT P                   R24      hi ; i)z!echo 'SSH connection established'Tcapture_outputtextr   zSSH connection failed: zSSH connection to r'   z
 timed outN)rA   r?   
subprocessrun
returncodestderrstripstdoutr   TimeoutExpiredr   r   )r5   r@   result	error_msgs   &   r   r0   $SSHEnvironment._establish_connectionR   s    %%'

67	W^^C4QSTF  A%"MM//1JJV]]5H5H5J	"%<YK#HII & (( 	W!3DII;a		{*UVV	Ws   AB /)B <Cc                    < V ^8  d   QhRS[ /# r   r	   r   )r   r"   s   "r   r   r#   ]   s     $ $S $r   c                v    V P                  4       pVP                  R4       \        P                  ! VRR^
R7      pVP                  P                  4       pV'       d*   VP                  ^ 8X  d   \        P                  RV4       V# T P                  R8X  d   R# RT P                   2#   \         d     L/i ; i)z(Detect the remote user's home directory.z
echo $HOMETrD   zSSH: remote home = %srootz/rootz/home/)rA   r?   rG   rH   rL   rK   rI   loggerdebug	Exceptionr   )r5   r@   rN   homes   &   r   r1   "SSHEnvironment._detect_remote_home]   s    		))+CJJ|$^^C4QSTF==&&(D))Q.4d; 99		{##  		s   AB* (B* *B87B8c                   < V ^8  d   QhRR/# r   r
   )r   r"   s   "r   r   r#   n   s     -J -Jd -Jr   c                    V P                    R2p^ RIHpHp . ROpRV P                   R2pV P
                  ^8w  d   VRV P
                   2,          pV P                  '       d   VRV P                   2,          pVP                  RV.4       V P                   RV P                   2pV! 4        EF	  pVR	,          P                  R
V^4      p\        \        V4      P                  4      p	V P                  4       p
V
P                  RV	 24       \         P"                  ! V
RR^
R7       WGR,          V RV 2.,           p\         P"                  ! VRR^R7      pVP$                  ^ 8X  d!   \&        P)                  RVR,          V4       K  \&        P+                  RVP,                  P/                  4       4       EK  	  V! VR7       F  pVR	,          pV P                  4       p
V
P                  RV 24       \         P"                  ! V
RR^
R7       VVR,          P1                  R4      R,           V RV R2.,           p\         P"                  ! VRR^<R7      pVP$                  ^ 8X  d!   \&        P)                  RVR,          V4       K  \&        P+                  RVP,                  P/                  4       4       K  	  R#   \2         d"   p\&        P+                  RT4        Rp?R# Rp?ii ; i)z?Rsync skills directory and credential files to the remote host.z/.hermes)get_credential_file_mountsget_skills_directory_mountzssh -o ControlPath=z -o ControlMaster=autoz -p z -i z-er'   container_pathz/root/.hermesz	mkdir -p TrD   	host_pathr(   zSSH: synced credential %s -> %sz SSH: rsync credential failed: %s)container_base/zSSH: synced skills dir %s -> %sz SSH: rsync skills dir failed: %sz*SSH: could not sync skills/credentials: %sN)rsyncz-azz--timeout=30z--safe-links)r2   tools.credential_filesr]   r^   r/   r   r   r>   r   r   replacer   r   parentrA   r?   rG   rH   rI   rV   inforW   rJ   rK   rstriprX   )r5   ra   r]   r^   
rsync_basessh_optsdest_prefixmount_entryremote_path
parent_dir	mkdir_cmdr@   rN   skills_mountes   &              r   r3   +SSHEnvironment._sync_skills_and_credentialsn   s   +	J $ 1 12(;NeIJ,T-@-@,AAWXHyyBd499+..}}}d4==/22tX./!YYKq4K  :;)*:;CCOUcefg k!2!9!9:
 335	  9ZL!9:yDRTU $<Q{m>\#]]#DtUWX$$)KK A;{C[]hiLL!CV]]EXEXEZ[  < !;. Y*+;< 335	  9[M!:;yDRTU  -44S9C?"m1[M3$  $DtUWX$$)KK A<P[C\^ijLL!CV]]EXEXEZ[ !Z  	JLLEqII	Js   A#J? &IJ? ?K+
K&&K+r   
stdin_datac          
      T   < V ^8  d   QhRS[ RS[ RS[R,          RS[ R,          RS[/# r   commandr   r   Nrs   r	   r   r    dict)r   r"   s   "r   r   r#      sB     U Us U UtU$JU26Ur   c               F   < V P                  4        \        SV `	  WW4R 7      # ))r   rs   )r3   r)   execute)r5   rv   r   r   rs   r6   s   &&&$$r   rz   SSHEnvironment.execute   s$    
 	))+wwWTTr   g333333?c                    < V ^8  d   QhRS[ /# rR   rS   )r   r"   s   "r   r   r#      s     5 5c 5r   c                     R V P                    2# )z/tmp/hermes-ssh-)_session_id)r5   s   &r   _temp_prefixSSHEnvironment._temp_prefix   s    !$"2"2!344r   c                4   < V ^8  d   QhRS[ P                  /# rR   )rG   Popen)r   r"   s   "r   r   r#      s     	
 	
j&6&6 	
r   c                    V P                  4       pVP                  R 4       \        P                  ! V\        P                  \        P                  \        P
                  RR7      # )zbash -lT)stdinrL   rJ   rF   )rA   r?   rG   r   PIPEDEVNULLr5   r@   s   & r   _spawn_shell_process#SSHEnvironment._spawn_shell_process   sL    %%'

9//??%%
 	
r   c                6   < V ^8  d   QhRS[ RS[S[ ,          /# )r   pathsr	   )r   r:   )r   r"   s   "r   r   r#      s     % %s %tCy %r   c                  a \        V4      ^8X  dU   V P                  4       pVP                  RV^ ,           R24        \        P                  ! VRR^
R7      pVP
                  .# RV P                   R2oRP                  V3R	 lV 4       4      pV P                  4       pVP                  V4        \        P                  ! VRR^
R7      pVP
                  P                  SR
,           4      p\        \        V4      4       Uu. uF  qf\        V4      8  d	   WV,          MRNK  	  up#   \        P                  \        3 d    R.u # i ; iu upi   \        P                  \        3 d    R.\        T4      ,          u # i ; i)   cat z 2>/dev/nullTrD    __HERMES_SEP___z; c              3   6   <"   T F  pR V RS R2x  K  	  R# 5i)r   z 2>/dev/null; echo ''Nr
   ).0pdelims   & r   	<genexpr>2SSHEnvironment._read_temp_files.<locals>.<genexpr>   s$      
<Aqd1#)%2Es   
)lenrA   r?   rG   rH   rL   rM   OSErrorr~   joinsplitrange)r5   r   r@   rN   scriptpartsir   s   &*     @r   _read_temp_filesSSHEnvironment._read_temp_files   se   u:?))+CJJeAhZ|45#4 &   0 014 
<A
 
 %%'

6	%^^DtRF MM''5E@Ec%j@QR@Q1CJEHB6@QRR --w7 t S))73 	%4#e*$$	%s6   &D* 3AE "E'E *E
EE .FFc                    V P                   f   R # V P                  4       pVP                  RV P                    R24        \        P                  ! VR^R7       R #   \        P
                  \        3 d     R # i ; i)Nz	pkill -P z 2>/dev/null; trueTrE   r   )
_shell_pidrA   r?   rG   rH   rM   r   r   s   & r   _kill_shell_children#SSHEnvironment._kill_shell_children   sj    ??"%%'

Yt//ABC	NN3tQ?))73 		s   A A;:A;c                    V P                  4       pVP                  R V P                   R24        \        P                  ! VR^R7       R#   \        P
                  \        3 d     R# i ; i)zrm -f z-*Tr   N)rA   r?   r   rG   rH   rM   r   r   s   & r   _cleanup_temp_files"SSHEnvironment._cleanup_temp_files   s^    %%'

VD--.b12	NN3tQ?))73 		s   A A+*A+c          
      T   < V ^8  d   QhRS[ RS[ RS[R,          RS[ R,          RS[/# ru   rw   )r   r"   s   "r   r   r#      sI     HR HR HR# HR"%*HR%(4ZHR;?HRr   c               r  aa T;'       g    V P                   pV P                  V4      w  rgVR 8X  d   RV 2pMWVP                  R4      '       d%   R\        P                  ! VR,          4       RV 2pMR\        P                  ! V4       RV 2pT;'       g    V P
                  p	Ve   Ve
   Wt,           p
M	Ve   Tp
MTp
V P                  4       pVP                  V4       V P                  W:4      pVP                  RR4       . o\        P                  ! T\        P                  \        P                  V
'       d   \        P                  M\        P                  R	R
7      oV
'       d7    SP                  P!                  V
4       SP                  P#                  4        VV3R lp\(        P*                  ! VR	R7      pVP-                  4        \.        P0                  ! 4       V	,           pSP3                  4       f   \5        4       '       dR   SP7                  4         SP9                  ^R7       VP?                  ^R7       RRP?                  S4      R,           R^/# \.        P0                  ! 4       V8  d4   SP=                  4        VP?                  ^R7       V PA                  V	4      # \.        PB                  ! R4       K  VP?                  ^R7       RRP?                  S4      RSPD                  /#   \$        \&        3 d     ELhi ; i  \        P:                   d    SP=                  4         ELi ; i)~zcd ~ && z~/zcd ~/:r   NNz && zcd Nr   T)rL   rJ   r   rF   c                  v   <  SP                    F  p SP                  V 4       K  	  R #   \         d     R # i ; iN)rL   r?   rX   )line_output_chunksprocs    r   _drain/SSHEnvironment._execute_oneshot.<locals>._drain  s6     KKD"))$/ ( s   $) 88)targetdaemon)r   outputr   z
[Command interrupted]rI   g?)#r   _prepare_command
startswithshlexquoter   rA   r?   _build_run_kwargspoprG   r   r   STDOUTr   r   writecloseBrokenPipeErrorr   	threadingThreadstarttime	monotonicpollr   	terminatewaitrM   killr   _timeout_resultsleeprI   )r5   rv   r   r   rs   work_direxec_command
sudo_stdinwrappedeffective_timeouteffective_stdinr@   kwargsr   readerdeadliner   r   s   &&&$$           @@r   _execute_oneshotSSHEnvironment._execute_oneshot   s    ??$((#'#8#8#A s? /G  &&ekk(2,78\NKGEKK12$|nEG#33t||!j&<(5O#(O(O%%'

7''A

9d#??$$%4*//*:L:L
 

  1

  "	 !!=>>#&77iik!  IIaI( A&bggn58QQ #  ~~(*		A&++,=>>JJsOA"''.1<QQC $W- & "00  IIK s$   ,5K5 L 5L
	L
%L65L6c                  < \         SV `  4        V P                  P                  4       '       df    R RRV P                   2RRV P                   RV P
                   2.p\        P                  ! VR^R7        V P                  P                  4        R# R#   \        \        P                  3 d     L<i ; i  \         d     R# i ; i)	r   r<   r=   z-Oexitr'   Tr   N)r)   cleanupr/   existsr   r   rG   rH   r   SubprocessErrorunlink)r5   r@   r6   s   & r   r   SSHEnvironment.cleanup-  s    %%''dl43F3F2G$HV		{!DII;%?As4C##**, (
 Z778   s$   AB 9B8 B54B58CCc                &   < V ^8  d   Qh/ S[ ;R&   # )r   _poll_interval_start)float)r   r"   s   "r   r   r#      s     T  &U r   )r2   r-   r/   r   r   r   r   r   )r   <      r   Fr   )r   )__name__
__module____qualname____firstlineno____doc__r*   rA   r0   r1   r3   rz   r   propertyr   r   r   r   r   r   r   __annotate_func____static_attributes____classdictcell____classcell__)r6   r"   s   @@r   r   r      s      * ** "	W$ $"-J -J^U&*U)-U U #'5 5	
 	
% %6HR/3HR26HR HRTe  r   r   )r   loggingr   r   rG   r+   r   r   pathlibr   tools.environments.baser   #tools.environments.persistent_shellr   tools.interruptr   	getLoggerr   rV   r   r   r
   r   r   <module>r      sO    Q         3 D *			8	$
^)? ^r   